| Overall Statistics |
|
Total Orders 296 Average Win 0.24% Average Loss -0.22% Compounding Annual Return 18.923% Drawdown 6.000% Expectancy 0.040 Start Equity 100000 End Equity 102645.82 Net Profit 2.646% Sharpe Ratio 0.512 Sortino Ratio 0.718 Probabilistic Sharpe Ratio 45.554% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.11 Alpha 0.108 Beta 1.244 Annual Standard Deviation 0.181 Annual Variance 0.033 Information Ratio 0.764 Tracking Error 0.138 Treynor Ratio 0.075 Total Fees $373.69 Estimated Strategy Capacity $900000.00 Lowest Capacity Asset APEI TXHW77OK7VQD Portfolio Turnover 26.67% Drawdown Recovery 19 |
# region imports
from AlgorithmImports import *
import numpy as np
import pandas as pd
# endregion
class CreativeYellowGreenArmadillo(QCAlgorithm):
def initialize(self):
self.set_start_date(2026, 1, 1)
self.set_cash(100000)
self.settings.seed_initial_prices = True
# Universe settings
self._universe_size = 20
self._lookback_days = 15 # Need 10 days for correlation
self._rebalance_days = 5
# Add universe
self._universe = self.add_universe(self._select_coarse)
# Schedule rebalancing
self.schedule.on(self.date_rules.every_day(),
self.time_rules.after_market_open("SPY", 30),
self._rebalance)
# Track last rebalance time
self._last_rebalance = self.time
def _select_coarse(self, coarse):
"""Universe selection using WorldQuant Brain alpha formula"""
# Filter for liquid stocks
filtered = [x for x in coarse if x.has_fundamental_data
and x.price > 5
and x.dollar_volume > 10000000]
if len(filtered) < self._universe_size:
return [x.symbol for x in filtered]
# Get historical data for all symbols
symbols = [x.symbol for x in filtered]
history = self.history(symbols, self._lookback_days, Resolution.DAILY)
if history.empty:
return symbols[:self._universe_size]
# Calculate alpha scores
scores = {}
for symbol in symbols:
try:
if symbol not in history.index.get_level_values(0):
continue
df = history.loc[symbol]
if len(df) < 10: # Need at least 10 days
continue
close = df['close'].values
open_price = df['open'].values
volume = df['volume'].values
# Calculate alpha: (-rank(ts_covariance(rank(close),rank(volume),5)))+rank(-ts_corr(open,volume,10))
alpha = self._calculate_alpha(close, open_price, volume)
if not np.isnan(alpha):
scores[symbol] = alpha
except Exception as e:
continue
# Select top stocks by alpha score
sorted_symbols = sorted(scores.items(), key=lambda x: x[1], reverse=True)
selected = [symbol for symbol, score in sorted_symbols[:self._universe_size]]
return selected
def _calculate_alpha(self, close, open_price, volume):
"""Calculate WorldQuant Brain alpha formula"""
# Part 1: -rank(ts_covariance(rank(close), rank(volume), 5))
rank_close = self._rank(close)
rank_volume = self._rank(volume)
# Calculate 5-day rolling covariance
cov_values = []
for i in range(4, len(rank_close)):
window_close = rank_close[i-4:i+1]
window_volume = rank_volume[i-4:i+1]
cov = np.cov(window_close, window_volume)[0, 1]
cov_values.append(cov)
if len(cov_values) == 0:
return np.nan
# Rank the covariance values (cross-sectional would be across stocks, here we approximate with time series)
rank_cov = self._rank(np.array(cov_values))
part1 = -rank_cov[-1] # Take the most recent value and negate
# Part 2: rank(-ts_corr(open, volume, 10))
if len(open_price) < 10:
return np.nan
# Calculate 10-day rolling correlation
corr_values = []
for i in range(9, len(open_price)):
window_open = open_price[i-9:i+1]
window_volume = volume[i-9:i+1]
corr = np.corrcoef(window_open, window_volume)[0, 1]
corr_values.append(corr)
if len(corr_values) == 0:
return np.nan
# Rank the negative correlation values
rank_neg_corr = self._rank(-np.array(corr_values))
part2 = rank_neg_corr[-1] # Take the most recent value
# Combine
alpha = part1 + part2
return alpha
def _rank(self, values):
"""Rank values from 0 to 1"""
if len(values) <= 1:
return np.array([0.5] * len(values))
# Use pandas for ranking
series = pd.Series(values)
ranks = series.rank(method='average', pct=True)
return ranks.values
def _rebalance(self):
"""Rebalance portfolio"""
# Only rebalance every N days
if (self.time - self._last_rebalance).days < self._rebalance_days:
return
self._last_rebalance = self.time
# Get active universe members
active_securities = [x for x in self._universe.selected]
if len(active_securities) == 0:
return
# Equal weight portfolio
weight = 1.0 / len(active_securities)
targets = [PortfolioTarget(symbol, weight) for symbol in active_securities]
self.set_holdings(targets, liquidate_existing_holdings=True)