pdmt5 0.1.9__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pdmt5/trading.py
CHANGED
|
@@ -131,6 +131,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
131
131
|
self,
|
|
132
132
|
symbol: str | None = None,
|
|
133
133
|
order_filling_mode: Literal["IOC", "FOK", "RETURN"] = "IOC",
|
|
134
|
+
raise_on_error: bool = False,
|
|
134
135
|
dry_run: bool = False,
|
|
135
136
|
**kwargs: Any, # noqa: ANN401
|
|
136
137
|
) -> list[dict[str, Any]]:
|
|
@@ -139,6 +140,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
139
140
|
Args:
|
|
140
141
|
symbol: Optional symbol filter.
|
|
141
142
|
order_filling_mode: Order filling mode, either "IOC", "FOK", or "RETURN".
|
|
143
|
+
raise_on_error: If True, raise an exception on error.
|
|
142
144
|
dry_run: If True, only check the order without sending it.
|
|
143
145
|
**kwargs: Additional keyword arguments for request parameters.
|
|
144
146
|
|
|
@@ -170,6 +172,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
170
172
|
"position": p["ticket"],
|
|
171
173
|
**kwargs,
|
|
172
174
|
},
|
|
175
|
+
raise_on_error=raise_on_error,
|
|
173
176
|
dry_run=dry_run,
|
|
174
177
|
)
|
|
175
178
|
for p in positions_dict
|
|
@@ -225,6 +228,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
225
228
|
order_side: Literal["BUY", "SELL"],
|
|
226
229
|
order_filling_mode: Literal["IOC", "FOK", "RETURN"] = "IOC",
|
|
227
230
|
order_time_mode: Literal["GTC", "DAY", "SPECIFIED", "SPECIFIED_DAY"] = "GTC",
|
|
231
|
+
raise_on_error: bool = False,
|
|
228
232
|
dry_run: bool = False,
|
|
229
233
|
**kwargs: Any, # noqa: ANN401
|
|
230
234
|
) -> dict[str, Any]:
|
|
@@ -237,12 +241,14 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
237
241
|
order_filling_mode: Order filling mode, either "IOC", "FOK", or "RETURN".
|
|
238
242
|
order_time_mode: Order time mode, either "GTC", "DAY", "SPECIFIED",
|
|
239
243
|
or "SPECIFIED_DAY".
|
|
244
|
+
raise_on_error: If True, raise an error on operation failure.
|
|
240
245
|
dry_run: If True, only check the order without sending it.
|
|
241
246
|
**kwargs: Additional keyword arguments for request parameters.
|
|
242
247
|
|
|
243
248
|
Returns:
|
|
244
249
|
Dictionary with operation result.
|
|
245
250
|
"""
|
|
251
|
+
self.logger.info("Placing market order: %s %s %s", order_side, volume, symbol)
|
|
246
252
|
return self._send_or_check_order(
|
|
247
253
|
request={
|
|
248
254
|
"action": self.mt5.TRADE_ACTION_DEAL,
|
|
@@ -255,6 +261,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
255
261
|
"type_time": getattr(self.mt5, f"ORDER_TIME_{order_time_mode.upper()}"),
|
|
256
262
|
**kwargs,
|
|
257
263
|
},
|
|
264
|
+
raise_on_error=raise_on_error,
|
|
258
265
|
dry_run=dry_run,
|
|
259
266
|
)
|
|
260
267
|
|
|
@@ -264,6 +271,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
264
271
|
stop_loss: float | None = None,
|
|
265
272
|
take_profit: float | None = None,
|
|
266
273
|
tickets: list[int] | None = None,
|
|
274
|
+
raise_on_error: bool = False,
|
|
267
275
|
dry_run: bool = False,
|
|
268
276
|
**kwargs: Any, # noqa: ANN401
|
|
269
277
|
) -> list[dict[str, Any]]:
|
|
@@ -275,6 +283,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
275
283
|
take_profit: New Take Profit price. If None, it will not be changed.
|
|
276
284
|
tickets: List of position tickets to filter positions. If None, all open
|
|
277
285
|
positions for the symbol will be considered.
|
|
286
|
+
raise_on_error: If True, raise an error on operation failure.
|
|
278
287
|
dry_run: If True, only check the order without sending it.
|
|
279
288
|
**kwargs: Additional keyword arguments for request parameters.
|
|
280
289
|
|
|
@@ -315,8 +324,17 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
315
324
|
if sl != p["sl"] or tp != p["tp"]
|
|
316
325
|
]
|
|
317
326
|
if order_requests:
|
|
327
|
+
self.logger.info(
|
|
328
|
+
"Updating SL/TP for %d positions for %s: %s/%s",
|
|
329
|
+
len(order_requests),
|
|
330
|
+
symbol,
|
|
331
|
+
sl,
|
|
332
|
+
tp,
|
|
333
|
+
)
|
|
318
334
|
return [
|
|
319
|
-
self._send_or_check_order(
|
|
335
|
+
self._send_or_check_order(
|
|
336
|
+
request=r, raise_on_error=raise_on_error, dry_run=dry_run
|
|
337
|
+
)
|
|
320
338
|
for r in order_requests
|
|
321
339
|
]
|
|
322
340
|
else:
|
|
@@ -354,15 +372,22 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
354
372
|
else symbol_info_tick["ask"]
|
|
355
373
|
),
|
|
356
374
|
)
|
|
375
|
+
result = {"volume": symbol_info["volume_min"], "margin": margin}
|
|
357
376
|
if margin:
|
|
358
|
-
|
|
377
|
+
self.logger.info(
|
|
378
|
+
"Calculated minimum %s order margin for %s: %s",
|
|
379
|
+
order_side,
|
|
380
|
+
symbol,
|
|
381
|
+
result,
|
|
382
|
+
)
|
|
359
383
|
else:
|
|
360
384
|
self.logger.warning(
|
|
361
|
-
"
|
|
362
|
-
symbol,
|
|
385
|
+
"Calculated minimum order margin to %s %s: %s",
|
|
363
386
|
order_side,
|
|
387
|
+
symbol,
|
|
388
|
+
result,
|
|
364
389
|
)
|
|
365
|
-
|
|
390
|
+
return result
|
|
366
391
|
|
|
367
392
|
def calculate_volume_by_margin(
|
|
368
393
|
self,
|
|
@@ -385,12 +410,19 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
385
410
|
order_side=order_side,
|
|
386
411
|
)
|
|
387
412
|
if min_order_margin_dict["margin"]:
|
|
388
|
-
|
|
413
|
+
result = (
|
|
389
414
|
floor(margin / min_order_margin_dict["margin"])
|
|
390
415
|
* min_order_margin_dict["volume"]
|
|
391
416
|
)
|
|
392
417
|
else:
|
|
393
|
-
|
|
418
|
+
result = 0.0
|
|
419
|
+
self.logger.info(
|
|
420
|
+
"Calculated volume by margin to %s %s: %s",
|
|
421
|
+
order_side,
|
|
422
|
+
symbol,
|
|
423
|
+
result,
|
|
424
|
+
)
|
|
425
|
+
return result
|
|
394
426
|
|
|
395
427
|
def calculate_spread_ratio(
|
|
396
428
|
self,
|
|
@@ -405,11 +437,13 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
405
437
|
Spread ratio as a float.
|
|
406
438
|
"""
|
|
407
439
|
symbol_info_tick = self.symbol_info_tick_as_dict(symbol=symbol)
|
|
408
|
-
|
|
440
|
+
result = (
|
|
409
441
|
(symbol_info_tick["ask"] - symbol_info_tick["bid"])
|
|
410
442
|
/ (symbol_info_tick["ask"] + symbol_info_tick["bid"])
|
|
411
443
|
* 2
|
|
412
444
|
)
|
|
445
|
+
self.logger.info("Calculated spread ratio for %s: %s", symbol, result)
|
|
446
|
+
return result
|
|
413
447
|
|
|
414
448
|
def fetch_latest_rates_as_df(
|
|
415
449
|
self,
|
|
@@ -440,13 +474,20 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
440
474
|
)
|
|
441
475
|
raise Mt5TradingError(error_message) from e
|
|
442
476
|
else:
|
|
443
|
-
|
|
477
|
+
result = self.copy_rates_from_pos_as_df(
|
|
444
478
|
symbol=symbol,
|
|
445
479
|
timeframe=timeframe,
|
|
446
480
|
start_pos=0,
|
|
447
481
|
count=count,
|
|
448
482
|
index_keys=index_keys,
|
|
449
483
|
)
|
|
484
|
+
self.logger.info(
|
|
485
|
+
"Fetched latest %s rates for %s: %d rows",
|
|
486
|
+
granularity,
|
|
487
|
+
symbol,
|
|
488
|
+
result.shape[0],
|
|
489
|
+
)
|
|
490
|
+
return result
|
|
450
491
|
|
|
451
492
|
def fetch_latest_ticks_as_df(
|
|
452
493
|
self,
|
|
@@ -465,13 +506,19 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
465
506
|
pd.DataFrame: Tick data with time index.
|
|
466
507
|
"""
|
|
467
508
|
last_tick_time = self.symbol_info_tick_as_dict(symbol=symbol)["time"]
|
|
468
|
-
|
|
509
|
+
result = self.copy_ticks_range_as_df(
|
|
469
510
|
symbol=symbol,
|
|
470
511
|
date_from=(last_tick_time - timedelta(seconds=seconds)),
|
|
471
512
|
date_to=(last_tick_time + timedelta(seconds=seconds)),
|
|
472
513
|
flags=self.mt5.COPY_TICKS_ALL,
|
|
473
514
|
index_keys=index_keys,
|
|
474
515
|
)
|
|
516
|
+
self.logger.info(
|
|
517
|
+
"Fetched latest ticks for %s: %d rows",
|
|
518
|
+
symbol,
|
|
519
|
+
result.shape[0],
|
|
520
|
+
)
|
|
521
|
+
return result
|
|
475
522
|
|
|
476
523
|
def collect_entry_deals_as_df(
|
|
477
524
|
self,
|
|
@@ -497,14 +544,20 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
497
544
|
index_keys=index_keys,
|
|
498
545
|
)
|
|
499
546
|
if deals_df.empty:
|
|
500
|
-
|
|
547
|
+
result = deals_df
|
|
501
548
|
else:
|
|
502
|
-
|
|
549
|
+
result = deals_df.pipe(
|
|
503
550
|
lambda d: d[
|
|
504
551
|
d["entry"]
|
|
505
552
|
& d["type"].isin({self.mt5.DEAL_TYPE_BUY, self.mt5.DEAL_TYPE_SELL})
|
|
506
553
|
]
|
|
507
554
|
)
|
|
555
|
+
self.logger.info(
|
|
556
|
+
"Collected entry deals for %s: %d rows",
|
|
557
|
+
symbol,
|
|
558
|
+
result.shape[0],
|
|
559
|
+
)
|
|
560
|
+
return result
|
|
508
561
|
|
|
509
562
|
def fetch_positions_with_metrics_as_df(
|
|
510
563
|
self,
|
|
@@ -520,7 +573,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
520
573
|
"""
|
|
521
574
|
positions_df = self.positions_get_as_df(symbol=symbol)
|
|
522
575
|
if positions_df.empty:
|
|
523
|
-
|
|
576
|
+
result = positions_df
|
|
524
577
|
else:
|
|
525
578
|
symbol_info_tick = self.symbol_info_tick_as_dict(symbol=symbol)
|
|
526
579
|
ask_margin = self.order_calc_margin(
|
|
@@ -535,7 +588,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
535
588
|
volume=1,
|
|
536
589
|
price=symbol_info_tick["bid"],
|
|
537
590
|
)
|
|
538
|
-
|
|
591
|
+
result = (
|
|
539
592
|
positions_df.assign(
|
|
540
593
|
elapsed_seconds=lambda d: (
|
|
541
594
|
symbol_info_tick["time"] - d["time"]
|
|
@@ -566,6 +619,12 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
566
619
|
)
|
|
567
620
|
.drop(columns=["buy_i", "sell_i", "sign", "underlier_increase_ratio"])
|
|
568
621
|
)
|
|
622
|
+
self.logger.info(
|
|
623
|
+
"Fetched positions with metrics for %s: %d rows",
|
|
624
|
+
symbol,
|
|
625
|
+
result.shape[0],
|
|
626
|
+
)
|
|
627
|
+
return result
|
|
569
628
|
|
|
570
629
|
def calculate_new_position_margin_ratio(
|
|
571
630
|
self,
|
|
@@ -585,7 +644,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
585
644
|
"""
|
|
586
645
|
account_info = self.account_info_as_dict()
|
|
587
646
|
if not account_info["equity"]:
|
|
588
|
-
|
|
647
|
+
result = 0.0
|
|
589
648
|
else:
|
|
590
649
|
positions_df = self.fetch_positions_with_metrics_as_df(symbol=symbol)
|
|
591
650
|
current_signed_margin = (
|
|
@@ -610,6 +669,12 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
610
669
|
)
|
|
611
670
|
else:
|
|
612
671
|
new_signed_margin = 0
|
|
613
|
-
|
|
672
|
+
result = abs(
|
|
614
673
|
(new_signed_margin + current_signed_margin) / account_info["equity"]
|
|
615
674
|
)
|
|
675
|
+
self.logger.info(
|
|
676
|
+
"Calculated new position margin ratio for %s: %s",
|
|
677
|
+
symbol,
|
|
678
|
+
result,
|
|
679
|
+
)
|
|
680
|
+
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdmt5
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Pandas-based data handler for MetaTrader 5
|
|
5
5
|
Project-URL: Repository, https://github.com/dceoy/pdmt5.git
|
|
6
6
|
Author-email: dceoy <dceoy@users.noreply.github.com>
|
|
@@ -15,7 +15,7 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
18
|
-
Requires-Python:
|
|
18
|
+
Requires-Python: <3.14,>=3.11
|
|
19
19
|
Requires-Dist: metatrader5>=5.0.4424; sys_platform == 'win32'
|
|
20
20
|
Requires-Dist: pandas>=2.2.2
|
|
21
21
|
Requires-Dist: pydantic>=2.9.0
|
|
@@ -56,8 +56,7 @@ Pandas-based data handler for MetaTrader 5
|
|
|
56
56
|
### Using pip
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
pip install -U pdmt5
|
|
60
|
-
pip install -U MetaTrader5
|
|
59
|
+
pip install -U pdmt5 MetaTrader5
|
|
61
60
|
```
|
|
62
61
|
|
|
63
62
|
### Using uv
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
pdmt5/__init__.py,sha256=QbSFrsi7_bgFzb-ma4DmmUjR90UvrqKMnRZq1wPRmoI,446
|
|
2
2
|
pdmt5/dataframe.py,sha256=rUWtR23hrXBdBqzJhbOlIemNy73RrjSTZZJUhwoL6io,38084
|
|
3
3
|
pdmt5/mt5.py,sha256=KgxHapIrh5b4L0wIOAQIjfXNZafalihbFrh9fhYHmrI,32254
|
|
4
|
-
pdmt5/trading.py,sha256=
|
|
4
|
+
pdmt5/trading.py,sha256=Qd4RhZprDcWTzT3JmKl8XGVq8i9hExNdPSJbCRdUx-s,25569
|
|
5
5
|
pdmt5/utils.py,sha256=Ll5Q3OE5h1A_sZ_qVEnOPGniFlT6_MmHfuu0zqeLdeU,3913
|
|
6
|
-
pdmt5-0.1.
|
|
7
|
-
pdmt5-0.1.
|
|
8
|
-
pdmt5-0.1.
|
|
9
|
-
pdmt5-0.1.
|
|
6
|
+
pdmt5-0.2.1.dist-info/METADATA,sha256=OjDjumI_5kGHyEjpIg-xgZyGJSULRUJM_LQnv2IeJ-4,16100
|
|
7
|
+
pdmt5-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
+
pdmt5-0.2.1.dist-info/licenses/LICENSE,sha256=iABrdaUGOBWLYotFupB_PGe8arV5o7rVhn-_vK6P704,1073
|
|
9
|
+
pdmt5-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|