| Overall Statistics |
|
Total Trades 62 Average Win 0.55% Average Loss -0.26% Compounding Annual Return 0.266% Drawdown 3.700% Expectancy 0.013 Net Profit 0.084% Sharpe Ratio 0.083 Probabilistic Sharpe Ratio 30.575% Loss Rate 68% Win Rate 32% Profit-Loss Ratio 2.14 Alpha 0.009 Beta -0.019 Annual Standard Deviation 0.044 Annual Variance 0.002 Information Ratio -2.21 Tracking Error 0.135 Treynor Ratio -0.188 Total Fees $62.00 Estimated Strategy Capacity $690000.00 Lowest Capacity Asset HSGX VW390IT7P4YT |
from datetime import datetime, date, time
from datetime import timedelta
import pandas as pd
class TradeStrategyTest(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021,3, 1) #Set Start Date
self.SetEndDate(2021,6,21) #Set End Date
self.SetCash(30000) #Set Strategy Cash
self.SetWarmUp(210)
self.ticket = None # Flag for position status
self.expiry = self.Time
self.openPrice = -1 # Pointer to store opening price
self.lowestPrice = -1 # pointer to store latest daily high
# Set TimeZone
self.SetTimeZone("America/New_York")
# Add Equities
self.Equities = ['OCGN', 'CLOV']
# Add Equities from .csv file - UNDER TESTING NOT COMPLETE!!!!
# Create a dataframe from csv
#self.Equities = pd.read_csv('test.csv', delimiter=',')
self.Indicators = dict()
self.Charts = dict()
self.Consolidators = dict()
# Set resoltuion for Equity data
for Symbol in self.Equities:
self.Consolidators[Symbol] = dict()
self.AddEquity(Symbol, Resolution.Second)
# Create our consolidators
self.Consolidators[Symbol]['Con1'] = TradeBarConsolidator(timedelta(minutes=15))
self.Consolidators[Symbol]['Con2'] = TradeBarConsolidator(timedelta(minutes=2))
# Register our Handlers
self.Consolidators[Symbol]['Con1'].DataConsolidated += self.On_W1
self.Consolidators[Symbol]['Con2'].DataConsolidated += self.On_W2
self.Indicators[Symbol] = dict()
self.Indicators[Symbol]['RSI'] = dict()
self.Indicators[Symbol]['RSI']['15'] = RelativeStrengthIndex(Symbol, 14, MovingAverageType.Wilders)
self.Indicators[Symbol]['RSI']['2'] = RelativeStrengthIndex(Symbol, 14, MovingAverageType.Wilders)
# Register the indicaors with our stock and consolidator
self.RegisterIndicator(Symbol, self.Indicators[Symbol]['RSI']['15'], self.Consolidators[Symbol]['Con1'])
self.RegisterIndicator(Symbol, self.Indicators[Symbol]['RSI']['2'], self.Consolidators[Symbol]['Con2'])
# Finally add our consolidators to the subscription
# manager in order to receive updates from the engine
self.SubscriptionManager.AddConsolidator(Symbol, self.Consolidators[Symbol]['Con1'])
self.SubscriptionManager.AddConsolidator(Symbol, self.Consolidators[Symbol]['Con2'])
# Set Fee Model
for Symbol in self.Equities:
self.Securities[Symbol].FeeModel = ConstantFeeModel(1.00)
def OnData(self, data):
# Loop through our equities
for Symbol in self.Equities:
# Set local variables
close = self.Securities[Symbol].Close
quantity = self.CalculateOrderQuantity(Symbol,1*0.2)
AskPrice = self.Securities[Symbol].AskPrice
BidPrice = self.Securities[Symbol].BidPrice
Spread = (AskPrice - BidPrice)
self.RSI1 = self.Indicators[Symbol]['RSI']['15'].Current.Value
self.RSI2 = self.Indicators[Symbol]['RSI']['2'].Current.Value
# Warm up Condition
if self.IsWarmingUp or not data.Bars.ContainsKey(Symbol):# or not self.RSI1.IsReady or not self.RSI2.IsReady:
return
# Setup Open and Close Prices and Bars
if self.Time >= self.expiry:
self.previous_day_close = self.Securities[Symbol].Close
self.expiry = Expiry.EndOfDay(self.Time)
self.previous_bar_close = data[Symbol].Close
change_from_close = (((self.previous_bar_close - self.previous_day_close) / self.previous_bar_close)*100)
#Obtain Low of Day and Update
bar = data[Symbol]
if not bar.IsFillForward and self.lowestPrice < 0:
self.openPrice = bar.Open
self.lowestPrice = bar.Low
if self.lowestPrice < 0:
return
price = bar.Low
if price < self.lowestPrice: # If we observe a new low
self.lowestPrice = price
# IMPORTANT!!! Time variables to set open/close times and compare them to current time.
# Convert times to variables (necessary for time comparison)
currentTime = self.Time
openTime = time(9,30)
closeTime = time(12,0)
# Convert string to format that can be compared (comparison does not work if you do not do this)
# These comparisons are to test it works, before implementing them in the # Buy Conditions function below
# It is OK to comment them out here, as they are just for testing. Real function below.
#currentTime.strftime('%H%M') >= openTime.strftime('%H%M')
#currentTime.strftime('%H%M') <= closeTime.strftime('%H%M')
# Buy Conditions
if not self.Securities[Symbol].Invested and self.ticket is None and (currentTime.strftime('%H%M') >= openTime.strftime('%H%M') and currentTime.strftime('%H%M') <= closeTime.strftime('%H%M')):
#
# If buy conditions are satisfied then place MarketOrder
if ((self.RSI1 <=70 and self.RSI1 >=20) and (self.RSI2 >0 and self.RSI2 <=25) and (self.previous_bar_close <= self.lowestPrice)):
self.ticket = self.MarketOrder(Symbol, quantity, tag ="Market Buy")
# Place Profit take and Stop Loss orders then reset to None
self.LimitOrder(Symbol, -quantity, close * 1.03, tag = "Profit Take")
self.StopMarketOrder(Symbol, -quantity, close * 0.99, tag = "Stopped Out")
self.ticket = None
else:
# Close position if open for more than 15 minutes and set ticket to None
if self.ticket is not None and (self.Time > self.ticket.Time + timedelta(minutes = 15)):
self.Liquidate(Symbol)
self.ticket = None
# Cancel remaining order if limit order or stop loss order is executed
def OnOrderEvent(self, orderEvent):
order = self.Transactions.GetOrderById(orderEvent.OrderId)
if order.Status == OrderStatus.Filled:
if order.Type == OrderType.Limit or order.Type == OrderType.StopMarket:
self.Transactions.CancelOpenOrders(order.Symbol)
if order.Status == OrderStatus.Canceled:
self.Log(str(orderEvent))
#Reset Daily :Pointer
def OnEndOfDay(self, symbol):
self.lowestPrice = -1
# DEBUG FLAGS - IMPORTANT!!!
#self.Plot('RSI', 'W1', self.RSI1.Current.Value)
#self.Plot('RSI', 'W2', self.RSI2.Current.Value)
# Update Consolidator
def On_W1(self,sender,bar):
'''
This method will be called every time a new 15 minute bar is ready.
bar = The incoming Tradebar. This is different to the data object in OnData()
'''
#self.RSI1.Update(bar.Time,bar.Close)
Symbol = str(bar.get_Symbol())
self.Plot(Symbol+' RSI', 'W1', self.Indicators[Symbol]['RSI']['15'].Current.Value)
# Update Consolidator
def On_W2(self,sender,bar):
'''
This method will be called every time a new 2 minute bar is ready.
bar = The incoming Tradebar. This is different to the data object in OnData()
'''
#self.RSI2.Update(bar.Time,bar.Close)
Symbol = str(bar.get_Symbol())
self.Plot(Symbol+' RSI', 'W2', self.Indicators[Symbol]['RSI']['2'].Current.Value)