Compare commits

...
7 Commits
13 changed files with 450 additions and 63 deletions
+30 -28
View File
@@ -444,9 +444,6 @@ class TradingAlgorithm(object):
'data frequency: {}'.format(data_frequency) 'data frequency: {}'.format(data_frequency)
) )
print 'first_dates:', all_dates[:10]
print 'last_dates:', all_dates[:-10]
self.engine = SimplePipelineEngine( self.engine = SimplePipelineEngine(
get_loader, get_loader,
all_dates, all_dates,
@@ -745,14 +742,15 @@ class TradingAlgorithm(object):
for perf in self.get_generator(): for perf in self.get_generator():
perfs.append(perf) perfs.append(perf)
# convert perf dict to pandas dataframe
daily_stats = self._create_daily_stats(perfs)
self.analyze(daily_stats) # convert perf dict to pandas dataframe
stats = self._create_daily_stats(perfs)
self.analyze(stats)
finally: finally:
self.data_portal = None self.data_portal = None
return daily_stats return stats
def _write_and_map_id_index_to_sids(self, identifiers, as_of_date): def _write_and_map_id_index_to_sids(self, identifiers, as_of_date):
# Build new Assets for identifiers that can't be resolved as # Build new Assets for identifiers that can't be resolved as
@@ -1143,14 +1141,12 @@ class TradingAlgorithm(object):
date_rule = date_rule or date_rules.every_day() date_rule = date_rule or date_rules.every_day()
if freq is 'daily': if freq is 'daily':
# ignore time rule in daily mode # Ignore any time rules in daily mode.
# every_minute in daily mode does nothing.
time_rule = time_rules.every_minute() time_rule = time_rules.every_minute()
else: else:
# use provided time rule or default to every minute or 5 minutes # use provided time rule or default to every minute
# based on desired data frequency. time_rule = time_rule or time_rules.every_minute()
time_rule = time_rule or (time_rules.every_5_minutes()
if freq is '5-minute' else
time_rules.every_minute())
# Check the type of the algorithm's schedule before pulling calendar # Check the type of the algorithm's schedule before pulling calendar
# Note that the ExchangeTradingSchedule is currently the only # Note that the ExchangeTradingSchedule is currently the only
@@ -1174,7 +1170,13 @@ class TradingAlgorithm(object):
) )
self.add_event( self.add_event(
make_eventrule(date_rule, time_rule, cal, half_days), make_eventrule(
date_rule,
time_rule,
cal,
half_days=half_days,
data_frequency=self.data_frequency,
),
func, func,
) )
@@ -1706,12 +1708,12 @@ class TradingAlgorithm(object):
return dt return dt
@api_method @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. """Set the slippage models for the simulation.
Parameters Parameters
---------- ----------
us_equities : EquitySlippageModel equities : EquitySlippageModel
The slippage model to use for trading US equities. The slippage model to use for trading US equities.
us_futures : FutureSlippageModel us_futures : FutureSlippageModel
The slippage model to use for trading US futures. The slippage model to use for trading US futures.
@@ -1723,14 +1725,14 @@ class TradingAlgorithm(object):
if self.initialized: if self.initialized:
raise SetSlippagePostInit() raise SetSlippagePostInit()
if us_equities is not None: if equities is not None:
if Equity not in us_equities.allowed_asset_types: if Equity not in equities.allowed_asset_types:
raise IncompatibleSlippageModel( raise IncompatibleSlippageModel(
asset_type='equities', asset_type='equities',
given_model=us_equities, given_model=equities,
supported_asset_types=us_equities.allowed_asset_types, 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 us_futures is not None:
if Future not in us_futures.allowed_asset_types: if Future not in us_futures.allowed_asset_types:
@@ -1742,12 +1744,12 @@ class TradingAlgorithm(object):
self.blotter.slippage_models[Future] = us_futures self.blotter.slippage_models[Future] = us_futures
@api_method @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. """Sets the commission models for the simulation.
Parameters Parameters
---------- ----------
us_equities : EquityCommissionModel equities : EquityCommissionModel
The commission model to use for trading US equities. The commission model to use for trading US equities.
us_futures : FutureCommissionModel us_futures : FutureCommissionModel
The commission model to use for trading US futures. The commission model to use for trading US futures.
@@ -1761,14 +1763,14 @@ class TradingAlgorithm(object):
if self.initialized: if self.initialized:
raise SetCommissionPostInit() raise SetCommissionPostInit()
if us_equities is not None: if equities is not None:
if Equity not in us_equities.allowed_asset_types: if Equity not in equities.allowed_asset_types:
raise IncompatibleCommissionModel( raise IncompatibleCommissionModel(
asset_type='equities', asset_type='equities',
given_model=us_equities, given_model=equities,
supported_asset_types=us_equities.allowed_asset_types, 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 us_futures is not None:
if Future not in us_futures.allowed_asset_types: if Future not in us_futures.allowed_asset_types:
+7 -3
View File
@@ -44,7 +44,7 @@ def five_minute_value(ndarray[long_t, ndim=1] market_opens,
q = cython.cdiv(pos, five_minutes_per_day) q = cython.cdiv(pos, five_minutes_per_day)
r = cython.cmod(pos, five_minutes_per_day) r = cython.cmod(pos, five_minutes_per_day)
return market_opens[q] + r return market_opens[q] + 5 * r
def find_position_of_minute(ndarray[long_t, ndim=1] market_opens, def find_position_of_minute(ndarray[long_t, ndim=1] market_opens,
ndarray[long_t, ndim=1] market_closes, ndarray[long_t, ndim=1] market_closes,
@@ -112,10 +112,14 @@ def find_position_of_five_minute(ndarray[long_t, ndim=1] market_opens,
market_open = market_opens[market_open_loc] market_open = market_opens[market_open_loc]
market_close = market_closes[market_open_loc] market_close = market_closes[market_open_loc]
if not forward_fill and ((five_minute_val - market_open) >= five_minutes_per_day): val_open_offset = (five_minute_val - market_open)/5
close_open_offset = (market_close - market_open)/5
if not forward_fill and val_open_offset >= five_minutes_per_day:
raise ValueError("Given five minutes is not between an open and a close") raise ValueError("Given five minutes is not between an open and a close")
delta = int_min(five_minute_val - market_open, market_close - market_open) # clamp offset to close index
delta = int_min(val_open_offset, close_open_offset)
return (market_open_loc * five_minutes_per_day) + delta return (market_open_loc * five_minutes_per_day) + delta
+2 -2
View File
@@ -90,13 +90,13 @@ def cache_relative(bundle_name, timestr, environ=None):
def daily_relative(bundle_name, timestr, environ=None): def daily_relative(bundle_name, timestr, environ=None):
return bundle_name, timestr, 'daily.bcolz' return bundle_name, timestr, 'daily_equities.bcolz'
def five_minute_relative(bundle_name, timestr, environ=None): def five_minute_relative(bundle_name, timestr, environ=None):
return bundle_name, timestr, 'five_minute.bcolz' return bundle_name, timestr, 'five_minute.bcolz'
def minute_relative(bundle_name, timestr, environ=None): def minute_relative(bundle_name, timestr, environ=None):
return bundle_name, timestr, 'minute.bcolz' return bundle_name, timestr, 'minute_equities.bcolz'
def asset_db_relative(bundle_name, timestr, environ=None, db_version=None): def asset_db_relative(bundle_name, timestr, environ=None, db_version=None):
+1 -1
View File
@@ -289,7 +289,7 @@ class DataPortal(object):
self._daily_aggregator = DailyHistoryAggregator( self._daily_aggregator = DailyHistoryAggregator(
self.trading_calendar.schedule.market_open, self.trading_calendar.schedule.market_open,
_dispatch_minute_reader, _dispatch_session_reader,
self.trading_calendar self.trading_calendar
) )
self._history_loader = DailyHistoryLoader( self._history_loader = DailyHistoryLoader(
+15 -4
View File
@@ -60,7 +60,7 @@ OPEN_FIVE_MINUTES_PER_DAY = 288
DEFAULT_EXPECTEDLEN_CRYPTO = OPEN_FIVE_MINUTES_PER_DAY * 366 * 15 DEFAULT_EXPECTEDLEN_CRYPTO = OPEN_FIVE_MINUTES_PER_DAY * 366 * 15
OHLC_RATIO = 1000000 OHLC_RATIO = 1000
OHLC = frozenset(['open', 'high', 'low', 'close']) OHLC = frozenset(['open', 'high', 'low', 'close'])
OHLCV = frozenset(['open', 'high', 'low', 'close', 'volume']) OHLCV = frozenset(['open', 'high', 'low', 'close', 'volume'])
@@ -1151,6 +1151,7 @@ class BcolzFiveMinuteBarReader(FiveMinuteBarReader):
if field != 'volume': if field != 'volume':
value *= self._ohlc_ratio_inverse_for_sid(sid) value *= self._ohlc_ratio_inverse_for_sid(sid)
#print 'minute pos: {}, {}: {}'.format(minute_pos, field, value)
return value return value
def get_last_traded_dt(self, asset, dt): def get_last_traded_dt(self, asset, dt):
@@ -1161,8 +1162,8 @@ class BcolzFiveMinuteBarReader(FiveMinuteBarReader):
def _find_last_traded_five_minute_position(self, asset, dt): def _find_last_traded_five_minute_position(self, asset, dt):
volumes = self._open_minute_file('volume', asset) volumes = self._open_minute_file('volume', asset)
start_date_minute = asset.start_date.value / NANOS_IN_FIVE_MINUTE start_date_minute = asset.start_date.value / NANOS_IN_MINUTE
dt_minute = dt.value / NANOS_IN_FIVE_MINUTE dt_minute = dt.value / NANOS_IN_MINUTE
try: try:
# if we know of a dt before which this asset has no volume, # if we know of a dt before which this asset has no volume,
@@ -1227,7 +1228,7 @@ class BcolzFiveMinuteBarReader(FiveMinuteBarReader):
return find_position_of_five_minute( return find_position_of_five_minute(
self._market_open_values, self._market_open_values,
self._market_close_values, self._market_close_values,
minute_dt.value / NANOS_IN_FIVE_MINUTE, minute_dt.value / NANOS_IN_MINUTE,
self._five_minutes_per_day, self._five_minutes_per_day,
False, False,
) )
@@ -1252,11 +1253,19 @@ class BcolzFiveMinuteBarReader(FiveMinuteBarReader):
(minutes in range, sids) with a dtype of float64, containing the (minutes in range, sids) with a dtype of float64, containing the
values for the respective field over start and end dt range. values for the respective field over start and end dt range.
""" """
print 'start_dt:', start_dt
print 'end_dt:', end_dt
start_idx = self._find_position_of_five_minute(start_dt) start_idx = self._find_position_of_five_minute(start_dt)
end_idx = self._find_position_of_five_minute(end_dt) end_idx = self._find_position_of_five_minute(end_dt)
print 'start_idx:', start_idx
print 'end_idex:', end_idx
num_minutes = (end_idx - start_idx + 1) num_minutes = (end_idx - start_idx + 1)
print 'num_minutes:', num_minutes
results = [] results = []
indices_to_exclude = self._exclusion_indices_for_range( indices_to_exclude = self._exclusion_indices_for_range(
@@ -1293,6 +1302,8 @@ class BcolzFiveMinuteBarReader(FiveMinuteBarReader):
out[:len(where), i][where] = values[where] out[:len(where), i][where] = values[where]
results.append(out) results.append(out)
print 'results:', results
return results return results
+1 -1
View File
@@ -763,7 +763,7 @@ class BcolzDailyBarReader(SessionBarReader):
if price == 0: if price == 0:
return nan return nan
else: else:
return price * 0.001 return price * 0.000001
else: else:
return price return price
+143
View File
@@ -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']] / context.TICK_SIZE).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()
+13 -8
View File
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from catalyst.finance.slippage import VolumeShareSlippage
from catalyst.api import ( from catalyst.api import (
order_target_value, order_target_value,
symbol, symbol,
@@ -23,7 +25,6 @@ from catalyst.api import (
get_open_orders, get_open_orders,
) )
def initialize(context): def initialize(context):
context.ASSET_NAME = 'USDT_BTC' context.ASSET_NAME = 'USDT_BTC'
context.TARGET_HODL_RATIO = 0.8 context.TARGET_HODL_RATIO = 0.8
@@ -42,8 +43,6 @@ def initialize(context):
def handle_data(context, data): def handle_data(context, data):
context.i += 1 context.i += 1
print 'i:', context.i
starting_cash = context.portfolio.starting_cash starting_cash = context.portfolio.starting_cash
target_hodl_value = context.TARGET_HODL_RATIO * starting_cash target_hodl_value = context.TARGET_HODL_RATIO * starting_cash
reserve_value = context.RESERVE_RATIO * starting_cash reserve_value = context.RESERVE_RATIO * starting_cash
@@ -73,6 +72,7 @@ def handle_data(context, data):
record( record(
price=price, price=price,
volume=data[context.asset].volume,
cash=cash, cash=cash,
starting_cash=context.portfolio.starting_cash, starting_cash=context.portfolio.starting_cash,
leverage=context.account.leverage, leverage=context.account.leverage,
@@ -80,12 +80,13 @@ def handle_data(context, data):
def analyze(context=None, results=None): def analyze(context=None, results=None):
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
# Plot the portfolio and asset data. # Plot the portfolio and asset data.
ax1 = plt.subplot(511) ax1 = plt.subplot(611)
results[['portfolio_value']].plot(ax=ax1) results[['portfolio_value']].plot(ax=ax1)
ax1.set_ylabel('Portfolio Value (USD)') 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)) ax2.set_ylabel('{asset} (USD)'.format(asset=context.ASSET_NAME))
(context.TICK_SIZE * results[['price']]).plot(ax=ax2) (context.TICK_SIZE * results[['price']]).plot(ax=ax2)
@@ -101,11 +102,11 @@ def analyze(context=None, results=None):
color='g', color='g',
) )
ax3 = plt.subplot(513, sharex=ax1) ax3 = plt.subplot(613, sharex=ax1)
results[['leverage', 'alpha', 'beta']].plot(ax=ax3) results[['leverage', 'alpha', 'beta']].plot(ax=ax3)
ax3.set_ylabel('Leverage ') ax3.set_ylabel('Leverage ')
ax4 = plt.subplot(514, sharex=ax1) ax4 = plt.subplot(614, sharex=ax1)
results[['starting_cash', 'cash']].plot(ax=ax4) results[['starting_cash', 'cash']].plot(ax=ax4)
ax4.set_ylabel('Cash (USD)') ax4.set_ylabel('Cash (USD)')
@@ -119,7 +120,7 @@ def analyze(context=None, results=None):
'benchmark_period_return', 'benchmark_period_return',
]] ]]
ax5 = plt.subplot(515, sharex=ax1) ax5 = plt.subplot(615, sharex=ax1)
results[[ results[[
'treasury', 'treasury',
'algorithm', 'algorithm',
@@ -127,6 +128,10 @@ def analyze(context=None, results=None):
]].plot(ax=ax5) ]].plot(ax=ax5)
ax5.set_ylabel('Percent Change') ax5.set_ylabel('Percent Change')
ax6 = plt.subplot(616, sharex=ax1)
(results[['volume']] / context.TICK_SIZE).plot(ax=ax6)
ax6.set_ylabel('Volume (mCoins/5min)')
plt.legend(loc=3) plt.legend(loc=3)
# Show the plot. # Show the plot.
+189
View File
@@ -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']] / context.TICK_SIZE).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()
+2 -2
View File
@@ -52,7 +52,7 @@ def initialize(context):
schedule_function( schedule_function(
rebalance, rebalance,
time_rules=times_rules.every_minute(), date_rule=date_rules.every_day(),
) )
@@ -178,7 +178,7 @@ def analyze(context=None, results=None):
ax5.set_ylabel('Percent Change') ax5.set_ylabel('Percent Change')
ax6 = plt.subplot(616, sharex=ax1) ax6 = plt.subplot(616, sharex=ax1)
results[['volume']].plot(ax=ax6) (results[['volume']] / context.TICK_SIZE).plot(ax=ax6)
ax6.set_ylabel('Volume (mBTC/day)') ax6.set_ylabel('Volume (mBTC/day)')
plt.legend(loc=3) plt.legend(loc=3)
+4 -2
View File
@@ -189,14 +189,14 @@ class PerformanceTracker(object):
@property @property
def progress(self): def progress(self):
if self.emission_rate == 'minute': if self.emission_rate in set(('minute', '5-minute')):
# Fake a value # Fake a value
return 1.0 return 1.0
elif self.emission_rate == 'daily': elif self.emission_rate == 'daily':
return self.session_count / self.total_session_count return self.session_count / self.total_session_count
def set_date(self, date): def set_date(self, date):
if self.emission_rate == 'minute': if self.emission_rate in set(('minute', '5-minute')):
self.saved_dt = date self.saved_dt = date
self.todays_performance.period_close = self.saved_dt self.todays_performance.period_close = self.saved_dt
@@ -370,7 +370,9 @@ class PerformanceTracker(object):
bench_since_open, bench_since_open,
account.leverage) account.leverage)
assert self.emission_rate in set(('minute', '5-minute'))
minute_packet = self.to_dict(emission_type='minute') minute_packet = self.to_dict(emission_type='minute')
return minute_packet return minute_packet
def handle_market_close(self, dt, data_portal): def handle_market_close(self, dt, data_portal):
@@ -57,6 +57,7 @@ class CryptoPricingLoader(PipelineLoader):
self.raw_price_loader = reader self.raw_price_loader = reader
self._columns = dataset.columns self._columns = dataset.columns
self._all_sessions = all_sessions self._all_sessions = all_sessions
self._data_frequency = data_frequency
@classmethod @classmethod
def from_files(cls, pricing_path): def from_files(cls, pricing_path):
@@ -106,13 +107,6 @@ class CryptoPricingLoader(PipelineLoader):
def _shift_dates(dates, start_date, end_date, shift): def _shift_dates(dates, start_date, end_date, shift):
print 'dates.head:\n', dates[:10]
print 'dates.tail:\n', dates[:-10]
print 'start_date:', start_date
print 'end_date:', end_date
print 'shift:', shift
try: try:
start = dates.get_loc(start_date) start = dates.get_loc(start_date)
except KeyError: except KeyError:
+43 -6
View File
@@ -47,6 +47,8 @@ __all__ = [
'NDaysBeforeLastTradingDayOfMonth', 'NDaysBeforeLastTradingDayOfMonth',
'StatefulRule', 'StatefulRule',
'OncePerDay', 'OncePerDay',
'OncePerFiveMinutes',
'OncePerMinute',
# Factory API # Factory API
'date_rules', 'date_rules',
@@ -552,15 +554,18 @@ class StatefulRule(EventRule):
""" """
self.should_trigger = callable_ self.should_trigger = callable_
class OncePerInterval(StatefulRule):
class OncePerDay(StatefulRule):
def __init__(self, rule=None): def __init__(self, rule=None):
self.triggered = False self.triggered = False
self.date = None self.date = None
self.next_date = None self.next_date = None
super(OncePerDay, self).__init__(rule) super(OncePerInterval, self).__init__(rule)
@lazyval
def interval(self):
raise NotImplementedError
def should_trigger(self, dt): def should_trigger(self, dt):
if self.date is None or dt >= self.next_date: if self.date is None or dt >= self.next_date:
@@ -570,13 +575,30 @@ class OncePerDay(StatefulRule):
# record the timestamp for the next day, so that we can use it # record the timestamp for the next day, so that we can use it
# to know if we've moved to the next day # to know if we've moved to the next day
self.next_date = dt + pd.Timedelta(1, unit="d") self.next_date = dt + self.interval
if not self.triggered and self.rule.should_trigger(dt): if not self.triggered and self.rule.should_trigger(dt):
self.triggered = True self.triggered = True
return True return True
class OncePerDay(OncePerInterval):
@lazyval
def interval(self):
return pd.Timedelta(1, unit='d')
class OncePerFiveMinutes(OncePerInterval):
@lazyval
def interval(self):
return pd.Timedelta(5, unit='m')
class OncePerMinute(OncePerInterval):
@lazyval
def interval(self):
return pd.Timedelta(1, unit='m')
# Factory API # Factory API
class date_rules(object): class date_rules(object):
@@ -612,7 +634,11 @@ class calendars(object):
US_FUTURES = sentinel('US_FUTURES') US_FUTURES = sentinel('US_FUTURES')
def make_eventrule(date_rule, time_rule, cal, half_days=True): def make_eventrule(date_rule,
time_rule,
cal,
half_days=True,
data_frequency=None):
""" """
Constructs an event rule from the factory api. Constructs an event rule from the factory api.
""" """
@@ -628,4 +654,15 @@ def make_eventrule(date_rule, time_rule, cal, half_days=True):
nhd_rule.cal = cal nhd_rule.cal = cal
inner_rule = date_rule & time_rule & nhd_rule inner_rule = date_rule & time_rule & nhd_rule
return OncePerDay(rule=inner_rule) if data_frequency == 'daily':
return OncePerDay(rule=inner_rule)
elif data_frequency == '5-minute':
return OncePerFiveMinutes(rule=inner_rule)
elif data_frequency == 'minute':
return OncePerMinute(rule=inner_rule)
else:
raise ValueError(
'Cannot make event rule for data frequency: {}'.format(
data_frequency,
)
)