bbstrader 0.1.8__py3-none-any.whl → 0.1.91__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.
Potentially problematic release.
This version of bbstrader might be problematic. Click here for more details.
- bbstrader/__ini__.py +4 -2
- bbstrader/btengine/__init__.py +5 -5
- bbstrader/btengine/backtest.py +51 -10
- bbstrader/btengine/data.py +147 -55
- bbstrader/btengine/event.py +4 -1
- bbstrader/btengine/execution.py +125 -23
- bbstrader/btengine/performance.py +4 -7
- bbstrader/btengine/portfolio.py +34 -13
- bbstrader/btengine/strategy.py +466 -6
- bbstrader/config.py +111 -0
- bbstrader/metatrader/__init__.py +4 -4
- bbstrader/metatrader/account.py +357 -55
- bbstrader/metatrader/rates.py +234 -31
- bbstrader/metatrader/risk.py +35 -24
- bbstrader/metatrader/trade.py +361 -173
- bbstrader/metatrader/utils.py +2 -53
- bbstrader/models/factors.py +0 -0
- bbstrader/models/ml.py +0 -0
- bbstrader/models/optimization.py +0 -0
- bbstrader/trading/__init__.py +1 -1
- bbstrader/trading/execution.py +329 -215
- bbstrader/trading/scripts.py +57 -0
- bbstrader/trading/strategies.py +49 -71
- bbstrader/tseries.py +274 -39
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/METADATA +11 -3
- bbstrader-0.1.91.dist-info/RECORD +31 -0
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/WHEEL +1 -1
- bbstrader-0.1.8.dist-info/RECORD +0 -26
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/LICENSE +0 -0
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/top_level.txt +0 -0
bbstrader/metatrader/account.py
CHANGED
|
@@ -4,18 +4,50 @@ import pandas as pd
|
|
|
4
4
|
import urllib.request
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
import MetaTrader5 as mt5
|
|
7
|
-
from
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
from currency_converter import (
|
|
9
|
+
SINGLE_DAY_ECB_URL,
|
|
10
|
+
CurrencyConverter
|
|
11
|
+
)
|
|
8
12
|
from bbstrader.metatrader.utils import (
|
|
9
|
-
raise_mt5_error,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
raise_mt5_error,
|
|
14
|
+
AccountInfo,
|
|
15
|
+
TerminalInfo,
|
|
16
|
+
InvalidBroker,
|
|
17
|
+
SymbolInfo,
|
|
18
|
+
TickInfo,
|
|
19
|
+
TradeRequest,
|
|
20
|
+
OrderCheckResult,
|
|
21
|
+
OrderSentResult,
|
|
22
|
+
TradePosition,
|
|
23
|
+
TradeOrder,
|
|
24
|
+
TradeDeal,
|
|
25
|
+
)
|
|
26
|
+
from typing import (
|
|
27
|
+
Tuple,
|
|
28
|
+
Union,
|
|
29
|
+
List,
|
|
30
|
+
Dict,
|
|
31
|
+
Any,
|
|
32
|
+
Optional,
|
|
33
|
+
Literal,
|
|
34
|
+
overload
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
load_dotenv()
|
|
13
38
|
|
|
39
|
+
__all__ = [
|
|
40
|
+
"Account",
|
|
41
|
+
"Broker",
|
|
42
|
+
"AdmiralMarktsGroup",
|
|
43
|
+
"JustGlobalMarkets",
|
|
44
|
+
"FTMO",
|
|
45
|
+
]
|
|
14
46
|
|
|
15
47
|
__BROKERS__ = {
|
|
16
|
-
'AMG':
|
|
17
|
-
'JGM':
|
|
18
|
-
'FTMO':
|
|
48
|
+
'AMG': 'Admirals Group AS',
|
|
49
|
+
'JGM': 'Just Global Markets Ltd.',
|
|
50
|
+
'FTMO': 'FTMO S.R.O.'
|
|
19
51
|
}
|
|
20
52
|
|
|
21
53
|
BROKERS_TIMEZONES = {
|
|
@@ -24,12 +56,12 @@ BROKERS_TIMEZONES = {
|
|
|
24
56
|
'FTMO': 'Europe/Helsinki'
|
|
25
57
|
}
|
|
26
58
|
|
|
27
|
-
_ADMIRAL_MARKETS_URL_ =
|
|
59
|
+
_ADMIRAL_MARKETS_URL_ = os.getenv("ADMIRAL_MARKETS_URL")
|
|
28
60
|
_ADMIRAL_MARKETS_PRODUCTS_ = ["Stocks", "ETFs",
|
|
29
61
|
"Indices", "Commodities", "Futures", "Forex"]
|
|
30
|
-
_JUST_MARKETS_URL_ =
|
|
62
|
+
_JUST_MARKETS_URL_ = os.getenv("JUST_MARKETS_URL")
|
|
31
63
|
_JUST_MARKETS_PRODUCTS_ = ["Stocks", "Crypto", "indices", "Commodities", "Forex"]
|
|
32
|
-
_FTMO_URL_ =
|
|
64
|
+
_FTMO_URL_ = os.getenv("FTMO_URL")
|
|
33
65
|
|
|
34
66
|
INIT_MSG = (
|
|
35
67
|
f"\n* Ensure you have a good and stable internet connexion\n"
|
|
@@ -54,6 +86,114 @@ _SYMBOLS_TYPE_ = {
|
|
|
54
86
|
"CRYPTO": r'\b(Cryptos?)\b'
|
|
55
87
|
}
|
|
56
88
|
|
|
89
|
+
_COUNTRY_MAP_ = {
|
|
90
|
+
"USA": r"\b(US)\b",
|
|
91
|
+
"AUS": r"\b(Australia)\b",
|
|
92
|
+
"BEL": r"\b(Belgium)\b",
|
|
93
|
+
"DNK": r"\b(Denmark)\b",
|
|
94
|
+
"FIN": r"\b(Finland)\b",
|
|
95
|
+
"FRA": r"\b(France)\b",
|
|
96
|
+
"DEU": r"\b(Germany)\b",
|
|
97
|
+
"NLD": r"\b(Netherlands)\b",
|
|
98
|
+
"NOR": r"\b(Norway)\b",
|
|
99
|
+
"PRT": r"\b(Portugal)\b",
|
|
100
|
+
"ESP": r"\b(Spain)\b",
|
|
101
|
+
"SWE": r"\b(Sweden)\b",
|
|
102
|
+
"GBR": r"\b(UK)\b",
|
|
103
|
+
"CHE": r"\b(Switzerland)\b",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
AMG_EXCHANGES = {
|
|
107
|
+
'XASX': r"Australia.*\(ASX\)",
|
|
108
|
+
'XBRU': r"Belgium.*\(Euronext\)",
|
|
109
|
+
'XCSE': r"Denmark.*\(CSE\)",
|
|
110
|
+
'XHEL': r"Finland.*\(NASDAQ\)",
|
|
111
|
+
'XPAR': r"France.*\(Euronext\)",
|
|
112
|
+
'XETR': r"Germany.*\(Xetra\)",
|
|
113
|
+
'XAMS': r"Netherlands.*\(Euronext\)",
|
|
114
|
+
'XOSL': r"Norway.*\(NASDAQ\)",
|
|
115
|
+
'XLIS': r"Portugal.*\(Euronext\)",
|
|
116
|
+
'XMAD': r"Spain.*\(BME\)",
|
|
117
|
+
'XSTO': r"Sweden.*\(NASDAQ\)",
|
|
118
|
+
'XLON': r"UK.*\(LSE\)",
|
|
119
|
+
'XNYS': r"US.*\((NYSE|ARCA|AMEX)\)",
|
|
120
|
+
'NYSE': r"US.*\(NYSE\)",
|
|
121
|
+
'ARCA': r"US.*\(ARCA\)",
|
|
122
|
+
'AMEX': r"US.*\(AMEX\)",
|
|
123
|
+
'NASDAQ': r"US.*\(NASDAQ\)",
|
|
124
|
+
'BATS': r"US.*\(BATS\)",
|
|
125
|
+
'XSWX': r"Switzerland.*\(SWX\)"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
def check_mt5_connection():
|
|
129
|
+
try:
|
|
130
|
+
init = mt5.initialize()
|
|
131
|
+
if not init:
|
|
132
|
+
raise_mt5_error(INIT_MSG)
|
|
133
|
+
except Exception:
|
|
134
|
+
raise_mt5_error(INIT_MSG)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class Broker(object):
|
|
138
|
+
def __init__(self, name: str=None):
|
|
139
|
+
if name is None:
|
|
140
|
+
check_mt5_connection()
|
|
141
|
+
self._name = mt5.account_info().company
|
|
142
|
+
else:
|
|
143
|
+
self._name = name
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def name(self):
|
|
147
|
+
return self._name
|
|
148
|
+
|
|
149
|
+
def __str__(self):
|
|
150
|
+
return self.name
|
|
151
|
+
|
|
152
|
+
def __eq__(self, orther) -> bool:
|
|
153
|
+
return self.name == orther.name
|
|
154
|
+
|
|
155
|
+
def __ne__(self, orther) -> bool:
|
|
156
|
+
return self.name != orther.name
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class AdmiralMarktsGroup(Broker):
|
|
160
|
+
def __init__(self):
|
|
161
|
+
super().__init__("Admirals Group AS")
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def timezone(self) -> str:
|
|
165
|
+
return BROKERS_TIMEZONES['AMG']
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class JustGlobalMarkets(Broker):
|
|
169
|
+
def __init__(self):
|
|
170
|
+
super().__init__("Just Global Markets Ltd.")
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def timezone(self) -> str:
|
|
174
|
+
return BROKERS_TIMEZONES['JGM']
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class FTMO(Broker):
|
|
178
|
+
def __init__(self):
|
|
179
|
+
super().__init__("FTMO S.R.O.")
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def timezone(self) -> str:
|
|
183
|
+
return BROKERS_TIMEZONES['FTMO']
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class AMP(Broker):
|
|
187
|
+
...
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
BROKERS: Dict[str, Broker] = {
|
|
191
|
+
'AMG': AdmiralMarktsGroup(),
|
|
192
|
+
'JGM': JustGlobalMarkets(),
|
|
193
|
+
'FTMO': FTMO()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
57
197
|
class Account(object):
|
|
58
198
|
"""
|
|
59
199
|
The `Account` class is utilized to retrieve information about
|
|
@@ -91,23 +231,72 @@ class Account(object):
|
|
|
91
231
|
"""
|
|
92
232
|
|
|
93
233
|
def __init__(self):
|
|
94
|
-
|
|
95
|
-
raise_mt5_error(message=INIT_MSG)
|
|
234
|
+
check_mt5_connection()
|
|
96
235
|
self._check_brokers()
|
|
97
236
|
|
|
98
237
|
def _check_brokers(self):
|
|
99
|
-
supported =
|
|
100
|
-
broker
|
|
101
|
-
if broker not in supported.values():
|
|
238
|
+
supported = BROKERS.copy()
|
|
239
|
+
if self.broker not in supported.values():
|
|
102
240
|
msg = (
|
|
103
|
-
f"{broker} is not currently supported broker for the Account() class\n"
|
|
104
|
-
f"Currently Supported brokers are: {', '.join(supported.values())}\n"
|
|
105
|
-
f"For {supported['AMG']}, See [{amg_url}]\n"
|
|
106
|
-
f"For {supported['JGM']}, See [{jgm_url}]\n"
|
|
107
|
-
f"For {supported['FTMO']}, See [{ftmo_url}]\n"
|
|
241
|
+
f"{self.broker.name} is not currently supported broker for the Account() class\n"
|
|
242
|
+
f"Currently Supported brokers are: {', '.join([b.name for b in supported.values()])}\n"
|
|
243
|
+
f"For {supported['AMG'].name}, See [{amg_url}]\n"
|
|
244
|
+
f"For {supported['JGM'].name}, See [{jgm_url}]\n"
|
|
245
|
+
f"For {supported['FTMO'].name}, See [{ftmo_url}]\n"
|
|
108
246
|
)
|
|
109
247
|
raise InvalidBroker(message=msg)
|
|
110
|
-
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def broker(self) -> Broker:
|
|
251
|
+
return Broker(self.get_terminal_info().company)
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def timezone(self) -> str:
|
|
255
|
+
for broker in BROKERS.values():
|
|
256
|
+
if broker == self.broker:
|
|
257
|
+
return broker.timezone
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def name(self)-> str:
|
|
261
|
+
return self.get_account_info().name
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def number(self)-> int:
|
|
265
|
+
return self.get_account_info().login
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def server(self)-> str:
|
|
269
|
+
"""The name of the trade server to which the client terminal is connected.
|
|
270
|
+
(e.g., 'AdmiralsGroup-Demo')
|
|
271
|
+
"""
|
|
272
|
+
return self.get_account_info().server
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def balance(self) -> float:
|
|
276
|
+
return self.get_account_info().balance
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def leverage(self) -> int:
|
|
280
|
+
return self.get_account_info().leverage
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def equity(self) -> float:
|
|
284
|
+
return self.get_account_info().equity
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def currency(self) -> str:
|
|
288
|
+
return self.get_account_info().currency
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def language(self) -> str:
|
|
292
|
+
"""The language of the terminal interface."""
|
|
293
|
+
return self.get_terminal_info().language
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def maxbars(self) -> int:
|
|
297
|
+
"""The maximal bars count on the chart."""
|
|
298
|
+
return self.get_terminal_info().maxbars
|
|
299
|
+
|
|
111
300
|
def get_account_info(
|
|
112
301
|
self,
|
|
113
302
|
account: Optional[int] = None,
|
|
@@ -272,8 +461,8 @@ class Account(object):
|
|
|
272
461
|
c = CurrencyConverter(filename)
|
|
273
462
|
os.remove(filename)
|
|
274
463
|
supported = c.currencies
|
|
275
|
-
if (from_c not in supported
|
|
276
|
-
|
|
464
|
+
if (from_c not in supported
|
|
465
|
+
or to_c not in supported
|
|
277
466
|
):
|
|
278
467
|
rate = qty
|
|
279
468
|
else:
|
|
@@ -293,7 +482,7 @@ class Account(object):
|
|
|
293
482
|
|
|
294
483
|
Exemple:
|
|
295
484
|
>>> account = Account()
|
|
296
|
-
>>> account.
|
|
485
|
+
>>> account.get_currency_rates('EURUSD')
|
|
297
486
|
{'bc': 'EUR', 'mc': 'EUR', 'pc': 'USD', 'ac': 'USD'}
|
|
298
487
|
"""
|
|
299
488
|
info = self.get_symbol_info(symbol)
|
|
@@ -489,16 +678,19 @@ class Account(object):
|
|
|
489
678
|
|
|
490
679
|
Notes:
|
|
491
680
|
This mthods works primarly with Admirals Group AS products,
|
|
492
|
-
For other brokers use `get_symbols()`
|
|
681
|
+
For other brokers use `get_symbols()` or this method will use it by default.
|
|
493
682
|
"""
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
683
|
+
if self.broker != AdmiralMarktsGroup():
|
|
684
|
+
return self.get_symbols(symbol_type='FX')
|
|
685
|
+
else:
|
|
686
|
+
fx_categories = {
|
|
687
|
+
"majors": r"\b(Majors?)\b",
|
|
688
|
+
"minors": r"\b(Minors?)\b",
|
|
689
|
+
"exotics": r"\b(Exotics?)\b",
|
|
690
|
+
}
|
|
691
|
+
return self._get_symbols_by_category('forex', category, fx_categories)
|
|
500
692
|
|
|
501
|
-
def
|
|
693
|
+
def get_stocks_from_country(self, country_code: str = 'USA', etf=True) -> List[str]:
|
|
502
694
|
"""
|
|
503
695
|
Retrieves a list of stock symbols from a specific country.
|
|
504
696
|
|
|
@@ -530,25 +722,133 @@ class Account(object):
|
|
|
530
722
|
|
|
531
723
|
Notes:
|
|
532
724
|
This mthods works primarly with Admirals Group AS products,
|
|
533
|
-
For other brokers use `get_symbols()`
|
|
725
|
+
For other brokers use `get_symbols()` or this method will use it by default.
|
|
534
726
|
"""
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
727
|
+
|
|
728
|
+
if self.broker != AdmiralMarktsGroup():
|
|
729
|
+
stocks = self.get_symbols(symbol_type='STK')
|
|
730
|
+
return stocks
|
|
731
|
+
else:
|
|
732
|
+
country_map = _COUNTRY_MAP_
|
|
733
|
+
stocks = self._get_symbols_by_category('STK', country_code, country_map)
|
|
734
|
+
if etf:
|
|
735
|
+
etfs = self._get_symbols_by_category('ETF', country_code, country_map)
|
|
736
|
+
return stocks + etfs
|
|
737
|
+
return stocks
|
|
738
|
+
|
|
739
|
+
def get_stocks_from_exchange(self, exchange_code: str = 'XNYS', etf=True) -> List[str]:
|
|
740
|
+
"""
|
|
741
|
+
Get stock symbols from a specific exchange using the ISO Code for the exchange.
|
|
742
|
+
|
|
743
|
+
Supported exchanges are from Admirals Group AS products:
|
|
744
|
+
* **XASX:** **Australian Securities Exchange**
|
|
745
|
+
* **XBRU:** **Euronext Brussels Exchange**
|
|
746
|
+
* **XCSE:** **Copenhagen Stock Exchange**
|
|
747
|
+
* **XHEL:** **NASDAQ OMX Helsinki**
|
|
748
|
+
* **XPAR:** **Euronext Paris**
|
|
749
|
+
* **XETR:** **Xetra Frankfurt**
|
|
750
|
+
* **XOSL:** **Oslo Stock Exchange**
|
|
751
|
+
* **XLIS:** **Euronext Lisbon**
|
|
752
|
+
* **XMAD:** **Bolsa de Madrid**
|
|
753
|
+
* **XSTO:** **NASDAQ OMX Stockholm**
|
|
754
|
+
* **XLON:** **London Stock Exchange**
|
|
755
|
+
* **NYSE:** **New York Stock Exchange**
|
|
756
|
+
* **ARCA:** **NYSE ARCA**
|
|
757
|
+
* **AMEX:** **NYSE AMEX**
|
|
758
|
+
* **XNYS:** **New York Stock Exchange (AMEX, ARCA, NYSE)**
|
|
759
|
+
* **NASDAQ:** **NASDAQ**
|
|
760
|
+
* **BATS:** **BATS Exchange**
|
|
761
|
+
* **XSWX:** **SWX Swiss Exchange**
|
|
762
|
+
* **XAMS:** **Euronext Amsterdam**
|
|
763
|
+
|
|
764
|
+
Args:
|
|
765
|
+
exchange_code (str, optional): The ISO code of the exchange.
|
|
766
|
+
etf (bool, optional): If True, include ETFs from the exchange. Defaults to True.
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
list: A list of stock symbol names from the specified exchange.
|
|
770
|
+
|
|
771
|
+
Raises:
|
|
772
|
+
ValueError: If an unsupported exchange is provided.
|
|
773
|
+
|
|
774
|
+
Notes:
|
|
775
|
+
This mthods works primarly with Admirals Group AS products,
|
|
776
|
+
For other brokers use `get_symbols()` or this method will use it by default.
|
|
777
|
+
"""
|
|
778
|
+
if self.broker != AdmiralMarktsGroup():
|
|
779
|
+
stocks = self.get_symbols(symbol_type='STK')
|
|
780
|
+
return stocks
|
|
781
|
+
else:
|
|
782
|
+
exchange_map = AMG_EXCHANGES
|
|
783
|
+
stocks = self._get_symbols_by_category('STK', exchange_code, exchange_map)
|
|
784
|
+
if etf:
|
|
785
|
+
etfs = self._get_symbols_by_category('ETF', exchange_code, exchange_map)
|
|
786
|
+
return stocks + etfs
|
|
787
|
+
return stocks
|
|
788
|
+
|
|
789
|
+
def get_future_symbols(self, category: str = 'ALL') -> List[str]:
|
|
790
|
+
"""
|
|
791
|
+
Retrieves a list of future symbols belonging to a specific category.
|
|
792
|
+
|
|
793
|
+
Args:
|
|
794
|
+
category : The category of future symbols to retrieve.
|
|
795
|
+
Possible values are 'ALL', 'agricultures', 'energies', 'metals'.
|
|
796
|
+
Defaults to 'ALL'.
|
|
797
|
+
|
|
798
|
+
Returns:
|
|
799
|
+
list: A list of future symbol names matching the specified category.
|
|
800
|
+
|
|
801
|
+
Raises:
|
|
802
|
+
ValueError: If an unsupported category is provided.
|
|
803
|
+
|
|
804
|
+
Notes:
|
|
805
|
+
This mthods works primarly with Admirals Group AS products,
|
|
806
|
+
For other brokers use `get_symbols()` or this method will use it by default.
|
|
807
|
+
"""
|
|
808
|
+
category = category.lower()
|
|
809
|
+
if self.broker != AdmiralMarktsGroup():
|
|
810
|
+
return self.get_symbols(symbol_type='FUT')
|
|
811
|
+
elif category in ['all', 'index']:
|
|
812
|
+
categories = {
|
|
813
|
+
"all": r"\b(Futures?)\b",
|
|
814
|
+
"index": r"\b(Index)\b",
|
|
815
|
+
}
|
|
816
|
+
return self._get_symbols_by_category('FUT', category, categories)
|
|
817
|
+
else:
|
|
818
|
+
metals = []
|
|
819
|
+
energies = []
|
|
820
|
+
agricultures = []
|
|
821
|
+
bonds = []
|
|
822
|
+
commodities = self.get_symbols(symbol_type='COMD')
|
|
823
|
+
futures = self.get_symbols(symbol_type='FUT')
|
|
824
|
+
for symbol in futures:
|
|
825
|
+
info = self.get_symbol_info(symbol)
|
|
826
|
+
if info.name.startswith('_'):
|
|
827
|
+
if 'XAU' in info.name:
|
|
828
|
+
metals.append(info.name)
|
|
829
|
+
if 'oil' in info.name.lower():
|
|
830
|
+
energies.append(info.name)
|
|
831
|
+
name = info.name.split('_')[1]
|
|
832
|
+
if name in commodities:
|
|
833
|
+
_info = self.get_symbol_info(name)
|
|
834
|
+
if 'Metals' in _info.path:
|
|
835
|
+
metals.append(info.name)
|
|
836
|
+
elif 'Energies' in _info.path:
|
|
837
|
+
energies.append(info.name)
|
|
838
|
+
elif 'Agricultures' in _info.path:
|
|
839
|
+
agricultures.append(info.name)
|
|
840
|
+
|
|
841
|
+
elif info.name.startswith('#'):
|
|
842
|
+
if 'Index' not in info.path:
|
|
843
|
+
bonds.append(info.name)
|
|
844
|
+
if category == 'metals':
|
|
845
|
+
return metals
|
|
846
|
+
elif category == 'energies':
|
|
847
|
+
return energies
|
|
848
|
+
elif category == 'agricultures':
|
|
849
|
+
return agricultures
|
|
850
|
+
elif category == 'bonds':
|
|
851
|
+
return bonds
|
|
552
852
|
|
|
553
853
|
def get_symbol_info(self, symbol: str) -> Union[SymbolInfo, None]:
|
|
554
854
|
"""Get symbol properties
|
|
@@ -654,12 +954,12 @@ class Account(object):
|
|
|
654
954
|
Raises:
|
|
655
955
|
MT5TerminalError: A specific exception based on the error code.
|
|
656
956
|
"""
|
|
657
|
-
|
|
957
|
+
actions = {
|
|
658
958
|
'buy': mt5.ORDER_TYPE_BUY,
|
|
659
959
|
'sell': mt5.ORDER_TYPE_SELL
|
|
660
960
|
}
|
|
661
961
|
try:
|
|
662
|
-
margin = mt5.order_calc_margin(
|
|
962
|
+
margin = mt5.order_calc_margin(actions[action], symbol, lot, price)
|
|
663
963
|
if margin is None:
|
|
664
964
|
return None
|
|
665
965
|
return margin
|
|
@@ -691,7 +991,7 @@ class Account(object):
|
|
|
691
991
|
try:
|
|
692
992
|
result = mt5.order_check(request)
|
|
693
993
|
result_dict = result._asdict()
|
|
694
|
-
trade_request =
|
|
994
|
+
trade_request = TradeRequest(**result.request._asdict())
|
|
695
995
|
result_dict['request'] = trade_request
|
|
696
996
|
return OrderCheckResult(**result_dict)
|
|
697
997
|
except Exception as e:
|
|
@@ -717,7 +1017,7 @@ class Account(object):
|
|
|
717
1017
|
try:
|
|
718
1018
|
result = mt5.order_send(request)
|
|
719
1019
|
result_dict = result._asdict()
|
|
720
|
-
trade_request =
|
|
1020
|
+
trade_request = TradeRequest(**result.request._asdict())
|
|
721
1021
|
result_dict['request'] = trade_request
|
|
722
1022
|
return OrderSentResult(**result_dict)
|
|
723
1023
|
except Exception as e:
|
|
@@ -898,6 +1198,8 @@ class Account(object):
|
|
|
898
1198
|
df = pd.DataFrame(list(position_deals),
|
|
899
1199
|
columns=position_deals[0]._asdict())
|
|
900
1200
|
df['time'] = pd.to_datetime(df['time'], unit='s')
|
|
1201
|
+
df.drop(['time_msc', 'external_id'], axis=1, inplace=True)
|
|
1202
|
+
df.set_index('time', inplace=True)
|
|
901
1203
|
if save:
|
|
902
1204
|
file = "trade_history.csv"
|
|
903
1205
|
df.to_csv(file)
|