Donchian Channel Indicator


1) Donchian Channel Indicator gets ready before the input period (I guess it is 3 period less than the input). It calculates the highest high according to this intrinsic period. Log file:

2) How do I initialize it with historical data? I work on AlgorithmFramework structure and warm-up period didn't work. I tried something like the following from the example, but the I couldn't figure out what the format of the historical input has to be.

history = algorithm.History(addedSymbols, self.period, self.resolution)
ticker = SymbolCache.GetTicker(symbol)
# ???
self.donchian.Update(history.loc[ticker].Index, history.loc[ticker])
# ???



Update Backtest

If you use self.DCH(), the indicator will be updated automatically. You don't need self.RegisterIndicator(). You only need to initialize the indicator with Update().

For indicator constructor DonchianChannel("Donchian", period), you can use the Update() method to initialize the indicator and then use RegisterIndicator(symbol, indicator, consolidator) to update the indicator automatically.

This is an IBaseDataBar indicator because the calculation formula uses the open/high/low/close price. You should construct a QuoteBar with 

public QuoteBar(
DateTime time,
Symbol symbol,
IBar bid,
decimal lastBidSize,
IBar ask,
decimal lastAskSize,
Nullable<TimeSpan> period

Please check the documentation


Hello Jing Wu , 

thank you. I applied your suggestion into 'framework' structure. 
The donchian.UpperBand gets a value in the second period but donchian.LowerBand waits for 7 periods and is zero until then.
Why is it so?



 Hi Atacan,

The issue lies in the lastBidSize and lastAskSize. To construct the quote bar, we can not set their values to be None. The correct syntax should be 

history = self.History([eurusd.Symbol], 10, Resolution.Daily)
for tuple in history.itertuples():
bar = QuoteBar(tuple.Index-timedelta(days=1),
Bar(tuple.bidclose, tuple.bidhigh, tuple.bidlow, tuple.bidopen),
Bar(tuple.askclose, tuple.askhigh, tuple.asklow, tuple.askopen),

There's also a typo in your algorithm  (the fourth argument of Bar() method is tuple.bidopen instead of symbolData.Symbol.

bar = QuoteBar(tuple.Index, symbolData.Symbol,
Bar(tuple.bidclose,tuple.bidhigh,tuple.bidlow, tuple.bidopen), 0,
Bar(tuple.askclose,tuple.askhigh,tuple.asklow, tuple.askopen), 0,


Jing Wu hi,

I get this error: 

Runtime Error: This is a forward only indicator: EURUSD Input: 2017-01-17 00:00:00Z Previous: 2017-01-17 19:00:00Z

System.ArgumentException: This is a forward only indicator: EURUSD Input: 2017-01-17 00:00:00Z Previous: 2017-01-17 19:00:00Z
at QuantConnect.Indicators.IndicatorBase`1[T].Update (QuantConnect.Data.IBaseData input) [0x00063] in <40d1fbb2cc70483296faea5271a51178>:0
at QuantConnect.Algorithm.QCAlgorithm+<>c__DisplayClass477_0`1[T].b__1 (System.Object sender, QuantConnect.Data.IBaseData consolidated) [0x0000e] in :0
at QuantConnect.Data.Consolidators.DataConsolidator`1[TInput].OnDataConsolidated (QuantConnect.Data.IBaseData consolidated) [0x00010] in <3e4e2fe8d2794d82ba00323e20cf5973>:0
at QuantConnect.Data.Consolidators.PeriodCountConsolidatorBase`2[T, TConsolidated].OnDataConsolidated (TConsolidated e) [0x00001] in <3e4e2fe8d2794d82ba00323e20cf5973>:0
at QuantConnect.Data.Consolidators.PeriodCountConsolidatorBase`2[T, TConsolidated].Scan (System.DateTime currentLocalTime) [0x000a8] in <3e4e2fe8d2794d82ba00323e20cf5973>:0
at QuantConnect.Lean.Engine.AlgorithmManager.Run (QuantConnect.Packets.AlgorithmNodePacket job, QuantConnect.Interfaces.IAlgorithm algorithm, QuantConnect.Lean.Engine.DataFeeds.IDataManager dataManager, QuantConnect.Lean.Engine.DataFeeds.ISynchronizer synchronizer, QuantConnect.Lean.Engine.TransactionHandlers.ITransactionHandler transactions, QuantConnect.Lean.Engine.Results.IResultHandler results, QuantConnect.Lean.Engine.RealTime.IRealTimeHandler realtime, QuantConnect.Lean.Engine.Server.ILeanManager leanManager, QuantConnect.Lean.Engine.Alpha.IAlphaHandler alphas, System.Threading.CancellationToken token) [0x010f2] in Lean.Engine.Alpha.IAlphaHandler alphas, System.Threading.CancellationToken token) [0x010f2] in <20fbd5016b124364957b82e900d74eac>:0

I am confused also about the order of OHLC. Isn't it by standard in this order? Here it says OLHC.
Your correction is CHLO.


comment does not see the latest version. I paste the code


import numpy as np
from datetime import timedelta

from Alphas.EmaCrossAlphaModel import EmaCrossAlphaModel

from Risk.NullRiskManagementModel import NullRiskManagementModel

class BasicTemplateFrameworkAlgorithm(QCAlgorithmFramework):

def Initialize(self):

# self.SetWarmUp(10)
self.SetStartDate(2018, 1, 18) #Set Start Date
self.SetEndDate(2018, 10, 18) #Set End Date
self.SetCash(1000) #Set Strategy Cash

self.UniverseSettings.Resolution = Resolution.Daily
symbols = [Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda),
Symbol.Create("GBPUSD", SecurityType.Forex, Market.Oanda)]
self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) )



def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
# self.Debug("Purchased Stock: {0}".format(orderEvent.Symbol))

class customAlpha(AlphaModel):
def __init__(self):
self.resolution = Resolution.Daily
self.symbolDataBySymbol = {}

def Update(self, algorithm, data):
insights = []

for symbol, symbolData in self.symbolDataBySymbol.items():
algorithm.Log("Upper:"+ str(symbolData.donchian.UpperBand)+"Lower:"+ str(symbolData.donchian.LowerBand))

return insights

def OnSecuritiesChanged(self, algorithm, changes):
addedSymbols = [ x.Symbol for x in changes.AddedSecurities if x.Symbol not in self.symbolDataBySymbol]
if len(addedSymbols) == 0: return

history = algorithm.History(addedSymbols, 10, self.resolution)
for symbol in addedSymbols:
symbolData = SymbolData(algorithm, symbol, self.resolution)
self.symbolDataBySymbol[symbol] = symbolData
ticker = SymbolCache.GetTicker(symbol)

for tuple in history.loc[ticker].itertuples():

bar = QuoteBar(tuple.Index, symbolData.Symbol
, Bar(tuple.bidopen,tuple.bidhigh,tuple.bidlow, tuple.bidclose)
, 0
, Bar(tuple.askopen,tuple.askhigh,tuple.asklow, tuple.askclose)
, 0
symbolData.donchian.Update( bar)

class SymbolData:
'''Contains data specific to a symbol required by this model'''
def __init__(self, algorithm, symbol, resolution):
self.Symbol = symbol
self.resolution = resolution
self.donch_period = 7
self.ticker = SymbolCache.GetTicker(symbol)
self.donchian = DonchianChannel(self.ticker, self.donch_period, self.donch_period)
algorithm.RegisterIndicator(self.Symbol, self.donchian, self.resolution)

Sorry I made a mistake here. The time index in history request is the bar end time. When we create the QuoteBar with QuoteBar(timesymbolbidlastBidSizeasklastAskSizeperiod), the "time" argument should be the bar start time. Then the bar end time is time + period. 

To fix this issue, you need to subtract the unit time interval from the end time index in history request. For example, you use the daily history price to update the indicator, the "time" argument should be "tuple.Index-timedelta(days=1)". Please see the attached algorithm

For the order of OHLC in bar construction, you need to refer to the LEAN class documentation (search "Bar Constructor ") and follow the syntax 

public Bar(
decimal open,
decimal high,
decimal low,
decimal close


hello Jing Wu 

Thank you very much. 

FYI: Before this, the bands were being updated with every new bar. i.e. the UpperBand became equal to the current bar's high. I did not want that behaviour and defined a variable like "previous_band". After changing the first parameter, now it does not take the current bar into account and gives the highest high of only the previous n bars.

However, for another strategy I might need it to be updated with every bar. How would it be possible?


Now you only use the Update() to warm-up the indicator. Instead of using RegisterIndicator(), you can also use Update() to update the indicator value with the current bar value manually. 

# if manually update the indciator with the daily bar, remove the RegisterIndicator()
def Update(self, algorithm, data):
for symbol, symbolData in self.symbolDataBySymbol.items():
if data.ContainsKey(symbol):
quoteBar = data[symbol]

Update Backtest


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.


This discussion is closed