PythonMetaTrader5 2.3.2__py3-none-any.whl → 3.0.0__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.
Files changed (38) hide show
  1. PythonMetaTrader5/__init__.py +73 -535
  2. PythonMetaTrader5/adapters/__init__.py +1 -0
  3. PythonMetaTrader5/adapters/mt5_mapping.py +92 -0
  4. PythonMetaTrader5/adapters/mt5_session.py +48 -0
  5. PythonMetaTrader5/adapters/mt5_terminal.py +224 -0
  6. PythonMetaTrader5/adapters/system_clock.py +10 -0
  7. PythonMetaTrader5/application/__init__.py +1 -0
  8. PythonMetaTrader5/application/batch.py +60 -0
  9. PythonMetaTrader5/application/entries.py +163 -0
  10. PythonMetaTrader5/application/execution.py +108 -0
  11. PythonMetaTrader5/application/magic_scope.py +65 -0
  12. PythonMetaTrader5/application/ports.py +75 -0
  13. PythonMetaTrader5/application/trader.py +275 -0
  14. PythonMetaTrader5/connection.py +52 -0
  15. PythonMetaTrader5/domain/__init__.py +1 -0
  16. PythonMetaTrader5/domain/amounts.py +33 -0
  17. PythonMetaTrader5/domain/errors.py +53 -0
  18. PythonMetaTrader5/domain/inputs.py +106 -0
  19. PythonMetaTrader5/domain/market.py +91 -0
  20. PythonMetaTrader5/domain/orders.py +64 -0
  21. PythonMetaTrader5/domain/requests.py +32 -0
  22. PythonMetaTrader5/domain/retcodes.py +81 -0
  23. PythonMetaTrader5/domain/stops.py +85 -0
  24. PythonMetaTrader5/domain/symbol.py +158 -0
  25. PythonMetaTrader5/legacy/__init__.py +1 -0
  26. PythonMetaTrader5/legacy/arguments.py +52 -0
  27. PythonMetaTrader5/legacy/broker.py +284 -0
  28. PythonMetaTrader5/legacy/mt5_namespace.py +56 -0
  29. PythonMetaTrader5/legacy/views.py +123 -0
  30. PythonMetaTrader5/py.typed +0 -0
  31. PythonMetaTrader5/result.py +41 -0
  32. pythonmetatrader5-3.0.0.dist-info/METADATA +231 -0
  33. pythonmetatrader5-3.0.0.dist-info/RECORD +36 -0
  34. {pythonmetatrader5-2.3.2.dist-info → pythonmetatrader5-3.0.0.dist-info}/WHEEL +1 -1
  35. pythonmetatrader5-3.0.0.dist-info/licenses/LICENSE +16 -0
  36. pythonmetatrader5-2.3.2.dist-info/METADATA +0 -267
  37. pythonmetatrader5-2.3.2.dist-info/RECORD +0 -5
  38. {pythonmetatrader5-2.3.2.dist-info → pythonmetatrader5-3.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: PythonMetaTrader5
3
+ Version: 3.0.0
4
+ Summary: Safe, typed trading layer over the official MetaTrader5 Python API
5
+ Author-email: Wiktor Jelen <wiktorjn@gmail.com>
6
+ License-Expression: CC-BY-NC-4.0
7
+ Project-URL: Homepage, https://github.com/Akinzou/MetaTrader5-Python
8
+ Project-URL: Bug Tracker, https://github.com/Akinzou/MetaTrader5-Python/issues
9
+ Project-URL: Changelog, https://github.com/Akinzou/MetaTrader5-Python/blob/master/CHANGELOG.md
10
+ Keywords: metatrader5,mt5,trading,forex,algotrading
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Office/Business :: Financial :: Investment
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: MetaTrader5; sys_platform == "win32"
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8; extra == "dev"
29
+ Requires-Dist: pytest-cov>=5; extra == "dev"
30
+ Requires-Dist: ruff>=0.6; extra == "dev"
31
+ Requires-Dist: mypy>=1.10; extra == "dev"
32
+ Requires-Dist: build>=1.2; extra == "dev"
33
+ Requires-Dist: twine>=5; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # PythonMetaTrader5
37
+
38
+ [![CI](https://github.com/Akinzou/MetaTrader5-Python/actions/workflows/ci.yml/badge.svg)](https://github.com/Akinzou/MetaTrader5-Python/actions/workflows/ci.yml)
39
+ [![PyPI](https://img.shields.io/pypi/v/pythonmetatrader5)](https://pypi.org/project/PythonMetaTrader5/)
40
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue?logo=python&logoColor=white)
41
+ ![License](https://img.shields.io/badge/license-CC_BY--NC_4.0-lightgrey.svg)
42
+ [![PyPI Downloads](https://static.pepy.tech/badge/pythonmetatrader5)](https://pepy.tech/projects/pythonmetatrader5)
43
+ [![Discord](https://img.shields.io/badge/Discord-join-5865F2?logo=discord&logoColor=white)](https://discord.gg/BARYa55KS8)
44
+
45
+ A safe, typed trading layer over the official
46
+ [`MetaTrader5`](https://pypi.org/project/MetaTrader5/) Python package.
47
+
48
+ Built for bots (webhook executors, signal followers) where a trading call must
49
+ **never hang, never retry blindly, and never fail silently**:
50
+
51
+ - **Every trading call returns.** Requotes are retried a bounded number of times
52
+ with a fresh quote; everything else comes back immediately as a typed error.
53
+ (The deprecated `Broker.login(ToEffect=True)` is the one exception: as in
54
+ 2.x, it keeps trying until the login works.)
55
+ - **No silent defaults.** Values that MT5 would quietly read as something else
56
+ are refused before sending: `sl=0` ("no stop"), `ticket=0` (possibly "no
57
+ filter"), `volume=True` (one lot), NaN and infinities.
58
+ - **No double fills.** Timeouts and lost connections are never auto-retried:
59
+ the order may already be live.
60
+ - **Typed results.** `Ok(receipt)` or `Err(TradeError)` with an `ErrorKind`
61
+ (`MARKET_CLOSED`, `NO_MONEY`, `INVALID_STOPS`, ...) instead of magic numbers.
62
+ - **Correct prices and lots.** Prices snap to the symbol's tick grid, volumes
63
+ are checked against min/max/step, the filling mode is chosen from the symbol.
64
+ - **Magic numbers.** Tag your bot's trades and close/cancel only those.
65
+ - **SL/TP as prices or distances in points.**
66
+ - **2.x compatible.** The old `Broker` class still works (deprecated).
67
+
68
+ > One connection per process: the official package keeps a single global
69
+ > terminal session, so a second `connect()` (or its failed login) affects the
70
+ > first. Run one process per account.
71
+ >
72
+ > Thread-safe within the library: every terminal call holds one process-wide
73
+ > lock, so a threaded webhook server can share a `Trader`. The MT5 functions
74
+ > re-exported through `PythonMetaTrader5` hold it for each single call (a pair
75
+ > such as `order_send` + `last_error` is not atomic). Calls you make to the
76
+ > `MetaTrader5` module directly bypass it.
77
+ >
78
+ > Requires Windows with a MetaTrader 5 terminal installed (the official
79
+ > `MetaTrader5` package is Windows-only). Enable **Algo Trading** in the terminal.
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ pip install PythonMetaTrader5
85
+ ```
86
+
87
+ ## Quick start
88
+
89
+ ```python
90
+ from PythonMetaTrader5 import Err, Ok, Side, Stop, connect
91
+
92
+ match connect(12345678, "password", "MetaQuotes-Demo", magic=1001):
93
+ case Err(error):
94
+ raise SystemExit(f"cannot connect: {error}")
95
+ case Ok(trader):
96
+ pass
97
+
98
+ with trader: # shuts the terminal connection down on exit
99
+ result = trader.open_position(
100
+ "EURUSD", Side.BUY, 0.10,
101
+ sl=Stop.points(1000), # 1000 points below the entry price
102
+ tp=1.12500, # a plain float is an absolute price
103
+ comment="webhook #42",
104
+ )
105
+ match result:
106
+ case Ok(receipt):
107
+ print("filled at", receipt.price, "deal", receipt.deal)
108
+ case Err(error):
109
+ print("not opened:", error.kind, error.message)
110
+
111
+ trader.close_positions("EURUSD") # only this bot's (magic 1001) positions
112
+ ```
113
+
114
+ ## API
115
+
116
+ `connect(login, password, server, *, path=None, timeout_ms=60000, magic=0,
117
+ deviation=10, retry=None) -> Result[Trader, TradeError]`
118
+
119
+ `login` must be a positive integer or a string of ASCII digits; `True`,
120
+ `12345678.9` and similar values are rejected rather than coerced.
121
+
122
+ | `Trader` method | Returns |
123
+ |---|---|
124
+ | `open_position(symbol, side, volume, *, sl=None, tp=None, price=None, comment=None)` | `Result[TradeReceipt, TradeError]` |
125
+ | `place_limit_order(symbol, side, volume, price, *, sl=None, tp=None, comment=None)` | `Result[TradeReceipt, TradeError]` |
126
+ | `close_positions(symbol=None, *, ticket=None, magic=<own>, comment=None)` | `Result[list[TradeReceipt], BatchFailure]` |
127
+ | `cancel_pending_orders(symbol=None, *, magic=<own>, kind=None)` | `Result[list[TradeReceipt], BatchFailure]` |
128
+ | `positions(symbol=None, *, ticket=None, magic=None)` | `Result[list[Position], TradeError]` |
129
+ | `pending_orders(symbol=None, *, magic=None, kind=None)` | `Result[list[PendingOrder], TradeError]` |
130
+ | `shutdown()` | `None` |
131
+
132
+ - `side`: `Side.BUY` / `Side.SELL`, or the strings `"buy"`, `"sell"`, `"long"`, `"short"`.
133
+ - `sl` / `tp`: a float is an **absolute price**; `Stop.points(n)` is a distance
134
+ of `n` points from the entry price (`Stop.at_price(p)` is the explicit form).
135
+ For limit orders the distance is measured from the limit price, and for a
136
+ market order with an explicit `price` from that price. A float stop that is
137
+ zero, negative or not finite is rejected (`INVALID_STOPS`); pass `None` for
138
+ no stop. `Stop.points()` / `Stop.at_price()` raise `ValueError` unless the
139
+ value is a positive finite number (bool and str included; a zero stop would
140
+ mean "no stop" to MT5); the sign of a points distance is ignored. A `kind`
141
+ that is not a `StopKind` raises `TypeError`. A stop that resolves outside
142
+ the tick grid's range, or to zero or below, is `INVALID_STOPS` when the
143
+ order is built. For a value that may be bad at runtime, such as
144
+ an ATR that is NaN during warm-up, use
145
+ `Stop.parse(value, StopKind.POINTS)`, which returns `Ok(stop)` /
146
+ `Err(INVALID_STOPS)`.
147
+ - Amounts may be `int`, `float`, `Decimal` or numpy scalars; `bool` and strings
148
+ are refused (the deprecated `Broker` still converts numeric strings).
149
+ - `price` (market orders): send at this price once instead of the live quote.
150
+ - `comment`: at most 29 UTF-16 units: 29 characters, but characters outside
151
+ the BMP, such as most emoji, count as 2 (combined emoji more). The `MetaTrader5` package refuses 30 or more (measured on 5.0.6231; its
152
+ docs say 31). Longer comments, and comments that are not `str`, are rejected
153
+ before sending, not truncated.
154
+ - **`close_positions()` and `cancel_pending_orders()` act only on the trader's
155
+ own `magic`** unless you pass another one (ADR 0011); `magic=None` is still
156
+ "own". To act on the whole account, manual trades included, pass
157
+ `magic=ANY_MAGIC`. A `ticket` is closed whatever its magic. `positions()`
158
+ and `pending_orders()` still list every magic by default.
159
+ - Manual trades have magic 0, the `connect()` default, so set your own `magic`
160
+ for this to protect them. Until then a close/cancel that affects magic-0
161
+ items logs a warning; `magic=0` passed explicitly acknowledges it. On a
162
+ netting account a position keeps the magic of the deal that opened it, so
163
+ merged volume cannot be told apart by magic; a defaulted close/cancel that
164
+ matches nothing logs how many items with other magics it left open.
165
+ - A magic must be an integer from 0 to 2\*\*64-1 (MT5's ulong); `connect()`
166
+ returns `Err(INVALID_ARGUMENT)` otherwise, e.g. for `None`. An empty string
167
+ `""` as symbol is rejected too.
168
+ - `BatchFailure.errors` names every position/order that failed
169
+ (`TradeError.ticket`). `BatchFailure.succeeded` holds the receipts of the
170
+ ones that worked; their `order` / `deal` are the IDs of the closing deal, not
171
+ the position ticket.
172
+ - `ticket` must be a positive integer; `0`, `True` or `"7"` are rejected.
173
+
174
+ ### Errors
175
+
176
+ `TradeError(kind, message, retcode=None, receipt=None, ticket=None)`. The
177
+ `receipt` carries the broker's raw answer when there was one. Retry policy:
178
+
179
+ | Server answer | What happens |
180
+ |---|---|
181
+ | market deal (entry or close): done | `Ok(receipt)` |
182
+ | pending order: placed or done | `Ok(receipt)` |
183
+ | market deal: partial fill | `PARTIAL_FILL` with the receipt; part of the deal exists, never re-sent |
184
+ | market deal: placed (queued, not executed) | `OUTCOME_UNKNOWN` with the receipt, never re-sent |
185
+ | requote, price changed, no quotes, too many requests | retried with a fresh quote, up to `RetryPolicy.attempts` (default 3, 0.5 s apart), then `RETRIES_EXHAUSTED` |
186
+ | timeout, no connection, or `order_send` lost in IPC | `OUTCOME_UNKNOWN`, never retried: check `positions()` first |
187
+ | anything else | the matching `ErrorKind`, returned at once |
188
+ | refused before sending: the terminal's symbol data cannot price an order (point 0, NaN tick size, volume step 0, ...) | `BROKEN_SYMBOL_DATA`, the same on every resend: escalate, don't retry. Closing still works on such symbols |
189
+
190
+ ```python
191
+ from PythonMetaTrader5 import RetryPolicy, connect
192
+
193
+ connect(..., retry=RetryPolicy(attempts=5, delay_seconds=0.2))
194
+ ```
195
+
196
+ ### Logging
197
+
198
+ The library logs through the standard `logging` module under the
199
+ `PythonMetaTrader5` logger and never configures handlers itself. Without any
200
+ logging configuration, warnings and errors still reach stderr.
201
+
202
+ ## Migrating from 2.x
203
+
204
+ `from PythonMetaTrader5 import Broker` keeps working and emits a
205
+ `DeprecationWarning`. `from PythonMetaTrader5 import *` still brings in the
206
+ `MetaTrader5` functions and constants, as in 2.x. Several 2.x behaviours were unsafe and changed: see
207
+ [MIGRATION.md](MIGRATION.md).
208
+
209
+ ## Development
210
+
211
+ ```bash
212
+ python -m venv .venv && .venv\Scripts\activate
213
+ pip install -e ".[dev]"
214
+ ruff check src tests scripts && ruff format --check src tests scripts
215
+ mypy
216
+ pytest --cov
217
+ ```
218
+
219
+ `tests/contract` compares the unit-test fakes with the installed `MetaTrader5`
220
+ package and is skipped where it is not installed (anywhere but Windows).
221
+ Integration tests trade on a **demo** account and are skipped unless
222
+ `MT5_LOGIN`, `MT5_PASSWORD` and `MT5_SERVER` are set (they refuse to run on a
223
+ live account): `pytest -m integration`.
224
+
225
+ Architecture and decisions: [docs/architecture.md](docs/architecture.md),
226
+ [docs/adr](docs/adr).
227
+
228
+ ## License
229
+
230
+ [CC BY-NC 4.0](LICENSE). Trading carries risk; this software comes without any
231
+ warranty.
@@ -0,0 +1,36 @@
1
+ PythonMetaTrader5/__init__.py,sha256=bALjGPZAZJwaduG6zQ_2ifF1yYSlSUwug6iRujTnw58,2761
2
+ PythonMetaTrader5/connection.py,sha256=xA6VhhqNM-oXGh3ieWS8bCr9yAb4aEVtontgDsHwrQ4,1898
3
+ PythonMetaTrader5/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ PythonMetaTrader5/result.py,sha256=Q3RpcspZgb8nEf8Tnm_W4cx9IldyUDIN_DiCwZg5nEQ,956
5
+ PythonMetaTrader5/adapters/__init__.py,sha256=SfTHYRiDD8Qyi9zAtjh1WQJbT2XKL9C58zDM7P-RU0c,67
6
+ PythonMetaTrader5/adapters/mt5_mapping.py,sha256=YYQO_pFSbavKpeir2zGbZyYmMcp_tqssJa0la_VaB2Y,3089
7
+ PythonMetaTrader5/adapters/mt5_session.py,sha256=hoXlaKMdM1INqOE8Cc4bo3S8vvWNivXq_d1jgA0v9YE,1837
8
+ PythonMetaTrader5/adapters/mt5_terminal.py,sha256=OlpWUIR8wYAb7slKcCulni3JiwsQB8574FcOq6oQPpw,9476
9
+ PythonMetaTrader5/adapters/system_clock.py,sha256=h5dDN-xHNREghu0pCfbO5wM7BvawZQ9YDDAm36KpTkM,188
10
+ PythonMetaTrader5/application/__init__.py,sha256=sAbMrgqvIrwVA1TMdDfkVyh3dO9matXRVTiihb91ns4,64
11
+ PythonMetaTrader5/application/batch.py,sha256=krKHiBXU49aaBGmtC82O8L2oNKjUny5hqdC8YNBPR1o,1940
12
+ PythonMetaTrader5/application/entries.py,sha256=8uL4rI7h4iZjEh4nGVpi5UJQBL2Z7pdpXKN7cNKjWuQ,6025
13
+ PythonMetaTrader5/application/execution.py,sha256=uCJlMnvJKCteAvFw8mLBgndoYU_oooBn91VTpWS2P_Y,4208
14
+ PythonMetaTrader5/application/magic_scope.py,sha256=zxUyY_8z-96SCwr3lu-W8AdVqoG_Ug17TQHabnJdwOQ,2429
15
+ PythonMetaTrader5/application/ports.py,sha256=yeJrWLbdh1ncdYxn8c9cNGMdwppReTrigCPPc8OIKoA,2642
16
+ PythonMetaTrader5/application/trader.py,sha256=Ahw1WEleyYGs3V0b59pmBLdf878pHiSJ31r7dyN0PFM,10203
17
+ PythonMetaTrader5/domain/__init__.py,sha256=ZZ7iypJWkoG-nK2y_Dj0CG4IRZETi7IGJ90jY06cak8,57
18
+ PythonMetaTrader5/domain/amounts.py,sha256=ab_shtLiqn1I__9R7LtG_Vrz2hjrKwFqUZ1JE5F4tTo,1475
19
+ PythonMetaTrader5/domain/errors.py,sha256=Or7wDSlBO6Yo6miQiQOnXsInUXR67aMRhLhtZXKSVz8,2013
20
+ PythonMetaTrader5/domain/inputs.py,sha256=g_Z3s3e0H-VlKqNvvBTnXdavkVcKXt9kzE-PYryfcB0,4075
21
+ PythonMetaTrader5/domain/market.py,sha256=rBMc17-Uki3boAvPcDZNNPbYRtFxVFWGrGxoPZaBYrE,2051
22
+ PythonMetaTrader5/domain/orders.py,sha256=QA_AlOENgEZSD0ssP0WYJeJRdOC4zZ4z8-xtyRr0RTI,2399
23
+ PythonMetaTrader5/domain/requests.py,sha256=WngSUCpcSlQGBn8a7pMBV-TKVwNtFFqzRQNMuWZ-F9A,857
24
+ PythonMetaTrader5/domain/retcodes.py,sha256=Zstjn2Z-fbl4LZY7EacvSnYnxKJLxj5tzKoNF2YXDTI,3825
25
+ PythonMetaTrader5/domain/stops.py,sha256=G-eKU1z_LiKTugkfuPbMfoyUpCww1NhB7GqrNDKjTKU,3325
26
+ PythonMetaTrader5/domain/symbol.py,sha256=UnNJETCa7JXXqZrEWXVk07AxSBpOPsptV-aKtbPlJH8,7039
27
+ PythonMetaTrader5/legacy/__init__.py,sha256=g-HN0OW4CQ2xmYPdzjsIKoFJlZQquypd_wbairdEWiw,72
28
+ PythonMetaTrader5/legacy/arguments.py,sha256=bkklwl-9e6-JSY3bdPztwaElLXz8ro0qLieVkqozlbI,1802
29
+ PythonMetaTrader5/legacy/broker.py,sha256=qQC9gCsNQqjpH-AvX6PG6uVxU8Sm5bnziyDChoG_1sg,11168
30
+ PythonMetaTrader5/legacy/mt5_namespace.py,sha256=z-Woo4SnWu7ACk4j9zZ62h5K41cwhAae4ZAwtl4PKRQ,2411
31
+ PythonMetaTrader5/legacy/views.py,sha256=O_vDxru28cIn6j0kqNJerDcyVbRY4oLEnTtSAlA1H08,3260
32
+ pythonmetatrader5-3.0.0.dist-info/licenses/LICENSE,sha256=XDOg33_Bini4gyXwSu3XzYs5Gf3Han6tAojB-dZD5DQ,659
33
+ pythonmetatrader5-3.0.0.dist-info/METADATA,sha256=IUDIfEZPg2tx9fyBQLvdUL1hch2pMAxicnQ1M4U978Y,11658
34
+ pythonmetatrader5-3.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
35
+ pythonmetatrader5-3.0.0.dist-info/top_level.txt,sha256=r8xFh3BXAc7RVgR1j6wCYfyk_mm_DaDoGUYFk2IqBLE,18
36
+ pythonmetatrader5-3.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,16 @@
1
+ PythonMetaTrader5
2
+ Copyright (c) Wiktor Jelen
3
+
4
+ This work is licensed under the Creative Commons Attribution-NonCommercial 4.0
5
+ International License (CC BY-NC 4.0).
6
+
7
+ You are free to share and adapt this work for non-commercial purposes, provided
8
+ you give appropriate credit, link to the license, and indicate if changes were
9
+ made.
10
+
11
+ Full legal code: https://creativecommons.org/licenses/by-nc/4.0/legalcode
12
+ Summary: https://creativecommons.org/licenses/by-nc/4.0/
13
+
14
+ THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. TRADING FINANCIAL
15
+ INSTRUMENTS CARRIES RISK; THE AUTHORS ARE NOT LIABLE FOR ANY LOSSES INCURRED
16
+ THROUGH THE USE OF THIS SOFTWARE.
@@ -1,267 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: PythonMetaTrader5
3
- Version: 2.3.2
4
- Summary: Connect MT5 with Python
5
- Author-email: Wiktor Jelen <wiktorjn@gmail.com>
6
- License: CC BY-NC 4.0
7
- Project-URL: Homepage, https://github.com/Akinzou/MetaTrader5-Python
8
- Project-URL: Bug Tracker, https://github.com/Akinzou/MetaTrader5-Python/issues
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.6
13
- Description-Content-Type: text/markdown
14
- Requires-Dist: MetaTrader5
15
- Requires-Dist: loguru
16
- Requires-Dist: pyautogui
17
-
18
- # PythonMetaTrader5 [![PyPI Publish](https://github.com/Akinzou/MetaTrader5-Python/actions/workflows/python-publish.yml/badge.svg)](https://github.com/Akinzou/MetaTrader5-Python/actions/workflows/python-publish.yml) ![PyPI](https://img.shields.io/pypi/v/pythonmetatrader5) ![Python](https://img.shields.io/badge/python-3.6%2B-blue?logo=python&logoColor=white) ![License](https://img.shields.io/badge/license-CC_BY--NC_4.0-lightgrey.svg) [![PyPI Downloads](https://static.pepy.tech/badge/pythonmetatrader5)](https://pepy.tech/projects/pythonmetatrader5)
19
-
20
- [![Discord](https://img.shields.io/badge/Join_us_on-Discord-5865F2?logo=discord&logoColor=white&style=for-the-badge)](https://discord.gg/BARYa55KS8)
21
-
22
-
23
- # MetaTrader5 Python Broker Helper
24
-
25
- Minimal, production‑ready helper around the official **MetaTrader5** Python package,
26
- with a single market-entry function and clean handling of **SL/TP as absolute prices or offsets in POINTS**.
27
- Includes utilities to read positions, close them, and manage pending orders.
28
-
29
- > Works with: `MetaTrader5`, `loguru`, `pyautogui`
30
-
31
- ---
32
-
33
- ## Features
34
-
35
- - ✅ One function to open market positions: `OpenPosition(...)`
36
- - ✅ SL/TP can be passed **as absolute prices** or **offsets in POINTS**
37
- - ✅ Automatic price normalization to instrument `digits`
38
- - ✅ Consistent error handling (common MT5 retcodes mapped to messages)
39
- - ✅ Backward‑compat wrappers: `Buy`, `Sell`, `BuySL`, `SellSL`
40
- - ✅ Position helpers: `GetPositions`, `GetPosition`, `GetPositionsBySymbol`
41
- - ✅ Closing and pending orders helpers: `Close`, `SellLimit`, `BuyLimit`, `CancelOrders`
42
-
43
- ---
44
-
45
- ## Installation
46
-
47
- ```bash
48
- pip install MetaTrader5
49
- ```
50
-
51
- > Ensure your MetaTrader 5 terminal is running, logged in, and **Algo Trading** is enabled.
52
-
53
- ---
54
-
55
- ## Quick Start
56
-
57
- ```python
58
- from PythonMetaTrader5 import Broker
59
- import time
60
-
61
- # Login to your account
62
- broker = Broker(96540048, "PASSWORD", "MetaQuotes-Demo")
63
- time.sleep(1) # small grace period after login
64
-
65
- # Open BUY with SL/TP given as OFFSETS IN POINTS
66
- r = broker.OpenPosition(
67
- "EURUSD", "buy", 0.10,
68
- sl=1000, sl_type="offset", # 1000 points (≈ 100 pips on 5-digit symbol)
69
- tp=1500, tp_type="offset"
70
- )
71
-
72
- # Read positions for a symbol
73
- xau_positions = broker.GetPositionsBySymbol("XAUUSD", as_dict=True)
74
-
75
- # Close all positions on a symbol
76
- broker.Close("EURUSD")
77
-
78
- # Cancel pending limit orders on a symbol
79
- broker.CancelOrders("EURUSD")
80
-
81
- # Graceful shutdown
82
- broker.Disconnect()
83
- ```
84
-
85
- ---
86
-
87
- ## SL/TP Semantics (Important)
88
-
89
- - `sl_type` / `tp_type`:
90
- - `"absolute"` → you pass **absolute prices** (e.g., `1.23456`)
91
- - `"offset"` → you pass **offsets in POINTS** (converted using `symbol_info(symbol).point`)
92
-
93
- - Offsets are applied from the **current tick price** (`ask` for BUY, `bid` for SELL):
94
- - BUY : `SL = ask - |sl|*point`, `TP = ask + |tp|*point`
95
- - SELL: `SL = bid + |sl|*point`, `TP = bid - |tp|*point`
96
-
97
- - All calculated prices (including absolute inputs) are normalized to the instrument `digits`.
98
-
99
- > If you get `Wrong SL (10016)`, check `symbol_info(symbol).stops_level` and multiply by `point` to know the minimal allowed distance.
100
-
101
- ---
102
-
103
- ## API Reference
104
-
105
- ### `class Broker`
106
-
107
- #### `__init__(self, log, password, server)`
108
- Initialize MT5, attempt login, and print account info on success.
109
-
110
- - **log**: login number (int or str)
111
- - **password**: account password (str)
112
- - **server**: broker server name (str)
113
-
114
- #### `login(self, ToEffect=False) -> bool`
115
- Retrying login loop. If `ToEffect` is `False`, returns immediately on failure.
116
-
117
- ---
118
-
119
- ### Market Entry
120
-
121
- #### `OpenPosition(self, symbol, side, volume, *, sl=None, tp=None, price=None, comment=None, ticket=None, sl_type="absolute", tp_type="absolute")`
122
- Open a market position.
123
-
124
- - **symbol**: e.g., `"EURUSD"`
125
- - **side**: `"buy" | "sell" | "long" | "short"`
126
- - **volume**: lot size (float)
127
- - **sl**/**tp**: number (see `sl_type`/`tp_type`)
128
- - **sl_type**/**tp_type**: `"absolute"` (prices) or `"offset"` (**POINTS**)
129
- - **price**: optional direct price; if `None`, uses current tick (ask/bid) and retries on transient errors
130
- - **comment**: optional MT5 order comment
131
- - **ticket**: existing position id if needed (rarely used for market entry)
132
-
133
- **Returns**: result of `order_send` (MT5 `TradeResult`)
134
-
135
- **Examples**:
136
- ```python
137
- # Absolute prices
138
- broker.OpenPosition("XAUUSD", "sell", 0.10, sl=2400.0, tp=2380.0, sl_type="absolute", tp_type="absolute")
139
-
140
- # Offsets in POINTS
141
- broker.OpenPosition("EURUSD", "buy", 0.10, sl=1000, tp=1500, sl_type="offset", tp_type="offset")
142
- ```
143
-
144
- ---
145
-
146
- ### Backward‑Compat Wrappers
147
-
148
- #### `Buy(self, symbol, volume, price=None, *, comment=None, ticket=None)`
149
- Market BUY via `OpenPosition`.
150
-
151
- #### `Sell(self, symbol, volume, price=None, *, comment=None, ticket=None)`
152
- Market SELL via `OpenPosition`.
153
-
154
- #### `BuySL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None, sl_type="absolute")`
155
- BUY with SL specified (absolute or offset in points).
156
-
157
- #### `SellSL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None, sl_type="absolute")`
158
- SELL with SL specified (absolute or offset in points).
159
-
160
- ---
161
-
162
- ### Close & Pending Orders
163
-
164
- #### `Close(self, symbol, *, comment=None, ticket=None) -> bool`
165
- Iteratively closes all positions for a **ticket** or for a **symbol** (same filtering semantics as `positions_get`). Returns `False` if nothing to close.
166
-
167
- #### `SellLimit(self, symbol, volume, stoplimit, price=None, comment=None, ticket=None)`
168
- Create a SELL LIMIT order at `stoplimit`. If current `bid >= stoplimit`, executes a market SELL immediately for consistency.
169
-
170
- #### `BuyLimit(self, symbol, volume, stoplimit, price=None, comment=None, ticket=None)`
171
- Create a BUY LIMIT order at `stoplimit`. If current `ask <= stoplimit`, executes a market BUY immediately.
172
-
173
- #### `CancelOrders(self, symbol)`
174
- Cancel all BUY/SELL LIMIT pending orders on the given symbol.
175
-
176
- #### `Disconnect(self)`
177
- Shutdown MT5 connection.
178
-
179
- ---
180
-
181
- ### Positions Helpers
182
-
183
- #### `GetPositionsBySymbol(self, symbol: str, as_dict: bool = False) -> list`
184
- Return all open positions for a symbol. If `as_dict=True`, returns a list of plain dictionaries (ready for JSON/GUI).
185
-
186
- #### `GetPositions(self, symbol=None, *, ticket=None, as_dict: bool = False) -> list`
187
- Return positions with the same filter style as `Close()`:
188
- - `ticket` provided → that single position
189
- - else `symbol` provided → all for that symbol
190
- - else → all positions
191
-
192
- #### `GetPosition(self, symbol=None, *, ticket=None, as_dict: bool = False)`
193
- Return the first matching position (or `None`). Useful when at most one position is expected.
194
-
195
- **Example**:
196
- ```python
197
- p = broker.GetPosition(symbol="XAUUSD", as_dict=True)
198
- if p:
199
- print("Ticket:", p["ticket"], "Type:", "BUY" if p["type"] == 0 else "SELL", "Profit:", p["profit"])
200
- ```
201
-
202
- ---
203
-
204
- ## Error Handling & Retcodes
205
-
206
- - Known retcodes are mapped in `RetCodes`:
207
- - `10027`: Enable Algo Trading in MetaTrader5 app
208
- - `10018`: Market closed
209
- - `10016`: Wrong SL
210
-
211
- - For `10027`, a `Ctrl+E` hotkey is issued via `pyautogui` to toggle Algo Trading.
212
- - For `10016`, retries are **not** attempted (invalid SL/TP).
213
-
214
- ---
215
-
216
- ## Advanced Examples
217
-
218
- ```python
219
- # 1) BUY with SL absolute price, TP as offset in points
220
- broker.OpenPosition(
221
- "US500", "buy", 1.0,
222
- sl=5780.0, sl_type="absolute",
223
- tp=200, tp_type="offset"
224
- )
225
-
226
- # 2) SELL with both offsets in points
227
- broker.OpenPosition(
228
- "EURUSD", "sell", 0.2,
229
- sl=800, tp=1200,
230
- sl_type="offset", tp_type="offset"
231
- )
232
-
233
- # 3) Get all positions as dicts and filter by profit
234
- pos = broker.GetPositions(as_dict=True)
235
- winners = [p for p in pos if p["profit"] > 0]
236
-
237
- # 4) Close by ticket
238
- p = broker.GetPosition(symbol="XAUUSD")
239
- if p:
240
- broker.Close("XAUUSD", ticket=p.ticket)
241
-
242
- # 5) Pending orders
243
- broker.BuyLimit("EURUSD", 0.10, stoplimit=1.10000)
244
- broker.SellLimit("EURUSD", 0.10, stoplimit=1.12000)
245
- broker.CancelOrders("EURUSD")
246
- ```
247
-
248
- ---
249
-
250
- ## Notes
251
-
252
- - Ensure the symbol is visible/active in MT5 Market Watch.
253
- - Check `stops_level`/`freeze_level` on your broker if SL/TP are rejected:
254
- ```python
255
- info = symbol_info("EURUSD")
256
- print(info.point, info.digits, info.stops_level, info.freeze_level, info.volume_min, info.volume_step)
257
- ```
258
- - `volume` must respect broker `volume_min`/`volume_step`.
259
-
260
- ---
261
-
262
- ## License
263
-
264
- CC-BY-NC 4.0
265
-
266
-
267
-
@@ -1,5 +0,0 @@
1
- PythonMetaTrader5/__init__.py,sha256=FQYk4BheVnCjEDqIKFw_i5bR6tTOquydzNtxbKkO9F8,21318
2
- pythonmetatrader5-2.3.2.dist-info/METADATA,sha256=APqxO1ni0V5HA0gN37blvK0QPYxjnz0kcOFxvB669jc,8814
3
- pythonmetatrader5-2.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
4
- pythonmetatrader5-2.3.2.dist-info/top_level.txt,sha256=r8xFh3BXAc7RVgR1j6wCYfyk_mm_DaDoGUYFk2IqBLE,18
5
- pythonmetatrader5-2.3.2.dist-info/RECORD,,