bbstrader 0.2.97__tar.gz → 0.2.99__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.2.97/bbstrader.egg-info → bbstrader-0.2.99}/PKG-INFO +12 -16
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/__main__.py +1 -1
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/backtest.py +7 -7
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/event.py +12 -4
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/execution.py +3 -3
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/portfolio.py +3 -3
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/strategy.py +12 -5
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/account.py +30 -6
- bbstrader-0.2.99/bbstrader/metatrader/analysis.py +98 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/copier.py +77 -57
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/trade.py +82 -118
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/utils.py +16 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/factors.py +97 -97
- bbstrader-0.2.99/bbstrader/trading/execution.py +977 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/scripts.py +10 -11
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/strategies.py +13 -5
- {bbstrader-0.2.97 → bbstrader-0.2.99/bbstrader.egg-info}/PKG-INFO +12 -16
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/SOURCES.txt +1 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/requires.txt +11 -15
- {bbstrader-0.2.97 → bbstrader-0.2.99}/requirements.txt +11 -16
- {bbstrader-0.2.97 → bbstrader-0.2.99}/setup.py +1 -1
- bbstrader-0.2.97/bbstrader/trading/execution.py +0 -857
- {bbstrader-0.2.97 → bbstrader-0.2.99}/LICENSE +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/MANIFEST.in +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/README.md +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/__ini__.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/__init__.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/data.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/performance.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/scripts.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/compat.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/config.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/core/__init__.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/core/data.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/core/utils.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/ibkr/__init__.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/ibkr/utils.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/__init__.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/rates.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/risk.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/scripts.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/__init__.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/ml.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/nlp.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/optimization.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/portfolio.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/risk.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/__init__.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/utils.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/tseries.py +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/dependency_links.txt +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/entry_points.txt +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/top_level.txt +0 -0
- {bbstrader-0.2.97 → bbstrader-0.2.99}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bbstrader
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.99
|
|
4
4
|
Summary: Simplified Investment & Trading Toolkit
|
|
5
5
|
Home-page: https://github.com/bbalouki/bbstrader
|
|
6
6
|
Download-URL: https://pypi.org/project/bbstrader/
|
|
@@ -24,21 +24,24 @@ Classifier: Operating System :: MacOS
|
|
|
24
24
|
Classifier: License :: OSI Approved :: MIT License
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
|
-
Requires-Dist:
|
|
27
|
+
Requires-Dist: numpy<2.0.0,>=1.26.0
|
|
28
|
+
Requires-Dist: scikit-learn
|
|
29
|
+
Requires-Dist: statsmodels
|
|
30
|
+
Requires-Dist: seaborn
|
|
31
|
+
Requires-Dist: lightgbm
|
|
32
|
+
Requires-Dist: dash
|
|
33
|
+
Requires-Dist: nltk
|
|
34
|
+
Requires-Dist: spacy
|
|
35
|
+
Requires-Dist: pmdarima
|
|
36
|
+
Requires-Dist: pyportfolioopt
|
|
37
|
+
Requires-Dist: alphalens-reloaded
|
|
28
38
|
Requires-Dist: pandas_ta
|
|
29
|
-
Requires-Dist: numpy<2.0.0
|
|
30
39
|
Requires-Dist: yfinance
|
|
31
|
-
Requires-Dist: scipy
|
|
32
40
|
Requires-Dist: hmmlearn
|
|
33
|
-
Requires-Dist: pmdarima
|
|
34
41
|
Requires-Dist: arch
|
|
35
42
|
Requires-Dist: hurst
|
|
36
|
-
Requires-Dist: seaborn
|
|
37
|
-
Requires-Dist: statsmodels
|
|
38
|
-
Requires-Dist: matplotlib
|
|
39
43
|
Requires-Dist: filterpy
|
|
40
44
|
Requires-Dist: pykalman
|
|
41
|
-
Requires-Dist: pytest
|
|
42
45
|
Requires-Dist: CurrencyConverter
|
|
43
46
|
Requires-Dist: tabulate
|
|
44
47
|
Requires-Dist: python-dotenv
|
|
@@ -46,24 +49,17 @@ Requires-Dist: ipython
|
|
|
46
49
|
Requires-Dist: QuantStats
|
|
47
50
|
Requires-Dist: exchange-calendars
|
|
48
51
|
Requires-Dist: tqdm
|
|
49
|
-
Requires-Dist: scikit-learn
|
|
50
52
|
Requires-Dist: notify-py
|
|
51
53
|
Requires-Dist: python-telegram-bot
|
|
52
|
-
Requires-Dist: pyportfolioopt
|
|
53
54
|
Requires-Dist: eodhd
|
|
54
55
|
Requires-Dist: financetoolkit
|
|
55
56
|
Requires-Dist: PyYAML
|
|
56
57
|
Requires-Dist: tables
|
|
57
|
-
Requires-Dist: lightgbm
|
|
58
|
-
Requires-Dist: alphalens-reloaded
|
|
59
58
|
Requires-Dist: pyfiglet
|
|
60
59
|
Requires-Dist: colorama
|
|
61
60
|
Requires-Dist: praw
|
|
62
61
|
Requires-Dist: tweepy
|
|
63
62
|
Requires-Dist: beautifulsoup4
|
|
64
|
-
Requires-Dist: dash
|
|
65
|
-
Requires-Dist: nltk
|
|
66
|
-
Requires-Dist: spacy
|
|
67
63
|
Requires-Dist: textblob
|
|
68
64
|
Requires-Dist: vaderSentiment
|
|
69
65
|
Provides-Extra: mt5
|
|
@@ -16,7 +16,7 @@ USAGE_TEXT = """
|
|
|
16
16
|
Modules:
|
|
17
17
|
copier: Copy trades from one MetaTrader account to another or multiple accounts
|
|
18
18
|
backtest: Backtest a strategy, see bbstrader.btengine.backtest.run_backtest
|
|
19
|
-
execution: Execute a strategy, see bbstrader.trading.execution.
|
|
19
|
+
execution: Execute a strategy, see bbstrader.trading.execution.Mt5ExecutionEngine
|
|
20
20
|
|
|
21
21
|
python -m bbstrader --run <module> --help for more information on the module
|
|
22
22
|
"""
|
|
@@ -4,7 +4,7 @@ from datetime import datetime
|
|
|
4
4
|
from typing import List, Literal, Optional
|
|
5
5
|
|
|
6
6
|
from tabulate import tabulate
|
|
7
|
-
|
|
7
|
+
from bbstrader.btengine.event import Events
|
|
8
8
|
from bbstrader.btengine.data import DataHandler
|
|
9
9
|
from bbstrader.btengine.execution import ExecutionHandler, SimExecutionHandler
|
|
10
10
|
from bbstrader.btengine.portfolio import Portfolio
|
|
@@ -165,19 +165,19 @@ class BacktestEngine(Backtest):
|
|
|
165
165
|
break
|
|
166
166
|
else:
|
|
167
167
|
if event is not None:
|
|
168
|
-
if event.type ==
|
|
168
|
+
if event.type == Events.MARKET:
|
|
169
169
|
self.strategy.calculate_signals(event)
|
|
170
170
|
self.portfolio.update_timeindex(event)
|
|
171
171
|
|
|
172
|
-
elif event.type ==
|
|
172
|
+
elif event.type == Events.SIGNAL:
|
|
173
173
|
self.signals += 1
|
|
174
174
|
self.portfolio.update_signal(event)
|
|
175
175
|
|
|
176
|
-
elif event.type ==
|
|
176
|
+
elif event.type == Events.ORDER:
|
|
177
177
|
self.orders += 1
|
|
178
178
|
self.execution_handler.execute_order(event)
|
|
179
179
|
|
|
180
|
-
elif event.type ==
|
|
180
|
+
elif event.type == Events.FILL:
|
|
181
181
|
self.fills += 1
|
|
182
182
|
self.portfolio.update_fill(event)
|
|
183
183
|
self.strategy.update_trades_from_fill(event)
|
|
@@ -354,7 +354,7 @@ def run_backtest_with(engine: Literal["bbstrader", "cerebro", "zipline"], **kwar
|
|
|
354
354
|
)
|
|
355
355
|
elif engine == "cerebro":
|
|
356
356
|
# TODO:
|
|
357
|
-
|
|
357
|
+
raise NotImplementedError("cerebro engine is not supported yet")
|
|
358
358
|
elif engine == "zipline":
|
|
359
359
|
# TODO:
|
|
360
|
-
|
|
360
|
+
raise NotImplementedError("zipline engine is not supported yet")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from enum import Enum
|
|
2
3
|
from typing import Literal
|
|
3
4
|
|
|
4
5
|
__all__ = ["Event", "MarketEvent", "SignalEvent", "OrderEvent", "FillEvent"]
|
|
@@ -18,6 +19,13 @@ class Event(object):
|
|
|
18
19
|
...
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
class Events(Enum):
|
|
23
|
+
MARKET = "MARKET"
|
|
24
|
+
SIGNAL = "SIGNAL"
|
|
25
|
+
ORDER = "ORDER"
|
|
26
|
+
FILL = "FILL"
|
|
27
|
+
|
|
28
|
+
|
|
21
29
|
class MarketEvent(Event):
|
|
22
30
|
"""
|
|
23
31
|
Market Events are triggered when the outer while loop of the backtesting
|
|
@@ -32,7 +40,7 @@ class MarketEvent(Event):
|
|
|
32
40
|
"""
|
|
33
41
|
Initialises the MarketEvent.
|
|
34
42
|
"""
|
|
35
|
-
self.type =
|
|
43
|
+
self.type = Events.MARKET
|
|
36
44
|
|
|
37
45
|
|
|
38
46
|
class SignalEvent(Event):
|
|
@@ -72,7 +80,7 @@ class SignalEvent(Event):
|
|
|
72
80
|
price (int | float): An optional price to be used when the signal is generated.
|
|
73
81
|
stoplimit (int | float): An optional stop-limit price for the signal
|
|
74
82
|
"""
|
|
75
|
-
self.type =
|
|
83
|
+
self.type = Events.SIGNAL
|
|
76
84
|
self.strategy_id = strategy_id
|
|
77
85
|
self.symbol = symbol
|
|
78
86
|
self.datetime = datetime
|
|
@@ -118,7 +126,7 @@ class OrderEvent(Event):
|
|
|
118
126
|
price (int | float): The price at which to order.
|
|
119
127
|
signal (str): The signal that generated the order.
|
|
120
128
|
"""
|
|
121
|
-
self.type =
|
|
129
|
+
self.type = Events.ORDER
|
|
122
130
|
self.symbol = symbol
|
|
123
131
|
self.order_type = order_type
|
|
124
132
|
self.quantity = quantity
|
|
@@ -191,7 +199,7 @@ class FillEvent(Event):
|
|
|
191
199
|
commission (float | None): An optional commission sent from IB.
|
|
192
200
|
order (str): The order that this fill is related
|
|
193
201
|
"""
|
|
194
|
-
self.type =
|
|
202
|
+
self.type = Events.FILL
|
|
195
203
|
self.timeindex = timeindex
|
|
196
204
|
self.symbol = symbol
|
|
197
205
|
self.exchange = exchange
|
|
@@ -4,7 +4,7 @@ from queue import Queue
|
|
|
4
4
|
from loguru import logger
|
|
5
5
|
|
|
6
6
|
from bbstrader.btengine.data import DataHandler
|
|
7
|
-
from bbstrader.btengine.event import FillEvent, OrderEvent
|
|
7
|
+
from bbstrader.btengine.event import Events, FillEvent, OrderEvent
|
|
8
8
|
from bbstrader.config import BBSTRADER_DIR
|
|
9
9
|
from bbstrader.metatrader.account import Account
|
|
10
10
|
|
|
@@ -80,7 +80,7 @@ class SimExecutionHandler(ExecutionHandler):
|
|
|
80
80
|
Args:
|
|
81
81
|
event (OrderEvent): Contains an Event object with order information.
|
|
82
82
|
"""
|
|
83
|
-
if event.type ==
|
|
83
|
+
if event.type == Events.ORDER:
|
|
84
84
|
dtime = self.bardata.get_latest_bar_datetime(event.symbol)
|
|
85
85
|
fill_event = FillEvent(
|
|
86
86
|
timeindex=dtime,
|
|
@@ -233,7 +233,7 @@ class MT5ExecutionHandler(ExecutionHandler):
|
|
|
233
233
|
Args:
|
|
234
234
|
event (OrderEvent): Contains an Event object with order information.
|
|
235
235
|
"""
|
|
236
|
-
if event.type ==
|
|
236
|
+
if event.type == Events.ORDER:
|
|
237
237
|
symbol = event.symbol
|
|
238
238
|
direction = event.direction
|
|
239
239
|
quantity = event.quantity
|
|
@@ -6,7 +6,7 @@ import pandas as pd
|
|
|
6
6
|
import quantstats as qs
|
|
7
7
|
|
|
8
8
|
from bbstrader.btengine.data import DataHandler
|
|
9
|
-
from bbstrader.btengine.event import FillEvent, MarketEvent, OrderEvent, SignalEvent
|
|
9
|
+
from bbstrader.btengine.event import Events, FillEvent, MarketEvent, OrderEvent, SignalEvent
|
|
10
10
|
from bbstrader.btengine.performance import (
|
|
11
11
|
create_drawdowns,
|
|
12
12
|
create_sharpe_ratio,
|
|
@@ -282,7 +282,7 @@ class Portfolio(object):
|
|
|
282
282
|
Updates the portfolio current positions and holdings
|
|
283
283
|
from a FillEvent.
|
|
284
284
|
"""
|
|
285
|
-
if event.type ==
|
|
285
|
+
if event.type == Events.FILL:
|
|
286
286
|
self.update_positions_from_fill(event)
|
|
287
287
|
self.update_holdings_from_fill(event)
|
|
288
288
|
|
|
@@ -337,7 +337,7 @@ class Portfolio(object):
|
|
|
337
337
|
Acts on a SignalEvent to generate new orders
|
|
338
338
|
based on the portfolio logic.
|
|
339
339
|
"""
|
|
340
|
-
if event.type ==
|
|
340
|
+
if event.type == Events.SIGNAL:
|
|
341
341
|
order_event = self.generate_order(event)
|
|
342
342
|
self.events.put(order_event)
|
|
343
343
|
|
|
@@ -10,15 +10,15 @@ import pytz
|
|
|
10
10
|
from loguru import logger
|
|
11
11
|
|
|
12
12
|
from bbstrader.btengine.data import DataHandler
|
|
13
|
-
from bbstrader.btengine.event import FillEvent, SignalEvent
|
|
13
|
+
from bbstrader.btengine.event import Events, FillEvent, SignalEvent
|
|
14
14
|
from bbstrader.config import BBSTRADER_DIR
|
|
15
|
-
from bbstrader.metatrader.trade import TradeSignal
|
|
16
15
|
from bbstrader.metatrader.account import (
|
|
17
16
|
Account,
|
|
18
17
|
AdmiralMarktsGroup,
|
|
19
18
|
PepperstoneGroupLimited,
|
|
20
19
|
)
|
|
21
20
|
from bbstrader.metatrader.rates import Rates
|
|
21
|
+
from bbstrader.metatrader.trade import TradeSignal
|
|
22
22
|
from bbstrader.models.optimization import optimized_weights
|
|
23
23
|
|
|
24
24
|
__all__ = ["Strategy", "MT5Strategy"]
|
|
@@ -67,7 +67,7 @@ class MT5Strategy(Strategy):
|
|
|
67
67
|
"""
|
|
68
68
|
A `MT5Strategy()` object is a subclass of `Strategy` that is used to
|
|
69
69
|
calculate signals for the MetaTrader 5 trading platform. The signals
|
|
70
|
-
are generated by the `MT5Strategy` object and sent to the the `
|
|
70
|
+
are generated by the `MT5Strategy` object and sent to the the `Mt5ExecutionEngine`
|
|
71
71
|
for live trading and `MT5BacktestEngine` objects for backtesting.
|
|
72
72
|
"""
|
|
73
73
|
|
|
@@ -146,9 +146,9 @@ class MT5Strategy(Strategy):
|
|
|
146
146
|
def _initialize_portfolio(self):
|
|
147
147
|
positions = ["LONG", "SHORT"]
|
|
148
148
|
orders = ["BLMT", "BSTP", "BSTPLMT", "SLMT", "SSTP", "SSTPLMT"]
|
|
149
|
+
self._orders: Dict[str, Dict[str, List[SignalEvent]]] = {}
|
|
149
150
|
self._positions: Dict[str, Dict[str, int | float]] = {}
|
|
150
151
|
self._trades: Dict[str, Dict[str, int]] = {}
|
|
151
|
-
self._orders: Dict[str, Dict[str, List[SignalEvent]]] = {}
|
|
152
152
|
for symbol in self.symbols:
|
|
153
153
|
self._positions[symbol] = {}
|
|
154
154
|
self._orders[symbol] = {}
|
|
@@ -188,7 +188,7 @@ class MT5Strategy(Strategy):
|
|
|
188
188
|
This method updates the trades for the strategy based on the fill event.
|
|
189
189
|
It is used to keep track of the number of trades executed for each order.
|
|
190
190
|
"""
|
|
191
|
-
if event.type ==
|
|
191
|
+
if event.type == Events.FILL:
|
|
192
192
|
if event.order != "EXIT":
|
|
193
193
|
self._trades[event.symbol][event.order] += 1
|
|
194
194
|
elif event.order == "EXIT" and event.direction == "BUY":
|
|
@@ -206,6 +206,7 @@ class MT5Strategy(Strategy):
|
|
|
206
206
|
- ``price``: The price at which to execute the action, used for pending orders.
|
|
207
207
|
- ``stoplimit``: The stop-limit price for STOP-LIMIT orders, used for pending stop limit orders.
|
|
208
208
|
- ``id``: The unique identifier for the strategy or order.
|
|
209
|
+
- ``comment``: An optional comment or description related to the trade signal.
|
|
209
210
|
"""
|
|
210
211
|
pass
|
|
211
212
|
|
|
@@ -677,6 +678,12 @@ class MT5Strategy(Strategy):
|
|
|
677
678
|
if period_count == 0 or period_count is None:
|
|
678
679
|
return True
|
|
679
680
|
return period_count % signal_inverval == 0
|
|
681
|
+
|
|
682
|
+
@staticmethod
|
|
683
|
+
def stop_time(time_zone: str, stop_time: str) -> bool:
|
|
684
|
+
now = datetime.now(pytz.timezone(time_zone)).time()
|
|
685
|
+
stop_time = datetime.strptime(stop_time, "%H:%M").time()
|
|
686
|
+
return now >= stop_time
|
|
680
687
|
|
|
681
688
|
def ispositions(
|
|
682
689
|
self, symbol, strategy_id, position, max_trades, one_true=False, account=None
|
|
@@ -9,6 +9,7 @@ from currency_converter import SINGLE_DAY_ECB_URL, CurrencyConverter
|
|
|
9
9
|
|
|
10
10
|
from bbstrader.metatrader.utils import (
|
|
11
11
|
AccountInfo,
|
|
12
|
+
BookInfo,
|
|
12
13
|
InvalidBroker,
|
|
13
14
|
OrderCheckResult,
|
|
14
15
|
OrderSentResult,
|
|
@@ -188,10 +189,12 @@ def check_mt5_connection(**kwargs):
|
|
|
188
189
|
except Exception:
|
|
189
190
|
raise_mt5_error(INIT_MSG)
|
|
190
191
|
|
|
192
|
+
|
|
191
193
|
def shutdown_mt5():
|
|
192
194
|
"""Close the connection to the MetaTrader 5 terminal."""
|
|
193
195
|
mt5.shutdown()
|
|
194
|
-
|
|
196
|
+
|
|
197
|
+
|
|
195
198
|
class Broker(object):
|
|
196
199
|
def __init__(self, name: str = None, **kwargs):
|
|
197
200
|
if name is None:
|
|
@@ -323,7 +326,7 @@ class Account(object):
|
|
|
323
326
|
f"For {supported['FTMO'].name}, See [{ftmo_url}]\n"
|
|
324
327
|
)
|
|
325
328
|
raise InvalidBroker(message=msg)
|
|
326
|
-
|
|
329
|
+
|
|
327
330
|
def shutdown(self):
|
|
328
331
|
"""Close the connection to the MetaTrader 5 terminal."""
|
|
329
332
|
shutdown_mt5()
|
|
@@ -1037,6 +1040,29 @@ class Account(object):
|
|
|
1037
1040
|
"""
|
|
1038
1041
|
self._show_info(self.get_tick_info, "tick", symbol=symbol)
|
|
1039
1042
|
|
|
1043
|
+
def get_market_book(self, symbol: str) -> Union[Tuple[BookInfo], None]:
|
|
1044
|
+
"""
|
|
1045
|
+
Get the Market Depth content for a specific symbol.
|
|
1046
|
+
Args:
|
|
1047
|
+
symbol (str): Financial instrument name. Required unnamed parameter.
|
|
1048
|
+
The symbol name should be specified in the same format as in the Market Watch window.
|
|
1049
|
+
|
|
1050
|
+
Returns:
|
|
1051
|
+
The Market Depth content as a tuple from BookInfo entries featuring order type, price and volume in lots.
|
|
1052
|
+
Return None in case of an error.
|
|
1053
|
+
|
|
1054
|
+
Raises:
|
|
1055
|
+
MT5TerminalError: A specific exception based on the error code.
|
|
1056
|
+
"""
|
|
1057
|
+
try:
|
|
1058
|
+
book = mt5.market_book_get(symbol)
|
|
1059
|
+
if book is None:
|
|
1060
|
+
return None
|
|
1061
|
+
else:
|
|
1062
|
+
return tuple([BookInfo(**entry._asdict()) for entry in book])
|
|
1063
|
+
except Exception as e:
|
|
1064
|
+
raise_mt5_error(e)
|
|
1065
|
+
|
|
1040
1066
|
def calculate_margin(
|
|
1041
1067
|
self, action: Literal["buy", "sell"], symbol: str, lot: float, price: float
|
|
1042
1068
|
) -> float:
|
|
@@ -1299,8 +1325,7 @@ class Account(object):
|
|
|
1299
1325
|
df.drop(["time_msc", "external_id"], axis=1, inplace=True)
|
|
1300
1326
|
df.set_index("time", inplace=True)
|
|
1301
1327
|
if save:
|
|
1302
|
-
|
|
1303
|
-
df.to_csv(file)
|
|
1328
|
+
df.to_csv("trades_history.csv")
|
|
1304
1329
|
if to_df:
|
|
1305
1330
|
return df
|
|
1306
1331
|
else:
|
|
@@ -1507,8 +1532,7 @@ class Account(object):
|
|
|
1507
1532
|
df["time_done"] = pd.to_datetime(df["time_done"], unit="s")
|
|
1508
1533
|
|
|
1509
1534
|
if save:
|
|
1510
|
-
|
|
1511
|
-
df.to_csv(file)
|
|
1535
|
+
df.to_csv("orders_history.csv")
|
|
1512
1536
|
if to_df:
|
|
1513
1537
|
return df
|
|
1514
1538
|
else:
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import MetaTrader5 as mt5
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import seaborn as sns
|
|
6
|
+
|
|
7
|
+
from bbstrader.metatrader.account import check_mt5_connection
|
|
8
|
+
from bbstrader.metatrader.utils import TIMEFRAMES
|
|
9
|
+
|
|
10
|
+
sns.set_theme()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _get_data(path, symbol, timeframe, bars):
|
|
14
|
+
check_mt5_connection(path=path)
|
|
15
|
+
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, bars)
|
|
16
|
+
df = pd.DataFrame(rates)
|
|
17
|
+
df["time"] = pd.to_datetime(df["time"], unit="s")
|
|
18
|
+
return df
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def volume_profile(df, bins):
|
|
22
|
+
prices = (df["high"] + df["low"]) / 2
|
|
23
|
+
volumes = df["tick_volume"]
|
|
24
|
+
hist, bin_edges = np.histogram(prices, bins=bins, weights=volumes)
|
|
25
|
+
bin_centers = 0.5 * (bin_edges[:-1] + bin_edges[1:])
|
|
26
|
+
return hist, bin_edges, bin_centers
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def value_area(hist, bin_centers, percentage):
|
|
30
|
+
total_volume = np.sum(hist)
|
|
31
|
+
poc_index = np.argmax(hist)
|
|
32
|
+
poc = bin_centers[poc_index]
|
|
33
|
+
|
|
34
|
+
sorted_indices = np.argsort(hist)[::-1]
|
|
35
|
+
volume_accum = 0
|
|
36
|
+
value_area_indices = []
|
|
37
|
+
|
|
38
|
+
for idx in sorted_indices:
|
|
39
|
+
volume_accum += hist[idx]
|
|
40
|
+
value_area_indices.append(idx)
|
|
41
|
+
if volume_accum >= percentage * total_volume:
|
|
42
|
+
break
|
|
43
|
+
|
|
44
|
+
vah = max(bin_centers[i] for i in value_area_indices)
|
|
45
|
+
val = min(bin_centers[i] for i in value_area_indices)
|
|
46
|
+
return poc, vah, val
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def display_volume_profile(
|
|
50
|
+
symbol,
|
|
51
|
+
path,
|
|
52
|
+
timeframe: str = "1m",
|
|
53
|
+
bars: int = 1440,
|
|
54
|
+
bins: int = 100,
|
|
55
|
+
va_percentage: float = 0.7,
|
|
56
|
+
):
|
|
57
|
+
"""
|
|
58
|
+
Display a volume profile chart for a given market symbol using historical data.
|
|
59
|
+
|
|
60
|
+
This function retrieves historical price and volume data for a given symbol and
|
|
61
|
+
plots a vertical volume profile chart showing the volume distribution across
|
|
62
|
+
price levels. It highlights key levels such as:
|
|
63
|
+
- Point of Control (POC): Price level with the highest traded volume.
|
|
64
|
+
- Value Area High (VAH): Upper bound of the value area.
|
|
65
|
+
- Value Area Low (VAL): Lower bound of the value area.
|
|
66
|
+
- Current Price: Latest bid price from MetaTrader 5.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
symbol (str): Market symbol (e.g., "AAPL", "EURUSD").
|
|
70
|
+
path (str): Path to the historical data see ``bbstrader.metatrader.account.check_mt5_connection()``.
|
|
71
|
+
timeframe (str, optional): Timeframe for each candle (default is "1m").
|
|
72
|
+
bars (int, optional): Number of historical bars to fetch (default is 1440).
|
|
73
|
+
bins (int, optional): Number of price bins for volume profile calculation (default is 100).
|
|
74
|
+
va_percentage (float, optional): Percentage of total volume to define the value area (default is 0.7).
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
None: Displays a matplotlib chart of the volume profile.
|
|
78
|
+
"""
|
|
79
|
+
df = _get_data(path, symbol, TIMEFRAMES[timeframe], bars)
|
|
80
|
+
hist, bin_edges, bin_centers = volume_profile(df, bins)
|
|
81
|
+
poc, vah, val = value_area(hist, bin_centers, va_percentage)
|
|
82
|
+
current_price = mt5.symbol_info_tick(symbol).bid
|
|
83
|
+
|
|
84
|
+
plt.figure(figsize=(6, 10))
|
|
85
|
+
plt.barh(bin_centers, hist, height=bin_centers[1] - bin_centers[0], color="skyblue")
|
|
86
|
+
plt.axhline(poc, color="red", linestyle="--", label=f"POC: {poc:.5f}")
|
|
87
|
+
plt.axhline(vah, color="green", linestyle="--", label=f"VAH: {vah:.5f}")
|
|
88
|
+
plt.axhline(val, color="orange", linestyle="--", label=f"VAL: {val:.5f}")
|
|
89
|
+
plt.axhline(
|
|
90
|
+
current_price, color="black", linestyle=":", label=f"Price: {current_price:.5f}"
|
|
91
|
+
)
|
|
92
|
+
plt.legend()
|
|
93
|
+
plt.title("Volume Profile")
|
|
94
|
+
plt.xlabel("Volume")
|
|
95
|
+
plt.ylabel("Price")
|
|
96
|
+
plt.grid(True)
|
|
97
|
+
plt.tight_layout()
|
|
98
|
+
plt.show()
|