Hello,
I’m still new to QuantConnect and I used the recent time to read a lot in the community in order to get my head around the main concepts. In parallel I tried to practice with code.
I would like to code a basic framework that can handle the most common parts like:
- defining the main parameters (account size, backtesting timeframe, symbol, market, resolution, brokerage, leverage, etc.)
- defining the criteria/rules for the trading setup (including indicators if required)
- defining the risk and order management (position size, trade entry, stop loss, take profit)
- using Debug function
- using RollingWindow function
- using Schedule function
Please find below my example code.
This example strategy is NOT designed to be profitable, but for learning purposes only! :-)
In this example the strategy is long-only and consists of 3 simple criteria:
- if fast EMA > slow EMA then long
- only enter position between 9-10am UTC
- only have one position at a time
The RollingWindow function is used to derive the entry, stop loss and take profit levels from the previous candle:
- stop buy at previous candle high
- stop loss at previous candle low
- take profit at previous candle high plus 10%
The Schedule function is used to handle the required trading time slot (9-10am UTC).
The Risk Management is determined by 2 requirements:
- max risk per trade = 0.5% from portfolio value
- max equity per trade = portfolio value * max leverage * 25%
I would like to use this first example code to identify and solve the main issues and potential misconceptions that I (probably) have and then take it further from there.
Your feedback and comments are highly appreciated!
Cheers,
Thom
class SessionTrend(QCAlgorithm):
def Initialize(self):
# input
self.SetTimeZone('Europe/London')
self.SetCash(100000)
self.SetStartDate(2019,4,1)
self.SetEndDate(2019,4,30)
self._max_risk_per_trade_factor = 0.005
self._max_equity_per_trade_factor = 0.25
self._maxLeverage_factor = 20.0
self._symbol = 'EURUSD'
self._pricePrecision = 4
self._resolution = Resolution.Hour
self._market = Market.Oanda
self._brokerage = BrokerageName.OandaBrokerage
self.SetBrokerageModel(self._brokerage, AccountType.Margin)
# add Price Data to data
self._eurusd = self.AddForex(self._symbol, self._resolution, self._market, leverage=10.0)
# RollingWindow
self._window = RollingWindow[QuoteBar](2)
# indicators
self._ema_fast = self.EMA(self._symbol, 8, self._resolution)
self._ema_slow = self.EMA(self._symbol, 21, self._resolution)
# schedule
self._session = False
self.Schedule.On(self.DateRules.EveryDay(self._symbol), self.TimeRules.At(9, 0), Action(self._sessionOn))
self.Schedule.On(self.DateRules.EveryDay(self._symbol), self.TimeRules.At(10, 0), Action(self._sessionOff))
def _sessionOn(self):
self._session = True
def _sessionOff(self):
self._session = False
def OnData(self, data):
# retrieve current price data
self._currentPrice = self.Securities[self._symbol].Close
# add price data to RollingWindow
self._window.Add(data[self._symbol])
# wait for indicator to be ready.
if not self._ema_slow.IsReady: return
# Setup criteria
_invested = self.Portfolio[self._symbol].Invested
_ema_bullish = (self._ema_fast.Current.Value > self._ema_slow.Current.Value)
_condition = not _invested and self._session and _ema_bullish
# long orders
if _condition:
# trade setup
self._stopBuy = round(self._window[1].High, self._pricePrecision)
self._stopLoss = round(self._window[1].Low, self._pricePrecision)
self._takeProfit = round(self._window[1].High * 1.1, self._pricePrecision)
# risk management
self._max_equity_per_trade = self.Portfolio.TotalPortfolioValue * self._maxLeverage_factor * self._max_equity_per_trade_factor # max allowed equity per trade [currency]
self._max_risk_per_trade = round(self.Portfolio.TotalPortfolioValue * self._max_risk_per_trade_factor, 0) # max allowed risk per trade [currency]
self._risk_per_unit = round(self._stopBuy - self._stopLoss, self._pricePrecision) # risk per unit [currency]
self._max_quantity_equity = round(self._max_equity_per_trade / self._stopBuy, 0) # max quantity per trade [nr. of contracts] based on max equity
self._max_quantity_risk = round(self._max_risk_per_trade / self._risk_per_unit, 0) # max quantity per trade [nr. of contracts] based on max risk
self._quantity = round(min(self._max_quantity_equity, self._max_quantity_risk), 0) # number of contracts to be ordered
# order management
self._marketBuyTicket = self.MarketOrder(self._symbol, self._quantity, False, 'market buy')
self._limitSellTicket = self.LimitOrder(self._symbol, -self._quantity, self._takeProfit, 'target limit sell')
self._stopMarketSellTicket = self.StopMarketOrder(self._symbol, -self._quantity, self._stopLoss, 'stop market sell')
self.Debug(f'order time: {self.Time}, tag: {self._marketBuyTicket.Tag}, quantity: {self._marketBuyTicket.Quantity}, fill price: {self._marketBuyTicket.AverageFillPrice}, status: {self._marketBuyTicket.Status}')
def OnOrderEvent(self, orderEvent):
_orderFromEvent = self.Transactions.GetOrderById(orderEvent.OrderId)
_orderTicketFromEvent = self.Transactions.GetOrderTicket(orderEvent.OrderId)
_openOrderTickets = self.Transactions.GetOrderTickets()
# OCO
if _orderTicketFromEvent.Status == OrderStatus.Filled:
# cancel stop loss order
if _orderTicketFromEvent.Tag == 'target limit sell':
for _ticket in _openOrderTickets:
if _ticket.Tag == 'stop market sell':
self.Debug(f'event time: {orderEvent.UtcTime}, event: {_orderTicketFromEvent.Tag}, cancelled order: {_ticket.Tag}')
self.Transactions.CancelOrder(_ticket.OrderId)
# cancel take profit order
elif _orderTicketFromEvent.Tag == 'stop market sell':
for _ticket in _openOrderTickets:
if _ticket.Tag == 'target limit sell':
self.Debug(f'event time: {orderEvent.UtcTime}, event: {_orderTicketFromEvent.Tag}, cancelled order: {_ticket.Tag}')
self.Transactions.CancelOrder(_ticket.OrderId)
Douglas Stridsberg
Hey!
Before you go any further, you should familiarise yourself with the Framework Algorithm and use it. It will make your development process a lot easier and cleaner, as you'll be splitting your algorithm up into modules which all do separate things (alpha generation, portfolio construction, risk management, etc.) and you won't have to clog up your OnData function.
Thom Yorke
Thanks Douglas, I will do that!
Thom Yorke
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!