From 4f1927bc34d4eaa18c7a9cd9179ccdcca51393b7 Mon Sep 17 00:00:00 2001 From: Frederic Fortier Date: Mon, 14 Aug 2017 18:37:36 -0400 Subject: [PATCH] Polishing live trading components --- catalyst/exchange/bitfinex.py | 2 +- catalyst/exchange/data_portal_exchange.py | 2 +- catalyst/exchange/exchange.py | 4 +- catalyst/exchange/exchange_clock.py | 23 ++++-------- tests/exchange/test_bitfinex.py | 4 +- tests/exchange/test_clock.py | 45 +++++++++++++++++++++++ 6 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 tests/exchange/test_clock.py diff --git a/catalyst/exchange/bitfinex.py b/catalyst/exchange/bitfinex.py index 8a0152e5..b88a516e 100644 --- a/catalyst/exchange/bitfinex.py +++ b/catalyst/exchange/bitfinex.py @@ -308,7 +308,7 @@ class Bitfinex(Exchange): last_traded=pd.Timestamp.utcfromtimestamp(candles[0] / 1000.0), ) - if not field in ohlc: + if field not in ohlc: raise KeyError('Invalid column: ' + str(field)) return ohlc[field] diff --git a/catalyst/exchange/data_portal_exchange.py b/catalyst/exchange/data_portal_exchange.py index fcb61641..b61a234f 100644 --- a/catalyst/exchange/data_portal_exchange.py +++ b/catalyst/exchange/data_portal_exchange.py @@ -45,7 +45,7 @@ class DataPortalExchange(DataPortal): # The returned dataframe contains today's value as a NaN because # end_dt points to the current wall clock. We drop today's # value to be in sync with the simulation's behavior. - today = pd.to_datetime('now').date() + today = pd.Timestamp.utcnow() return history_window[history_window.index.date != today] def get_spot_value(self, assets, field, dt, data_frequency): diff --git a/catalyst/exchange/exchange.py b/catalyst/exchange/exchange.py index 0756be09..682f98d1 100644 --- a/catalyst/exchange/exchange.py +++ b/catalyst/exchange/exchange.py @@ -1,7 +1,7 @@ import abc -from collections import namedtuple -from abc import ABCMeta, abstractmethod, abstractproperty import json +from abc import ABCMeta, abstractmethod, abstractproperty + import pandas as pd from catalyst.assets._assets import Asset diff --git a/catalyst/exchange/exchange_clock.py b/catalyst/exchange/exchange_clock.py index 1750977f..4dee0586 100644 --- a/catalyst/exchange/exchange_clock.py +++ b/catalyst/exchange/exchange_clock.py @@ -40,11 +40,11 @@ class ExchangeClock(object): """ def __init__(self, - sessions, - execution_opens, - execution_closes, - before_trading_start_minutes, - minute_emission, + sessions=None, + execution_opens=None, + execution_closes=None, + before_trading_start_minutes=None, + minute_emission=False, time_skew=pd.Timedelta("0s")): self.sessions = sessions self.execution_opens = execution_opens @@ -55,21 +55,14 @@ class ExchangeClock(object): self._last_emit = None self._before_trading_start_bar_yielded = False - # It is expected to have this clock created once a day (ideally prior - # to BEFORE_TRADING_START_BAR event). Multiple days (sessions) are - # not supported. - assert len(self.sessions) == 1 - def __iter__(self): - yield self.sessions[0], SESSION_START + yield pd.Timestamp.utcnow(), SESSION_START while True: current_time = pd.Timestamp.utcnow() - server_time = (current_time + self.time_skew).floor('1 min') + server_time = current_time.floor('1 min') - if (self._last_emit is None or - server_time - self._last_emit >= - pd.Timedelta('1 minute')): + if self._last_emit is None or server_time > self._last_emit: self._last_emit = server_time yield server_time, BAR diff --git a/tests/exchange/test_bitfinex.py b/tests/exchange/test_bitfinex.py index 608ef5a9..2ed83c63 100644 --- a/tests/exchange/test_bitfinex.py +++ b/tests/exchange/test_bitfinex.py @@ -44,7 +44,7 @@ class BitfinexTestCase(BaseExchangeTestCase): asset=bitfinex.get_asset('eth_usd'), style=LimitOrder(limit_price=200), limit_price=200, - amount=1, + amount=0.5, stop_price=None ) log.info('order created {}'.format(order_id)) @@ -53,7 +53,7 @@ class BitfinexTestCase(BaseExchangeTestCase): def test_get_order(self): log.info('querying orders from bitfinex') bitfinex = Bitfinex() - response = bitfinex.get_order(order_id=3330866978) + response = bitfinex.get_order(order_id=3361248395) log.info('the order: {}'.format(response)) pass diff --git a/tests/exchange/test_clock.py b/tests/exchange/test_clock.py new file mode 100644 index 00000000..eeafc8ac --- /dev/null +++ b/tests/exchange/test_clock.py @@ -0,0 +1,45 @@ +from unittest import TestCase +from logbook import Logger +from mock import patch, sentinel +from catalyst.exchange.exchange_clock import ExchangeClock +from catalyst.utils.calendars.trading_calendar import days_at_time +from datetime import time +from collections import defaultdict +from catalyst.utils.calendars import get_calendar +import pandas as pd + +log = Logger('BitfinexTestCase') + + +class BitfinexTestCase(TestCase): + def setUp(self): + self.internal_clock = None + self.events = defaultdict(list) + + def advance_clock(self, x): + """Mock function for sleep. Advances the internal clock by 1 min""" + # The internal clock advance time must be 1 minute to match + # MinutesSimulationClock's update frequency + self.internal_clock += pd.Timedelta('1 min') + + def get_clock(self, arg, *args, **kwargs): + """Mock function for pandas.to_datetime which is used to query the + current time in RealtimeClock""" + assert arg == "now" + return self.internal_clock + + def test_clock(self): + with patch('catalyst.exchange.exchange_clock.pd.to_datetime') as to_dt, \ + patch('catalyst.exchange.exchange_clock.sleep') as sleep: + clock = ExchangeClock() + to_dt.side_effect = self.get_clock + sleep.side_effect = self.advance_clock + start_time = pd.Timestamp.utcnow() + self.internal_clock = start_time + + log.info('listing events') + events = list(clock) + + # Event 0 is SESSION_START which always happens at 00:00. + ts, event_type = events[1] + pass