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.

Files changed (54) hide show
  1. {bbstrader-0.2.97/bbstrader.egg-info → bbstrader-0.2.99}/PKG-INFO +12 -16
  2. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/__main__.py +1 -1
  3. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/backtest.py +7 -7
  4. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/event.py +12 -4
  5. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/execution.py +3 -3
  6. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/portfolio.py +3 -3
  7. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/strategy.py +12 -5
  8. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/account.py +30 -6
  9. bbstrader-0.2.99/bbstrader/metatrader/analysis.py +98 -0
  10. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/copier.py +77 -57
  11. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/trade.py +82 -118
  12. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/utils.py +16 -0
  13. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/factors.py +97 -97
  14. bbstrader-0.2.99/bbstrader/trading/execution.py +977 -0
  15. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/scripts.py +10 -11
  16. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/strategies.py +13 -5
  17. {bbstrader-0.2.97 → bbstrader-0.2.99/bbstrader.egg-info}/PKG-INFO +12 -16
  18. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/SOURCES.txt +1 -0
  19. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/requires.txt +11 -15
  20. {bbstrader-0.2.97 → bbstrader-0.2.99}/requirements.txt +11 -16
  21. {bbstrader-0.2.97 → bbstrader-0.2.99}/setup.py +1 -1
  22. bbstrader-0.2.97/bbstrader/trading/execution.py +0 -857
  23. {bbstrader-0.2.97 → bbstrader-0.2.99}/LICENSE +0 -0
  24. {bbstrader-0.2.97 → bbstrader-0.2.99}/MANIFEST.in +0 -0
  25. {bbstrader-0.2.97 → bbstrader-0.2.99}/README.md +0 -0
  26. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/__ini__.py +0 -0
  27. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/__init__.py +0 -0
  28. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/data.py +0 -0
  29. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/performance.py +0 -0
  30. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/btengine/scripts.py +0 -0
  31. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/compat.py +0 -0
  32. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/config.py +0 -0
  33. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/core/__init__.py +0 -0
  34. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/core/data.py +0 -0
  35. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/core/utils.py +0 -0
  36. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/ibkr/__init__.py +0 -0
  37. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/ibkr/utils.py +0 -0
  38. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/__init__.py +0 -0
  39. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/rates.py +0 -0
  40. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/risk.py +0 -0
  41. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/metatrader/scripts.py +0 -0
  42. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/__init__.py +0 -0
  43. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/ml.py +0 -0
  44. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/nlp.py +0 -0
  45. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/optimization.py +0 -0
  46. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/portfolio.py +0 -0
  47. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/models/risk.py +0 -0
  48. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/__init__.py +0 -0
  49. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/trading/utils.py +0 -0
  50. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader/tseries.py +0 -0
  51. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/dependency_links.txt +0 -0
  52. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/entry_points.txt +0 -0
  53. {bbstrader-0.2.97 → bbstrader-0.2.99}/bbstrader.egg-info/top_level.txt +0 -0
  54. {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.97
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: pandas
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.MT5ExecutionEngine
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 == "MARKET":
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 == "SIGNAL":
172
+ elif event.type == Events.SIGNAL:
173
173
  self.signals += 1
174
174
  self.portfolio.update_signal(event)
175
175
 
176
- elif event.type == "ORDER":
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 == "FILL":
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
- pass
357
+ raise NotImplementedError("cerebro engine is not supported yet")
358
358
  elif engine == "zipline":
359
359
  # TODO:
360
- pass
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 = "MARKET"
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 = "SIGNAL"
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 = "ORDER"
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 = "FILL"
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 == "ORDER":
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 == "ORDER":
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 == "FILL":
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 == "SIGNAL":
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 `MT5ExecutionEngine`
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 == "FILL":
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
- file = "trade_history.csv"
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
- file = "trade_history.csv"
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()