I add 1 hour frequence bars data and consolidated into 4 hours bars, but when I add a breakpoint to check the data in OnData, I find "self.Time" is out of consolidated Bar's "Open time" and "Close time";
Please check my code and set a breakpoint on line 57 and then run it step by step; most time you would see in OnData "self.TIme"/"data.TIme" is out of symbolData.Bars[0]'s time range as below picture.
I think on above piture, “self.Time” and “data.Time” are both the current one hour bar's Open Time and their values shoud be [conBar.Time, conBar.EndTime), but in the fact, their values are out of this range.
I guess maybe this is because of Time Zone problem, but I can't confirm and don't know how to correct it.
from collections import deque
from AlgorithmImports import *
class ConsolidatedIndicators(QCAlgorithm):
def Initialize(self):
self.SetTimeZone("UTC")
self.SetStartDate(2021, 2, 25)
# self.SetEndDate(2022, 3, 2)
self.SetBrokerageModel(BrokerageName.Binance, AccountType.Cash)
self.SetAccountCurrency("USDT")
self.SetCash(100000)
self.SetBenchmark(Symbol.Create("BTCUSDT", SecurityType.Crypto, Market.Binance))
self.Data = {}
self.symbols = {'BTCUSDT': 'BTCUSDT', 'ETHUSDT': 'ETHUSDT', 'XRPUSDT': 'XRPUSDT', 'ADAUSDT': 'ADAUSDT',
'DOTUSDT': 'DOTUSDT', 'MATICUSDT': 'MATICUSDT', 'ATOMUSDT': 'ATOMUSDT', 'LINKUSDT': 'LINKUSDT',
'TRXUSDT': 'TRXUSDT', 'FILUSDT': 'FILUSDT', 'LTCUSDT': 'LTCUSDT', 'ETCUSDT': 'ETCUSDT',
'BCHUSDT': 'BCHUSDT'}
BarPeriod = TimeSpan.FromHours(4)
coins = list(self.symbols.keys())
for si in coins:
try:
crypto = self.AddCrypto(si, Resolution.Hour, Market.Binance)
self.Data[si] = SymbolData(crypto.Symbol, BarPeriod)
self.Securities[si].SetFeeModel(CustomFeeModel())
except:
self.symbols.pop(si)
self.Log(f"can't load {si}")
for symbol, symbolData in self.Data.items():
symbolData.Indicators['CCI'] = IndicatorCCI()
symbolData.Indicators['REV'] = IndicatorReverse()
consolidator = TradeBarConsolidator(
BarPeriod) if symbolData.Symbol.SecurityType == SecurityType.Crypto else QuoteBarConsolidator(BarPeriod)
consolidator.DataConsolidated += self.OnDataConsolidated
self.SubscriptionManager.AddConsolidator(symbolData.Symbol, consolidator)
self.SetWarmup(200, Resolution.Hour)
def OnDataConsolidated(self, sender, bar):
self.Data[bar.Symbol.Value].Bars.Add(bar)
for _, indi in self.Data[bar.Symbol.Value].Indicators.items():
indi.Update(bar)
def OnData(self, data):
if self.IsWarmingUp:
return
symbolData = self.Data['BTCUSDT']
conBar = symbolData.Bars[0]
for symbol in self.Data.keys():
symbolData = self.Data[symbol]
if symbolData.IsReady() and symbolData.WasJustUpdated(data.Time):
pass
else:
self.Log(f"{symbol} not has been prepared.")
return
self.Log('Run Once.')
class SymbolData(object):
def __init__(self, symbol, barPeriod, windowSize=5):
self.Symbol = symbol
self.BarPeriod = barPeriod
self.Bars = RollingWindow[IBaseDataBar](windowSize)
self.Indicators = {}
def IsReady(self):
if self.Bars.IsReady:
for _, indi in self.Indicators.items():
if not indi.IsReady:
return False
else:
return False
return True
def WasJustUpdated(self, current):
return self.Bars.Count > 0 and self.Bars[0].Time <= current - self.BarPeriod < self.Bars[0].EndTime
class CustomFeeModel:
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.001
return OrderFee(CashAmount(fee, 'USDT'))
class IndicatorCCI:
def __init__(self, name='CCI', period=20):
self.Name = name
self.period = period
self.Time = datetime.min
self.Value = 0
self.IsReady = False
self.closes = deque(maxlen=self.period)
self.oldest_close = None
self.MA = deque(maxlen=self.period)
self.MA_plus_abs = deque(maxlen=self.period)
self.oldest_MA_plus_abs = None
self.MD = None
def Update(self, input):
closei = input.Close
self.closes.append(closei)
self.Time = input.EndTime
if len(self.closes) == self.period:
if self.oldest_close is None:
self.MA.append(sum(self.closes) / self.period)
self.MA_plus_abs.append(abs(self.MA[-1] - closei))
self.oldest_close = self.closes[0]
else:
self.MA.append(self.MA[-1] + (closei - self.oldest_close) / self.period)
self.MA_plus_abs.append(abs(self.MA[-1] - closei))
self.oldest_close = self.closes[0]
if len(self.MA_plus_abs) == self.period:
if self.oldest_MA_plus_abs is None:
self.IsReady = True
self.MD = sum(self.MA_plus_abs) / self.period
else:
self.MD += (self.MA_plus_abs[-1] - self.oldest_MA_plus_abs) / self.period
self.oldest_MA_plus_abs = self.MA_plus_abs[0]
self.Value = ((input.High + input.Low + closei) / 3 - self.MA[-1]) / self.MD
class IndicatorReverse:
def __init__(self, name='Reverse', period=10):
self.Name = name
self.period = period
self.Time = datetime.min
self.Value = 0
self.IsReady = False
self.closes = deque(maxlen=self.period)
def Update(self, input):
self.closes.append(input.Close)
self.Time = input.EndTime
if not self.IsReady:
if len(self.closes) < self.period:
return
else:
self.IsReady = True
self.Value = self.closes[-1] / self.closes[0] - 1.0
Fred Painchaud
Hi Frank,
In your picture:
self.Time and data.Time are the current hour bar's END TIME.
And conBar is the last consolidated 4-hour bar for BTCUSDT, from 00:00 to 04:00.
ALL on 25 Feb 2021.
I don't see any issue. Sorry. Something I am missing?
Note that OnData does not receive consolidated bars but hourly bars in your case. It is called every hour and your rolling windows receive 4-hour consolidated bars. So sometimes, in OnData, the bars you get are in-between 4-hour bars.
Fred
Frank cao
Hi Fred Painchaud
Thanks for your reply, the problem is as below:
In the picture, just as you said “self.Time” and “data.Time” is the 1 hour bar's end time; and “conBar” is the latest Consolidated 4 hours' Bar, then “conBar.Time” and “conBar.EndTime” should be Open Time and End Time of the 4 hours' Bar, so “self.Time” should be in the range of Open Time and End Time, but it is not as the picture shows.
Fred Painchaud
Hi Frank,
Your consolidated bars (4-hour) will be:
Your hour bars will be:
The ones you are currently seeing are in bold.
The next 4-hour bar will only be ready at 08:00. Your hour bars are never inside your 4 hour bars. The consolidator emit 4-hour bars at the end of the 4-hour period, so at 04:00, 08:00, 12:00, 16:00, 20:00 and 00:00.
Hope this is clearer.
Strong read recommendation:
Fred
Frank cao
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!