| Overall Statistics |
|
Total Orders 1 Average Win 0% Average Loss 0% Compounding Annual Return 11.991% Drawdown 30.200% Expectancy 0 Start Equity 100000 End Equity 504093.25 Net Profit 404.093% Sharpe Ratio 0.577 Sortino Ratio 0.589 Probabilistic Sharpe Ratio 6.614% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.004 Beta 0.911 Annual Standard Deviation 0.131 Annual Variance 0.017 Information Ratio -0.772 Tracking Error 0.015 Treynor Ratio 0.083 Total Fees $4.35 Estimated Strategy Capacity $84000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 0.02% |
from AlgorithmImports import *
class MultiTimeframeTrendStrategy(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
# self.SetEndDate(2015, 1, 1)
self.SetCash(100_000)
spy = self.AddEquity("SPY", Resolution.Hour)
spy.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.spySymbol = spy.Symbol
self.SetBenchmark("SPY")
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
# Initialize opening and previous bar prices for multiple time frames
self.weeklyOpenPrice = None
self.dailyOpenPrice = None
self.hourlyOpenPrice = None
self.lastDailyHigh = None
self.lastDailyLow = None
self.lastHourlyHigh = None
self.lastHourlyLow = None
self.lastHour = self.Time.hour
# Schedule functions to update weekly, daily, and hourly opening prices
self.lastTradeWeek = -1 # Initialize to an invalid week number
self.Schedule.On(self.DateRules.EveryDay(self.spySymbol), self.TimeRules.AfterMarketOpen(self.spySymbol, 1), self.CheckIfNewWeek)
self.Schedule.On(self.DateRules.EveryDay(self.spySymbol), self.TimeRules.At(9, 30), self.DailyOpen)
def OnData(self, data):
# Ensure the algorithm isn't warming up and that SPY data is available.
if self.IsWarmingUp or self.spySymbol not in data or data[self.spySymbol] is None:
return
# Update hourly price data and track high/low for Inside Bar detection.
if self.Time.hour != self.lastHour:
if data.Bars.ContainsKey(self.spySymbol):
self.lastHour = self.Time.hour
self.insideBarDetected = False # Reset inside bar detection for the new hour
bar = data.Bars[self.spySymbol]
self.hourlyOpenPrice = bar.Open
self.lastHourlyHigh = bar.High
self.lastHourlyLow = bar.Low
# Detect an Inside Bar based on the last hourly high and low.
if self.lastHourlyHigh is not None and self.lastHourlyLow is not None:
if self.hourlyOpenPrice < self.lastHourlyHigh and self.hourlyOpenPrice > self.lastHourlyLow:
self.Debug("Detected an Inside Bar at the hourly time frame")
self.insideBarDetected = True
# self.Log(f' IsWarmingUp {self.IsWarmingUp}, insideBar {self.insideBarDetected}, lastHour {self.lastHour}, hourly Price: {self.hourlyOpenPrice:,.2f}, HourlyHigh {self.lastHourlyHigh:,.2f}, HourlyLow {self.lastHourlyLow:,.2f}, weeklyPrice {self.weeklyOpenPrice:,.2f}, dailyPrice {self.dailyOpenPrice:,.2f}')
# Ensure all necessary prices are set before proceeding.
if None in [self.weeklyOpenPrice, self.dailyOpenPrice, self.hourlyOpenPrice]:
return
# if 0.00 in [self.weeklyOpenPrice, self.dailyOpenPrice, self.hourlyOpenPrice]:
# return
# Current price from the data.
price = data[self.spySymbol].Price
# Execute trades based on Inside Bar breakout and trend continuity.
if self.insideBarDetected:
spyInvested = self.Portfolio[self.spySymbol].Invested
# Entry condition for a long position when current price breaks above the Inside Bar's high
self.Log(f'price {price:,.2f}, HourlyHigh {self.lastHourlyHigh:,.2f}, HourlyLow {self.lastHourlyLow:,.2f}, weeklyPrice {self.weeklyOpenPrice:,.2f}, dailyPrice {self.dailyOpenPrice:,.2f}')
if price >= self.lastHourlyHigh and price > self.weeklyOpenPrice and price > self.dailyOpenPrice and price > self.hourlyOpenPrice:
# if price > self.lastHourlyLow and price > self.weeklyOpenPrice and price > self.dailyOpenPrice and price > self.hourlyOpenPrice:
if not spyInvested:
self.Log("Inside Bar broken upwards, aligning with TFC for a long position")
self.SetHoldings(self.spySymbol, 1)
# Exit condition if the price breaks below the Inside Bar's low
elif spyInvested and price < self.lastHourlyLow:
self.Log("Inside Bar broken downwards, exiting position.")
self.Liquidate(self.spySymbol)
def CheckIfNewWeek(self):
currentWeek = self.Time.isocalendar()[1]
if currentWeek != self.lastTradeWeek:
self.lastTradeWeek = currentWeek
self.WeeklyOpen()
def WeeklyOpen(self):
self.weeklyOpenPrice = self.Securities[self.spySymbol].Price
self.Log(f"Weekly open price updated on {self.Time.strftime('%Y-%m-%d')}: {self.weeklyOpenPrice}")
def DailyOpen(self):
bar = self.Securities[self.spySymbol]
self.dailyOpenPrice = bar.Price
self.lastDailyHigh = bar.High
self.lastDailyLow = bar.Low
self.Log(f"Daily open price updated on {self.Time.strftime('%Y-%m-%d')}: {self.dailyOpenPrice}")