| Overall Statistics |
|
Total Trades 34 Average Win 0% Average Loss -1.58% Compounding Annual Return 69.580% Drawdown 10.400% Expectancy -1 Net Profit 22.448% Sharpe Ratio 2.243 Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0 Alpha 1.502 Beta -49.033 Annual Standard Deviation 0.243 Annual Variance 0.059 Information Ratio 2.162 Tracking Error 0.243 Treynor Ratio -0.011 Total Fees $185.90 |
import numpy as np
import pandas as pd
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from datetime import datetime, timedelta
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data.Market import TradeBar
import json
import math
from QuantConnect.Data import SubscriptionDataSource
from QuantConnect.Python import PythonData
class TemplateAlgorithmUniverseSelection(QCAlgorithm):
'''Advanced Template Architecture'''
def Initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialize.'''
# Chart - Master Containers for the Charts:
stockPlot_1 = Chart('RSI')
stockPlot_2 = Chart('Margin Remaining')
#Establish universe-wide settings
self.UniverseSettings.Resolution = Resolution.Daily
self.UniverseSettings.Leverage = 2
#Initial investment and backtest period
self.SetStartDate(2019,1,1) #Set Start Date
self.SetEndDate(datetime.now().date() - timedelta(1)) #Set End Date
self.SetCash(1000000) #Set Strategy Cash
#Initialize list of Open Orders
self.OpenOrders = self.Transactions.GetOpenOrders()
#Capture initial investment for risk off purposes
self.marginRemaining = self.Portfolio.MarginRemaining
self.OpenPortValue = self.Portfolio.TotalPortfolioValue
self.ClosingPortValue = self.Portfolio.TotalPortfolioValue
self.CurrentPortValue = self.Portfolio.TotalPortfolioValue
self.CurrentHoldValue = self.Portfolio.TotalHoldingsValue
self.OpenHoldValue = self.Portfolio.TotalHoldingsValue
self.ClosingHoldValue = self.Portfolio.TotalHoldingsValue
#Universe
self.AddEquity("SPY", Resolution.Daily)
self.AddUniverse(self.CoarseSelectionFunction, self.MyFineFundamentalFunction)
#Brokerage Model
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
'''Schedule Functions Here'''
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 1), self.CheckLiveTrading)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 1), self.Charts)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 1), self.CheckDailyLosses)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 1), self.UpdatePortValues)
for x in range (20,390,20):
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", x), self.UpdatePortValues)
for y in range (23,390,23):
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", y), self.CheckDailyLosses)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY", 1), self.CapturePortfolioValue)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY", 1), self.ClearOpenOrders)
'''Set Warmup Here'''
self.SetWarmup(TimeSpan.FromDays(30))
#Universe Selection
def CoarseSelectionFunction(self, coarse):
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
filtered = [ x.Symbol for x in sortedByDollarVolume
if x.Price > 10 and x.DollarVolume > 10000000 ]
return filtered[:500]
def MyFineFundamentalFunction(self, fine):
sortedByPeRatio = sorted(fine, key=lambda x: x.ValuationRatios.PERatio, reverse=False)
return [ x.Symbol for x in sortedByPeRatio[:10] ]
#OnData
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.'''
'''Arguments:
data: Slice object keyed by symbol containing the stock data'''
weighting = 1.0 / self.Securities.Count
for security in self.Securities.Values:
if not self.Securities[security.Symbol].Invested:
self.SetHoldings(security.Symbol, weighting)
#Charts
def Charts(self):
#Plot any relevant portfolio metrics
self.Plot('Margin Remaining', 'Margin Remaining', self.marginRemaining)
#self.PlotIndicator('RSI', self.tslaRsi, self.vixRsi, self.spyRsi)
#AtClose
def OnEndOfDay(self):
self.Log("Trading Day Has Ended")
#MarginCallWarning
def OnMarginCallWarning(self):
#Get a list of open orders to avoid margin issues
self.OpenOrders = self.Transactions.GetOpenOrders()
#Cancel any open orders.
if len(self.OpenOrders)> 0:
for x in self.OpenOrders:
self.Transactions.CancelOrder(x.Id)
#Rebalance to free up capital
self.Log("Rebalacing due to tight margin conditions")
self.MarginCall()
self.Log("WARNING: Day Start Portfolio Value: ${0} | Current Portfolio Value: ${1} | Loss: {2}%".format(
round(self.ClosingPortValue,2),
round(self.Portfolio.TotalPortfolioValue,2),
round( ( (int(self.ClosingPortValue)/int(self.Portfolio.TotalPortfolioValue)) -1)*100,2), ) )
#CheckLive
#Check connection at open and note the value gap
def CheckLiveTrading(self):
#Capture portfolio metrics at open. Verify live connection. Log previous close and overnight gap up/down
self.OpenOrders = self.Transactions.GetOpenOrders()
self.OpenPortValue = self.Portfolio.TotalPortfolioValue
self.OpenHoldValue = self.Portfolio.TotalHoldingsValue
self.Gap = round( ( ( ( int(self.OpenPortValue)/int(self.ClosingPortValue) ) - 1) * 100),2)
#Perform actions based on overnight changes in portfolio value
if self.Gap >= 10.00:
self.Log("Huge gap up today! {0}%!".format(self.Gap))
self.OhWow()
if self.Gap <= -4.25:
self.Log("Huge gap down today! {0}%!".format(self.Gap))
self.OhShit()
self.Log("Trading Live! || Yesterday's Closing Value: ${0}|| Opening Value: {1}% gap".format(self.ClosingPortValue, self.Gap))
return
#CaputureValue
#Capture the closing value of the portfolio and any open orders
def CapturePortfolioValue(self):
self.OpenOrders = self.Transactions.GetOpenOrders()
self.ClosingPortValue = self.Portfolio.TotalPortfolioValue
self.ClosingHoldValue = self.Portfolio.TotalHoldingsValue
self.Log("End Of Day Portfolio Values Have Been Captured")
return
#ClearOrders
#Clear open orders if there are 5 or more
def ClearOpenOrders(self):
#Get a list of open orders to avoid margin issues
self.OpenOrders = self.Transactions.GetOpenOrders()
#Cancel any open orders.
if len(self.OpenOrders)> 5:
for x in self.OpenOrders:
self.Transactions.CancelOrder(x.Id)
self.Log("Open Orders Have Been Closed.")
else:
return
#Update Portfolio Values
def UpdatePortValues(self):
if(self.IsMarketOpen("SPY")):
self.marginRemaining = self.Portfolio.MarginRemaining
self.CurrentPortValue = self.Portfolio.TotalPortfolioValue
self.CurrentHoldValue = self.Portfolio.TotalHoldingsValue
self.Log("Portfolio Values Have Been Updated")
#CheckLosses
#Check intraday losses and run a defensive function if a 5.6% drop is recognized at any time
def CheckDailyLosses(self):
if(self.IsMarketOpen("SPY")):
self.CurrentPerformance = round( ((float(self.CurrentPortValue)/float(self.ClosingPortValue))-1)*100,2)
if (self.CurrentPortValue <= self.ClosingPortValue*0.944):
self.HighLosses()
else: self.Log("Current Performance: {0}%".format(self.CurrentPerformance))
return
#HighLosses
#Liquidate most holdings after a 5.6% drop from previous portfolio close value.
def HighLosses(self):
#Get a list of open orders to avoid margin issues
self.OpenOrders = self.Transactions.GetOpenOrders()
#Cancel any open orders.
if len(self.OpenOrders)> 0:
for x in self.OpenOrders:
self.Transactions.CancelOrder(x.Id)
#Set portfolio to risk averse proportions and log important information
self.OhShit()
#Log important portfolio information when this function fires
self.Log("WARNING: Rebalancing due to excessive daily losses || Day Start Portfolio Value: ${0} || Current Portfolio Value: ${1} || Loss: {2}% || Gap at open: {3}%".format(
round(self.ClosingPortValue,2),
round(self.Portfolio.TotalPortfolioValue,2),
round( ( (int(self.ClosingPortValue)/int(self.Portfolio.TotalPortfolioValue)) -1)*100,2),
self.Gap) )
#Reset the reference point to catch any further 5.6% decreases with the new holdings.
self.ClosingPortValue = self.Portfolio.TotalPortfolioValue
#If there were any open orders left this will log how many. We cancelled them all, so this would be on the broker or connectivity
if (len(self.OpenOrders)> 0):
self.Log("Number of open orders: {0}".format( len(self.OpenOrders )))
return
#Ohshit
def OhShit(self):
return
#OhWow
def OhWow(self):
return
#MarginCall
def MarginCall(self):
return
#SPY
def SpyFunc(self):
self.Log("SPY Function Has Fired. || SPY RSI: {0}".format(
round(self.spyRsi.Current.Value),3))
#Used to control leverage
self.OpenOrders = self.Transactions.GetOpenOrders()
return
#TSLA
def TslaFunc(self):
self.Log("TSLA Function Has Fired. || Relevant Metrics: TSLA RSI: {0}".format(
round(self.tslaRsi.Current.Value,3)))
#Refresh open orders
self.OpenOrders = self.Transactions.GetOpenOrders()
return
#TVIX
def Func3(self):
#Get open orders. Used to prevent inappropriate use of leverage
self.OpenOrders = self.Transactions.GetOpenOrders()
return
'''
class Vix(PythonData):
#New VIX Object
def GetSource(self, config, date, isLiveMode):
#if isLiveMode:
return SubscriptionDataSource("http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv", SubscriptionTransportMedium.RemoteFile);
def Reader(self, config, line, date, isLiveMode):
# New VIX object
index = Vix()
index.Symbol = config.Symbol
#if isLiveMode:
if not (line.strip() and line[0].isdigit()): return None
try:
# Example File Format:
# Date, Open High Low Close Volume Turnover
# 1/1/2008 7792.9 7799.9 7722.65 7748.7 116534670 6107.78
data = line.split(',')
index.Time = datetime.strptime(data[0], "%m/%d/%Y")
index.Value = data[4]
index["Close"] = float(data[4])
except ValueError:
# Do nothing
return None
return index
'''