pdmt5 0.1.8__tar.gz → 0.1.9__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 (32) hide show
  1. {pdmt5-0.1.8 → pdmt5-0.1.9}/PKG-INFO +5 -5
  2. {pdmt5-0.1.8 → pdmt5-0.1.9}/README.md +4 -4
  3. {pdmt5-0.1.8 → pdmt5-0.1.9}/pdmt5/trading.py +73 -13
  4. {pdmt5-0.1.8 → pdmt5-0.1.9}/pyproject.toml +1 -1
  5. {pdmt5-0.1.8 → pdmt5-0.1.9}/tests/test_trading.py +125 -9
  6. {pdmt5-0.1.8 → pdmt5-0.1.9}/uv.lock +1 -1
  7. {pdmt5-0.1.8 → pdmt5-0.1.9}/.claude/settings.json +0 -0
  8. {pdmt5-0.1.8 → pdmt5-0.1.9}/.github/FUNDING.yml +0 -0
  9. {pdmt5-0.1.8 → pdmt5-0.1.9}/.github/copilot-instructions.md +0 -0
  10. {pdmt5-0.1.8 → pdmt5-0.1.9}/.github/dependabot.yml +0 -0
  11. {pdmt5-0.1.8 → pdmt5-0.1.9}/.github/workflows/ci.yml +0 -0
  12. {pdmt5-0.1.8 → pdmt5-0.1.9}/.github/workflows/claude.yml +0 -0
  13. {pdmt5-0.1.8 → pdmt5-0.1.9}/.gitignore +0 -0
  14. {pdmt5-0.1.8 → pdmt5-0.1.9}/CLAUDE.md +0 -0
  15. {pdmt5-0.1.8 → pdmt5-0.1.9}/LICENSE +0 -0
  16. {pdmt5-0.1.8 → pdmt5-0.1.9}/docs/api/dataframe.md +0 -0
  17. {pdmt5-0.1.8 → pdmt5-0.1.9}/docs/api/index.md +0 -0
  18. {pdmt5-0.1.8 → pdmt5-0.1.9}/docs/api/mt5.md +0 -0
  19. {pdmt5-0.1.8 → pdmt5-0.1.9}/docs/api/trading.md +0 -0
  20. {pdmt5-0.1.8 → pdmt5-0.1.9}/docs/api/utils.md +0 -0
  21. {pdmt5-0.1.8 → pdmt5-0.1.9}/docs/index.md +0 -0
  22. {pdmt5-0.1.8 → pdmt5-0.1.9}/mkdocs.yml +0 -0
  23. {pdmt5-0.1.8 → pdmt5-0.1.9}/pdmt5/__init__.py +0 -0
  24. {pdmt5-0.1.8 → pdmt5-0.1.9}/pdmt5/dataframe.py +0 -0
  25. {pdmt5-0.1.8 → pdmt5-0.1.9}/pdmt5/mt5.py +0 -0
  26. {pdmt5-0.1.8 → pdmt5-0.1.9}/pdmt5/utils.py +0 -0
  27. {pdmt5-0.1.8 → pdmt5-0.1.9}/renovate.json +0 -0
  28. {pdmt5-0.1.8 → pdmt5-0.1.9}/tests/__init__.py +0 -0
  29. {pdmt5-0.1.8 → pdmt5-0.1.9}/tests/test_dataframe.py +0 -0
  30. {pdmt5-0.1.8 → pdmt5-0.1.9}/tests/test_init.py +0 -0
  31. {pdmt5-0.1.8 → pdmt5-0.1.9}/tests/test_mt5.py +0 -0
  32. {pdmt5-0.1.8 → pdmt5-0.1.9}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdmt5
3
- Version: 0.1.8
3
+ Version: 0.1.9
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>
@@ -53,14 +53,14 @@ Pandas-based data handler for MetaTrader 5
53
53
 
54
54
  ## Installation
55
55
 
56
- ### From GitHub
56
+ ### Using pip
57
57
 
58
58
  ```bash
59
- git clone https://github.com/dceoy/pdmt5.git
60
- pip install -U --no-cache-dir ./pdmt5
59
+ pip install -U pdmt5
60
+ pip install -U MetaTrader5
61
61
  ```
62
62
 
63
- ### Using uv (recommended for development)
63
+ ### Using uv
64
64
 
65
65
  ```bash
66
66
  git clone https://github.com/dceoy/pdmt5.git
@@ -30,14 +30,14 @@ Pandas-based data handler for MetaTrader 5
30
30
 
31
31
  ## Installation
32
32
 
33
- ### From GitHub
33
+ ### Using pip
34
34
 
35
35
  ```bash
36
- git clone https://github.com/dceoy/pdmt5.git
37
- pip install -U --no-cache-dir ./pdmt5
36
+ pip install -U pdmt5
37
+ pip install -U MetaTrader5
38
38
  ```
39
39
 
40
- ### Using uv (recommended for development)
40
+ ### Using uv
41
41
 
42
42
  ```bash
43
43
  git clone https://github.com/dceoy/pdmt5.git
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from datetime import timedelta
6
+ from functools import cached_property
6
7
  from math import floor
7
8
  from typing import TYPE_CHECKING, Any, Literal
8
9
 
@@ -28,6 +29,67 @@ class Mt5TradingClient(Mt5DataClient):
28
29
 
29
30
  model_config = ConfigDict(frozen=True)
30
31
 
32
+ @cached_property
33
+ def mt5_successful_trade_retcodes(self) -> set[int]:
34
+ """Set of successful trade return codes.
35
+
36
+ Returns:
37
+ Set of successful trade return codes.
38
+ """
39
+ return {
40
+ self.mt5.TRADE_RETCODE_PLACED, # 10008
41
+ self.mt5.TRADE_RETCODE_DONE, # 10009
42
+ self.mt5.TRADE_RETCODE_DONE_PARTIAL, # 10010
43
+ }
44
+
45
+ @cached_property
46
+ def mt5_failed_trade_retcodes(self) -> set[int]:
47
+ """Set of failed trade return codes.
48
+
49
+ Returns:
50
+ Set of failed trade return codes.
51
+ """
52
+ return {
53
+ self.mt5.TRADE_RETCODE_REQUOTE, # 10004
54
+ self.mt5.TRADE_RETCODE_REJECT, # 10006
55
+ self.mt5.TRADE_RETCODE_CANCEL, # 10007
56
+ self.mt5.TRADE_RETCODE_ERROR, # 10011
57
+ self.mt5.TRADE_RETCODE_TIMEOUT, # 10012
58
+ self.mt5.TRADE_RETCODE_INVALID, # 10013
59
+ self.mt5.TRADE_RETCODE_INVALID_VOLUME, # 10014
60
+ self.mt5.TRADE_RETCODE_INVALID_PRICE, # 10015
61
+ self.mt5.TRADE_RETCODE_INVALID_STOPS, # 10016
62
+ self.mt5.TRADE_RETCODE_TRADE_DISABLED, # 10017
63
+ self.mt5.TRADE_RETCODE_MARKET_CLOSED, # 10018
64
+ self.mt5.TRADE_RETCODE_NO_MONEY, # 10019
65
+ self.mt5.TRADE_RETCODE_PRICE_CHANGED, # 10020
66
+ self.mt5.TRADE_RETCODE_PRICE_OFF, # 10021
67
+ self.mt5.TRADE_RETCODE_INVALID_EXPIRATION, # 10022
68
+ self.mt5.TRADE_RETCODE_ORDER_CHANGED, # 10023
69
+ self.mt5.TRADE_RETCODE_TOO_MANY_REQUESTS, # 10024
70
+ self.mt5.TRADE_RETCODE_NO_CHANGES, # 10025
71
+ self.mt5.TRADE_RETCODE_SERVER_DISABLES_AT, # 10026
72
+ self.mt5.TRADE_RETCODE_CLIENT_DISABLES_AT, # 10027
73
+ self.mt5.TRADE_RETCODE_LOCKED, # 10028
74
+ self.mt5.TRADE_RETCODE_FROZEN, # 10029
75
+ self.mt5.TRADE_RETCODE_INVALID_FILL, # 10030
76
+ self.mt5.TRADE_RETCODE_CONNECTION, # 10031
77
+ self.mt5.TRADE_RETCODE_ONLY_REAL, # 10032
78
+ self.mt5.TRADE_RETCODE_LIMIT_ORDERS, # 10033
79
+ self.mt5.TRADE_RETCODE_LIMIT_VOLUME, # 10034
80
+ self.mt5.TRADE_RETCODE_INVALID_ORDER, # 10035
81
+ self.mt5.TRADE_RETCODE_POSITION_CLOSED, # 10036
82
+ self.mt5.TRADE_RETCODE_INVALID_CLOSE_VOLUME, # 10038
83
+ self.mt5.TRADE_RETCODE_CLOSE_ORDER_EXIST, # 10039
84
+ self.mt5.TRADE_RETCODE_LIMIT_POSITIONS, # 10040
85
+ self.mt5.TRADE_RETCODE_REJECT_CANCEL, # 10041
86
+ self.mt5.TRADE_RETCODE_LONG_ONLY, # 10042
87
+ self.mt5.TRADE_RETCODE_SHORT_ONLY, # 10043
88
+ self.mt5.TRADE_RETCODE_CLOSE_ONLY, # 10044
89
+ self.mt5.TRADE_RETCODE_FIFO_CLOSE, # 10045
90
+ self.mt5.TRADE_RETCODE_HEDGE_PROHIBITED, # 10046
91
+ }
92
+
31
93
  def close_open_positions(
32
94
  self,
33
95
  symbols: str | list[str] | tuple[str, ...] | None = None,
@@ -116,12 +178,14 @@ class Mt5TradingClient(Mt5DataClient):
116
178
  def _send_or_check_order(
117
179
  self,
118
180
  request: dict[str, Any],
181
+ raise_on_error: bool = False,
119
182
  dry_run: bool = False,
120
183
  ) -> dict[str, Any]:
121
184
  """Send or check an order request.
122
185
 
123
186
  Args:
124
187
  request: Order request dictionary.
188
+ raise_on_error: If True, raise an error on operation failure.
125
189
  dry_run: If True, only check the order without sending it.
126
190
 
127
191
  Returns:
@@ -138,25 +202,21 @@ class Mt5TradingClient(Mt5DataClient):
138
202
  response = self.order_send_as_dict(request=request)
139
203
  order_func = "order_send"
140
204
  retcode = response.get("retcode")
141
- if ((not dry_run) and retcode == self.mt5.TRADE_RETCODE_DONE) or (
142
- dry_run and retcode == 0
205
+ if (dry_run and retcode == 0) or (
206
+ not dry_run and retcode in self.mt5_successful_trade_retcodes
143
207
  ):
144
208
  self.logger.info("response: %s", response)
145
209
  return response
146
- elif retcode in {
147
- self.mt5.TRADE_RETCODE_TRADE_DISABLED,
148
- self.mt5.TRADE_RETCODE_MARKET_CLOSED,
149
- self.mt5.TRADE_RETCODE_NO_CHANGES,
150
- }:
151
- self.logger.info("response: %s", response)
152
- comment = response.get("comment", "Unknown error")
153
- self.logger.warning("%s() failed and skipped. <= `%s`", order_func, comment)
154
- return response
155
- else:
210
+ elif raise_on_error:
156
211
  self.logger.error("response: %s", response)
157
- comment = response.get("comment", "Unknown error")
212
+ comment = response.get("comment")
158
213
  error_message = f"{order_func}() failed and aborted. <= `{comment}`"
159
214
  raise Mt5TradingError(error_message)
215
+ else:
216
+ self.logger.warning("response: %s", response)
217
+ comment = response.get("comment")
218
+ self.logger.warning("%s() failed and skipped. <= `%s`", order_func, comment)
219
+ return response
160
220
 
161
221
  def place_market_order(
162
222
  self,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pdmt5"
3
- version = "0.1.8"
3
+ version = "0.1.9"
4
4
  description = "Pandas-based data handler for MetaTrader 5"
5
5
  authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
6
6
  maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
@@ -20,7 +20,7 @@ Mt5TradingClient.model_rebuild()
20
20
 
21
21
 
22
22
  @pytest.fixture(autouse=True)
23
- def mock_mt5_import(
23
+ def mock_mt5_import( # noqa: PLR0915
24
24
  request: pytest.FixtureRequest,
25
25
  mocker: MockerFixture,
26
26
  ) -> Generator[ModuleType | None, None, None]:
@@ -69,10 +69,50 @@ def mock_mt5_import(
69
69
  mock_mt5.ORDER_FILLING_FOK = 2
70
70
  mock_mt5.ORDER_FILLING_RETURN = 3
71
71
  mock_mt5.ORDER_TIME_GTC = 0
72
+
73
+ # Trade return codes
74
+ mock_mt5.TRADE_RETCODE_REQUOTE = 10004
75
+ mock_mt5.TRADE_RETCODE_REJECT = 10006
76
+ mock_mt5.TRADE_RETCODE_CANCEL = 10007
77
+ mock_mt5.TRADE_RETCODE_PLACED = 10008
72
78
  mock_mt5.TRADE_RETCODE_DONE = 10009
79
+ mock_mt5.TRADE_RETCODE_DONE_PARTIAL = 10010
80
+ mock_mt5.TRADE_RETCODE_ERROR = 10011
81
+ mock_mt5.TRADE_RETCODE_TIMEOUT = 10012
82
+ mock_mt5.TRADE_RETCODE_INVALID = 10013
83
+ mock_mt5.TRADE_RETCODE_INVALID_VOLUME = 10014
84
+ mock_mt5.TRADE_RETCODE_INVALID_PRICE = 10015
85
+ mock_mt5.TRADE_RETCODE_INVALID_STOPS = 10016
73
86
  mock_mt5.TRADE_RETCODE_TRADE_DISABLED = 10017
74
87
  mock_mt5.TRADE_RETCODE_MARKET_CLOSED = 10018
88
+ mock_mt5.TRADE_RETCODE_NO_MONEY = 10019
89
+ mock_mt5.TRADE_RETCODE_PRICE_CHANGED = 10020
90
+ mock_mt5.TRADE_RETCODE_PRICE_OFF = 10021
91
+ mock_mt5.TRADE_RETCODE_INVALID_EXPIRATION = 10022
92
+ mock_mt5.TRADE_RETCODE_ORDER_CHANGED = 10023
93
+ mock_mt5.TRADE_RETCODE_TOO_MANY_REQUESTS = 10024
75
94
  mock_mt5.TRADE_RETCODE_NO_CHANGES = 10025
95
+ mock_mt5.TRADE_RETCODE_SERVER_DISABLES_AT = 10026
96
+ mock_mt5.TRADE_RETCODE_CLIENT_DISABLES_AT = 10027
97
+ mock_mt5.TRADE_RETCODE_LOCKED = 10028
98
+ mock_mt5.TRADE_RETCODE_FROZEN = 10029
99
+ mock_mt5.TRADE_RETCODE_INVALID_FILL = 10030
100
+ mock_mt5.TRADE_RETCODE_CONNECTION = 10031
101
+ mock_mt5.TRADE_RETCODE_ONLY_REAL = 10032
102
+ mock_mt5.TRADE_RETCODE_LIMIT_ORDERS = 10033
103
+ mock_mt5.TRADE_RETCODE_LIMIT_VOLUME = 10034
104
+ mock_mt5.TRADE_RETCODE_INVALID_ORDER = 10035
105
+ mock_mt5.TRADE_RETCODE_POSITION_CLOSED = 10036
106
+ mock_mt5.TRADE_RETCODE_INVALID_CLOSE_VOLUME = 10038
107
+ mock_mt5.TRADE_RETCODE_CLOSE_ORDER_EXIST = 10039
108
+ mock_mt5.TRADE_RETCODE_LIMIT_POSITIONS = 10040
109
+ mock_mt5.TRADE_RETCODE_REJECT_CANCEL = 10041
110
+ mock_mt5.TRADE_RETCODE_LONG_ONLY = 10042
111
+ mock_mt5.TRADE_RETCODE_SHORT_ONLY = 10043
112
+ mock_mt5.TRADE_RETCODE_CLOSE_ONLY = 10044
113
+ mock_mt5.TRADE_RETCODE_FIFO_CLOSE = 10045
114
+ mock_mt5.TRADE_RETCODE_HEDGE_PROHIBITED = 10046
115
+
76
116
  mock_mt5.RES_S_OK = 1
77
117
  mock_mt5.DEAL_TYPE_BUY = 0
78
118
  mock_mt5.DEAL_TYPE_SELL = 1
@@ -612,15 +652,15 @@ class TestMt5TradingClient:
612
652
  "type": 1,
613
653
  }
614
654
 
615
- # Mock failure response
616
- mock_mt5_import.order_send.return_value.retcode = 10004
655
+ # Mock failure response with error retcode
656
+ mock_mt5_import.order_send.return_value.retcode = 10006
617
657
  mock_mt5_import.order_send.return_value._asdict.return_value = {
618
- "retcode": 10004,
658
+ "retcode": 10006,
619
659
  "comment": "Invalid request",
620
660
  }
621
661
 
622
662
  with pytest.raises(Mt5TradingError, match=r"order_send\(\) failed and aborted"):
623
- client._send_or_check_order(request)
663
+ client._send_or_check_order(request, raise_on_error=True)
624
664
 
625
665
  def test_send_or_check_order_dry_run_failure(
626
666
  self,
@@ -638,17 +678,17 @@ class TestMt5TradingClient:
638
678
  "type": 1,
639
679
  }
640
680
 
641
- # Mock failure response
642
- mock_mt5_import.order_check.return_value.retcode = 10004
681
+ # Mock failure response with non-zero retcode for dry run
682
+ mock_mt5_import.order_check.return_value.retcode = 10013
643
683
  mock_mt5_import.order_check.return_value._asdict.return_value = {
644
- "retcode": 10004,
684
+ "retcode": 10013,
645
685
  "comment": "Invalid request",
646
686
  }
647
687
 
648
688
  with pytest.raises(
649
689
  Mt5TradingError, match=r"order_check\(\) failed and aborted"
650
690
  ):
651
- client._send_or_check_order(request, dry_run=True)
691
+ client._send_or_check_order(request, raise_on_error=True, dry_run=True)
652
692
 
653
693
  def test_send_or_check_order_dry_run_override(
654
694
  self,
@@ -1896,3 +1936,79 @@ class TestMt5TradingClient:
1896
1936
  assert isinstance(result, list)
1897
1937
  assert len(result) == 2
1898
1938
  assert all(r["retcode"] == 10009 for r in result)
1939
+
1940
+ def test_mt5_successful_trade_retcodes_property(
1941
+ self, mock_mt5_import: ModuleType
1942
+ ) -> None:
1943
+ """Test mt5_successful_trade_retcodes property returns correct set of codes."""
1944
+ client = Mt5TradingClient(mt5=mock_mt5_import)
1945
+
1946
+ # Get the property value
1947
+ retcodes = client.mt5_successful_trade_retcodes
1948
+
1949
+ # Verify it's a set
1950
+ assert isinstance(retcodes, set)
1951
+
1952
+ # Verify the expected codes are present
1953
+ assert retcodes == {
1954
+ mock_mt5_import.TRADE_RETCODE_PLACED, # 10008
1955
+ mock_mt5_import.TRADE_RETCODE_DONE, # 10009
1956
+ mock_mt5_import.TRADE_RETCODE_DONE_PARTIAL, # 10010
1957
+ }
1958
+
1959
+ def test_mt5_failed_trade_retcodes_property(
1960
+ self, mock_mt5_import: ModuleType
1961
+ ) -> None:
1962
+ """Test mt5_failed_trade_retcodes property returns correct set of codes."""
1963
+ client = Mt5TradingClient(mt5=mock_mt5_import)
1964
+
1965
+ # Get the property value
1966
+ retcodes = client.mt5_failed_trade_retcodes
1967
+
1968
+ # Verify it's a set
1969
+ assert isinstance(retcodes, set)
1970
+
1971
+ # Verify it contains the expected codes
1972
+ expected_codes = {
1973
+ mock_mt5_import.TRADE_RETCODE_REQUOTE, # 10004
1974
+ mock_mt5_import.TRADE_RETCODE_REJECT, # 10006
1975
+ mock_mt5_import.TRADE_RETCODE_CANCEL, # 10007
1976
+ mock_mt5_import.TRADE_RETCODE_ERROR, # 10011
1977
+ mock_mt5_import.TRADE_RETCODE_TIMEOUT, # 10012
1978
+ mock_mt5_import.TRADE_RETCODE_INVALID, # 10013
1979
+ mock_mt5_import.TRADE_RETCODE_INVALID_VOLUME, # 10014
1980
+ mock_mt5_import.TRADE_RETCODE_INVALID_PRICE, # 10015
1981
+ mock_mt5_import.TRADE_RETCODE_INVALID_STOPS, # 10016
1982
+ mock_mt5_import.TRADE_RETCODE_TRADE_DISABLED, # 10017
1983
+ mock_mt5_import.TRADE_RETCODE_MARKET_CLOSED, # 10018
1984
+ mock_mt5_import.TRADE_RETCODE_NO_MONEY, # 10019
1985
+ mock_mt5_import.TRADE_RETCODE_PRICE_CHANGED, # 10020
1986
+ mock_mt5_import.TRADE_RETCODE_PRICE_OFF, # 10021
1987
+ mock_mt5_import.TRADE_RETCODE_INVALID_EXPIRATION, # 10022
1988
+ mock_mt5_import.TRADE_RETCODE_ORDER_CHANGED, # 10023
1989
+ mock_mt5_import.TRADE_RETCODE_TOO_MANY_REQUESTS, # 10024
1990
+ mock_mt5_import.TRADE_RETCODE_NO_CHANGES, # 10025
1991
+ mock_mt5_import.TRADE_RETCODE_SERVER_DISABLES_AT, # 10026
1992
+ mock_mt5_import.TRADE_RETCODE_CLIENT_DISABLES_AT, # 10027
1993
+ mock_mt5_import.TRADE_RETCODE_LOCKED, # 10028
1994
+ mock_mt5_import.TRADE_RETCODE_FROZEN, # 10029
1995
+ mock_mt5_import.TRADE_RETCODE_INVALID_FILL, # 10030
1996
+ mock_mt5_import.TRADE_RETCODE_CONNECTION, # 10031
1997
+ mock_mt5_import.TRADE_RETCODE_ONLY_REAL, # 10032
1998
+ mock_mt5_import.TRADE_RETCODE_LIMIT_ORDERS, # 10033
1999
+ mock_mt5_import.TRADE_RETCODE_LIMIT_VOLUME, # 10034
2000
+ mock_mt5_import.TRADE_RETCODE_INVALID_ORDER, # 10035
2001
+ mock_mt5_import.TRADE_RETCODE_POSITION_CLOSED, # 10036
2002
+ mock_mt5_import.TRADE_RETCODE_INVALID_CLOSE_VOLUME, # 10038
2003
+ mock_mt5_import.TRADE_RETCODE_CLOSE_ORDER_EXIST, # 10039
2004
+ mock_mt5_import.TRADE_RETCODE_LIMIT_POSITIONS, # 10040
2005
+ mock_mt5_import.TRADE_RETCODE_REJECT_CANCEL, # 10041
2006
+ mock_mt5_import.TRADE_RETCODE_LONG_ONLY, # 10042
2007
+ mock_mt5_import.TRADE_RETCODE_SHORT_ONLY, # 10043
2008
+ mock_mt5_import.TRADE_RETCODE_CLOSE_ONLY, # 10044
2009
+ mock_mt5_import.TRADE_RETCODE_FIFO_CLOSE, # 10045
2010
+ mock_mt5_import.TRADE_RETCODE_HEDGE_PROHIBITED, # 10046
2011
+ }
2012
+
2013
+ # Verify all expected codes are present
2014
+ assert retcodes == expected_codes
@@ -613,7 +613,7 @@ wheels = [
613
613
 
614
614
  [[package]]
615
615
  name = "pdmt5"
616
- version = "0.1.8"
616
+ version = "0.1.9"
617
617
  source = { editable = "." }
618
618
  dependencies = [
619
619
  { name = "metatrader5", marker = "sys_platform == 'win32'" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes