bbstrader 0.3.5__tar.gz → 0.3.6__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.
Potentially problematic release.
This version of bbstrader might be problematic. Click here for more details.
- {bbstrader-0.3.5/bbstrader.egg-info → bbstrader-0.3.6}/PKG-INFO +7 -21
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/__init__.py +10 -1
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/__main__.py +5 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/apps/_copier.py +3 -3
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/strategy.py +113 -38
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/account.py +51 -26
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/analysis.py +30 -16
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/copier.py +75 -40
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/trade.py +29 -39
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/utils.py +5 -4
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/models/nlp.py +83 -66
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/trading/execution.py +39 -22
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/tseries.py +103 -127
- {bbstrader-0.3.5 → bbstrader-0.3.6/bbstrader.egg-info}/PKG-INFO +7 -21
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader.egg-info/SOURCES.txt +15 -1
- bbstrader-0.3.6/bbstrader.egg-info/top_level.txt +6 -0
- bbstrader-0.3.6/docs/conf.py +56 -0
- bbstrader-0.3.6/pyproject.toml +51 -0
- bbstrader-0.3.6/setup.py +5 -0
- bbstrader-0.3.6/tests/__init__.py +0 -0
- bbstrader-0.3.6/tests/engine/__init__.py +1 -0
- bbstrader-0.3.6/tests/engine/test_backtest.py +58 -0
- bbstrader-0.3.6/tests/engine/test_data.py +536 -0
- bbstrader-0.3.6/tests/engine/test_events.py +300 -0
- bbstrader-0.3.6/tests/engine/test_execution.py +219 -0
- bbstrader-0.3.6/tests/engine/test_portfolio.py +307 -0
- bbstrader-0.3.6/tests/metatrader/__init__.py +0 -0
- bbstrader-0.3.6/tests/metatrader/test_account.py +1769 -0
- bbstrader-0.3.6/tests/metatrader/test_rates.py +292 -0
- bbstrader-0.3.6/tests/metatrader/test_risk_management.py +700 -0
- bbstrader-0.3.6/tests/metatrader/test_trade.py +439 -0
- bbstrader-0.3.5/bbstrader.egg-info/top_level.txt +0 -1
- bbstrader-0.3.5/setup.py +0 -101
- {bbstrader-0.3.5 → bbstrader-0.3.6}/LICENSE +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/MANIFEST.in +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/README.md +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/apps/__init__.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/__init__.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/backtest.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/data.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/event.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/execution.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/performance.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/portfolio.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/btengine/scripts.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/compat.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/config.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/core/__init__.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/core/data.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/core/scripts.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/core/utils.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/ibkr/__init__.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/ibkr/utils.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/__init__.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/rates.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/risk.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/metatrader/scripts.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/models/__init__.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/models/factors.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/models/ml.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/models/optimization.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/models/portfolio.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/models/risk.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/trading/__init__.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/trading/scripts.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/trading/strategies.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader/trading/utils.py +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader.egg-info/dependency_links.txt +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader.egg-info/entry_points.txt +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/bbstrader.egg-info/requires.txt +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/requirements.txt +0 -0
- {bbstrader-0.3.5 → bbstrader-0.3.6}/setup.cfg +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bbstrader
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Simplified Investment & Trading Toolkit
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
License: MIT
|
|
5
|
+
Author-email: Bertin Balouki SIMYELI <bertin@bbstrader.com>
|
|
6
|
+
Maintainer-email: Bertin Balouki SIMYELI <bertin@bbstrader.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/bbalouki/bbstrader
|
|
9
|
+
Project-URL: Download, https://pypi.org/project/bbstrader/
|
|
11
10
|
Project-URL: Documentation, https://bbstrader.readthedocs.io/en/latest/
|
|
12
11
|
Project-URL: Source Code, https://github.com/bbalouki/bbstrader
|
|
13
12
|
Keywords: Finance,Toolkit,Financial,Analysis,Fundamental,Quantitative,Database,Equities,Currencies,Economics,ETFs,Funds,Indices,Moneymarkets,Commodities,Futures,CFDs,Derivatives,Trading,Investing,Portfolio,Optimization,Performance
|
|
@@ -19,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
18
|
Classifier: Operating System :: Microsoft :: Windows
|
|
20
19
|
Classifier: Operating System :: POSIX :: Linux
|
|
21
20
|
Classifier: Operating System :: MacOS
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: alphalens-reloaded>=0.4.6
|
|
@@ -58,21 +58,7 @@ Requires-Dist: vaderSentiment>=3.3.2
|
|
|
58
58
|
Requires-Dist: yfinance>=0.2.65
|
|
59
59
|
Provides-Extra: mt5
|
|
60
60
|
Requires-Dist: MetaTrader5; extra == "mt5"
|
|
61
|
-
Dynamic: author
|
|
62
|
-
Dynamic: author-email
|
|
63
|
-
Dynamic: classifier
|
|
64
|
-
Dynamic: description
|
|
65
|
-
Dynamic: description-content-type
|
|
66
|
-
Dynamic: download-url
|
|
67
|
-
Dynamic: home-page
|
|
68
|
-
Dynamic: keywords
|
|
69
|
-
Dynamic: license
|
|
70
61
|
Dynamic: license-file
|
|
71
|
-
Dynamic: maintainer
|
|
72
|
-
Dynamic: project-url
|
|
73
|
-
Dynamic: provides-extra
|
|
74
|
-
Dynamic: requires-dist
|
|
75
|
-
Dynamic: summary
|
|
76
62
|
|
|
77
63
|
# Simplified Investment & Trading Toolkit
|
|
78
64
|
[](https://bbstrader.readthedocs.io/en/latest/?badge=latest)
|
|
@@ -7,7 +7,14 @@ __author__ = "Bertin Balouki SIMYELI"
|
|
|
7
7
|
__copyright__ = "2023-2025 Bertin Balouki SIMYELI"
|
|
8
8
|
__email__ = "bertin@bbstrader.com"
|
|
9
9
|
__license__ = "MIT"
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
__version__ = version("bbstrader")
|
|
15
|
+
except PackageNotFoundError:
|
|
16
|
+
__version__ = "unknown"
|
|
17
|
+
|
|
11
18
|
|
|
12
19
|
from bbstrader import compat # noqa: F401
|
|
13
20
|
from bbstrader import core # noqa: F401
|
|
@@ -17,3 +24,5 @@ from bbstrader import models # noqa: F401
|
|
|
17
24
|
from bbstrader import trading # noqa: F401
|
|
18
25
|
from bbstrader import tseries # noqa: F401
|
|
19
26
|
from bbstrader.config import config_logger # noqa: F401
|
|
27
|
+
|
|
28
|
+
version = __version__
|
|
@@ -10,6 +10,7 @@ from bbstrader.btengine.scripts import backtest
|
|
|
10
10
|
from bbstrader.core.scripts import send_news_feed
|
|
11
11
|
from bbstrader.metatrader.scripts import copy_trades
|
|
12
12
|
from bbstrader.trading.scripts import execute_strategy
|
|
13
|
+
from . import __author__, __version__
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class _Module(Enum):
|
|
@@ -50,6 +51,10 @@ def main():
|
|
|
50
51
|
if ("-h" in sys.argv or "--help" in sys.argv) and args.run is None:
|
|
51
52
|
print(Fore.WHITE + USAGE_TEXT)
|
|
52
53
|
sys.exit(0)
|
|
54
|
+
if ("-v" in sys.argv or "--version" in sys.argv):
|
|
55
|
+
print(Fore.GREEN + f"bbstrader version {__version__}")
|
|
56
|
+
print(Fore.WHITE + f"bbstrader maintained and supported by {__author__}")
|
|
57
|
+
sys.exit(0)
|
|
53
58
|
try:
|
|
54
59
|
match args.run:
|
|
55
60
|
case _Module.COPIER.value:
|
|
@@ -431,7 +431,7 @@ class TradeCopierApp(object):
|
|
|
431
431
|
or not dest["path"].get()
|
|
432
432
|
):
|
|
433
433
|
messagebox.showerror(
|
|
434
|
-
"Error", f"Destination account {i+1} details are incomplete."
|
|
434
|
+
"Error", f"Destination account {i + 1} details are incomplete."
|
|
435
435
|
)
|
|
436
436
|
return False
|
|
437
437
|
if (
|
|
@@ -440,7 +440,7 @@ class TradeCopierApp(object):
|
|
|
440
440
|
):
|
|
441
441
|
messagebox.showerror(
|
|
442
442
|
"Error",
|
|
443
|
-
f"Value is required for mode '{dest['mode'].get()}' in Destination {i+1}.",
|
|
443
|
+
f"Value is required for mode '{dest['mode'].get()}' in Destination {i + 1}.",
|
|
444
444
|
)
|
|
445
445
|
return False
|
|
446
446
|
try:
|
|
@@ -451,7 +451,7 @@ class TradeCopierApp(object):
|
|
|
451
451
|
except ValueError:
|
|
452
452
|
messagebox.showerror(
|
|
453
453
|
"Error",
|
|
454
|
-
f"Invalid Value or Slippage for Destination {i+1}. "
|
|
454
|
+
f"Invalid Value or Slippage for Destination {i + 1}. "
|
|
455
455
|
"Must be a number (Slippage must be an integer).",
|
|
456
456
|
)
|
|
457
457
|
return False
|
|
@@ -2,7 +2,7 @@ import string
|
|
|
2
2
|
from abc import ABCMeta, abstractmethod
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from queue import Queue
|
|
5
|
-
from typing import Dict, List, Literal, Union
|
|
5
|
+
from typing import Callable, Dict, List, Literal, Union
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import pandas as pd
|
|
@@ -11,19 +11,19 @@ from loguru import logger
|
|
|
11
11
|
|
|
12
12
|
from bbstrader.btengine.data import DataHandler
|
|
13
13
|
from bbstrader.btengine.event import Events, FillEvent, SignalEvent
|
|
14
|
-
from bbstrader.metatrader.trade import generate_signal, TradeAction
|
|
15
14
|
from bbstrader.config import BBSTRADER_DIR
|
|
16
15
|
from bbstrader.metatrader import (
|
|
17
16
|
Account,
|
|
18
17
|
AdmiralMarktsGroup,
|
|
19
18
|
MetaQuotes,
|
|
20
19
|
PepperstoneGroupLimited,
|
|
21
|
-
TradeOrder,
|
|
22
20
|
Rates,
|
|
23
|
-
|
|
21
|
+
SymbolType,
|
|
22
|
+
TradeOrder,
|
|
23
|
+
TradeSignal,
|
|
24
24
|
TradingMode,
|
|
25
|
-
SymbolType
|
|
26
25
|
)
|
|
26
|
+
from bbstrader.metatrader.trade import TradeAction, generate_signal
|
|
27
27
|
from bbstrader.models.optimization import optimized_weights
|
|
28
28
|
|
|
29
29
|
__all__ = ["Strategy", "MT5Strategy"]
|
|
@@ -79,10 +79,11 @@ class MT5Strategy(Strategy):
|
|
|
79
79
|
It is recommanded that every strategy specfic method to be a private method
|
|
80
80
|
in order to avoid naming collusion.
|
|
81
81
|
"""
|
|
82
|
+
|
|
82
83
|
tf: str
|
|
83
84
|
id: int
|
|
84
85
|
ID: int
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
max_trades: Dict[str, int]
|
|
87
88
|
risk_budget: Dict[str, float] | str | None
|
|
88
89
|
|
|
@@ -116,8 +117,10 @@ class MT5Strategy(Strategy):
|
|
|
116
117
|
self.symbols = symbol_list
|
|
117
118
|
self.mode = mode
|
|
118
119
|
if self.mode not in [TradingMode.BACKTEST, TradingMode.LIVE]:
|
|
119
|
-
raise ValueError(
|
|
120
|
-
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"Mode must be an instance of {type(TradingMode)} not {type(self.mode)}"
|
|
122
|
+
)
|
|
123
|
+
|
|
121
124
|
self.risk_budget = self._check_risk_budget(**kwargs)
|
|
122
125
|
|
|
123
126
|
self.max_trades = kwargs.get("max_trades", {s: 1 for s in self.symbols})
|
|
@@ -189,7 +192,7 @@ class MT5Strategy(Strategy):
|
|
|
189
192
|
def _initialize_portfolio(self):
|
|
190
193
|
self._orders = {}
|
|
191
194
|
self._positions = {}
|
|
192
|
-
self._trades =
|
|
195
|
+
self._trades = {}
|
|
193
196
|
for symbol in self.symbols:
|
|
194
197
|
self._positions[symbol] = {}
|
|
195
198
|
self._orders[symbol] = {}
|
|
@@ -254,30 +257,40 @@ class MT5Strategy(Strategy):
|
|
|
254
257
|
def signal(self, signal: int, symbol: str) -> TradeSignal:
|
|
255
258
|
"""
|
|
256
259
|
Generate a ``TradeSignal`` object based on the signal value.
|
|
257
|
-
Args:
|
|
258
|
-
signal : An integer value representing the signal type:
|
|
259
|
-
0: BUY
|
|
260
|
-
1: SELL
|
|
261
|
-
2: EXIT_LONG
|
|
262
|
-
3: EXIT_SHORT
|
|
263
|
-
4: EXIT_ALL_POSITIONS
|
|
264
|
-
5: EXIT_ALL_ORDERS
|
|
265
|
-
6: EXIT_STOP
|
|
266
|
-
7: EXIT_LIMIT
|
|
267
|
-
|
|
268
|
-
symbol : The symbol for the trade.
|
|
269
|
-
|
|
270
|
-
Returns:
|
|
271
|
-
TradeSignal : A ``TradeSignal`` object representing the trade signal.
|
|
272
|
-
|
|
273
|
-
Note:
|
|
274
|
-
This generate only common signals. For more complex signals, use `generate_signal` directly.
|
|
275
260
|
|
|
276
|
-
|
|
277
|
-
|
|
261
|
+
Parameters
|
|
262
|
+
----------
|
|
263
|
+
signal : int
|
|
264
|
+
An integer value representing the signal type:
|
|
265
|
+
* 0: BUY
|
|
266
|
+
* 1: SELL
|
|
267
|
+
* 2: EXIT_LONG
|
|
268
|
+
* 3: EXIT_SHORT
|
|
269
|
+
* 4: EXIT_ALL_POSITIONS
|
|
270
|
+
* 5: EXIT_ALL_ORDERS
|
|
271
|
+
* 6: EXIT_STOP
|
|
272
|
+
* 7: EXIT_LIMIT
|
|
273
|
+
symbol : str
|
|
274
|
+
The symbol for the trade.
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
TradeSignal
|
|
279
|
+
A ``TradeSignal`` object representing the trade signal.
|
|
280
|
+
|
|
281
|
+
Raises
|
|
282
|
+
------
|
|
283
|
+
ValueError
|
|
284
|
+
If the signal value is not between 0 and 7.
|
|
285
|
+
|
|
286
|
+
Notes
|
|
287
|
+
-----
|
|
288
|
+
This generates only common signals. For more complex signals, use
|
|
289
|
+
``generate_signal`` directly.
|
|
278
290
|
"""
|
|
291
|
+
|
|
279
292
|
signal_id = getattr(self, "id", None) or getattr(self, "ID")
|
|
280
|
-
|
|
293
|
+
|
|
281
294
|
match signal:
|
|
282
295
|
case 0:
|
|
283
296
|
return generate_signal(signal_id, symbol, TradeAction.BUY)
|
|
@@ -288,7 +301,9 @@ class MT5Strategy(Strategy):
|
|
|
288
301
|
case 3:
|
|
289
302
|
return generate_signal(signal_id, symbol, TradeAction.EXIT_SHORT)
|
|
290
303
|
case 4:
|
|
291
|
-
return generate_signal(
|
|
304
|
+
return generate_signal(
|
|
305
|
+
signal_id, symbol, TradeAction.EXIT_ALL_POSITIONS
|
|
306
|
+
)
|
|
292
307
|
case 5:
|
|
293
308
|
return generate_signal(signal_id, symbol, TradeAction.EXIT_ALL_ORDERS)
|
|
294
309
|
case 6:
|
|
@@ -296,8 +311,64 @@ class MT5Strategy(Strategy):
|
|
|
296
311
|
case 7:
|
|
297
312
|
return generate_signal(signal_id, symbol, TradeAction.EXIT_LIMIT)
|
|
298
313
|
case _:
|
|
299
|
-
raise ValueError(
|
|
314
|
+
raise ValueError(
|
|
315
|
+
f"Invalid signal value: {signal}. Must be an integer between 0 and 7."
|
|
316
|
+
)
|
|
300
317
|
|
|
318
|
+
def send_trade_repport(self, perf_analyzer: Callable, **kwargs):
|
|
319
|
+
"""
|
|
320
|
+
Generates and sends a trade report message containing performance metrics for the current strategy.
|
|
321
|
+
This method retrieves the trade history for the current account, filters it by the strategy's ID,
|
|
322
|
+
computes performance metrics using the provided `perf_analyzer` callable, and formats the results
|
|
323
|
+
into a message. The message includes account information, strategy details, a timestamp, and
|
|
324
|
+
performance metrics. The message is then sent via Telegram using the specified bot token and chat ID.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
perf_analyzer (Callable): A function or callable object that takes the filtered trade history
|
|
328
|
+
(as a DataFrame) and additional keyword arguments, and returns a DataFrame of performance metrics.
|
|
329
|
+
**kwargs: Additional keyword arguments, which may include
|
|
330
|
+
- Any other param requires by ``perf_analyzer``
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
from bbstrader.trading.utils import send_message
|
|
334
|
+
|
|
335
|
+
history = self.account.get_trades_history()
|
|
336
|
+
if history is None:
|
|
337
|
+
return
|
|
338
|
+
|
|
339
|
+
ID = getattr(self, "id", None) or getattr(self, "ID")
|
|
340
|
+
history = history[history["magic"] == ID]
|
|
341
|
+
performance = perf_analyzer(history, **kwargs)
|
|
342
|
+
|
|
343
|
+
account = self.kwargs.get("account", "MT5 Account")
|
|
344
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
345
|
+
|
|
346
|
+
header = (
|
|
347
|
+
f"==== TRADE REPORT =====\n\n"
|
|
348
|
+
f"ACCOUNT: {account}\n"
|
|
349
|
+
f"STRATEGY: {self.NAME}\n"
|
|
350
|
+
f"ID: {ID}\n"
|
|
351
|
+
f"DESCRIPTION: {self.DESCRIPTION}\n"
|
|
352
|
+
f"TIMESTAMP: {timestamp}\n\n"
|
|
353
|
+
f"📊 PERFORMANCE:\n"
|
|
354
|
+
)
|
|
355
|
+
metrics = performance.iloc[0].to_dict()
|
|
356
|
+
|
|
357
|
+
lines = []
|
|
358
|
+
for key, value in metrics.items():
|
|
359
|
+
if isinstance(value, float):
|
|
360
|
+
value = round(value, 4)
|
|
361
|
+
lines.append(f"{key:<15}: {value}")
|
|
362
|
+
|
|
363
|
+
performance_str = "\n".join(lines)
|
|
364
|
+
message = f"{header}{performance_str}"
|
|
365
|
+
|
|
366
|
+
send_message(
|
|
367
|
+
message=message,
|
|
368
|
+
telegram=True,
|
|
369
|
+
token=self.kwargs.get("bot_token"),
|
|
370
|
+
chat_id=self.kwargs.get("chat_id"),
|
|
371
|
+
)
|
|
301
372
|
|
|
302
373
|
def perform_period_end_checks(self, *args, **kwargs):
|
|
303
374
|
"""
|
|
@@ -831,8 +902,10 @@ class MT5Strategy(Strategy):
|
|
|
831
902
|
)
|
|
832
903
|
return prices
|
|
833
904
|
return np.array([])
|
|
834
|
-
|
|
835
|
-
def get_active_orders(
|
|
905
|
+
|
|
906
|
+
def get_active_orders(
|
|
907
|
+
self, symbol: str, strategy_id: int, order_type: int = None
|
|
908
|
+
) -> List[TradeOrder]:
|
|
836
909
|
"""
|
|
837
910
|
Get the active orders for a given symbol and strategy.
|
|
838
911
|
|
|
@@ -850,7 +923,9 @@ class MT5Strategy(Strategy):
|
|
|
850
923
|
Returns:
|
|
851
924
|
List[TradeOrder] : A list of active orders for the given symbol and strategy.
|
|
852
925
|
"""
|
|
853
|
-
orders = [
|
|
926
|
+
orders = [
|
|
927
|
+
o for o in self.orders if o.symbol == symbol and o.magic == strategy_id
|
|
928
|
+
]
|
|
854
929
|
if order_type is not None and len(orders) > 0:
|
|
855
930
|
orders = [o for o in orders if o.type == order_type]
|
|
856
931
|
return orders
|
|
@@ -866,7 +941,7 @@ class MT5Strategy(Strategy):
|
|
|
866
941
|
elif len(prices) in range(2, self.max_trades[asset] + 1):
|
|
867
942
|
price = np.mean(prices)
|
|
868
943
|
if price is not None:
|
|
869
|
-
if position == 0:
|
|
944
|
+
if position == 0:
|
|
870
945
|
return self.calculate_pct_change(ask, price) >= th
|
|
871
946
|
elif position == 1:
|
|
872
947
|
return self.calculate_pct_change(bid, price) <= -th
|
|
@@ -920,7 +995,7 @@ class MT5Strategy(Strategy):
|
|
|
920
995
|
Returns:
|
|
921
996
|
mt5_equivalent : The MetaTrader 5 equivalent symbols for the symbols in the list.
|
|
922
997
|
"""
|
|
923
|
-
|
|
998
|
+
|
|
924
999
|
account = Account(**kwargs)
|
|
925
1000
|
mt5_symbols = account.get_symbols(symbol_type=symbol_type)
|
|
926
1001
|
mt5_equivalent = []
|
|
@@ -933,7 +1008,7 @@ class MT5Strategy(Strategy):
|
|
|
933
1008
|
mt5_equivalent.append(s)
|
|
934
1009
|
|
|
935
1010
|
def _get_pepperstone_symbols():
|
|
936
|
-
|
|
1011
|
+
for s in mt5_symbols:
|
|
937
1012
|
for symbol in symbols:
|
|
938
1013
|
if s.split(".")[0] == symbol:
|
|
939
1014
|
mt5_equivalent.append(s)
|
|
@@ -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 (
|
|
@@ -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:
|