| Overall Statistics |
|
Total Orders 5903 Average Win 0.20% Average Loss -0.12% Compounding Annual Return 14.262% Drawdown 25.400% Expectancy 0.263 Start Equity 1000000 End Equity 2128863.92 Net Profit 112.886% Sharpe Ratio 0.547 Sortino Ratio 0.559 Probabilistic Sharpe Ratio 17.577% Loss Rate 54% Win Rate 46% Profit-Loss Ratio 1.73 Alpha 0.034 Beta 0.473 Annual Standard Deviation 0.153 Annual Variance 0.024 Information Ratio -0.142 Tracking Error 0.158 Treynor Ratio 0.177 Total Fees $227067.44 Estimated Strategy Capacity $98000000.00 Lowest Capacity Asset MRK R735QTJ8XC9X Portfolio Turnover 22.44% |
#region imports
from AlgorithmImports import *
from scipy.stats import norm, zscore
#endregion
class MeanReversionDemo(QCAlgorithm):
def initialize(self):
self.set_start_date(2019, 1, 1)
self.set_end_date(2024, 9, 1)
self.set_brokerage_model(BrokerageName.ALPHA_STREAMS)
self.set_cash(1000000)
self.set_benchmark("SPY")
self.set_portfolio_construction(InsightWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
# Add "SHY" to the assets list
self.assets = ['PG', 'JNJ', 'MRK', 'GILD', 'SO', 'DUK', 'CL', 'BDX', 'BAESY', 'KMB',
'COR', 'GIS', 'HSY', 'ED', 'XEL', 'WEC', 'ORAN', 'CHT', 'AEE', 'CMS',
'CPB', 'CAG', 'SJM', 'TXNM', 'NEA', 'EQC','FLO', 'NAC', 'SAFT', 'EBF', 'SHY']
for asset in self.assets:
self.add_equity(asset, Resolution.MINUTE)
# Schedule the event using "SHY"
self.schedule.on(self.date_rules.every_day(), self.time_rules.before_market_close("SHY", 5), self.every_day_before_market_close)
def every_day_before_market_close(self):
qb = self
# Fetch history on our universe
df = qb.history(qb.securities.Keys, 120, Resolution.DAILY)
if df.empty: return
# Make all of them into a single time index.
df = df.close.unstack(level=0)
# Calculate the truth value of the most recent price being less than 1 std away from the mean
classifier = df.le(df.mean().subtract(df.std()*0.9)).iloc[-1]
if not classifier.any(): return
# Get the z-score for the True values, then compute the expected return and probability
z_score = df.apply(zscore)[[classifier.index[i] for i in range(classifier.size) if classifier.iloc[i]]]
magnitude = -z_score * df.std() / df
confidence = (-z_score).apply(norm.cdf)
# Get the latest values
magnitude = magnitude.iloc[-1].fillna(0)
confidence = confidence.iloc[-1].fillna(0)
# Get the weights, then zip together to iterate over later
weight = confidence - 1 / (magnitude + 1)
weight = weight[weight > 0].fillna(0)
sum_ = np.sum(weight)
if sum_ > 0:
weight = (weight) / sum_
selected = zip(weight.index, magnitude, confidence, weight)
else:
return
# ==============================
insights = []
for symbol, magnitude, confidence, weight in selected:
#avg = self.history(symbol, timedelta(days=126)).close.mean()
#if avg >= self.securities[symbol].price:
# continue
insights.append( Insight.price(symbol, timedelta(days=1), InsightDirection.UP, magnitude, confidence, None, weight) )
self.emit_insights(insights)