137 lines
4.7 KiB
Python
137 lines
4.7 KiB
Python
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from scipy.stats import pearsonr
|
|
import warnings
|
|
warnings.filterwarnings('ignore')
|
|
|
|
# Import our utility functions
|
|
from .backshift import backshift
|
|
from .fwdshift import fwdshift
|
|
from .smartmean import smartmean
|
|
from .smartstd import smartstd
|
|
from .calculateMaxDD import calculateMaxDD
|
|
from .data_loader import load_futures_data
|
|
|
|
def main():
|
|
"""
|
|
Python implementation of the TU_mom.m momentum trading strategy.
|
|
"""
|
|
print("TU Momentum Trading Strategy")
|
|
print("=" * 40)
|
|
|
|
try:
|
|
# Try to load real Treasury futures data
|
|
print("Loading Treasury futures data...")
|
|
data = load_futures_data('TU', '20120813')
|
|
tday = data['tday']
|
|
cl = data['cl'][:, 0] # Use first contract for simplicity
|
|
|
|
print(f"Loaded {len(tday)} days of Treasury futures data")
|
|
|
|
except (FileNotFoundError, KeyError) as e:
|
|
print(f"Could not load real data ({e}), using synthetic data for demonstration...")
|
|
|
|
# Synthetic data for demonstration
|
|
np.random.seed(42)
|
|
n_days = 2000
|
|
tday = np.arange(20090102, 20090102 + n_days)
|
|
cl = 100 * np.cumprod(1 + np.random.normal(0, 0.01, n_days))
|
|
|
|
# Correlation tests
|
|
print("\nCorrelation Analysis:")
|
|
print("Lookback\tHolddays\tCorrelation\tp-value")
|
|
print("-" * 50)
|
|
|
|
for lookback in [1, 5, 10, 25, 60, 120, 250]:
|
|
for holddays in [1, 5, 10, 25, 60, 120, 250]:
|
|
# Calculate lagged returns
|
|
ret_lag = (cl - backshift(lookback, cl)) / backshift(lookback, cl)
|
|
ret_fut = (fwdshift(holddays, cl) - cl) / cl
|
|
|
|
# Remove bad dates (NaN values)
|
|
bad_dates = np.isnan(ret_lag) | np.isnan(ret_fut)
|
|
ret_lag_clean = ret_lag[~bad_dates]
|
|
ret_fut_clean = ret_fut[~bad_dates]
|
|
|
|
if len(ret_lag_clean) == 0:
|
|
continue
|
|
|
|
# Create independent set
|
|
if lookback >= holddays:
|
|
indep_set = np.arange(0, len(ret_lag_clean), holddays)
|
|
else:
|
|
indep_set = np.arange(0, len(ret_lag_clean), lookback)
|
|
|
|
ret_lag_indep = ret_lag_clean[indep_set]
|
|
ret_fut_indep = ret_fut_clean[indep_set]
|
|
|
|
# Calculate correlation
|
|
if len(ret_lag_indep) > 1:
|
|
cc, pval = pearsonr(ret_lag_indep, ret_fut_indep)
|
|
print(f"{lookback:3d}\t\t{holddays:3d}\t\t{cc:7.4f}\t\t{pval:6.4f}")
|
|
|
|
# Trading strategy implementation
|
|
lookback = 250
|
|
holddays = 25
|
|
|
|
print(f"\nImplementing Trading Strategy:")
|
|
print(f"Lookback: {lookback} days, Hold: {holddays} days")
|
|
|
|
# Generate trading signals
|
|
longs = cl > backshift(lookback, cl)
|
|
shorts = cl < backshift(lookback, cl)
|
|
|
|
# Initialize positions
|
|
pos = np.zeros(len(cl))
|
|
|
|
# Build position over holding period
|
|
for h in range(holddays):
|
|
long_lag = backshift(h, longs.astype(float))
|
|
long_lag = np.nan_to_num(long_lag, nan=0).astype(bool)
|
|
|
|
short_lag = backshift(h, shorts.astype(float))
|
|
short_lag = np.nan_to_num(short_lag, nan=0).astype(bool)
|
|
|
|
pos[long_lag] += 1
|
|
pos[short_lag] -= 1
|
|
|
|
# Calculate returns
|
|
ret = (backshift(1, pos) * (cl - backshift(1, cl)) / backshift(1, cl)) / holddays
|
|
ret = np.nan_to_num(ret, nan=0)
|
|
|
|
# Find start index (equivalent to finding date 20090102)
|
|
idx = 250 # Start after sufficient data for lookback
|
|
|
|
# Calculate cumulative returns
|
|
cumret = np.cumprod(1 + ret[idx:]) - 1
|
|
|
|
# Plot results
|
|
plt.figure(figsize=(12, 6))
|
|
plt.plot(cumret)
|
|
plt.title('TU Momentum Strategy - Cumulative Returns')
|
|
plt.xlabel('Days')
|
|
plt.ylabel('Cumulative Return')
|
|
plt.grid(True)
|
|
plt.show()
|
|
|
|
# Performance metrics
|
|
strategy_returns = ret[idx:]
|
|
avg_ann_ret = 252 * smartmean(strategy_returns)
|
|
ann_volatility = np.sqrt(252) * smartstd(strategy_returns)
|
|
sharpe_ratio = avg_ann_ret / ann_volatility if ann_volatility != 0 else 0
|
|
apr = np.prod(1 + strategy_returns) ** (252 / len(strategy_returns)) - 1
|
|
|
|
maxDD, maxDDD = calculateMaxDD(cumret)
|
|
kelly_f = np.mean(strategy_returns) / np.var(strategy_returns) if np.var(strategy_returns) != 0 else 0
|
|
|
|
print(f"\nPerformance Metrics:")
|
|
print(f"Average Annual Return: {avg_ann_ret:7.4f}")
|
|
print(f"Annual Volatility: {ann_volatility:7.4f}")
|
|
print(f"Sharpe Ratio: {sharpe_ratio:4.2f}")
|
|
print(f"APR: {apr:10.4f}")
|
|
print(f"Max Drawdown: {maxDD:.6f}")
|
|
print(f"Max Drawdown Duration: {int(maxDDD)} days")
|
|
print(f"Kelly f: {kelly_f:.6f}")
|
|
|
|
if __name__ == "__main__":
|
|
main() |