pdmt5 0.1.6__py3-none-any.whl → 0.1.8__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
|
@@ -6,7 +6,7 @@ from datetime import timedelta
|
|
|
6
6
|
from math import floor
|
|
7
7
|
from typing import TYPE_CHECKING, Any, Literal
|
|
8
8
|
|
|
9
|
-
from pydantic import ConfigDict
|
|
9
|
+
from pydantic import ConfigDict
|
|
10
10
|
|
|
11
11
|
from .dataframe import Mt5DataClient
|
|
12
12
|
from .mt5 import Mt5RuntimeError
|
|
@@ -27,15 +27,11 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
model_config = ConfigDict(frozen=True)
|
|
30
|
-
order_filling_mode: Literal["IOC", "FOK", "RETURN"] = Field(
|
|
31
|
-
default="IOC",
|
|
32
|
-
description="Order filling mode: 'IOC' (Immediate or Cancel), "
|
|
33
|
-
"'FOK' (Fill or Kill), 'RETURN' (Return if not filled)",
|
|
34
|
-
)
|
|
35
30
|
|
|
36
31
|
def close_open_positions(
|
|
37
32
|
self,
|
|
38
33
|
symbols: str | list[str] | tuple[str, ...] | None = None,
|
|
34
|
+
order_filling_mode: Literal["IOC", "FOK", "RETURN"] = "IOC",
|
|
39
35
|
dry_run: bool = False,
|
|
40
36
|
**kwargs: Any, # noqa: ANN401
|
|
41
37
|
) -> dict[str, list[dict[str, Any]]]:
|
|
@@ -44,6 +40,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
44
40
|
Args:
|
|
45
41
|
symbols: Optional symbol or list of symbols to filter positions.
|
|
46
42
|
If None, all symbols will be considered.
|
|
43
|
+
order_filling_mode: Order filling mode, either "IOC", "FOK", or "RETURN".
|
|
47
44
|
dry_run: If True, only check the order without sending it.
|
|
48
45
|
**kwargs: Additional keyword arguments for request parameters.
|
|
49
46
|
|
|
@@ -59,13 +56,19 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
59
56
|
symbol_list = self.symbols_get()
|
|
60
57
|
self.logger.info("Fetching and closing positions for symbols: %s", symbol_list)
|
|
61
58
|
return {
|
|
62
|
-
s: self._fetch_and_close_position(
|
|
59
|
+
s: self._fetch_and_close_position(
|
|
60
|
+
symbol=s,
|
|
61
|
+
order_filling_mode=order_filling_mode,
|
|
62
|
+
dry_run=dry_run,
|
|
63
|
+
**kwargs,
|
|
64
|
+
)
|
|
63
65
|
for s in symbol_list
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
def _fetch_and_close_position(
|
|
67
69
|
self,
|
|
68
70
|
symbol: str | None = None,
|
|
71
|
+
order_filling_mode: Literal["IOC", "FOK", "RETURN"] = "IOC",
|
|
69
72
|
dry_run: bool = False,
|
|
70
73
|
**kwargs: Any, # noqa: ANN401
|
|
71
74
|
) -> list[dict[str, Any]]:
|
|
@@ -73,6 +76,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
73
76
|
|
|
74
77
|
Args:
|
|
75
78
|
symbol: Optional symbol filter.
|
|
79
|
+
order_filling_mode: Order filling mode, either "IOC", "FOK", or "RETURN".
|
|
76
80
|
dry_run: If True, only check the order without sending it.
|
|
77
81
|
**kwargs: Additional keyword arguments for request parameters.
|
|
78
82
|
|
|
@@ -85,10 +89,6 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
85
89
|
return []
|
|
86
90
|
else:
|
|
87
91
|
self.logger.info("Closing open positions for symbol: %s", symbol)
|
|
88
|
-
order_filling_type = getattr(
|
|
89
|
-
self.mt5,
|
|
90
|
-
f"ORDER_FILLING_{self.order_filling_mode}",
|
|
91
|
-
)
|
|
92
92
|
return [
|
|
93
93
|
self._send_or_check_order(
|
|
94
94
|
request={
|
|
@@ -100,7 +100,10 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
100
100
|
if p["type"] == self.mt5.POSITION_TYPE_BUY
|
|
101
101
|
else self.mt5.ORDER_TYPE_BUY
|
|
102
102
|
),
|
|
103
|
-
"type_filling":
|
|
103
|
+
"type_filling": getattr(
|
|
104
|
+
self.mt5,
|
|
105
|
+
f"ORDER_FILLING_{order_filling_mode}",
|
|
106
|
+
),
|
|
104
107
|
"type_time": self.mt5.ORDER_TIME_GTC,
|
|
105
108
|
"position": p["ticket"],
|
|
106
109
|
**kwargs,
|
|
@@ -143,6 +146,7 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
143
146
|
elif retcode in {
|
|
144
147
|
self.mt5.TRADE_RETCODE_TRADE_DISABLED,
|
|
145
148
|
self.mt5.TRADE_RETCODE_MARKET_CLOSED,
|
|
149
|
+
self.mt5.TRADE_RETCODE_NO_CHANGES,
|
|
146
150
|
}:
|
|
147
151
|
self.logger.info("response: %s", response)
|
|
148
152
|
comment = response.get("comment", "Unknown error")
|
|
@@ -280,19 +284,25 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
280
284
|
"""
|
|
281
285
|
symbol_info = self.symbol_info_as_dict(symbol=symbol)
|
|
282
286
|
symbol_info_tick = self.symbol_info_tick_as_dict(symbol=symbol)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if order_side == "SELL"
|
|
292
|
-
else symbol_info_tick["ask"]
|
|
293
|
-
),
|
|
287
|
+
margin = self.order_calc_margin(
|
|
288
|
+
action=getattr(self.mt5, f"ORDER_TYPE_{order_side.upper()}"),
|
|
289
|
+
symbol=symbol,
|
|
290
|
+
volume=symbol_info["volume_min"],
|
|
291
|
+
price=(
|
|
292
|
+
symbol_info_tick["bid"]
|
|
293
|
+
if order_side == "SELL"
|
|
294
|
+
else symbol_info_tick["ask"]
|
|
294
295
|
),
|
|
295
|
-
|
|
296
|
+
)
|
|
297
|
+
if margin:
|
|
298
|
+
return {"volume": symbol_info["volume_min"], "margin": margin}
|
|
299
|
+
else:
|
|
300
|
+
self.logger.warning(
|
|
301
|
+
"No margin available for symbol: %s with order side: %s",
|
|
302
|
+
symbol,
|
|
303
|
+
order_side,
|
|
304
|
+
)
|
|
305
|
+
return {"volume": symbol_info["volume_min"], "margin": 0.0}
|
|
296
306
|
|
|
297
307
|
def calculate_volume_by_margin(
|
|
298
308
|
self,
|
|
@@ -314,10 +324,13 @@ class Mt5TradingClient(Mt5DataClient):
|
|
|
314
324
|
symbol=symbol,
|
|
315
325
|
order_side=order_side,
|
|
316
326
|
)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
327
|
+
if min_order_margin_dict["margin"]:
|
|
328
|
+
return (
|
|
329
|
+
floor(margin / min_order_margin_dict["margin"])
|
|
330
|
+
* min_order_margin_dict["volume"]
|
|
331
|
+
)
|
|
332
|
+
else:
|
|
333
|
+
return 0.0
|
|
321
334
|
|
|
322
335
|
def calculate_spread_ratio(
|
|
323
336
|
self,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdmt5
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
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>
|
|
@@ -191,9 +191,6 @@ Extends Mt5Client with pandas DataFrame and dictionary conversions:
|
|
|
191
191
|
|
|
192
192
|
Advanced trading operations client that extends Mt5DataClient:
|
|
193
193
|
|
|
194
|
-
- **Trading Configuration**:
|
|
195
|
-
- `order_filling_mode` - Order execution mode: "IOC" (default), "FOK", or "RETURN"
|
|
196
|
-
- `dry_run` - Test mode flag for simulating trades without execution
|
|
197
194
|
- **Position Management**:
|
|
198
195
|
- `close_open_positions()` - Close all positions for specified symbol(s)
|
|
199
196
|
- `place_market_order()` - Place market orders with configurable side, volume, and execution modes
|
|
@@ -213,7 +210,6 @@ Advanced trading operations client that extends Mt5DataClient:
|
|
|
213
210
|
- Comprehensive error handling with `Mt5TradingError`
|
|
214
211
|
- Support for batch operations on multiple symbols
|
|
215
212
|
- Automatic position closing with proper order type reversal
|
|
216
|
-
- Dry run mode for strategy testing without real trades
|
|
217
213
|
|
|
218
214
|
### Configuration
|
|
219
215
|
|
|
@@ -290,8 +286,8 @@ with Mt5DataClient(config=config) as client:
|
|
|
290
286
|
```python
|
|
291
287
|
from pdmt5 import Mt5TradingClient
|
|
292
288
|
|
|
293
|
-
# Create trading client
|
|
294
|
-
with Mt5TradingClient(config=config
|
|
289
|
+
# Create trading client
|
|
290
|
+
with Mt5TradingClient(config=config) as trader:
|
|
295
291
|
# Place a market buy order
|
|
296
292
|
order_result = trader.place_market_order(
|
|
297
293
|
symbol="EURUSD",
|
|
@@ -319,25 +315,16 @@ with Mt5TradingClient(config=config, order_filling_mode="IOC") as trader:
|
|
|
319
315
|
)
|
|
320
316
|
print(f"New position margin ratio: {margin_ratio:.2%}")
|
|
321
317
|
|
|
322
|
-
# Close all EURUSD positions
|
|
323
|
-
results = trader.close_open_positions(
|
|
318
|
+
# Close all EURUSD positions with specific order filling mode
|
|
319
|
+
results = trader.close_open_positions(
|
|
320
|
+
symbols="EURUSD",
|
|
321
|
+
order_filling_mode="FOK" # Fill or Kill
|
|
322
|
+
)
|
|
324
323
|
|
|
325
324
|
if results:
|
|
326
325
|
for symbol, close_results in results.items():
|
|
327
326
|
for result in close_results:
|
|
328
327
|
print(f"Closed position {result.get('position')} with result: {result['retcode']}")
|
|
329
|
-
|
|
330
|
-
# Using dry run mode for testing
|
|
331
|
-
trader_dry = Mt5TradingClient(config=config, dry_run=True)
|
|
332
|
-
with trader_dry:
|
|
333
|
-
# Test placing an order without actual execution
|
|
334
|
-
test_order = trader_dry.place_market_order(
|
|
335
|
-
symbol="GBPUSD",
|
|
336
|
-
volume=0.1,
|
|
337
|
-
order_side="SELL",
|
|
338
|
-
dry_run=True # Override instance setting
|
|
339
|
-
)
|
|
340
|
-
print(f"Test order validation: {test_order['retcode']}")
|
|
341
328
|
```
|
|
342
329
|
|
|
343
330
|
### Market Analysis with Mt5TradingClient
|
|
@@ -405,7 +392,7 @@ cd pdmt5
|
|
|
405
392
|
uv sync
|
|
406
393
|
|
|
407
394
|
# Run tests
|
|
408
|
-
uv run pytest
|
|
395
|
+
uv run pytest tests/ -v
|
|
409
396
|
|
|
410
397
|
# Run type checking
|
|
411
398
|
uv run pyright .
|
|
@@ -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=GWznfYp2ndUwJqGBunxIJ9xwiQ-9oOwuUV8UaMcny1w,20655
|
|
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.8.dist-info/METADATA,sha256=6wf-gKgkEt16UKITUBimrAsicdwtbN8z6mB0_Q0HgwE,16176
|
|
7
|
+
pdmt5-0.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
+
pdmt5-0.1.8.dist-info/licenses/LICENSE,sha256=iABrdaUGOBWLYotFupB_PGe8arV5o7rVhn-_vK6P704,1073
|
|
9
|
+
pdmt5-0.1.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|