From b699996d2b82b1934db1dc45a868f0a2349f9edf Mon Sep 17 00:00:00 2001 From: ic-gcp <64914822+ic-gcp@users.noreply.github.com> Date: Sat, 16 Jan 2021 00:05:18 +0500 Subject: [PATCH 1/2] Use timezone aware timestamps Changed code to use timezone aware timestamps for dtbegin and dtend, so that they are converted correctly to the users passed timezone --- alpaca_backtrader_api/alpacadata.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/alpaca_backtrader_api/alpacadata.py b/alpaca_backtrader_api/alpacadata.py index 79835c57..afb310f1 100644 --- a/alpaca_backtrader_api/alpacadata.py +++ b/alpaca_backtrader_api/alpacadata.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta +import pytz from backtrader.feed import DataBase from backtrader import date2num, num2date from backtrader.utils.py3 import queue, with_metaclass @@ -132,6 +133,7 @@ class AlpacaData(with_metaclass(MetaAlpacaData, DataBase)): ('reconnect', True), ('reconnections', -1), # forever ('reconntimeout', 5.0), + ('tz', pytz.timezone('US/Eastern')), ) _store = alpacastore.AlpacaStore @@ -210,11 +212,11 @@ def _st_start(self, instart=True, tmout=None): self.put_notification(self.DELAYED) dtend = None if self.todate < float('inf'): - dtend = num2date(self.todate) + dtend = self.p.tz.localize(num2date(self.todate)) dtbegin = None if self.fromdate > float('-inf'): - dtbegin = num2date(self.fromdate) + dtbegin = self.p.tz.localize(num2date(self.fromdate)) self.qhist = self.o.candles( self.p.dataname, dtbegin, dtend, @@ -319,12 +321,12 @@ def _load(self): # len == 1 ... forwarded for the 1st time dtbegin = self.datetime.datetime(-1) elif self.fromdate > float('-inf'): - dtbegin = num2date(self.fromdate) + dtbegin = self.p.tz.localize(num2date(self.fromdate)) else: # 1st bar and no begin set # passing None to fetch max possible in 1 request dtbegin = None - dtend = datetime.utcfromtimestamp(int(msg['time'])) + dtend = datetime.fromtimestamp(int(msg['time']), tz=pytz.utc) self.qhist = self.o.candles( self.p.dataname, dtbegin, dtend, From e53fdb673543ed0b71cbc50b835fe7239180b46a Mon Sep 17 00:00:00 2001 From: ic-gcp <64914822+ic-gcp@users.noreply.github.com> Date: Sat, 16 Jan 2021 00:09:32 +0500 Subject: [PATCH 2/2] Changed _make_sure_dates_are_initialized_properly Made changes in the function "_make_sure_dates_are_initialized_properly" so that dtend and dtbegin are localized correctly according to the timezone specified by the user. --- alpaca_backtrader_api/alpacastore.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/alpaca_backtrader_api/alpacastore.py b/alpaca_backtrader_api/alpacastore.py index bccdb364..5c10b6f7 100644 --- a/alpaca_backtrader_api/alpacastore.py +++ b/alpaca_backtrader_api/alpacastore.py @@ -170,6 +170,7 @@ async def on_trade(self, conn, stream, msg): class MetaSingleton(MetaParams): '''Metaclass to make a metaclassed class a singleton''' + def __init__(cls, name, bases, dct): super(MetaSingleton, cls).__init__(name, bases, dct) cls._singleton = None @@ -451,18 +452,12 @@ def _make_sure_dates_are_initialized_properly(self, dtbegin, dtend, if not dtend: dtend = pd.Timestamp('now', tz=NY) else: - dtend = pd.Timestamp(pytz.timezone(NY).localize(dtend)) + if dtend.tzinfo: + dtend = pd.Timestamp(dtend.astimezone(pytz.timezone(NY))) + else: + dtend = pd.Timestamp(pytz.timezone(NY).localize(dtend)) if granularity == Granularity.Minute: calendar = trading_calendars.get_calendar(name='NYSE') - if pd.Timestamp('now', tz=NY).date() == dtend.date(): - if calendar.is_open_on_minute(dtend): - # we execute during market open, we don't want today's data - # we will receive it through the websocket - dtend = dtend.replace(hour=15, - minute=59, - second=0, - microsecond=0) - dtend -= timedelta(days=1) while not calendar.is_open_on_minute(dtend): dtend = dtend.replace(hour=15, minute=59, @@ -474,7 +469,10 @@ def _make_sure_dates_are_initialized_properly(self, dtbegin, dtend, delta = timedelta(days=days) dtbegin = dtend - delta else: - dtbegin = pd.Timestamp(pytz.timezone(NY).localize(dtbegin)) + if dtbegin.tzinfo: + dtbegin = pd.Timestamp(dtbegin.astimezone(pytz.timezone(NY))) + else: + dtbegin = pd.Timestamp(pytz.timezone(NY).localize(dtbegin)) while dtbegin > dtend: # if we start the script during market hours we could get this # situation. this resolves that. @@ -500,6 +498,7 @@ def get_aggs_from_polygon(self, time window of 2 weeks, and split the calls until we get all required data """ + def _clear_out_of_market_hours(df): """ only interested in samples between 9:30, 16:00 NY time @@ -569,6 +568,7 @@ def get_aggs_from_alpaca(self, but we need to manipulate it to be able to work with it smoothly and return data the same way polygon does """ + def _iterate_api_calls(): """ you could get max 1000 samples from the server. if we need more @@ -829,6 +829,7 @@ def _check_if_transaction_occurred(order_id): if trans is None: break self._process_transaction(order_id, trans) + while True: try: if self.q_ordercreate.empty(): @@ -916,7 +917,7 @@ def _transaction(self, trans): self._transpend[oid].append(trans) self._process_transaction(oid, trans) - _X_ORDER_FILLED = ('partially_filled', 'filled', ) + _X_ORDER_FILLED = ('partially_filled', 'filled',) def _process_transaction(self, oid, trans): try: