From 7de1e7c99e78dfe2f4f19df852c2cd41b3f6e238 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 21 Jul 2017 03:59:38 -0700 Subject: [PATCH] Separated 5 minute and daily examples --- catalyst/algorithm.py | 28 ++-- catalyst/examples/buy_and_hodl.5min.py | 143 +++++++++++++++++++ catalyst/examples/buy_and_hodl.py | 21 +-- catalyst/examples/dual_vwap.5min.py | 189 +++++++++++++++++++++++++ catalyst/examples/dual_vwap.py | 2 +- 5 files changed, 360 insertions(+), 23 deletions(-) create mode 100644 catalyst/examples/buy_and_hodl.5min.py create mode 100644 catalyst/examples/dual_vwap.5min.py diff --git a/catalyst/algorithm.py b/catalyst/algorithm.py index 3aec52b6..bfb65983 100644 --- a/catalyst/algorithm.py +++ b/catalyst/algorithm.py @@ -1712,12 +1712,12 @@ class TradingAlgorithm(object): return dt @api_method - def set_slippage(self, us_equities=None, us_futures=None): + def set_slippage(self, equities=None, us_futures=None): """Set the slippage models for the simulation. Parameters ---------- - us_equities : EquitySlippageModel + equities : EquitySlippageModel The slippage model to use for trading US equities. us_futures : FutureSlippageModel The slippage model to use for trading US futures. @@ -1729,14 +1729,14 @@ class TradingAlgorithm(object): if self.initialized: raise SetSlippagePostInit() - if us_equities is not None: - if Equity not in us_equities.allowed_asset_types: + if equities is not None: + if Equity not in equities.allowed_asset_types: raise IncompatibleSlippageModel( asset_type='equities', - given_model=us_equities, - supported_asset_types=us_equities.allowed_asset_types, + given_model=equities, + supported_asset_types=equities.allowed_asset_types, ) - self.blotter.slippage_models[Equity] = us_equities + self.blotter.slippage_models[Equity] = equities if us_futures is not None: if Future not in us_futures.allowed_asset_types: @@ -1748,12 +1748,12 @@ class TradingAlgorithm(object): self.blotter.slippage_models[Future] = us_futures @api_method - def set_commission(self, us_equities=None, us_futures=None): + def set_commission(self, equities=None, us_futures=None): """Sets the commission models for the simulation. Parameters ---------- - us_equities : EquityCommissionModel + equities : EquityCommissionModel The commission model to use for trading US equities. us_futures : FutureCommissionModel The commission model to use for trading US futures. @@ -1767,14 +1767,14 @@ class TradingAlgorithm(object): if self.initialized: raise SetCommissionPostInit() - if us_equities is not None: - if Equity not in us_equities.allowed_asset_types: + if equities is not None: + if Equity not in equities.allowed_asset_types: raise IncompatibleCommissionModel( asset_type='equities', - given_model=us_equities, - supported_asset_types=us_equities.allowed_asset_types, + given_model=equities, + supported_asset_types=equities.allowed_asset_types, ) - self.blotter.commission_models[Equity] = us_equities + self.blotter.commission_models[Equity] = equities if us_futures is not None: if Future not in us_futures.allowed_asset_types: diff --git a/catalyst/examples/buy_and_hodl.5min.py b/catalyst/examples/buy_and_hodl.5min.py new file mode 100644 index 00000000..5e6fed5f --- /dev/null +++ b/catalyst/examples/buy_and_hodl.5min.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# +# Copyright 2017 Enigma MPC, Inc. +# Copyright 2015 Quantopian, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from catalyst.finance.slippage import VolumeShareSlippage + +from catalyst.api import ( + order_target_value, + symbol, + record, + cancel_order, + get_open_orders, + set_slippage, +) + + +def initialize(context): + context.ASSET_NAME = 'USDT_BTC' + context.TARGET_HODL_RATIO = 0.8 + context.RESERVE_RATIO = 1.0 - context.TARGET_HODL_RATIO + + # For all trading pairs in the poloniex bundle, the default denomination + # currently supported by Catalyst is 1/1000th of a full coin. Use this + # constant to scale the price of up to that of a full coin if desired. + context.TICK_SIZE = 1000.0 + + context.is_buying = True + context.asset = symbol(context.ASSET_NAME) + + context.i = 0 + + set_slippage(equities=VolumeShareSlippage(volume_limit=0.1)) + +def handle_data(context, data): + context.i += 1 + + starting_cash = context.portfolio.starting_cash + target_hodl_value = context.TARGET_HODL_RATIO * starting_cash + reserve_value = context.RESERVE_RATIO * starting_cash + + # Cancel any outstanding orders + orders = get_open_orders(context.asset) or [] + for order in orders: + cancel_order(order) + + # Stop buying after passing the reserve threshold + cash = context.portfolio.cash + if cash <= reserve_value: + context.is_buying = False + + # Retrieve current asset price from pricing data + price = data[context.asset].price + + # 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_target_value( + context.asset, + target_hodl_value, + limit_price=price*1.1, + stop_price=price*0.9, + ) + + record( + price=price, + volume=data[context.asset].volume, + cash=cash, + starting_cash=context.portfolio.starting_cash, + leverage=context.account.leverage, + ) + +def analyze(context=None, results=None): + import matplotlib.pyplot as plt + + # Plot the portfolio and asset data. + ax1 = plt.subplot(611) + results[['portfolio_value']].plot(ax=ax1) + ax1.set_ylabel('Portfolio Value (USD)') + + ax2 = plt.subplot(612, sharex=ax1) + ax2.set_ylabel('{asset} (USD)'.format(asset=context.ASSET_NAME)) + (context.TICK_SIZE * results[['price']]).plot(ax=ax2) + + trans = results.ix[[t != [] for t in results.transactions]] + buys = trans.ix[ + [t[0]['amount'] > 0 for t in trans.transactions] + ] + ax2.plot( + buys.index, + context.TICK_SIZE * results.price[buys.index], + '^', + markersize=10, + color='g', + ) + + ax3 = plt.subplot(613, sharex=ax1) + results[['leverage', 'alpha', 'beta']].plot(ax=ax3) + ax3.set_ylabel('Leverage ') + + ax4 = plt.subplot(614, sharex=ax1) + results[['starting_cash', 'cash']].plot(ax=ax4) + ax4.set_ylabel('Cash (USD)') + + results[[ + 'treasury', + 'algorithm', + 'benchmark', + ]] = results[[ + 'treasury_period_return', + 'algorithm_period_return', + 'benchmark_period_return', + ]] + + ax5 = plt.subplot(615, sharex=ax1) + results[[ + 'treasury', + 'algorithm', + 'benchmark', + ]].plot(ax=ax5) + ax5.set_ylabel('Percent Change') + + ax6 = plt.subplot(616, sharex=ax1) + results[['volume']].plot(ax=ax6) + ax6.set_ylabel('Volume (mCoins/5min)') + + plt.legend(loc=3) + + # Show the plot. + plt.gcf().set_size_inches(18, 8) + plt.show() diff --git a/catalyst/examples/buy_and_hodl.py b/catalyst/examples/buy_and_hodl.py index 57b7f19e..2b4ebbc0 100644 --- a/catalyst/examples/buy_and_hodl.py +++ b/catalyst/examples/buy_and_hodl.py @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from catalyst.finance.slippage import VolumeShareSlippage + from catalyst.api import ( order_target_value, symbol, @@ -23,7 +25,6 @@ from catalyst.api import ( get_open_orders, ) - def initialize(context): context.ASSET_NAME = 'USDT_BTC' context.TARGET_HODL_RATIO = 0.8 @@ -42,8 +43,6 @@ def initialize(context): def handle_data(context, data): context.i += 1 - print 'i:', context.i - starting_cash = context.portfolio.starting_cash target_hodl_value = context.TARGET_HODL_RATIO * starting_cash reserve_value = context.RESERVE_RATIO * starting_cash @@ -73,6 +72,7 @@ def handle_data(context, data): record( price=price, + volume=data[context.asset].volume, cash=cash, starting_cash=context.portfolio.starting_cash, leverage=context.account.leverage, @@ -80,12 +80,13 @@ def handle_data(context, data): def analyze(context=None, results=None): import matplotlib.pyplot as plt + # Plot the portfolio and asset data. - ax1 = plt.subplot(511) + ax1 = plt.subplot(611) results[['portfolio_value']].plot(ax=ax1) ax1.set_ylabel('Portfolio Value (USD)') - ax2 = plt.subplot(512, sharex=ax1) + ax2 = plt.subplot(612, sharex=ax1) ax2.set_ylabel('{asset} (USD)'.format(asset=context.ASSET_NAME)) (context.TICK_SIZE * results[['price']]).plot(ax=ax2) @@ -101,11 +102,11 @@ def analyze(context=None, results=None): color='g', ) - ax3 = plt.subplot(513, sharex=ax1) + ax3 = plt.subplot(613, sharex=ax1) results[['leverage', 'alpha', 'beta']].plot(ax=ax3) ax3.set_ylabel('Leverage ') - ax4 = plt.subplot(514, sharex=ax1) + ax4 = plt.subplot(614, sharex=ax1) results[['starting_cash', 'cash']].plot(ax=ax4) ax4.set_ylabel('Cash (USD)') @@ -119,7 +120,7 @@ def analyze(context=None, results=None): 'benchmark_period_return', ]] - ax5 = plt.subplot(515, sharex=ax1) + ax5 = plt.subplot(615, sharex=ax1) results[[ 'treasury', 'algorithm', @@ -127,6 +128,10 @@ def analyze(context=None, results=None): ]].plot(ax=ax5) ax5.set_ylabel('Percent Change') + ax6 = plt.subplot(616, sharex=ax1) + results[['volume']].plot(ax=ax6) + ax6.set_ylabel('Volume (mCoins/5min)') + plt.legend(loc=3) # Show the plot. diff --git a/catalyst/examples/dual_vwap.5min.py b/catalyst/examples/dual_vwap.5min.py new file mode 100644 index 00000000..bf90c493 --- /dev/null +++ b/catalyst/examples/dual_vwap.5min.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# +# Copyright 2017 Enigma MPC, Inc. +# Copyright 2014 Quantopian, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from catalyst.api import ( + order_target_percent, + record, + symbol, + get_open_orders, + set_max_leverage, + schedule_function, + date_rules, + time_rules, + attach_pipeline, + pipeline_output, +) + +from catalyst.pipeline import Pipeline +from catalyst.pipeline.data import CryptoPricing +from catalyst.pipeline.factors.crypto import VWAP + + +def initialize(context): + context.ASSET_NAME = 'USDT_BTC' + context.TARGET_INVESTMENT_RATIO = 0.8 + context.SHORT_WINDOW = 30 * 288 + context.LONG_WINDOW = 100 * 288 + + # For all trading pairs in the poloniex bundle, the default denomination + # currently supported by Catalyst is 1/1000th of a full coin. Use this + # constant to scale the price of up to that of a full coin if desired. + context.TICK_SIZE = 1000.0 + + context.i = 0 + context.asset = symbol(context.ASSET_NAME) + + set_max_leverage(1.0) + + attach_pipeline(make_pipeline(context), 'vwap_pipeline') + + schedule_function( + rebalance, + time_rule=time_rules.every_minute(), + ) + + +def before_trading_start(context, data): + context.pipeline_data = pipeline_output('vwap_pipeline') + +def make_pipeline(context): + return Pipeline( + columns={ + 'price': CryptoPricing.open.latest, + 'volume': CryptoPricing.volume.latest, + 'short_mavg': VWAP(window_length=context.SHORT_WINDOW), + 'long_mavg': VWAP(window_length=context.LONG_WINDOW), + } + ) + +def rebalance(context, data): + context.i += 1 + + # skip first LONG_WINDOW bars to fill windows + if context.i < context.LONG_WINDOW: + return + + # get pipeline data for asset of interest + pipeline_data = context.pipeline_data + pipeline_data = pipeline_data[pipeline_data.index == context.asset].iloc[0] + + # retrieve long and short moving averages from pipeline + short_mavg = pipeline_data.short_mavg + long_mavg = pipeline_data.long_mavg + price = pipeline_data.price + volume = pipeline_data.volume + + # check that order has not already been placed + open_orders = get_open_orders() + if context.asset not in open_orders: + # check that the asset of interest can currently be traded + if data.can_trade(context.asset): + # adjust portfolio based on comparison of long and short vwap + if short_mavg > long_mavg: + order_target_percent( + context.asset, + context.TARGET_INVESTMENT_RATIO, + ) + elif short_mavg < long_mavg: + order_target_percent( + context.asset, + 0.0, + ) + + record( + price=price, + cash=context.portfolio.cash, + leverage=context.account.leverage, + short_mavg=short_mavg, + long_mavg=long_mavg, + volume=volume, + ) + + + +def analyze(context=None, results=None): + import matplotlib.pyplot as plt + + # Plot the portfolio and asset data. + ax1 = plt.subplot(611) + results[['portfolio_value']].plot(ax=ax1) + ax1.set_ylabel('Portfolio value (USD)') + + ax2 = plt.subplot(612, sharex=ax1) + ax2.set_ylabel('{asset} (USD)'.format(asset=context.ASSET_NAME)) + (context.TICK_SIZE*results[['price', 'short_mavg', 'long_mavg']]).plot(ax=ax2) + + trans = results.ix[[t != [] for t in results.transactions]] + amounts = [t[0]['amount'] for t in trans.transactions] + + buys = trans.ix[ + [t[0]['amount'] > 0 for t in trans.transactions] + ] + sells = trans.ix[ + [t[0]['amount'] < 0 for t in trans.transactions] + ] + + ax2.plot( + buys.index, + context.TICK_SIZE * results.price[buys.index], + '^', + markersize=10, + color='g', + ) + ax2.plot( + sells.index, + context.TICK_SIZE * results.price[sells.index], + 'v', + markersize=10, + color='r', + ) + + ax3 = plt.subplot(613, sharex=ax1) + results[['leverage', 'alpha', 'beta']].plot(ax=ax3) + ax3.set_ylabel('Leverage (USD)') + + ax4 = plt.subplot(614, sharex=ax1) + results[['cash']].plot(ax=ax4) + ax4.set_ylabel('Cash (USD)') + + results[[ + 'treasury', + 'algorithm', + 'benchmark', + ]] = results[[ + 'treasury_period_return', + 'algorithm_period_return', + 'benchmark_period_return', + ]] + + ax5 = plt.subplot(615, sharex=ax1) + results[[ + 'treasury', + 'algorithm', + 'benchmark', + ]].plot(ax=ax5) + ax5.set_ylabel('Percent Change') + + ax6 = plt.subplot(616, sharex=ax1) + results[['volume']].plot(ax=ax6) + ax6.set_ylabel('Volume (mBTC/day)') + + plt.legend(loc=3) + + # Show the plot. + plt.gcf().set_size_inches(18, 8) + plt.show() diff --git a/catalyst/examples/dual_vwap.py b/catalyst/examples/dual_vwap.py index 52c1789d..18a32584 100644 --- a/catalyst/examples/dual_vwap.py +++ b/catalyst/examples/dual_vwap.py @@ -52,7 +52,7 @@ def initialize(context): schedule_function( rebalance, - time_rules=times_rules.every_minute(), + date_rule=date_rules.every_day(), )