| Overall Statistics |
|
Total Orders 1698 Average Win 0.91% Average Loss -0.61% Compounding Annual Return 88.037% Drawdown 16.900% Expectancy 0.841 Start Equity 100000 End Equity 353987.64 Net Profit 253.988% Sharpe Ratio 2.384 Sortino Ratio 3.015 Probabilistic Sharpe Ratio 96.563% Loss Rate 26% Win Rate 74% Profit-Loss Ratio 1.50 Alpha 0.361 Beta 1.394 Annual Standard Deviation 0.223 Annual Variance 0.05 Information Ratio 2.378 Tracking Error 0.172 Treynor Ratio 0.382 Total Fees $1306.61 Estimated Strategy Capacity $0 Lowest Capacity Asset GOOCV VP83T1ZUHROL Portfolio Turnover 15.22% Drawdown Recovery 73 |
from AlgorithmImports import *
import numpy as np
import pandas as pd
from datetime import timedelta
class TBMR_PA_Adaptive(QCAlgorithm):
def PredictPrice(self, df):
close = df['close']
# Trend indicators
recent_mean = close.rolling(10).mean().iloc[-1]
short_mean = close.rolling(5).mean().iloc[-1]
long_mean = close.rolling(20).mean().iloc[-1]
# Momentum / slope
momentum = (short_mean - long_mean) / long_mean
# Volatility
vol = close.pct_change().rolling(10).std().iloc[-1]
# Dynamic weights based on momentum and volatility
# Strong upward trend -> rely more on last price
# High volatility -> rely more on recent mean
w_last = min(max(0.3 + momentum*2 - vol*5, 0), 1)
w_mean = 1 - w_last
# Compute predicted price with small random noise
noise = np.random.normal(0, 0.002)
predicted = w_mean * recent_mean + w_last * close.iloc[-1]
return predicted * (1 + noise)
def Initialize(self):
self.SetStartDate(2023,1,1)
self.SetEndDate(2025,1,1)
self.SetCash(100000)
tickers = [
"AAPL","MSFT","AMZN","GOOG","META","TSLA","NVDA","JPM","V","UNH",
"HD","PG","MA","DIS","PYPL","ADBE","NFLX","INTC","CRM","KO",
"PFE","MRK","PEP","CSCO","ABBV","AVGO","T","XOM","CVX","NKE",
"ORCL","ACN","MCD","ABT","BMY","COST","WMT","QCOM","TXN","LIN",
"NEE","HON","LOW","UPS","PM","RTX","INTU","SBUX","GS","AMGN","MDT"
]
self.symbols = [self.AddEquity(t, Resolution.Daily).Symbol for t in tickers]
self.lookback = 50
self.k_multiplier = 1.4 # Dynamic entry/exit threshold multiplier
self.min_holding_days = 3
# Store last entry date for each symbol
self.last_entry = {sym: self.Time - timedelta(days=10) for sym in self.symbols}
# Schedule TBM-PA daily run
self.Schedule.On(self.DateRules.EveryDay(),
self.TimeRules.AfterMarketOpen("SPY", 5),
self.RunTBMR)
def RunTBMR(self):
active_trades = []
current_time = self.Time
for sym in self.symbols:
history = self.History(sym, self.lookback, Resolution.Daily)
if history.empty or len(history) < self.lookback:
continue
df = pd.DataFrame(history['close'])
df.columns = ['close']
predicted = self.PredictPrice(df)
current_price = df['close'].iloc[-1]
deviation = (current_price - predicted)/predicted
# Calculate volatility
sigma = df['close'].pct_change().rolling(10).std().iloc[-1]
sigma = sigma if not np.isnan(sigma) else 0.01
# Dynamic thresholds
entry_thresh = -self.k_multiplier * sigma
exit_thresh = self.k_multiplier * sigma
# Minimum holding period check
days_since_entry = (current_time - self.last_entry[sym]).days
# Entry
if deviation < entry_thresh and not self.Portfolio[sym].Invested and days_since_entry >= self.min_holding_days:
active_trades.append((sym, sigma))
self.last_entry[sym] = current_time
# Exit
elif deviation > exit_thresh and self.Portfolio[sym].Invested:
self.Liquidate(sym)
# Plot tracking
self.Plot(f"TBMR-{sym.Value}", "Predicted", predicted)
self.Plot(f"TBMR-{sym.Value}", "Price", current_price)
# ===== Portfolio Allocation =====
if active_trades:
# Allocate inversely to volatility
total_inverse_vol = sum(1/s for _, s in active_trades)
for sym, sigma in active_trades:
weight = (1/sigma)/total_inverse_vol * 0.95 # max 95% capital
self.SetHoldings(sym, weight)
self.Plot("Portfolio","Equity",self.Portfolio.TotalPortfolioValue)