pdmt5 0.1.3__tar.gz → 0.1.5__tar.gz

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.
Files changed (34) hide show
  1. {pdmt5-0.1.3 → pdmt5-0.1.5}/.claude/settings.json +2 -2
  2. pdmt5-0.1.5/PKG-INFO +462 -0
  3. pdmt5-0.1.5/README.md +439 -0
  4. pdmt5-0.1.5/pdmt5/trading.py +522 -0
  5. {pdmt5-0.1.3 → pdmt5-0.1.5}/pyproject.toml +2 -2
  6. {pdmt5-0.1.3 → pdmt5-0.1.5}/test/test_trading.py +754 -48
  7. {pdmt5-0.1.3 → pdmt5-0.1.5}/uv.lock +1 -1
  8. pdmt5-0.1.3/PKG-INFO +0 -306
  9. pdmt5-0.1.3/README.md +0 -283
  10. pdmt5-0.1.3/pdmt5/trading.py +0 -277
  11. {pdmt5-0.1.3 → pdmt5-0.1.5}/.github/FUNDING.yml +0 -0
  12. {pdmt5-0.1.3 → pdmt5-0.1.5}/.github/copilot-instructions.md +0 -0
  13. {pdmt5-0.1.3 → pdmt5-0.1.5}/.github/dependabot.yml +0 -0
  14. {pdmt5-0.1.3 → pdmt5-0.1.5}/.github/workflows/ci.yml +0 -0
  15. {pdmt5-0.1.3 → pdmt5-0.1.5}/.gitignore +0 -0
  16. {pdmt5-0.1.3 → pdmt5-0.1.5}/CLAUDE.md +0 -0
  17. {pdmt5-0.1.3 → pdmt5-0.1.5}/LICENSE +0 -0
  18. {pdmt5-0.1.3 → pdmt5-0.1.5}/docs/api/dataframe.md +0 -0
  19. {pdmt5-0.1.3 → pdmt5-0.1.5}/docs/api/index.md +0 -0
  20. {pdmt5-0.1.3 → pdmt5-0.1.5}/docs/api/mt5.md +0 -0
  21. {pdmt5-0.1.3 → pdmt5-0.1.5}/docs/api/trading.md +0 -0
  22. {pdmt5-0.1.3 → pdmt5-0.1.5}/docs/api/utils.md +0 -0
  23. {pdmt5-0.1.3 → pdmt5-0.1.5}/docs/index.md +0 -0
  24. {pdmt5-0.1.3 → pdmt5-0.1.5}/mkdocs.yml +0 -0
  25. {pdmt5-0.1.3 → pdmt5-0.1.5}/pdmt5/__init__.py +0 -0
  26. {pdmt5-0.1.3 → pdmt5-0.1.5}/pdmt5/dataframe.py +0 -0
  27. {pdmt5-0.1.3 → pdmt5-0.1.5}/pdmt5/mt5.py +0 -0
  28. {pdmt5-0.1.3 → pdmt5-0.1.5}/pdmt5/utils.py +0 -0
  29. {pdmt5-0.1.3 → pdmt5-0.1.5}/renovate.json +0 -0
  30. {pdmt5-0.1.3 → pdmt5-0.1.5}/test/__init__.py +0 -0
  31. {pdmt5-0.1.3 → pdmt5-0.1.5}/test/test_dataframe.py +0 -0
  32. {pdmt5-0.1.3 → pdmt5-0.1.5}/test/test_init.py +0 -0
  33. {pdmt5-0.1.3 → pdmt5-0.1.5}/test/test_mt5.py +0 -0
  34. {pdmt5-0.1.3 → pdmt5-0.1.5}/test/test_utils.py +0 -0
@@ -6,10 +6,10 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "uv run ruff check --fix . && uv run ruff format . && uv run pyright . && uv run pytest"
9
+ "command": "uv run ruff format . && uv run ruff check --fix . && uv run pyright . && uv run pytest"
10
10
  }
11
11
  ]
12
12
  }
13
13
  ]
14
14
  }
15
- }
15
+ }
pdmt5-0.1.5/PKG-INFO ADDED
@@ -0,0 +1,462 @@
1
+ Metadata-Version: 2.4
2
+ Name: pdmt5
3
+ Version: 0.1.5
4
+ Summary: Pandas-based data handler for MetaTrader 5
5
+ Project-URL: Repository, https://github.com/dceoy/pdmt5.git
6
+ Author-email: dceoy <dceoy@users.noreply.github.com>
7
+ Maintainer-email: dceoy <dceoy@users.noreply.github.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Financial and Insurance Industry
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Office/Business :: Financial :: Investment
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: metatrader5>=5.0.4424; sys_platform == 'win32'
20
+ Requires-Dist: pandas>=2.2.2
21
+ Requires-Dist: pydantic>=2.9.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # pdmt5
25
+
26
+ Pandas-based data handler for MetaTrader 5
27
+
28
+ [![CI/CD](https://github.com/dceoy/pdmt5/actions/workflows/ci.yml/badge.svg)](https://github.com/dceoy/pdmt5/actions/workflows/ci.yml)
29
+ [![Python Version](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
+ [![Platform](https://img.shields.io/badge/platform-Windows-blue.svg)](https://www.microsoft.com/windows)
32
+
33
+ ## Overview
34
+
35
+ **pdmt5** is a Python package that provides a pandas-based interface for MetaTrader 5 (MT5), making it easier to work with financial market data in Python. It automatically converts MT5's native data structures into pandas DataFrames, enabling seamless integration with data science workflows.
36
+
37
+ ### Key Features
38
+
39
+ - 📊 **Pandas Integration**: All data returned as pandas DataFrames for easy analysis
40
+ - 🔧 **Type Safety**: Full type hints with strict pyright checking and pydantic validation
41
+ - 🏦 **Comprehensive MT5 Coverage**: Account info, market data, tick data, orders, positions, and more
42
+ - 🚀 **Context Manager Support**: Clean initialization and cleanup with `with` statements
43
+ - 📈 **Time Series Ready**: OHLCV data with proper datetime indexing
44
+ - 🛡️ **Robust Error Handling**: Custom exceptions with detailed MT5 error information
45
+
46
+ ## Requirements
47
+
48
+ - **Operating System**: Windows (required by MetaTrader5 API)
49
+ - **Python**: 3.11 or higher
50
+ - **MetaTrader 5**: Terminal must be installed
51
+
52
+ ## Installation
53
+
54
+ ### From GitHub
55
+
56
+ ```bash
57
+ git clone https://github.com/dceoy/pdmt5.git
58
+ pip install -U --no-cache-dir ./pdmt5
59
+ ```
60
+
61
+ ### Using uv (recommended for development)
62
+
63
+ ```bash
64
+ git clone https://github.com/dceoy/pdmt5.git
65
+ cd pdmt5
66
+ uv sync
67
+ ```
68
+
69
+ ## Quick Start
70
+
71
+ ```python
72
+ import MetaTrader5 as mt5
73
+ from datetime import datetime
74
+ from pdmt5 import Mt5DataClient, Mt5Config
75
+
76
+ # Configure connection
77
+ config = Mt5Config(
78
+ login=12345678,
79
+ password="your_password",
80
+ server="YourBroker-Server",
81
+ timeout=60000
82
+ )
83
+
84
+ # Use as context manager
85
+ with Mt5DataClient(config=config) as client:
86
+ # Get account information as DataFrame
87
+ account_info = client.account_info_as_df()
88
+ print(account_info)
89
+
90
+ # Get OHLCV data as DataFrame
91
+ rates = client.copy_rates_from_as_df(
92
+ symbol="EURUSD",
93
+ timeframe=mt5.TIMEFRAME_H1,
94
+ date_from=datetime(2024, 1, 1),
95
+ count=100
96
+ )
97
+ print(rates.head())
98
+
99
+ # Get current positions as DataFrame
100
+ positions = client.positions_get_as_df()
101
+ print(positions)
102
+ ```
103
+
104
+ ## Core Components
105
+
106
+ ### Mt5Client
107
+
108
+ The base client wrapper for all MetaTrader5 operations with context manager support:
109
+
110
+ - **Connection Management**:
111
+ - `initialize()` - Establish connection with MT5 terminal (with optional path, login, password, server, timeout)
112
+ - `login()` - Connect to trading account with credentials
113
+ - `shutdown()` - Close MT5 terminal connection
114
+ - Context manager support (`with` statement) for automatic initialization/cleanup
115
+ - **Terminal Information**:
116
+ - `version()` - Get MT5 terminal version, build, and release date
117
+ - `last_error()` - Get last error code and description
118
+ - `account_info()` - Get current trading account information
119
+ - `terminal_info()` - Get terminal status and settings
120
+ - **Symbol Operations**:
121
+ - `symbols_total()` - Get total number of financial instruments
122
+ - `symbols_get()` - Get all symbols or filter by group
123
+ - `symbol_info()` - Get detailed data on specific symbol
124
+ - `symbol_info_tick()` - Get last tick for symbol
125
+ - `symbol_select()` - Show/hide symbol in MarketWatch
126
+ - **Market Depth**:
127
+ - `market_book_add()` - Subscribe to Market Depth events
128
+ - `market_book_get()` - Get current Market Depth data
129
+ - `market_book_release()` - Unsubscribe from Market Depth
130
+ - **Market Data**:
131
+ - `copy_rates_from()` - Get bars from specified date
132
+ - `copy_rates_from_pos()` - Get bars from specified position
133
+ - `copy_rates_range()` - Get bars for date range
134
+ - `copy_ticks_from()` - Get ticks from specified date
135
+ - `copy_ticks_range()` - Get ticks for date range
136
+ - **Order Operations**:
137
+ - `orders_total()` - Get number of active orders
138
+ - `orders_get()` - Get active orders with optional filters
139
+ - `order_calc_margin()` - Calculate required margin
140
+ - `order_calc_profit()` - Calculate potential profit
141
+ - `order_check()` - Check if order can be placed
142
+ - `order_send()` - Send order to trade server
143
+ - **Position Operations**:
144
+ - `positions_total()` - Get number of open positions
145
+ - `positions_get()` - Get open positions with optional filters
146
+ - **Trading History**:
147
+ - `history_orders_total()` - Get number of historical orders
148
+ - `history_orders_get()` - Get historical orders with filters
149
+ - `history_deals_total()` - Get number of historical deals
150
+ - `history_deals_get()` - Get historical deals with filters
151
+
152
+ ### Mt5DataClient
153
+
154
+ Extends Mt5Client with pandas DataFrame and dictionary conversions:
155
+
156
+ - **Enhanced Connection**:
157
+ - `initialize_and_login_mt5()` - Combined initialization and login with retry logic
158
+ - Configurable retry attempts via `retry_count` parameter
159
+ - **DataFrame/Dictionary Conversions**: All methods have both `_as_df` and `_as_dict` variants:
160
+ - `version_as_dict/df()` - MT5 version information
161
+ - `last_error_as_dict/df()` - Last error details
162
+ - `account_info_as_dict/df()` - Account information
163
+ - `terminal_info_as_dict/df()` - Terminal information
164
+ - `symbols_get_as_dicts/df()` - Symbol list with optional group filter
165
+ - `symbol_info_as_dict/df()` - Single symbol information
166
+ - `symbol_info_tick_as_dict/df()` - Last tick data
167
+ - `market_book_get_as_dicts/df()` - Market depth data
168
+ - **OHLCV Data Methods**:
169
+ - `copy_rates_from_as_dicts/df()` - Historical bars from date
170
+ - `copy_rates_from_pos_as_dicts/df()` - Historical bars from position
171
+ - `copy_rates_range_as_dicts/df()` - Historical bars for date range
172
+ - **Tick Data Methods**:
173
+ - `copy_ticks_from_as_dicts/df()` - Historical ticks from date
174
+ - `copy_ticks_range_as_dicts/df()` - Historical ticks for date range
175
+ - **Trading Data Methods**:
176
+ - `orders_get_as_dicts/df()` - Active orders with filters
177
+ - `order_check_as_dict/df()` - Order validation results
178
+ - `order_send_as_dict/df()` - Order execution results
179
+ - `positions_get_as_dicts/df()` - Open positions with filters
180
+ - `history_orders_get_as_dicts/df()` - Historical orders with date/ticket/position filters
181
+ - `history_deals_get_as_dicts/df()` - Historical deals with date/ticket/position filters
182
+ - **Features**:
183
+ - Automatic time conversion to datetime objects
184
+ - Optional DataFrame indexing with `index_keys` parameter
185
+ - Input validation for dates, counts, and positions
186
+ - Pydantic-based configuration via `Mt5Config`
187
+
188
+ ### Mt5TradingClient
189
+
190
+ Advanced trading operations client that extends Mt5DataClient:
191
+
192
+ - **Trading Configuration**:
193
+ - `order_filling_mode` - Order execution mode: "IOC" (default), "FOK", or "RETURN"
194
+ - `dry_run` - Test mode flag for simulating trades without execution
195
+ - **Position Management**:
196
+ - `close_open_positions()` - Close all positions for specified symbol(s)
197
+ - `place_market_order()` - Place market orders with configurable side, volume, and execution modes
198
+ - `update_open_position_sltp()` - Modify stop loss and take profit levels for open positions
199
+ - **Market Analysis**:
200
+ - `calculate_minimum_order_margins()` - Calculate minimum required margins for buy/sell orders
201
+ - `calculate_spread_ratio()` - Calculate normalized bid-ask spread ratio
202
+ - `calculate_new_position_margin_ratio()` - Calculate margin ratio for potential new positions
203
+ - **Simplified Data Access**:
204
+ - `fetch_latest_rates_as_df()` - Get recent OHLC data with timeframe strings (e.g., "M1", "H1", "D1")
205
+ - `fetch_latest_ticks_as_df()` - Get tick data for specified seconds around last tick
206
+ - `collect_entry_deals_as_df()` - Filter and collect entry deals (BUY/SELL) from history
207
+ - `fetch_positions_with_metrics_as_df()` - Get open positions with calculated metrics (elapsed time, margin, profit ratios)
208
+ - **Features**:
209
+ - Smart order routing with configurable filling modes
210
+ - Comprehensive error handling with `Mt5TradingError`
211
+ - Support for batch operations on multiple symbols
212
+ - Automatic position closing with proper order type reversal
213
+ - Dry run mode for strategy testing without real trades
214
+
215
+ ### Configuration
216
+
217
+ ```python
218
+ from pdmt5 import Mt5Config
219
+
220
+ config = Mt5Config(
221
+ login=12345678, # MT5 account number
222
+ password="password", # MT5 password
223
+ server="Broker-Server", # MT5 server name
224
+ timeout=60000 # Connection timeout in ms
225
+ )
226
+ ```
227
+
228
+ ## Examples
229
+
230
+ ### Getting Historical Data
231
+
232
+ ```python
233
+ import MetaTrader5 as mt5
234
+ from datetime import datetime
235
+
236
+ with Mt5DataClient(config=config) as client:
237
+ # Get last 1000 H1 bars for EURUSD as DataFrame
238
+ df = client.copy_rates_from_as_df(
239
+ symbol="EURUSD",
240
+ timeframe=mt5.TIMEFRAME_H1,
241
+ date_from=datetime.now(),
242
+ count=1000
243
+ )
244
+
245
+ # Data includes: time, open, high, low, close, tick_volume, spread, real_volume
246
+ print(df.columns)
247
+ print(df.describe())
248
+ ```
249
+
250
+ ### Working with Tick Data
251
+
252
+ ```python
253
+ from datetime import datetime, timedelta
254
+
255
+ with Mt5DataClient(config=config) as client:
256
+ # Get ticks for the last hour as DataFrame
257
+ ticks = client.copy_ticks_from_as_df(
258
+ symbol="EURUSD",
259
+ date_from=datetime.now() - timedelta(hours=1),
260
+ count=10000,
261
+ flags=mt5.COPY_TICKS_ALL
262
+ )
263
+
264
+ # Tick data includes: time, bid, ask, last, volume, flags
265
+ print(ticks.head())
266
+ ```
267
+
268
+ ### Analyzing Positions
269
+
270
+ ```python
271
+ with Mt5DataClient(config=config) as client:
272
+ # Get all open positions as DataFrame
273
+ positions = client.positions_get_as_df()
274
+
275
+ if not positions.empty:
276
+ # Calculate summary statistics
277
+ summary = positions.groupby('symbol').agg({
278
+ 'volume': 'sum',
279
+ 'profit': 'sum',
280
+ 'price_open': 'mean'
281
+ })
282
+ print(summary)
283
+ ```
284
+
285
+ ### Trading Operations
286
+
287
+ ```python
288
+ from pdmt5 import Mt5TradingClient
289
+
290
+ # Create trading client with specific order filling mode
291
+ with Mt5TradingClient(config=config, order_filling_mode="IOC") as trader:
292
+ # Place a market buy order
293
+ order_result = trader.place_market_order(
294
+ symbol="EURUSD",
295
+ volume=0.1,
296
+ order_side="BUY",
297
+ order_filling_mode="IOC", # Immediate or Cancel
298
+ order_time_mode="GTC" # Good Till Cancelled
299
+ )
300
+ print(f"Order placed: {order_result['retcode']}")
301
+
302
+ # Update stop loss and take profit for an open position
303
+ if positions := trader.positions_get_as_df(symbol="EURUSD"):
304
+ position_ticket = positions.iloc[0]['ticket']
305
+ update_result = trader.update_open_position_sltp(
306
+ symbol="EURUSD",
307
+ position_ticket=position_ticket,
308
+ sl=1.0950, # New stop loss
309
+ tp=1.1050 # New take profit
310
+ )
311
+ print(f"Position updated: {update_result['retcode']}")
312
+
313
+ # Calculate margin ratio for a new position
314
+ margin_ratio = trader.calculate_new_position_margin_ratio(
315
+ symbol="EURUSD",
316
+ new_side="SELL",
317
+ new_volume=0.2
318
+ )
319
+ print(f"New position margin ratio: {margin_ratio:.2%}")
320
+
321
+ # Close all EURUSD positions
322
+ results = trader.close_open_positions(symbols="EURUSD")
323
+
324
+ if results:
325
+ for symbol, close_results in results.items():
326
+ for result in close_results:
327
+ print(f"Closed position {result.get('position')} with result: {result['retcode']}")
328
+
329
+ # Using dry run mode for testing
330
+ trader_dry = Mt5TradingClient(config=config, dry_run=True)
331
+ with trader_dry:
332
+ # Test placing an order without actual execution
333
+ test_order = trader_dry.place_market_order(
334
+ symbol="GBPUSD",
335
+ volume=0.1,
336
+ order_side="SELL",
337
+ dry_run=True # Override instance setting
338
+ )
339
+ print(f"Test order validation: {test_order['retcode']}")
340
+ ```
341
+
342
+ ### Market Analysis with Mt5TradingClient
343
+
344
+ ```python
345
+ with Mt5TradingClient(config=config) as trader:
346
+ # Calculate spread ratio for EURUSD
347
+ spread_ratio = trader.calculate_spread_ratio("EURUSD")
348
+ print(f"EURUSD spread ratio: {spread_ratio:.5f}")
349
+
350
+ # Get minimum order margins
351
+ margins = trader.calculate_minimum_order_margins("EURUSD")
352
+ print(f"Minimum ask margin: {margins['ask']}")
353
+ print(f"Minimum bid margin: {margins['bid']}")
354
+
355
+ # Get recent OHLC data with custom timeframe
356
+ rates_df = trader.fetch_latest_rates_as_df(
357
+ symbol="EURUSD",
358
+ granularity="M15", # 15-minute bars
359
+ count=100
360
+ )
361
+ print(rates_df.tail())
362
+
363
+ # Get tick data for the last 60 seconds
364
+ ticks_df = trader.fetch_latest_ticks_as_df(
365
+ symbol="EURUSD",
366
+ seconds=60
367
+ )
368
+ print(f"Received {len(ticks_df)} ticks")
369
+
370
+ # Collect entry deals for the last hour
371
+ deals_df = trader.collect_entry_deals_as_df(
372
+ symbol="EURUSD",
373
+ history_seconds=3600
374
+ )
375
+ if not deals_df.empty:
376
+ print(f"Found {len(deals_df)} entry deals")
377
+ print(deals_df[['time', 'type', 'volume', 'price']].head())
378
+
379
+ # Get positions with calculated metrics
380
+ positions_df = trader.fetch_positions_with_metrics_as_df("EURUSD")
381
+ if not positions_df.empty:
382
+ print(f"Open positions with metrics:")
383
+ print(positions_df[['ticket', 'volume', 'profit', 'elapsed_seconds', 'underlier_profit_ratio']].head())
384
+ ```
385
+
386
+ ## Development
387
+
388
+ ### Setup Development Environment
389
+
390
+ ```bash
391
+ # Clone repository
392
+ git clone https://github.com/dceoy/pdmt5.git
393
+ cd pdmt5
394
+
395
+ # Install with uv
396
+ uv sync
397
+
398
+ # Run tests
399
+ uv run pytest test/ -v
400
+
401
+ # Run type checking
402
+ uv run pyright .
403
+
404
+ # Run linting
405
+ uv run ruff check --fix .
406
+ uv run ruff format .
407
+ ```
408
+
409
+ ### Code Quality
410
+
411
+ This project maintains high code quality standards:
412
+
413
+ - **Type Checking**: Strict mode with pyright
414
+ - **Linting**: Comprehensive ruff configuration with 40+ rule categories
415
+ - **Testing**: pytest with coverage tracking (minimum 90%)
416
+ - **Documentation**: Google-style docstrings
417
+
418
+ ## Error Handling
419
+
420
+ The package provides detailed error information:
421
+
422
+ ```python
423
+ from pdmt5 import Mt5RuntimeError
424
+
425
+ try:
426
+ with Mt5DataClient(config=config) as client:
427
+ data = client.copy_rates_from("INVALID", mt5.TIMEFRAME_H1, datetime.now(), 100)
428
+ except Mt5RuntimeError as e:
429
+ print(f"MT5 Error: {e}")
430
+ print(f"Error code: {e.error_code}")
431
+ print(f"Description: {e.description}")
432
+ ```
433
+
434
+ ## Limitations
435
+
436
+ - **Windows Only**: Due to MetaTrader5 API requirements
437
+ - **MT5 Terminal Required**: The MetaTrader 5 terminal must be installed
438
+ - **Single Thread**: MT5 API is not thread-safe
439
+
440
+ ## Contributing
441
+
442
+ Contributions are welcome! Please:
443
+
444
+ 1. Fork the repository
445
+ 2. Create a feature branch
446
+ 3. Ensure tests pass and coverage is maintained
447
+ 4. Submit a pull request
448
+
449
+ See [CLAUDE.md](CLAUDE.md) for development guidelines.
450
+
451
+ ## License
452
+
453
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
454
+
455
+ ## Author
456
+
457
+ Daichi Narushima, Ph.D.
458
+
459
+ ## Acknowledgments
460
+
461
+ - MetaTrader 5 for providing the Python API
462
+ - The pandas community for the excellent data manipulation tools