# Import modules required
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Python import PythonData
from datetime import date, timedelta, datetime
import decimal
import numpy as np
# Useful references:
# https://www.quantconnect.com/lean/documentation/topic16.html
# -> bad link for "Hard Method" Static Sourcing
# https://www.quantconnect.com/docs/algorithm-reference/importing-custom-data
class BasicTemplateAlgorithm(QCAlgorithm):
def Initialize(self):
'''Initialize algorithm.'''
# Set backtest details
self.SetStartDate(2018,9,1)
self.SetEndDate(2018,9,8)
self.SetCash(100000)
#self.SetBenchmark("SPY")
self.UniverseSettings.Resolution = Resolution.Minute
# German XETRA (IBIS) exchange / Berlin time (9:00-17:30)
# https://www.interactivebrokers.com/en/index.php?f=2222&exch=ibis
self.SetTimeZone(TimeZones.Berlin)
# Define Brokerage model and account type
self.SetBrokerageModel(
BrokerageName.InteractiveBrokersBrokerage,
AccountType.Margin)
# Define stocks for system
self.assets = ["1COV"] # I'll eventually add more here...
# System User Inputs
self.trading_time_start = '09:02'
#self.exchange_open_time = '09:00' # for reference (Berlin tz)
self.trading_time_stop = '17:20'
self.exchange_close_time = '17:30' # Berlin tz
# Add equities to algorithm
equities = {}
equity_data = {}
for asset in self.assets:
equities[asset] = self.AddEquity(asset, Resolution.Minute)
#market=Market.Germany # does this even matter for custom data?
#market default seems to be Market.USA
equity_data[asset] = self.AddData(MyCustomData, asset)
equities[asset].MarginModel = PatternDayTradingMarginModel()
# Just adding this so the algo will run
self.spy = "SPY"
self.AddEquity(self.spy, Resolution.Minute)
# Create variable(s) used for algorithm
self.entries = False
# Schedule functions
# Check for entry signals at self.trading_time_start
start_split = self.trading_time_start.split(':')
start_hrs = int(start_split[0])
start_minutes = int(start_split[1])
self.Log('call MyEntries at {}:{}'.format(start_hrs, start_minutes))
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.At(start_hrs, start_minutes),
Action(self.MyEntries)
)
# Schedule closing all positions at self.trading_time_stop
stop_split = self.trading_time_stop.split(':')
stop_hrs = int(stop_split[0])
stop_minutes = int(stop_split[1])
self.Log('call CloseAllPositions at {}:{}'.format(stop_hrs, stop_minutes))
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.At(stop_hrs, stop_minutes),
Action(self.CloseAllPositions)
)
# Schedule end of day function
close_split = self.exchange_close_time.split(':')
close_hrs = int(close_split[0])
close_minutes = int(close_split[1])
self.Log('call OnEndOfDay at {}:{}'.format(close_hrs, close_minutes))
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.At(close_hrs, close_minutes),
Action(self.OnEndOfDay)
)
def MyEntries(self):
'''Enter positions for the day.'''
self.Log('MyEntries() called')
try:
for asset in self.assets:
# Get latest closing price
#self.Log('{} close: {}'.format(asset, self.close[asset]))
# Place market entry order
self.MarketOrder(asset, 100)
except:
pass
# Update self.entries variable
self.entries = True
def CancelOpenOrders(self):
'''Cancel all open orders.'''
open_orders = self.Transactions.GetOpenOrders()
if open_orders:
for order in open_orders:
self.Log('Cancelling order: {}'.format(order))
self.Transactions.CancelOrder(order.Id)
def CancelSymbolOpenOrders(self, symbol):
'''Cancel all open orders for a given symbol.'''
open_orders = self.Transactions.GetOpenOrders(symbol)
if open_orders:
for order in open_orders:
self.Log('Cancelling order: {}'.format(order))
self.Transactions.CancelOrder(order.Id)
def CloseAllPositions(self):
'''Close all positions.'''
# Cancel open orders
self.CancelOpenOrders()
# Close all equity positions
self.Log('Closing all positions for the end of the trading day')
for asset in self.assets:
shares = self.Portfolio[asset].Quantity
if shares != 0:
self.MarketOrder(asset, -shares)
self.Log(' Close position for {} shares of {}'.format(
shares, asset))
def OnData(self, slice):
'''Each new data point will be pumped in here.'''
# Skip once positions are entered for the day
if self.entries:
return
# Get the latest prices?
self.close = {}
for asset in self.assets:
if slice.ContainsKey(asset):
self.close[asset] = data[asset].Close
self.Debug("Time: {}, {} close: {}".format(
datetime.now(), asset, self.close[asset]))
def OnEndOfDay(self):
'''Called at the end of every day.'''
# Reset variable
self.entries = False
def OnOrderEvent(self, orderEvent):
# Skip if not filled
if orderEvent.Status != OrderStatus.Filled:
return
# Get order info
order = self.Transactions.GetOrderById(orderEvent.OrderId)
# Check for filled entry order
current_qty = self.Portfolio[order.Symbol].Quantity
order_qty = order.Quantity
if current_qty == order_qty:
self.Log('Filled entry order: {}'.format(order))
elif current_qty == 0:
# Cancel other open exit order for symbol
self.Log('Filled exit order: {}'.format(order))
self.CancelSymbolOpenOrders(order.Symbol)
# Print order details when desired
self.Log(str(orderEvent))
class MyCustomData(PythonData):
'''
Custom Data Class
REFs:
https://www.quantconnect.com/forum/discussion/4079/python-best-practise-for-using-consolidator-on-custom-data/p1
'''
def GetSource(self, config, date, isLiveMode):
file = "https://docs.google.com/spreadsheets/d/e/2PACX-1vReHX1zAOU8Kgylr1npfZjxw8b52vXA5EH5MpBkFqm2-eN1GVXYi7ei8T_a1xiJReDNulRerDmqpg9L/pub?output=csv"
return SubscriptionDataSource(
file, SubscriptionTransportMedium.RemoteFile)
def Reader(self, config, line, date, isLiveMode):
# If first character is not digit, pass
if not (line.strip() and line[0].isdigit()):
return None
# New object
asset = MyCustomData()
asset.Symbol = config.Symbol
#asset.DataType = MarketDataType.TradeBar
try:
# Example File Format:
# Date Open High Low Close Volume
# 2017-01-02 09:02:00+01:00 64.88 64.88 64.43 64.43 8082
# Date, Open High Low Close Volume Turnover
# 2011-09-13 7792.9 7799.9 7722.65 7748.7 116534670 6107.78
data = line.split(',')
_date = datetime.strptime(data[0].split(' ')[0], "%Y-%m-%d")
_time = data[0].split(' ')[1]
#asset.Time = ?
asset.Value = decimal.Decimal(data[4])
asset["Open"] = float(data[1])
asset["High"] = float(data[2])
asset["Low"] = float(data[3])
asset["Close"] = float(data[4])
asset["Volume"] = float(data[5])
except ValueError:
# Do nothing
return None
return asset