Compare commits

...
19 Commits
Author SHA1 Message Date
Frederic Fortier 49b6792399 Merge branch 'develop' 2018-02-09 12:01:57 -05:00
Frederic Fortier 6b1982274e DOC: updated release notes for 0.5.3 2018-02-09 02:44:45 -05:00
Frederic Fortier bc58640a3a Merge remote-tracking branch 'origin/develop' into develop 2018-02-09 02:41:08 -05:00
Frederic Fortier 511af7946e BUG: for issue #219, created another unit test which compares current price against last candle 2018-02-09 02:40:53 -05:00
Victor Grau Serrat dcdb3d9d7a MAINT: removing many warnings from building docs 2018-02-09 00:07:38 -07:00
Frederic Fortier 5c77cfc68d BUG: for issue #219, fixed a resampling issue 2018-02-09 01:38:22 -05:00
Frederic Fortier 11f8e36ff0 Merge remote-tracking branch 'origin/develop' into develop 2018-02-09 00:56:45 -05:00
Frederic Fortier 44e4e66f5c BLD: adding more log messages 2018-02-09 00:56:35 -05:00
Victor Grau Serrat 3c4c6c3dfd DOC: small edits, eliminating sphinx warnings 2018-02-08 22:10:57 -07:00
Victor Grau Serrat bebebc46dc DOC: small edits, eliminating sphinx warnings 2018-02-08 22:07:58 -07:00
Frederic Fortier 403d7f9c29 Merge branch 'develop' 2018-02-08 17:32:47 -05:00
Frederic Fortier 09ad397ee6 DOC: adjusted the release notes 2018-02-08 17:31:09 -05:00
Frederic Fortier e56e1f8e21 BUG: fixed an issue with open orders 2018-02-08 17:26:48 -05:00
Frederic Fortier 00f232e2d7 BUG: fixed sample algo 2018-02-08 17:10:41 -05:00
Frederic Fortier a820f66bdc BLD: adjusted unit test 2018-02-08 16:57:23 -05:00
Frederic Fortier 82d318a1c0 BUG: fixed issue #216 with bad candle data 2018-02-08 15:51:55 -05:00
Frederic Fortier 97afbf781e Merge branch 'master' into develop 2018-02-08 01:13:29 -05:00
Frederic Fortier 43a5f8858b DOC: updated release number 2018-02-08 01:11:09 -05:00
Frederic Fortier dc04cd8781 Merge branch 'master' into develop 2018-02-08 00:59:46 -05:00
25 changed files with 817 additions and 298 deletions
+1 -3
View File
@@ -17,9 +17,7 @@ insights regarding a particular strategy's performance. Catalyst also supports
live-trading of crypto-assets starting with three exchanges (Bitfinex, Bittrex, live-trading of crypto-assets starting with three exchanges (Bitfinex, Bittrex,
and Poloniex) with more being added over time. Catalyst empowers users to share and Poloniex) with more being added over time. Catalyst empowers users to share
and curate data and build profitable, data-driven investment strategies. Please and curate data and build profitable, data-driven investment strategies. Please
visit `enigma.co <https://www.enigma.co>`_ to learn more about Catalyst, or visit `enigma.co <https://www.enigma.co>`_ to learn more about Catalyst.
refer to the `whitepaper <https://www.enigma.co/enigma_catalyst.pdf>`_ for
further technical details.
Catalyst builds on top of the well-established Catalyst builds on top of the well-established
`Zipline <https://github.com/quantopian/zipline>`_ project. We did our best to `Zipline <https://github.com/quantopian/zipline>`_ project. We did our best to
+5 -5
View File
@@ -939,7 +939,7 @@ class TradingAlgorithm(object):
The field to query. The options have the following meanings: The field to query. The options have the following meanings:
arena : str arena : str
The arena from the simulation parameters. This will normally The arena from the simulation parameters. This will normally
be ``'backtest'`` but some systems may use this distinguish be ``backtest`` but some systems may use this distinguish
live trading from backtesting. live trading from backtesting.
data_frequency : {'daily', 'minute'} data_frequency : {'daily', 'minute'}
data_frequency tells the algorithm if it is running with data_frequency tells the algorithm if it is running with
@@ -954,7 +954,7 @@ class TradingAlgorithm(object):
The platform that the code is running on. By default this The platform that the code is running on. By default this
will be the string 'catalyst'. This can allow algorithms to will be the string 'catalyst'. This can allow algorithms to
know if they are running on the Quantopian platform instead. know if they are running on the Quantopian platform instead.
* : dict[str -> any] \* : dict[str -> any]
Returns all of the fields in a dictionary. Returns all of the fields in a dictionary.
Returns Returns
@@ -1032,7 +1032,7 @@ class TradingAlgorithm(object):
argument is the name of the column in the preprocessed dataframe argument is the name of the column in the preprocessed dataframe
containing the symbols. This will be used along with the date containing the symbols. This will be used along with the date
information to map the sids in the asset finder. information to map the sids in the asset finder.
**kwargs \*\*kwargs
Forwarded to :func:`pandas.read_csv`. Forwarded to :func:`pandas.read_csv`.
Returns Returns
@@ -1156,7 +1156,7 @@ class TradingAlgorithm(object):
Parameters Parameters
---------- ----------
**kwargs \*\*kwargs
The names and values to record. The names and values to record.
Notes Notes
@@ -1273,7 +1273,7 @@ class TradingAlgorithm(object):
Parameters Parameters
---------- ----------
*args : iterable[str] \*args : iterable[str]
The ticker symbols to lookup. The ticker symbols to lookup.
Returns Returns
+3 -3
View File
@@ -60,7 +60,7 @@ def _handle_data(context, data):
rsi=rsi, rsi=rsi,
) )
orders = get_open_orders(context.asset) orders = context.blotter.open_orders
if orders: if orders:
log.info('skipping bar until all open orders execute') log.info('skipping bar until all open orders execute')
return return
@@ -146,11 +146,11 @@ if __name__ == '__main__':
live = True live = True
if live: if live:
run_algorithm( run_algorithm(
capital_base=0.001, capital_base=1000,
initialize=initialize, initialize=initialize,
handle_data=handle_data, handle_data=handle_data,
analyze=analyze, analyze=analyze,
exchange_name='binance', exchange_name='bittrex',
live=True, live=True,
algo_namespace=algo_namespace, algo_namespace=algo_namespace,
base_currency='btc', base_currency='btc',
+38 -25
View File
@@ -20,8 +20,8 @@ def initialize(context):
def handle_data(context, data): def handle_data(context, data):
# define the windows for the moving averages # define the windows for the moving averages
short_window = 50 short_window = 2
long_window = 200 long_window = 2
# Skip as many bars as long_window to properly compute the average # Skip as many bars as long_window to properly compute the average
context.i += 1 context.i += 1
@@ -32,16 +32,18 @@ def handle_data(context, data):
# moving average with the appropriate parameters. We choose to use # moving average with the appropriate parameters. We choose to use
# minute bars for this simulation -> freq="1m" # minute bars for this simulation -> freq="1m"
# Returns a pandas dataframe. # Returns a pandas dataframe.
short_mavg = data.history(context.asset, short_data = data.history(context.asset,
'price', 'price',
bar_count=short_window, bar_count=short_window,
frequency="1m", frequency="1T",
).mean() )
long_mavg = data.history(context.asset, short_mavg = short_data.mean()
long_data = data.history(context.asset,
'price', 'price',
bar_count=long_window, bar_count=long_window,
frequency="1m", frequency="1T",
).mean() )
long_mavg = long_data.mean()
# Let's keep the price of our asset in a more handy variable # Let's keep the price of our asset in a more handy variable
price = data.current(context.asset, 'price') price = data.current(context.asset, 'price')
@@ -82,7 +84,6 @@ def handle_data(context, data):
def analyze(context, perf): def analyze(context, perf):
# Get the base_currency that was passed as a parameter to the simulation # Get the base_currency that was passed as a parameter to the simulation
exchange = list(context.exchanges.values())[0] exchange = list(context.exchanges.values())[0]
base_currency = exchange.base_currency.upper() base_currency = exchange.base_currency.upper()
@@ -93,7 +94,7 @@ def analyze(context, perf):
ax1.legend_.remove() ax1.legend_.remove()
ax1.set_ylabel('Portfolio Value\n({})'.format(base_currency)) ax1.set_ylabel('Portfolio Value\n({})'.format(base_currency))
start, end = ax1.get_ylim() start, end = ax1.get_ylim()
ax1.yaxis.set_ticks(np.arange(start, end, (end-start)/5)) ax1.yaxis.set_ticks(np.arange(start, end, (end - start) / 5))
# Second chart: Plot asset price, moving averages and buys/sells # Second chart: Plot asset price, moving averages and buys/sells
ax2 = plt.subplot(412, sharex=ax1) ax2 = plt.subplot(412, sharex=ax1)
@@ -104,9 +105,9 @@ def analyze(context, perf):
ax2.set_ylabel('{asset}\n({base})'.format( ax2.set_ylabel('{asset}\n({base})'.format(
asset=context.asset.symbol, asset=context.asset.symbol,
base=base_currency base=base_currency
)) ))
start, end = ax2.get_ylim() start, end = ax2.get_ylim()
ax2.yaxis.set_ticks(np.arange(start, end, (end-start)/5)) ax2.yaxis.set_ticks(np.arange(start, end, (end - start) / 5))
transaction_df = extract_transactions(perf) transaction_df = extract_transactions(perf)
if not transaction_df.empty: if not transaction_df.empty:
@@ -136,28 +137,40 @@ def analyze(context, perf):
ax3.legend_.remove() ax3.legend_.remove()
ax3.set_ylabel('Percent Change') ax3.set_ylabel('Percent Change')
start, end = ax3.get_ylim() start, end = ax3.get_ylim()
ax3.yaxis.set_ticks(np.arange(start, end, (end-start)/5)) ax3.yaxis.set_ticks(np.arange(start, end, (end - start) / 5))
# Fourth chart: Plot our cash # Fourth chart: Plot our cash
ax4 = plt.subplot(414, sharex=ax1) ax4 = plt.subplot(414, sharex=ax1)
perf.cash.plot(ax=ax4) perf.cash.plot(ax=ax4)
ax4.set_ylabel('Cash\n({})'.format(base_currency)) ax4.set_ylabel('Cash\n({})'.format(base_currency))
start, end = ax4.get_ylim() start, end = ax4.get_ylim()
ax4.yaxis.set_ticks(np.arange(0, end, end/5)) ax4.yaxis.set_ticks(np.arange(0, end, end / 5))
plt.show() plt.show()
if __name__ == '__main__': if __name__ == '__main__':
run_algorithm( run_algorithm(
capital_base=1000, capital_base=1000,
data_frequency='minute', data_frequency='minute',
initialize=initialize, initialize=initialize,
handle_data=handle_data, handle_data=handle_data,
analyze=analyze, analyze=analyze,
exchange_name='bitfinex', exchange_name='bitfinex',
algo_namespace=NAMESPACE, algo_namespace=NAMESPACE,
base_currency='usd', base_currency='usd',
start=pd.to_datetime('2017-9-22', utc=True), simulate_orders=True,
end=pd.to_datetime('2017-9-23', utc=True), live=True,
) )
# run_algorithm(
# capital_base=1000,
# data_frequency='minute',
# initialize=initialize,
# handle_data=handle_data,
# analyze=analyze,
# exchange_name='bitfinex',
# algo_namespace=NAMESPACE,
# base_currency='usd',
# start=pd.to_datetime('2017-9-22', utc=True),
# end=pd.to_datetime('2017-9-23', utc=True),
# )
+10 -10
View File
@@ -425,15 +425,12 @@ class CCXT(Exchange):
'Please provide either start_dt or end_dt, not both.' 'Please provide either start_dt or end_dt, not both.'
) )
elif end_dt is not None: if start_dt is None:
# Make sure that end_dt really wants data in the past # TODO: determine why binance is failing
# if it's close to now, we skip the 'since' parameters to if end_dt is None and self.name not in ['binance']:
# lower the probability of error end_dt = pd.Timestamp.utcnow()
bars_to_now = pd.date_range(
end_dt, pd.Timestamp.utcnow(), freq=freq if end_dt is not None:
)
# See: https://github.com/ccxt/ccxt/issues/1360
if len(bars_to_now) > 1 or self.name in ['poloniex']:
dt_range = get_periods_range( dt_range = get_periods_range(
end_dt=end_dt, end_dt=end_dt,
periods=bar_count, periods=bar_count,
@@ -441,10 +438,13 @@ class CCXT(Exchange):
) )
start_dt = dt_range[0] start_dt = dt_range[0]
since = None
if start_dt is not None: if start_dt is not None:
# Convert out start date to a UNIX timestamp, then translate to
# milliseconds
delta = start_dt - get_epoch() delta = start_dt - get_epoch()
since = int(delta.total_seconds()) * 1000 since = int(delta.total_seconds()) * 1000
else:
since = None
candles = dict() candles = dict()
for index, asset in enumerate(assets): for index, asset in enumerate(assets):
+8 -2
View File
@@ -391,8 +391,6 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
log.warn("Can't initialize signal handler inside another thread." log.warn("Can't initialize signal handler inside another thread."
"Exit should be handled by the user.") "Exit should be handled by the user.")
log.info('initialized trading algorithm in live mode')
def interrupt_algorithm(self): def interrupt_algorithm(self):
self.is_running = False self.is_running = False
@@ -874,6 +872,13 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
raise NotImplementedError() raise NotImplementedError()
def _get_open_orders(self, asset=None): def _get_open_orders(self, asset=None):
if self.simulate_orders:
raise ValueError(
'The get_open_orders() method only works in live mode. '
'The purpose is to list open orders on the exchange '
'regardless who placed them. To list the open orders of '
'this algo, use `context.blotter.open_orders`.'
)
if asset: if asset:
exchange = self.exchanges[asset.exchange] exchange = self.exchanges[asset.exchange]
return exchange.get_open_orders(asset) return exchange.get_open_orders(asset)
@@ -907,6 +912,7 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
If an asset is passed then this will return a list of the open If an asset is passed then this will return a list of the open
orders for this asset. orders for this asset.
""" """
# TODO: should this be a shortcut to the open orders in the blotter?
return retry( return retry(
action=self._get_open_orders, action=self._get_open_orders,
attempts=self.attempts['get_open_orders_attempts'], attempts=self.attempts['get_open_orders_attempts'],
+16 -25
View File
@@ -458,7 +458,7 @@ class ExchangeBundle:
last_entry = None last_entry = None
if start is None or \ if start is None or \
(earliest_trade is not None and earliest_trade > start): (earliest_trade is not None and earliest_trade > start):
start = earliest_trade start = earliest_trade
if last_entry is not None and (end is None or end > last_entry): if last_entry is not None and (end is None or end > last_entry):
@@ -600,14 +600,14 @@ class ExchangeBundle:
if show_breakdown: if show_breakdown:
for asset in chunks: for asset in chunks:
with maybe_show_progress( with maybe_show_progress(
chunks[asset], chunks[asset],
show_progress, show_progress,
label='Ingesting {frequency} price data for ' label='Ingesting {frequency} price data for '
'{symbol} on {exchange}'.format( '{symbol} on {exchange}'.format(
exchange=self.exchange_name, exchange=self.exchange_name,
frequency=data_frequency, frequency=data_frequency,
symbol=asset.symbol symbol=asset.symbol
)) as it: )) as it:
for chunk in it: for chunk in it:
problems += self.ingest_ctable( problems += self.ingest_ctable(
asset=chunk['asset'], asset=chunk['asset'],
@@ -625,13 +625,13 @@ class ExchangeBundle:
key=lambda chunk: pd.to_datetime(chunk['period']) key=lambda chunk: pd.to_datetime(chunk['period'])
) )
with maybe_show_progress( with maybe_show_progress(
all_chunks, all_chunks,
show_progress, show_progress,
label='Ingesting {frequency} price data on ' label='Ingesting {frequency} price data on '
'{exchange}'.format( '{exchange}'.format(
exchange=self.exchange_name, exchange=self.exchange_name,
frequency=data_frequency, frequency=data_frequency,
)) as it: )) as it:
for chunk in it: for chunk in it:
problems += self.ingest_ctable( problems += self.ingest_ctable(
asset=chunk['asset'], asset=chunk['asset'],
@@ -830,7 +830,6 @@ class ExchangeBundle:
field, field,
data_frequency, data_frequency,
algo_end_dt=None, algo_end_dt=None,
trailing_bar_count=None,
force_auto_ingest=False force_auto_ingest=False
): ):
""" """
@@ -858,7 +857,6 @@ class ExchangeBundle:
bar_count=bar_count, bar_count=bar_count,
field=field, field=field,
data_frequency=data_frequency, data_frequency=data_frequency,
trailing_bar_count=trailing_bar_count,
) )
return pd.DataFrame(series) return pd.DataFrame(series)
@@ -887,7 +885,6 @@ class ExchangeBundle:
field=field, field=field,
data_frequency=data_frequency, data_frequency=data_frequency,
reset_reader=True, reset_reader=True,
trailing_bar_count=trailing_bar_count,
) )
return series return series
@@ -898,7 +895,6 @@ class ExchangeBundle:
bar_count=bar_count, bar_count=bar_count,
field=field, field=field,
data_frequency=data_frequency, data_frequency=data_frequency,
trailing_bar_count=trailing_bar_count,
) )
return pd.DataFrame(series) return pd.DataFrame(series)
@@ -962,12 +958,7 @@ class ExchangeBundle:
bar_count, bar_count,
field, field,
data_frequency, data_frequency,
trailing_bar_count=None,
reset_reader=False): reset_reader=False):
if trailing_bar_count:
delta = get_delta(trailing_bar_count, data_frequency)
end_dt += delta
start_dt = get_start_dt(end_dt, bar_count, data_frequency, False) start_dt = get_start_dt(end_dt, bar_count, data_frequency, False)
start_dt, _ = self.get_adj_dates( start_dt, _ = self.get_adj_dates(
start_dt, end_dt, assets, data_frequency start_dt, end_dt, assets, data_frequency
@@ -298,7 +298,6 @@ class DataPortalExchangeBacktest(DataPortalExchangeBase):
frequency, data_frequency frequency, data_frequency
) )
adj_bar_count = candle_size * bar_count adj_bar_count = candle_size * bar_count
trailing_bar_count = candle_size - 1
if data_frequency == 'minute' and adj_data_frequency == 'daily': if data_frequency == 'minute' and adj_data_frequency == 'daily':
end_dt = end_dt.floor('1D') end_dt = end_dt.floor('1D')
@@ -310,7 +309,6 @@ class DataPortalExchangeBacktest(DataPortalExchangeBase):
field=field, field=field,
data_frequency=adj_data_frequency, data_frequency=adj_data_frequency,
algo_end_dt=self._last_available_session, algo_end_dt=self._last_available_session,
trailing_bar_count=trailing_bar_count,
) )
df = resample_history_df(pd.DataFrame(series), freq, field) df = resample_history_df(pd.DataFrame(series), freq, field)
+1 -1
View File
@@ -540,7 +540,7 @@ def resample_history_df(df, freq, field):
else: else:
raise ValueError('Invalid field.') raise ValueError('Invalid field.')
resampled_df = df.resample(freq).agg(agg) resampled_df = df.resample(freq, closed='left', label='left').agg(agg)
return resampled_df return resampled_df
+376
View File
@@ -0,0 +1,376 @@
# -*- coding: utf-8 -*-
# !/usr/bin/env python2
import sys
import os
import pandas as pd
import signal
# import talib
from logbook import Logger
from catalyst import run_algorithm
from catalyst.api import (
symbol,
record,
order,
order_target,
order_target_percent,
get_open_orders
)
from catalyst.finance import commission
# from base.telegrambot import TelegramBot
class GracefulKiller:
# Source: https://stackoverflow.com/a/31464349
def __init__(self, context):
self.kill_now = False
self.signal = 0
self.context = context
signal.signal(signal.SIGINT, self.exit_gracefully)
def exit_gracefully(self, signum, frame):
self.kill_now = True
self.signal = signum
if hasattr(self.context,
'telegram_bot') and self.context.telegram_bot is not None:
self.context.telegram_bot.updater.stop()
sys.exit(0)
def exit(self):
return self.kill_now
class SimulationParameters:
MODE = 'paper'
CAPITAL_BASE = 1000
"""
Capital base used on this simulation
"""
DATA_FREQUECY = 'minute'
EXCHANGE_NAME = 'bitfinex'
# EXCHANGE_NAME = 'binance'
"""
Exchange used on this simulation
"""
DATA_DIR = '/home/av/Dropbox/simulations/data'
ALGO_NAMESPACE = os.path.basename(__file__).split('.')[0]
ALGO_NAMESPACE_IMAGE = '{}/{}/{}.png'.format(DATA_DIR, 'images',
ALGO_NAMESPACE)
ALGO_NAMESPACE_RESULTS_TABLE = '{}/{}/{}.csv'.format(DATA_DIR, 'tables',
ALGO_NAMESPACE + '_results')
ALGO_NAMESPACE_TRANSACTIONS_TABLE = '{}/{}/{}.csv'.format(DATA_DIR,
'tables',
ALGO_NAMESPACE + '_transactions')
BASE_CURRENCY = 'usd'
# BASE_CURRENCY = 'usdt'
# SHORT PERIOD
START_DATE = '2017-09-07'
"""
Start date used on this simulation
"""
END_DATE = '2017-12-12'
"""
End date used on this simulation
"""
SKIP_FIRST_CANDLES = 0
# CANDLES_SAMPLE_RATE = 60
# CANDLES_SAMPLE_RATE = 30
CANDLES_SAMPLE_RATE = 1
"""
Candle interval used on this simulation (in minutes)
"""
# http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
# 30 minute interval ohlcv data (the standard data required for candlestick or
# indicators/signals)
# 30T means 30 minutes re-sampling of one minute data.
# CANDLES_FREQUENCY = '60T'
# CANDLES_FREQUENCY = '30T'
CANDLES_FREQUENCY = '1T'
CANDLES_BUFFER_SIZE = 48
COIN_PAIR = 'btc_usd'
# COIN_PAIR = 'btc_usdt'
"""
Coin pair used on this simulation
"""
# TRANSACTIONS
COMMISSION_FEE = 0.0030
BUY_MIN_AMOUNT = 5 # i.e: USD
SELL_MIN_AMOUNT = 0.001 # i.e: USD
BUY_SELL_PERCENTAGE = 1 # 0.50
BUY_PERCENTAGE = BUY_SELL_PERCENTAGE
SELL_PERCENTAGE = BUY_SELL_PERCENTAGE
BASE_PRICE = 'close'
"""
Base price used (close / Heiken Ashi)
"""
log = None
parameters = None
def print_facts(context):
context.log.info("""
Index: {}
Date: {}
Candle:
O: {}
H: {}
L: {}
C: {}
V: {}
Metrics:
...
Portfolio:
Base price: {}
Base coin (coin2/usd): {}
Amount (coin1/btc): {}
""".format(
# Facts
context.i,
context.curr_minute,
context.candles_open[-1],
context.candles_high[-1],
context.candles_low[-1],
context.candles_close[-1],
context.candles_volume[-1],
# Metrics
# ...
# Portfolio
context.curr_base_price,
context.portfolio.cash,
context.portfolio.positions[context.coin_pair].amount,
))
def print_facts_telegram(context):
price = context.curr_base_price
amount = context.portfolio.positions[context.coin_pair].amount
pnl = context.portfolio.pnl
capital_used = context.portfolio.capital_used
portfolio_value = context.portfolio.portfolio_value
portfolio_returns = context.portfolio.returns
starting_cash = context.portfolio.starting_cash
cash = context.portfolio.cash
msg = """
Status...
Price: {}
Starting cash: {}
Cash: {}
Capital used: {}
Amount: {}
Portfolio value: {}
Returns: {}
PnL: {}
""".format(
price,
starting_cash,
cash,
capital_used,
amount,
portfolio_value,
portfolio_returns,
pnl,
)
if hasattr(context, 'telegram_bot') and context.telegram_bot is not None:
context.telegram_bot.msg(msg)
def default_initialize(context):
# FIXME: set_benchmark
# set_benchmark(symbol(context.parameters.COIN_PAIR))
context.coin_pair = symbol(context.parameters.COIN_PAIR)
context.base_price = None
context.current_day = None
context.counter = -1
context.i = 0
context.candles_sample_rate = context.parameters.CANDLES_SAMPLE_RATE
context.candles_frequency = context.parameters.CANDLES_FREQUENCY
context.candles_buffer_size = context.parameters.CANDLES_BUFFER_SIZE
context.set_commission(
commission.PerShare(cost=context.parameters.COMMISSION_FEE))
def default_handle_data(context, data):
context.curr_minute = data.current_dt
context.counter += 1
if context.candles_sample_rate == 1:
context.i += 1
elif context.counter % context.candles_sample_rate != 0:
context.i += 1
return
if context.i < context.parameters.SKIP_FIRST_CANDLES:
return
context.candles_open = data.history(
context.coin_pair,
'open',
bar_count=context.candles_buffer_size,
frequency=context.candles_frequency)
context.candles_high = data.history(
context.coin_pair,
'high',
bar_count=context.candles_buffer_size,
frequency=context.candles_frequency)
context.candles_low = data.history(
context.coin_pair,
'low',
bar_count=context.candles_buffer_size,
frequency=context.candles_frequency)
context.candles_close = data.history(
context.coin_pair,
'price',
bar_count=context.candles_buffer_size,
frequency=context.candles_frequency)
context.candles_volume = data.history(
context.coin_pair,
'volume',
bar_count=context.candles_buffer_size,
frequency=context.candles_frequency)
# FIXME: Here is the error!
# The candles_close frame shows more or less always a value of 94, while
# bitcoin price is very different from that
print(context.candles_close)
context.base_prices = context.candles_close
cash = context.portfolio.cash
amount = context.portfolio.positions[context.coin_pair].amount
price = data.current(context.coin_pair, 'price')
order_id = None
context.last_base_price = context.base_prices[-2]
context.curr_base_price = context.base_prices[-1]
# TA calculations
# ...
# Sanity checks
# assert cash >= 0
if cash < 0:
import ipdb;
ipdb.set_trace() # BREAKPOINT
print_facts(context)
print_facts_telegram(context)
# Order management
net_shares = 0
if context.counter == 2:
brute_shares = (cash / price) * context.parameters.BUY_PERCENTAGE
share_commission_fee = brute_shares * context.parameters.COMMISSION_FEE
net_shares = brute_shares - share_commission_fee
buy_order_id = order(context.coin_pair, net_shares)
if context.counter == 3:
brute_shares = amount * context.parameters.SELL_PERCENTAGE
share_commission_fee = brute_shares * context.parameters.COMMISSION_FEE
net_shares = -(brute_shares - share_commission_fee)
sell_order_id = order(context.coin_pair, net_shares)
# Record
record(
price=price,
foo='bar',
# volume=current['volume'],
# price_change=price_change,
# Metrics
cash=cash,
# buy=context.buy,
# sell=context.sell
)
def default_analyze(context=None, perf=None):
pass
def initialize(context):
global log
context.parameters = parameters
context.log = Logger(context.parameters.ALGO_NAMESPACE)
log = context.log
default_initialize(context)
context.killer = GracefulKiller(context)
context.telegram_bot = None
# TELEGRAM_TOKEN='token'
# context.telegram_bot = TelegramBot()
# context.telegram_bot.initialize(TELEGRAM_TOKEN, context)
if __name__ == '__main__':
# Parameters:
parameters = SimulationParameters()
start_date = pd.to_datetime(parameters.START_DATE, utc=True)
end_date = pd.to_datetime(parameters.END_DATE, utc=True)
if parameters.MODE == 'backtest':
results = run_algorithm(
capital_base=parameters.CAPITAL_BASE,
data_frequency=parameters.DATA_FREQUECY,
initialize=initialize,
handle_data=default_handle_data,
analyze=default_analyze,
exchange_name=parameters.EXCHANGE_NAME,
algo_namespace=parameters.ALGO_NAMESPACE,
base_currency=parameters.BASE_CURRENCY,
start=start_date,
end=end_date,
live=False,
live_graph=False
)
returns_daily = results
results.to_csv('{}'.format(parameters.ALGO_NAMESPACE_RESULTS_TABLE))
# returns_daily = returns_minutely.add(1).groupby(pd.TimeGrouper('24H')).prod().add(-1)
# FIXME: pyfolio integration
# pf_data = pyfolio.utils.extract_rets_pos_txn_from_zipline(results)
# pf_data = pyfolio.utils.extract_rets_pos_txn_from_zipline(results[:'2017-01-01'])
# pyfolio.create_full_tear_sheet(*pf_data)
elif parameters.MODE == 'paper':
results = run_algorithm(
capital_base=parameters.CAPITAL_BASE,
data_frequency=parameters.DATA_FREQUECY,
initialize=initialize,
handle_data=default_handle_data,
analyze=default_analyze,
exchange_name=parameters.EXCHANGE_NAME,
algo_namespace=parameters.ALGO_NAMESPACE,
base_currency=parameters.BASE_CURRENCY,
live=True,
simulate_orders=True,
live_graph=False
)
elif parameters.MODE == 'live':
results = run_algorithm(
initialize=initialize,
handle_data=default_handle_data,
analyze=default_analyze,
exchange_name=parameters.EXCHANGE_NAME,
algo_namespace=parameters.ALGO_NAMESPACE,
base_currency=parameters.BASE_CURRENCY,
live=True,
live_graph=True
)
+6 -8
View File
@@ -55,6 +55,7 @@ class _RunAlgoError(click.ClickException, ValueError):
---------- ----------
pyfunc_msg : str pyfunc_msg : str
The message that will be shown when called as a python function. The message that will be shown when called as a python function.
cmdline_msg : str cmdline_msg : str
The message that will be shown on the command line. The message that will be shown on the command line.
""" """
@@ -416,7 +417,8 @@ def run_algorithm(initialize,
auth_aliases=None, auth_aliases=None,
stats_output=None, stats_output=None,
output=os.devnull): output=os.devnull):
"""Run a trading algorithm. """
Run a trading algorithm.
Parameters Parameters
---------- ----------
@@ -458,7 +460,7 @@ def run_algorithm(initialize,
This argument is mutually exclusive with ``data``. This argument is mutually exclusive with ``data``.
default_extension : bool, optional default_extension : bool, optional
Should the default catalyst extension be loaded. This is found at Should the default catalyst extension be loaded. This is found at
``$ZIPLINE_ROOT/extension.py`` ``$CATALYST_ROOT/extension.py``
extensions : iterable[str], optional extensions : iterable[str], optional
The names of any other extensions to load. Each element may either be The names of any other extensions to load. Each element may either be
a dotted module path like ``a.b.c`` or a path to a python file ending a dotted module path like ``a.b.c`` or a path to a python file ending
@@ -469,12 +471,8 @@ def run_algorithm(initialize,
environ : mapping[str -> str], optional environ : mapping[str -> str], optional
The os environment to use. Many extensions use this to get parameters. The os environment to use. Many extensions use this to get parameters.
This defaults to ``os.environ``. This defaults to ``os.environ``.
live: execute live trading live : bool, optional
exchange_conn: The exchange connection parameters Execute algorithm in live trading mode.
Supported Exchanges
-------------------
bitfinex
Returns Returns
------- -------
+173 -179
View File
@@ -4,7 +4,7 @@ API Reference
Running a Backtest Running a Backtest
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
.. autofunction:: zipline.run_algorithm(...) .. autofunction:: catalyst.run_algorithm(...)
Algorithm API Algorithm API
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@@ -18,341 +18,335 @@ currently-executing :class:`~zipline.algorithm.TradingAlgorithm` instance.
Data Object Data Object
``````````` ```````````
.. autoclass:: zipline.protocol.BarData .. autoclass:: catalyst.protocol.BarData
:members: :members:
Scheduling Functions Scheduling Functions
```````````````````` ````````````````````
.. autofunction:: zipline.api.schedule_function .. autofunction:: catalyst.api.schedule_function
.. autoclass:: zipline.api.date_rules .. autoclass:: catalyst.api.date_rules
:members: :members:
:undoc-members: :undoc-members:
.. autoclass:: zipline.api.time_rules .. autoclass:: catalyst.api.time_rules
:members: :members:
Orders Orders
`````` ``````
.. autofunction:: zipline.api.order .. autofunction:: catalyst.api.order
.. autofunction:: zipline.api.order_value .. autofunction:: catalyst.api.order_value
.. autofunction:: zipline.api.order_percent .. autofunction:: catalyst.api.order_percent
.. autofunction:: zipline.api.order_target .. autofunction:: catalyst.api.order_target
.. autofunction:: zipline.api.order_target_value .. autofunction:: catalyst.api.order_target_value
.. autofunction:: zipline.api.order_target_percent .. autofunction:: catalyst.api.order_target_percent
.. autoclass:: zipline.finance.execution.ExecutionStyle .. autoclass:: catalyst.finance.execution.ExecutionStyle
:members: :members:
.. autoclass:: zipline.finance.execution.MarketOrder .. autoclass:: catalyst.finance.execution.MarketOrder
.. autoclass:: zipline.finance.execution.LimitOrder .. autoclass:: catalyst.finance.execution.LimitOrder
.. autoclass:: zipline.finance.execution.StopOrder .. autoclass:: catalyst.finance.execution.StopOrder
.. autoclass:: zipline.finance.execution.StopLimitOrder .. autoclass:: catalyst.finance.execution.StopLimitOrder
.. autofunction:: zipline.api.get_order .. autofunction:: catalyst.api.get_order
.. autofunction:: zipline.api.get_open_orders .. autofunction:: catalyst.api.get_open_orders
.. autofunction:: zipline.api.cancel_order .. autofunction:: catalyst.api.cancel_order
Order Cancellation Policies Order Cancellation Policies
''''''''''''''''''''''''''' '''''''''''''''''''''''''''
.. autofunction:: zipline.api.set_cancel_policy .. autofunction:: catalyst.api.set_cancel_policy
.. autoclass:: zipline.finance.cancel_policy.CancelPolicy .. autoclass:: catalyst.finance.cancel_policy.CancelPolicy
:members: :members:
.. autofunction:: zipline.api.EODCancel .. autofunction:: catalyst.api.EODCancel
.. autofunction:: zipline.api.NeverCancel .. autofunction:: catalyst.api.NeverCancel
Assets Assets
`````` ``````
.. autofunction:: zipline.api.symbol .. autofunction:: catalyst.api.symbol
.. autofunction:: zipline.api.symbols .. autofunction:: catalyst.api.symbols
.. autofunction:: zipline.api.future_symbol .. autofunction:: catalyst.api.set_symbol_lookup_date
.. autofunction:: zipline.api.set_symbol_lookup_date .. autofunction:: catalyst.api.sid
.. autofunction:: zipline.api.sid
Trading Controls Trading Controls
```````````````` ````````````````
Zipline provides trading controls to help ensure that the algorithm is zipline provides trading controls to help ensure that the algorithm is
performing as expected. The functions help protect the algorithm from certian performing as expected. The functions help protect the algorithm from certian
bugs that could cause undesirable behavior when trading with real money. bugs that could cause undesirable behavior when trading with real money.
.. autofunction:: zipline.api.set_do_not_order_list .. autofunction:: catalyst.api.set_do_not_order_list
.. autofunction:: zipline.api.set_long_only .. autofunction:: catalyst.api.set_long_only
.. autofunction:: zipline.api.set_max_leverage .. autofunction:: catalyst.api.set_max_leverage
.. autofunction:: zipline.api.set_max_order_count .. autofunction:: catalyst.api.set_max_order_count
.. autofunction:: zipline.api.set_max_order_size .. autofunction:: catalyst.api.set_max_order_size
.. autofunction:: zipline.api.set_max_position_size .. autofunction:: catalyst.api.set_max_position_size
Simulation Parameters Simulation Parameters
````````````````````` `````````````````````
.. autofunction:: zipline.api.set_benchmark .. autofunction:: catalyst.api.set_benchmark
Commission Models Commission Models
''''''''''''''''' '''''''''''''''''
.. autofunction:: zipline.api.set_commission .. autofunction:: catalyst.api.set_commission
.. autoclass:: zipline.finance.commission.CommissionModel .. autoclass:: catalyst.finance.commission.CommissionModel
:members: :members:
.. autoclass:: zipline.finance.commission.PerShare .. autoclass:: catalyst.finance.commission.PerShare
.. autoclass:: zipline.finance.commission.PerTrade .. autoclass:: catalyst.finance.commission.PerTrade
.. autoclass:: zipline.finance.commission.PerDollar .. autoclass:: catalyst.finance.commission.PerDollar
Slippage Models Slippage Models
''''''''''''''' '''''''''''''''
.. autofunction:: zipline.api.set_slippage .. autofunction:: catalyst.api.set_slippage
.. autoclass:: zipline.finance.slippage.SlippageModel .. autoclass:: catalyst.finance.slippage.SlippageModel
:members: :members:
.. autoclass:: zipline.finance.slippage.FixedSlippage .. autoclass:: catalyst.finance.slippage.FixedSlippage
.. autoclass:: zipline.finance.slippage.VolumeShareSlippage .. autoclass:: catalyst.finance.slippage.VolumeShareSlippage
Pipeline Pipeline
```````` ````````
For more information, see :ref:`pipeline-api` Not supported yet.
.. autofunction:: zipline.api.attach_pipeline .. For more information, see :ref:`pipeline-api`
.. autofunction:: zipline.api.pipeline_output .. .. autofunction:: catalyst.api.attach_pipeline
.. .. autofunction:: catalyst.api.pipeline_output
Miscellaneous Miscellaneous
````````````` `````````````
.. autofunction:: zipline.api.record .. autofunction:: catalyst.api.record
.. autofunction:: zipline.api.get_environment .. autofunction:: catalyst.api.get_environment
.. autofunction:: zipline.api.fetch_csv .. autofunction:: catalyst.api.fetch_csv
.. _pipeline-api: .. _pipeline-api:
Pipeline API .. Pipeline API
~~~~~~~~~~~~ .. ~~~~~~~~~~~~
.. autoclass:: zipline.pipeline.Pipeline .. .. autoclass:: zipline.pipeline.Pipeline
:members: .. :members:
:member-order: groupwise .. :member-order: groupwise
.. autoclass:: zipline.pipeline.CustomFactor .. .. autoclass:: zipline.pipeline.CustomFactor
:members: .. :members:
:member-order: groupwise .. :member-order: groupwise
.. autoclass:: zipline.pipeline.filters.Filter .. .. autoclass:: zipline.pipeline.filters.Filter
:members: __and__, __or__ .. :members: __and__, __or__
:exclude-members: dtype .. :exclude-members: dtype
.. autoclass:: zipline.pipeline.factors.Factor .. .. autoclass:: zipline.pipeline.factors.Factor
:members: bottom, deciles, demean, linear_regression, pearsonr, .. :members: bottom, deciles, demean, linear_regression, pearsonr,
percentile_between, quantiles, quartiles, quintiles, rank, .. percentile_between, quantiles, quartiles, quintiles, rank,
spearmanr, top, winsorize, zscore, isnan, notnan, isfinite, eq, .. spearmanr, top, winsorize, zscore, isnan, notnan, isfinite, eq,
__add__, __sub__, __mul__, __div__, __mod__, __pow__, __lt__, .. \__add__, \__sub__, \__mul__, \__div__, \__mod__, \__pow__,
__le__, __ne__, __ge__, __gt__ .. \__lt__, \__le__, \__ne__, \__ge__, \__gt__
:exclude-members: dtype .. :exclude-members: dtype
:member-order: bysource .. :member-order: bysource
.. autoclass:: zipline.pipeline.term.Term .. .. autoclass:: zipline.pipeline.term.Term
:members: .. :members:
:exclude-members: compute_extra_rows, dependencies, inputs, mask, windowed .. :exclude-members: compute_extra_rows, dependencies, inputs, mask, windowed
.. autoclass:: zipline.pipeline.data.USEquityPricing .. .. autoclass:: zipline.pipeline.data.USEquityPricing
:members: open, high, low, close, volume .. :members: open, high, low, close, volume
:undoc-members: .. :undoc-members:
Built-in Factors .. Built-in Factors
```````````````` .. ````````````````
.. autoclass:: zipline.pipeline.factors.AverageDollarVolume .. .. autoclass:: zipline.pipeline.factors.AverageDollarVolume
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.BollingerBands .. .. autoclass:: zipline.pipeline.factors.BollingerBands
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.BusinessDaysSincePreviousEvent .. .. autoclass:: zipline.pipeline.factors.BusinessDaysSincePreviousEvent
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.BusinessDaysUntilNextEvent .. .. autoclass:: zipline.pipeline.factors.BusinessDaysUntilNextEvent
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingAverage .. .. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingAverage
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingStdDev .. .. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingStdDev
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.Latest .. .. autoclass:: zipline.pipeline.factors.Latest
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.MaxDrawdown .. .. autoclass:: zipline.pipeline.factors.MaxDrawdown
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.Returns .. .. autoclass:: zipline.pipeline.factors.Returns
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.RollingLinearRegressionOfReturns .. .. autoclass:: zipline.pipeline.factors.RollingLinearRegressionOfReturns
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.RollingPearsonOfReturns .. .. autoclass:: zipline.pipeline.factors.RollingPearsonOfReturns
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.RollingSpearmanOfReturns .. .. autoclass:: zipline.pipeline.factors.RollingSpearmanOfReturns
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.RSI .. .. autoclass:: zipline.pipeline.factors.RSI
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.SimpleMovingAverage .. .. autoclass:: zipline.pipeline.factors.SimpleMovingAverage
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.VWAP .. .. autoclass:: zipline.pipeline.factors.VWAP
:members: .. :members:
.. autoclass:: zipline.pipeline.factors.WeightedAverageValue .. .. autoclass:: zipline.pipeline.factors.WeightedAverageValue
:members: .. :members:
Pipeline Engine .. Pipeline Engine
``````````````` .. ```````````````
.. autoclass:: zipline.pipeline.engine.PipelineEngine .. .. autoclass:: zipline.pipeline.engine.PipelineEngine
:members: run_pipeline, run_chunked_pipeline .. :members: run_pipeline, run_chunked_pipeline
:member-order: bysource .. :member-order: bysource
.. autoclass:: zipline.pipeline.engine.SimplePipelineEngine .. .. autoclass:: zipline.pipeline.engine.SimplePipelineEngine
:members: __init__, run_pipeline, run_chunked_pipeline .. :members: __init__, run_pipeline, run_chunked_pipeline
:member-order: bysource .. :member-order: bysource
.. autofunction:: zipline.pipeline.engine.default_populate_initial_workspace .. .. autofunction:: zipline.pipeline.engine.default_populate_initial_workspace
Data Loaders .. Data Loaders
```````````` .. ````````````
.. autoclass:: zipline.pipeline.loaders.equity_pricing_loader.USEquityPricingLoader .. .. autoclass:: zipline.pipeline.loaders.equity_pricing_loader.USEquityPricingLoader
:members: __init__, from_files, load_adjusted_array .. :members: __init__, from_files, load_adjusted_array
:member-order: bysource .. :member-order: bysource
Asset Metadata Asset Metadata
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
.. autoclass:: zipline.assets.Asset .. autoclass:: catalyst.assets.Asset
:members: :members:
.. autoclass:: zipline.assets.Equity .. autoclass:: catalyst.assets.AssetConvertible
:members:
.. autoclass:: zipline.assets.Future
:members:
.. autoclass:: zipline.assets.AssetConvertible
:members: :members:
Trading Calendar API Trading Calendar API
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
.. autofunction:: zipline.utils.calendars.get_calendar .. autofunction:: catalyst.utils.calendars.get_calendar
.. autoclass:: zipline.utils.calendars.TradingCalendar .. autoclass:: catalyst.utils.calendars.TradingCalendar
:members: :members:
.. autofunction:: zipline.utils.calendars.register_calendar .. autofunction:: catalyst.utils.calendars.register_calendar
.. autofunction:: zipline.utils.calendars.register_calendar_type .. autofunction:: catalyst.utils.calendars.register_calendar_type
.. autofunction:: zipline.utils.calendars.deregister_calendar .. autofunction:: catalyst.utils.calendars.deregister_calendar
.. autofunction:: zipline.utils.calendars.clear_calendars .. autofunction:: catalyst.utils.calendars.clear_calendars
Data API Data API
~~~~~~~~ ~~~~~~~~
Writers .. Writers
``````` .. ```````
.. autoclass:: zipline.data.minute_bars.BcolzMinuteBarWriter .. .. autoclass:: zipline.data.minute_bars.BcolzMinuteBarWriter
:members: .. :members:
.. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarWriter .. .. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarWriter
:members: .. :members:
.. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentWriter .. .. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentWriter
:members: .. :members:
.. autoclass:: zipline.assets.AssetDBWriter .. .. autoclass:: zipline.assets.AssetDBWriter
:members: .. :members:
Readers .. Readers
``````` .. ```````
.. autoclass:: zipline.data.minute_bars.BcolzMinuteBarReader .. .. autoclass:: zipline.data.minute_bars.BcolzMinuteBarReader
:members: .. :members:
.. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarReader .. .. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarReader
:members: .. :members:
.. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentReader .. .. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentReader
:members: .. :members:
.. autoclass:: zipline.assets.AssetFinder .. .. autoclass:: zipline.assets.AssetFinder
:members: .. :members:
.. autoclass:: zipline.data.data_portal.DataPortal .. .. autoclass:: zipline.data.data_portal.DataPortal
:members: .. :members:
Bundles .. Bundles
``````` .. ```````
.. autofunction:: zipline.data.bundles.register .. .. autofunction:: zipline.data.bundles.register
.. autofunction:: zipline.data.bundles.ingest(name, environ=os.environ, date=None, show_progress=True) .. .. autofunction:: zipline.data.bundles.ingest(name, environ=os.environ, date=None, show_progress=True)
.. autofunction:: zipline.data.bundles.load(name, environ=os.environ, date=None) .. .. autofunction:: zipline.data.bundles.load(name, environ=os.environ, date=None)
.. autofunction:: zipline.data.bundles.unregister .. .. autofunction:: zipline.data.bundles.unregister
.. data:: zipline.data.bundles.bundles .. .. data:: zipline.data.bundles.bundles
The bundles that have been registered as a mapping from bundle name to bundle .. The bundles that have been registered as a mapping from bundle name to bundle
data. This mapping is immutable and should only be updated through .. data. This mapping is immutable and should only be updated through
:func:`~zipline.data.bundles.register` or .. :func:`~zipline.data.bundles.register` or
:func:`~zipline.data.bundles.unregister`. .. :func:`~zipline.data.bundles.unregister`.
.. autofunction:: zipline.data.bundles.yahoo_equities .. .. autofunction:: zipline.data.bundles.yahoo_equities
@@ -362,16 +356,16 @@ Utilities
Caching Caching
``````` ```````
.. autoclass:: zipline.utils.cache.CachedObject .. autoclass:: catalyst.utils.cache.CachedObject
.. autoclass:: zipline.utils.cache.ExpiringCache .. autoclass:: catalyst.utils.cache.ExpiringCache
.. autoclass:: zipline.utils.cache.dataframe_cache .. autoclass:: catalyst.utils.cache.dataframe_cache
.. autoclass:: zipline.utils.cache.working_file .. autoclass:: catalyst.utils.cache.working_file
.. autoclass:: zipline.utils.cache.working_dir .. autoclass:: catalyst.utils.cache.working_dir
Command Line Command Line
```````````` ````````````
.. autofunction:: zipline.utils.cli.maybe_show_progress .. autofunction:: catalyst.utils.cli.maybe_show_progress
+7 -5
View File
@@ -168,7 +168,7 @@ We'll start with the CLI, and introduce the ``run_algorithm()`` in the last
example of this tutorial. Some of the :doc:`example algorithms <example-algos>` example of this tutorial. Some of the :doc:`example algorithms <example-algos>`
provide instructions on how to run them both from the CLI, and using the provide instructions on how to run them both from the CLI, and using the
:func:`~catalyst.run_algorithm` function. For the third method, refer to the :func:`~catalyst.run_algorithm` function. For the third method, refer to the
corresponding section on :doc:`Catalyst & Jupyter Notebook <jupyter>` after you corresponding section on :ref:`Catalyst & Jupyter Notebook <jupyter>` after you
have assimilated the contents of this tutorial. have assimilated the contents of this tutorial.
Command line interface Command line interface
@@ -473,6 +473,7 @@ Which we execute by running:
</div> </div>
| |
There is a row for each trading day, starting on the first day of our There is a row for each trading day, starting on the first day of our
simulation Jan 1st, 2016. In the columns you can find various simulation Jan 1st, 2016. In the columns you can find various
information about the state of your algorithm. The column information about the state of your algorithm. The column
@@ -518,7 +519,7 @@ alongside enigma-catalyst (with the exception of the ``Conda`` install, where it
was included by default inside the conda environment we created). If for any was included by default inside the conda environment we created). If for any
reason you don't have it installed, you can add it by running: reason you don't have it installed, you can add it by running:
.. code-block:: python .. code-block:: bash
(catalyst)$ pip install matplotlib (catalyst)$ pip install matplotlib
@@ -806,6 +807,7 @@ the ``scikit-learn`` functions require ``numpy.ndarray``\ s rather than
``pandas.DataFrame``\ s, so you can simply pass the underlying ``pandas.DataFrame``\ s, so you can simply pass the underlying
``ndarray`` of a ``DataFrame`` via ``.values``). ``ndarray`` of a ``DataFrame`` via ``.values``).
.. _jupyter:
Jupyter Notebook Jupyter Notebook
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@@ -826,13 +828,13 @@ In order to use Jupyter Notebook, you first have to install it inside your
environment. It's available as ``pip`` package, so regardless of how you environment. It's available as ``pip`` package, so regardless of how you
installed Catalyst, go inside your catalyst environemnt and run: installed Catalyst, go inside your catalyst environemnt and run:
.. code:: bash .. code-block:: bash
(catalyst)$ pip install jupyter (catalyst)$ pip install jupyter
Once you have Jupyter Notebook installed, every time you want to use it run: Once you have Jupyter Notebook installed, every time you want to use it run:
.. code:: bash .. code-block:: bash
(catalyst)$ jupyter notebook (catalyst)$ jupyter notebook
@@ -846,7 +848,7 @@ Before running your algorithms inside the Jupyter Notebook, remember to ingest
the data from the command line interface (CLI). In the example below, you would the data from the command line interface (CLI). In the example below, you would
need to run first: need to run first:
.. code:: bash .. code-block:: bash
catalyst ingest-exchange -x bitfinex -i btc_usd catalyst ingest-exchange -x bitfinex -i btc_usd
+5 -2
View File
@@ -27,8 +27,8 @@ extlinks = {
# -- Docstrings --------------------------------------------------------------- # -- Docstrings ---------------------------------------------------------------
#extensions += ['numpydoc'] extensions += ['numpydoc']
#numpydoc_show_class_members = False numpydoc_show_class_members = False
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates'] templates_path = ['.templates']
@@ -97,3 +97,6 @@ intersphinx_mapping = {
doctest_global_setup = "import catalyst" doctest_global_setup = "import catalyst"
todo_include_todos = True todo_include_todos = True
suppress_warnings = ['image.nonlocal_uri']
+7 -17
View File
@@ -36,25 +36,15 @@ Finally, you can build the C extensions by running:
$ python setup.py build_ext --inplace $ python setup.py build_ext --inplace
.. To finish, make sure `tests`__ pass. Development with Docker
-----------------------
.. __ #style-guide-running-tests If you want to work with zipline using a `Docker`__ container, you'll need to
build the ``Dockerfile`` in the Zipline root directory, and then build
``Dockerfile-dev``. Instructions for building both containers can be found in
``Dockerfile`` and ``Dockerfile-dev``, respectively.
.. If you get an error running nosetests after setting up a fresh virtualenv, please try running __ https://docs.docker.com/get-started/
.. code-block
.. # where zipline is the name of your virtualenv
.. $ deactivate zipline
.. $ workon zipline
.. Development with Docker
.. -----------------------
..If you want to work with zipline using a `Docker`__ container, you'll need to build the ``Dockerfile`` in the Zipline root directory, and then build ``Dockerfile-dev``. Instructions for building both containers can be found in ``Dockerfile`` and ``Dockerfile-dev``, respectively.
.. __ https://docs.docker.com/get-started/
Git Branching Structure Git Branching Structure
----------------------- -----------------------
+1
View File
@@ -1,4 +1,5 @@
| |
Example Algorithms Example Algorithms
================== ==================
+2
View File
@@ -1,6 +1,8 @@
.. include:: ../../README.rst .. include:: ../../README.rst
| |
| |
Table of Contents Table of Contents
----------------- -----------------
+2 -1
View File
@@ -298,7 +298,7 @@ Troubleshooting ``pip`` Install
.. _pipenv: .. _pipenv:
Installing with ``pipenv`` Installing with ``pipenv``
------------------------- --------------------------
Installing Catalyst via ``pipenv`` is perhaps easier that installing it via Installing Catalyst via ``pipenv`` is perhaps easier that installing it via
``pip`` itself but you need to install ``pipenv`` first via ``pip``. ``pip`` itself but you need to install ``pipenv`` first via ``pip``.
@@ -476,6 +476,7 @@ mentioned above are as follows:
default you get 0 as the Value Data) default you get 0 as the Value Data)
| |
- **The installer has encountered an unexpected error installing this package. - **The installer has encountered an unexpected error installing this package.
This may indicate a problem with this package. The error code is 2503.** This may indicate a problem with this package. The error code is 2503.**
+1 -1
View File
@@ -113,7 +113,7 @@ Currency symbols (e.g. btc, eth, ltc) follow the Bittrex convention.
Here are some examples: Here are some examples:
.. code-block:: json .. code:: python
# With Bitfinex # With Bitfinex
bitcoin_usd_asset = symbol('btc_usd') bitcoin_usd_asset = symbol('btc_usd')
+17 -1
View File
@@ -2,7 +2,23 @@
Release Notes Release Notes
============= =============
Version 0.5.0 Version 0.5.3
^^^^^^^^^^^^^
**Release Date**: 2018-02-09
Bug Fixes
~~~~~~~~~
- Fixed an issue with last candle in backtesting :issue:`219`
Version 0.5.2
^^^^^^^^^^^^^
**Release Date**: 2018-02-08
Bug Fixes
~~~~~~~~~
- Fixed an issue with live candle values :issue:`216` and :issue:`199`
Version 0.5.1
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
**Release Date**: 2018-02-07 **Release Date**: 2018-02-07
+5
View File
@@ -11,6 +11,7 @@ Installation: MacOS
| |
| |
Installation: Windows Installation: Windows
--------------------- ---------------------
@@ -21,6 +22,7 @@ Where things go smoothly:
<iframe width="560" height="315" src="https://www.youtube.com/embed/H8HqcEbZmkk" frameborder="0" allowfullscreen></iframe> <iframe width="560" height="315" src="https://www.youtube.com/embed/H8HqcEbZmkk" frameborder="0" allowfullscreen></iframe>
| |
Where things don't: Where things don't:
.. raw:: html .. raw:: html
@@ -29,6 +31,7 @@ Where things don't:
| |
| |
Backtesting a Strategy Backtesting a Strategy
---------------------- ----------------------
@@ -44,6 +47,7 @@ sell. Hopefully, well ride the waves.
| |
| |
Live Trading a Strategy Live Trading a Strategy
----------------------- -----------------------
@@ -54,5 +58,6 @@ in the previous video, we now take it to trade live against the Bittrex exchange
.. raw:: html .. raw:: html
<iframe width="560" height="315" src="https://www.youtube.com/embed/NupiE-Xuglw" frameborder="0" allowfullscreen></iframe> <iframe width="560" height="315" src="https://www.youtube.com/embed/NupiE-Xuglw" frameborder="0" allowfullscreen></iframe>
| |
| |
+1 -1
View File
@@ -16,7 +16,7 @@ babel==1.3
docutils==0.12 docutils==0.12
snowballstemmer==1.2.0 snowballstemmer==1.2.0
sphinx-rtd-theme==0.1.8 sphinx-rtd-theme==0.1.8
sphinx==1.3.4 sphinx==1.6.7
pbr==1.10.0 pbr==1.10.0
mock==2.0.0 mock==2.0.0
+1 -1
View File
@@ -1,4 +1,4 @@
Sphinx>=1.3.2 Sphinx==1.6.7
numpydoc>=0.5.0 numpydoc>=0.5.0
sphinx-autobuild==0.6.0 sphinx-autobuild==0.6.0
docutils==0.12 docutils==0.12
+9 -5
View File
@@ -1,8 +1,7 @@
import pandas as pd import pandas as pd
from logbook import Logger from logbook import Logger
from catalyst.testing import ZiplineTestCase from catalyst.exchange.utils.stats_utils import set_print_settings
from catalyst.testing.fixtures import WithLogger
from .base import BaseExchangeTestCase from .base import BaseExchangeTestCase
from catalyst.exchange.ccxt.ccxt_exchange import CCXT from catalyst.exchange.ccxt.ccxt_exchange import CCXT
from catalyst.exchange.exchange_execution import ExchangeLimitOrder from catalyst.exchange.exchange_execution import ExchangeLimitOrder
@@ -15,7 +14,7 @@ log = Logger('test_ccxt')
class TestCCXT(BaseExchangeTestCase): class TestCCXT(BaseExchangeTestCase):
@classmethod @classmethod
def setup(self): def setup(self):
exchange_name = 'binance' exchange_name = 'bittrex'
auth = get_exchange_auth(exchange_name) auth = get_exchange_auth(exchange_name)
self.exchange = CCXT( self.exchange = CCXT(
exchange_name=exchange_name, exchange_name=exchange_name,
@@ -58,15 +57,20 @@ class TestCCXT(BaseExchangeTestCase):
def test_get_candles(self): def test_get_candles(self):
log.info('retrieving candles') log.info('retrieving candles')
candles = self.exchange.get_candles( candles = self.exchange.get_candles(
freq='30T', freq='1T',
assets=[self.exchange.get_asset('eth_btc')], assets=[self.exchange.get_asset('eth_btc')],
bar_count=200, bar_count=200,
start_dt=pd.to_datetime('2017-09-01', utc=True) # start_dt=pd.to_datetime('2017-09-01', utc=True),
) )
for asset in candles: for asset in candles:
df = pd.DataFrame(candles[asset]) df = pd.DataFrame(candles[asset])
df.set_index('last_traded', drop=True, inplace=True) df.set_index('last_traded', drop=True, inplace=True)
set_print_settings()
print('got {} candles'.format(len(df)))
print(df.head(10))
print(df.tail(10))
pass pass
def test_tickers(self): def test_tickers(self):
@@ -2,6 +2,7 @@ import random
import os import os
import pandas as pd import pandas as pd
from datetime import timedelta
from logbook import TestHandler from logbook import TestHandler
from pandas.util.testing import assert_frame_equal from pandas.util.testing import assert_frame_equal
@@ -12,6 +13,7 @@ from catalyst.exchange.utils.exchange_utils import get_candles_df
from catalyst.exchange.utils.factory import get_exchange from catalyst.exchange.utils.factory import get_exchange
from catalyst.exchange.utils.test_utils import output_df, \ from catalyst.exchange.utils.test_utils import output_df, \
select_random_assets select_random_assets
from catalyst.exchange.utils.stats_utils import set_print_settings
pd.set_option('display.expand_frame_repr', False) pd.set_option('display.expand_frame_repr', False)
pd.set_option('precision', 8) pd.set_option('precision', 8)
@@ -58,6 +60,12 @@ class TestSuiteBundle:
log_catcher = TestHandler() log_catcher = TestHandler()
with log_catcher: with log_catcher:
symbols = [asset.symbol for asset in assets]
print(
'comparing data for {}/{} with {} timeframe until {}'.format(
exchange.name, symbols, freq, end_dt
)
)
data['bundle'] = data_portal.get_history_window( data['bundle'] = data_portal.get_history_window(
assets=assets, assets=assets,
end_dt=end_dt, end_dt=end_dt,
@@ -66,6 +74,12 @@ class TestSuiteBundle:
field='close', field='close',
data_frequency=data_frequency, data_frequency=data_frequency,
) )
set_print_settings()
print(
'the bundle first / last row:\n{}'.format(
data['bundle'].iloc[[-1, 0]]
)
)
candles = exchange.get_candles( candles = exchange.get_candles(
end_dt=end_dt, end_dt=end_dt,
freq=freq, freq=freq,
@@ -79,6 +93,11 @@ class TestSuiteBundle:
bar_count=bar_count, bar_count=bar_count,
end_dt=end_dt, end_dt=end_dt,
) )
print(
'the exchange first / last row:\n{}'.format(
data['exchange'].iloc[[-1, 0]]
)
)
for source in data: for source in data:
df = data[source] df = data[source]
path, folder = output_df( path, folder = output_df(
@@ -106,6 +125,65 @@ class TestSuiteBundle:
pass pass
def compare_current_with_last_candle(self, exchange, assets, end_dt,
freq, data_frequency, data_portal):
"""
Creates DataFrames from the bundle and exchange for the specified
data set.
Parameters
----------
exchange: Exchange
assets
end_dt
bar_count
freq
data_frequency
data_portal
Returns
-------
"""
data = dict()
assets = sorted(assets, key=lambda a: a.symbol)
log_catcher = TestHandler()
with log_catcher:
symbols = [asset.symbol for asset in assets]
print(
'comparing data for {}/{} with {} timeframe on {}'.format(
exchange.name, symbols, freq, end_dt
)
)
data['candle'] = data_portal.get_history_window(
assets=assets,
end_dt=end_dt,
bar_count=1,
frequency=freq,
field='close',
data_frequency=data_frequency,
)
set_print_settings()
print(
'the bundle first / last row:\n{}'.format(
data['candle'].iloc[[-1]]
)
)
current = data_portal.get_spot_value(
assets=assets,
field='close',
dt=end_dt,
data_frequency=data_frequency,
)
data['current'] = pd.Series(data=current, index=assets)
print(
'the current price:\n{}'.format(
data['current']
)
)
pass
def test_validate_bundles(self): def test_validate_bundles(self):
# exchange_population = 3 # exchange_population = 3
asset_population = 3 asset_population = 3
@@ -139,6 +217,7 @@ class TestSuiteBundle:
if end_dt is None or asset_end_dt < end_dt: if end_dt is None or asset_end_dt < end_dt:
end_dt = asset_end_dt end_dt = asset_end_dt
end_dt = end_dt + timedelta(minutes=3)
dt_range = pd.date_range( dt_range = pd.date_range(
end=end_dt, periods=bar_count, freq=freq end=end_dt, periods=bar_count, freq=freq
) )
@@ -152,3 +231,45 @@ class TestSuiteBundle:
data_portal=data_portal, data_portal=data_portal,
) )
pass pass
def test_validate_last_candle(self):
# exchange_population = 3
asset_population = 3
data_frequency = random.choice(['minute'])
# bundle = 'dailyBundle' if data_frequency
# == 'daily' else 'minuteBundle'
# exchanges = select_random_exchanges(
# population=exchange_population,
# features=[bundle],
# ) # Type: list[Exchange]
exchanges = [get_exchange('poloniex', skip_init=True)]
data_portal = TestSuiteBundle.get_data_portal(exchanges)
for exchange in exchanges:
exchange.init()
frequencies = exchange.get_candle_frequencies(data_frequency)
freq = random.sample(frequencies, 1)[0]
assets = select_random_assets(
exchange.assets, asset_population
)
end_dt = None
for asset in assets:
attribute = 'end_{}'.format(data_frequency)
asset_end_dt = getattr(asset, attribute)
if end_dt is None or asset_end_dt < end_dt:
end_dt = asset_end_dt
end_dt = end_dt + timedelta(minutes=3)
self.compare_current_with_last_candle(
exchange=exchange,
assets=assets,
end_dt=end_dt,
freq=freq,
data_frequency=data_frequency,
data_portal=data_portal,
)
pass