progress: added zscore fit
This commit is contained in:
parent
b87b40a6ed
commit
0e83142d0a
33
configuration/crypto_zscore.cfg
Normal file
33
configuration/crypto_zscore.cfg
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"security_type": "CRYPTO",
|
||||
"data_directory": "./data/crypto",
|
||||
"datafiles": [
|
||||
"2025*.mktdata.ohlcv.db"
|
||||
],
|
||||
"db_table_name": "md_1min_bars",
|
||||
"exchange_id": "BNBSPOT",
|
||||
"instrument_id_pfx": "PAIR-",
|
||||
"funding_per_pair": 2000.0,
|
||||
|
||||
# ====== Trading Parameters ======
|
||||
"price_column": "close",
|
||||
"dis-equilibrium_open_trshld": 2.0,
|
||||
"dis-equilibrium_close_trshld": 0.5,
|
||||
"training_minutes": 120,
|
||||
"fit_method_class": "pt_trading.z-score_rolling_fit.ZScoreRollingFit",
|
||||
|
||||
# ====== Stop Conditions ======
|
||||
"stop_close_conditions": {
|
||||
"profit": 2.0,
|
||||
"loss": -0.5
|
||||
}
|
||||
|
||||
# ====== End of Session Closeout ======
|
||||
"close_outstanding_positions": true,
|
||||
# "close_outstanding_positions": false,
|
||||
"trading_hours": {
|
||||
"begin_session": "9:30:00",
|
||||
"end_session": "21:30:00",
|
||||
"timezone": "America/New_York"
|
||||
}
|
||||
}
|
||||
35
configuration/equity_zscore.cfg
Normal file
35
configuration/equity_zscore.cfg
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"security_type": "EQUITY",
|
||||
"data_directory": "./data/equity",
|
||||
"datafiles": [
|
||||
"202506*.mktdata.ohlcv.db",
|
||||
],
|
||||
"db_table_name": "md_1min_bars",
|
||||
"exchange_id": "ALPACA",
|
||||
"instrument_id_pfx": "STOCK-",
|
||||
"exclude_instruments": ["CAN"],
|
||||
|
||||
"funding_per_pair": 2000.0,
|
||||
|
||||
# ====== Trading Parameters ======
|
||||
"price_column": "close",
|
||||
"dis-equilibrium_open_trshld": 2.0,
|
||||
"dis-equilibrium_close_trshld": 1.0,
|
||||
"training_minutes": 120,
|
||||
"fit_method_class": "pt_trading.z-score_rolling_fit.ZScoreRollingFit",
|
||||
|
||||
# ====== Stop Conditions ======
|
||||
"stop_close_conditions": {
|
||||
"profit": 2.0,
|
||||
"loss": -0.5
|
||||
}
|
||||
|
||||
# ====== End of Session Closeout ======
|
||||
"close_outstanding_positions": true,
|
||||
# "close_outstanding_positions": false,
|
||||
"trading_hours": {
|
||||
"begin_session": "9:30:00",
|
||||
"end_session": "15:30:00",
|
||||
"timezone": "America/New_York"
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,14 @@ from statsmodels.tsa.vector_ar.vecm import VECM, VECMResults
|
||||
NanoPerMin = 1e9
|
||||
|
||||
class RollingFit(PairsTradingFitMethod):
|
||||
'''
|
||||
N O T E:
|
||||
=========
|
||||
- This class remains to be abstract
|
||||
- The following methods are to be implemented in the subclass:
|
||||
- create_trading_pair()
|
||||
=========
|
||||
'''
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
|
||||
@ -17,9 +17,8 @@ class VECMTradingPair(TradingPair):
|
||||
self.pair_predict_result_ = None
|
||||
|
||||
def _train_pair(self) -> None:
|
||||
# print('*' * 80 + '\n' + f"**************** {self} IS COINTEGRATED ****************\n" + '*' * 80)
|
||||
self._fit_VECM()
|
||||
assert self.training_df_ is not None and self.vecm_fit_ is not None
|
||||
assert self.vecm_fit_ is not None
|
||||
diseq_series = self.training_df_[self.colnames()] @ self.vecm_fit_.beta
|
||||
# print(diseq_series.shape)
|
||||
self.training_mu_ = float(diseq_series[0].mean())
|
||||
|
||||
78
lib/pt_trading/z-score_rolling_fit.py
Normal file
78
lib/pt_trading/z-score_rolling_fit.py
Normal file
@ -0,0 +1,78 @@
|
||||
from typing import Any, Dict, Optional, cast
|
||||
|
||||
import pandas as pd
|
||||
from pt_trading.results import BacktestResult
|
||||
from pt_trading.rolling_window_fit import RollingFit
|
||||
from pt_trading.trading_pair import TradingPair
|
||||
import statsmodels.api as sm
|
||||
|
||||
NanoPerMin = 1e9
|
||||
class ZScoreTradingPair(TradingPair):
|
||||
zscore_model_: Optional[sm.regression.linear_model.RegressionResultsWrapper]
|
||||
pair_predict_result_: Optional[pd.DataFrame]
|
||||
zscore_df_: Optional[pd.DataFrame]
|
||||
|
||||
def __init__(self, config: Dict[str, Any], market_data: pd.DataFrame, symbol_a: str, symbol_b: str, price_column: str):
|
||||
super().__init__(config, market_data, symbol_a, symbol_b, price_column)
|
||||
self.zscore_model_ = None
|
||||
self.pair_predict_result_ = None
|
||||
self.zscore_df_ = None
|
||||
|
||||
def _fit_zscore(self) -> None:
|
||||
assert self.training_df_ is not None
|
||||
a = self.training_df_[self.colnames()].iloc[:, 0]
|
||||
b = self.training_df_[self.colnames()].iloc[:, 1]
|
||||
|
||||
a,b = a.align(b, axis=0)
|
||||
|
||||
|
||||
X = sm.add_constant(b)
|
||||
self.zscore_model_ = sm.OLS(a, X).fit()
|
||||
assert self.zscore_model_ is not None
|
||||
hedge_ratio = self.zscore_model_.params.iloc[1]
|
||||
|
||||
# Calculate spread and Z-score
|
||||
spread = a - hedge_ratio * b
|
||||
self.zscore_df_ = (spread - spread.mean()) / spread.std()
|
||||
|
||||
def predict(self) -> pd.DataFrame:
|
||||
self._fit_zscore()
|
||||
assert self.zscore_df_ is not None
|
||||
self.training_df_["dis-equilibrium"] = self.zscore_df_
|
||||
self.training_df_["scaled_dis-equilibrium"] = abs(self.zscore_df_)
|
||||
|
||||
assert self.testing_df_ is not None
|
||||
assert self.zscore_df_ is not None
|
||||
predicted_df = self.testing_df_
|
||||
|
||||
predicted_df["disequilibrium"] = self.zscore_df_
|
||||
predicted_df["scaled_disequilibrium"] = abs(self.zscore_df_)
|
||||
|
||||
predicted_df = predicted_df.reset_index(drop=True)
|
||||
if self.pair_predict_result_ is None:
|
||||
self.pair_predict_result_ = predicted_df
|
||||
else:
|
||||
self.pair_predict_result_ = pd.concat([self.pair_predict_result_, predicted_df], ignore_index=True)
|
||||
# Reset index to ensure proper indexing
|
||||
self.pair_predict_result_ = self.pair_predict_result_.reset_index(drop=True)
|
||||
return self.pair_predict_result_
|
||||
|
||||
|
||||
class ZScoreRollingFit(RollingFit):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def run_pair(
|
||||
self, pair: TradingPair, bt_result: BacktestResult
|
||||
) -> Optional[pd.DataFrame]:
|
||||
return super().run_pair(pair, bt_result)
|
||||
def create_trading_pair(
|
||||
self, config: Dict, market_data: pd.DataFrame, symbol_a: str, symbol_b: str, price_column: str
|
||||
) -> TradingPair:
|
||||
return ZScoreTradingPair(
|
||||
config=config,
|
||||
market_data=market_data,
|
||||
symbol_a=symbol_a,
|
||||
symbol_b=symbol_b,
|
||||
price_column=price_column
|
||||
)
|
||||
@ -74,6 +74,7 @@ PyYAML>=6.0
|
||||
reportlab>=3.6.8
|
||||
requests>=2.25.1
|
||||
requests-file>=1.5.1
|
||||
scipy<1.13.0
|
||||
seaborn>=0.13.2
|
||||
SecretStorage>=3.3.1
|
||||
setproctitle>=1.2.2
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user