bbstrader 0.2.991__py3-none-any.whl → 0.3.1__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/__init__.py +1 -1
- bbstrader/__main__.py +35 -14
- bbstrader/btengine/data.py +4 -2
- bbstrader/btengine/execution.py +24 -14
- bbstrader/btengine/strategy.py +33 -15
- bbstrader/core/data.py +93 -30
- bbstrader/core/scripts.py +130 -0
- bbstrader/metatrader/account.py +115 -126
- bbstrader/metatrader/copier.py +114 -39
- bbstrader/metatrader/rates.py +14 -13
- bbstrader/metatrader/risk.py +13 -11
- bbstrader/metatrader/scripts.py +26 -12
- bbstrader/metatrader/trade.py +60 -54
- bbstrader/metatrader/utils.py +80 -26
- bbstrader/models/factors.py +3 -1
- bbstrader/models/ml.py +2 -1
- bbstrader/models/nlp.py +123 -70
- bbstrader/trading/execution.py +74 -36
- bbstrader/trading/strategies.py +15 -14
- bbstrader/tseries.py +8 -9
- bbstrader-0.3.1.dist-info/METADATA +466 -0
- bbstrader-0.3.1.dist-info/RECORD +47 -0
- {bbstrader-0.2.991.dist-info → bbstrader-0.3.1.dist-info}/WHEEL +1 -1
- bbstrader/__ini__.py +0 -20
- bbstrader-0.2.991.dist-info/METADATA +0 -191
- bbstrader-0.2.991.dist-info/RECORD +0 -47
- {bbstrader-0.2.991.dist-info → bbstrader-0.3.1.dist-info}/entry_points.txt +0 -0
- {bbstrader-0.2.991.dist-info → bbstrader-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {bbstrader-0.2.991.dist-info → bbstrader-0.3.1.dist-info}/top_level.txt +0 -0
bbstrader/metatrader/trade.py
CHANGED
|
@@ -33,12 +33,6 @@ __all__ = [
|
|
|
33
33
|
"create_trade_instance",
|
|
34
34
|
]
|
|
35
35
|
|
|
36
|
-
FILLING_TYPE = [
|
|
37
|
-
Mt5.ORDER_FILLING_IOC,
|
|
38
|
-
Mt5.ORDER_FILLING_RETURN,
|
|
39
|
-
Mt5.ORDER_FILLING_BOC,
|
|
40
|
-
]
|
|
41
|
-
|
|
42
36
|
log.add(
|
|
43
37
|
f"{BBSTRADER_DIR}/logs/trade.log",
|
|
44
38
|
enqueue=True,
|
|
@@ -50,6 +44,13 @@ global LOGGER
|
|
|
50
44
|
LOGGER = log
|
|
51
45
|
|
|
52
46
|
|
|
47
|
+
FILLING_TYPE = [
|
|
48
|
+
Mt5.ORDER_FILLING_IOC,
|
|
49
|
+
Mt5.ORDER_FILLING_RETURN,
|
|
50
|
+
Mt5.ORDER_FILLING_BOC,
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
53
54
|
class TradeAction(Enum):
|
|
54
55
|
"""
|
|
55
56
|
An enumeration class for trade actions.
|
|
@@ -133,10 +134,21 @@ class TradeSignal:
|
|
|
133
134
|
def __repr__(self):
|
|
134
135
|
return (
|
|
135
136
|
f"TradeSignal(id={self.id}, symbol='{self.symbol}', action='{self.action.value}', "
|
|
136
|
-
f"price={self.price}, stoplimit={self.stoplimit}
|
|
137
|
+
f"price={self.price}, stoplimit={self.stoplimit}, comment='{self.comment or ''}')"
|
|
137
138
|
)
|
|
138
139
|
|
|
139
140
|
|
|
141
|
+
class TradingMode(Enum):
|
|
142
|
+
BACKTEST = "BACKTEST"
|
|
143
|
+
LIVE = "LIVE"
|
|
144
|
+
|
|
145
|
+
def isbacktest(self) -> bool:
|
|
146
|
+
return self == TradingMode.BACKTEST
|
|
147
|
+
|
|
148
|
+
def islive(self) -> bool:
|
|
149
|
+
return self == TradingMode.LIVE
|
|
150
|
+
|
|
151
|
+
|
|
140
152
|
Buys = Literal["BMKT", "BLMT", "BSTP", "BSTPLMT"]
|
|
141
153
|
Sells = Literal["SMKT", "SLMT", "SSTP", "SSTPLMT"]
|
|
142
154
|
Positions = Literal["all", "buy", "sell", "profitable", "losing"]
|
|
@@ -152,6 +164,7 @@ Orders = Literal[
|
|
|
152
164
|
|
|
153
165
|
EXPERT_ID = 98181105
|
|
154
166
|
|
|
167
|
+
|
|
155
168
|
class Trade(RiskManagement):
|
|
156
169
|
"""
|
|
157
170
|
Extends the `RiskManagement` class to include specific trading operations,
|
|
@@ -729,10 +742,8 @@ class Trade(RiskManagement):
|
|
|
729
742
|
self.check_order(request)
|
|
730
743
|
result = self.send_order(request)
|
|
731
744
|
except Exception as e:
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
result.retcode, display=True, add_msg=f"{e}{addtionnal}"
|
|
735
|
-
)
|
|
745
|
+
msg = trade_retcode_message(result.retcode)
|
|
746
|
+
LOGGER.error(f"Trade Order Request, {msg}{addtionnal}, {e}")
|
|
736
747
|
if result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
737
748
|
if result.retcode == Mt5.TRADE_RETCODE_INVALID_FILL: # 10030
|
|
738
749
|
for fill in FILLING_TYPE:
|
|
@@ -762,10 +773,8 @@ class Trade(RiskManagement):
|
|
|
762
773
|
self.check_order(request)
|
|
763
774
|
result = self.send_order(request)
|
|
764
775
|
except Exception as e:
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
result.retcode, display=True, add_msg=f"{e}{addtionnal}"
|
|
768
|
-
)
|
|
776
|
+
msg = trade_retcode_message(result.retcode)
|
|
777
|
+
LOGGER.error(f"Trade Order Request, {msg}{addtionnal}, {e}")
|
|
769
778
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
770
779
|
break
|
|
771
780
|
tries += 1
|
|
@@ -776,7 +785,7 @@ class Trade(RiskManagement):
|
|
|
776
785
|
if type != "BMKT" or type != "SMKT":
|
|
777
786
|
self.opened_orders.append(result.order)
|
|
778
787
|
long_msg = (
|
|
779
|
-
f"1. {pos} Order #{result.order} Sent, Symbol: {self.symbol}, Price: @{price}, "
|
|
788
|
+
f"1. {pos} Order #{result.order} Sent, Symbol: {self.symbol}, Price: @{round(price, 5)}, "
|
|
780
789
|
f"Lot(s): {result.volume}, Sl: {self.get_stop_loss()}, "
|
|
781
790
|
f"Tp: {self.get_take_profit()}"
|
|
782
791
|
)
|
|
@@ -797,7 +806,7 @@ class Trade(RiskManagement):
|
|
|
797
806
|
profit = round(self.get_account_info().profit, 5)
|
|
798
807
|
order_info = (
|
|
799
808
|
f"2. {order_type} Position Opened, Symbol: {self.symbol}, Price: @{round(position.price_open, 5)}, "
|
|
800
|
-
f"Sl: @{position.sl} Tp: @{position.tp}"
|
|
809
|
+
f"Sl: @{round(position.sl, 5)} Tp: @{round(position.tp, 5)}"
|
|
801
810
|
)
|
|
802
811
|
LOGGER.info(order_info)
|
|
803
812
|
pos_info = (
|
|
@@ -834,7 +843,7 @@ class Trade(RiskManagement):
|
|
|
834
843
|
action (str): (`'BMKT'`, `'SMKT'`) for Market orders
|
|
835
844
|
or (`'BLMT', 'SLMT', 'BSTP', 'SSTP', 'BSTPLMT', 'SSTPLMT'`) for pending orders
|
|
836
845
|
price (float): The price at which to open an order
|
|
837
|
-
stoplimit (float): A price a pending Limit order is set at
|
|
846
|
+
stoplimit (float): A price a pending Limit order is set at
|
|
838
847
|
when the price reaches the 'price' value (this condition is mandatory).
|
|
839
848
|
The pending order is not passed to the trading system until that moment
|
|
840
849
|
id (int): The strategy id or expert Id
|
|
@@ -873,30 +882,30 @@ class Trade(RiskManagement):
|
|
|
873
882
|
@property
|
|
874
883
|
def orders(self):
|
|
875
884
|
"""Return all opened order's tickets"""
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
return None
|
|
885
|
+
current_orders = self.get_current_orders() or []
|
|
886
|
+
opened_orders = set(current_orders + self.opened_orders)
|
|
887
|
+
return list(opened_orders) if len(opened_orders) != 0 else None
|
|
879
888
|
|
|
880
889
|
@property
|
|
881
890
|
def positions(self):
|
|
882
891
|
"""Return all opened position's tickets"""
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
return None
|
|
892
|
+
current_positions = self.get_current_positions() or []
|
|
893
|
+
opened_positions = set(current_positions + self.opened_positions)
|
|
894
|
+
return list(opened_positions) if len(opened_positions) != 0 else None
|
|
886
895
|
|
|
887
896
|
@property
|
|
888
897
|
def buypos(self):
|
|
889
898
|
"""Return all buy opened position's tickets"""
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
return None
|
|
899
|
+
buy_positions = self.get_current_buys() or []
|
|
900
|
+
buy_positions = set(buy_positions + self.buy_positions)
|
|
901
|
+
return list(buy_positions) if len(buy_positions) != 0 else None
|
|
893
902
|
|
|
894
903
|
@property
|
|
895
904
|
def sellpos(self):
|
|
896
905
|
"""Return all sell opened position's tickets"""
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
return None
|
|
906
|
+
sell_positions = self.get_current_sells() or []
|
|
907
|
+
sell_positions = set(sell_positions + self.sell_positions)
|
|
908
|
+
return list(sell_positions) if len(sell_positions) != 0 else None
|
|
900
909
|
|
|
901
910
|
@property
|
|
902
911
|
def bepos(self):
|
|
@@ -1214,7 +1223,7 @@ class Trade(RiskManagement):
|
|
|
1214
1223
|
Sets the break-even level for a given trading position.
|
|
1215
1224
|
|
|
1216
1225
|
Args:
|
|
1217
|
-
position (TradePosition): The trading position for which the break-even is to be set.
|
|
1226
|
+
position (TradePosition): The trading position for which the break-even is to be set.
|
|
1218
1227
|
This is the value return by `mt5.positions_get()`.
|
|
1219
1228
|
be (int): The break-even level in points.
|
|
1220
1229
|
level (float): The break-even level in price, if set to None , it will be calated automaticaly.
|
|
@@ -1283,10 +1292,8 @@ class Trade(RiskManagement):
|
|
|
1283
1292
|
self.check_order(request)
|
|
1284
1293
|
result = self.send_order(request)
|
|
1285
1294
|
except Exception as e:
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
result.retcode, display=True, add_msg=f"{e}{addtionnal}"
|
|
1289
|
-
)
|
|
1295
|
+
msg = trade_retcode_message(result.retcode)
|
|
1296
|
+
LOGGER.error(f"Break-Even Order Request, {msg}{addtionnal}, Error: {e}")
|
|
1290
1297
|
if result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
1291
1298
|
msg = trade_retcode_message(result.retcode)
|
|
1292
1299
|
if result.retcode != Mt5.TRADE_RETCODE_NO_CHANGES:
|
|
@@ -1303,17 +1310,15 @@ class Trade(RiskManagement):
|
|
|
1303
1310
|
self.check_order(request)
|
|
1304
1311
|
result = self.send_order(request)
|
|
1305
1312
|
except Exception as e:
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
result.retcode, display=True, add_msg=f"{e}{addtionnal}"
|
|
1309
|
-
)
|
|
1313
|
+
msg = trade_retcode_message(result.retcode)
|
|
1314
|
+
LOGGER.error(f"Break-Even Order Request, {msg}{addtionnal}, Error: {e}")
|
|
1310
1315
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
1311
1316
|
break
|
|
1312
1317
|
tries += 1
|
|
1313
1318
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
1314
1319
|
msg = trade_retcode_message(result.retcode)
|
|
1315
1320
|
LOGGER.info(f"Break-Even Order {msg}{addtionnal}")
|
|
1316
|
-
info = f"Stop loss set to Break-even, Position: #{tiket}, Symbol: {self.symbol}, Price: @{price}"
|
|
1321
|
+
info = f"Stop loss set to Break-even, Position: #{tiket}, Symbol: {self.symbol}, Price: @{round(price, 5)}"
|
|
1317
1322
|
LOGGER.info(info)
|
|
1318
1323
|
self.break_even_status.append(tiket)
|
|
1319
1324
|
|
|
@@ -1391,10 +1396,8 @@ class Trade(RiskManagement):
|
|
|
1391
1396
|
self.check_order(request)
|
|
1392
1397
|
result = self.send_order(request)
|
|
1393
1398
|
except Exception as e:
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
result.retcode, display=True, add_msg=f"{e}{addtionnal}"
|
|
1397
|
-
)
|
|
1399
|
+
msg = trade_retcode_message(result.retcode)
|
|
1400
|
+
LOGGER.error(f"Closing {type.capitalize()} Request, {msg}{addtionnal}, Error: {e}")
|
|
1398
1401
|
if result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
1399
1402
|
if result.retcode == Mt5.TRADE_RETCODE_INVALID_FILL: # 10030
|
|
1400
1403
|
for fill in FILLING_TYPE:
|
|
@@ -1406,7 +1409,8 @@ class Trade(RiskManagement):
|
|
|
1406
1409
|
self._retcodes.append(result.retcode)
|
|
1407
1410
|
msg = trade_retcode_message(result.retcode)
|
|
1408
1411
|
LOGGER.error(
|
|
1409
|
-
f"Closing Order Request, {type.capitalize()}: #{ticket},
|
|
1412
|
+
f"Closing Order Request, {type.capitalize()}: #{ticket}, "
|
|
1413
|
+
f"RETCODE={result.retcode}: {msg}{addtionnal}"
|
|
1410
1414
|
)
|
|
1411
1415
|
else:
|
|
1412
1416
|
tries = 0
|
|
@@ -1416,17 +1420,18 @@ class Trade(RiskManagement):
|
|
|
1416
1420
|
self.check_order(request)
|
|
1417
1421
|
result = self.send_order(request)
|
|
1418
1422
|
except Exception as e:
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
result.retcode, display=True, add_msg=f"{e}{addtionnal}"
|
|
1422
|
-
)
|
|
1423
|
+
msg = trade_retcode_message(result.retcode)
|
|
1424
|
+
LOGGER.error(f"Closing {type.capitalize()} Request, {msg}{addtionnal}, Error: {e}")
|
|
1423
1425
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
1424
1426
|
break
|
|
1425
1427
|
tries += 1
|
|
1426
1428
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
1427
1429
|
msg = trade_retcode_message(result.retcode)
|
|
1428
1430
|
LOGGER.info(f"Closing Order {msg}{addtionnal}")
|
|
1429
|
-
info =
|
|
1431
|
+
info = (
|
|
1432
|
+
f"{type.capitalize()} #{ticket} closed, Symbol: {self.symbol},"
|
|
1433
|
+
f"Price: @{round(request.get('price', 0.0), 5)}"
|
|
1434
|
+
)
|
|
1430
1435
|
LOGGER.info(info)
|
|
1431
1436
|
return True
|
|
1432
1437
|
else:
|
|
@@ -1446,7 +1451,7 @@ class Trade(RiskManagement):
|
|
|
1446
1451
|
Args:
|
|
1447
1452
|
ticket (int): Order ticket to modify (e.g TradeOrder.ticket)
|
|
1448
1453
|
price (float): The price at which to modify the order
|
|
1449
|
-
stoplimit (float): A price a pending Limit order is set at
|
|
1454
|
+
stoplimit (float): A price a pending Limit order is set at
|
|
1450
1455
|
when the price reaches the 'price' value (this condition is mandatory).
|
|
1451
1456
|
The pending order is not passed to the trading system until that moment
|
|
1452
1457
|
sl (float): The stop loss in points
|
|
@@ -1455,7 +1460,7 @@ class Trade(RiskManagement):
|
|
|
1455
1460
|
orders = self.get_orders(ticket=ticket) or []
|
|
1456
1461
|
if len(orders) == 0:
|
|
1457
1462
|
LOGGER.error(
|
|
1458
|
-
f"Order #{ticket} not found, SYMBOL={self.symbol}, PRICE={price}"
|
|
1463
|
+
f"Order #{ticket} not found, SYMBOL={self.symbol}, PRICE={round(price, 5)}"
|
|
1459
1464
|
)
|
|
1460
1465
|
return
|
|
1461
1466
|
order = orders[0]
|
|
@@ -1471,7 +1476,8 @@ class Trade(RiskManagement):
|
|
|
1471
1476
|
result = self.send_order(request)
|
|
1472
1477
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
1473
1478
|
LOGGER.info(
|
|
1474
|
-
f"Order #{ticket} modified, SYMBOL={self.symbol}, PRICE={price
|
|
1479
|
+
f"Order #{ticket} modified, SYMBOL={self.symbol}, PRICE={round(price, 5)},"
|
|
1480
|
+
f"SL={round(sl, 5)}, TP={round(tp, 5)}, STOP_LIMIT={round(stoplimit, 5)}"
|
|
1475
1481
|
)
|
|
1476
1482
|
else:
|
|
1477
1483
|
msg = trade_retcode_message(result.retcode)
|
|
@@ -1597,7 +1603,7 @@ class Trade(RiskManagement):
|
|
|
1597
1603
|
):
|
|
1598
1604
|
"""
|
|
1599
1605
|
Args:
|
|
1600
|
-
order_type (str): Type of orders to close
|
|
1606
|
+
order_type (str): Type of orders to close
|
|
1601
1607
|
('all', 'buy_stops', 'sell_stops', 'buy_limits', 'sell_limits', 'buy_stop_limits', 'sell_stop_limits')
|
|
1602
1608
|
id (int): The unique ID of the Expert or Strategy
|
|
1603
1609
|
comment (str): Comment for the closing position
|
bbstrader/metatrader/utils.py
CHANGED
|
@@ -14,6 +14,7 @@ __all__ = [
|
|
|
14
14
|
"TerminalInfo",
|
|
15
15
|
"AccountInfo",
|
|
16
16
|
"SymbolInfo",
|
|
17
|
+
"SymbolType",
|
|
17
18
|
"TickInfo",
|
|
18
19
|
"TradeRequest",
|
|
19
20
|
"OrderCheckResult",
|
|
@@ -70,27 +71,31 @@ class TimeFrame(Enum):
|
|
|
70
71
|
Rrepresent a time frame object
|
|
71
72
|
"""
|
|
72
73
|
|
|
73
|
-
M1 = "1m"
|
|
74
|
-
M2 = "2m"
|
|
75
|
-
M3 = "3m"
|
|
76
|
-
M4 = "4m"
|
|
77
|
-
M5 = "5m"
|
|
78
|
-
M6 = "6m"
|
|
79
|
-
M10 = "10m"
|
|
80
|
-
M12 = "12m"
|
|
81
|
-
M15 = "15m"
|
|
82
|
-
M20 = "20m"
|
|
83
|
-
M30 = "30m"
|
|
84
|
-
H1 = "1h"
|
|
85
|
-
H2 = "2h"
|
|
86
|
-
H3 = "3h"
|
|
87
|
-
H4 = "4h"
|
|
88
|
-
H6 = "6h"
|
|
89
|
-
H8 = "8h"
|
|
90
|
-
H12 = "12h"
|
|
91
|
-
D1 = "D1"
|
|
92
|
-
W1 = "W1"
|
|
93
|
-
MN1 = "MN1"
|
|
74
|
+
M1 = TIMEFRAMES["1m"]
|
|
75
|
+
M2 = TIMEFRAMES["2m"]
|
|
76
|
+
M3 = TIMEFRAMES["3m"]
|
|
77
|
+
M4 = TIMEFRAMES["4m"]
|
|
78
|
+
M5 = TIMEFRAMES["5m"]
|
|
79
|
+
M6 = TIMEFRAMES["6m"]
|
|
80
|
+
M10 = TIMEFRAMES["10m"]
|
|
81
|
+
M12 = TIMEFRAMES["12m"]
|
|
82
|
+
M15 = TIMEFRAMES["15m"]
|
|
83
|
+
M20 = TIMEFRAMES["20m"]
|
|
84
|
+
M30 = TIMEFRAMES["30m"]
|
|
85
|
+
H1 = TIMEFRAMES["1h"]
|
|
86
|
+
H2 = TIMEFRAMES["2h"]
|
|
87
|
+
H3 = TIMEFRAMES["3h"]
|
|
88
|
+
H4 = TIMEFRAMES["4h"]
|
|
89
|
+
H6 = TIMEFRAMES["6h"]
|
|
90
|
+
H8 = TIMEFRAMES["8h"]
|
|
91
|
+
H12 = TIMEFRAMES["12h"]
|
|
92
|
+
D1 = TIMEFRAMES["D1"]
|
|
93
|
+
W1 = TIMEFRAMES["W1"]
|
|
94
|
+
MN1 = TIMEFRAMES["MN1"]
|
|
95
|
+
|
|
96
|
+
def __str__(self):
|
|
97
|
+
"""Return the string representation of the time frame."""
|
|
98
|
+
return self.name
|
|
94
99
|
|
|
95
100
|
|
|
96
101
|
class TerminalInfo(NamedTuple):
|
|
@@ -263,6 +268,23 @@ class SymbolInfo(NamedTuple):
|
|
|
263
268
|
path: str
|
|
264
269
|
|
|
265
270
|
|
|
271
|
+
class SymbolType(Enum):
|
|
272
|
+
"""
|
|
273
|
+
Represents the type of a symbol.
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
FOREX = "FOREX" # Forex currency pairs
|
|
277
|
+
FUTURES = "FUTURES" # Futures contracts
|
|
278
|
+
STOCKS = "STOCKS" # Stocks and shares
|
|
279
|
+
BONDS = "BONDS" # Bonds
|
|
280
|
+
CRYPTO = "CRYPTO" # Cryptocurrencies
|
|
281
|
+
ETFs = "ETFs" # Exchange-Traded Funds
|
|
282
|
+
INDICES = "INDICES" # Market indices
|
|
283
|
+
COMMODITIES = "COMMODITIES" # Commodities
|
|
284
|
+
OPTIONS = "OPTIONS" # Options contracts
|
|
285
|
+
unknown = "UNKNOWN" # Unknown or unsupported type
|
|
286
|
+
|
|
287
|
+
|
|
266
288
|
class TickInfo(NamedTuple):
|
|
267
289
|
"""
|
|
268
290
|
Represents the last tick for the specified financial instrument.
|
|
@@ -465,10 +487,12 @@ class MT5TerminalError(Exception):
|
|
|
465
487
|
self.message = message
|
|
466
488
|
|
|
467
489
|
def __str__(self) -> str:
|
|
468
|
-
if self.message is None:
|
|
469
|
-
|
|
470
|
-
else:
|
|
471
|
-
|
|
490
|
+
# if self.message is None:
|
|
491
|
+
# return f"{self.__class__.__name__}"
|
|
492
|
+
# else:
|
|
493
|
+
# return f"{self.__class__.__name__}, {self.message}"
|
|
494
|
+
msg_str = str(self.message) if self.message is not None else ""
|
|
495
|
+
return f"{self.code} - {self.__class__.__name__}: {msg_str}"
|
|
472
496
|
|
|
473
497
|
|
|
474
498
|
class GenericFail(MT5TerminalError):
|
|
@@ -561,6 +585,21 @@ class InternalFailTimeout(InternalFailError):
|
|
|
561
585
|
super().__init__(MT5.RES_E_INTERNAL_FAIL_TIMEOUT, message)
|
|
562
586
|
|
|
563
587
|
|
|
588
|
+
RES_E_FAIL = 1 # Generic error
|
|
589
|
+
RES_E_INVALID_PARAMS = 2 # Invalid parameters
|
|
590
|
+
RES_E_NOT_FOUND = 3 # Not found
|
|
591
|
+
RES_E_INVALID_VERSION = 4 # Invalid version
|
|
592
|
+
RES_E_AUTH_FAILED = 5 # Authorization failed
|
|
593
|
+
RES_E_UNSUPPORTED = 6 # Unsupported method
|
|
594
|
+
RES_E_AUTO_TRADING_DISABLED = 7 # Autotrading disabled
|
|
595
|
+
|
|
596
|
+
# Actual internal error codes from MetaTrader5
|
|
597
|
+
RES_E_INTERNAL_FAIL_CONNECT = -10000
|
|
598
|
+
RES_E_INTERNAL_FAIL_INIT = -10001
|
|
599
|
+
RES_E_INTERNAL_FAIL_SEND = -10006
|
|
600
|
+
RES_E_INTERNAL_FAIL_RECEIVE = -10007
|
|
601
|
+
RES_E_INTERNAL_FAIL_TIMEOUT = -10008
|
|
602
|
+
|
|
564
603
|
# Dictionary to map error codes to exception classes
|
|
565
604
|
_ERROR_CODE_TO_EXCEPTION_ = {
|
|
566
605
|
MT5.RES_E_FAIL: GenericFail,
|
|
@@ -575,6 +614,18 @@ _ERROR_CODE_TO_EXCEPTION_ = {
|
|
|
575
614
|
MT5.RES_E_INTERNAL_FAIL_INIT: InternalFailInit,
|
|
576
615
|
MT5.RES_E_INTERNAL_FAIL_CONNECT: InternalFailConnect,
|
|
577
616
|
MT5.RES_E_INTERNAL_FAIL_TIMEOUT: InternalFailTimeout,
|
|
617
|
+
RES_E_FAIL: GenericFail,
|
|
618
|
+
RES_E_INVALID_PARAMS: InvalidParams,
|
|
619
|
+
RES_E_NOT_FOUND: HistoryNotFound,
|
|
620
|
+
RES_E_INVALID_VERSION: InvalidVersion,
|
|
621
|
+
RES_E_AUTH_FAILED: AuthFailed,
|
|
622
|
+
RES_E_UNSUPPORTED: UnsupportedMethod,
|
|
623
|
+
RES_E_AUTO_TRADING_DISABLED: AutoTradingDisabled,
|
|
624
|
+
RES_E_INTERNAL_FAIL_SEND: InternalFailSend,
|
|
625
|
+
RES_E_INTERNAL_FAIL_RECEIVE: InternalFailReceive,
|
|
626
|
+
RES_E_INTERNAL_FAIL_INIT: InternalFailInit,
|
|
627
|
+
RES_E_INTERNAL_FAIL_CONNECT: InternalFailConnect,
|
|
628
|
+
RES_E_INTERNAL_FAIL_TIMEOUT: InternalFailTimeout,
|
|
578
629
|
}
|
|
579
630
|
|
|
580
631
|
|
|
@@ -588,7 +639,10 @@ def raise_mt5_error(message: Optional[str] = None):
|
|
|
588
639
|
MT5TerminalError: A specific exception based on the error code.
|
|
589
640
|
"""
|
|
590
641
|
error = _ERROR_CODE_TO_EXCEPTION_.get(MT5.last_error()[0])
|
|
591
|
-
|
|
642
|
+
if error is not None:
|
|
643
|
+
raise Exception(f"{error(None)} {message or MT5.last_error()[1]}")
|
|
644
|
+
else:
|
|
645
|
+
raise Exception(f"{message or MT5.last_error()[1]}")
|
|
592
646
|
|
|
593
647
|
|
|
594
648
|
_ORDER_FILLING_TYPE_ = "https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties#enum_order_type_filling"
|
bbstrader/models/factors.py
CHANGED
|
@@ -28,8 +28,10 @@ def _download_and_process_data(source, tickers, start, end, tf, path, **kwargs):
|
|
|
28
28
|
end=end,
|
|
29
29
|
progress=False,
|
|
30
30
|
multi_level_index=False,
|
|
31
|
+
auto_adjust=True,
|
|
31
32
|
)
|
|
32
|
-
|
|
33
|
+
if "Adj Close" in data.columns:
|
|
34
|
+
data = data.drop(columns=["Adj Close"], axis=1)
|
|
33
35
|
elif source == "mt5":
|
|
34
36
|
start, end = pd.Timestamp(start), pd.Timestamp(end)
|
|
35
37
|
data = download_historical_data(
|
bbstrader/models/ml.py
CHANGED
|
@@ -250,12 +250,13 @@ class LightGBModel(object):
|
|
|
250
250
|
data = pd.concat(data)
|
|
251
251
|
data = (
|
|
252
252
|
data.rename(columns={s: s.lower().replace(" ", "_") for s in data.columns})
|
|
253
|
-
.drop(columns=["adj_close"])
|
|
254
253
|
.set_index("symbol", append=True)
|
|
255
254
|
.swaplevel()
|
|
256
255
|
.sort_index()
|
|
257
256
|
.dropna()
|
|
258
257
|
)
|
|
258
|
+
if "adj_close" in data.columns:
|
|
259
|
+
data = data.drop(columns=["adj_close"])
|
|
259
260
|
return data
|
|
260
261
|
|
|
261
262
|
def download_metadata(self, tickers):
|