| Overall Statistics |
|
Total Orders 154 Average Win 0.28% Average Loss -0.20% Compounding Annual Return 1.728% Drawdown 2.900% Expectancy 0.083 Start Equity 100000 End Equity 101183.04 Net Profit 1.183% Sharpe Ratio 0.115 Sortino Ratio 0.111 Probabilistic Sharpe Ratio 22.846% Loss Rate 55% Win Rate 45% Profit-Loss Ratio 1.38 Alpha 0.007 Beta -0.017 Annual Standard Deviation 0.044 Annual Variance 0.002 Information Ratio -0.272 Tracking Error 0.331 Treynor Ratio -0.301 Total Fees $254.14 Estimated Strategy Capacity $15000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 58.04% |
from AlgorithmImports import *
class OrderBookImbalanceStrategy(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 4)
self.SetEndDate(2020, 9, 10)
self.SetCash(100000)
# Add SPY with quote data explicitly requested
equity = self.AddEquity("SPY", Resolution.Minute)
equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.symbol = equity.Symbol
# Initialize SMA indicators for bid and ask volumes
self.bid_volume_sma = SimpleMovingAverage(5)
self.ask_volume_sma = SimpleMovingAverage(5)
# Position tracking flags
self.is_long = False
self.is_short = False
# Schedule trading decisions every 5 minutes
self.Schedule.On(self.DateRules.EveryDay(self.symbol),
self.TimeRules.Every(timedelta(minutes=5)),
self.ExecuteTrades)
# Create custom chart to track OBI values
self.Plot("Strategy", "OBI", 0)
def OnData(self, data):
# Specifically check for QuoteBars data
if not data.QuoteBars.ContainsKey(self.symbol):
return
# Get the quote bar
quote_bar = data.QuoteBars[self.symbol]
# Update SMAs with bid and ask volumes
self.bid_volume_sma.Update(self.Time, quote_bar.LastBidSize)
self.ask_volume_sma.Update(self.Time, quote_bar.LastAskSize)
def ExecuteTrades(self):
# Check if SMAs are ready
if not self.bid_volume_sma.IsReady or not self.ask_volume_sma.IsReady:
return
# Get current values from SMAs
bid_vol = self.bid_volume_sma.Current.Value
ask_vol = self.ask_volume_sma.Current.Value
# Avoid division by zero
if bid_vol + ask_vol == 0:
self.Debug("Skipping trade evaluation: Bid + Ask volume is zero")
return
# Calculate Order Book Imbalance using the formula: OBI(t) = (B(t) - A(t)) / (B(t) + A(t))
obi = (bid_vol - ask_vol) / (bid_vol + ask_vol)
# Plot the current OBI value
self.Plot("Strategy", "OBI", obi)
# Log OBI value
self.Debug(f"Time: {self.Time}, OBI: {obi}")
# Trading logic based on OBI thresholds
# Long signal - OBI > 0.7
if obi > 0.9 and not self.is_long:
if self.is_short:
# Close short position first
self.Liquidate()
self.is_short = False
# Enter long position
self.SetHoldings(self.symbol, 0.95) # Using 95% of portfolio
self.is_long = True
self.Debug(f"LONG signal at OBI: {obi}")
# Short signal - OBI < -0.7
elif obi < -0.9 and not self.is_short:
if self.is_long:
# Close long position first
self.Liquidate()
self.is_long = False
# Enter short position
self.SetHoldings(self.symbol, -0.95) # Using 95% of portfolio for short
self.is_short = True
self.Debug(f"SHORT signal at OBI: {obi}")
# Exit long position when OBI drops below 0.7
elif self.is_long and obi < 0.7:
self.Liquidate()
self.is_long = False
self.Debug(f"EXIT LONG at OBI: {obi}")
# Exit short position when OBI rises above -0.7
elif self.is_short and obi > -0.7:
self.Liquidate()
self.is_short = False
self.Debug(f"EXIT SHORT at OBI: {obi}")