bbstrader 0.1.9__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 +348 -53
- bbstrader/metatrader/rates.py +232 -27
- bbstrader/metatrader/risk.py +34 -23
- bbstrader/metatrader/trade.py +321 -165
- 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 +268 -164
- bbstrader/trading/scripts.py +57 -0
- bbstrader/trading/strategies.py +41 -65
- bbstrader/tseries.py +274 -39
- {bbstrader-0.1.9.dist-info → bbstrader-0.1.91.dist-info}/METADATA +11 -3
- bbstrader-0.1.91.dist-info/RECORD +31 -0
- {bbstrader-0.1.9.dist-info → bbstrader-0.1.91.dist-info}/WHEEL +1 -1
- bbstrader-0.1.9.dist-info/RECORD +0 -26
- {bbstrader-0.1.9.dist-info → bbstrader-0.1.91.dist-info}/LICENSE +0 -0
- {bbstrader-0.1.9.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,45 @@ _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
|
+
|
|
57
128
|
def check_mt5_connection():
|
|
58
129
|
try:
|
|
59
130
|
init = mt5.initialize()
|
|
@@ -62,6 +133,67 @@ def check_mt5_connection():
|
|
|
62
133
|
except Exception:
|
|
63
134
|
raise_mt5_error(INIT_MSG)
|
|
64
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
|
+
|
|
65
197
|
class Account(object):
|
|
66
198
|
"""
|
|
67
199
|
The `Account` class is utilized to retrieve information about
|
|
@@ -103,18 +235,68 @@ class Account(object):
|
|
|
103
235
|
self._check_brokers()
|
|
104
236
|
|
|
105
237
|
def _check_brokers(self):
|
|
106
|
-
supported =
|
|
107
|
-
broker
|
|
108
|
-
if broker not in supported.values():
|
|
238
|
+
supported = BROKERS.copy()
|
|
239
|
+
if self.broker not in supported.values():
|
|
109
240
|
msg = (
|
|
110
|
-
f"{broker} is not currently supported broker for the Account() class\n"
|
|
111
|
-
f"Currently Supported brokers are: {', '.join(supported.values())}\n"
|
|
112
|
-
f"For {supported['AMG']}, See [{amg_url}]\n"
|
|
113
|
-
f"For {supported['JGM']}, See [{jgm_url}]\n"
|
|
114
|
-
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"
|
|
115
246
|
)
|
|
116
247
|
raise InvalidBroker(message=msg)
|
|
117
|
-
|
|
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
|
+
|
|
118
300
|
def get_account_info(
|
|
119
301
|
self,
|
|
120
302
|
account: Optional[int] = None,
|
|
@@ -279,8 +461,8 @@ class Account(object):
|
|
|
279
461
|
c = CurrencyConverter(filename)
|
|
280
462
|
os.remove(filename)
|
|
281
463
|
supported = c.currencies
|
|
282
|
-
if (from_c not in supported
|
|
283
|
-
|
|
464
|
+
if (from_c not in supported
|
|
465
|
+
or to_c not in supported
|
|
284
466
|
):
|
|
285
467
|
rate = qty
|
|
286
468
|
else:
|
|
@@ -300,7 +482,7 @@ class Account(object):
|
|
|
300
482
|
|
|
301
483
|
Exemple:
|
|
302
484
|
>>> account = Account()
|
|
303
|
-
>>> account.
|
|
485
|
+
>>> account.get_currency_rates('EURUSD')
|
|
304
486
|
{'bc': 'EUR', 'mc': 'EUR', 'pc': 'USD', 'ac': 'USD'}
|
|
305
487
|
"""
|
|
306
488
|
info = self.get_symbol_info(symbol)
|
|
@@ -496,16 +678,19 @@ class Account(object):
|
|
|
496
678
|
|
|
497
679
|
Notes:
|
|
498
680
|
This mthods works primarly with Admirals Group AS products,
|
|
499
|
-
For other brokers use `get_symbols()`
|
|
681
|
+
For other brokers use `get_symbols()` or this method will use it by default.
|
|
500
682
|
"""
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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)
|
|
507
692
|
|
|
508
|
-
def
|
|
693
|
+
def get_stocks_from_country(self, country_code: str = 'USA', etf=True) -> List[str]:
|
|
509
694
|
"""
|
|
510
695
|
Retrieves a list of stock symbols from a specific country.
|
|
511
696
|
|
|
@@ -537,25 +722,133 @@ class Account(object):
|
|
|
537
722
|
|
|
538
723
|
Notes:
|
|
539
724
|
This mthods works primarly with Admirals Group AS products,
|
|
540
|
-
For other brokers use `get_symbols()`
|
|
725
|
+
For other brokers use `get_symbols()` or this method will use it by default.
|
|
541
726
|
"""
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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
|
|
559
852
|
|
|
560
853
|
def get_symbol_info(self, symbol: str) -> Union[SymbolInfo, None]:
|
|
561
854
|
"""Get symbol properties
|
|
@@ -661,12 +954,12 @@ class Account(object):
|
|
|
661
954
|
Raises:
|
|
662
955
|
MT5TerminalError: A specific exception based on the error code.
|
|
663
956
|
"""
|
|
664
|
-
|
|
957
|
+
actions = {
|
|
665
958
|
'buy': mt5.ORDER_TYPE_BUY,
|
|
666
959
|
'sell': mt5.ORDER_TYPE_SELL
|
|
667
960
|
}
|
|
668
961
|
try:
|
|
669
|
-
margin = mt5.order_calc_margin(
|
|
962
|
+
margin = mt5.order_calc_margin(actions[action], symbol, lot, price)
|
|
670
963
|
if margin is None:
|
|
671
964
|
return None
|
|
672
965
|
return margin
|
|
@@ -698,7 +991,7 @@ class Account(object):
|
|
|
698
991
|
try:
|
|
699
992
|
result = mt5.order_check(request)
|
|
700
993
|
result_dict = result._asdict()
|
|
701
|
-
trade_request =
|
|
994
|
+
trade_request = TradeRequest(**result.request._asdict())
|
|
702
995
|
result_dict['request'] = trade_request
|
|
703
996
|
return OrderCheckResult(**result_dict)
|
|
704
997
|
except Exception as e:
|
|
@@ -724,7 +1017,7 @@ class Account(object):
|
|
|
724
1017
|
try:
|
|
725
1018
|
result = mt5.order_send(request)
|
|
726
1019
|
result_dict = result._asdict()
|
|
727
|
-
trade_request =
|
|
1020
|
+
trade_request = TradeRequest(**result.request._asdict())
|
|
728
1021
|
result_dict['request'] = trade_request
|
|
729
1022
|
return OrderSentResult(**result_dict)
|
|
730
1023
|
except Exception as e:
|
|
@@ -905,6 +1198,8 @@ class Account(object):
|
|
|
905
1198
|
df = pd.DataFrame(list(position_deals),
|
|
906
1199
|
columns=position_deals[0]._asdict())
|
|
907
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)
|
|
908
1203
|
if save:
|
|
909
1204
|
file = "trade_history.csv"
|
|
910
1205
|
df.to_csv(file)
|