| Overall Statistics |
|
Total Trades 35 Average Win 36.03% Average Loss -2.31% Compounding Annual Return 13.605% Drawdown 27.600% Expectancy 0.956 Net Profit 46.796% Sharpe Ratio 0.8 Probabilistic Sharpe Ratio 32.858% Loss Rate 88% Win Rate 12% Profit-Loss Ratio 15.62 Alpha 0.028 Beta 0.755 Annual Standard Deviation 0.2 Annual Variance 0.04 Information Ratio -0.133 Tracking Error 0.116 Treynor Ratio 0.212 Total Fees $35.00 Estimated Strategy Capacity $350000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
import numpy as np
class SimpleBreakoutExample(QCAlgorithm):
def Initialize(self):
# Set the cash for backtest
self.SetCash(10000)
# Start and end dates for backtest
self.SetStartDate(2009,1,1)
self.SetEndDate(2030,1,1)
# Add asset
self.symbol = self.AddEquity("SPY", Resolution.Second).Symbol
# Lookback length for b/o (in days)
self.lookback = 31
# Upper/lower limit for lookback length
self.ceiling, self.floor = 31, 1
# Price offset for stop order
self.initialStopRisk = 0.98
self.trailingStopRisk = 0.89
# Schedule function 10 minutes after every market open
self.Schedule.On(self.DateRules.EveryDay(self.symbol), \
self.TimeRules.AfterMarketOpen(self.symbol, 10), \
Action(self.EveryMarketOpen))
def OnData(self, data):
# Plot security's price
self.Plot("Data Chart", self.symbol, self.Securities[self.symbol].Close)
def EveryMarketOpen(self):
# Dynamically determine lookback length based on 30 day volatility change rate
close = self.History(self.symbol, 31, Resolution.Second)["close"]
todayvol = np.std(close[1:31])
yesterdayvol = np.std(close[0:30])
deltavol = (todayvol - yesterdayvol) / todayvol
self.lookback = round(self.lookback * (1 + deltavol))
# Account for upper/lower limit of lockback length
if self.lookback > self.ceiling:
self.lookback = self.ceiling
elif self.lookback < self.floor:
self.lookback = self.floor
# List of daily highs
self.high = self.History(self.symbol, self.lookback, Resolution.Second)["high"]
# Buy in case of breakout
if not self.Securities[self.symbol].Invested and \
self.Securities[self.symbol].Close >= max(self.high[:-1]):
self.SetHoldings(self.symbol, 1)
self.breakoutlvl = max(self.high[:-1])
self.highestPrice = self.breakoutlvl
# Create trailing stop loss if invested
if self.Securities[self.symbol].Invested:
# If no order exists, send stop-loss
if not self.Transactions.GetOpenOrders(self.symbol):
self.stopMarketTicket = self.StopMarketOrder(self.symbol, \
-self.Portfolio[self.symbol].Quantity, \
self.initialStopRisk * self.breakoutlvl)
# Check if the asset's price is higher than highestPrice & trailing stop price not below initial stop price
if self.Securities[self.symbol].Close > self.highestPrice and \
self.initialStopRisk * self.breakoutlvl < self.Securities[self.symbol].Close * self.trailingStopRisk:
# Save the new high to highestPrice
self.highestPrice = self.Securities[self.symbol].Close
# Update the stop price
updateFields = UpdateOrderFields()
updateFields.StopPrice = self.Securities[self.symbol].Close * self.trailingStopRisk
self.stopMarketTicket.Update(updateFields)
# Print the new stop price with Debug()
self.Debug(updateFields.StopPrice)
# Plot trailing stop's price
self.Plot("Data Chart", "Stop Price", self.stopMarketTicket.Get(OrderField.StopPrice))