bbstrader 0.1.9__py3-none-any.whl → 0.1.92__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bbstrader might be problematic. Click here for more details.

bbstrader/tseries.py CHANGED
@@ -6,32 +6,35 @@ tasks such as cointegration testing, volatility modeling,
6
6
  and filter-based estimation to assist in trading strategy development,
7
7
  market analysis, and financial data exploration.
8
8
  """
9
- import numpy as np
10
- import pandas as pd
11
9
  import pprint
12
10
  import warnings
11
+ import numpy as np
12
+ import pandas as pd
13
+ from tqdm import tqdm
13
14
  import yfinance as yf
14
- from arch import arch_model
15
- from statsmodels.tsa.arima.model import ARIMA
16
15
  import pmdarima as pm
17
- import matplotlib.pyplot as plt
16
+ import seaborn as sns
18
17
  import statsmodels.api as sm
18
+ import matplotlib.pyplot as plt
19
19
  import statsmodels.tsa.stattools as ts
20
- from numpy import cumsum, log, polyfit, sqrt, std, subtract
21
- from numpy.random import randn
22
20
  from hurst import compute_Hc
21
+ from arch import arch_model
23
22
  from scipy.optimize import minimize
24
23
  from filterpy.kalman import KalmanFilter
24
+ from pykalman import KalmanFilter as PyKalmanFilter
25
25
  from statsmodels.tsa.vector_ar.vecm import coint_johansen
26
26
  from statsmodels.graphics.tsaplots import plot_acf
27
+ from statsmodels.tsa.stattools import adfuller, coint
28
+ from statsmodels.tsa.arima.model import ARIMA
29
+ from statsmodels.tsa.vector_ar.var_model import VAR
30
+ from sklearn.model_selection import GridSearchCV
31
+ from sklearn.tree import DecisionTreeClassifier
32
+ from sklearn.linear_model import LogisticRegressionCV
33
+ from statsmodels.stats.diagnostic import acorr_ljungbox
27
34
  from itertools import combinations
28
35
  from typing import Union, List, Tuple
29
- from statsmodels.stats.diagnostic import acorr_ljungbox
30
- from arch.utility.exceptions import ConvergenceWarning as ArchWarning
31
- from statsmodels.tools.sm_exceptions import ConvergenceWarning as StatsWarning
32
36
  warnings.filterwarnings("ignore")
33
- warnings.filterwarnings("ignore", category=StatsWarning, module='statsmodels')
34
- warnings.filterwarnings("ignore", category=ArchWarning, module='arch')
37
+
35
38
 
36
39
 
37
40
  __all__ = [
@@ -117,18 +120,23 @@ def fit_best_arima(window_data: Union[pd.Series, np.ndarray]):
117
120
  stepwise=True
118
121
  )
119
122
  final_order = model.order
120
- try:
121
- best_arima_model = ARIMA(
122
- window_data + 1e-5, order=final_order, missing='drop').fit()
123
- return best_arima_model
124
- except np.linalg.LinAlgError:
125
- # Catch specific linear algebra errors
126
- print("LinAlgError occurred, skipping this data point.")
127
- return None
128
- except Exception as e:
129
- # Catch any other unexpected errors and log them
130
- print(f"An error occurred: {e}")
131
- return None
123
+ from arch.utility.exceptions import ConvergenceWarning as ArchWarning
124
+ from statsmodels.tools.sm_exceptions import ConvergenceWarning as StatsWarning
125
+ with warnings.catch_warnings():
126
+ warnings.filterwarnings("ignore", category=StatsWarning, module='statsmodels')
127
+ warnings.filterwarnings("ignore", category=ArchWarning, module='arch')
128
+ try:
129
+ best_arima_model = ARIMA(
130
+ window_data + 1e-5, order=final_order, missing='drop').fit()
131
+ return best_arima_model
132
+ except np.linalg.LinAlgError:
133
+ # Catch specific linear algebra errors
134
+ print("LinAlgError occurred, skipping this data point.")
135
+ return None
136
+ except Exception as e:
137
+ # Catch any other unexpected errors and log them
138
+ print(f"An error occurred: {e}")
139
+ return None
132
140
 
133
141
 
134
142
  def fit_garch(window_data: Union[pd.Series, np.ndarray]):
@@ -684,10 +692,10 @@ def _hurst(ts):
684
692
  lags = range(2, 100)
685
693
 
686
694
  # Calculate the array of the variances of the lagged differences
687
- tau = [sqrt(std(subtract(ts[lag:], ts[:-lag]))) for lag in lags]
695
+ tau = [np.sqrt(np.std(np.subtract(ts[lag:], ts[:-lag]))) for lag in lags]
688
696
 
689
697
  # Use a linear fit to estimate the Hurst Exponent
690
- poly = polyfit(log(lags), log(tau), 1)
698
+ poly = np.polyfit(np.log(lags), np.log(tau), 1)
691
699
 
692
700
  # Return the Hurst exponent from the polyfit output
693
701
  return poly[0] * 2.0
@@ -721,9 +729,9 @@ def run_hurst_test(symbol: str, start: str, end: str):
721
729
  data = yf.download(symbol, start=start, end=end)
722
730
 
723
731
  # Create a Geometric Brownian Motion, Mean-Reverting, and Trending Series
724
- gbm = log(cumsum(randn(100000))+1000)
725
- mr = log(randn(100000)+1000)
726
- tr = log(cumsum(randn(100000)+1)+1000)
732
+ gbm = np.log(np.cumsum(np.random.randn(100000))+1000)
733
+ mr = np.log(np.random.randn(100000)+1000)
734
+ tr = np.log(np.cumsum(np.random.randn(100000)+1)+1000)
727
735
 
728
736
  # Output the Hurst Exponent for each of the series
729
737
  print(f"\nHurst(GBM): {_hurst(gbm)}")
@@ -898,7 +906,7 @@ class KalmanFilterModel():
898
906
  You can learn more here https://en.wikipedia.org/wiki/Kalman_filter
899
907
  """
900
908
 
901
- def __init__(self, tickers: list | tuple, **kwargs):
909
+ def __init__(self, tickers: List | Tuple, **kwargs):
902
910
  """
903
911
  Initializes the Kalman Filter strategy.
904
912
 
@@ -907,7 +915,7 @@ class KalmanFilterModel():
907
915
  A list or tuple of ticker symbols representing financial instruments.
908
916
 
909
917
  kwargs : Keyword arguments for additional parameters,
910
- specifically `delta` and `vt`
918
+ specifically `delta` and `vt`
911
919
  """
912
920
  self.tickers = tickers
913
921
  assert self.tickers is not None
@@ -936,7 +944,8 @@ class KalmanFilterModel():
936
944
 
937
945
  return kf
938
946
 
939
- def calc_slope_intercep(self, prices: np.ndarray):
947
+ Array = np.ndarray
948
+ def calc_slope_intercep(self, prices: Array) -> Tuple:
940
949
  """
941
950
  Calculates and returns the slope and intercept
942
951
  of the relationship between the provided prices using the Kalman Filter.
@@ -957,8 +966,8 @@ class KalmanFilterModel():
957
966
  intercept = kf.x.copy().flatten()[1]
958
967
 
959
968
  return slope, intercept
960
-
961
- def calculate_etqt(self, prices: np.ndarray):
969
+
970
+ def calculate_etqt(self, prices: Array) -> Tuple:
962
971
  """
963
972
  Calculates the forecast error and standard deviation of the predictions
964
973
  using the Kalman Filter.
@@ -1144,21 +1153,19 @@ class OrnsteinUhlenbeck():
1144
1153
  ) / sigma**2 + 0.5 * n * np.log(2 * np.pi * sigma**2)
1145
1154
  return neg_ll
1146
1155
 
1147
- def simulate_process(self, rts=None, n=100, p=None):
1156
+ def simulate_process(self, returns=None, n=100, p=None):
1148
1157
  """
1149
1158
  Simulates the OU process multiple times .
1150
1159
 
1151
1160
  Args:
1152
- rts (np.ndarray): Historical returns.
1161
+ returns (np.ndarray): Historical returns.
1153
1162
  n (int): Number of simulations to perform.
1154
1163
  p (int): Number of time steps.
1155
1164
 
1156
1165
  Returns:
1157
1166
  np.ndarray: 2D array representing simulated processes.
1158
1167
  """
1159
- if rts is not None:
1160
- returns = rts
1161
- else:
1168
+ if returns is None:
1162
1169
  returns = self.returns
1163
1170
  if p is not None:
1164
1171
  T = p
@@ -1180,3 +1187,231 @@ class OrnsteinUhlenbeck():
1180
1187
  self.sigma_hat * dW_matrix[:, t]
1181
1188
  )
1182
1189
  return simulations_matrix
1190
+
1191
+
1192
+ def remove_correlated_assets(df: pd.DataFrame, cutoff=.99):
1193
+ corr = df.corr().stack()
1194
+ corr = corr[corr < 1]
1195
+ to_check = corr[corr.abs() > cutoff].index
1196
+ keep, drop = set(), set()
1197
+ for s1, s2 in to_check:
1198
+ if s1 not in keep:
1199
+ if s2 not in keep:
1200
+ keep.add(s1)
1201
+ drop.add(s2)
1202
+ else:
1203
+ drop.add(s1)
1204
+ else:
1205
+ keep.discard(s2)
1206
+ drop.add(s2)
1207
+ return df.drop(drop, axis=1)
1208
+
1209
+
1210
+ def check_stationarity(df: pd.DataFrame):
1211
+ results = []
1212
+ for ticker, prices in df.items():
1213
+ results.append([ticker, adfuller(prices, regression='ct')[1]])
1214
+ return pd.DataFrame(results, columns=['ticker', 'adf']).sort_values('adf')
1215
+
1216
+
1217
+ def remove_stationary_assets(df: pd.DataFrame, pval=.05):
1218
+ test_result = check_stationarity(df)
1219
+ stationary = test_result.loc[test_result.adf <= pval, 'ticker'].tolist()
1220
+ return df.drop(stationary, axis=1).sort_index()
1221
+
1222
+
1223
+ def select_assets(df: pd.DataFrame, n=100, start=None, end=None):
1224
+ idx = pd.IndexSlice
1225
+ start = start or df.index.get_level_values('date').min()
1226
+ end = end or df.index.get_level_values('date').max()
1227
+ df = (df
1228
+ .loc[lambda df: ~df.index.duplicated()]
1229
+ .sort_index()
1230
+ .loc[idx[:, f'{start}':f'{end}'], :]
1231
+ .assign(dv=lambda df: df.close.mul(df.volume)))
1232
+
1233
+ # select n assets with the highest average trading volume
1234
+ # we are taking a shortcut to simplify; should select
1235
+ # based on historical only, e.g. yearly rolling avg
1236
+ most_traded = (df.groupby(level='ticker')
1237
+ .dv.mean()
1238
+ .nlargest(n=n).index)
1239
+
1240
+ df = (df.loc[idx[most_traded, :], 'close']
1241
+ .unstack('ticker')
1242
+ .ffill(limit=5) # fill up to five values
1243
+ .dropna(axis=1)) # remove assets with any missing values
1244
+
1245
+ df = remove_correlated_assets(df)
1246
+ return remove_stationary_assets(df).sort_index()
1247
+
1248
+ def compute_pair_metrics(security: pd.Series, candidates: pd.DataFrame):
1249
+ security = security.div(security.iloc[0])
1250
+ ticker = security.name
1251
+ candidates = candidates.div(candidates.iloc[0])
1252
+ spreads = candidates.sub(security, axis=0)
1253
+ n, m = spreads.shape
1254
+ X = np.ones(shape=(n, 2))
1255
+ X[:, 1] = np.arange(1, n + 1)
1256
+
1257
+ # compute drift
1258
+ drift = ((np.linalg.inv(X.T @ X) @ X.T @ spreads).iloc[1]
1259
+ .to_frame('drift'))
1260
+
1261
+ # compute volatility
1262
+ vol = spreads.std().to_frame('vol')
1263
+
1264
+ # return correlation
1265
+ corr_ret = (candidates.pct_change()
1266
+ .corrwith(security.pct_change())
1267
+ .to_frame('corr_ret'))
1268
+
1269
+ # normalized price series correlation
1270
+ corr = candidates.corrwith(security).to_frame('corr')
1271
+ metrics = drift.join(vol).join(corr).join(corr_ret).assign(n=n)
1272
+
1273
+ tests = []
1274
+ # run cointegration tests
1275
+ for candidate, prices in tqdm(candidates.items()):
1276
+ df = pd.DataFrame({'s1': security, 's2': prices})
1277
+ var = VAR(df.values)
1278
+ lags = var.select_order() # select VAR order
1279
+ k_ar_diff = lags.selected_orders['aic']
1280
+ # Johansen Test with constant Term and estd. lag order
1281
+ cj0 = coint_johansen(df, det_order=0, k_ar_diff=k_ar_diff)
1282
+ # Engle-Granger Tests
1283
+ t1, p1 = coint(security, prices, trend='c')[:2]
1284
+ t2, p2 = coint(prices, security, trend='c')[:2]
1285
+ tests.append([ticker, candidate, t1, p1, t2, p2,
1286
+ k_ar_diff, *cj0.lr1])
1287
+ columns = ['s1', 's2', 't1', 'p1', 't2', 'p2', 'k_ar_diff', 'trace0', 'trace1']
1288
+ tests = pd.DataFrame(tests, columns=columns).set_index('s2')
1289
+ return metrics.join(tests)
1290
+
1291
+ CRITICAL_VALUES = {
1292
+ 0: {.9: 13.4294, .95: 15.4943, .99: 19.9349},
1293
+ 1: {.9: 2.7055, .95: 3.8415, .99: 6.6349}
1294
+ }
1295
+
1296
+ def find_cointegrated_pairs(securities: pd.DataFrame, candidates: pd.DataFrame,
1297
+ n=None, start=None, stop=None):
1298
+ trace0_cv = CRITICAL_VALUES[0][.95] # critical value for 0 cointegration relationships
1299
+ trace1_cv = CRITICAL_VALUES[1][.95] # critical value for 1 cointegration relationship
1300
+ spreads = []
1301
+ if start is not None and stop is not None:
1302
+ securities = securities.loc[str(start): str(stop), :]
1303
+ candidates = candidates.loc[str(start): str(stop), :]
1304
+ for i, (ticker, prices) in enumerate(securities.items(), 1):
1305
+ df = compute_pair_metrics(prices, candidates)
1306
+ spreads.append(df.set_index('s1', append=True))
1307
+ spreads = pd.concat(spreads)
1308
+ spreads.index.names = ['s2', 's1']
1309
+ spreads = spreads.swaplevel()
1310
+ spreads['t'] = spreads[['t1', 't2']].min(axis=1)
1311
+ spreads['p'] = spreads[['p1', 'p2']].min(axis=1)
1312
+ spreads['joh_sig'] = ((spreads.trace0 > trace0_cv) &
1313
+ (spreads.trace1 > trace1_cv)).astype(int)
1314
+ spreads['eg_sig'] = (spreads.p < .05).astype(int)
1315
+ spreads['s1_dep'] = spreads.p1 < spreads.p2
1316
+ spreads['coint'] = (spreads.joh_sig & spreads.eg_sig).astype(int)
1317
+ # select top n pairs
1318
+ if n is not None:
1319
+ top_pairs = (spreads.query('coint == 1')
1320
+ .sort_values('t', ascending=False)
1321
+ .head(n))
1322
+ else:
1323
+ top_pairs = spreads.query('coint == 1')
1324
+ return top_pairs
1325
+
1326
+ def analyze_cointegrated_pairs(spreads: pd.DataFrame, plot_coint=False, cosstab=False,
1327
+ heuristics=False, log_reg=False, decis_tree=False):
1328
+ if plot_coint:
1329
+ trace0_cv = CRITICAL_VALUES[0][.95]
1330
+ spreads = spreads.reset_index()
1331
+ sns.scatterplot(x=np.log1p(spreads.t.abs()),
1332
+ y=np.log1p(spreads.trace1),
1333
+ hue='coint', data=spreads[spreads.trace0>trace0_cv]);
1334
+ fig, axes = plt.subplots(ncols=4, figsize=(20, 5))
1335
+ for i, heuristic in enumerate(['drift', 'vol', 'corr', 'corr_ret']):
1336
+ sns.boxplot(x='coint', y=heuristic, data=spreads, ax=axes[i])
1337
+ fig.tight_layout();
1338
+ if heuristics:
1339
+ spreads = spreads.reset_index()
1340
+ h = spreads.groupby(spreads.coint)[
1341
+ ['drift', 'vol', 'corr']].describe().stack(level=0).swaplevel().sort_index()
1342
+ print(h)
1343
+ if log_reg:
1344
+ y = spreads.coint
1345
+ X = spreads[['drift', 'vol', 'corr', 'corr_ret']]
1346
+ log_reg = LogisticRegressionCV(Cs=np.logspace(-10, 10, 21),
1347
+ class_weight='balanced',
1348
+ scoring='roc_auc')
1349
+ log_reg.fit(X=X, y=y)
1350
+ Cs = log_reg.Cs_
1351
+ scores = pd.DataFrame(log_reg.scores_[True], columns=Cs).mean()
1352
+ scores.plot(logx=True);
1353
+ res = f'C:{np.log10(scores.idxmax()):.2f}, AUC: {scores.max():.2%}'
1354
+ print(res)
1355
+ print(log_reg.coef_)
1356
+ if decis_tree:
1357
+ model = DecisionTreeClassifier(class_weight='balanced')
1358
+ decision_tree = GridSearchCV(model,
1359
+ param_grid={'max_depth': list(range(1, 10))},
1360
+ cv=5,
1361
+ scoring='roc_auc')
1362
+ y = spreads.coint
1363
+ X = spreads[['drift', 'vol', 'corr', 'corr_ret']]
1364
+ decision_tree.fit(X, y)
1365
+ res = f'{decision_tree.best_score_:.2%}, Depth: {decision_tree.best_params_["max_depth"]}'
1366
+ print(res)
1367
+ if cosstab:
1368
+ pd.set_option('display.float_format', lambda x: f'{x:.2%}')
1369
+ print(pd.crosstab(spreads.eg_sig, spreads.joh_sig))
1370
+ print(pd.crosstab(spreads.eg_sig, spreads.joh_sig, normalize=True))
1371
+
1372
+
1373
+ def select_candidate_pairs(pairs: pd.DataFrame):
1374
+ candidates = pairs.query('coint == 1').copy()
1375
+ candidates['y'] = candidates.apply(lambda x: x.s1 if x.s1_dep else x.s2, axis=1)
1376
+ candidates['x'] = candidates.apply(lambda x: x.s2 if x.s1_dep else x.s1, axis=1)
1377
+ candidates.drop(['s1_dep', 's1', 's2'], axis=1)
1378
+ return candidates[['x', 'y']].to_dict(orient='records')
1379
+
1380
+
1381
+ def KFSmoother(self, prices: pd.Series | np.ndarray) -> pd.Series | np.ndarray:
1382
+ """Estimate rolling mean using Kalman Smoothing."""
1383
+ kf = PyKalmanFilter(
1384
+ transition_matrices=np.eye(1),
1385
+ observation_matrices=np.eye(1),
1386
+ initial_state_mean=0,
1387
+ initial_state_covariance=1,
1388
+ observation_covariance=1,
1389
+ transition_covariance=0.05
1390
+ )
1391
+ if isinstance(prices, pd.Series):
1392
+ state_means, _ = kf.filter(prices.values)
1393
+ return pd.Series(state_means.flatten(), index=prices.index)
1394
+ elif isinstance(prices, np.ndarray):
1395
+ state_means, _ = kf.filter(prices)
1396
+ return state_means.flatten()
1397
+
1398
+
1399
+ def KFHedgeRatio(self, x: pd.Series, y: pd.Series) -> np.ndarray:
1400
+ """Estimate Hedge Ratio using Kalman Filter."""
1401
+ delta = 1e-3
1402
+ trans_cov = delta / (1 - delta) * np.eye(2)
1403
+ obs_mat = np.expand_dims(np.vstack([[x], [np.ones(len(x))]]).T, axis=1)
1404
+
1405
+ kf = PyKalmanFilter(
1406
+ n_dim_obs=1, n_dim_state=2,
1407
+ initial_state_mean=[0, 0],
1408
+ initial_state_covariance=np.ones((2, 2)),
1409
+ transition_matrices=np.eye(2),
1410
+ observation_matrices=obs_mat,
1411
+ observation_covariance=2,
1412
+ transition_covariance=trans_cov
1413
+ )
1414
+ state_means, _ = kf.filter(y.values)
1415
+ # Indexing with [:, 0] in state_means[:, 0] extracts only the first state variable of
1416
+ # each Kalman Filter estimate, which is the estimated hedge ratio.
1417
+ return -state_means[:, 0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bbstrader
3
- Version: 0.1.9
3
+ Version: 0.1.92
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/
@@ -34,12 +34,21 @@ Requires-Dist: seaborn
34
34
  Requires-Dist: statsmodels
35
35
  Requires-Dist: matplotlib
36
36
  Requires-Dist: filterpy
37
+ Requires-Dist: pykalman
37
38
  Requires-Dist: pytest
38
39
  Requires-Dist: CurrencyConverter
39
40
  Requires-Dist: tabulate
41
+ Requires-Dist: python-dotenv
40
42
  Requires-Dist: ipython
41
- Requires-Dist: quantstats
42
- Requires-Dist: Metatrader5
43
+ Requires-Dist: QuantStats
44
+ Requires-Dist: exchange-calendars
45
+ Requires-Dist: tqdm
46
+ Requires-Dist: scikit-learn
47
+ Requires-Dist: notify-py
48
+ Requires-Dist: python-telegram-bot
49
+ Requires-Dist: pyportfolioopt
50
+ Provides-Extra: mt5
51
+ Requires-Dist: MetaTrader5 ; extra == 'mt5'
43
52
 
44
53
  # Simplified Investment & Trading Toolkit
45
54
  ![bbstrader](https://github.com/bbalouki/bbstrader/blob/main/assets/bbstrader_logo.png?raw=true)
@@ -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=bgQNiS_kb1zWyX_8_OIDGTBlHGC5pw7ZMIQ5csYDahA,14115
6
+ bbstrader/btengine/data.py,sha256=A6jUqDnjl-w1OSzbLLPfS1WfJ8Se25AqigJs9pbe0wc,17966
7
+ bbstrader/btengine/event.py,sha256=zF_ST4tcjV5uJJVV1IbRXQgCLbca2R2fmE7A2MaIno4,8748
8
+ bbstrader/btengine/execution.py,sha256=i-vI9LGqVtEIKfH_T5Airv-gI4t1X75CfuFIuQdogkA,10187
9
+ bbstrader/btengine/performance.py,sha256=bKwj1_CSygvggLKTXPASp2eWhDdwyCf06ayUaXwdh4E,10655
10
+ bbstrader/btengine/portfolio.py,sha256=9Jw0UA2gPu-YkUNenoMSHt8t-axtbl6veWXgcRMTQ14,16074
11
+ bbstrader/btengine/strategy.py,sha256=w5vwlpJNIVjslaHprWDr-3j-JJPiO1EvqeyrJmorByE,25721
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=1dJHbVqoT41m3EhF0wRe7dSGe5Kf3o5Maskkw-i5qsQ,20810
15
+ bbstrader/metatrader/risk.py,sha256=8FcLY8pgV8_rxAcjx179sdqaMu66wl-fDFPZvdihfUw,25953
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=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=0ZOCinveMCSxpL4gTBO5lbZ6jb1HXp0CeIjO-_fNGro,6521
22
+ bbstrader/models/portfolios.py,sha256=TyLcYwxW86t1qH6YS5xBaiKB9Owezq3ovffjR0Othlw,8184
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=Ze_YFrdgU9mRZsS4J08lZNE77HBwxrun5rdqHkS4dzE,25348
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.92.dist-info/LICENSE,sha256=1EudjwwP2oTJy8Vh0e-Kzv8VZZU95y-t6c3DYhR51uc,1115
29
+ bbstrader-0.1.92.dist-info/METADATA,sha256=LZdzZUHKait-lIvprJ85AxfRuHp8VE5QPS69y8uZIIs,9932
30
+ bbstrader-0.1.92.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
31
+ bbstrader-0.1.92.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
32
+ bbstrader-0.1.92.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,26 +0,0 @@
1
- bbstrader/__ini__.py,sha256=lKhqaZN2fuGU2DP3oufJ_XcFlznnyNTlhklWcOaTPMY,444
2
- bbstrader/tseries.py,sha256=i6Vl5QeySMj51n4EchvAaGnHl1xQLdvO7Eka3QkP_Mg,43647
3
- bbstrader/btengine/__init__.py,sha256=gNANXdJ_b4JQaHgqXTbRSZzJOdZRQop5258GQYIAY8s,2949
4
- bbstrader/btengine/backtest.py,sha256=bUnuxHByddDkY2VzCTSBBOIbdRzsy9VXROFYL3VNwzE,12450
5
- bbstrader/btengine/data.py,sha256=B2cqkZZGIhedqMHeZBNB6RHGyeP-TFGOSlMoZq4uFUw,14007
6
- bbstrader/btengine/event.py,sha256=dY2DFwRPDCEdsX7GnLiwpth73tOpnbjgqIqbkNMZu1U,8286
7
- bbstrader/btengine/execution.py,sha256=noat6ZxhbBYZQ0GLFYw78enp0vZCbeoGyOok4aSjWCA,5077
8
- bbstrader/btengine/performance.py,sha256=STb5xJQiTa25xZdYYmjpKW46iWWcB_-6wB6PCPbhREI,10745
9
- bbstrader/btengine/portfolio.py,sha256=LFsl38GbdYTJwd08ep1n4_1FPsRodQz1DHDPVSj-rBU,14882
10
- bbstrader/btengine/strategy.py,sha256=uLQdsEzI8-86Cj7HWkL8blVFxT0sNxEcLVnkBil6ZLk,1550
11
- bbstrader/metatrader/__init__.py,sha256=y4JLz05esm3PKerHMgtd3dmRpa7yUvWVuj_xOnlhXSA,261
12
- bbstrader/metatrader/account.py,sha256=5f6emtVEvXmxUnlmqFpC4hhL8sxOzOrdp7_pwJhXPhw,44716
13
- bbstrader/metatrader/rates.py,sha256=FyG4WWq1NaCdHNxEW_8u3eOt0Wms7E_U4FIOIcUDWco,9605
14
- bbstrader/metatrader/risk.py,sha256=Nz3RqkVFNA5FeXIqINjmGUaj88YaYGw23XsVOvaHjB8,25831
15
- bbstrader/metatrader/trade.py,sha256=M5ho8x_NRVCEWO_dmDCiJrhhbcfpnia6tOmQRfV7p4U,62967
16
- bbstrader/metatrader/utils.py,sha256=xc5VYQ-eejpHIPJIhLi8vxAF5zjix5NSXywXu2Eb7LI,19186
17
- bbstrader/models/__init__.py,sha256=6tAj9V9vgwesgPVMKznwRB3k8-Ec8Q73Di5p2UO0qlA,274
18
- bbstrader/models/risk.py,sha256=Pm_WoGI-vtPW75fwo_7ptF2Br-xQYBwrAAOIgqDQmy8,15120
19
- bbstrader/trading/__init__.py,sha256=3_PeIcuQ7znoCPdq8FqRmbCc0czNiernpIRkBXJZvg0,452
20
- bbstrader/trading/execution.py,sha256=Y3iKLbgCNVLw2XTDUM8Mv6hlcO8H83VyMKlZgjAKX2c,20744
21
- bbstrader/trading/strategies.py,sha256=uYVeFeqVHHYrkgk4Eiziivj0iIO0RQKXCAoOSQ4XVRE,36972
22
- bbstrader-0.1.9.dist-info/LICENSE,sha256=1EudjwwP2oTJy8Vh0e-Kzv8VZZU95y-t6c3DYhR51uc,1115
23
- bbstrader-0.1.9.dist-info/METADATA,sha256=aJILSlgr7yOdyozYyZPseOUUy7WkJcgHUv-TnudXSvY,9660
24
- bbstrader-0.1.9.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
25
- bbstrader-0.1.9.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
26
- bbstrader-0.1.9.dist-info/RECORD,,