| Overall Statistics |
|
Total Orders 1064 Average Win 0.51% Average Loss -0.25% Compounding Annual Return 8.354% Drawdown 10.600% Expectancy 1.088 Start Equity 1000000 End Equity 4530587.97 Net Profit 353.059% Sharpe Ratio 0.722 Sortino Ratio 0.87 Probabilistic Sharpe Ratio 67.124% Loss Rate 31% Win Rate 69% Profit-Loss Ratio 2.01 Alpha 0.034 Beta 0.063 Annual Standard Deviation 0.052 Annual Variance 0.003 Information Ratio -0.185 Tracking Error 0.161 Treynor Ratio 0.603 Total Fees $15529.45 Estimated Strategy Capacity $0 Lowest Capacity Asset IEF SGNKIKYGE9NP Portfolio Turnover 0.84% Drawdown Recovery 494 |
from AlgorithmImports import *
class MinVar(QCAlgorithm):
def Initialize(self):
# Backtest period
self.set_start_date(2007, 3, 1)
self.set_cash(1_000_000)
# Set the brokerage model and account type
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)
# Equities
self.spy = self.add_equity("SPY", Resolution.DAILY).symbol
self.ief = self.add_equity("IEF", Resolution.DAILY).symbol
self.gld = self.add_equity("GLD", Resolution.DAILY).symbol
self.uup = self.add_equity("UUP", Resolution.DAILY).symbol
self.dbc = self.add_equity("DBC", Resolution.DAILY).symbol
self.set_benchmark(self.spy)
# Volatility target
self.target_vol = 0.06 # 6% annualized
# Maximum allowed leerage
self.max_leverage = 2.0
# Lookback window (days)
self.lookback = 90
# Warm-up period so that prices are available
self.set_warm_up(self.lookback)
# Monthly rebalance
self.schedule.on(
self.date_rules.month_start(self.spy),
self.time_rules.after_market_open(self.spy, 30),
self.Rebalance)
def Rebalance(self):
if self.is_warming_up: return
# Get historical prices
prices = self.history([self.spy, self.ief, self.gld, self.uup, self.dbc],
self.lookback, Resolution.DAILY)
if prices.empty: return
# Pivot to price dataframe
prices = prices.close.unstack(level=0)
# Compute daily returns
returns = prices.pct_change().dropna()
# Volatility and the covariance matrix
mu = np.ones(len(returns.columns))
vol = returns.std() * np.sqrt(252.)
cov = returns.cov() * 252.
# Min-Var Weights
try:
weights = np.linalg.solve(cov, mu)
except:
return
weights = np.nan_to_num(weights)
weights = pd.Series(weights, index = returns.columns)
weights[weights < 0] = 0.
s = weights.abs().sum()
if s > 0:
weights /= s
# Portfolio annualized volatility
port_vol = np.sqrt(weights.T @ cov @ weights)
# Apply vol and leverage control
leverage = 1.
if port_vol > 0:
leverage = min(self.target_vol / port_vol, self.max_leverage)
weights *= leverage
# Set holdings
targets = [
PortfolioTarget(symbol, float(weight))
for symbol, weight in weights.items()
if self.securities[symbol].is_tradable
and self.securities[symbol].has_data
and self.securities[symbol].price > 0
]
self.set_holdings(targets)