| Overall Statistics |
|
Total Trades 9309 Average Win 0.46% Average Loss -0.11% Compounding Annual Return -4.806% Drawdown 25.400% Expectancy -0.015 Net Profit -10.119% Sharpe Ratio -0.397 Sortino Ratio -0.567 Probabilistic Sharpe Ratio 2.107% Loss Rate 82% Win Rate 18% Profit-Loss Ratio 4.34 Alpha -0.067 Beta 0.665 Annual Standard Deviation 0.15 Annual Variance 0.022 Information Ratio -0.591 Tracking Error 0.119 Treynor Ratio -0.089 Total Fees $12027.11 Estimated Strategy Capacity $8400000.00 Lowest Capacity Asset QQQ RIWIV7K5Z9LX Portfolio Turnover 1171.47% |
#region imports
from AlgorithmImports import *
#endregion
class Strategy:
def __init__(self, algo, symbol):
self.algo = algo
self.symbol = symbol
self.day_close = None
self.day_open = None
self.entries_by_date = {}
self.high_of_day = None
self.low_of_day = None
# Just to see how it's done -- not too bad.
self.bb = self.algo.BB(self.symbol, self.algo.length, self.algo.nstd, Resolution.Minute)
self.vwap = self.algo.VWAP(self.symbol)
self.sma = self.algo.SMA(self.symbol, self.algo.length)
self.sigma = self.algo.STD(self.symbol, self.algo.length)
self.smas = RollingWindow[float](self.algo.n + 1)
self.vwaps = RollingWindow[float](self.algo.n + 1)
# Decided on a delegate method, rather than a consolidator
# just simpler, less boilerplate.
def OnData(self, data: Slice):
data = data.Bars
if not data.ContainsKey(self.symbol): return
bar = data[self.symbol]
tm = bar.EndTime
self.UpdateWindows(bar)
if self.IsReady:
if self.EntriesToday(tm) < self.algo.entries_per_day:
entered = self.EntryLogic(bar)
if entered:
self.IncrementEntriesToday(tm)
if self.Invested:
self.ExitPackage()
if self.algo.signal_exit:
if self.IsLong and bar.Close < self.vwaps[0]:
self.algo.Liquidate(self.symbol, "VWAP LX")
if self.IsShort and bar.Close > self.vwaps[0]:
self.algo.Liquidate(self.symbol, "VWAP SX")
def UpdateWindows(self, bar):
if self.sma.IsReady:
self.smas.Add(self.sma.Current.Value)
if self.vwap.IsReady:
self.vwaps.Add(self.vwap.Current.Value)
def EntryLogic(self, bar):
alloc_pct = 1.0 / len(self.algo.strats)
long_ok = self.algo.direction >= 0 and not self.IsLong
if long_ok:
sma_ok = bar.Close > self.sma.Current.Value
sma_trend = self.smas[0] > self.smas[self.algo.n]
above_vwap = bar.Close > self.vwaps[0]
# A bit hacked -- just using what we have available. (No vwap bands)
# below_lower = bar.Close < self.vwaps[0] - self.sigma.Current.Value * self.algo.nstd
# Removed sma_ok and sma_trend
if above_vwap:
self.algo.SetHoldings(self.symbol, alloc_pct)
return True
short_ok = self.algo.direction <= 0 and not self.IsShort
if short_ok:
below_vwap = bar.Close < self.vwaps[0]
if below_vwap:
self.algo.SetHoldings(self.symbol, -alloc_pct)
return True
def ExitPackage(self):
self.StopPct(self.algo.stop_pct)
self.TgtPct(self.algo.tgt_pct)
def StopPct(self, pct):
if pct == 0.0: return
urpct = self.algo.Portfolio[self.symbol].UnrealizedProfitPercent
ur = self.algo.Portfolio[self.symbol].UnrealizedProfit
if self.algo.Portfolio[self.symbol].UnrealizedProfitPercent < -1 * pct:
self.algo.Liquidate(self.symbol, tag=f"Stop -- {ur}")
def TgtPct(self, pct):
if pct == 0.0: return
ur = self.algo.Portfolio[self.symbol].UnrealizedProfit
if self.algo.Portfolio[self.symbol].UnrealizedProfitPercent > pct:
self.algo.Liquidate(self.symbol, tag=f"Tgt -- {ur}")
@property
def Sigma(self):
if self.IsReady:
return self.bb.Upper.Current.Value - self.bb.Middle.Current.Value
@property
def IsReady(self):
return self.bb.IsReady and self.vwap.IsReady and self.sma.IsReady and self.vwaps.IsReady
@property
def Invested(self):
return self.algo.Portfolio[self.symbol].Invested
@property
def IsLong(self):
return self.algo.Portfolio[self.symbol].IsLong
@property
def IsShort(self):
return self.algo.Portfolio[self.symbol].IsShort
def EntriesToday(self, bar_end_time):
date = bar_end_time.date()
if date not in self.entries_by_date:
self.entries_by_date[date] = 0
return self.entries_by_date[date]
def IncrementEntriesToday(self, bar_end_time):
date = bar_end_time.date()
if date not in self.entries_by_date:
self.entries_by_date[date] = 1
else:
self.entries_by_date[date] += 1# region imports
from AlgorithmImports import *
# endregion
from Strategy import Strategy
class SwimmingRedTermite(QCAlgorithm):
tickers = ['QQQ']
length = 50
nstd = 2
# Direction -- 1 is long only, -1 is short only, 0 is both.
direction = 1
entries_per_day = 100
# Bars back, used for slope measure.
n = 7
# If 0, will not set.
# .01 == 1%
tgt_pct = 0.0
stop_pct = 0.0
signal_exit = True
eodx = True
symbols = []
strats = {}
def Initialize(self):
self.SetStartDate(2022, 1, 1)
self.SetCash(100000)
# Set benchmark -- SPY (most common, need it unrelated to tickers)
self.AddEquity('SPY', Resolution.Minute)
self.SetBenchmark("SPY")
# symbol = self.AddEquity("QQQ", Resolution.Minute).Symbol
for t in self.tickers:
s = self.AddEquity(t, Resolution.Minute).Symbol
self.symbols.append(s)
self.strats[s] = Strategy(self, s)
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.BeforeMarketClose("SPY", 1),
self.EODx)
def EODx(self):
if self.eodx:
self.Liquidate(tag="EOD")
def OnData(self, data: Slice):
gap_pcts = []
for symbol, strat in self.strats.items():
strat.OnData(data)