pdmt5 0.1.5__py3-none-any.whl → 0.1.6__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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from datetime import timedelta
|
|
6
|
+
from math import floor
|
|
6
7
|
from typing import TYPE_CHECKING, Any, Literal
|
|
7
8
|
|
|
8
9
|
from pydantic import ConfigDict, Field
|
|
@@ -263,41 +264,60 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
263
264
|
)
|
|
264
265
|
return []
|
|
265
266
|
|
|
266
|
-
def
|
|
267
|
-
|
|
267
|
+
def calculate_minimum_order_margin(
|
|
268
|
+
self,
|
|
269
|
+
symbol: str,
|
|
270
|
+
order_side: Literal["BUY", "SELL"],
|
|
271
|
+
) -> dict[str, float]:
|
|
272
|
+
"""Calculate the minimum order margins for a given symbol.
|
|
268
273
|
|
|
269
274
|
Args:
|
|
270
|
-
symbol: Symbol for which to calculate minimum order margins.
|
|
275
|
+
symbol: Symbol for which to calculate the minimum order margins.
|
|
276
|
+
order_side: Optional side of the order, either "BUY" or "SELL".
|
|
271
277
|
|
|
272
278
|
Returns:
|
|
273
|
-
Dictionary with margin
|
|
274
|
-
|
|
275
|
-
Raises:
|
|
276
|
-
Mt5TradingError: If margin calculation fails.
|
|
279
|
+
Dictionary with minimum volume and margin for the specified order side.
|
|
277
280
|
"""
|
|
278
281
|
symbol_info = self.symbol_info_as_dict(symbol=symbol)
|
|
279
282
|
symbol_info_tick = self.symbol_info_tick_as_dict(symbol=symbol)
|
|
280
|
-
|
|
281
|
-
|
|
283
|
+
return {
|
|
284
|
+
"volume": symbol_info["volume_min"],
|
|
285
|
+
"margin": self.order_calc_margin(
|
|
286
|
+
action=getattr(self.mt5, f"ORDER_TYPE_{order_side.upper()}"),
|
|
287
|
+
symbol=symbol,
|
|
288
|
+
volume=symbol_info["volume_min"],
|
|
289
|
+
price=(
|
|
290
|
+
symbol_info_tick["bid"]
|
|
291
|
+
if order_side == "SELL"
|
|
292
|
+
else symbol_info_tick["ask"]
|
|
293
|
+
),
|
|
294
|
+
),
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
def calculate_volume_by_margin(
|
|
298
|
+
self,
|
|
299
|
+
symbol: str,
|
|
300
|
+
margin: float,
|
|
301
|
+
order_side: Literal["BUY", "SELL"],
|
|
302
|
+
) -> float:
|
|
303
|
+
"""Calculate volume based on margin for a given symbol and order side.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
symbol: Symbol for which to calculate the volume.
|
|
307
|
+
margin: Margin amount to use for the calculation.
|
|
308
|
+
order_side: Side of the order, either "BUY" or "SELL".
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Calculated volume as a float.
|
|
312
|
+
"""
|
|
313
|
+
min_order_margin_dict = self.calculate_minimum_order_margin(
|
|
282
314
|
symbol=symbol,
|
|
283
|
-
|
|
284
|
-
price=symbol_info_tick["ask"],
|
|
315
|
+
order_side=order_side,
|
|
285
316
|
)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
volume=symbol_info["volume_min"],
|
|
290
|
-
price=symbol_info_tick["bid"],
|
|
317
|
+
return (
|
|
318
|
+
floor(margin / min_order_margin_dict["margin"])
|
|
319
|
+
* min_order_margin_dict["volume"]
|
|
291
320
|
)
|
|
292
|
-
min_order_margins = {"ask": min_ask_order_margin, "bid": min_bid_order_margin}
|
|
293
|
-
self.logger.info("Minimum order margins for %s: %s", symbol, min_order_margins)
|
|
294
|
-
if all(min_order_margins.values()):
|
|
295
|
-
return min_order_margins
|
|
296
|
-
else:
|
|
297
|
-
error_message = (
|
|
298
|
-
f"Failed to calculate minimum order margins for symbol: {symbol}."
|
|
299
|
-
)
|
|
300
|
-
raise Mt5TradingError(error_message)
|
|
301
321
|
|
|
302
322
|
def calculate_spread_ratio(
|
|
303
323
|
self,
|
|
@@ -477,15 +497,15 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
477
497
|
def calculate_new_position_margin_ratio(
|
|
478
498
|
self,
|
|
479
499
|
symbol: str,
|
|
480
|
-
|
|
481
|
-
|
|
500
|
+
new_position_side: Literal["BUY", "SELL"] | None = None,
|
|
501
|
+
new_position_volume: float = 0,
|
|
482
502
|
) -> float:
|
|
483
503
|
"""Calculate the margin ratio for a new position.
|
|
484
504
|
|
|
485
505
|
Args:
|
|
486
506
|
symbol: Symbol for which to calculate the margin ratio.
|
|
487
|
-
|
|
488
|
-
|
|
507
|
+
new_position_side: Side of the new position, either "BUY" or "SELL".
|
|
508
|
+
new_position_volume: Volume of the new position.
|
|
489
509
|
|
|
490
510
|
Returns:
|
|
491
511
|
float: Margin ratio for the new position as a fraction of account equity.
|
|
@@ -499,20 +519,20 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
499
519
|
positions_df["signed_margin"].sum() if positions_df.size else 0
|
|
500
520
|
)
|
|
501
521
|
symbol_info_tick = self.symbol_info_tick_as_dict(symbol=symbol)
|
|
502
|
-
if
|
|
522
|
+
if not (new_position_side and new_position_volume):
|
|
503
523
|
new_signed_margin = 0
|
|
504
|
-
elif
|
|
524
|
+
elif new_position_side.upper() == "BUY":
|
|
505
525
|
new_signed_margin = self.order_calc_margin(
|
|
506
526
|
action=self.mt5.ORDER_TYPE_BUY,
|
|
507
527
|
symbol=symbol,
|
|
508
|
-
volume=
|
|
528
|
+
volume=new_position_volume,
|
|
509
529
|
price=symbol_info_tick["ask"],
|
|
510
530
|
)
|
|
511
|
-
elif
|
|
531
|
+
elif new_position_side.upper() == "SELL":
|
|
512
532
|
new_signed_margin = -self.order_calc_margin(
|
|
513
533
|
action=self.mt5.ORDER_TYPE_SELL,
|
|
514
534
|
symbol=symbol,
|
|
515
|
-
volume=
|
|
535
|
+
volume=new_position_volume,
|
|
516
536
|
price=symbol_info_tick["bid"],
|
|
517
537
|
)
|
|
518
538
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdmt5
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
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>
|
|
@@ -42,6 +42,8 @@ Pandas-based data handler for MetaTrader 5
|
|
|
42
42
|
- 🚀 **Context Manager Support**: Clean initialization and cleanup with `with` statements
|
|
43
43
|
- 📈 **Time Series Ready**: OHLCV data with proper datetime indexing
|
|
44
44
|
- 🛡️ **Robust Error Handling**: Custom exceptions with detailed MT5 error information
|
|
45
|
+
- 💰 **Advanced Trading Operations**: Position management, margin calculations, and risk analysis tools
|
|
46
|
+
- 🧪 **Dry Run Mode**: Test trading strategies without executing real trades
|
|
45
47
|
|
|
46
48
|
## Requirements
|
|
47
49
|
|
|
@@ -195,9 +197,10 @@ Advanced trading operations client that extends Mt5DataClient:
|
|
|
195
197
|
- **Position Management**:
|
|
196
198
|
- `close_open_positions()` - Close all positions for specified symbol(s)
|
|
197
199
|
- `place_market_order()` - Place market orders with configurable side, volume, and execution modes
|
|
198
|
-
- `
|
|
199
|
-
- **
|
|
200
|
-
- `
|
|
200
|
+
- `update_sltp_for_open_positions()` - Modify stop loss and take profit levels for open positions
|
|
201
|
+
- **Margin Calculations**:
|
|
202
|
+
- `calculate_minimum_order_margin()` - Calculate minimum required margin for a specific order side
|
|
203
|
+
- `calculate_volume_by_margin()` - Calculate maximum volume for given margin amount
|
|
201
204
|
- `calculate_spread_ratio()` - Calculate normalized bid-ask spread ratio
|
|
202
205
|
- `calculate_new_position_margin_ratio()` - Calculate margin ratio for potential new positions
|
|
203
206
|
- **Simplified Data Access**:
|
|
@@ -299,22 +302,20 @@ with Mt5TradingClient(config=config, order_filling_mode="IOC") as trader:
|
|
|
299
302
|
)
|
|
300
303
|
print(f"Order placed: {order_result['retcode']}")
|
|
301
304
|
|
|
302
|
-
# Update stop loss and take profit for
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
)
|
|
311
|
-
print(f"Position updated: {update_result['retcode']}")
|
|
305
|
+
# Update stop loss and take profit for open positions
|
|
306
|
+
update_results = trader.update_sltp_for_open_positions(
|
|
307
|
+
symbol="EURUSD",
|
|
308
|
+
stop_loss=1.0950, # New stop loss
|
|
309
|
+
take_profit=1.1050 # New take profit
|
|
310
|
+
)
|
|
311
|
+
for result in update_results:
|
|
312
|
+
print(f"Position updated: {result['retcode']}")
|
|
312
313
|
|
|
313
314
|
# Calculate margin ratio for a new position
|
|
314
315
|
margin_ratio = trader.calculate_new_position_margin_ratio(
|
|
315
316
|
symbol="EURUSD",
|
|
316
|
-
|
|
317
|
-
|
|
317
|
+
new_position_side="SELL",
|
|
318
|
+
new_position_volume=0.2
|
|
318
319
|
)
|
|
319
320
|
print(f"New position margin ratio: {margin_ratio:.2%}")
|
|
320
321
|
|
|
@@ -347,10 +348,18 @@ with Mt5TradingClient(config=config) as trader:
|
|
|
347
348
|
spread_ratio = trader.calculate_spread_ratio("EURUSD")
|
|
348
349
|
print(f"EURUSD spread ratio: {spread_ratio:.5f}")
|
|
349
350
|
|
|
350
|
-
# Get minimum order
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
print(f"Minimum
|
|
351
|
+
# Get minimum order margin for BUY and SELL
|
|
352
|
+
buy_margin = trader.calculate_minimum_order_margin("EURUSD", "BUY")
|
|
353
|
+
sell_margin = trader.calculate_minimum_order_margin("EURUSD", "SELL")
|
|
354
|
+
print(f"Minimum BUY margin: {buy_margin['margin']} (volume: {buy_margin['volume']})")
|
|
355
|
+
print(f"Minimum SELL margin: {sell_margin['margin']} (volume: {sell_margin['volume']})")
|
|
356
|
+
|
|
357
|
+
# Calculate volume by margin
|
|
358
|
+
available_margin = 1000.0
|
|
359
|
+
max_buy_volume = trader.calculate_volume_by_margin("EURUSD", available_margin, "BUY")
|
|
360
|
+
max_sell_volume = trader.calculate_volume_by_margin("EURUSD", available_margin, "SELL")
|
|
361
|
+
print(f"Max BUY volume for ${available_margin}: {max_buy_volume}")
|
|
362
|
+
print(f"Max SELL volume for ${available_margin}: {max_sell_volume}")
|
|
354
363
|
|
|
355
364
|
# Get recent OHLC data with custom timeframe
|
|
356
365
|
rates_df = trader.fetch_latest_rates_as_df(
|
|
@@ -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=GOOYsbsjKtp26dw1VB6siZ1luBttD8cv-KNZi6pPUEs,20107
|
|
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.1.6.dist-info/METADATA,sha256=cBYUp4lIyQuw7aRN7dphpgtejgW5jamAvYImYQiQS9o,16824
|
|
7
|
+
pdmt5-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
+
pdmt5-0.1.6.dist-info/licenses/LICENSE,sha256=iABrdaUGOBWLYotFupB_PGe8arV5o7rVhn-_vK6P704,1073
|
|
9
|
+
pdmt5-0.1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|