bbstrader 0.3.5__py3-none-any.whl → 0.3.7__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 +11 -2
- bbstrader/__main__.py +6 -1
- bbstrader/apps/_copier.py +43 -40
- bbstrader/btengine/backtest.py +33 -28
- bbstrader/btengine/data.py +105 -81
- bbstrader/btengine/event.py +21 -22
- bbstrader/btengine/execution.py +51 -24
- bbstrader/btengine/performance.py +23 -12
- bbstrader/btengine/portfolio.py +40 -30
- bbstrader/btengine/scripts.py +13 -12
- bbstrader/btengine/strategy.py +396 -134
- bbstrader/compat.py +4 -3
- bbstrader/config.py +20 -36
- bbstrader/core/data.py +76 -48
- bbstrader/core/scripts.py +22 -21
- bbstrader/core/utils.py +13 -12
- bbstrader/metatrader/account.py +51 -26
- bbstrader/metatrader/analysis.py +30 -16
- bbstrader/metatrader/copier.py +75 -40
- bbstrader/metatrader/trade.py +29 -39
- bbstrader/metatrader/utils.py +5 -4
- bbstrader/models/nlp.py +83 -66
- bbstrader/trading/execution.py +45 -22
- bbstrader/tseries.py +158 -166
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.7.dist-info}/METADATA +7 -21
- bbstrader-0.3.7.dist-info/RECORD +62 -0
- bbstrader-0.3.7.dist-info/top_level.txt +3 -0
- docs/conf.py +56 -0
- tests/__init__.py +0 -0
- tests/engine/__init__.py +1 -0
- tests/engine/test_backtest.py +58 -0
- tests/engine/test_data.py +536 -0
- tests/engine/test_events.py +300 -0
- tests/engine/test_execution.py +219 -0
- tests/engine/test_portfolio.py +308 -0
- tests/metatrader/__init__.py +0 -0
- tests/metatrader/test_account.py +1769 -0
- tests/metatrader/test_rates.py +292 -0
- tests/metatrader/test_risk_management.py +700 -0
- tests/metatrader/test_trade.py +439 -0
- bbstrader-0.3.5.dist-info/RECORD +0 -49
- bbstrader-0.3.5.dist-info/top_level.txt +0 -1
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.7.dist-info}/WHEEL +0 -0
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.7.dist-info}/entry_points.txt +0 -0
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.7.dist-info}/licenses/LICENSE +0 -0
bbstrader/metatrader/account.py
CHANGED
|
@@ -157,22 +157,38 @@ def check_mt5_connection(
|
|
|
157
157
|
"""
|
|
158
158
|
Initialize the connection to the MetaTrader 5 terminal.
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
path : str, optional
|
|
163
|
+
Path to the MetaTrader 5 terminal executable file.
|
|
164
|
+
Defaults to ``None`` (e.g., ``"C:/Program Files/MetaTrader 5/terminal64.exe"``).
|
|
165
|
+
login : int, optional
|
|
166
|
+
The login ID of the trading account. Defaults to ``None``.
|
|
167
|
+
password : str, optional
|
|
168
|
+
The password of the trading account. Defaults to ``None``.
|
|
169
|
+
server : str, optional
|
|
170
|
+
The name of the trade server to which the client terminal is connected.
|
|
171
|
+
Defaults to ``None``.
|
|
172
|
+
timeout : int, optional
|
|
173
|
+
Connection timeout in milliseconds. Defaults to ``60_000``.
|
|
174
|
+
portable : bool, optional
|
|
175
|
+
If ``True``, the portable mode of the terminal is used.
|
|
176
|
+
Defaults to ``False``.
|
|
177
|
+
See: https://www.metatrader5.com/en/terminal/help/start_advanced/start#portable
|
|
178
|
+
|
|
179
|
+
Returns
|
|
180
|
+
-------
|
|
181
|
+
bool
|
|
182
|
+
``True`` if the connection is successfully established, otherwise ``False``.
|
|
183
|
+
|
|
184
|
+
Notes
|
|
185
|
+
-----
|
|
186
|
+
If you want to launch multiple terminal instances:
|
|
187
|
+
|
|
188
|
+
* First, launch each terminal in **portable mode**.
|
|
189
|
+
* See instructions: https://www.metatrader5.com/en/terminal/help/start_advanced/start#configuration_file
|
|
175
190
|
"""
|
|
191
|
+
|
|
176
192
|
if login is not None and server is not None:
|
|
177
193
|
account_info = mt5.account_info()
|
|
178
194
|
if account_info is not None:
|
|
@@ -201,9 +217,9 @@ def check_mt5_connection(
|
|
|
201
217
|
else:
|
|
202
218
|
init = mt5.initialize()
|
|
203
219
|
if not init:
|
|
204
|
-
raise_mt5_error(INIT_MSG)
|
|
220
|
+
raise_mt5_error(str(mt5.last_error()) + INIT_MSG)
|
|
205
221
|
except Exception:
|
|
206
|
-
raise_mt5_error(INIT_MSG)
|
|
222
|
+
raise_mt5_error(str(mt5.last_error()) + INIT_MSG)
|
|
207
223
|
return init
|
|
208
224
|
|
|
209
225
|
|
|
@@ -1209,17 +1225,26 @@ class Account(object):
|
|
|
1209
1225
|
def get_market_book(self, symbol: str) -> Tuple[BookInfo]:
|
|
1210
1226
|
"""
|
|
1211
1227
|
Get the Market Depth content for a specific symbol.
|
|
1212
|
-
Args:
|
|
1213
|
-
symbol (str): Financial instrument name. Required unnamed parameter.
|
|
1214
|
-
The symbol name should be specified in the same format as in the Market Watch window.
|
|
1215
|
-
|
|
1216
|
-
Returns:
|
|
1217
|
-
The Market Depth content as a tuple from BookInfo entries featuring order type, price and volume in lots.
|
|
1218
|
-
Return None in case of an error.
|
|
1219
1228
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1229
|
+
Parameters
|
|
1230
|
+
----------
|
|
1231
|
+
symbol : str
|
|
1232
|
+
Financial instrument name. The symbol should be specified in the
|
|
1233
|
+
same format as in the Market Watch window.
|
|
1234
|
+
|
|
1235
|
+
Returns
|
|
1236
|
+
-------
|
|
1237
|
+
tuple of BookInfo or None
|
|
1238
|
+
The Market Depth content as a tuple of ``BookInfo`` entries
|
|
1239
|
+
(order type, price, and volume in lots).
|
|
1240
|
+
Returns ``None`` in case of an error.
|
|
1241
|
+
|
|
1242
|
+
Raises
|
|
1243
|
+
------
|
|
1244
|
+
MT5TerminalError
|
|
1245
|
+
A specific exception based on the error code.
|
|
1222
1246
|
"""
|
|
1247
|
+
|
|
1223
1248
|
try:
|
|
1224
1249
|
book = mt5.market_book_get(symbol)
|
|
1225
1250
|
return (
|
bbstrader/metatrader/analysis.py
CHANGED
|
@@ -57,23 +57,37 @@ def display_volume_profile(
|
|
|
57
57
|
|
|
58
58
|
This function retrieves historical price and volume data for a given symbol and
|
|
59
59
|
plots a vertical volume profile chart showing the volume distribution across
|
|
60
|
-
price levels.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
price levels.
|
|
61
|
+
|
|
62
|
+
Highlights
|
|
63
|
+
----------
|
|
64
|
+
* **Point of Control (POC)**: Price level with the highest traded volume.
|
|
65
|
+
* **Value Area High (VAH)**: Upper bound of the value area.
|
|
66
|
+
* **Value Area Low (VAL)**: Lower bound of the value area.
|
|
67
|
+
* **Current Price**: Latest bid price from MetaTrader 5.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
symbol : str
|
|
72
|
+
Market symbol (e.g., ``"AAPL"``, ``"EURUSD"``).
|
|
73
|
+
path : str
|
|
74
|
+
Path to the historical data. See
|
|
75
|
+
``bbstrader.metatrader.account.check_mt5_connection()``.
|
|
76
|
+
timeframe : str, optional
|
|
77
|
+
Timeframe for each candle. Default is ``"1m"``.
|
|
78
|
+
bars : int, optional
|
|
79
|
+
Number of historical bars to fetch. Default is ``1440``.
|
|
80
|
+
bins : int, optional
|
|
81
|
+
Number of price bins for volume profile calculation. Default is ``100``.
|
|
82
|
+
va_percentage : float, optional
|
|
83
|
+
Percentage of total volume to define the value area. Default is ``0.7``.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
None
|
|
88
|
+
Displays a matplotlib chart of the volume profile.
|
|
76
89
|
"""
|
|
90
|
+
|
|
77
91
|
check_mt5_connection(path=path)
|
|
78
92
|
df = _get_data(symbol, TIMEFRAMES[timeframe], bars)
|
|
79
93
|
if df.empty:
|
bbstrader/metatrader/copier.py
CHANGED
|
@@ -1177,29 +1177,45 @@ def RunCopier(
|
|
|
1177
1177
|
shutdown_event=None,
|
|
1178
1178
|
log_queue=None,
|
|
1179
1179
|
):
|
|
1180
|
-
"""
|
|
1180
|
+
"""
|
|
1181
|
+
Initialize and run a TradeCopier instance in a single process.
|
|
1181
1182
|
|
|
1182
1183
|
This function serves as a straightforward wrapper to start a copying session
|
|
1183
1184
|
that handles one source account and one or more destination accounts
|
|
1184
|
-
|
|
1185
|
+
sequentially within the same thread. It does not create any new processes itself.
|
|
1185
1186
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1187
|
+
Use Cases
|
|
1188
|
+
---------
|
|
1189
|
+
* Simpler, command-line based use cases.
|
|
1190
|
+
* Scenarios where parallelism is not required.
|
|
1191
|
+
* As the target for ``RunMultipleCopier``, where each process handles a
|
|
1190
1192
|
full source-to-destinations session.
|
|
1191
1193
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1194
|
+
Parameters
|
|
1195
|
+
----------
|
|
1196
|
+
source : dict
|
|
1197
|
+
Configuration dictionary for the source account.
|
|
1198
|
+
destinations : list
|
|
1199
|
+
A list of configuration dictionaries, one for each
|
|
1200
|
+
destination account to be processed sequentially.
|
|
1201
|
+
sleeptime : float
|
|
1202
|
+
The time in seconds to wait after completing a full
|
|
1203
|
+
cycle through all destinations.
|
|
1204
|
+
start_time : str
|
|
1205
|
+
The time of day to start copying (e.g., ``"08:00"``).
|
|
1206
|
+
end_time : str
|
|
1207
|
+
The time of day to stop copying (e.g., ``"22:00"``).
|
|
1208
|
+
custom_logger : logging.Logger, optional
|
|
1209
|
+
An optional custom logger instance.
|
|
1210
|
+
shutdown_event : multiprocessing.Event, optional
|
|
1211
|
+
An event to signal shutdown.
|
|
1212
|
+
log_queue : multiprocessing.Queue, optional
|
|
1213
|
+
A queue for log messages.
|
|
1214
|
+
|
|
1215
|
+
Returns
|
|
1216
|
+
-------
|
|
1217
|
+
None
|
|
1218
|
+
Runs until stopped via ``shutdown_event`` or external interruption.
|
|
1203
1219
|
"""
|
|
1204
1220
|
copier = TradeCopier(
|
|
1205
1221
|
source,
|
|
@@ -1224,7 +1240,8 @@ def RunMultipleCopier(
|
|
|
1224
1240
|
custom_logger=None,
|
|
1225
1241
|
log_queue=None,
|
|
1226
1242
|
):
|
|
1227
|
-
"""
|
|
1243
|
+
"""
|
|
1244
|
+
Manage multiple, independent trade copying sessions in parallel.
|
|
1228
1245
|
|
|
1229
1246
|
This function acts as a high-level manager that takes a list of account
|
|
1230
1247
|
setups and creates a separate, dedicated process for each one. Each process
|
|
@@ -1232,28 +1249,46 @@ def RunMultipleCopier(
|
|
|
1232
1249
|
destination accounts.
|
|
1233
1250
|
|
|
1234
1251
|
The parallelism occurs at the **source account level**. Within each spawned
|
|
1235
|
-
process, the destinations for that source are handled sequentially by
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
accounts
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1252
|
+
process, the destinations for that source are handled sequentially by
|
|
1253
|
+
``RunCopier``.
|
|
1254
|
+
|
|
1255
|
+
Example
|
|
1256
|
+
-------
|
|
1257
|
+
An example ``accounts`` structure:
|
|
1258
|
+
|
|
1259
|
+
.. code-block:: python
|
|
1260
|
+
|
|
1261
|
+
accounts = [
|
|
1262
|
+
{"source": {...}, "destinations": [{...}, {...}]}, # -> Process 1
|
|
1263
|
+
{"source": {...}, "destinations": [{...}]} # -> Process 2
|
|
1264
|
+
]
|
|
1265
|
+
|
|
1266
|
+
Parameters
|
|
1267
|
+
----------
|
|
1268
|
+
accounts : list of dict
|
|
1269
|
+
A list of account configurations. Each item must be a dictionary with
|
|
1270
|
+
a ``source`` key and a ``destinations`` key.
|
|
1271
|
+
sleeptime : float, optional
|
|
1272
|
+
The sleep time passed down to each ``RunCopier`` process.
|
|
1273
|
+
start_delay : float, optional
|
|
1274
|
+
A delay in seconds between starting each new process.
|
|
1275
|
+
Helps prevent resource contention by staggering the initialization of
|
|
1276
|
+
multiple MetaTrader 5 terminals.
|
|
1277
|
+
start_time : str, optional
|
|
1278
|
+
The start time passed down to each ``RunCopier`` process.
|
|
1279
|
+
end_time : str, optional
|
|
1280
|
+
The end time passed down to each ``RunCopier`` process.
|
|
1281
|
+
shutdown_event : multiprocessing.Event, optional
|
|
1282
|
+
An event to signal shutdown to all child processes.
|
|
1283
|
+
custom_logger : logging.Logger, optional
|
|
1284
|
+
An optional custom logger instance.
|
|
1285
|
+
log_queue : multiprocessing.Queue, optional
|
|
1286
|
+
A queue for aggregating log messages from all child processes.
|
|
1287
|
+
|
|
1288
|
+
Returns
|
|
1289
|
+
-------
|
|
1290
|
+
None
|
|
1291
|
+
Runs until stopped via ``shutdown_event`` or external interruption.
|
|
1257
1292
|
"""
|
|
1258
1293
|
processes = []
|
|
1259
1294
|
|
bbstrader/metatrader/trade.py
CHANGED
|
@@ -95,27 +95,16 @@ class TradeSignal:
|
|
|
95
95
|
"""
|
|
96
96
|
Represents a trading signal generated by a trading system or strategy.
|
|
97
97
|
|
|
98
|
+
Notes
|
|
99
|
+
-----
|
|
98
100
|
Attributes:
|
|
99
|
-
id (int):
|
|
100
|
-
A unique identifier for the trade signal or the strategy.
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Must be an instance of the `TradeAction` enum (e.g., BUY, SELL).
|
|
109
|
-
|
|
110
|
-
price (float, optional):
|
|
111
|
-
The price at which the trade should be executed.
|
|
112
|
-
|
|
113
|
-
stoplimit (float, optional):
|
|
114
|
-
A stop-limit price for the trade.
|
|
115
|
-
Must not be set without specifying a price.
|
|
116
|
-
|
|
117
|
-
comment (str, optional):
|
|
118
|
-
An optional comment or description related to the trade signal.
|
|
102
|
+
- id (int): A unique identifier for the trade signal or the strategy.
|
|
103
|
+
- symbol (str): The trading symbol (e.g., stock ticker, forex pair, crypto asset).
|
|
104
|
+
- action (TradeAction): The trading action to perform. Must be an instance of the ``TradeAction`` enum (e.g., BUY, SELL).
|
|
105
|
+
- price (float, optional): The price at which the trade should be executed.
|
|
106
|
+
- stoplimit (float, optional): A stop-limit price for the trade. Must not be set without specifying a price.
|
|
107
|
+
- comment (str, optional): An optional comment or description related to the trade signal.
|
|
119
108
|
"""
|
|
120
109
|
|
|
121
110
|
id: int
|
|
@@ -781,14 +770,14 @@ class Trade(RiskManagement):
|
|
|
781
770
|
self.check_order(request)
|
|
782
771
|
result = self.send_order(request)
|
|
783
772
|
except Exception as e:
|
|
784
|
-
msg = trade_retcode_message(result.retcode)
|
|
773
|
+
msg = trade_retcode_message(result.retcode) if result else "N/A"
|
|
785
774
|
LOGGER.error(f"Trade Order Request, {msg}{addtionnal}, {e}")
|
|
786
775
|
if result and result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
787
776
|
if result.retcode == Mt5.TRADE_RETCODE_INVALID_FILL: # 10030
|
|
788
777
|
for fill in FILLING_TYPE:
|
|
789
778
|
request["type_filling"] = fill
|
|
790
779
|
result = self.send_order(request)
|
|
791
|
-
if result and
|
|
780
|
+
if result and result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
792
781
|
break
|
|
793
782
|
elif result.retcode == Mt5.TRADE_RETCODE_INVALID_VOLUME: # 10014
|
|
794
783
|
new_volume = int(request["volume"])
|
|
@@ -812,14 +801,14 @@ class Trade(RiskManagement):
|
|
|
812
801
|
self.check_order(request)
|
|
813
802
|
result = self.send_order(request)
|
|
814
803
|
except Exception as e:
|
|
815
|
-
msg = trade_retcode_message(result.retcode)
|
|
804
|
+
msg = trade_retcode_message(result.retcode) if result else "N/A"
|
|
816
805
|
LOGGER.error(f"Trade Order Request, {msg}{addtionnal}, {e}")
|
|
817
|
-
if result and
|
|
806
|
+
if result and result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
818
807
|
break
|
|
819
808
|
tries += 1
|
|
820
809
|
# Print the result
|
|
821
|
-
if result and
|
|
822
|
-
msg = trade_retcode_message(result.retcode)
|
|
810
|
+
if result and result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
811
|
+
msg = trade_retcode_message(result.retcode)
|
|
823
812
|
LOGGER.info(f"Trade Order {msg}{addtionnal}")
|
|
824
813
|
if type != "BMKT" or type != "SMKT":
|
|
825
814
|
self.opened_orders.append(result.order)
|
|
@@ -855,7 +844,7 @@ class Trade(RiskManagement):
|
|
|
855
844
|
LOGGER.info(pos_info)
|
|
856
845
|
return True
|
|
857
846
|
else:
|
|
858
|
-
msg = trade_retcode_message(result.retcode)
|
|
847
|
+
msg = trade_retcode_message(result.retcode) if result else "N/A"
|
|
859
848
|
LOGGER.error(
|
|
860
849
|
f"Unable to Open Position, RETCODE={result.retcode}: {msg}{addtionnal}"
|
|
861
850
|
)
|
|
@@ -1332,10 +1321,10 @@ class Trade(RiskManagement):
|
|
|
1332
1321
|
self.check_order(request)
|
|
1333
1322
|
result = self.send_order(request)
|
|
1334
1323
|
except Exception as e:
|
|
1335
|
-
msg = trade_retcode_message(result.retcode)
|
|
1324
|
+
msg = trade_retcode_message(result.retcode) if result else "N/A"
|
|
1336
1325
|
LOGGER.error(f"Break-Even Order Request, {msg}{addtionnal}, Error: {e}")
|
|
1337
|
-
if result and
|
|
1338
|
-
msg = trade_retcode_message(result.retcode)
|
|
1326
|
+
if result and result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
1327
|
+
msg = trade_retcode_message(result.retcode)
|
|
1339
1328
|
if result.retcode != Mt5.TRADE_RETCODE_NO_CHANGES:
|
|
1340
1329
|
LOGGER.error(
|
|
1341
1330
|
f"Break-Even Order Request, Position: #{tiket}, RETCODE={result.retcode}: {msg}{addtionnal}"
|
|
@@ -1350,15 +1339,15 @@ class Trade(RiskManagement):
|
|
|
1350
1339
|
self.check_order(request)
|
|
1351
1340
|
result = self.send_order(request)
|
|
1352
1341
|
except Exception as e:
|
|
1353
|
-
msg = trade_retcode_message(result.retcode)
|
|
1342
|
+
msg = trade_retcode_message(result.retcode) if result else "N/A"
|
|
1354
1343
|
LOGGER.error(
|
|
1355
1344
|
f"Break-Even Order Request, {msg}{addtionnal}, Error: {e}"
|
|
1356
1345
|
)
|
|
1357
|
-
if result and
|
|
1346
|
+
if result and result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
1358
1347
|
break
|
|
1359
1348
|
tries += 1
|
|
1360
|
-
if result and
|
|
1361
|
-
msg = trade_retcode_message(result.retcode)
|
|
1349
|
+
if result and result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
1350
|
+
msg = trade_retcode_message(result.retcode)
|
|
1362
1351
|
LOGGER.info(f"Break-Even Order {msg}{addtionnal}")
|
|
1363
1352
|
info = f"Stop loss set to Break-even, Position: #{tiket}, Symbol: {self.symbol}, Price: @{round(price, 5)}"
|
|
1364
1353
|
LOGGER.info(info)
|
|
@@ -1434,7 +1423,7 @@ class Trade(RiskManagement):
|
|
|
1434
1423
|
"""
|
|
1435
1424
|
ticket = request[type]
|
|
1436
1425
|
addtionnal = f", SYMBOL={self.symbol}"
|
|
1437
|
-
result = None
|
|
1426
|
+
result = None
|
|
1438
1427
|
try:
|
|
1439
1428
|
self.check_order(request)
|
|
1440
1429
|
result = self.send_order(request)
|
|
@@ -1839,15 +1828,16 @@ class Trade(RiskManagement):
|
|
|
1839
1828
|
|
|
1840
1829
|
def sleep_time(self, weekend=False):
|
|
1841
1830
|
if weekend:
|
|
1842
|
-
#
|
|
1843
|
-
|
|
1831
|
+
# calculate number of minute from now and monday start
|
|
1832
|
+
multiplyer = {"friday": 3, "saturday": 2, "sunday": 1}
|
|
1833
|
+
current_time = datetime.strptime(self.current_time(), "%H:%M")
|
|
1844
1834
|
monday_time = datetime.strptime(self.start, "%H:%M")
|
|
1845
|
-
intra_day_diff = (monday_time -
|
|
1846
|
-
inter_day_diff =
|
|
1835
|
+
intra_day_diff = (monday_time - current_time).total_seconds() // 60
|
|
1836
|
+
inter_day_diff = multiplyer[datetime.now().strftime("%A").lower()] * 24 * 60
|
|
1847
1837
|
total_minutes = inter_day_diff + intra_day_diff
|
|
1848
1838
|
return total_minutes
|
|
1849
1839
|
else:
|
|
1850
|
-
#
|
|
1840
|
+
# calculate number of minute from the end to the start
|
|
1851
1841
|
start = datetime.strptime(self.start, "%H:%M")
|
|
1852
1842
|
end = datetime.strptime(self.current_time(), "%H:%M")
|
|
1853
1843
|
minutes = (end - start).total_seconds() // 60
|
bbstrader/metatrader/utils.py
CHANGED
|
@@ -607,7 +607,8 @@ class AutoTradingDisabled(MT5TerminalError):
|
|
|
607
607
|
class InternalFailError(MT5TerminalError):
|
|
608
608
|
"""Base exception class for internal IPC errors."""
|
|
609
609
|
|
|
610
|
-
|
|
610
|
+
def __init__(self, code, message):
|
|
611
|
+
super().__init__(code, message)
|
|
611
612
|
|
|
612
613
|
|
|
613
614
|
class InternalFailSend(InternalFailError):
|
|
@@ -700,9 +701,9 @@ def raise_mt5_error(message: Optional[str] = None):
|
|
|
700
701
|
"""
|
|
701
702
|
if message and isinstance(message, Exception):
|
|
702
703
|
message = str(message)
|
|
703
|
-
|
|
704
|
-
if
|
|
705
|
-
raise
|
|
704
|
+
exception = _ERROR_CODE_TO_EXCEPTION_.get(MT5.last_error()[0])
|
|
705
|
+
if exception is not None:
|
|
706
|
+
raise exception(f"{message or MT5.last_error()[1]}")
|
|
706
707
|
else:
|
|
707
708
|
raise Exception(f"{message or MT5.last_error()[1]}")
|
|
708
709
|
|