From 37a07e03e533c96f3f7c2738017d73b151654cb9 Mon Sep 17 00:00:00 2001 From: Frederic Fortier Date: Fri, 18 Aug 2017 02:03:00 -0400 Subject: [PATCH] Minor fixes --- catalyst/examples/buy_and_hold_live.py | 6 +-- catalyst/exchange/bitfinex.py | 30 +++++++---- catalyst/exchange/exchange_clock.py | 2 +- docs/source/live-trading-blueprint.md | 72 ++++++++++++++------------ 4 files changed, 61 insertions(+), 49 deletions(-) diff --git a/catalyst/examples/buy_and_hold_live.py b/catalyst/examples/buy_and_hold_live.py index 25c57533..985c06fa 100644 --- a/catalyst/examples/buy_and_hold_live.py +++ b/catalyst/examples/buy_and_hold_live.py @@ -18,7 +18,7 @@ log = Logger('buy_and_hold_live') def initialize(context): log.info('initializing algo') - context.asset = symbol('eos_usd') + context.asset = symbol('eos_btc') context.TARGET_HODL_RATIO = 0.8 context.RESERVE_RATIO = 1.0 - context.TARGET_HODL_RATIO @@ -53,7 +53,7 @@ def handle_data(context, data): # Check if still buying and could (approximately) afford another purchase if context.is_buying and cash > price: # Place order to make position in asset equal to target_hodl_value - order(context.asset, 1, limit_price=price + 1.1) + order(context.asset, 1, limit_price=price * 1.1) # This works # order_target_value( # context.asset, @@ -79,7 +79,7 @@ exchange_conn = dict( name='bitfinex', key='', secret=b'', - base_currency='usd' + base_currency='btc' ) run_algorithm( initialize=initialize, diff --git a/catalyst/exchange/bitfinex.py b/catalyst/exchange/bitfinex.py index e317a52d..8dbb1ca8 100644 --- a/catalyst/exchange/bitfinex.py +++ b/catalyst/exchange/bitfinex.py @@ -193,7 +193,8 @@ class Bitfinex(Exchange): portfolio.cash = float(base_position['available']) if portfolio.positions: - tickers = self.tickers(portfolio.positions.keys()) + assets = portfolio.positions.keys() + tickers = self.tickers(assets) portfolio.positions_value = 0.0 for ticker in tickers: # TODO: convert if the position is not in the base currency @@ -209,8 +210,9 @@ class Bitfinex(Exchange): @property def portfolio(self): """ - TODO: I'm not sure how that's used yet - :return: + Return the Portfolio + + :return: """ if self.store.portfolio is None: portfolio = ExchangePortfolio( @@ -402,18 +404,16 @@ class Bitfinex(Exchange): :func:`catalyst.api.order_value` :func:`catalyst.api.order_percent` """ - log.debug( - 'ordering {amount} {symbol} {style}'.format( - amount=amount, - symbol=asset.symbol, - style=style - ) - ) - if amount == 0: log.warn('skipping order amount of 0') return None + base_currency = asset.symbol.split('_')[1] + if base_currency.lower() != self.base_currency.lower(): + raise NotImplementedError( + 'Currency pairs must share their base with the exchange.' + ) + is_buy = (amount > 0) if isinstance(style, MarketOrder): @@ -432,6 +432,14 @@ class Bitfinex(Exchange): else: raise NotImplementedError('%s orders not available' % style) + log.debug( + 'ordering {amount} {symbol} for {price}'.format( + amount=amount, + symbol=asset.symbol, + price=price + ) + ) + exchange_symbol = self.get_symbol(asset) req = dict( symbol=exchange_symbol, diff --git a/catalyst/exchange/exchange_clock.py b/catalyst/exchange/exchange_clock.py index f3866ce8..4698297c 100644 --- a/catalyst/exchange/exchange_clock.py +++ b/catalyst/exchange/exchange_clock.py @@ -64,8 +64,8 @@ class ExchangeClock(object): server_time = current_time.floor('1 min') if self._last_emit is None or server_time > self._last_emit: + log.debug('emitting minutely bar: {}'.format(server_time)) - print 'emitting bar %s' % server_time self._last_emit = server_time yield server_time, BAR diff --git a/docs/source/live-trading-blueprint.md b/docs/source/live-trading-blueprint.md index 327986e0..d26638f2 100644 --- a/docs/source/live-trading-blueprint.md +++ b/docs/source/live-trading-blueprint.md @@ -3,36 +3,33 @@ The purpose of this document is to allow project contributors navigate through the ongoing live trading implementation.

Components

-At a high level the following components have been implemented to coerce +At a high level, the following components have been implemented to coerce zipline into live trading.

Exchange

*catalyst/exchange* -Exchange is a new package which introduces the concept of cryptocurrency -exchanges to zipline. The package contains all new component -implementations adapted to characteristics of exchanges. +Exchange is a new package introducing cryptocurrency +exchanges to zipline. The package contains mostly new implementations +of existing components, adapted to characteristics of exchanges. -Here are some key characteristics which makes exchanges different from -equity and futures currently implemented in zipline. +Here are some key characteristics which make cryptocurrency exchanges +exchanges different compared to equity brokers. * They trade around the clock. * Currency symbols are inconsistent across exchanges. -* They trade currency pairs, i.e. the base currency is not always be USD. -This is a significant departure from the equity market. Additional -business logic will be required both to assess performance and -manage trades. -* The cryptocurrency market being relatively immature, there are still -significant price arbitrage opportunities between exchanges. -In contrast with the equity markets, trader usually trade directly -against an exchange (as oppose to using a broker). Consequently, -to extract maximum alpha, the platform should not only support -multiple exchanges, but also multiple exchanges per algorithm. +* They trade currency pairs (i.e. the base currency is not always be USD). +This is a paradigm shift in context of zipline. Additional +business logic will be required to manage the portfolio data and orders. +* The price of a single asset might vary across exchanges. This means +arbitrage opportunities. Consequently, to extract maximum alpha, the +platform should not only support multiple exchanges, but also multiple +exchanges per algorithm. * The fee model is usually more complex than that of an equity broker. It can vary drastically between exchanges. -* There are no splits, mergers, etc to worry about. -* Their order book is publicly available, the platform should access to -it as it can be used to drastically reduce slippage. +* There are no splits, mergers, etc. to worry about. +* A complete order book is usually available, the platform should +offer access to it order to help traders reduce slippage.

New Components

These components of the exchange package were added to the zipline @@ -42,7 +39,7 @@ sources. *catalyst/exchange/exchange.py* -Abstract class which acts an interface for the implementation of +Abstract class which acts as an interface for the implementation of various exchanges. It also contains logic common to all exchanges.

Bitfinex

@@ -59,14 +56,20 @@ Extends the zipline DataPortal to route spot data to the exchange. This is critical because it allows the algoritm to request data in real-time. -For example, `data.current(asset, 'price')` retrieves the current price of the asset, not the price at the time -of yielding the bar this is critical to minimize slippage. +For example, `data.current(asset, 'price')` retrieves the current price +of the asset, not the price at the time of yielding the bar this +is critical to minimize slippage. + +At the time of writing, it only supports spot data but I believe that +it should be extended to historical data as well. Some exchanges +have better historical data APIs than others. This will need to +be considered during each individual implementation.

ExchangeClock

*catalyst/exchange/exchange_clock.py* -An implementation to the zipline Clock which runs 24/7. It yeilds a +An implementation to the zipline Clock which runs 24/7. It yields a bar every minute.

AssetFinderExchange

@@ -76,15 +79,14 @@ bar every minute. An alternate implementation of AssetFinder which locates each asset against the exchanges instead of bundle databases. -For example, `symbol('eth_usd')` retrieves an Asset object against the exchange as opposed to querying -a database of equities. +For example, `symbol('eth_usd')` should return an Ethereum/USD asset +regardless of currency notation of the target exchange. -I have created a dictionary of currencies for the Bitfinex exchange. -The primary goal is to standardize the symbol notation across exchanges. -Here is a snippet of the file. +To acheive this, I have created a dictionary of currencies for the +Bitfinex exchange. Here is what it looks like. * Each key represents the exchange specific symbol. -* The symbol attribute represents the standard symbol which -should be common across exchanges for the given currency. +* The symbol attribute represents the abstract symbol common across +all exchanges for the given currency. * The start_date attribute should correspond to its first trading day on the exchange. @@ -132,13 +134,15 @@ business logic to enable live trading. The run_algorithm interface is an entry point to execute an algorithm in zipline. This component was already modified for -the catalyst concurrency bundles. I added conditional logic to -which should not break any of the existing backtesting implementations. +the catalyst concurrency bundles. I added conditional logic +which should not interfere with backtesting. -At a high-level the run_algorithm method now contains two additional +In a nutshell, the run_algorithm method now contains three additional parameters: * live: If True, zipline will attempt to trade live. If False or not specified, it will run a backtest as normal. +* algo_namespace: An arbitrary namespace for the current algorithm. +It will be used to persist data between runs. * exchange_conn: A dictionary containing the attributes required to instantiate an exchange. Here is an example for Bitfinex: @@ -189,7 +193,7 @@ cost basis would correspond to all positions, not just those initiated by the algorithm. * It would not be possible impose trading limits on algorithms. -It follow that Portfolio metrics should be calculated using a strategic +It follows that Portfolio metrics should be calculated using a strategic combination of the exchange data and algorithm activity. While tracking the activity of an algorithm works well in backtesting, it is more challenging during live trading. A live algorithm might run over