diff --git a/catalyst/examples/buy_the_dip_live.py b/catalyst/examples/buy_the_dip_live.py index 18a33fb6..e992dbe8 100644 --- a/catalyst/examples/buy_the_dip_live.py +++ b/catalyst/examples/buy_the_dip_live.py @@ -1,5 +1,6 @@ +import talib from logbook import Logger -from catalyst.utils.run_algo import run_algorithm + from catalyst.api import ( order, order_target_percent, @@ -7,10 +8,7 @@ from catalyst.api import ( record, get_open_orders, ) -from catalyst.errors import ZiplineError -from catalyst.exchange.exchange_utils import get_exchange_folder, \ - download_exchange_symbols, get_exchange_auth -import talib +from catalyst.utils.run_algo import run_algorithm algo_namespace = 'buy_the_dip_live' log = Logger(algo_namespace) @@ -48,8 +46,10 @@ def _handle_data(context, data): buy_increment = 50 elif rsi <= 40: buy_increment = 20 - else: + elif rsi <= 70: buy_increment = 5 + else: + buy_increment = None cash = context.portfolio.cash log.info('base currency available: {cash}'.format(cash=cash)) @@ -84,23 +84,14 @@ def _handle_data(context, data): ) ) - # if position.amount > 0: - # order_target_percent( - # asset=context.asset, - # target=0, - # limit_price=price * (1 - context.SLIPPAGE_ALLOWED), - # ) - # log.debug('liquidated the position') - # return - if position.amount >= context.TARGET_POSITIONS: log.info('reached positions target: {}'.format(position.amount)) return if price < cost_basis: is_buy = True - elif position > 0 and \ - (price > cost_basis * (1 + context.PROFIT_TARGET) or rsi > 70): + elif position.amount > 0 and \ + price > cost_basis * (1 + context.PROFIT_TARGET): profit = (price * position.amount) - (cost_basis * position.amount) log.info('closing position, taking profit: {}'.format(profit)) order_target_percent( diff --git a/catalyst/exchange/algorithm_exchange.py b/catalyst/exchange/algorithm_exchange.py index 72c46cf5..00d90b8b 100644 --- a/catalyst/exchange/algorithm_exchange.py +++ b/catalyst/exchange/algorithm_exchange.py @@ -130,7 +130,6 @@ class ExchangeTradingAlgorithm(TradingAlgorithm): execution_closes = \ self.trading_calendar.execution_time_from_close(market_closes) - return ExchangeClock( self.sim_params.sessions, execution_opens, @@ -141,6 +140,12 @@ class ExchangeTradingAlgorithm(TradingAlgorithm): ) def _create_generator(self, sim_params): + if self.perf_tracker is None: + self.perf_tracker = get_algo_object( + algo_name=self.algo_namespace, + key='perf_tracker' + ) + # Call the simulation trading algorithm for side-effects: # it creates the perf tracker TradingAlgorithm._create_generator(self, sim_params) @@ -271,13 +276,6 @@ class ExchangeTradingAlgorithm(TradingAlgorithm): if not self.is_running: return - if self.minute_perfs is None: - perfs = get_algo_object( - algo_name=self.algo_namespace, - key='minute_perfs' - ) - self.minute_perfs = perfs if perfs is not None else [] - self._update_portfolio() transactions = self._check_open_orders() @@ -309,8 +307,8 @@ class ExchangeTradingAlgorithm(TradingAlgorithm): try: save_algo_object( algo_name=self.algo_namespace, - key='minute_perfs', - obj=self.minute_perfs + key='perf_tracker', + obj=self.perf_tracker ) except Exception as e: log.warn('unable to save minute perfs to disk: {}'.format(e)) @@ -318,7 +316,7 @@ class ExchangeTradingAlgorithm(TradingAlgorithm): try: save_algo_object( algo_name=self.algo_namespace, - key='portfolio', + key='portfolio_{}'.format(self.exchange.name), obj=self.exchange.portfolio ) except Exception as e: diff --git a/catalyst/exchange/exchange.py b/catalyst/exchange/exchange.py index 4f8c3640..649b45a3 100644 --- a/catalyst/exchange/exchange.py +++ b/catalyst/exchange/exchange.py @@ -268,6 +268,7 @@ class Exchange: sleep_time = random.uniform(0.5, 0.8) sleep(sleep_time) # TODO: This does not always! Why is that? Open an issue with zipline. + # See: https://github.com/zipline-live/zipline/issues/26 value = self.minute_reader.get_value( sid=asset.sid, dt=dt, @@ -281,13 +282,13 @@ class Exchange: if field not in ohlc: raise KeyError('Invalid column: %s' % field) - df = pd.DataFrame( - [ohlc], - index=pd.DatetimeIndex([dt]), - columns=['open', 'high', 'low', 'close', 'volume'] - ) - if self.minute_writer is not None: + df = pd.DataFrame( + [ohlc], + index=pd.DatetimeIndex([dt]), + columns=['open', 'high', 'low', 'close', 'volume'] + ) + try: self.minute_writer.write_sid( sid=asset.sid, diff --git a/catalyst/exchange/exchange_portfolio.py b/catalyst/exchange/exchange_portfolio.py index f0e076ce..ded8a2a4 100644 --- a/catalyst/exchange/exchange_portfolio.py +++ b/catalyst/exchange/exchange_portfolio.py @@ -7,6 +7,15 @@ log = Logger('ExchangePortfolio') class ExchangePortfolio(Portfolio): + """ + Since the goal is to support multiple exchanges, it makes sense to + include additional stats in the portfolio object. + + Instead of relying on the performance tracker, each exchange portfolio + tracks its own holding. This offers a separation between tracking an + exchange and the statistics of the algorithm. + """ + def __init__(self, start_date, starting_cash=None): self.capital_used = 0.0 self.starting_cash = starting_cash