bbstrader 0.1.91__py3-none-any.whl → 0.1.93__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.

@@ -0,0 +1,177 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from pypfopt import risk_models
4
+ from pypfopt import expected_returns
5
+ from pypfopt.efficient_frontier import EfficientFrontier
6
+ from pypfopt.hierarchical_portfolio import HRPOpt
7
+ import warnings
8
+
9
+ __all__ = [
10
+ 'markowitz_weights',
11
+ 'hierarchical_risk_parity',
12
+ 'equal_weighted',
13
+ 'optimized_weights'
14
+ ]
15
+
16
+ def markowitz_weights(prices=None, rfr=0.0, freq=252):
17
+ """
18
+ Calculates optimal portfolio weights using Markowitz's mean-variance optimization (Max Sharpe Ratio) with multiple solvers.
19
+
20
+ Parameters:
21
+ ----------
22
+ prices : pd.DataFrame, optional
23
+ Price data for assets, where rows represent time periods and columns represent assets.
24
+ freq : int, optional
25
+ Frequency of the data, such as 252 for daily returns in a year (default is 252).
26
+
27
+ Returns:
28
+ -------
29
+ dict
30
+ Dictionary containing the optimal asset weights for maximizing the Sharpe ratio, normalized to sum to 1.
31
+
32
+ Notes:
33
+ -----
34
+ This function attempts to maximize the Sharpe ratio by iterating through various solvers ('SCS', 'ECOS', 'OSQP')
35
+ from the PyPortfolioOpt library. If a solver fails, it proceeds to the next one. If none succeed, an error message
36
+ is printed for each solver that fails.
37
+
38
+ This function is useful for portfolio with a small number of assets, as it may not scale well for large portfolios.
39
+
40
+ Raises:
41
+ ------
42
+ Exception
43
+ If all solvers fail, each will print an exception error message during runtime.
44
+ """
45
+ returns = expected_returns.mean_historical_return(prices, frequency=freq)
46
+ cov = risk_models.sample_cov(prices, frequency=freq)
47
+
48
+ # Try different solvers to maximize Sharpe ratio
49
+ for solver in ['SCS', 'ECOS', 'OSQP']:
50
+ ef = EfficientFrontier(expected_returns=returns,
51
+ cov_matrix=cov,
52
+ weight_bounds=(0, 1),
53
+ solver=solver)
54
+ try:
55
+ weights = ef.max_sharpe(risk_free_rate=rfr)
56
+ return ef.clean_weights()
57
+ except Exception as e:
58
+ print(f"Solver {solver} failed with error: {e}")
59
+
60
+ def hierarchical_risk_parity(prices=None, returns=None, freq=252):
61
+ """
62
+ Computes asset weights using Hierarchical Risk Parity (HRP) for risk-averse portfolio allocation.
63
+
64
+ Parameters:
65
+ ----------
66
+ prices : pd.DataFrame, optional
67
+ Price data for assets; if provided, daily returns will be calculated.
68
+ returns : pd.DataFrame, optional
69
+ Daily returns for assets. One of `prices` or `returns` must be provided.
70
+ freq : int, optional
71
+ Number of days to consider in calculating portfolio weights (default is 252).
72
+
73
+ Returns:
74
+ -------
75
+ dict
76
+ Optimized asset weights using the HRP method, with asset weights summing to 1.
77
+
78
+ Raises:
79
+ ------
80
+ ValueError
81
+ If neither `prices` nor `returns` are provided.
82
+
83
+ Notes:
84
+ -----
85
+ Hierarchical Risk Parity is particularly useful for portfolios with a large number of assets,
86
+ as it mitigates issues of multicollinearity and estimation errors in covariance matrices by
87
+ using hierarchical clustering.
88
+ """
89
+ warnings.filterwarnings("ignore")
90
+ if returns is None and prices is None:
91
+ raise ValueError("Either prices or returns must be provided")
92
+ if returns is None:
93
+ returns = prices.pct_change().dropna(how='all')
94
+ # Remove duplicate columns and index
95
+ returns = returns.loc[:, ~returns.columns.duplicated()]
96
+ returns = returns.loc[~returns.index.duplicated(keep='first')]
97
+ hrp = HRPOpt(returns=returns.iloc[-freq:])
98
+ return hrp.optimize()
99
+
100
+ def equal_weighted(prices=None, returns=None, round_digits=5):
101
+ """
102
+ Generates an equal-weighted portfolio by assigning an equal proportion to each asset.
103
+
104
+ Parameters:
105
+ ----------
106
+ prices : pd.DataFrame, optional
107
+ Price data for assets, where each column represents an asset.
108
+ returns : pd.DataFrame, optional
109
+ Return data for assets. One of `prices` or `returns` must be provided.
110
+ round_digits : int, optional
111
+ Number of decimal places to round each weight to (default is 5).
112
+
113
+ Returns:
114
+ -------
115
+ dict
116
+ Dictionary with equal weights assigned to each asset, summing to 1.
117
+
118
+ Raises:
119
+ ------
120
+ ValueError
121
+ If neither `prices` nor `returns` are provided.
122
+
123
+ Notes:
124
+ -----
125
+ Equal weighting is a simple allocation method that assumes equal importance across all assets,
126
+ useful as a baseline model and when no strong views exist on asset return expectations or risk.
127
+ """
128
+ if returns is None and prices is None:
129
+ raise ValueError("Either prices or returns must be provided")
130
+ if returns is None:
131
+ n = len(prices.columns)
132
+ columns = prices.columns
133
+ else:
134
+ n = len(returns.columns)
135
+ columns = returns.columns
136
+ return {col: round(1/n, round_digits) for col in columns}
137
+
138
+ def optimized_weights(prices=None, returns=None, rfr=0.0, freq=252, method='equal'):
139
+ """
140
+ Selects an optimization method to calculate portfolio weights based on user preference.
141
+
142
+ Parameters:
143
+ ----------
144
+ prices : pd.DataFrame, optional
145
+ Price data for assets, required for certain methods.
146
+ returns : pd.DataFrame, optional
147
+ Returns data for assets, an alternative input for certain methods.
148
+ freq : int, optional
149
+ Number of days for calculating portfolio weights, such as 252 for a year's worth of daily returns (default is 252).
150
+ method : str, optional
151
+ Optimization method to use ('markowitz', 'hrp', or 'equal') (default is 'markowitz').
152
+
153
+ Returns:
154
+ -------
155
+ dict
156
+ Dictionary containing optimized asset weights based on the chosen method.
157
+
158
+ Raises:
159
+ ------
160
+ ValueError
161
+ If an unknown optimization method is specified.
162
+
163
+ Notes:
164
+ -----
165
+ This function integrates different optimization methods:
166
+ - 'markowitz': mean-variance optimization with max Sharpe ratio
167
+ - 'hrp': Hierarchical Risk Parity, for risk-based clustering of assets
168
+ - 'equal': Equal weighting across all assets
169
+ """
170
+ if method == 'markowitz':
171
+ return markowitz_weights(prices=prices, rfr=rfr, freq=freq)
172
+ elif method == 'hrp':
173
+ return hierarchical_risk_parity(prices=prices, returns=returns, freq=freq)
174
+ elif method == 'equal':
175
+ return equal_weighted(prices=prices, returns=returns)
176
+ else:
177
+ raise ValueError(f"Unknown method: {method}")
@@ -0,0 +1,205 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import seaborn as sns
4
+ import matplotlib.pyplot as plt
5
+ from sklearn.decomposition import PCA
6
+ from sklearn.preprocessing import scale
7
+ from bbstrader.models.optimization import (
8
+ markowitz_weights,
9
+ hierarchical_risk_parity,
10
+ equal_weighted
11
+ )
12
+
13
+ __all__ = [
14
+ 'EigenPortfolios'
15
+ ]
16
+
17
+ class EigenPortfolios(object):
18
+ """
19
+ The `EigenPortfolios` class applies Principal Component Analysis (PCA) to a covariance matrix of normalized asset returns
20
+ to derive portfolios (eigenportfolios) that capture distinct risk factors in the asset returns. Each eigenportfolio
21
+ represents a principal component of the return covariance matrix, ordered by the magnitude of its eigenvalue. These
22
+ portfolios capture most of the variance in asset returns and are mutually uncorrelated.
23
+
24
+ """
25
+ def __init__(self):
26
+ self.returns = None
27
+ self.n_portfolios = None
28
+ self._portfolios = None
29
+ self._fit_called = False
30
+
31
+ def get_portfolios(self) -> pd.DataFrame:
32
+ """
33
+ Returns the computed eigenportfolios (weights of assets in each portfolio).
34
+
35
+ Returns:
36
+ -------
37
+ pd.DataFrame
38
+ DataFrame containing eigenportfolio weights for each asset.
39
+
40
+ Raises:
41
+ ------
42
+ ValueError
43
+ If `fit()` has not been called before retrieving portfolios.
44
+ """
45
+ if not self._fit_called:
46
+ raise ValueError("fit() must be called first")
47
+ return self._portfolios
48
+
49
+ def fit(self, returns: pd.DataFrame, n_portfolios: int=4) -> pd.DataFrame:
50
+ """
51
+ Computes the eigenportfolios based on PCA of the asset returns' covariance matrix.
52
+
53
+ Parameters:
54
+ ----------
55
+ returns : pd.DataFrame
56
+ Historical returns of assets to be used for PCA.
57
+ n_portfolios : int, optional
58
+ Number of eigenportfolios to compute (default is 4).
59
+
60
+ Returns:
61
+ -------
62
+ pd.DataFrame
63
+ DataFrame containing normalized weights for each eigenportfolio.
64
+
65
+ Notes:
66
+ -----
67
+ This method performs winsorization and normalization on returns to reduce the impact of outliers
68
+ and achieve zero mean and unit variance. It uses the first `n_portfolios` principal components
69
+ as portfolio weights.
70
+ """
71
+ # Winsorize and normalize the returns
72
+ normed_returns = scale(returns
73
+ .clip(lower=returns.quantile(q=.025),
74
+ upper=returns.quantile(q=.975),
75
+ axis=1)
76
+ .apply(lambda x: x.sub(x.mean()).div(x.std())))
77
+ returns = returns.dropna(thresh=int(normed_returns.shape[0] * .95), axis=1)
78
+ returns = returns.dropna(thresh=int(normed_returns.shape[1] * .95))
79
+
80
+ cov = returns.cov()
81
+ cov.columns = cov.columns.astype(str)
82
+ pca = PCA()
83
+ pca.fit(cov)
84
+
85
+ top_portfolios = pd.DataFrame(pca.components_[:n_portfolios], columns=cov.columns)
86
+ eigen_portfolios = top_portfolios.div(top_portfolios.sum(axis=1), axis=0)
87
+ eigen_portfolios.index = [f"Portfolio {i}" for i in range(1, n_portfolios + 1)]
88
+ self._portfolios = eigen_portfolios
89
+ self.returns = returns
90
+ self.n_portfolios = n_portfolios
91
+ self._fit_called = True
92
+
93
+ def plot_weights(self):
94
+ """
95
+ Plots the weights of each asset in each eigenportfolio as bar charts.
96
+
97
+ Notes:
98
+ -----
99
+ Each subplot represents one eigenportfolio, showing the contribution of each asset.
100
+ """
101
+ eigen_portfolios = self.get_portfolios()
102
+ n_cols = 2
103
+ n_rows = (self.n_portfolios + 1) // n_cols
104
+ figsize = (n_cols * 10, n_rows * 5)
105
+ axes = eigen_portfolios.T.plot.bar(subplots=True,
106
+ layout=(n_rows, n_cols),
107
+ figsize=figsize,
108
+ legend=False)
109
+ for ax in axes.flatten():
110
+ ax.set_ylabel('Portfolio Weight')
111
+ ax.set_xlabel('')
112
+
113
+ sns.despine()
114
+ plt.tight_layout()
115
+ plt.show()
116
+
117
+ def plot_performance(self):
118
+ """
119
+ Plots the cumulative returns of each eigenportfolio over time.
120
+
121
+ Notes:
122
+ -----
123
+ This method calculates the historical cumulative performance of each eigenportfolio
124
+ by weighting asset returns according to eigenportfolio weights.
125
+ """
126
+ eigen_portfolios = self.get_portfolios()
127
+ returns = self.returns.copy()
128
+
129
+ n_cols = 2
130
+ n_rows = (self.n_portfolios + 1 + n_cols - 1) // n_cols
131
+ figsize = (n_cols * 10, n_rows * 5)
132
+ fig, axes = plt.subplots(nrows=n_rows, ncols=n_cols,
133
+ figsize=figsize, sharex=True)
134
+ axes = axes.flatten()
135
+ returns.mean(1).add(1).cumprod().sub(1).plot(title='The Market', ax=axes[0])
136
+
137
+ for i in range(self.n_portfolios):
138
+ rc = returns.mul(eigen_portfolios.iloc[i]).sum(1).add(1).cumprod().sub(1)
139
+ rc.plot(title=f'Portfolio {i+1}', ax=axes[i + 1], lw=1, rot=0)
140
+
141
+ for j in range(self.n_portfolios + 1, len(axes)):
142
+ fig.delaxes(axes[j])
143
+
144
+ for i in range(self.n_portfolios + 1):
145
+ axes[i].set_xlabel('')
146
+
147
+ sns.despine()
148
+ fig.tight_layout()
149
+ plt.show()
150
+
151
+ def optimize(self, portfolio: int = 1, optimizer: str = 'hrp', prices=None, freq=252, plot=True):
152
+ """
153
+ Optimizes the chosen eigenportfolio based on a specified optimization method.
154
+
155
+ Parameters:
156
+ ----------
157
+ portfolio : int, optional
158
+ Index of the eigenportfolio to optimize (default is 1).
159
+ optimizer : str, optional
160
+ Optimization method: 'markowitz', 'hrp' (Hierarchical Risk Parity), or 'equal' (default is 'hrp').
161
+ prices : pd.DataFrame, optional
162
+ Asset prices used for Markowitz optimization (required if optimizer is 'markowitz').
163
+ freq : int, optional
164
+ Frequency of returns (e.g., 252 for daily returns).
165
+ plot : bool, optional
166
+ Whether to plot the performance of the optimized portfolio (default is True).
167
+
168
+ Returns:
169
+ -------
170
+ dict
171
+ Dictionary of optimized asset weights.
172
+
173
+ Raises:
174
+ ------
175
+ ValueError
176
+ If an unknown optimizer is specified, or if prices are not provided when using Markowitz optimization.
177
+
178
+ Notes:
179
+ -----
180
+ The optimization method varies based on risk-return assumptions, with options for traditional Markowitz optimization,
181
+ Hierarchical Risk Parity, or equal weighting.
182
+ """
183
+ portfolio = self.get_portfolios().iloc[portfolio - 1]
184
+ returns = self.returns.loc[:, portfolio.index]
185
+ returns = returns.loc[:, ~returns.columns.duplicated()]
186
+ returns = returns.loc[~returns.index.duplicated(keep='first')]
187
+ if optimizer == 'markowitz':
188
+ if prices is None:
189
+ raise ValueError("prices must be provided for markowitz optimization")
190
+ prices = prices.loc[:, returns.columns]
191
+ weights = markowitz_weights(prices=prices, freq=freq)
192
+ elif optimizer == 'hrp':
193
+ weights = hierarchical_risk_parity(returns=returns, freq=freq)
194
+ elif optimizer == 'equal':
195
+ weights = equal_weighted(returns=returns)
196
+ else:
197
+ raise ValueError(f"Unknown optimizer: {optimizer}")
198
+ if plot:
199
+ # plot the optimized potfolio performance
200
+ returns = returns.filter(weights.keys())
201
+ rc = returns.mul(weights).sum(1).add(1).cumprod().sub(1)
202
+ rc.plot(title=f'Optimized {portfolio.name}', lw=1, rot=0)
203
+ sns.despine()
204
+ plt.show()
205
+ return weights
@@ -78,10 +78,11 @@ EXIT_SIGNAL_ACTIONS = {
78
78
 
79
79
  def _mt5_execution(
80
80
  symbol_list, trades_instances, strategy_cls, /,
81
- mm, trail, stop_trail, trail_after_points, be_plus_points, show_positions_orders,
82
- time_frame, iter_time, use_trade_time, period, period_end_action, closing_pnl, trading_days,
81
+ mm, optimizer, trail, stop_trail, trail_after_points, be_plus_points, show_positions_orders,
82
+ iter_time, use_trade_time, period, period_end_action, closing_pnl, trading_days,
83
83
  comment, **kwargs):
84
84
  symbols = symbol_list.copy()
85
+ time_frame = kwargs.get('time_frame', '15m')
85
86
  STRATEGY = kwargs.get('strategy_name')
86
87
  mtrades = kwargs.get('max_trades')
87
88
  notify = kwargs.get('notify', False)
@@ -89,8 +90,16 @@ def _mt5_execution(
89
90
  telegram = kwargs.get('telegram', False)
90
91
  bot_token = kwargs.get('bot_token')
91
92
  chat_id = kwargs.get('chat_id')
93
+
94
+ def update_risk(weights):
95
+ if weights is not None:
96
+ for symbol in symbols:
97
+ if symbol not in weights:
98
+ continue
99
+ trade = trades_instances[symbol]
100
+ trade.dailydd = round(weights[symbol], 5)
92
101
 
93
- def _send_notification(self, signal):
102
+ def _send_notification(signal):
94
103
  send_message(message=signal, notify_me=notify,
95
104
  telegram=telegram, token=bot_token, chat_id=chat_id)
96
105
 
@@ -104,8 +113,6 @@ def _mt5_execution(
104
113
  if not mm:
105
114
  return
106
115
  if buys is not None or sells is not None:
107
- logger.info(
108
- f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
109
116
  trades_instances[symbol].break_even(
110
117
  mm=mm, trail=trail, stop_trail=stop_trail,
111
118
  trail_after_points=trail_after_points, be_plus_points=be_plus_points)
@@ -119,10 +126,10 @@ def _mt5_execution(
119
126
  check_mt5_connection()
120
127
  strategy: MT5Strategy = strategy_cls(symbol_list=symbols, mode='live', **kwargs)
121
128
  except Exception as e:
122
- logger.error(f"Error initializing strategy, {e}, STRATEGY={STRATEGY}")
129
+ logger.error(f"Initializing strategy, {e}, STRATEGY={STRATEGY}")
123
130
  return
124
131
  logger.info(
125
- f'Running {STRATEGY} Strategy on {symbols} in {time_frame} Interval ...')
132
+ f'Running {STRATEGY} Strategy in {time_frame} Interval ...')
126
133
 
127
134
  while True:
128
135
  try:
@@ -132,7 +139,9 @@ def _mt5_execution(
132
139
  time.sleep(0.5)
133
140
  positions_orders = {}
134
141
  for type in POSITIONS_TYPES + ORDERS_TYPES:
142
+ positions_orders[type] = {}
135
143
  for symbol in symbols:
144
+ positions_orders[type][symbol] = None
136
145
  func = getattr(trades_instances[symbol], f"get_current_{type}")
137
146
  positions_orders[type][symbol] = func()
138
147
  buys = positions_orders['buys']
@@ -154,12 +163,14 @@ def _mt5_execution(
154
163
  }
155
164
 
156
165
  except Exception as e:
157
- logger.error(f"{e}, STRATEGY={STRATEGY}")
166
+ logger.error(f"Handling Positions and Orders, {e}, STRATEGY={STRATEGY}")
158
167
  continue
159
168
  time.sleep(0.5)
160
169
  try:
161
170
  check_mt5_connection()
162
171
  signals = strategy.calculate_signals()
172
+ weights = strategy.apply_risk_management(optimizer)
173
+ update_risk(weights)
163
174
  except Exception as e:
164
175
  logger.error(f"Calculating signal, {e}, STRATEGY={STRATEGY}")
165
176
  continue
@@ -181,9 +192,10 @@ def _mt5_execution(
181
192
  if signal is not None:
182
193
  signal = 'BMKT' if signal == 'LONG' else signal
183
194
  signal = 'SMKT' if signal == 'SHORT' else signal
184
- info = f"SIGNAL = {signal}, SYMBOL={trade.symbol}, STRATEGY={STRATEGY}"
195
+ info = f"SIGNAL = {signal}, SYMBOL={trade.symbol}, STRATEGY={STRATEGY}, TIMEFRAME={time_frame}"
185
196
  msg = f"Sending {signal} Order ... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}"
186
- logger.info(info)
197
+ if signal not in EXIT_SIGNAL_ACTIONS:
198
+ logger.info(info)
187
199
  if signal in EXIT_SIGNAL_ACTIONS:
188
200
  for exit_signal, actions in EXIT_SIGNAL_ACTIONS.items():
189
201
  for position_type, order_type in actions.items():
@@ -235,13 +247,15 @@ def _mt5_execution(
235
247
  elif signal in SELLS and short_market[symbol]:
236
248
  logger.info(riskmsg)
237
249
  check(buys[symbol], sells[symbol], symbol)
250
+ else:
251
+ check(buys[symbol], sells[symbol], symbol)
238
252
  else:
239
253
  logger.info(
240
254
  f"Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
241
255
  check(buys[symbol], sells[symbol], symbol)
242
256
 
243
257
  except Exception as e:
244
- logger.error(f"{e}, SYMBOL={symbol}, STRATEGY={STRATEGY}")
258
+ logger.error(f"Handling Signals {e}, SYMBOL={symbol}, STRATEGY={STRATEGY}")
245
259
  continue
246
260
  time.sleep((60 * iter_time) - 1.0)
247
261
  if iter_time == 1:
@@ -253,7 +267,6 @@ def _mt5_execution(
253
267
  f"iter_time must be a multiple of the {time_frame} !!!"
254
268
  f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
255
269
  )
256
- print()
257
270
  try:
258
271
  FRIDAY = 'friday'
259
272
  check_mt5_connection()
@@ -434,12 +447,12 @@ class MT5ExecutionEngine():
434
447
  strategy_cls: Strategy,
435
448
  /,
436
449
  mm: bool = True,
450
+ optimizer: str = 'equal',
437
451
  trail: bool = True,
438
452
  stop_trail: Optional[int] = None,
439
453
  trail_after_points: Optional[int] = None,
440
454
  be_plus_points: Optional[int] = None,
441
455
  show_positions_orders: bool = False,
442
- time_frame: str = '15m',
443
456
  iter_time: int | float = 5,
444
457
  use_trade_time: bool = True,
445
458
  period: Literal['day', 'week', 'month'] = 'week',
@@ -455,8 +468,9 @@ class MT5ExecutionEngine():
455
468
  trades_instances : Dictionary of Trade instances
456
469
  strategy_cls : Strategy class to use for trading
457
470
  mm : Enable Money Management. Defaults to False.
471
+ optimizer : Risk management optimizer. Defaults to 'equal'.
472
+ See `bbstrader.models.optimization` module for more information.
458
473
  show_positions_orders : Print open positions and orders. Defaults to False.
459
- time_frame : Time frame to trade. Defaults to '15m'.
460
474
  iter_time : Interval to check for signals and `mm`. Defaults to 5.
461
475
  use_trade_time : Open trades after the time is completed. Defaults to True.
462
476
  period : Period to trade. Defaults to 'week'.
@@ -466,6 +480,7 @@ class MT5ExecutionEngine():
466
480
  trading_days : Trading days in a week. Defaults to monday to friday.
467
481
  comment: Comment for trades. Defaults to None.
468
482
  **kwargs: Additional keyword arguments
483
+ _ time_frame : Time frame to trade. Defaults to '15m'.
469
484
  - strategy_name (Optional[str]): Strategy name. Defaults to None.
470
485
  - max_trades (Dict[str, int]): Maximum trades per symbol. Defaults to None.
471
486
  - notify (bool): Enable notifications. Defaults to False.
@@ -495,12 +510,12 @@ class MT5ExecutionEngine():
495
510
  self.trades_instances = trades_instances
496
511
  self.strategy_cls = strategy_cls
497
512
  self.mm = mm
513
+ self.optimizer = optimizer
498
514
  self.trail = trail
499
515
  self.stop_trail = stop_trail
500
516
  self.trail_after_points = trail_after_points
501
517
  self.be_plus_points = be_plus_points
502
518
  self.show_positions_orders = show_positions_orders
503
- self.time_frame = time_frame
504
519
  self.iter_time = iter_time
505
520
  self.use_trade_time = use_trade_time
506
521
  self.period = period
@@ -517,12 +532,12 @@ class MT5ExecutionEngine():
517
532
  self.trades_instances,
518
533
  self.strategy_cls,
519
534
  mm=self.mm,
535
+ optimizer=self.optimizer,
520
536
  trail=self.trail,
521
537
  stop_trail=self.stop_trail,
522
538
  trail_after_points=self.trail_after_points,
523
539
  be_plus_points=self.be_plus_points,
524
540
  show_positions_orders=self.show_positions_orders,
525
- time_frame=self.time_frame,
526
541
  iter_time=self.iter_time,
527
542
  use_trade_time=self.use_trade_time,
528
543
  period=self.period,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bbstrader
3
- Version: 0.1.91
3
+ Version: 0.1.93
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/
@@ -46,6 +46,7 @@ Requires-Dist: tqdm
46
46
  Requires-Dist: scikit-learn
47
47
  Requires-Dist: notify-py
48
48
  Requires-Dist: python-telegram-bot
49
+ Requires-Dist: pyportfolioopt
49
50
  Provides-Extra: mt5
50
51
  Requires-Dist: MetaTrader5 ; extra == 'mt5'
51
52
 
@@ -0,0 +1,32 @@
1
+ bbstrader/__ini__.py,sha256=rCTy-3g2RlDAgIZ7cSET9-I74MwuCXpp-xGVTFS8NNc,482
2
+ bbstrader/config.py,sha256=_AD_Cd-w5zyabm1CBPNGhzcZuSjThB7jyzTcjbrIlUQ,3618
3
+ bbstrader/tseries.py,sha256=qJKLxHnPOjB7dXon-ITK7vU1fAuvl8evzET6lSSnijQ,53572
4
+ bbstrader/btengine/__init__.py,sha256=OaXZTjgDwqWrjPq-CNE4kJkmriKXt9t5pIghW1MDTeo,2911
5
+ bbstrader/btengine/backtest.py,sha256=A3S84jpGTE_zhguOEGoGu6H_4ws4Iq5sf0n7TZaUYfQ,14615
6
+ bbstrader/btengine/data.py,sha256=A6jUqDnjl-w1OSzbLLPfS1WfJ8Se25AqigJs9pbe0wc,17966
7
+ bbstrader/btengine/event.py,sha256=zF_ST4tcjV5uJJVV1IbRXQgCLbca2R2fmE7A2MaIno4,8748
8
+ bbstrader/btengine/execution.py,sha256=Fs6Hk64DxEOEVzAjsQ3CIVvYifWLLgkDjOixSh_Ghsc,10282
9
+ bbstrader/btengine/performance.py,sha256=WTYzB50lUD5aShPIEebbQPlaC2NVW6VfxdgGHjcIIAw,10707
10
+ bbstrader/btengine/portfolio.py,sha256=wCRmGxaZvihUPlXIlZp9cQo9fqPP-Tk5oALjknMfnos,16055
11
+ bbstrader/btengine/strategy.py,sha256=6IN1KQ-a-IQgbCEOflKTtGh-ouztwsVjik6TuMg6CY0,30210
12
+ bbstrader/metatrader/__init__.py,sha256=OLVOB_EieEb1P72I8V4Vem8kQWJ__D_L3c_wfwqY-9k,211
13
+ bbstrader/metatrader/account.py,sha256=3KeGLZ397kctf3EW_y8n9ENswAMU0tBQJuX_L0VXMrI,53909
14
+ bbstrader/metatrader/rates.py,sha256=1dJHbVqoT41m3EhF0wRe7dSGe5Kf3o5Maskkw-i5qsQ,20810
15
+ bbstrader/metatrader/risk.py,sha256=8NnH1kRvWd_JLieCpVty6hHKz2awrIQV2c8oykxELh0,26596
16
+ bbstrader/metatrader/trade.py,sha256=uigDah9n_rVJiwSslTAArLP94sde1dxYyGyRVIPPgb4,70210
17
+ bbstrader/metatrader/utils.py,sha256=BTaZun4DKWpCxBBzY0SLQqqz7n_7F_R1F59APfyaa3E,17666
18
+ bbstrader/models/__init__.py,sha256=mpxtXYEcE8hwNDbzJf8MRqnBIa2T1voraEk0U0ri53c,437
19
+ bbstrader/models/factors.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ bbstrader/models/ml.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ bbstrader/models/optimization.py,sha256=JlMsda9L-ADSgw4YPE4o3CsL1Yyxfeahf9kUb-EZqqM,6699
22
+ bbstrader/models/portfolios.py,sha256=dFTZ3maRVY_O3UOIoRlLCbAow3SiLTQYt1q5DNaRUxE,8223
23
+ bbstrader/models/risk.py,sha256=Pm_WoGI-vtPW75fwo_7ptF2Br-xQYBwrAAOIgqDQmy8,15120
24
+ bbstrader/trading/__init__.py,sha256=3CCzV5rQbH8NthjDJhD0_2FABvpiCmkeC9cVeoW7bi4,438
25
+ bbstrader/trading/execution.py,sha256=p_SKUziBBDuGiOmuxBsgBvxWu5nDVGZmtQBKw8OoZgE,25967
26
+ bbstrader/trading/scripts.py,sha256=rQmnG_4F_MuUEc96RXpAQT4kXrC-FkscsgHKgDAR_-Y,1902
27
+ bbstrader/trading/strategies.py,sha256=ztKNL4Nmlb-4N8_cq0OJyn3E2cRcdKdKu3FeTbZrHsU,36402
28
+ bbstrader-0.1.93.dist-info/LICENSE,sha256=1EudjwwP2oTJy8Vh0e-Kzv8VZZU95y-t6c3DYhR51uc,1115
29
+ bbstrader-0.1.93.dist-info/METADATA,sha256=XByGtsQ885U8oZpZOR9DPhQjdfi5eHbScJyGlcbbjxM,9932
30
+ bbstrader-0.1.93.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
31
+ bbstrader-0.1.93.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
32
+ bbstrader-0.1.93.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (75.5.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,31 +0,0 @@
1
- bbstrader/__ini__.py,sha256=rCTy-3g2RlDAgIZ7cSET9-I74MwuCXpp-xGVTFS8NNc,482
2
- bbstrader/config.py,sha256=_AD_Cd-w5zyabm1CBPNGhzcZuSjThB7jyzTcjbrIlUQ,3618
3
- bbstrader/tseries.py,sha256=qJKLxHnPOjB7dXon-ITK7vU1fAuvl8evzET6lSSnijQ,53572
4
- bbstrader/btengine/__init__.py,sha256=OaXZTjgDwqWrjPq-CNE4kJkmriKXt9t5pIghW1MDTeo,2911
5
- bbstrader/btengine/backtest.py,sha256=HDuCNUETLUBZ0R8AVLeT5gZVVk8UQ6oeReUtDvUFhIk,13681
6
- bbstrader/btengine/data.py,sha256=zeF3O7_vPT3NvAxlAcUS9OoxF09XhypDhGiVRml3U1A,17622
7
- bbstrader/btengine/event.py,sha256=DQTmUQ4l4yZgL47hW1dGv__CecNF-rDqsyFst5kCQvA,8453
8
- bbstrader/btengine/execution.py,sha256=gijnRvknNi921TjtF9wSyBG_nI0e2cda2NKIm3LJh1k,10222
9
- bbstrader/btengine/performance.py,sha256=bKwj1_CSygvggLKTXPASp2eWhDdwyCf06ayUaXwdh4E,10655
10
- bbstrader/btengine/portfolio.py,sha256=uf1fx0RuTTQNNxiYmCYW_sh6sBWbtNq4uIRusKA5MdY,15399
11
- bbstrader/btengine/strategy.py,sha256=ktTzlJzSTo9zhZsILwj2vtPHjAkI-aSnKA8PcT3nF6Y,23206
12
- bbstrader/metatrader/__init__.py,sha256=OLVOB_EieEb1P72I8V4Vem8kQWJ__D_L3c_wfwqY-9k,211
13
- bbstrader/metatrader/account.py,sha256=hVH83vnAdfMOzUsF9PiWelqxa7HaLSTpCVlUEePnSZg,53912
14
- bbstrader/metatrader/rates.py,sha256=xWTsM8PMPGdgwoPXcIRvHpJ1FKRIXHJcn4qrgmA7XRg,18737
15
- bbstrader/metatrader/risk.py,sha256=8FcLY8pgV8_rxAcjx179sdqaMu66wl-fDFPZvdihfUw,25953
16
- bbstrader/metatrader/trade.py,sha256=68hYIA01OPApRgGNdS1YFgM83ZemkCmv6mTXOCS_kWc,70052
17
- bbstrader/metatrader/utils.py,sha256=BTaZun4DKWpCxBBzY0SLQqqz7n_7F_R1F59APfyaa3E,17666
18
- bbstrader/models/__init__.py,sha256=6tAj9V9vgwesgPVMKznwRB3k8-Ec8Q73Di5p2UO0qlA,274
19
- bbstrader/models/factors.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- bbstrader/models/ml.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- bbstrader/models/optimization.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- bbstrader/models/risk.py,sha256=Pm_WoGI-vtPW75fwo_7ptF2Br-xQYBwrAAOIgqDQmy8,15120
23
- bbstrader/trading/__init__.py,sha256=3CCzV5rQbH8NthjDJhD0_2FABvpiCmkeC9cVeoW7bi4,438
24
- bbstrader/trading/execution.py,sha256=5mGanLIB1nPfbXA9r_uKGDCkcV-ilVgZmssF7Oh4SeI,25186
25
- bbstrader/trading/scripts.py,sha256=rQmnG_4F_MuUEc96RXpAQT4kXrC-FkscsgHKgDAR_-Y,1902
26
- bbstrader/trading/strategies.py,sha256=ztKNL4Nmlb-4N8_cq0OJyn3E2cRcdKdKu3FeTbZrHsU,36402
27
- bbstrader-0.1.91.dist-info/LICENSE,sha256=1EudjwwP2oTJy8Vh0e-Kzv8VZZU95y-t6c3DYhR51uc,1115
28
- bbstrader-0.1.91.dist-info/METADATA,sha256=ZwHR-DYmIhULp_5dh48oQ_lGR0VYQnkGG8GcaQe1KP4,9901
29
- bbstrader-0.1.91.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
30
- bbstrader-0.1.91.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
31
- bbstrader-0.1.91.dist-info/RECORD,,