mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-22 12:40:30 +08:00
Compare commits
40
Commits
0.3.9
...
unit-tests
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c571c1c2ae | ||
|
|
f279101393 | ||
|
|
96a27d083c | ||
|
|
f995f451a7 | ||
|
|
a3838fc00f | ||
|
|
4db8131397 | ||
|
|
207bce6216 | ||
|
|
8fb8b80a12 | ||
|
|
b762689225 | ||
|
|
7bbb6e0b42 | ||
|
|
b587804e3e | ||
|
|
5660247da2 | ||
|
|
fc2c44a6b7 | ||
|
|
4eb8a6eb0f | ||
|
|
c8eaa11f80 | ||
|
|
55a9d76b9b | ||
|
|
7abd992d17 | ||
|
|
daeccaed36 | ||
|
|
25358a4077 | ||
|
|
7cf4f84e89 | ||
|
|
b63199e4e1 | ||
|
|
4af08be7e8 | ||
|
|
606148e19c | ||
|
|
d38e560265 | ||
|
|
ffd1bc07cc | ||
|
|
dfcfe5a370 | ||
|
|
803823eac0 | ||
|
|
5a18e09730 | ||
|
|
dd41f8c006 | ||
|
|
7a2a4817fe | ||
|
|
105522e5ab | ||
|
|
a52e201f86 | ||
|
|
697ff54125 | ||
|
|
4bcd34bd78 | ||
|
|
64c52c7a3c | ||
|
|
1c143eb9ea | ||
|
|
1da4ccfb8c | ||
|
|
a61b22b821 | ||
|
|
d07e0edd88 | ||
|
|
c2821ab77b |
+35
-13
@@ -396,11 +396,15 @@ cdef class Future(Asset):
|
||||
|
||||
cdef class TradingPair(Asset):
|
||||
cdef readonly float leverage
|
||||
cdef readonly object market_currency
|
||||
cdef readonly object quote_currency
|
||||
cdef readonly object base_currency
|
||||
cdef readonly object end_daily
|
||||
cdef readonly object end_minute
|
||||
cdef readonly object exchange_symbol
|
||||
cdef readonly float maker
|
||||
cdef readonly float taker
|
||||
cdef readonly int trading_state
|
||||
cdef readonly object data_source
|
||||
|
||||
_kwargnames = frozenset({
|
||||
'sid',
|
||||
@@ -413,12 +417,16 @@ cdef class TradingPair(Asset):
|
||||
'exchange',
|
||||
'exchange_full',
|
||||
'leverage',
|
||||
'market_currency',
|
||||
'quote_currency',
|
||||
'base_currency',
|
||||
'end_daily',
|
||||
'end_minute',
|
||||
'exchange_symbol',
|
||||
'min_trade_size'
|
||||
'min_trade_size',
|
||||
'maker',
|
||||
'taker',
|
||||
'trading_state',
|
||||
'data_source'
|
||||
})
|
||||
def __init__(self,
|
||||
object symbol,
|
||||
@@ -434,10 +442,14 @@ cdef class TradingPair(Asset):
|
||||
object first_traded=None,
|
||||
object auto_close_date=None,
|
||||
object exchange_full=None,
|
||||
object min_trade_size=None):
|
||||
float min_trade_size=0.0001,
|
||||
float maker=0.0015,
|
||||
float taker=0.0025,
|
||||
int trading_state=0,
|
||||
object data_source='catalyst'):
|
||||
"""
|
||||
Replicates the Asset constructor with some built-in conventions
|
||||
and a new 'leverage' attribute.
|
||||
and adds properties for leverage and fees.
|
||||
|
||||
Symbol
|
||||
------
|
||||
@@ -469,8 +481,6 @@ cdef class TradingPair(Asset):
|
||||
highest volume and market cap generally benefit from high leverage.
|
||||
New currencies from ICO generally cannot be leveraged.
|
||||
|
||||
The leverage value is either None or and integer.
|
||||
|
||||
Leverage allows you to open a larger position with a smaller amount
|
||||
of funds. For example, if you open a $5,000 position in BTC/USD
|
||||
with 5:1 leverage, only one-fifth of this amount, or $1000, will be
|
||||
@@ -480,6 +490,11 @@ cdef class TradingPair(Asset):
|
||||
the position. If you open with 1:1 leverage, $5,000 of your balance
|
||||
will be tied to the position.
|
||||
|
||||
Fees
|
||||
----
|
||||
Exchanges generally charge a taker (taking from the order book) or
|
||||
maker (adding to the order book) fee.
|
||||
|
||||
:param symbol:
|
||||
:param exchange:
|
||||
:param start_date:
|
||||
@@ -494,11 +509,14 @@ cdef class TradingPair(Asset):
|
||||
:param auto_close_date:
|
||||
:param exchange_full:
|
||||
:param min_trade_size:
|
||||
:param maker:
|
||||
:param taker:
|
||||
:param data_source
|
||||
"""
|
||||
|
||||
symbol = symbol.lower()
|
||||
try:
|
||||
self.market_currency, self.base_currency = symbol.split('_')
|
||||
self.base_currency, self.quote_currency = symbol.split('_')
|
||||
except Exception as e:
|
||||
raise InvalidSymbolError(symbol=symbol, error=e)
|
||||
|
||||
@@ -512,7 +530,7 @@ cdef class TradingPair(Asset):
|
||||
asset_name = ' / '.join(symbol.split('_')).upper()
|
||||
|
||||
if start_date is None:
|
||||
start_date = pd.Timestamp.utcnow()
|
||||
start_date = pd.to_datetime('2009-1-1', utc=True)
|
||||
|
||||
if end_date is None:
|
||||
end_date = pd.Timestamp.utcnow() + timedelta(days=365)
|
||||
@@ -527,19 +545,23 @@ cdef class TradingPair(Asset):
|
||||
first_traded=first_traded,
|
||||
auto_close_date=auto_close_date,
|
||||
exchange_full=exchange_full,
|
||||
min_trade_size=min_trade_size
|
||||
min_trade_size=min_trade_size,
|
||||
)
|
||||
|
||||
self.maker = maker
|
||||
self.taker = taker
|
||||
self.leverage = leverage
|
||||
self.end_daily = end_daily
|
||||
self.end_minute = end_minute
|
||||
self.exchange_symbol = exchange_symbol
|
||||
self.trading_state = trading_state
|
||||
self.data_source = data_source
|
||||
|
||||
def __repr__(self):
|
||||
return 'Trading Pair {symbol}({sid}) Exchange: {exchange}, ' \
|
||||
'Introduced On: {start_date}, ' \
|
||||
'Market Currency: {market_currency}, ' \
|
||||
'Base Currency: {base_currency}, ' \
|
||||
'Quote Currency: {quote_currency}, ' \
|
||||
'Exchange Leverage: {leverage}, ' \
|
||||
'Minimum Trade Size: {min_trade_size} ' \
|
||||
'Last daily ingestion: {end_daily} ' \
|
||||
@@ -548,7 +570,7 @@ cdef class TradingPair(Asset):
|
||||
sid=self.sid,
|
||||
exchange=self.exchange,
|
||||
start_date=self.start_date,
|
||||
market_currency=self.market_currency,
|
||||
quote_currency=self.quote_currency,
|
||||
base_currency=self.base_currency,
|
||||
leverage=self.leverage,
|
||||
min_trade_size=self.min_trade_size,
|
||||
@@ -578,7 +600,7 @@ cdef class TradingPair(Asset):
|
||||
-------
|
||||
boolean: whether the asset's exchange is open at the given minute.
|
||||
"""
|
||||
#TODO: consider implementing to spot holds
|
||||
#TODO: make more dymanic to catch holds
|
||||
return True
|
||||
|
||||
cpdef __reduce__(self):
|
||||
|
||||
@@ -7,7 +7,8 @@ import logbook
|
||||
For example, if you want to see the DEBUG messages, run:
|
||||
$ export CATALYST_LOG_LEVEL=10
|
||||
'''
|
||||
LOG_LEVEL = int(os.environ.get('CATALYST_LOG_LEVEL', logbook.INFO))
|
||||
# LOG_LEVEL = int(os.environ.get('CATALYST_LOG_LEVEL', logbook.INFO))
|
||||
LOG_LEVEL = logbook.DEBUG
|
||||
|
||||
SYMBOLS_URL = 'https://s3.amazonaws.com/enigmaco/catalyst-exchanges/' \
|
||||
'{exchange}/symbols.json'
|
||||
|
||||
@@ -142,8 +142,10 @@ def load_crypto_market_data(trading_day=None, trading_days=None,
|
||||
if exchange is None:
|
||||
# This is exceptional, since placing the import at the module scope
|
||||
# breaks things and it's only needed here
|
||||
from catalyst.exchange.poloniex.poloniex import Poloniex
|
||||
exchange = Poloniex('', '', '')
|
||||
from catalyst.exchange.factory import get_exchange
|
||||
exchange = get_exchange(
|
||||
exchange_name='poloniex', base_currency='usdt'
|
||||
)
|
||||
|
||||
benchmark_asset = exchange.get_asset(bm_symbol)
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
An overview of most of the trading strategies in this folder can be found in the
|
||||
`Examples Algorithms <https://enigmampc.github.io/catalyst/example-algos.html>`_
|
||||
section of our documentation website.
|
||||
@@ -83,15 +83,15 @@ def place_orders(context, amount, buying_price, selling_price, action):
|
||||
else:
|
||||
raise ValueError('invalid order action')
|
||||
|
||||
base_currency = enter_exchange.base_currency
|
||||
base_currency_amount = enter_exchange.portfolio.cash
|
||||
quote_currency = enter_exchange.quote_currency
|
||||
quote_currency_amount = enter_exchange.portfolio.cash
|
||||
|
||||
exit_balances = exit_exchange.get_balances()
|
||||
exit_currency = context.trading_pairs[
|
||||
context.selling_exchange].market_currency
|
||||
context.selling_exchange].quote_currency
|
||||
|
||||
if exit_currency in exit_balances:
|
||||
market_currency_amount = exit_balances[exit_currency]
|
||||
quote_currency_amount = exit_balances[exit_currency]
|
||||
else:
|
||||
log.warn(
|
||||
'the selling exchange {exchange_name} does not hold '
|
||||
@@ -102,25 +102,25 @@ def place_orders(context, amount, buying_price, selling_price, action):
|
||||
)
|
||||
return
|
||||
|
||||
if base_currency_amount < (amount * entry_price):
|
||||
adj_amount = base_currency_amount / entry_price
|
||||
if quote_currency_amount < (amount * entry_price):
|
||||
adj_amount = quote_currency_amount / entry_price
|
||||
log.warn(
|
||||
'not enough {base_currency} ({base_currency_amount}) to buy '
|
||||
'not enough {quote_currency} ({quote_currency_amount}) to buy '
|
||||
'{amount}, adjusting the amount to {adj_amount}'.format(
|
||||
base_currency=base_currency,
|
||||
base_currency_amount=base_currency_amount,
|
||||
quote_currency=quote_currency,
|
||||
quote_currency_amount=quote_currency_amount,
|
||||
amount=amount,
|
||||
adj_amount=adj_amount
|
||||
)
|
||||
)
|
||||
amount = adj_amount
|
||||
|
||||
elif market_currency_amount < amount:
|
||||
elif quote_currency_amount < amount:
|
||||
log.warn(
|
||||
'not enough {currency} ({currency_amount}) to sell '
|
||||
'{amount}, aborting'.format(
|
||||
currency=exit_currency,
|
||||
currency_amount=market_currency_amount,
|
||||
currency_amount=quote_currency_amount,
|
||||
amount=amount
|
||||
)
|
||||
)
|
||||
@@ -270,6 +270,6 @@ run_algorithm(
|
||||
exchange_name='poloniex,bitfinex',
|
||||
live=True,
|
||||
algo_namespace=algo_namespace,
|
||||
base_currency='btc',
|
||||
quote_currency='btc',
|
||||
live_graph=False
|
||||
)
|
||||
|
||||
@@ -15,18 +15,15 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from catalyst.api import (
|
||||
order_target_value,
|
||||
symbol,
|
||||
record,
|
||||
cancel_order,
|
||||
get_open_orders,
|
||||
)
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import (order_target_value, symbol, record,
|
||||
cancel_order, get_open_orders, )
|
||||
|
||||
|
||||
def initialize(context):
|
||||
context.ASSET_NAME = 'btc_usdt'
|
||||
context.ASSET_NAME = 'btc_usd'
|
||||
context.TARGET_HODL_RATIO = 0.8
|
||||
context.RESERVE_RATIO = 1.0 - context.TARGET_HODL_RATIO
|
||||
|
||||
@@ -58,6 +55,7 @@ def handle_data(context, data):
|
||||
|
||||
# Check if still buying and could (approximately) afford another purchase
|
||||
if context.is_buying and cash > price:
|
||||
print('buying')
|
||||
# Place order to make position in asset equal to target_hodl_value
|
||||
order_target_value(
|
||||
context.asset,
|
||||
@@ -76,27 +74,27 @@ 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(611)
|
||||
results[['portfolio_value']].plot(ax=ax1)
|
||||
ax1.set_ylabel('Portfolio Value (USD)')
|
||||
ax1.set_ylabel('Portfolio\nValue\n(USD)')
|
||||
|
||||
ax2 = plt.subplot(612, sharex=ax1)
|
||||
ax2.set_ylabel('{asset} (USD)'.format(asset=context.ASSET_NAME))
|
||||
ax2.set_ylabel('{asset}\n(USD)'.format(asset=context.ASSET_NAME))
|
||||
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,
|
||||
ax2.scatter(
|
||||
buys.index.to_pydatetime(),
|
||||
results.price[buys.index],
|
||||
'^',
|
||||
markersize=10,
|
||||
color='g',
|
||||
marker='^',
|
||||
s=100,
|
||||
c='g',
|
||||
label=''
|
||||
)
|
||||
|
||||
ax3 = plt.subplot(613, sharex=ax1)
|
||||
@@ -123,11 +121,11 @@ def analyze(context=None, results=None):
|
||||
'algorithm',
|
||||
'benchmark',
|
||||
]].plot(ax=ax5)
|
||||
ax5.set_ylabel('Percent Change')
|
||||
ax5.set_ylabel('Percent\nChange')
|
||||
|
||||
ax6 = plt.subplot(616, sharex=ax1)
|
||||
results[['volume']].plot(ax=ax6)
|
||||
ax6.set_ylabel('Volume (mCoins/5min)')
|
||||
ax6.set_ylabel('Volume')
|
||||
|
||||
plt.legend(loc=3)
|
||||
|
||||
@@ -135,3 +133,17 @@ def analyze(context=None, results=None):
|
||||
plt.gcf().set_size_inches(18, 8)
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_algorithm(
|
||||
capital_base=10000,
|
||||
data_frequency='daily',
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bitfinex',
|
||||
algo_namespace='buy_and_hodl',
|
||||
base_currency='usd',
|
||||
start=pd.to_datetime('2015-03-01', utc=True),
|
||||
end=pd.to_datetime('2017-10-31', utc=True),
|
||||
)
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
https://enigmampc.github.io/catalyst/beginner-tutorial.html
|
||||
|
||||
Run this example, by executing the following from your terminal:
|
||||
catalyst run -f buy_btc_simple.py -x bitfinex --start 2016-1-1 --end 2017-9-30 -o buy_btc_simple_out.pickle
|
||||
catalyst ingest-exchange -x bitfinex -f daily -i btc_usdt
|
||||
catalyst run -f buy_btc_simple.py -x bitfinex --start 2016-1-1 --end 2017-9-30 -o buy_btc_simple_out.pickle
|
||||
|
||||
If you want to run this code using another exchange, make sure that
|
||||
the asset is available on that exchange. For example, if you were to run
|
||||
@@ -12,7 +13,7 @@
|
||||
context.asset = symbol('btc_usdt') # note 'usdt' instead of 'usd'
|
||||
|
||||
and specify exchange poloniex as follows:
|
||||
|
||||
catalyst ingest-exchange -x poloniex -f daily -i btc_usdt
|
||||
catalyst run -f buy_btc_simple.py -x poloniex --start 2016-1-1 --end 2017-9-30 -o buy_btc_simple_out.pickle
|
||||
|
||||
To see which assets are available on each exchange, visit:
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from logbook import Logger
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import (order, record, symbol, order_target_percent,
|
||||
get_open_orders)
|
||||
from catalyst.exchange.stats_utils import extract_transactions
|
||||
|
||||
NAMESPACE = 'dual_moving_average'
|
||||
log = Logger(NAMESPACE)
|
||||
|
||||
def initialize(context):
|
||||
context.i = 0
|
||||
context.asset = symbol('ltc_usd')
|
||||
context.base_price = None
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# define the windows for the moving averages
|
||||
short_window = 50
|
||||
long_window = 200
|
||||
|
||||
# Skip as many bars as long_window to properly compute the average
|
||||
context.i += 1
|
||||
if context.i < long_window:
|
||||
return
|
||||
|
||||
# Compute moving averages calling data.history() for each
|
||||
# moving average with the appropriate parameters. We choose to use
|
||||
# minute bars for this simulation -> freq="1m"
|
||||
# Returns a pandas dataframe.
|
||||
short_mavg = data.history(context.asset, 'price',
|
||||
bar_count=short_window, frequency="1m").mean()
|
||||
long_mavg = data.history(context.asset, 'price',
|
||||
bar_count=long_window, frequency="1m").mean()
|
||||
|
||||
# Let's keep the price of our asset in a more handy variable
|
||||
price = data.current(context.asset, 'price')
|
||||
|
||||
# If base_price is not set, we use the current value. This is the
|
||||
# price at the first bar which we reference to calculate price_change.
|
||||
if context.base_price is None:
|
||||
context.base_price = price
|
||||
price_change = (price - context.base_price) / context.base_price
|
||||
|
||||
# Save values for later inspection
|
||||
record(price=price,
|
||||
cash=context.portfolio.cash,
|
||||
price_change=price_change,
|
||||
short_mavg=short_mavg,
|
||||
long_mavg=long_mavg)
|
||||
|
||||
# Since we are using limit orders, some orders may not execute immediately
|
||||
# we wait until all orders are executed before considering more trades.
|
||||
orders = get_open_orders(context.asset)
|
||||
if len(orders) > 0:
|
||||
return
|
||||
|
||||
# Exit if we cannot trade
|
||||
if not data.can_trade(context.asset):
|
||||
return
|
||||
|
||||
# We check what's our position on our portfolio and trade accordingly
|
||||
pos_amount = context.portfolio.positions[context.asset].amount
|
||||
|
||||
# Trading logic
|
||||
if short_mavg > long_mavg and pos_amount == 0:
|
||||
# we buy 100% of our portfolio for this asset
|
||||
order_target_percent(context.asset, 1)
|
||||
elif short_mavg < long_mavg and pos_amount > 0:
|
||||
# we sell all our positions for this asset
|
||||
order_target_percent(context.asset, 0)
|
||||
|
||||
|
||||
def analyze(context, perf):
|
||||
|
||||
# Get the base_currency that was passed as a parameter to the simulation
|
||||
base_currency = context.exchanges.values()[0].base_currency.upper()
|
||||
|
||||
# First chart: Plot portfolio value using base_currency
|
||||
ax1 = plt.subplot(411)
|
||||
perf.loc[:, ['portfolio_value']].plot(ax=ax1)
|
||||
ax1.legend_.remove()
|
||||
ax1.set_ylabel('Portfolio Value\n({})'.format(base_currency))
|
||||
start, end = ax1.get_ylim()
|
||||
ax1.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
# Second chart: Plot asset price, moving averages and buys/sells
|
||||
ax2 = plt.subplot(412, sharex=ax1)
|
||||
perf.loc[:, ['price','short_mavg','long_mavg']].plot(ax=ax2, label='Price')
|
||||
ax2.legend_.remove()
|
||||
ax2.set_ylabel('{asset}\n({base})'.format(
|
||||
asset = context.asset.symbol,
|
||||
base = base_currency
|
||||
))
|
||||
start, end = ax2.get_ylim()
|
||||
ax2.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
transaction_df = extract_transactions(perf)
|
||||
if not transaction_df.empty:
|
||||
buy_df = transaction_df[transaction_df['amount'] > 0]
|
||||
sell_df = transaction_df[transaction_df['amount'] < 0]
|
||||
ax2.scatter(
|
||||
buy_df.index.to_pydatetime(),
|
||||
perf.loc[buy_df.index, 'price'],
|
||||
marker='^',
|
||||
s=100,
|
||||
c='green',
|
||||
label=''
|
||||
)
|
||||
ax2.scatter(
|
||||
sell_df.index.to_pydatetime(),
|
||||
perf.loc[sell_df.index, 'price'],
|
||||
marker='v',
|
||||
s=100,
|
||||
c='red',
|
||||
label=''
|
||||
)
|
||||
|
||||
# Third chart: Compare percentage change between our portfolio
|
||||
# and the price of the asset
|
||||
ax3 = plt.subplot(413, sharex=ax1)
|
||||
perf.loc[:, ['algorithm_period_return', 'price_change']].plot(ax=ax3)
|
||||
ax3.legend_.remove()
|
||||
ax3.set_ylabel('Percent Change')
|
||||
start, end = ax3.get_ylim()
|
||||
ax3.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
# Fourth chart: Plot our cash
|
||||
ax4 = plt.subplot(414, sharex=ax1)
|
||||
perf.cash.plot(ax=ax4)
|
||||
ax4.set_ylabel('Cash\n({})'.format(base_currency))
|
||||
start, end = ax4.get_ylim()
|
||||
ax4.yaxis.set_ticks(np.arange(0, end, end/5))
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
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),
|
||||
)
|
||||
@@ -5,6 +5,7 @@ import os
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib
|
||||
from logbook import Logger
|
||||
@@ -31,13 +32,13 @@ def initialize(context):
|
||||
# trading pairs) you want to backtest. You'll also want to define any
|
||||
# parameters or values you're going to use.
|
||||
|
||||
# In our example, we're looking at Ether in USD Tether.
|
||||
# In our example, we're looking at Neo in Ether.
|
||||
context.neo_eth = symbol('neo_eth')
|
||||
context.base_price = None
|
||||
context.current_day = None
|
||||
|
||||
context.RSI_OVERSOLD = 50
|
||||
context.RSI_OVERBOUGHT = 80
|
||||
context.RSI_OVERSOLD = 55
|
||||
context.RSI_OVERBOUGHT = 82
|
||||
context.CANDLE_SIZE = '5T'
|
||||
|
||||
context.start_time = time.time()
|
||||
@@ -160,13 +161,13 @@ def analyze(context=None, perf=None):
|
||||
# Plot the portfolio value over time.
|
||||
ax1 = plt.subplot(611)
|
||||
perf.loc[:, 'portfolio_value'].plot(ax=ax1)
|
||||
ax1.set_ylabel('Portfolio Value ({})'.format(base_currency))
|
||||
ax1.set_ylabel('Portfolio\nValue\n({})'.format(base_currency))
|
||||
|
||||
# Plot the price increase or decrease over time.
|
||||
ax2 = plt.subplot(612, sharex=ax1)
|
||||
perf.loc[:, 'price'].plot(ax=ax2, label='Price')
|
||||
|
||||
ax2.set_ylabel('{asset} ({base})'.format(
|
||||
ax2.set_ylabel('{asset}\n({base})'.format(
|
||||
asset=context.neo_eth.symbol, base=base_currency
|
||||
))
|
||||
|
||||
@@ -195,18 +196,19 @@ def analyze(context=None, perf=None):
|
||||
perf.loc[:, 'cash'].plot(
|
||||
ax=ax4, label='Base Currency ({})'.format(base_currency)
|
||||
)
|
||||
ax4.set_ylabel('Cash ({})'.format(base_currency))
|
||||
ax4.set_ylabel('Cash\n({})'.format(base_currency))
|
||||
|
||||
perf['algorithm'] = perf.loc[:, 'algorithm_period_return']
|
||||
|
||||
ax5 = plt.subplot(614, sharex=ax1)
|
||||
perf.loc[:, ['algorithm', 'price_change']].plot(ax=ax5)
|
||||
ax5.set_ylabel('Percent Change')
|
||||
ax5.set_ylabel('Percent\nChange')
|
||||
|
||||
ax6 = plt.subplot(615, sharex=ax1)
|
||||
perf.loc[:, 'rsi'].plot(ax=ax6, label='RSI')
|
||||
ax6.axhline(70, color='darkgoldenrod')
|
||||
ax6.axhline(30, color='darkgoldenrod')
|
||||
ax6.set_ylabel('RSI')
|
||||
ax6.axhline(context.RSI_OVERBOUGHT, color='darkgoldenrod')
|
||||
ax6.axhline(context.RSI_OVERSOLD, color='darkgoldenrod')
|
||||
|
||||
if not transaction_df.empty:
|
||||
ax6.scatter(
|
||||
@@ -226,6 +228,8 @@ def analyze(context=None, perf=None):
|
||||
label=''
|
||||
)
|
||||
plt.legend(loc=3)
|
||||
start, end = ax6.get_ylim()
|
||||
ax6.yaxis.set_ticks(np.arange(0, end, end/5))
|
||||
|
||||
# Show the plot.
|
||||
plt.gcf().set_size_inches(18, 8)
|
||||
@@ -235,7 +239,7 @@ def analyze(context=None, perf=None):
|
||||
|
||||
if __name__ == '__main__':
|
||||
# The execution mode: backtest or live
|
||||
MODE = 'backtest'
|
||||
MODE = 'live'
|
||||
|
||||
if MODE == 'backtest':
|
||||
folder = os.path.join(
|
||||
@@ -245,16 +249,16 @@ if __name__ == '__main__':
|
||||
|
||||
timestr = time.strftime('%Y%m%d-%H%M%S')
|
||||
out = os.path.join(folder, '{}.p'.format(timestr))
|
||||
# catalyst run -f catalyst/examples/mean_reversion_simple.py -x poloniex -s 2017-10-1 -e 2017-11-10 -c usdt -n mean-reversion --data-frequency minute --capital-base 10000
|
||||
# catalyst run -f catalyst/examples/mean_reversion_simple.py -x bitfinex -s 2017-10-1 -e 2017-11-10 -c usdt -n mean-reversion --data-frequency minute --capital-base 10000
|
||||
run_algorithm(
|
||||
capital_base=10000,
|
||||
capital_base=0.1,
|
||||
data_frequency='minute',
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bitfinex',
|
||||
algo_namespace=NAMESPACE,
|
||||
base_currency='usd',
|
||||
base_currency='eth',
|
||||
start=pd.to_datetime('2017-10-01', utc=True),
|
||||
end=pd.to_datetime('2017-11-10', utc=True),
|
||||
output=out
|
||||
@@ -263,7 +267,7 @@ if __name__ == '__main__':
|
||||
|
||||
elif MODE == 'live':
|
||||
run_algorithm(
|
||||
capital_base=0.5,
|
||||
capital_base=0.1,
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
'''Use this code to execute a portfolio optimization model. This code
|
||||
will select the portfolio with the maximum Sharpe Ratio. The parameters
|
||||
are set to use 180 days of historical data and rebalance every 30 days.
|
||||
|
||||
This is the code used in the following article:
|
||||
https://blog.enigma.co/markowitz-portfolio-optimization-for-cryptocurrencies-in-catalyst-b23c38652556
|
||||
|
||||
You can run this code using the Python interpreter:
|
||||
|
||||
$ python portfolio_optimization.py
|
||||
'''
|
||||
|
||||
from __future__ import division
|
||||
import os
|
||||
import pytz
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from scipy.optimize import minimize
|
||||
import matplotlib.pyplot as plt
|
||||
from datetime import datetime
|
||||
|
||||
from catalyst.api import record, symbol, symbols, order_target_percent
|
||||
from catalyst.utils.run_algo import run_algorithm
|
||||
|
||||
np.set_printoptions(threshold='nan', suppress=True)
|
||||
|
||||
|
||||
def initialize(context):
|
||||
# Portfolio assets list
|
||||
context.assets = symbols('btc_usdt', 'eth_usdt', 'ltc_usdt', 'dash_usdt',
|
||||
'xmr_usdt')
|
||||
context.nassets = len(context.assets)
|
||||
# Set the time window that will be used to compute expected return
|
||||
# and asset correlations
|
||||
context.window = 180
|
||||
# Set the number of days between each portfolio rebalancing
|
||||
context.rebalance_period = 30
|
||||
context.i = 0
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# Only rebalance at the beggining of the algorithm execution and
|
||||
# every multiple of the rebalance period
|
||||
if context.i == 0 or context.i%context.rebalance_period == 0:
|
||||
n = context.window
|
||||
prices = data.history(context.assets, fields='price',
|
||||
bar_count=n+1, frequency='1d')
|
||||
pr = np.asmatrix(prices)
|
||||
t_prices = prices.iloc[1:n+1]
|
||||
t_val = t_prices.values
|
||||
tminus_prices = prices.iloc[0:n]
|
||||
tminus_val = tminus_prices.values
|
||||
# Compute daily returns (r)
|
||||
r = np.asmatrix(t_val/tminus_val-1)
|
||||
# Compute the expected returns of each asset with the average
|
||||
# daily return for the selected time window
|
||||
m = np.asmatrix(np.mean(r, axis=0))
|
||||
# ###
|
||||
stds = np.std(r, axis=0)
|
||||
# Compute excess returns matrix (xr)
|
||||
xr = r - m
|
||||
# Matrix algebra to get variance-covariance matrix
|
||||
cov_m = np.dot(np.transpose(xr),xr)/n
|
||||
# Compute asset correlation matrix (informative only)
|
||||
corr_m = cov_m/np.dot(np.transpose(stds),stds)
|
||||
|
||||
# Define portfolio optimization parameters
|
||||
n_portfolios = 50000
|
||||
results_array = np.zeros((3+context.nassets,n_portfolios))
|
||||
for p in xrange(n_portfolios):
|
||||
weights = np.random.random(context.nassets)
|
||||
weights /= np.sum(weights)
|
||||
w = np.asmatrix(weights)
|
||||
p_r = np.sum(np.dot(w,np.transpose(m)))*365
|
||||
p_std = np.sqrt(np.dot(np.dot(w,cov_m),np.transpose(w)))*np.sqrt(365)
|
||||
|
||||
#store results in results array
|
||||
results_array[0,p] = p_r
|
||||
results_array[1,p] = p_std
|
||||
#store Sharpe Ratio (return / volatility) - risk free rate element
|
||||
#excluded for simplicity
|
||||
results_array[2,p] = results_array[0,p] / results_array[1,p]
|
||||
i = 0
|
||||
for iw in weights:
|
||||
results_array[3+i,p] = weights[i]
|
||||
i += 1
|
||||
|
||||
#convert results array to Pandas DataFrame
|
||||
results_frame = pd.DataFrame(np.transpose(results_array),
|
||||
columns=['r','stdev','sharpe']+context.assets)
|
||||
#locate position of portfolio with highest Sharpe Ratio
|
||||
max_sharpe_port = results_frame.iloc[results_frame['sharpe'].idxmax()]
|
||||
#locate positon of portfolio with minimum standard deviation
|
||||
min_vol_port = results_frame.iloc[results_frame['stdev'].idxmin()]
|
||||
|
||||
#order optimal weights for each asset
|
||||
for asset in context.assets:
|
||||
if data.can_trade(asset):
|
||||
order_target_percent(asset, max_sharpe_port[asset])
|
||||
|
||||
#create scatter plot coloured by Sharpe Ratio
|
||||
plt.scatter(results_frame.stdev,results_frame.r,c=results_frame.sharpe,cmap='RdYlGn')
|
||||
plt.xlabel('Volatility')
|
||||
plt.ylabel('Returns')
|
||||
plt.colorbar()
|
||||
#plot red star to highlight position of portfolio with highest Sharpe Ratio
|
||||
plt.scatter(max_sharpe_port[1],max_sharpe_port[0],marker='o',color='b',s=200)
|
||||
#plot green star to highlight position of minimum variance portfolio
|
||||
plt.show()
|
||||
print(max_sharpe_port)
|
||||
record(pr=pr,r=r, m=m, stds=stds ,max_sharpe_port=max_sharpe_port, corr_m=corr_m)
|
||||
context.i += 1
|
||||
|
||||
|
||||
def analyze(context=None, results=None):
|
||||
# Form DataFrame with selected data
|
||||
data = results[['pr','r','m','stds','max_sharpe_port','corr_m','portfolio_value']]
|
||||
|
||||
# Save results in CSV file
|
||||
filename = os.path.splitext(os.path.basename(__file__))[0]
|
||||
data.to_csv(filename + '.csv')
|
||||
|
||||
|
||||
# Bitcoin data is available from 2015-3-2. Dates vary for other tokens.
|
||||
start = datetime(2017, 1, 1, 0, 0, 0, 0, pytz.utc)
|
||||
end = datetime(2017, 8, 16, 0, 0, 0, 0, pytz.utc)
|
||||
results = run_algorithm(initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
start=start,
|
||||
end=end,
|
||||
exchange_name='poloniex',
|
||||
capital_base=100000, )
|
||||
@@ -9,7 +9,7 @@ from catalyst.exchange.stats_utils import get_pretty_stats, \
|
||||
|
||||
def initialize(context):
|
||||
print('initializing')
|
||||
context.asset = symbol('neo_usd')
|
||||
context.asset = symbol('neo_eth')
|
||||
context.base_price = None
|
||||
|
||||
|
||||
@@ -19,17 +19,14 @@ def handle_data(context, data):
|
||||
price = data.current(context.asset, 'close')
|
||||
print('got price {price}'.format(price=price))
|
||||
|
||||
try:
|
||||
prices = data.history(
|
||||
context.asset,
|
||||
fields='price',
|
||||
bar_count=14,
|
||||
frequency='15T'
|
||||
)
|
||||
rsi = talib.RSI(prices.values, timeperiod=14)[-1]
|
||||
print('got rsi: {}'.format(rsi))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
prices = data.history(
|
||||
context.asset,
|
||||
fields='price',
|
||||
bar_count=20,
|
||||
frequency='15T'
|
||||
)
|
||||
rsi = talib.RSI(prices.values, timeperiod=14)[-1]
|
||||
print('got rsi: {}'.format(rsi))
|
||||
|
||||
# If base_price is not set, we use the current value. This is the
|
||||
# price at the first bar which we reference to calculate price_change.
|
||||
@@ -126,8 +123,9 @@ run_algorithm(
|
||||
# initialize=initialize,
|
||||
# handle_data=handle_data,
|
||||
# analyze=None,
|
||||
# exchange_name='poloniex',
|
||||
# exchange_name='binance',
|
||||
# live=True,
|
||||
# algo_namespace='simple_loop',
|
||||
# base_currency='eth',
|
||||
# live_graph=False
|
||||
# live_graph=False,
|
||||
# )
|
||||
|
||||
@@ -0,0 +1,600 @@
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
import ccxt
|
||||
import pandas as pd
|
||||
from ccxt import ExchangeNotAvailable
|
||||
from six import string_types
|
||||
|
||||
from catalyst.finance.order import Order, ORDER_STATUS
|
||||
|
||||
from catalyst.algorithm import MarketOrder
|
||||
from catalyst.assets._assets import TradingPair
|
||||
from logbook import Logger
|
||||
|
||||
from catalyst.constants import LOG_LEVEL
|
||||
from catalyst.exchange.exchange import Exchange, ExchangeLimitOrder
|
||||
from catalyst.exchange.exchange_bundle import ExchangeBundle
|
||||
from catalyst.exchange.exchange_errors import InvalidHistoryFrequencyError, \
|
||||
ExchangeSymbolsNotFound, ExchangeRequestError, InvalidOrderStyle, \
|
||||
ExchangeNotFoundError
|
||||
from catalyst.exchange.exchange_utils import mixin_market_params, \
|
||||
from_ms_timestamp
|
||||
|
||||
log = Logger('CCXT', level=LOG_LEVEL)
|
||||
|
||||
SUPPORTED_EXCHANGES = dict(
|
||||
binance=ccxt.binance,
|
||||
bitfinex=ccxt.bitfinex,
|
||||
bittrex=ccxt.bittrex,
|
||||
poloniex=ccxt.poloniex,
|
||||
bitmex=ccxt.bitmex,
|
||||
gdax=ccxt.gdax,
|
||||
)
|
||||
|
||||
|
||||
class CCXT(Exchange):
|
||||
def __init__(self, exchange_name, key, secret, base_currency,
|
||||
portfolio=None):
|
||||
log.debug(
|
||||
'finding {} in CCXT exchanges:\n{}'.format(
|
||||
exchange_name, ccxt.exchanges
|
||||
)
|
||||
)
|
||||
try:
|
||||
# Making instantiation as explicit as possible for code tracking.
|
||||
if exchange_name in SUPPORTED_EXCHANGES:
|
||||
exchange_attr = SUPPORTED_EXCHANGES[exchange_name]
|
||||
|
||||
else:
|
||||
exchange_attr = getattr(ccxt, exchange_name)
|
||||
|
||||
self.api = exchange_attr({
|
||||
'apiKey': key,
|
||||
'secret': secret,
|
||||
})
|
||||
|
||||
except Exception:
|
||||
raise ExchangeNotFoundError(exchange_name=exchange_name)
|
||||
|
||||
self._symbol_maps = [None, None]
|
||||
|
||||
markets_symbols = self.api.load_markets()
|
||||
log.debug('the markets:\n{}'.format(markets_symbols))
|
||||
|
||||
self.name = exchange_name
|
||||
|
||||
self.markets = self.api.fetch_markets()
|
||||
self.load_assets()
|
||||
|
||||
self.base_currency = base_currency
|
||||
self._portfolio = portfolio
|
||||
self.transactions = defaultdict(list)
|
||||
|
||||
self.num_candles_limit = 2000
|
||||
self.max_requests_per_minute = 60
|
||||
self.request_cpt = dict()
|
||||
|
||||
self.bundle = ExchangeBundle(self.name)
|
||||
|
||||
def account(self):
|
||||
return None
|
||||
|
||||
def time_skew(self):
|
||||
return None
|
||||
|
||||
def get_market(self, symbol):
|
||||
"""
|
||||
The CCXT market.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol:
|
||||
The CCXT symbol.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict[str, Object]
|
||||
|
||||
"""
|
||||
s = self.get_symbol(symbol)
|
||||
market = next(
|
||||
(market for market in self.markets if market['symbol'] == s),
|
||||
None,
|
||||
)
|
||||
return market
|
||||
|
||||
def get_symbol(self, asset_or_symbol):
|
||||
"""
|
||||
The CCXT symbol.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset_or_symbol
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
"""
|
||||
symbol = asset_or_symbol if isinstance(
|
||||
asset_or_symbol, string_types
|
||||
) else asset_or_symbol.symbol
|
||||
|
||||
parts = symbol.split('_')
|
||||
return '{}/{}'.format(parts[0].upper(), parts[1].upper())
|
||||
|
||||
def get_catalyst_symbol(self, market_or_symbol):
|
||||
"""
|
||||
The Catalyst symbol.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
market_or_symbol
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
"""
|
||||
if isinstance(market_or_symbol, string_types):
|
||||
parts = market_or_symbol.split('/')
|
||||
return '{}_{}'.format(parts[0].lower(), parts[1].lower())
|
||||
|
||||
else:
|
||||
return '{}_{}'.format(
|
||||
market_or_symbol['base'].lower(),
|
||||
market_or_symbol['quote'].lower(),
|
||||
)
|
||||
|
||||
def get_timeframe(self, freq):
|
||||
"""
|
||||
The CCXT timeframe from the Catalyst frequency.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
freq: str
|
||||
The Catalyst frequency (Pandas convention)
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
|
||||
"""
|
||||
freq_match = re.match(r'([0-9].*)?(m|M|d|D|h|H|T)', freq, re.M | re.I)
|
||||
if freq_match:
|
||||
candle_size = int(freq_match.group(1)) \
|
||||
if freq_match.group(1) else 1
|
||||
|
||||
unit = freq_match.group(2)
|
||||
|
||||
else:
|
||||
raise InvalidHistoryFrequencyError(frequency=freq)
|
||||
|
||||
if unit.lower() == 'd':
|
||||
timeframe = '{}d'.format(candle_size)
|
||||
|
||||
elif unit.lower() == 'm' or unit == 'T':
|
||||
timeframe = '{}m'.format(candle_size)
|
||||
|
||||
elif unit.lower() == 'h' or unit == 'T':
|
||||
timeframe = '{}h'.format(candle_size)
|
||||
|
||||
return timeframe
|
||||
|
||||
def get_candles(self, freq, assets, bar_count=None, start_dt=None,
|
||||
end_dt=None):
|
||||
symbols = self.get_symbols(assets)
|
||||
timeframe = self.get_timeframe(freq)
|
||||
delta = start_dt - pd.to_datetime('1970-1-1', utc=True)
|
||||
ms = int(delta.total_seconds()) * 1000
|
||||
|
||||
candles = dict()
|
||||
for asset in assets:
|
||||
ohlcvs = self.api.fetch_ohlcv(
|
||||
symbol=symbols[0],
|
||||
timeframe=timeframe,
|
||||
since=ms,
|
||||
limit=bar_count,
|
||||
params={}
|
||||
)
|
||||
|
||||
candles[asset] = []
|
||||
for ohlcv in ohlcvs:
|
||||
candles[asset].append(dict(
|
||||
last_traded=pd.to_datetime(ohlcv[0], unit='ms', utc=True),
|
||||
open=ohlcv[1],
|
||||
high=ohlcv[2],
|
||||
low=ohlcv[3],
|
||||
close=ohlcv[4],
|
||||
volume=ohlcv[5]
|
||||
))
|
||||
|
||||
return candles
|
||||
|
||||
def _fetch_symbol_map(self, is_local):
|
||||
try:
|
||||
return self.fetch_symbol_map(is_local)
|
||||
except ExchangeSymbolsNotFound:
|
||||
return None
|
||||
|
||||
def get_asset_defs(self, market):
|
||||
"""
|
||||
The local and Catalyst definitions of the specified market.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
market: dict[str, Object]
|
||||
The CCXT market dicts.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict[str, Object]
|
||||
The asset definition.
|
||||
|
||||
"""
|
||||
asset_defs = []
|
||||
|
||||
for is_local in (False, True):
|
||||
asset_def = self.get_asset_def(market, is_local)
|
||||
asset_defs.append((asset_def, is_local))
|
||||
|
||||
return asset_defs
|
||||
|
||||
def get_asset_def(self, market, is_local=False):
|
||||
"""
|
||||
The asset definition (in symbols.json files) corresponding
|
||||
to the the specified market.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
market: dict[str, Object]
|
||||
The CCXT market dict.
|
||||
is_local
|
||||
Whether to search in local or Catalyst asset definitions.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict[str, Object]
|
||||
The asset definition.
|
||||
|
||||
"""
|
||||
exchange_symbol = market['id']
|
||||
|
||||
symbol_map = self._fetch_symbol_map(is_local)
|
||||
if symbol_map is not None:
|
||||
assets_lower = {k.lower(): v for k, v in symbol_map.items()}
|
||||
key = exchange_symbol.lower()
|
||||
|
||||
asset = assets_lower[key] if key in assets_lower else None
|
||||
if asset is not None:
|
||||
return asset
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
def create_trading_pair(self, market, asset_def, is_local):
|
||||
"""
|
||||
Creating a TradingPair from market and asset data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
market: dict[str, Object]
|
||||
asset_def: dict[str, Object]
|
||||
is_local: bool
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
"""
|
||||
data_source = 'local' if is_local else 'catalyst'
|
||||
params = dict(
|
||||
exchange=self.name,
|
||||
data_source=data_source,
|
||||
exchange_symbol=market['id'],
|
||||
)
|
||||
mixin_market_params(self.name, params, market)
|
||||
|
||||
if asset_def is not None:
|
||||
params['symbol'] = asset_def['symbol']
|
||||
|
||||
params['start_date'] = asset_def['start_date'] \
|
||||
if 'start_date' in asset_def else None
|
||||
|
||||
params['end_date'] = asset_def['end_date'] \
|
||||
if 'end_date' in asset_def else None
|
||||
|
||||
params['leverage'] = asset_def['leverage'] \
|
||||
if 'leverage' in asset_def else 1.0
|
||||
|
||||
params['asset_name'] = asset_def['asset_name'] \
|
||||
if 'asset_name' in asset_def else None
|
||||
|
||||
params['end_daily'] = asset_def['end_daily'] \
|
||||
if 'end_daily' in asset_def \
|
||||
and asset_def['end_daily'] != 'N/A' else None
|
||||
|
||||
params['end_minute'] = asset_def['end_minute'] \
|
||||
if 'end_minute' in asset_def \
|
||||
and asset_def['end_minute'] != 'N/A' else None
|
||||
|
||||
else:
|
||||
params['symbol'] = self.get_catalyst_symbol(market)
|
||||
# TODO: add as an optional column
|
||||
params['leverage'] = 1.0
|
||||
|
||||
return TradingPair(**params)
|
||||
|
||||
def load_assets(self):
|
||||
self.assets = []
|
||||
|
||||
for market in self.markets:
|
||||
asset_defs = self.get_asset_defs(market)
|
||||
|
||||
for asset_def in asset_defs:
|
||||
if asset_def[0] is not None or not asset_defs[1]:
|
||||
try:
|
||||
asset = self.create_trading_pair(
|
||||
market=market,
|
||||
asset_def=asset_def[0],
|
||||
is_local=asset_def[1]
|
||||
)
|
||||
self.assets.append(asset)
|
||||
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def get_balances(self):
|
||||
try:
|
||||
log.debug('retrieving wallets balances')
|
||||
balances = self.api.fetch_balance()
|
||||
|
||||
balances_lower = dict()
|
||||
for key in balances:
|
||||
balances_lower[key.lower()] = balances[key]
|
||||
|
||||
except Exception as e:
|
||||
log.debug('error retrieving balances: {}', e)
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
return balances_lower
|
||||
|
||||
def _create_order(self, order_status):
|
||||
"""
|
||||
Create a Catalyst order object from a CCXT order dictionary
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order_status: dict[str, Object]
|
||||
The order dict from the CCXT api.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Order
|
||||
The Catalyst order object
|
||||
|
||||
"""
|
||||
if order_status['status'] == 'canceled':
|
||||
status = ORDER_STATUS.CANCELLED
|
||||
|
||||
elif order_status['status'] == 'closed' and order_status['filled'] > 0:
|
||||
log.debug('found executed order {}'.format(order_status))
|
||||
status = ORDER_STATUS.FILLED
|
||||
|
||||
elif order_status['status'] == 'open':
|
||||
status = ORDER_STATUS.OPEN
|
||||
|
||||
else:
|
||||
raise ValueError('invalid state for order')
|
||||
|
||||
amount = order_status['amount']
|
||||
filled = order_status['filled']
|
||||
|
||||
if order_status['side'] == 'sell':
|
||||
amount = -amount
|
||||
filled = -filled
|
||||
|
||||
price = order_status['price']
|
||||
order_type = order_status['type']
|
||||
|
||||
limit_price = price if order_type == 'limit' else None
|
||||
stop_price = None # TODO: add support
|
||||
|
||||
executed_price = order_status['cost'] / order_status['amount']
|
||||
commission = order_status['fee']
|
||||
date = from_ms_timestamp(order_status['timestamp'])
|
||||
|
||||
# order_id = str(order_status['info']['clientOrderId'])
|
||||
order_id = order_status['id']
|
||||
|
||||
# TODO: this won't work, redo the packages with a different key.
|
||||
symbol = order_status['info']['symbol'] \
|
||||
if 'symbol' in order_status['info'] \
|
||||
else order_status['info']['Exchange']
|
||||
|
||||
order = Order(
|
||||
dt=date,
|
||||
asset=self.get_asset(symbol, is_exchange_symbol=True),
|
||||
amount=amount,
|
||||
stop=stop_price,
|
||||
limit=limit_price,
|
||||
filled=filled,
|
||||
id=order_id,
|
||||
commission=commission
|
||||
)
|
||||
order.status = status
|
||||
|
||||
return order, executed_price
|
||||
|
||||
def create_order(self, asset, amount, is_buy, style):
|
||||
symbol = self.get_symbol(asset)
|
||||
|
||||
if isinstance(style, ExchangeLimitOrder):
|
||||
price = style.get_limit_price(is_buy)
|
||||
order_type = 'limit'
|
||||
|
||||
elif isinstance(style, MarketOrder):
|
||||
price = None
|
||||
order_type = 'market'
|
||||
|
||||
else:
|
||||
raise InvalidOrderStyle(
|
||||
exchange=self.name,
|
||||
style=style.__class__.__name__
|
||||
)
|
||||
|
||||
side = 'buy' if amount > 0 else 'sell'
|
||||
|
||||
try:
|
||||
result = self.api.create_order(
|
||||
symbol=symbol,
|
||||
type=order_type,
|
||||
side=side,
|
||||
amount=abs(amount),
|
||||
price=price
|
||||
)
|
||||
except ExchangeNotAvailable as e:
|
||||
log.debug('unable to create order: {}'.format(e))
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
if 'info' not in result:
|
||||
raise ValueError('cannot use order without info attribute')
|
||||
|
||||
order_id = result['id']
|
||||
order = Order(
|
||||
dt=pd.Timestamp.utcnow(),
|
||||
asset=asset,
|
||||
amount=amount,
|
||||
stop=style.get_stop_price(is_buy),
|
||||
limit=style.get_limit_price(is_buy),
|
||||
id=order_id
|
||||
)
|
||||
return order
|
||||
|
||||
def get_open_orders(self, asset):
|
||||
try:
|
||||
symbol = self.get_symbol(asset)
|
||||
result = self.api.fetch_open_orders(
|
||||
symbol=symbol,
|
||||
since=None,
|
||||
limit=None,
|
||||
params=dict()
|
||||
)
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
orders = []
|
||||
for order_status in result:
|
||||
order, executed_price = self._create_order(order_status)
|
||||
if asset is None or asset == order.sid:
|
||||
orders.append(order)
|
||||
|
||||
return orders
|
||||
|
||||
def _get_asset_from_order(self, order_id):
|
||||
open_orders = self.portfolio.open_orders
|
||||
order = next(
|
||||
(open_orders[id] for id in open_orders if id == order_id),
|
||||
None
|
||||
) # type: Order
|
||||
return order.asset if order is not None else None
|
||||
|
||||
def get_order(self, order_id, asset_or_symbol=None):
|
||||
if asset_or_symbol is None and self.portfolio is not None:
|
||||
asset_or_symbol = self._get_asset_from_order(order_id)
|
||||
|
||||
if asset_or_symbol is None:
|
||||
log.debug(
|
||||
'order not found in memory, the request might fail '
|
||||
'on some exchanges.'
|
||||
)
|
||||
try:
|
||||
symbol = self.get_symbol(asset_or_symbol) \
|
||||
if asset_or_symbol is not None else None
|
||||
order_status = self.api.fetch_order(id=order_id, symbol=symbol)
|
||||
order, executed_price = self._create_order(order_status)
|
||||
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
return order, executed_price
|
||||
|
||||
def cancel_order(self, order_param, asset_or_symbol=None):
|
||||
order_id = order_param.id \
|
||||
if isinstance(order_param, Order) else order_param
|
||||
|
||||
if asset_or_symbol is None and self.portfolio is not None:
|
||||
asset_or_symbol = self._get_asset_from_order(order_id)
|
||||
|
||||
if asset_or_symbol is None:
|
||||
log.debug(
|
||||
'order not found in memory, cancelling order might fail '
|
||||
'on some exchanges.'
|
||||
)
|
||||
try:
|
||||
symbol = self.get_symbol(asset_or_symbol) \
|
||||
if asset_or_symbol is not None else None
|
||||
self.api.cancel_order(id=order_id, symbol=symbol)
|
||||
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
def tickers(self, assets):
|
||||
"""
|
||||
Retrieve current tick data for the given assets
|
||||
|
||||
Parameters
|
||||
----------
|
||||
assets: list[TradingPair]
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[dict[str, float]
|
||||
|
||||
"""
|
||||
tickers = dict()
|
||||
for asset in assets:
|
||||
ccxt_symbol = self.get_symbol(asset)
|
||||
ticker = self.api.fetch_ticker(ccxt_symbol)
|
||||
|
||||
ticker['last_traded'] = from_ms_timestamp(ticker['timestamp'])
|
||||
|
||||
if 'last_price' not in ticker:
|
||||
# TODO: any more exceptions?
|
||||
ticker['last_price'] = ticker['last']
|
||||
|
||||
# Using the volume represented in the base currency
|
||||
ticker['volume'] = ticker['baseVolume'] \
|
||||
if 'baseVolume' in ticker else 0
|
||||
|
||||
tickers[asset] = ticker
|
||||
|
||||
return tickers
|
||||
|
||||
def get_account(self):
|
||||
return None
|
||||
|
||||
def get_orderbook(self, asset, order_type='all', limit=None):
|
||||
ccxt_symbol = self.get_symbol(asset)
|
||||
|
||||
params = dict()
|
||||
if limit is not None:
|
||||
params['depth'] = limit
|
||||
|
||||
order_book = self.api.fetch_order_book(ccxt_symbol, params)
|
||||
|
||||
order_types = ['bids', 'asks'] if order_type == 'all' else [order_type]
|
||||
result = dict(last_traded=from_ms_timestamp(order_book['timestamp']))
|
||||
for index, order_type in enumerate(order_types):
|
||||
if limit is not None and index > limit - 1:
|
||||
break
|
||||
|
||||
result[order_type] = []
|
||||
for entry in order_book[order_type]:
|
||||
result[order_type].append(dict(
|
||||
rate=float(entry[0]),
|
||||
quantity=float(entry[1])
|
||||
))
|
||||
|
||||
return result
|
||||
+121
-123
@@ -8,15 +8,16 @@ import pandas as pd
|
||||
from catalyst.assets._assets import TradingPair
|
||||
from logbook import Logger
|
||||
|
||||
from catalyst.algorithm import MarketOrder
|
||||
from catalyst.constants import LOG_LEVEL
|
||||
from catalyst.data.data_portal import BASE_FIELDS
|
||||
from catalyst.exchange.bundle_utils import get_start_dt, \
|
||||
get_delta, get_periods, get_periods_range
|
||||
from catalyst.exchange.exchange_bundle import ExchangeBundle
|
||||
from catalyst.exchange.exchange_errors import MismatchingBaseCurrencies, \
|
||||
InvalidOrderStyle, BaseCurrencyNotFoundError, SymbolNotFoundOnExchange, \
|
||||
BaseCurrencyNotFoundError, SymbolNotFoundOnExchange, \
|
||||
PricingDataNotLoadedError, \
|
||||
NoDataAvailableOnExchange, ExchangeSymbolsNotFound
|
||||
NoDataAvailableOnExchange, NoValueForField
|
||||
from catalyst.exchange.exchange_execution import ExchangeStopLimitOrder, \
|
||||
ExchangeLimitOrder, ExchangeStopOrder
|
||||
from catalyst.exchange.exchange_portfolio import ExchangePortfolio
|
||||
@@ -33,8 +34,8 @@ class Exchange:
|
||||
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.assets = dict()
|
||||
self.local_assets = dict()
|
||||
self.assets = []
|
||||
self._symbol_maps = [None, None]
|
||||
self._portfolio = None
|
||||
self.minute_writer = None
|
||||
self.minute_reader = None
|
||||
@@ -132,7 +133,7 @@ class Exchange:
|
||||
|
||||
def get_symbol(self, asset):
|
||||
"""
|
||||
The the exchange specific symbol of the specified market.
|
||||
The exchange specific symbol of the specified market.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -145,9 +146,9 @@ class Exchange:
|
||||
"""
|
||||
symbol = None
|
||||
|
||||
for key in self.assets:
|
||||
if not symbol and self.assets[key].symbol == asset.symbol:
|
||||
symbol = key
|
||||
for a in self.assets:
|
||||
if not symbol and a.symbol == asset.symbol:
|
||||
symbol = a.symbol
|
||||
|
||||
if not symbol:
|
||||
raise ValueError('Currency %s not supported by exchange %s' %
|
||||
@@ -174,70 +175,104 @@ class Exchange:
|
||||
|
||||
return symbols
|
||||
|
||||
def get_assets(self, symbols=None, data_frequency=None):
|
||||
def get_assets(self, symbols=None, data_frequency=None,
|
||||
is_exchange_symbol=False,
|
||||
is_local=None):
|
||||
"""
|
||||
The list of markets for the specified symbols.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbols: list[str]
|
||||
data_frequency: str
|
||||
is_exchange_symbol: bool
|
||||
is_local: bool
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[TradingPair]
|
||||
A list of asset objects.
|
||||
|
||||
Notes
|
||||
-----
|
||||
See get_asset for details of each parameter.
|
||||
|
||||
"""
|
||||
if symbols is None:
|
||||
# Make a distinct list of all symbols
|
||||
symbols = list(set([asset.symbol for asset in self.assets]))
|
||||
is_exchange_symbol = False
|
||||
|
||||
assets = []
|
||||
|
||||
if symbols is not None:
|
||||
for symbol in symbols:
|
||||
asset = self.get_asset(symbol, data_frequency)
|
||||
assets.append(asset)
|
||||
else:
|
||||
for key in self.assets:
|
||||
assets.append(self.assets[key])
|
||||
|
||||
for symbol in symbols:
|
||||
asset = self.get_asset(
|
||||
symbol, data_frequency, is_exchange_symbol, is_local
|
||||
)
|
||||
assets.append(asset)
|
||||
return assets
|
||||
|
||||
def _find_asset(self, asset, symbol, data_frequency, is_local=False):
|
||||
assets = self.assets if not is_local else self.local_assets
|
||||
|
||||
for key in assets:
|
||||
if not asset and assets[key].symbol.lower() == symbol.lower() and (
|
||||
not data_frequency or (
|
||||
data_frequency == 'minute' and assets[
|
||||
key].end_minute is not None)):
|
||||
asset = assets[key]
|
||||
|
||||
return asset
|
||||
|
||||
def get_asset(self, symbol, data_frequency=None):
|
||||
def get_asset(self, symbol, data_frequency=None, is_exchange_symbol=False,
|
||||
is_local=None):
|
||||
"""
|
||||
The market for the specified symbol.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol: str
|
||||
The Catalyst or exchange symbol.
|
||||
|
||||
data_frequency: str
|
||||
Check for asset corresponding to the specified data_frequency.
|
||||
The same asset might exist in the Catalyst repository or
|
||||
locally (following a CSV ingestion). Filtering by
|
||||
data_frequency picks the right asset.
|
||||
|
||||
is_exchange_symbol: bool
|
||||
Whether the symbol uses the Catalyst or exchange convention.
|
||||
|
||||
is_local: bool
|
||||
For the local or Catalyst asset.
|
||||
|
||||
Returns
|
||||
-------
|
||||
TradingPair
|
||||
The asset object.
|
||||
|
||||
"""
|
||||
asset = None
|
||||
|
||||
log.debug('searching asset {} on the server')
|
||||
asset = self._find_asset(asset, symbol, data_frequency, False)
|
||||
log.debug(
|
||||
'searching assets for: {} {}'.format(
|
||||
self.name, symbol
|
||||
)
|
||||
)
|
||||
for a in self.assets:
|
||||
if asset is not None:
|
||||
break
|
||||
|
||||
log.debug('asset {} not found on the server, searching local assets')
|
||||
asset = self._find_asset(asset, symbol, data_frequency, True)
|
||||
if is_local is not None:
|
||||
data_source = 'local' if is_local else 'catalyst'
|
||||
applies = (a.data_source == data_source)
|
||||
|
||||
if not asset:
|
||||
all_values = list(self.assets.values()) + \
|
||||
list(self.local_assets.values())
|
||||
supported_symbols = [
|
||||
asset.symbol for asset in all_values
|
||||
]
|
||||
elif data_frequency is not None:
|
||||
applies = (
|
||||
(data_frequency == 'minute' and a.end_minute is not None)
|
||||
or (data_frequency == 'daily' and a.end_daily is not None)
|
||||
)
|
||||
|
||||
else:
|
||||
applies = True
|
||||
|
||||
# The symbol provided may use the Catalyst or the exchange
|
||||
# convention
|
||||
key = a.exchange_symbol if is_exchange_symbol else a.symbol
|
||||
if not asset and key.lower() == symbol.lower() and applies:
|
||||
asset = a
|
||||
|
||||
if asset is None:
|
||||
supported_symbols = sorted([
|
||||
asset.symbol for asset in self.assets
|
||||
])
|
||||
|
||||
raise SymbolNotFoundOnExchange(
|
||||
symbol=symbol,
|
||||
@@ -245,11 +280,20 @@ class Exchange:
|
||||
supported_symbols=supported_symbols
|
||||
)
|
||||
|
||||
log.debug('found asset: {}'.format(asset))
|
||||
return asset
|
||||
|
||||
def fetch_symbol_map(self, is_local=False):
|
||||
return get_exchange_symbols(self.name, is_local)
|
||||
index = 1 if is_local else 0
|
||||
if self._symbol_maps[index] is not None:
|
||||
return self._symbol_maps[index]
|
||||
|
||||
else:
|
||||
symbol_map = get_exchange_symbols(self.name, is_local)
|
||||
self._symbol_maps[index] = symbol_map
|
||||
return symbol_map
|
||||
|
||||
@abstractmethod
|
||||
def load_assets(self, is_local=False):
|
||||
"""
|
||||
Populate the 'assets' attribute with a dictionary of Assets.
|
||||
@@ -267,66 +311,7 @@ class Exchange:
|
||||
via its api.
|
||||
|
||||
"""
|
||||
try:
|
||||
symbol_map = self.fetch_symbol_map(is_local)
|
||||
except ExchangeSymbolsNotFound:
|
||||
return None
|
||||
|
||||
for exchange_symbol in symbol_map:
|
||||
asset = symbol_map[exchange_symbol]
|
||||
|
||||
if 'start_date' in asset:
|
||||
start_date = pd.to_datetime(asset['start_date'], utc=True)
|
||||
else:
|
||||
start_date = None
|
||||
|
||||
if 'end_date' in asset:
|
||||
end_date = pd.to_datetime(asset['end_date'], utc=True)
|
||||
else:
|
||||
end_date = None
|
||||
|
||||
if 'leverage' in asset:
|
||||
leverage = asset['leverage']
|
||||
else:
|
||||
leverage = 1.0
|
||||
|
||||
if 'asset_name' in asset:
|
||||
asset_name = asset['asset_name']
|
||||
else:
|
||||
asset_name = None
|
||||
|
||||
if 'min_trade_size' in asset:
|
||||
min_trade_size = asset['min_trade_size']
|
||||
else:
|
||||
min_trade_size = 0.0000001
|
||||
|
||||
if 'end_daily' in asset and asset['end_daily'] != 'N/A':
|
||||
end_daily = pd.to_datetime(asset['end_daily'], utc=True)
|
||||
else:
|
||||
end_daily = None
|
||||
|
||||
if 'end_minute' in asset and asset['end_minute'] != 'N/A':
|
||||
end_minute = pd.to_datetime(asset['end_minute'], utc=True)
|
||||
else:
|
||||
end_minute = None
|
||||
|
||||
trading_pair = TradingPair(
|
||||
symbol=asset['symbol'],
|
||||
exchange=self.name,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
leverage=leverage,
|
||||
asset_name=asset_name,
|
||||
min_trade_size=min_trade_size,
|
||||
end_daily=end_daily,
|
||||
end_minute=end_minute,
|
||||
exchange_symbol=exchange_symbol
|
||||
)
|
||||
|
||||
if is_local:
|
||||
self.local_assets[exchange_symbol] = trading_pair
|
||||
else:
|
||||
self.assets[exchange_symbol] = trading_pair
|
||||
pass
|
||||
|
||||
def check_open_orders(self):
|
||||
"""
|
||||
@@ -345,9 +330,11 @@ class Exchange:
|
||||
log.debug('found open order: {}'.format(order_id))
|
||||
|
||||
order, executed_price = self.get_order(order_id)
|
||||
log.debug('got updated order {} {}'.format(
|
||||
order, executed_price))
|
||||
|
||||
log.debug(
|
||||
'got updated order {} {}'.format(
|
||||
order, executed_price
|
||||
)
|
||||
)
|
||||
if order.status == ORDER_STATUS.FILLED:
|
||||
transaction = Transaction(
|
||||
asset=order.asset,
|
||||
@@ -409,12 +396,15 @@ class Exchange:
|
||||
if field not in BASE_FIELDS:
|
||||
raise KeyError('Invalid column: {}'.format(field))
|
||||
|
||||
values = []
|
||||
for asset in assets:
|
||||
value = self.get_single_spot_value(asset, field, data_frequency)
|
||||
values.append(value)
|
||||
tickers = self.tickers(assets)
|
||||
if field == 'close' or field == 'price':
|
||||
return [tickers[asset]['last'] for asset in tickers]
|
||||
|
||||
return values
|
||||
elif field == 'volume':
|
||||
return [tickers[asset]['volume'] for asset in tickers]
|
||||
|
||||
else:
|
||||
raise NoValueForField(field=field)
|
||||
|
||||
def get_single_spot_value(self, asset, field, data_frequency):
|
||||
"""
|
||||
@@ -688,7 +678,7 @@ class Exchange:
|
||||
log.debug('synchronizing portfolio with exchange {}'.format(self.name))
|
||||
balances = self.get_balances()
|
||||
|
||||
base_position_available = balances[self.base_currency] \
|
||||
base_position_available = balances[self.base_currency]['free'] \
|
||||
if self.base_currency in balances else None
|
||||
|
||||
if base_position_available is None:
|
||||
@@ -713,8 +703,9 @@ class Exchange:
|
||||
# TODO: convert if the position is not in the base currency
|
||||
ticker = tickers[asset]
|
||||
position = portfolio.positions[asset]
|
||||
|
||||
position.last_sale_price = ticker['last_price']
|
||||
position.last_sale_date = ticker['timestamp']
|
||||
position.last_sale_date = ticker['last_traded']
|
||||
|
||||
portfolio.positions_value += \
|
||||
position.amount * position.last_sale_price
|
||||
@@ -771,28 +762,30 @@ class Exchange:
|
||||
log.warn('skipping order amount of 0')
|
||||
return None
|
||||
|
||||
if asset.base_currency != self.base_currency.lower():
|
||||
if self.base_currency is None:
|
||||
raise ValueError('no base_currency defined for this exchange')
|
||||
|
||||
if asset.quote_currency != self.base_currency.lower():
|
||||
raise MismatchingBaseCurrencies(
|
||||
base_currency=asset.base_currency,
|
||||
base_currency=asset.quote_currency,
|
||||
algo_currency=self.base_currency
|
||||
)
|
||||
|
||||
is_buy = (amount > 0)
|
||||
|
||||
if limit_price is not None and stop_price is not None:
|
||||
style = ExchangeStopLimitOrder(limit_price, stop_price,
|
||||
exchange=self.name)
|
||||
style = ExchangeStopLimitOrder(
|
||||
limit_price, stop_price, exchange=self.name
|
||||
)
|
||||
|
||||
elif limit_price is not None:
|
||||
style = ExchangeLimitOrder(limit_price, exchange=self.name)
|
||||
|
||||
elif stop_price is not None:
|
||||
style = ExchangeStopOrder(stop_price, exchange=self.name)
|
||||
|
||||
elif style is not None:
|
||||
raise InvalidOrderStyle(exchange=self.name.title(),
|
||||
style=style.__class__.__name__)
|
||||
else:
|
||||
raise ValueError('Incomplete order data.')
|
||||
style = MarketOrder(exchange=self.name)
|
||||
|
||||
display_price = limit_price if limit_price is not None else stop_price
|
||||
log.debug(
|
||||
@@ -801,9 +794,10 @@ class Exchange:
|
||||
amount=amount,
|
||||
symbol=asset.symbol,
|
||||
type=style.__class__.__name__,
|
||||
price='{}{}'.format(display_price, asset.base_currency)
|
||||
price='{}{}'.format(display_price, asset.quote_currency)
|
||||
)
|
||||
)
|
||||
|
||||
order = self.create_order(asset, amount, is_buy, style)
|
||||
if order:
|
||||
self._portfolio.create_order(order)
|
||||
@@ -872,7 +866,7 @@ class Exchange:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_order(self, order_id):
|
||||
def get_order(self, order_id, symbol_or_asset=None):
|
||||
"""Lookup an order based on the order id returned from one of the
|
||||
order functions.
|
||||
|
||||
@@ -880,6 +874,8 @@ class Exchange:
|
||||
----------
|
||||
order_id : str
|
||||
The unique identifier for the order.
|
||||
symbol_or_asset: str|TradingPair
|
||||
The catalyst symbol, some exchanges need this
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -891,13 +887,15 @@ class Exchange:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def cancel_order(self, order_param):
|
||||
def cancel_order(self, order_param, symbol_or_asset=None):
|
||||
"""Cancel an open order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order_param : str or Order
|
||||
The order_id or order object to cancel.
|
||||
symbol_or_asset: str|TradingPair
|
||||
The catalyst symbol, some exchanges need this
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@@ -289,6 +289,7 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.algo_namespace = kwargs.pop('algo_namespace', None)
|
||||
self.live_graph = kwargs.pop('live_graph', None)
|
||||
self.simulate_orders = kwargs.pop('simulate_orders', None)
|
||||
|
||||
self._clock = None
|
||||
self.frame_stats = deque(maxlen=60)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import os
|
||||
import os
|
||||
import shutil
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
@@ -28,10 +27,9 @@ from catalyst.exchange.exchange_bcolz import BcolzExchangeBarReader, \
|
||||
from catalyst.exchange.exchange_errors import EmptyValuesInBundleError, \
|
||||
TempBundleNotFoundError, \
|
||||
NoDataAvailableOnExchange, \
|
||||
PricingDataNotLoadedError, DataCorruptionError, ExchangeSymbolsNotFound, \
|
||||
PricingDataValueError
|
||||
PricingDataNotLoadedError, DataCorruptionError, PricingDataValueError
|
||||
from catalyst.exchange.exchange_utils import get_exchange_folder, \
|
||||
get_exchange_symbols, save_exchange_symbols
|
||||
save_exchange_symbols, mixin_market_params
|
||||
from catalyst.utils.cli import maybe_show_progress
|
||||
from catalyst.utils.paths import ensure_directory
|
||||
|
||||
@@ -667,12 +665,11 @@ class ExchangeBundle:
|
||||
|
||||
"""
|
||||
log.info('ingesting csv file: {}'.format(path))
|
||||
try:
|
||||
symbols_def = get_exchange_symbols(
|
||||
self.exchange_name, is_local=True
|
||||
)
|
||||
except ExchangeSymbolsNotFound:
|
||||
symbols_def = dict()
|
||||
|
||||
if self.exchange is None:
|
||||
# Avoid circular dependencies
|
||||
from catalyst.exchange.factory import get_exchange
|
||||
self.exchange = get_exchange(self.exchange_name)
|
||||
|
||||
problems = []
|
||||
df = pd.read_csv(
|
||||
@@ -705,24 +702,40 @@ class ExchangeBundle:
|
||||
end_dt = df.index.get_level_values(1).max()
|
||||
end_dt_key = 'end_{}'.format(data_frequency)
|
||||
|
||||
if symbol is symbols_def:
|
||||
symbol_def = symbols_def[symbol]
|
||||
market = self.exchange.get_market(symbol)
|
||||
if market is None:
|
||||
raise ValueError('symbol not available in the exchange.')
|
||||
|
||||
start_dt = symbol_def['start_date'] \
|
||||
if symbol_def['start_date'] < start_dt else start_dt
|
||||
params = dict(
|
||||
exchange=self.exchange.name,
|
||||
data_source='local',
|
||||
exchange_symbol=market['id'],
|
||||
)
|
||||
mixin_market_params(self.exchange_name, params, market)
|
||||
|
||||
end_dt = symbol_def[end_dt_key] \
|
||||
if symbol_def[end_dt_key] > end_dt else end_dt
|
||||
asset_def = self.exchange.get_asset_def(market, True)
|
||||
if asset_def is not None:
|
||||
params['symbol'] = asset_def['symbol']
|
||||
|
||||
end_daily = end_dt \
|
||||
if data_frequency == 'daily' else symbol_def['end_daily']
|
||||
params['start_date'] = asset_def['start_date'] \
|
||||
if asset_def['start_date'] < start_dt else start_dt
|
||||
|
||||
end_minute = end_dt \
|
||||
if data_frequency == 'minute' else symbol_def['end_minute']
|
||||
params['end_date'] = asset_def[end_dt_key] \
|
||||
if asset_def[end_dt_key] > end_dt else end_dt
|
||||
|
||||
params['end_daily'] = end_dt \
|
||||
if data_frequency == 'daily' else asset_def['end_daily']
|
||||
|
||||
params['end_minute'] = end_dt \
|
||||
if data_frequency == 'minute' else asset_def['end_minute']
|
||||
|
||||
else:
|
||||
end_daily = end_dt if data_frequency == 'daily' else 'N/A'
|
||||
end_minute = end_dt if data_frequency == 'minute' else 'N/A'
|
||||
params['symbol'] = self.exchange.get_catalyst_symbol(market)
|
||||
|
||||
params['end_daily'] = end_dt \
|
||||
if data_frequency == 'daily' else 'N/A'
|
||||
params['end_minute'] = end_dt \
|
||||
if data_frequency == 'minute' else 'N/A'
|
||||
|
||||
if min_start_dt is None or start_dt < min_start_dt:
|
||||
min_start_dt = start_dt
|
||||
@@ -730,19 +743,8 @@ class ExchangeBundle:
|
||||
if max_end_dt is None or end_dt > max_end_dt:
|
||||
max_end_dt = end_dt
|
||||
|
||||
asset = TradingPair(
|
||||
symbol=symbol,
|
||||
exchange=self.exchange_name,
|
||||
start_date=start_dt,
|
||||
end_date=end_dt,
|
||||
leverage=0, # TODO: add as an optional column
|
||||
asset_name=symbol,
|
||||
min_trade_size=0, # TODO: add as an optional column
|
||||
end_daily=end_daily,
|
||||
end_minute=end_minute,
|
||||
exchange_symbol=symbol
|
||||
)
|
||||
assets[symbol] = asset
|
||||
asset = TradingPair(**params)
|
||||
assets[market['id']] = asset
|
||||
|
||||
save_exchange_symbols(self.exchange_name, assets, True)
|
||||
|
||||
|
||||
@@ -240,3 +240,7 @@ class NoDataAvailableOnExchange(ZiplineError):
|
||||
'Requested data for trading pair {symbol} is not available on exchange {exchange} '
|
||||
'in `{data_frequency}` frequency at this time. '
|
||||
'Check `http://enigma.co/catalyst/status` for market coverage.').strip()
|
||||
|
||||
|
||||
class NoValueForField(ZiplineError):
|
||||
msg = ('Value not found for field: {field}.').strip()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from catalyst.finance.execution import LimitOrder, StopOrder, StopLimitOrder
|
||||
from catalyst.finance.execution import LimitOrder, StopOrder, StopLimitOrder, MarketOrder
|
||||
|
||||
|
||||
class ExchangeLimitOrder(LimitOrder):
|
||||
|
||||
@@ -3,7 +3,6 @@ from logbook import Logger
|
||||
|
||||
from catalyst.constants import LOG_LEVEL
|
||||
from catalyst.protocol import Portfolio, Positions, Position
|
||||
from catalyst.utils.deprecate import deprecated
|
||||
|
||||
log = Logger('ExchangePortfolio', level=LOG_LEVEL)
|
||||
|
||||
@@ -11,7 +10,8 @@ log = Logger('ExchangePortfolio', level=LOG_LEVEL)
|
||||
class ExchangePortfolio(Portfolio):
|
||||
"""
|
||||
Since the goal is to support multiple exchanges, it makes sense to
|
||||
include additional stats in the portfolio object.
|
||||
include additional stats in the portfolio object. This fills the role
|
||||
of Blotter and Portfolio in live mode.
|
||||
|
||||
Instead of relying on the performance tracker, each exchange portfolio
|
||||
tracks its own holding. This offers a separation between tracking an
|
||||
@@ -89,32 +89,6 @@ class ExchangePortfolio(Portfolio):
|
||||
|
||||
log.debug('updated portfolio with executed order')
|
||||
|
||||
@deprecated
|
||||
def execute_transaction(self, transaction):
|
||||
# TODO: almost duplicate of execute_order. Not sure why Poloniex needs this.
|
||||
log.debug('executing transaction {}'.format(transaction.order_id))
|
||||
|
||||
order_position = self.positions[transaction.asset] \
|
||||
if transaction.asset in self.positions else None
|
||||
|
||||
if order_position is None:
|
||||
raise ValueError(
|
||||
'Trying to execute transaction for a position not held: %s' % transaction.order_id
|
||||
)
|
||||
|
||||
self.capital_used += transaction.amount * transaction.price
|
||||
|
||||
if transaction.amount > 0:
|
||||
if order_position.cost_basis > 0:
|
||||
order_position.cost_basis = np.average(
|
||||
[order_position.cost_basis, transaction.price],
|
||||
weights=[order_position.amount, transaction.amount]
|
||||
)
|
||||
else:
|
||||
order_position.cost_basis = transaction.price
|
||||
|
||||
log.debug('updated portfolio with executed order')
|
||||
|
||||
def remove_order(self, order):
|
||||
"""
|
||||
Removing an open order.
|
||||
|
||||
@@ -8,6 +8,7 @@ from datetime import date, datetime
|
||||
|
||||
import pandas as pd
|
||||
from catalyst.assets._assets import TradingPair
|
||||
from six import string_types
|
||||
from six.moves.urllib import request
|
||||
|
||||
from catalyst.constants import DATE_FORMAT, SYMBOLS_URL
|
||||
@@ -100,6 +101,20 @@ def download_exchange_symbols(exchange_name, environ=None):
|
||||
return response
|
||||
|
||||
|
||||
def symbols_parser(asset_def):
|
||||
for key, value in asset_def.items():
|
||||
match = isinstance(value, string_types) \
|
||||
and re.search(r'(\d{4}-\d{2}-\d{2})', value)
|
||||
|
||||
if match:
|
||||
try:
|
||||
asset_def[key] = pd.to_datetime(value, utc=True)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return asset_def
|
||||
|
||||
|
||||
def get_exchange_symbols(exchange_name, is_local=False, environ=None):
|
||||
"""
|
||||
The de-serialized content of the exchange's symbols.json.
|
||||
@@ -125,10 +140,10 @@ def get_exchange_symbols(exchange_name, is_local=False, environ=None):
|
||||
if os.path.isfile(filename):
|
||||
with open(filename) as data_file:
|
||||
try:
|
||||
data = json.load(data_file)
|
||||
data = json.load(data_file, object_hook=symbols_parser)
|
||||
return data
|
||||
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
return dict()
|
||||
else:
|
||||
raise ExchangeSymbolsNotFound(
|
||||
@@ -571,3 +586,44 @@ def resample_history_df(df, freq, field):
|
||||
|
||||
resampled_df = df.resample(freq).agg(agg)
|
||||
return resampled_df
|
||||
|
||||
|
||||
def mixin_market_params(exchange_name, params, market):
|
||||
"""
|
||||
Applies a CCXT market dict to parameters of TradingPair init.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
params: dict[Object]
|
||||
market: dict[Object]
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
"""
|
||||
# TODO: make this more externalized / configurable
|
||||
if 'lot' in market:
|
||||
params['min_trade_size'] = market['lot']
|
||||
|
||||
if exchange_name == 'bitfinex':
|
||||
params['maker'] = 0.001
|
||||
params['taker'] = 0.002
|
||||
|
||||
elif 'maker' in market and 'taker' in market \
|
||||
and market['maker'] is not None and market['taker'] is not None:
|
||||
params['maker'] = market['maker']
|
||||
params['taker'] = market['taker']
|
||||
|
||||
else:
|
||||
# TODO: default commission, make configurable
|
||||
params['maker'] = 0.0015
|
||||
params['taker'] = 0.0025
|
||||
|
||||
info = market['info'] if 'info' in market else None
|
||||
if info:
|
||||
if 'minimum_order_size' in info:
|
||||
params['min_trade_size'] = float(info['minimum_order_size'])
|
||||
|
||||
|
||||
def from_ms_timestamp(ms):
|
||||
return pd.to_datetime(ms, unit='ms', utc=True)
|
||||
|
||||
@@ -1,38 +1,31 @@
|
||||
from catalyst.exchange.bitfinex.bitfinex import Bitfinex
|
||||
from catalyst.exchange.bittrex.bittrex import Bittrex
|
||||
from catalyst.exchange.exchange_errors import ExchangeNotFoundError
|
||||
from catalyst.exchange.exchange_utils import get_exchange_auth
|
||||
from catalyst.exchange.poloniex.poloniex import Poloniex
|
||||
import os
|
||||
|
||||
from catalyst.exchange.ccxt.ccxt_exchange import CCXT
|
||||
from catalyst.exchange.exchange_errors import ExchangeAuthEmpty
|
||||
from catalyst.exchange.exchange_utils import get_exchange_auth, \
|
||||
get_exchange_folder
|
||||
|
||||
|
||||
def get_exchange(exchange_name, base_currency=None):
|
||||
def get_exchange(exchange_name, base_currency=None, portfolio=None,
|
||||
must_authenticate=False):
|
||||
exchange_auth = get_exchange_auth(exchange_name)
|
||||
if exchange_name == 'bitfinex':
|
||||
return Bitfinex(
|
||||
key=exchange_auth['key'],
|
||||
secret=exchange_auth['secret'],
|
||||
base_currency=base_currency,
|
||||
portfolio=None
|
||||
|
||||
has_auth = (exchange_auth['key'] != '' and exchange_auth['secret'] != '')
|
||||
if must_authenticate and not has_auth:
|
||||
raise ExchangeAuthEmpty(
|
||||
exchange=exchange_name.title(),
|
||||
filename=os.path.join(
|
||||
get_exchange_folder(exchange_name), 'auth.json'
|
||||
)
|
||||
)
|
||||
|
||||
elif exchange_name == 'bittrex':
|
||||
return Bittrex(
|
||||
key=exchange_auth['key'],
|
||||
secret=exchange_auth['secret'],
|
||||
base_currency=base_currency,
|
||||
portfolio=None
|
||||
)
|
||||
|
||||
elif exchange_name == 'poloniex':
|
||||
return Poloniex(
|
||||
key=exchange_auth['key'],
|
||||
secret=exchange_auth['secret'],
|
||||
base_currency=base_currency,
|
||||
portfolio=None
|
||||
)
|
||||
|
||||
else:
|
||||
raise ExchangeNotFoundError(exchange_name=exchange_name)
|
||||
return CCXT(
|
||||
exchange_name=exchange_name,
|
||||
key=exchange_auth['key'],
|
||||
secret=exchange_auth['secret'],
|
||||
base_currency=base_currency,
|
||||
portfolio=portfolio
|
||||
)
|
||||
|
||||
|
||||
def get_exchanges(exchange_names):
|
||||
|
||||
@@ -153,11 +153,11 @@ def get_pretty_stats(stats_df, recorded_cols=None, num_rows=10):
|
||||
def format_positions(positions):
|
||||
parts = []
|
||||
for position in positions:
|
||||
msg = '{amount:.2f}{market} cost basis {cost_basis:.4f}{base}'.format(
|
||||
msg = '{amount:.2f}{base} cost basis {cost_basis:.4f}{quote}'.format(
|
||||
amount=position['amount'],
|
||||
market=position['sid'].market_currency,
|
||||
base=position['sid'].base_currency,
|
||||
cost_basis=position['cost_basis'],
|
||||
base=position['sid'].base_currency
|
||||
quote=position['sid'].quote_currency
|
||||
)
|
||||
parts.append(msg)
|
||||
return ', '.join(parts)
|
||||
|
||||
@@ -55,4 +55,4 @@ from .core import ( # noqa
|
||||
write_bcolz_minute_data,
|
||||
write_compressed,
|
||||
)
|
||||
from .fixtures import ZiplineTestCase # noqa
|
||||
from .fixtures import CatalystTestCase # noqa
|
||||
|
||||
@@ -643,7 +643,7 @@ def create_data_portal_from_trade_history(asset_finder, trading_calendar,
|
||||
return DataPortal(
|
||||
asset_finder, trading_calendar,
|
||||
first_trading_day=equity_daily_reader.first_trading_day,
|
||||
equity_daily_reader=equity_daily_reader,
|
||||
daily_reader=equity_daily_reader,
|
||||
)
|
||||
else:
|
||||
minutes = trading_calendar.minutes_in_range(
|
||||
|
||||
@@ -62,7 +62,7 @@ from catalyst.utils.paths import ensure_directory
|
||||
catalyst_dir = os.path.dirname(catalyst.__file__)
|
||||
|
||||
|
||||
class ZiplineTestCase(with_metaclass(FinalMeta, TestCase)):
|
||||
class CatalystTestCase(with_metaclass(FinalMeta, TestCase)):
|
||||
"""
|
||||
Shared extensions to core unittest.TestCase.
|
||||
|
||||
@@ -92,7 +92,7 @@ class ZiplineTestCase(with_metaclass(FinalMeta, TestCase)):
|
||||
cls._base_init_fixtures_was_called = False
|
||||
cls.init_class_fixtures()
|
||||
assert cls._base_init_fixtures_was_called, (
|
||||
"ZiplineTestCase.init_class_fixtures() was not called.\n"
|
||||
"CatalystTestCase.init_class_fixtures() was not called.\n"
|
||||
"This probably means that you overrode init_class_fixtures"
|
||||
" without calling super()."
|
||||
)
|
||||
@@ -170,7 +170,7 @@ class ZiplineTestCase(with_metaclass(FinalMeta, TestCase)):
|
||||
self._init_instance_fixtures_was_called = False
|
||||
self.init_instance_fixtures()
|
||||
assert self._init_instance_fixtures_was_called, (
|
||||
"ZiplineTestCase.init_instance_fixtures() was not"
|
||||
"CatalystTestCase.init_instance_fixtures() was not"
|
||||
" called.\n"
|
||||
"This probably means that you overrode"
|
||||
" init_instance_fixtures without calling super()."
|
||||
@@ -251,7 +251,7 @@ def alias(attr_name):
|
||||
|
||||
class WithDefaultDateBounds(object):
|
||||
"""
|
||||
ZiplineTestCase mixin which makes it possible to synchronize date bounds
|
||||
CatalystTestCase mixin which makes it possible to synchronize date bounds
|
||||
across fixtures.
|
||||
|
||||
This fixture should always be the last fixture in bases of any fixture or
|
||||
@@ -264,13 +264,13 @@ class WithDefaultDateBounds(object):
|
||||
The date bounds to be used for fixtures that want to have consistent
|
||||
dates.
|
||||
"""
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-12-29', tz='utc')
|
||||
START_DATE = pd.Timestamp('2016-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2016-12-29', tz='utc')
|
||||
|
||||
|
||||
class WithLogger(object):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.log_handler as an instance-level
|
||||
CatalystTestCase mixin providing cls.log_handler as an instance-level
|
||||
fixture.
|
||||
|
||||
After init_instance_fixtures has been called `self.log_handler` will be a
|
||||
@@ -295,7 +295,7 @@ class WithLogger(object):
|
||||
|
||||
class WithAssetFinder(WithDefaultDateBounds):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.asset_finder as a class-level fixture.
|
||||
CatalystTestCase mixin providing cls.asset_finder as a class-level fixture.
|
||||
|
||||
After init_class_fixtures has been called, `cls.asset_finder` is populated
|
||||
with an AssetFinder.
|
||||
@@ -402,7 +402,7 @@ class WithAssetFinder(WithDefaultDateBounds):
|
||||
|
||||
class WithTradingCalendars(object):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.trading_calendar,
|
||||
CatalystTestCase mixin providing cls.trading_calendar,
|
||||
cls.all_trading_calendars, cls.trading_calendar_for_asset_type as a
|
||||
class-level fixture.
|
||||
|
||||
@@ -423,7 +423,7 @@ class WithTradingCalendars(object):
|
||||
with that asset type.
|
||||
"""
|
||||
TRADING_CALENDAR_STRS = ('NYSE',)
|
||||
TRADING_CALENDAR_FOR_ASSET_TYPE = {Equity: 'NYSE', Future: 'us_futures'}
|
||||
TRADING_CALENDAR_FOR_ASSET_TYPE = {Equity: 'NYSE', Future: 'us_futures', }
|
||||
TRADING_CALENDAR_FOR_EXCHANGE = {}
|
||||
# For backwards compatibility, exisitng tests and fixtures refer to
|
||||
# `trading_calendar` with the assumption that the value is the NYSE
|
||||
@@ -460,7 +460,7 @@ class WithTradingEnvironment(WithAssetFinder,
|
||||
WithTradingCalendars,
|
||||
WithDefaultDateBounds):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.env as a class-level fixture.
|
||||
CatalystTestCase mixin providing cls.env as a class-level fixture.
|
||||
|
||||
After ``init_class_fixtures`` has been called, `cls.env` is populated
|
||||
with a trading environment whose `asset_finder` is the result of
|
||||
@@ -560,7 +560,7 @@ class WithTradingEnvironment(WithAssetFinder,
|
||||
|
||||
class WithSimParams(WithTradingEnvironment):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.sim_params as a class level fixture.
|
||||
CatalystTestCase mixin providing cls.sim_params as a class level fixture.
|
||||
|
||||
The arguments used to construct the trading environment may be overridded
|
||||
by putting ``SIM_PARAMS_{argname}`` in the class dict except for the
|
||||
@@ -615,7 +615,7 @@ class WithSimParams(WithTradingEnvironment):
|
||||
|
||||
class WithTradingSessions(WithTradingCalendars, WithDefaultDateBounds):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.trading_days, cls.all_trading_sessions
|
||||
CatalystTestCase mixin providing cls.trading_days, cls.all_trading_sessions
|
||||
as a class-level fixture.
|
||||
|
||||
After init_class_fixtures has been called, `cls.all_trading_sessions`
|
||||
@@ -668,7 +668,7 @@ class WithTradingSessions(WithTradingCalendars, WithDefaultDateBounds):
|
||||
|
||||
class WithTmpDir(object):
|
||||
"""
|
||||
ZiplineTestCase mixing providing cls.tmpdir as a class-level fixture.
|
||||
CatalystTestCase mixing providing cls.tmpdir as a class-level fixture.
|
||||
|
||||
After init_class_fixtures has been called, `cls.tmpdir` is populated with
|
||||
a `testfixtures.TempDirectory` object whose path is `cls.TMP_DIR_PATH`.
|
||||
@@ -691,7 +691,7 @@ class WithTmpDir(object):
|
||||
|
||||
class WithInstanceTmpDir(object):
|
||||
"""
|
||||
ZiplineTestCase mixing providing self.tmpdir as an instance-level fixture.
|
||||
CatalystTestCase mixing providing self.tmpdir as an instance-level fixture.
|
||||
|
||||
After init_instance_fixtures has been called, `self.tmpdir` is populated
|
||||
with a `testfixtures.TempDirectory` object whose path is
|
||||
@@ -714,7 +714,7 @@ class WithInstanceTmpDir(object):
|
||||
|
||||
class WithEquityDailyBarData(WithTradingEnvironment):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.make_equity_daily_bar_data.
|
||||
CatalystTestCase mixin providing cls.make_equity_daily_bar_data.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@@ -810,7 +810,7 @@ class WithEquityDailyBarData(WithTradingEnvironment):
|
||||
|
||||
class WithBcolzEquityDailyBarReader(WithEquityDailyBarData, WithTmpDir):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.bcolz_daily_bar_path,
|
||||
CatalystTestCase mixin providing cls.bcolz_daily_bar_path,
|
||||
cls.bcolz_daily_bar_ctable, and cls.bcolz_equity_daily_bar_reader
|
||||
class level fixtures.
|
||||
|
||||
@@ -895,7 +895,7 @@ class WithBcolzEquityDailyBarReader(WithEquityDailyBarData, WithTmpDir):
|
||||
|
||||
class WithBcolzEquityDailyBarReaderFromCSVs(WithBcolzEquityDailyBarReader):
|
||||
"""
|
||||
ZiplineTestCase mixin that provides
|
||||
CatalystTestCase mixin that provides
|
||||
cls.bcolz_equity_daily_bar_reader from a mapping of sids to CSV
|
||||
file paths.
|
||||
"""
|
||||
@@ -925,7 +925,7 @@ class _WithMinuteBarDataBase(WithTradingEnvironment):
|
||||
|
||||
class WithEquityMinuteBarData(_WithMinuteBarDataBase):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.equity_minute_bar_days.
|
||||
CatalystTestCase mixin providing cls.equity_minute_bar_days.
|
||||
|
||||
After init_class_fixtures has been called:
|
||||
- `cls.equity_minute_bar_days` has the range over which data has been
|
||||
@@ -984,7 +984,7 @@ class WithEquityMinuteBarData(_WithMinuteBarDataBase):
|
||||
|
||||
class WithFutureMinuteBarData(_WithMinuteBarDataBase):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.future_minute_bar_days.
|
||||
CatalystTestCase mixin providing cls.future_minute_bar_days.
|
||||
|
||||
After init_class_fixtures has been called:
|
||||
- `cls.future_minute_bar_days` has the range over which data has been
|
||||
@@ -1044,7 +1044,7 @@ class WithFutureMinuteBarData(_WithMinuteBarDataBase):
|
||||
|
||||
class WithBcolzEquityMinuteBarReader(WithEquityMinuteBarData, WithTmpDir):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.bcolz_minute_bar_path,
|
||||
CatalystTestCase mixin providing cls.bcolz_minute_bar_path,
|
||||
cls.bcolz_minute_bar_ctable, and cls.bcolz_equity_minute_bar_reader
|
||||
class level fixtures.
|
||||
|
||||
@@ -1103,7 +1103,7 @@ class WithBcolzEquityMinuteBarReader(WithEquityMinuteBarData, WithTmpDir):
|
||||
|
||||
class WithBcolzFutureMinuteBarReader(WithFutureMinuteBarData, WithTmpDir):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.bcolz_minute_bar_path,
|
||||
CatalystTestCase mixin providing cls.bcolz_minute_bar_path,
|
||||
cls.bcolz_minute_bar_ctable, and cls.bcolz_equity_minute_bar_reader
|
||||
class level fixtures.
|
||||
|
||||
@@ -1227,7 +1227,7 @@ class WithConstantFutureMinuteBarData(WithFutureMinuteBarData):
|
||||
|
||||
class WithAdjustmentReader(WithBcolzEquityDailyBarReader):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.adjustment_reader as a class level
|
||||
CatalystTestCase mixin providing cls.adjustment_reader as a class level
|
||||
fixture.
|
||||
|
||||
After init_class_fixtures has been called, `cls.adjustment_reader` will be
|
||||
@@ -1359,7 +1359,7 @@ class WithEquityPricingPipelineEngine(WithAdjustmentReader,
|
||||
|
||||
class WithSeededRandomPipelineEngine(WithTradingSessions, WithAssetFinder):
|
||||
"""
|
||||
ZiplineTestCase mixin providing class-level fixtures for running pipelines
|
||||
CatalystTestCase mixin providing class-level fixtures for running pipelines
|
||||
against deterministically-generated random data.
|
||||
|
||||
Attributes
|
||||
@@ -1434,7 +1434,7 @@ class WithDataPortal(WithAdjustmentReader,
|
||||
WithBcolzEquityMinuteBarReader,
|
||||
WithBcolzFutureMinuteBarReader):
|
||||
"""
|
||||
ZiplineTestCase mixin providing self.data_portal as an instance level
|
||||
CatalystTestCase mixin providing self.data_portal as an instance level
|
||||
fixture.
|
||||
|
||||
After init_instance_fixtures has been called, `self.data_portal` will be
|
||||
@@ -1485,12 +1485,12 @@ class WithDataPortal(WithAdjustmentReader,
|
||||
self.env.asset_finder,
|
||||
self.trading_calendar,
|
||||
first_trading_day=self.DATA_PORTAL_FIRST_TRADING_DAY,
|
||||
equity_daily_reader=(
|
||||
daily_reader=(
|
||||
self.bcolz_equity_daily_bar_reader
|
||||
if self.DATA_PORTAL_USE_DAILY_DATA else
|
||||
None
|
||||
),
|
||||
equity_minute_reader=(
|
||||
minute_reader=(
|
||||
self.bcolz_equity_minute_bar_reader
|
||||
if self.DATA_PORTAL_USE_MINUTE_DATA else
|
||||
None
|
||||
@@ -1526,7 +1526,7 @@ class WithDataPortal(WithAdjustmentReader,
|
||||
|
||||
class WithResponses(object):
|
||||
"""
|
||||
ZiplineTestCase mixin that provides self.responses as an instance
|
||||
CatalystTestCase mixin that provides self.responses as an instance
|
||||
fixture.
|
||||
|
||||
After init_instance_fixtures has been called, `self.responses` will be
|
||||
|
||||
@@ -37,7 +37,7 @@ from catalyst.utils.input_validation import expect_types
|
||||
__all__ = ['load_from_yahoo', 'load_bars_from_yahoo']
|
||||
|
||||
|
||||
def create_simulation_parameters(year=2006, start=None, end=None,
|
||||
def create_simulation_parameters(year=2016, start=None, end=None,
|
||||
capital_base=float("1.0e5"),
|
||||
num_days=None,
|
||||
data_frequency='daily',
|
||||
|
||||
+19
-46
@@ -11,9 +11,7 @@ import pandas as pd
|
||||
|
||||
from catalyst.data.bundles import load
|
||||
from catalyst.data.data_portal import DataPortal
|
||||
from catalyst.exchange.bittrex.bittrex import Bittrex
|
||||
from catalyst.exchange.bitfinex.bitfinex import Bitfinex
|
||||
from catalyst.exchange.poloniex.poloniex import Poloniex
|
||||
from catalyst.exchange.factory import get_exchange
|
||||
|
||||
try:
|
||||
from pygments import highlight
|
||||
@@ -39,11 +37,9 @@ from catalyst.exchange.exchange_data_portal import DataPortalExchangeLive, \
|
||||
from catalyst.exchange.asset_finder_exchange import AssetFinderExchange
|
||||
from catalyst.exchange.exchange_portfolio import ExchangePortfolio
|
||||
from catalyst.exchange.exchange_errors import (
|
||||
ExchangeRequestError, ExchangeAuthEmpty,
|
||||
ExchangeRequestErrorTooManyAttempts,
|
||||
BaseCurrencyNotFoundError, ExchangeNotFoundError)
|
||||
from catalyst.exchange.exchange_utils import get_exchange_auth, \
|
||||
get_algo_object, get_exchange_folder
|
||||
ExchangeRequestError, ExchangeRequestErrorTooManyAttempts,
|
||||
BaseCurrencyNotFoundError)
|
||||
from catalyst.exchange.exchange_utils import get_algo_object
|
||||
from logbook import Logger
|
||||
|
||||
from catalyst.constants import LOG_LEVEL
|
||||
@@ -94,7 +90,8 @@ def _run(handle_data,
|
||||
exchange,
|
||||
algo_namespace,
|
||||
base_currency,
|
||||
live_graph):
|
||||
live_graph,
|
||||
simulate_orders):
|
||||
"""Run a backtest for the given algorithm.
|
||||
|
||||
This is shared between the cli and :func:`catalyst.run_algo`.
|
||||
@@ -164,42 +161,15 @@ def _run(handle_data,
|
||||
|
||||
if portfolio is None:
|
||||
portfolio = ExchangePortfolio(
|
||||
start_date=pd.Timestamp.utcnow()
|
||||
start if start is not None else pd.Timestamp.utcnow()
|
||||
)
|
||||
|
||||
# This corresponds to the json file containing api token info
|
||||
exchange_auth = get_exchange_auth(exchange_name)
|
||||
|
||||
if live and (exchange_auth['key'] == '' \
|
||||
or exchange_auth['secret'] == ''):
|
||||
raise ExchangeAuthEmpty(
|
||||
exchange=exchange_name.title(),
|
||||
filename=os.path.join(
|
||||
get_exchange_folder(exchange_name, environ), 'auth.json'))
|
||||
|
||||
if exchange_name == 'bitfinex':
|
||||
exchanges[exchange_name] = Bitfinex(
|
||||
key=exchange_auth['key'],
|
||||
secret=exchange_auth['secret'],
|
||||
base_currency=base_currency,
|
||||
portfolio=portfolio
|
||||
)
|
||||
elif exchange_name == 'bittrex':
|
||||
exchanges[exchange_name] = Bittrex(
|
||||
key=exchange_auth['key'],
|
||||
secret=exchange_auth['secret'],
|
||||
base_currency=base_currency,
|
||||
portfolio=portfolio
|
||||
)
|
||||
elif exchange_name == 'poloniex':
|
||||
exchanges[exchange_name] = Poloniex(
|
||||
key=exchange_auth['key'],
|
||||
secret=exchange_auth['secret'],
|
||||
base_currency=base_currency,
|
||||
portfolio=portfolio
|
||||
)
|
||||
else:
|
||||
raise ExchangeNotFoundError(exchange_name=exchange_name)
|
||||
exchanges[exchange_name] = get_exchange(
|
||||
exchange_name=exchange_name,
|
||||
base_currency=base_currency,
|
||||
portfolio=portfolio,
|
||||
must_authenticate=live,
|
||||
)
|
||||
|
||||
open_calendar = get_calendar('OPEN')
|
||||
|
||||
@@ -263,7 +233,7 @@ def _run(handle_data,
|
||||
)
|
||||
|
||||
if base_currency in balances:
|
||||
base_currency_available = balances[base_currency]
|
||||
base_currency_available = balances[base_currency]['free']
|
||||
log.info(
|
||||
'base currency available in the account: {} {}'.format(
|
||||
base_currency_available, base_currency
|
||||
@@ -308,7 +278,8 @@ def _run(handle_data,
|
||||
ExchangeTradingAlgorithmLive,
|
||||
exchanges=exchanges,
|
||||
algo_namespace=algo_namespace,
|
||||
live_graph=live_graph
|
||||
live_graph=live_graph,
|
||||
simulate_orders=simulate_orders
|
||||
)
|
||||
elif exchanges:
|
||||
# Removed the existing Poloniex fork to keep things simple
|
||||
@@ -470,6 +441,7 @@ def run_algorithm(initialize,
|
||||
base_currency=None,
|
||||
algo_namespace=None,
|
||||
live_graph=False,
|
||||
simulate_orders=True,
|
||||
output=os.devnull):
|
||||
"""Run a trading algorithm.
|
||||
|
||||
@@ -591,5 +563,6 @@ def run_algorithm(initialize,
|
||||
exchange=exchange_name,
|
||||
algo_namespace=algo_namespace,
|
||||
base_currency=base_currency,
|
||||
live_graph=live_graph
|
||||
live_graph=live_graph,
|
||||
simulate_orders=simulate_orders
|
||||
)
|
||||
|
||||
@@ -159,13 +159,17 @@ You can now test your algorithm using cryptoassets' historical pricing data,
|
||||
``catalyst`` provides three interfaces:
|
||||
|
||||
- A command-line interface (CLI),
|
||||
- the ``IPython Notebook`` magic,
|
||||
- and a :func:`~catalyst.run_algorithm` that you can call from other
|
||||
Python scripts.
|
||||
- a :func:`~catalyst.run_algorithm()` that you can call from other
|
||||
Python scripts,
|
||||
- and the ``Jupyter Notebook`` magic.
|
||||
|
||||
We'll start with the CLI, and introduce the ``IPython Notebook`` below. Some of
|
||||
the :doc:`example algorithms <example-algos>` provide instructions on how to run
|
||||
them both from the CLI, and using the :func:`~catalyst.run_algorithm` function.
|
||||
|
||||
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>`
|
||||
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
|
||||
corresponding section on :doc:`Catalyst & Jupyter Notebook <jupyter>` after you
|
||||
have assimilated the contents of this tutorial.
|
||||
|
||||
Command line interface
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -263,7 +267,7 @@ command line args all the time.
|
||||
Thus, to execute our algorithm from above and save the results to
|
||||
``buy_btc_simple_out.pickle`` we would call ``catalyst run`` as follows:
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: bash
|
||||
|
||||
catalyst run -f buy_btc_simple.py -x bitfinex --start 2016-1-1 --end 2017-9-30 -c usd --capital-base 100000 -o buy_btc_simple_out.pickle
|
||||
|
||||
@@ -542,6 +546,7 @@ only bought bitcoin every chance it got.
|
||||
sudo apt install python-tk
|
||||
|
||||
|
||||
.. _history:
|
||||
|
||||
Access to previous prices using ``history``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -560,78 +565,235 @@ If the short-mavg crosses from above we exit the positions as we assume
|
||||
the stock to go down further.
|
||||
|
||||
As we need to have access to previous prices to implement this strategy
|
||||
we need a new concept: History
|
||||
we need a new concept: History. ``data.history()`` is a convenience function
|
||||
that keeps a rolling window of data for you. The first argument is the number
|
||||
of bars you want to collect, the second argument is the unit (either ``'1d'``
|
||||
for daily or ``'1m'`` for minute frequency, but note that you need to have
|
||||
minute-level data when using ``1m``). This is a function we use in the
|
||||
``handle_data()`` section.
|
||||
|
||||
``data.history()`` is a convenience function that keeps a rolling window of
|
||||
data for you. The first argument is the number of bars you want to
|
||||
collect, the second argument is the unit (either ``'1d'`` for ``'1m'``
|
||||
but note that you need to have minute-level data for using ``1m``). This is
|
||||
a function we use in the ``handle_data()`` section:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
%load_ext catalyst
|
||||
You will note that the code below is substantially longer than the previous
|
||||
examples. Don't get overwhelmed by it as the logic is fairly simple and easy to
|
||||
follow. Most of the added some complexity has been added to beautify the output,
|
||||
which you can skim through for now. A copy of this algorithm is available in
|
||||
the ``examples`` directory:
|
||||
`dual_moving_average.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/dual_moving_average.py>`_.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
%%catalyst --start 2016-4-1 --end 2017-9-30 -x bitfinex
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from logbook import Logger
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from catalyst.api import order, record, symbol, order_target
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import (order, record, symbol, order_target_percent,
|
||||
get_open_orders)
|
||||
from catalyst.exchange.stats_utils import extract_transactions
|
||||
|
||||
NAMESPACE = 'dual_moving_average'
|
||||
log = Logger(NAMESPACE)
|
||||
|
||||
def initialize(context):
|
||||
context.i = 0
|
||||
context.asset = symbol('btc_usd')
|
||||
context.i = 0
|
||||
context.asset = symbol('ltc_usd')
|
||||
context.base_price = None
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# Skip first 150 days to get full windows
|
||||
context.i += 1
|
||||
if context.i < 150:
|
||||
# define the windows for the moving averages
|
||||
short_window = 50
|
||||
long_window = 200
|
||||
|
||||
# Skip as many bars as long_window to properly compute the average
|
||||
context.i += 1
|
||||
if context.i < long_window:
|
||||
return
|
||||
|
||||
# Compute averages
|
||||
# data.history() has to be called with the same params
|
||||
# from above and returns a pandas dataframe.
|
||||
short_mavg = data.history(context.asset, 'price', bar_count=50, frequency="1d").mean()
|
||||
long_mavg = data.history(context.asset, 'price', bar_count=150, frequency="1d").mean()
|
||||
# Compute moving averages calling data.history() for each
|
||||
# moving average with the appropriate parameters. We choose to use
|
||||
# minute bars for this simulation -> freq="1m"
|
||||
# Returns a pandas dataframe.
|
||||
short_mavg = data.history(context.asset, 'price',
|
||||
bar_count=short_window, frequency="1m").mean()
|
||||
long_mavg = data.history(context.asset, 'price',
|
||||
bar_count=long_window, frequency="1m").mean()
|
||||
|
||||
# Trading logic
|
||||
if short_mavg > long_mavg:
|
||||
# order_target orders as many shares as needed to
|
||||
# achieve the desired number of shares.
|
||||
order_target(context.asset, 100)
|
||||
elif short_mavg < long_mavg:
|
||||
order_target(context.asset, 0)
|
||||
# Let's keep the price of our asset in a more handy variable
|
||||
price = data.current(context.asset, 'price')
|
||||
|
||||
# If base_price is not set, we use the current value. This is the
|
||||
# price at the first bar which we reference to calculate price_change.
|
||||
if context.base_price is None:
|
||||
context.base_price = price
|
||||
price_change = (price - context.base_price) / context.base_price
|
||||
|
||||
# Save values for later inspection
|
||||
record(price=price,
|
||||
cash=context.portfolio.cash,
|
||||
price_change=price_change,
|
||||
short_mavg=short_mavg,
|
||||
long_mavg=long_mavg)
|
||||
|
||||
# Since we are using limit orders, some orders may not execute immediately
|
||||
# we wait until all orders are executed before considering more trades.
|
||||
orders = get_open_orders(context.asset)
|
||||
if len(orders) > 0:
|
||||
return
|
||||
|
||||
# Exit if we cannot trade
|
||||
if not data.can_trade(context.asset):
|
||||
return
|
||||
|
||||
# We check what's our position on our portfolio and trade accordingly
|
||||
pos_amount = context.portfolio.positions[context.asset].amount
|
||||
|
||||
# Trading logic
|
||||
if short_mavg > long_mavg and pos_amount == 0:
|
||||
# we buy 100% of our portfolio for this asset
|
||||
order_target_percent(context.asset, 1)
|
||||
elif short_mavg < long_mavg and pos_amount > 0:
|
||||
# we sell all our positions for this asset
|
||||
order_target_percent(context.asset, 0)
|
||||
|
||||
# Save values for later inspection
|
||||
record(btc=data.current(context.asset, 'price'),
|
||||
short_mavg=short_mavg,
|
||||
long_mavg=long_mavg)
|
||||
|
||||
def analyze(context, perf):
|
||||
import matplotlib.pyplot as plt
|
||||
fig = plt.figure(figsize=(12,12))
|
||||
ax1 = fig.add_subplot(211)
|
||||
perf.portfolio_value.plot(ax=ax1)
|
||||
ax1.set_ylabel('portfolio value in $')
|
||||
|
||||
ax2 = fig.add_subplot(212)
|
||||
perf['btc'].plot(ax=ax2)
|
||||
perf[['short_mavg', 'long_mavg']].plot(ax=ax2)
|
||||
# Get the base_currency that was passed as a parameter to the simulation
|
||||
base_currency = context.exchanges.values()[0].base_currency.upper()
|
||||
|
||||
perf_trans = perf.ix[[t != [] for t in perf.transactions]]
|
||||
buys = perf_trans.ix[[t[0]['amount'] > 0 for t in perf_trans.transactions]]
|
||||
sells = perf_trans.ix[
|
||||
[t[0]['amount'] < 0 for t in perf_trans.transactions]]
|
||||
ax2.plot(buys.index, perf.short_mavg.ix[buys.index],
|
||||
'^', markersize=10, color='m')
|
||||
ax2.plot(sells.index, perf.short_mavg.ix[sells.index],
|
||||
'v', markersize=10, color='k')
|
||||
ax2.set_ylabel('price in $')
|
||||
plt.legend(loc=0)
|
||||
plt.show()
|
||||
# First chart: Plot portfolio value using base_currency
|
||||
ax1 = plt.subplot(411)
|
||||
perf.loc[:, ['portfolio_value']].plot(ax=ax1)
|
||||
ax1.legend_.remove()
|
||||
ax1.set_ylabel('Portfolio Value\n({})'.format(base_currency))
|
||||
start, end = ax1.get_ylim()
|
||||
ax1.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
Here we are explicitly defining an ``analyze()`` function that gets
|
||||
automatically called once the backtest is done.
|
||||
# Second chart: Plot asset price, moving averages and buys/sells
|
||||
ax2 = plt.subplot(412, sharex=ax1)
|
||||
perf.loc[:, ['price','short_mavg','long_mavg']].plot(ax=ax2, label='Price')
|
||||
ax2.legend_.remove()
|
||||
ax2.set_ylabel('{asset}\n({base})'.format(
|
||||
asset = context.asset.symbol,
|
||||
base = base_currency
|
||||
))
|
||||
start, end = ax2.get_ylim()
|
||||
ax2.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
transaction_df = extract_transactions(perf)
|
||||
if not transaction_df.empty:
|
||||
buy_df = transaction_df[transaction_df['amount'] > 0]
|
||||
sell_df = transaction_df[transaction_df['amount'] < 0]
|
||||
ax2.scatter(
|
||||
buy_df.index.to_pydatetime(),
|
||||
perf.loc[buy_df.index, 'price'],
|
||||
marker='^',
|
||||
s=100,
|
||||
c='green',
|
||||
label=''
|
||||
)
|
||||
ax2.scatter(
|
||||
sell_df.index.to_pydatetime(),
|
||||
perf.loc[sell_df.index, 'price'],
|
||||
marker='v',
|
||||
s=100,
|
||||
c='red',
|
||||
label=''
|
||||
)
|
||||
|
||||
# Third chart: Compare percentage change between our portfolio
|
||||
# and the price of the asset
|
||||
ax3 = plt.subplot(413, sharex=ax1)
|
||||
perf.loc[:, ['algorithm_period_return', 'price_change']].plot(ax=ax3)
|
||||
ax3.legend_.remove()
|
||||
ax3.set_ylabel('Percent Change')
|
||||
start, end = ax3.get_ylim()
|
||||
ax3.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
# Fourth chart: Plot our cash
|
||||
ax4 = plt.subplot(414, sharex=ax1)
|
||||
perf.cash.plot(ax=ax4)
|
||||
ax4.set_ylabel('Cash\n({})'.format(base_currency))
|
||||
start, end = ax4.get_ylim()
|
||||
ax4.yaxis.set_ticks(np.arange(0, end, end/5))
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
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),
|
||||
)
|
||||
|
||||
In order to run the code above, you have to ingest the needed data first:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
catalyst ingest-exchange -x bitfinex -f minute -i ltc_usd
|
||||
|
||||
And then run the code above with the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
catalyst run -f dual_moving_average.py -x bitfinex -s 2017-9-22 -e 2017-9-23 --capital-base 1000 --base-currency usd --data-frequency minute -o out.pickle
|
||||
|
||||
Alternatively, we can make use of the ``run_algorithm()`` function included at
|
||||
the end of the file, where we can specify all the simulation parameters, and
|
||||
execute this file as a Python script:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python dual_moving_average.py
|
||||
|
||||
Either way, we obtain the following charts:
|
||||
|
||||
.. image:: https://s3.amazonaws.com/enigmaco-docs/github.io/tutorial_dual_moving_average.png
|
||||
|
||||
|
||||
A few comments on the code above:
|
||||
|
||||
At the beginning of our code, we import a number of Python libraries that we
|
||||
will be using in different parts of our script. It's good practice to keep all
|
||||
imports at the beginning of the file, as they are available globally
|
||||
throughout our script. All the libraries imported in this example are already
|
||||
present in your environment since they are prerequisites for the Catalyst
|
||||
installation.
|
||||
|
||||
Focus on the code that is inside ``handle_data()`` that is where all the
|
||||
trading logic occurs. You can safely dismiss most of the code in the
|
||||
``analyze()`` section, which is mostly to customize the visualization of the
|
||||
performance of our algorithm using the matplotlib library. You can copy and
|
||||
paste this whole section into other algorithms to obtain a similar display.
|
||||
|
||||
Inside the ``handle_data()``, we also used the ``order_target_percent()``
|
||||
function above. This and other functions like it can make order management
|
||||
and portfolio rebalancing much easier.
|
||||
|
||||
The ``ltc_usd`` asset was arbitrarily chosen. The values of 50 and 200 for the
|
||||
``short_window`` and ``long_window`` parameters are fairly common for a dual
|
||||
moving average crossover strategy from the world of traditional stocks (but
|
||||
bear in mind that they are usually used with daily bars instead of minute
|
||||
bars). The ``start`` and ``end`` dates have been chosen so as to demonstrate
|
||||
how our strategy can both perform better (blue line above green line on the
|
||||
``Percent Change`` chart) and worse (green line above blue line towards the end) than the
|
||||
price of the asset we are trading.
|
||||
|
||||
You can change any of these parameters: ``asset``, ``short_window``,
|
||||
``long_window``, ``start_date`` and ``end_date`` and compare the results, and
|
||||
you will see that in most cases, the performance is either worse than the
|
||||
price of the asset, or you are overfitting to one specific case. As we said
|
||||
at the beginning of this section, this strategy is probably not used by any
|
||||
serious trader anymore, but its educational purpose.
|
||||
|
||||
Although it might not be directly apparent, the power of ``history()``
|
||||
(pun intended) can not be under-estimated as most algorithms make use of
|
||||
@@ -643,21 +805,13 @@ the ``scikit-learn`` functions require ``numpy.ndarray``\ s rather than
|
||||
``pandas.DataFrame``\ s, so you can simply pass the underlying
|
||||
``ndarray`` of a ``DataFrame`` via ``.values``).
|
||||
|
||||
We also used the ``order_target()`` function above. This and other
|
||||
functions like it can make order management and portfolio rebalancing
|
||||
much easier.
|
||||
|
||||
|
||||
Conclusions
|
||||
~~~~~~~~~~~
|
||||
Next steps
|
||||
~~~~~~~~~~
|
||||
|
||||
We hope that this tutorial gave you a little insight into the
|
||||
architecture, API, and features of ``catalyst``. For next steps, check
|
||||
out some of the
|
||||
`examples <https://github.com/enigmampc/catalyst/tree/master/catalyst/examples>`__.
|
||||
The natural next step would be too look into the
|
||||
`buy_and_hodl <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/buy_and_hodl.py>`_
|
||||
example, which is a more elaborated and realistic version of the ``buy_btc_simple`` example presented in this tutorial.
|
||||
architecture, API, and features of Catalyst. For next steps, check
|
||||
out some of the other :doc:`example algorithms<example-algos>`.
|
||||
|
||||
Feel free to ask questions on the ``#catalyst_dev`` channel of our
|
||||
`Discord group <https://discord.gg/SJK32GY>`__ and report
|
||||
|
||||
+791
-334
File diff suppressed because it is too large
Load Diff
+50
-25
@@ -2,6 +2,32 @@
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
Version 0.3.10
|
||||
^^^^^^^^^^^^^
|
||||
**Release Date**: 2017-11-28
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
|
||||
- Fixed issue with fetching assets with daily frequency
|
||||
|
||||
Version 0.3.9
|
||||
^^^^^^^^^^^^^
|
||||
**Release Date**: 2017-11-28
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
|
||||
- Fixed sortino warning issues (:issue:`77`)
|
||||
- Adjusted computation of last candle of data.history (:issue:`71`)
|
||||
|
||||
Build
|
||||
~~~~~
|
||||
- Added capital_base parameter to live mode to limit cash (:issue:`79`)
|
||||
- Added support for csv ingestion (:issue:`65`)
|
||||
- Improved cash display in running stats (:issue:`80`)
|
||||
|
||||
|
||||
Version 0.3.8
|
||||
^^^^^^^^^^^^^
|
||||
**Release Date**: 2017-11-14
|
||||
@@ -59,7 +85,7 @@ Bug Fixes
|
||||
- Fixed issue with sell orders in backtesting
|
||||
- Fixed data frequency issues with data.history() in backtesting
|
||||
- Fixed an issue with can_trade()
|
||||
- Reduced the commission and slippage values to account for lower volume
|
||||
- Reduced the commission and slippage values to account for lower volume
|
||||
transactions
|
||||
|
||||
Build
|
||||
@@ -71,17 +97,17 @@ Documentation
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
- Improved installation notes for Windows C++ compiler and Conda
|
||||
- Addition of
|
||||
- Addition of
|
||||
`Jupyter Notebook guide <https://enigmampc.github.io/catalyst/jupyter.html>`_
|
||||
- Addition of
|
||||
- Addition of
|
||||
`Live Trading page <https://enigmampc.github.io/catalyst/live-trading.html>`_
|
||||
- Addition of
|
||||
- Addition of
|
||||
`Videos page <https://enigmampc.github.io/catalyst/videos.html>`_
|
||||
- Addition of
|
||||
- Addition of
|
||||
`Resources page <https://enigmampc.github.io/catalyst/resources.html>`_
|
||||
- Addition of `Development Guidelines
|
||||
- Addition of `Development Guidelines
|
||||
<https://enigmampc.github.io/catalyst/development-guidelines.html>`_
|
||||
- Addition of
|
||||
- Addition of
|
||||
`Release Notes <https://enigmampc.github.io/catalyst/releases.html>`_
|
||||
- Updated code docstrings
|
||||
|
||||
@@ -132,10 +158,10 @@ Bug Fixes
|
||||
~~~~~~~~~
|
||||
|
||||
- Fixed OS-dependent path issue in data bundle
|
||||
- Changed handling of empty ``auth.json``, instead of throwing an error for
|
||||
- Changed handling of empty ``auth.json``, instead of throwing an error for
|
||||
missing file
|
||||
- Updated ``etc/python2.7-environment.yml`` to work with Catalyst version 0.3
|
||||
- Updated ``catalyst/examples/buy_and_hodl.py`` and
|
||||
- Updated ``catalyst/examples/buy_and_hodl.py`` and
|
||||
``catalyst/examples/buy_low_sell_high.py`` to work with Catalyst version 0.3
|
||||
|
||||
|
||||
@@ -155,18 +181,18 @@ Version 0.2.dev5
|
||||
^^^^^^^^^^^^^^^^
|
||||
**Release Date**: 2017-10-03
|
||||
|
||||
- Fixes bug in data.history function that was formatting 'volume' data as
|
||||
integers, now they are returned as floats with up to 9 decimals of precision.
|
||||
- Fixes bug in data.history function that was formatting 'volume' data as
|
||||
integers, now they are returned as floats with up to 9 decimals of precision.
|
||||
Data bundles redone.
|
||||
|
||||
Version 0.2.dev4
|
||||
Version 0.2.dev4
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
**Release Date**: 2017-09-20
|
||||
|
||||
- Fixes bug in the pricing resolution of 1-minute data, now set to 8 decimal
|
||||
- Fixes bug in the pricing resolution of 1-minute data, now set to 8 decimal
|
||||
places. Pricing resolution of daily data remains set to 9 decimal places.
|
||||
- The current data bundle takes 340MB compressed for download, and 460MB
|
||||
- The current data bundle takes 340MB compressed for download, and 460MB
|
||||
uncompressed on disk for Catalyst to use.
|
||||
|
||||
Version 0.2.dev3
|
||||
@@ -176,14 +202,14 @@ Version 0.2.dev3
|
||||
|
||||
- 1-minute resolution OHLCV data bundle for backtesting from Poloniex exchange
|
||||
- Implementation of trading of fractional crypto assets (i.e. 0.01 BTC)
|
||||
- Minimum trade size of a coin can be configured on a per-coin basis, defaults
|
||||
to 0.00000001 in backtesting (most exchanges set the minimum trade to larger
|
||||
- Minimum trade size of a coin can be configured on a per-coin basis, defaults
|
||||
to 0.00000001 in backtesting (most exchanges set the minimum trade to larger
|
||||
amounts, which will impact live trading)
|
||||
- Increased pricing resolution from 3 to 9 decimal places
|
||||
- The current data bundle takes 40MB compressed for download, and 99MB
|
||||
- The current data bundle takes 40MB compressed for download, and 99MB
|
||||
uncompressed on disk for Catalyst to use.
|
||||
|
||||
Version 0.2.dev2
|
||||
Version 0.2.dev2
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
**Release Date**: 2017-09-07
|
||||
@@ -199,15 +225,15 @@ Version 0.2.dev1
|
||||
|
||||
- Comprehensive trading functionality against exchanges Bitfinex and Bittrex.
|
||||
- Support for all trading pairs available on each exchange.
|
||||
- Multiple algorithms can trade simultaneously against a single exchange
|
||||
- Multiple algorithms can trade simultaneously against a single exchange
|
||||
using the same account.
|
||||
- Each algorithm has a persisted state (i.e. algorithm can be stopped and
|
||||
restarted preserving the state without data loss) that tracks all open
|
||||
- Each algorithm has a persisted state (i.e. algorithm can be stopped and
|
||||
restarted preserving the state without data loss) that tracks all open
|
||||
orders, executed transactions and portfolio positions.
|
||||
|
||||
- Minute by minute portfolio performance metrics.
|
||||
|
||||
- Daily summary performance statistics compatible with pyfolio, a Python
|
||||
- Daily summary performance statistics compatible with pyfolio, a Python
|
||||
library for performance and risk analysis of financial portfolios
|
||||
|
||||
Version 0.1.dev9
|
||||
@@ -215,13 +241,13 @@ Version 0.1.dev9
|
||||
|
||||
**Release Date**: 2017-08-28
|
||||
|
||||
- Retrieval of crypto benchmark from bundle, instead of hitting Poloniex
|
||||
- Retrieval of crypto benchmark from bundle, instead of hitting Poloniex
|
||||
exchange directly
|
||||
- Change of bundle storage provider from Dropbox to AWS
|
||||
- Fix issue with 1/1000 scaling issue of prices in bundle
|
||||
|
||||
Version 0.1.dev8
|
||||
^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
**Release Date**: 2017-08-18
|
||||
|
||||
@@ -241,4 +267,3 @@ Version 0.1.dev6
|
||||
**Release Date**: 2017-07-13
|
||||
|
||||
- Initial public release
|
||||
|
||||
|
||||
+17
-1
@@ -32,7 +32,9 @@ Where things don't:
|
||||
Backtesting a Strategy
|
||||
----------------------
|
||||
|
||||
This algorithm is based on a simple momentum strategy. When the cryptoasset
|
||||
This is the first video of a two-part series on using Catalyst for algorithmic
|
||||
trading. This video implements a simple momentum strategy based on
|
||||
`mean reversion <example-algos.html#mean-reversion>`_: when the cryptoasset
|
||||
goes up quickly, we’re going to buy; when it goes down quickly, we’re going to
|
||||
sell. Hopefully, we’ll ride the waves.
|
||||
|
||||
@@ -40,3 +42,17 @@ sell. Hopefully, we’ll ride the waves.
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/JOBRwst9jUY" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
|
|
||||
|
|
||||
Live Trading a Strategy
|
||||
-----------------------
|
||||
|
||||
This is the second part of the two-part series on using Catalyst for algorithmic
|
||||
trading. Having backtested `our strategy <example-algos.html#mean-reversion>`_
|
||||
in the previous video, we now take it to trade live against the Bittrex exchange.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/NupiE-Xuglw" frameborder="0" allowfullscreen></iframe>
|
||||
|
|
||||
|
|
||||
@@ -105,7 +105,7 @@ None
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* Adds :class:`~zipline.testing.fixtures.ZiplineTestCase` which provides hooks
|
||||
* Adds :class:`~zipline.testing.fixtures.CatalystTestCase` which provides hooks
|
||||
to consume test fixtures. Fixtures are things like:
|
||||
:class:`~zipline.testing.fixtures.WithAssetFinder` which will make
|
||||
``self.asset_finder`` available to your test with some mock data
|
||||
|
||||
@@ -80,3 +80,6 @@ empyrical==0.2.1
|
||||
|
||||
tables==3.3.0
|
||||
|
||||
#Catalyst dependencies
|
||||
ccxt==1.10.251
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ from catalyst.errors import (
|
||||
CyclicCalendarAlias,
|
||||
InvalidCalendarName,
|
||||
)
|
||||
from catalyst.testing import ZiplineTestCase
|
||||
from catalyst.testing import CatalystTestCase
|
||||
from catalyst.utils.calendars.calendar_utils import TradingCalendarDispatcher
|
||||
from catalyst.utils.calendars.exchange_calendar_ice import ICEExchangeCalendar
|
||||
|
||||
|
||||
class CalendarAliasTestCase(ZiplineTestCase):
|
||||
class CalendarAliasTestCase(CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def init_class_fixtures(cls):
|
||||
|
||||
@@ -22,7 +22,7 @@ from catalyst.testing import (
|
||||
subtest,
|
||||
str_to_seconds,
|
||||
)
|
||||
from catalyst.testing.fixtures import WithInstanceTmpDir, ZiplineTestCase, \
|
||||
from catalyst.testing.fixtures import WithInstanceTmpDir, CatalystTestCase, \
|
||||
WithDefaultDateBounds
|
||||
from catalyst.testing.predicates import (
|
||||
assert_equal,
|
||||
@@ -45,7 +45,7 @@ _1_ns = pd.Timedelta(1, unit='ns')
|
||||
|
||||
class BundleCoreTestCase(WithInstanceTmpDir,
|
||||
WithDefaultDateBounds,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2014-01-06', tz='utc')
|
||||
END_DATE = pd.Timestamp('2014-01-10', tz='utc')
|
||||
|
||||
@@ -17,14 +17,14 @@ from catalyst.testing import (
|
||||
tmp_dir,
|
||||
patch_read_csv,
|
||||
)
|
||||
from catalyst.testing.fixtures import ZiplineTestCase
|
||||
from catalyst.testing.fixtures import CatalystTestCase
|
||||
from catalyst.testing.predicates import (
|
||||
assert_equal,
|
||||
)
|
||||
from catalyst.utils.functional import apply
|
||||
|
||||
|
||||
class QuandlBundleTestCase(ZiplineTestCase):
|
||||
class QuandlBundleTestCase(CatalystTestCase):
|
||||
symbols = 'AAPL', 'BRK_A', 'MSFT', 'ZEN'
|
||||
asset_start = pd.Timestamp('2014-01', tz='utc')
|
||||
asset_end = pd.Timestamp('2015-01', tz='utc')
|
||||
|
||||
@@ -10,12 +10,12 @@ from catalyst.data.bundles.core import _make_bundle_core
|
||||
from catalyst.data.bundles import yahoo_equities
|
||||
from catalyst.lib.adjustment import Float64Multiply
|
||||
from catalyst.testing import test_resource_path, tmp_dir, read_compressed
|
||||
from catalyst.testing.fixtures import WithResponses, ZiplineTestCase
|
||||
from catalyst.testing.fixtures import WithResponses, CatalystTestCase
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
|
||||
|
||||
class YahooBundleTestCase(WithResponses, ZiplineTestCase):
|
||||
class YahooBundleTestCase(WithResponses, CatalystTestCase):
|
||||
symbols = 'AAPL', 'IBM', 'MSFT'
|
||||
columns = 'open', 'high', 'low', 'close', 'volume'
|
||||
asset_start = pd.Timestamp('2014-01-02', tz='utc')
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
# 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.
|
||||
|
||||
|
||||
'''
|
||||
# ZIPLINE legacy test: Catalyst does not use DispatchBarReader, and thus
|
||||
# this test suite is irrelevant, and is commented out in its entirety
|
||||
|
||||
from numpy import array, nan
|
||||
from numpy.testing import assert_almost_equal
|
||||
from pandas import DataFrame, Timestamp
|
||||
@@ -31,7 +37,7 @@ from catalyst.testing.fixtures import (
|
||||
WithBcolzEquityDailyBarReader,
|
||||
WithBcolzFutureMinuteBarReader,
|
||||
WithTradingSessions,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
OHLC = ['open', 'high', 'low', 'close']
|
||||
@@ -40,7 +46,7 @@ OHLC = ['open', 'high', 'low', 'close']
|
||||
class AssetDispatchSessionBarTestCase(WithBcolzEquityDailyBarReader,
|
||||
WithBcolzFutureMinuteBarReader,
|
||||
WithTradingSessions,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
TRADING_CALENDAR_STRS = ('us_futures', 'NYSE')
|
||||
TRADING_CALENDAR_PRIMARY_CAL = 'us_futures'
|
||||
@@ -169,7 +175,7 @@ class AssetDispatchSessionBarTestCase(WithBcolzEquityDailyBarReader,
|
||||
|
||||
class AssetDispatchMinuteBarTestCase(WithBcolzEquityMinuteBarReader,
|
||||
WithBcolzFutureMinuteBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
TRADING_CALENDAR_STRS = ('us_futures', 'NYSE')
|
||||
TRADING_CALENDAR_PRIMARY_CAL = 'us_futures'
|
||||
@@ -330,3 +336,4 @@ class AssetDispatchMinuteBarTestCase(WithBcolzEquityMinuteBarReader,
|
||||
for i, (sid, expected, msg) in enumerate(expected_per_sid):
|
||||
for j, result in enumerate(results):
|
||||
assert_almost_equal(result[:, i], expected[j], err_msg=msg)
|
||||
'''
|
||||
@@ -38,8 +38,8 @@ from pandas import (
|
||||
from catalyst.data.bar_reader import NoDataOnDate
|
||||
from catalyst.data.minute_bars import (
|
||||
BcolzMinuteBarMetadata,
|
||||
BcolzMinuteBarWriter,
|
||||
BcolzMinuteBarReader,
|
||||
# BcolzMinuteBarWriter,
|
||||
# BcolzMinuteBarReader,
|
||||
BcolzMinuteOverlappingData,
|
||||
US_EQUITIES_MINUTES_PER_DAY,
|
||||
BcolzMinuteWriterColumnMismatch,
|
||||
@@ -47,24 +47,29 @@ from catalyst.data.minute_bars import (
|
||||
H5MinuteBarUpdateReader,
|
||||
)
|
||||
|
||||
from catalyst.exchange.exchange_bcolz import (
|
||||
BcolzExchangeBarWriter,
|
||||
BcolzExchangeBarReader,
|
||||
)
|
||||
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAssetFinder,
|
||||
WithInstanceTmpDir,
|
||||
WithTradingCalendars,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
# Calendar is set to cover several half days, to check a case where half
|
||||
# days would be read out of order in cases of windows which spanned over
|
||||
# multiple half days.
|
||||
TEST_CALENDAR_START = Timestamp('2014-06-02', tz='UTC')
|
||||
TEST_CALENDAR_STOP = Timestamp('2015-12-31', tz='UTC')
|
||||
TEST_CALENDAR_START = Timestamp('2015-06-02', tz='UTC')
|
||||
TEST_CALENDAR_STOP = Timestamp('2016-12-31', tz='UTC')
|
||||
|
||||
|
||||
class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
WithAssetFinder,
|
||||
WithInstanceTmpDir,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
ASSET_FINDER_EQUITY_SIDS = 1, 2
|
||||
|
||||
@@ -87,14 +92,14 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
|
||||
self.dest = self.instance_tmpdir.getpath('minute_bars')
|
||||
os.makedirs(self.dest)
|
||||
self.writer = BcolzMinuteBarWriter(
|
||||
self.dest,
|
||||
self.trading_calendar,
|
||||
TEST_CALENDAR_START,
|
||||
TEST_CALENDAR_STOP,
|
||||
US_EQUITIES_MINUTES_PER_DAY,
|
||||
self.writer = BcolzExchangeBarWriter(
|
||||
rootdir=self.dest,
|
||||
calendar=self.trading_calendar,
|
||||
start_session=TEST_CALENDAR_START,
|
||||
end_session=TEST_CALENDAR_STOP,
|
||||
data_frequency='minute',
|
||||
)
|
||||
self.reader = BcolzMinuteBarReader(self.dest)
|
||||
self.reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
def test_version(self):
|
||||
metadata = self.reader._get_metadata()
|
||||
@@ -152,7 +157,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
)
|
||||
|
||||
# Create a new writer with `ohlc_ratios_per_sid` defined.
|
||||
writer_with_ratios = BcolzMinuteBarWriter(
|
||||
writer_with_ratios = BcolzExchangeBarWriter(
|
||||
self.dest,
|
||||
self.trading_calendar,
|
||||
TEST_CALENDAR_START,
|
||||
@@ -161,7 +166,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
ohlc_ratios_per_sid={sid: 25},
|
||||
)
|
||||
writer_with_ratios.write_sid(sid, data)
|
||||
reader = BcolzMinuteBarReader(self.dest)
|
||||
reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
open_price = reader.get_value(sid, minute, 'open')
|
||||
self.assertEquals(10.0, open_price)
|
||||
@@ -449,7 +454,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
# of appending new days will be writing to an existing directory.
|
||||
cday = self.trading_calendar.schedule.index.freq
|
||||
new_end_session = TEST_CALENDAR_STOP + cday
|
||||
writer = BcolzMinuteBarWriter.open(self.dest, new_end_session)
|
||||
writer = BcolzExchangeBarWriter.open(self.dest, new_end_session)
|
||||
next_day_minute = dt + cday
|
||||
new_data = DataFrame(
|
||||
data=ohlcv,
|
||||
@@ -457,7 +462,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
writer.write_sid(sid, new_data)
|
||||
|
||||
# Get a new reader to test updated calendar.
|
||||
reader = BcolzMinuteBarReader(self.dest)
|
||||
reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
second_minute = dt + Timedelta(minutes=1)
|
||||
|
||||
@@ -802,7 +807,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
index=minutes)
|
||||
self.writer.write_sid(sids[1], data_2)
|
||||
|
||||
reader = BcolzMinuteBarReader(self.dest)
|
||||
reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
columns = ['open', 'high', 'low', 'close', 'volume']
|
||||
sids = [sids[0], sids[1]]
|
||||
@@ -854,7 +859,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
index=minutes)
|
||||
self.writer.write_sid(sids[1], data_2)
|
||||
|
||||
reader = BcolzMinuteBarReader(self.dest)
|
||||
reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
columns = ['open', 'high', 'low', 'close', 'volume']
|
||||
sids = [sids[0], sids[1]]
|
||||
@@ -877,6 +882,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
assert_almost_equal(data[sid].loc[minutes, col],
|
||||
arrays[i][j][minute_locs])
|
||||
|
||||
'''
|
||||
def test_adjust_non_trading_minutes(self):
|
||||
start_day = Timestamp('2015-06-01', tz='UTC')
|
||||
end_day = Timestamp('2015-06-02', tz='UTC')
|
||||
@@ -922,7 +928,9 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
Timestamp('2015-06-02 20:01:00', tz='UTC'),
|
||||
'open'
|
||||
)
|
||||
'''
|
||||
|
||||
'''
|
||||
def test_adjust_non_trading_minutes_half_days(self):
|
||||
# half day
|
||||
start_day = Timestamp('2015-11-27', tz='UTC')
|
||||
@@ -978,6 +986,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
Timestamp('2015-11-30 21:01:00', tz='UTC'),
|
||||
'open'
|
||||
)
|
||||
'''
|
||||
|
||||
def test_set_sid_attrs(self):
|
||||
"""Confirm that we can set the attributes of a sid's file correctly.
|
||||
@@ -1023,13 +1032,13 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
|
||||
# Open a new writer to cover `open` method, also truncating only
|
||||
# applies to an existing directory.
|
||||
writer = BcolzMinuteBarWriter.open(self.dest)
|
||||
writer = BcolzExchangeBarWriter.open(self.dest)
|
||||
|
||||
# Truncate to first day with data.
|
||||
writer.truncate(days[0])
|
||||
|
||||
# Refresh the reader since truncate update the metadata.
|
||||
self.reader = BcolzMinuteBarReader(self.dest)
|
||||
self.reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
self.assertEqual(self.writer.last_date_in_output_for_sid(sid), days[0])
|
||||
|
||||
@@ -1087,7 +1096,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
self.writer.truncate(self.test_calendar_start)
|
||||
|
||||
# Refresh the reader since truncate update the metadata.
|
||||
self.reader = BcolzMinuteBarReader(self.dest)
|
||||
self.reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
self.assertEqual(
|
||||
self.writer.last_date_in_output_for_sid(sid),
|
||||
@@ -1198,7 +1207,7 @@ class BcolzMinuteBarTestCase(WithTradingCalendars,
|
||||
self.writer.write(update_reader.read(minutes, sids))
|
||||
|
||||
# Refresh the reader since truncate update the metadata.
|
||||
reader = BcolzMinuteBarReader(self.dest)
|
||||
reader = BcolzExchangeBarReader(self.dest)
|
||||
|
||||
columns = ['open', 'high', 'low', 'close', 'volume']
|
||||
sids = [sids[0], sids[1]]
|
||||
|
||||
@@ -35,7 +35,7 @@ from catalyst.testing.fixtures import (
|
||||
WithBcolzEquityMinuteBarReader,
|
||||
WithBcolzEquityDailyBarReader,
|
||||
WithBcolzFutureMinuteBarReader,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
OHLC = ['open', 'high', 'low', 'close']
|
||||
@@ -254,7 +254,7 @@ EXPECTED_SESSIONS = {
|
||||
|
||||
class MinuteToDailyAggregationTestCase(WithBcolzEquityMinuteBarReader,
|
||||
WithBcolzFutureMinuteBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
# March 2016
|
||||
# Su Mo Tu We Th Fr Sa
|
||||
@@ -525,7 +525,7 @@ class MinuteToDailyAggregationTestCase(WithBcolzEquityMinuteBarReader,
|
||||
|
||||
|
||||
class TestMinuteToSession(WithEquityMinuteBarData,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
# March 2016
|
||||
# Su Mo Tu We Th Fr Sa
|
||||
@@ -565,7 +565,7 @@ class TestMinuteToSession(WithEquityMinuteBarData,
|
||||
|
||||
|
||||
class TestResampleSessionBars(WithBcolzFutureMinuteBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
TRADING_CALENDAR_STRS = ('us_futures',)
|
||||
TRADING_CALENDAR_PRIMARY_CAL = 'us_futures'
|
||||
@@ -667,7 +667,7 @@ class TestResampleSessionBars(WithBcolzFutureMinuteBarReader,
|
||||
|
||||
|
||||
class TestReindexMinuteBars(WithBcolzEquityMinuteBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
TRADING_CALENDAR_STRS = ('us_futures', 'NYSE')
|
||||
TRADING_CALENDAR_PRIMARY_CAL = 'us_futures'
|
||||
@@ -736,7 +736,7 @@ class TestReindexMinuteBars(WithBcolzEquityMinuteBarReader,
|
||||
|
||||
|
||||
class TestReindexSessionBars(WithBcolzEquityDailyBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
TRADING_CALENDAR_STRS = ('us_futures', 'NYSE')
|
||||
TRADING_CALENDAR_PRIMARY_CAL = 'us_futures'
|
||||
|
||||
@@ -50,7 +50,7 @@ from catalyst.testing.fixtures import (
|
||||
WithBcolzEquityDailyBarReader,
|
||||
WithTmpDir,
|
||||
WithTradingCalendars,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
|
||||
@@ -86,7 +86,7 @@ EQUITY_INFO['symbol'] = [chr(ord('A') + n) for n in range(len(EQUITY_INFO))]
|
||||
TEST_QUERY_ASSETS = EQUITY_INFO.index
|
||||
|
||||
|
||||
class BcolzDailyBarTestCase(WithBcolzEquityDailyBarReader, ZiplineTestCase):
|
||||
class BcolzDailyBarTestCase(WithBcolzEquityDailyBarReader, CatalystTestCase):
|
||||
EQUITY_DAILY_BAR_START_DATE = TEST_CALENDAR_START
|
||||
EQUITY_DAILY_BAR_END_DATE = TEST_CALENDAR_STOP
|
||||
|
||||
@@ -372,7 +372,7 @@ class BcolzDailyBarNeverReadAllTestCase(BcolzDailyBarTestCase):
|
||||
class BcolzDailyBarWriterMissingDataTestCase(WithAssetFinder,
|
||||
WithTmpDir,
|
||||
WithTradingCalendars,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
# Sid 3 is active from 2015-06-02 to 2015-06-30.
|
||||
MISSING_DATA_SID = 3
|
||||
# Leave out data for a day in the middle of the query range.
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
# 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.
|
||||
|
||||
'''
|
||||
# ZIPLINE legacy test: Catalyst only uses OPEN calendar, and thus
|
||||
# this test suite is irrelevant, and is commented out in its entirety
|
||||
|
||||
from unittest import TestCase
|
||||
import pandas as pd
|
||||
|
||||
@@ -41,3 +46,5 @@ class TestStatelessRulesCME(StatelessRulesTests, TestCase):
|
||||
|
||||
class TestStatefulRulesCME(StatefulRulesTests, TestCase):
|
||||
CALENDAR_STRING = "CME"
|
||||
|
||||
'''
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
# 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.
|
||||
|
||||
'''
|
||||
# ZIPLINE legacy test: Catalyst only uses OPEN calendar, and thus
|
||||
# this test suite is irrelevant, and is commented out in its entirety
|
||||
|
||||
from unittest import TestCase
|
||||
from datetime import timedelta
|
||||
import pandas as pd
|
||||
@@ -162,3 +167,5 @@ class TestStatelessRulesNYSE(StatelessRulesTests, TestCase):
|
||||
|
||||
class TestStatefulRulesNYSE(StatefulRulesTests, TestCase):
|
||||
CALENDAR_STRING = "NYSE"
|
||||
|
||||
'''
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pandas as pd
|
||||
from catalyst.exchange.ccxt.ccxt_exchange import CCXT
|
||||
from catalyst.finance.order import Order
|
||||
from base import BaseExchangeTestCase
|
||||
from logbook import Logger
|
||||
from catalyst.exchange.exchange_utils import get_exchange_auth
|
||||
from catalyst.utils.paths import ensure_directory
|
||||
|
||||
log = Logger('test_ccxt')
|
||||
|
||||
|
||||
class TestCCXT(BaseExchangeTestCase):
|
||||
@classmethod
|
||||
def setup(self):
|
||||
exchange_name = 'gdax'
|
||||
auth = get_exchange_auth(exchange_name)
|
||||
self.exchange = CCXT(
|
||||
exchange_name=exchange_name,
|
||||
key=auth['key'],
|
||||
secret=auth['secret'],
|
||||
base_currency='eth',
|
||||
portfolio=None
|
||||
)
|
||||
|
||||
def test_order(self):
|
||||
log.info('creating order')
|
||||
asset = self.exchange.get_asset('neo_eth')
|
||||
order_id = self.exchange.order(
|
||||
asset=asset,
|
||||
limit_price=0.07,
|
||||
amount=1,
|
||||
)
|
||||
log.info('order created {}'.format(order_id))
|
||||
assert order_id is not None
|
||||
pass
|
||||
|
||||
def test_open_orders(self):
|
||||
log.info('retrieving open orders')
|
||||
asset = self.exchange.get_asset('neo_eth')
|
||||
orders = self.exchange.get_open_orders(asset)
|
||||
pass
|
||||
|
||||
def test_get_order(self):
|
||||
log.info('retrieving order')
|
||||
order = self.exchange.get_order('2631386', 'neo_eth')
|
||||
# order = self.exchange.get_order('2631386')
|
||||
assert isinstance(order, Order)
|
||||
pass
|
||||
|
||||
def test_cancel_order(self, ):
|
||||
log.info('cancel order')
|
||||
self.exchange.cancel_order('2631386', 'neo_eth')
|
||||
pass
|
||||
|
||||
def test_get_candles(self):
|
||||
log.info('retrieving candles')
|
||||
candles = self.exchange.get_candles(
|
||||
freq='5T',
|
||||
assets=[self.exchange.get_asset('eth_btc')],
|
||||
bar_count=200,
|
||||
start_dt=pd.to_datetime('2017-01-01', utc=True)
|
||||
)
|
||||
|
||||
for asset in candles:
|
||||
df = pd.DataFrame(candles[asset])
|
||||
df.set_index('last_traded', drop=True, inplace=True)
|
||||
pass
|
||||
|
||||
def test_tickers(self):
|
||||
log.info('retrieving tickers')
|
||||
tickers = self.exchange.tickers([
|
||||
self.exchange.get_asset('eth_btc'),
|
||||
])
|
||||
assert len(tickers) == 1
|
||||
pass
|
||||
|
||||
def test_get_balances(self):
|
||||
log.info('testing wallet balances')
|
||||
balances = self.exchange.get_balances()
|
||||
pass
|
||||
|
||||
def test_get_account(self):
|
||||
log.info('testing account data')
|
||||
pass
|
||||
|
||||
def test_orderbook(self):
|
||||
log.info('testing order book for bittrex')
|
||||
asset = self.exchange.get_asset('eth_btc')
|
||||
orderbook = self.exchange.get_orderbook(asset, 'all', limit=10)
|
||||
pass
|
||||
|
||||
def test_get_fees(self):
|
||||
pass
|
||||
@@ -37,7 +37,7 @@ from catalyst.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
WithLogger,
|
||||
WithSimParams,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.classproperty import classproperty
|
||||
|
||||
@@ -46,7 +46,7 @@ class BlotterTestCase(WithCreateBarData,
|
||||
WithLogger,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-05', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-06', tz='utc')
|
||||
ASSET_FINDER_EQUITY_SIDS = 24, 25
|
||||
|
||||
@@ -19,7 +19,7 @@ from catalyst.finance.commission import (
|
||||
)
|
||||
from catalyst.finance.order import Order
|
||||
from catalyst.finance.transaction import Transaction
|
||||
from catalyst.testing import ZiplineTestCase, trades_by_sid_to_dfs
|
||||
from catalyst.testing import CatalystTestCase, trades_by_sid_to_dfs
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAssetFinder,
|
||||
WithSimParams,
|
||||
@@ -28,7 +28,7 @@ from catalyst.testing.fixtures import (
|
||||
from catalyst.utils import factory
|
||||
|
||||
|
||||
class CommissionUnitTests(WithAssetFinder, ZiplineTestCase):
|
||||
class CommissionUnitTests(WithAssetFinder, CatalystTestCase):
|
||||
ASSET_FINDER_EQUITY_SIDS = 1, 2
|
||||
|
||||
@classmethod
|
||||
@@ -272,7 +272,7 @@ class CommissionUnitTests(WithAssetFinder, ZiplineTestCase):
|
||||
self.assertAlmostEqual(15.3, model.calculate(order, txns[2]))
|
||||
|
||||
|
||||
class CommissionAlgorithmTests(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
class CommissionAlgorithmTests(WithDataPortal, WithSimParams, CatalystTestCase):
|
||||
# make sure order commissions are properly incremented
|
||||
|
||||
sidint, = ASSET_FINDER_EQUITY_SIDS = (133,)
|
||||
|
||||
@@ -48,7 +48,7 @@ from catalyst.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.classproperty import classproperty
|
||||
|
||||
@@ -59,7 +59,7 @@ TestOrder = namedtuple('TestOrder', 'limit direction')
|
||||
class SlippageTestCase(WithCreateBarData,
|
||||
WithSimParams,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-05 14:31', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-05 14:36', tz='utc')
|
||||
SIM_PARAMS_CAPITAL_BASE = 1.0e5
|
||||
@@ -566,7 +566,7 @@ class SlippageTestCase(WithCreateBarData,
|
||||
class VolumeShareSlippageTestCase(WithCreateBarData,
|
||||
WithSimParams,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2006-01-05 14:31', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-05 14:36', tz='utc')
|
||||
@@ -743,7 +743,7 @@ class VolumeShareSlippageTestCase(WithCreateBarData,
|
||||
class VolatilityVolumeShareTestCase(WithCreateBarData,
|
||||
WithSimParams,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
ASSET_START_DATE = pd.Timestamp('2006-02-10')
|
||||
|
||||
@@ -890,7 +890,7 @@ class VolatilityVolumeShareTestCase(WithCreateBarData,
|
||||
self.assertIsNone(amount)
|
||||
|
||||
|
||||
class MarketImpactTestCase(WithCreateBarData, ZiplineTestCase):
|
||||
class MarketImpactTestCase(WithCreateBarData, CatalystTestCase):
|
||||
|
||||
ASSET_FINDER_EQUITY_SIDS = (1,)
|
||||
|
||||
@@ -947,7 +947,7 @@ class MarketImpactTestCase(WithCreateBarData, ZiplineTestCase):
|
||||
|
||||
class OrdersStopTestCase(WithSimParams,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2006-01-05 14:31', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-05 14:36', tz='utc')
|
||||
|
||||
@@ -18,7 +18,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAssetFinder,
|
||||
WithTradingSessions,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
from catalyst.utils.functional import dzip_exact
|
||||
@@ -54,7 +54,7 @@ with_default_shape = with_defaults(shape=lambda self: self.default_shape)
|
||||
|
||||
class BasePipelineTestCase(WithTradingSessions,
|
||||
WithAssetFinder,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = Timestamp('2014', tz='UTC')
|
||||
END_DATE = Timestamp('2014-12-31', tz='UTC')
|
||||
ASSET_FINDER_EQUITY_SIDS = list(range(20))
|
||||
|
||||
@@ -34,7 +34,7 @@ from catalyst.pipeline.loaders.blaze.core import (
|
||||
NonPipelineField,
|
||||
)
|
||||
from catalyst.testing import (
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
parameter_space,
|
||||
tmp_asset_finder,
|
||||
)
|
||||
@@ -77,7 +77,7 @@ def _utc_localize_index_level_0(df):
|
||||
return df
|
||||
|
||||
|
||||
class BlazeToPipelineTestCase(WithAssetFinder, ZiplineTestCase):
|
||||
class BlazeToPipelineTestCase(WithAssetFinder, CatalystTestCase):
|
||||
START_DATE = pd.Timestamp(0)
|
||||
END_DATE = pd.Timestamp('2015')
|
||||
|
||||
@@ -1927,7 +1927,7 @@ class BlazeToPipelineTestCase(WithAssetFinder, ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class MiscTestCase(ZiplineTestCase):
|
||||
class MiscTestCase(CatalystTestCase):
|
||||
def test_exprdata_repr(self):
|
||||
strd = set()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import pandas as pd
|
||||
from catalyst.lib.labelarray import LabelArray
|
||||
from catalyst.pipeline import Classifier
|
||||
from catalyst.testing import parameter_space
|
||||
from catalyst.testing.fixtures import ZiplineTestCase
|
||||
from catalyst.testing.fixtures import CatalystTestCase
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.numpy_utils import (
|
||||
categorical_dtype,
|
||||
@@ -585,7 +585,7 @@ class ClassifierTestCase(BasePipelineTestCase):
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
||||
class TestPostProcessAndToWorkSpaceValue(ZiplineTestCase):
|
||||
class TestPostProcessAndToWorkSpaceValue(CatalystTestCase):
|
||||
def test_reversability_categorical(self):
|
||||
class F(Classifier):
|
||||
inputs = ()
|
||||
|
||||
@@ -13,7 +13,7 @@ from catalyst.pipeline import (
|
||||
from catalyst.pipeline.data.testing import TestingDataSet
|
||||
from catalyst.pipeline.factors.equity import SimpleMovingAverage
|
||||
from catalyst.pipeline.filters.smoothing import All
|
||||
from catalyst.testing import ZiplineTestCase, parameter_space
|
||||
from catalyst.testing import CatalystTestCase, parameter_space
|
||||
from catalyst.testing.fixtures import (
|
||||
WithTradingSessions,
|
||||
WithSeededRandomPipelineEngine,
|
||||
@@ -44,7 +44,7 @@ class NDaysAgoClassifier(CustomClassifier):
|
||||
out[:] = cats[0]
|
||||
|
||||
|
||||
class ComputeExtraRowsTestcase(WithTradingSessions, ZiplineTestCase):
|
||||
class ComputeExtraRowsTestcase(WithTradingSessions, CatalystTestCase):
|
||||
|
||||
DATA_MIN_DAY = pd.Timestamp('2012-06', tz='UTC')
|
||||
DATA_MAX_DAY = pd.Timestamp('2015', tz='UTC')
|
||||
@@ -555,7 +555,7 @@ class ComputeExtraRowsTestcase(WithTradingSessions, ZiplineTestCase):
|
||||
|
||||
|
||||
class DownsampledPipelineTestCase(WithSeededRandomPipelineEngine,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
# Extend into the last few days of 2013 to test year/quarter boundaries.
|
||||
START_DATE = pd.Timestamp('2013-12-15', tz='UTC')
|
||||
|
||||
@@ -81,7 +81,7 @@ from catalyst.testing.fixtures import (
|
||||
WithEquityPricingPipelineEngine,
|
||||
WithSeededRandomPipelineEngine,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.memoize import lazyval
|
||||
@@ -199,7 +199,7 @@ class WithConstantInputs(WithTradingEnvironment):
|
||||
cls.assets = cls.asset_finder.retrieve_all(cls.asset_ids)
|
||||
|
||||
|
||||
class ConstantInputTestCase(WithConstantInputs, ZiplineTestCase):
|
||||
class ConstantInputTestCase(WithConstantInputs, CatalystTestCase):
|
||||
def test_bad_dates(self):
|
||||
loader = self.loader
|
||||
engine = SimplePipelineEngine(
|
||||
@@ -816,7 +816,7 @@ class ConstantInputTestCase(WithConstantInputs, ZiplineTestCase):
|
||||
Loader2DataSet.col2)})
|
||||
|
||||
|
||||
class FrameInputTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
class FrameInputTestCase(WithTradingEnvironment, CatalystTestCase):
|
||||
asset_ids = ASSET_FINDER_EQUITY_SIDS = 1, 2, 3
|
||||
start = START_DATE = Timestamp('2015-01-01', tz='utc')
|
||||
end = END_DATE = Timestamp('2015-01-31', tz='utc')
|
||||
@@ -921,7 +921,7 @@ class FrameInputTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
|
||||
|
||||
class SyntheticBcolzTestCase(WithAdjustmentReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
first_asset_start = Timestamp('2015-04-01', tz='UTC')
|
||||
START_DATE = Timestamp('2015-01-01', tz='utc')
|
||||
END_DATE = Timestamp('2015-08-01', tz='utc')
|
||||
@@ -1079,7 +1079,7 @@ class SyntheticBcolzTestCase(WithAdjustmentReader,
|
||||
assert_frame_equal(expected, result)
|
||||
|
||||
|
||||
class ParameterizedFactorTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
class ParameterizedFactorTestCase(WithTradingEnvironment, CatalystTestCase):
|
||||
sids = ASSET_FINDER_EQUITY_SIDS = Int64Index([1, 2, 3])
|
||||
START_DATE = Timestamp('2015-01-31', tz='UTC')
|
||||
END_DATE = Timestamp('2015-03-01', tz='UTC')
|
||||
@@ -1297,7 +1297,7 @@ class ParameterizedFactorTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
|
||||
|
||||
class StringColumnTestCase(WithSeededRandomPipelineEngine,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
def test_string_classifiers_produce_categoricals(self):
|
||||
"""
|
||||
@@ -1327,7 +1327,7 @@ class StringColumnTestCase(WithSeededRandomPipelineEngine,
|
||||
|
||||
|
||||
class WindowSafetyPropagationTestCase(WithSeededRandomPipelineEngine,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
SEEDED_RANDOM_PIPELINE_SEED = 5
|
||||
|
||||
@@ -1378,7 +1378,7 @@ class WindowSafetyPropagationTestCase(WithSeededRandomPipelineEngine,
|
||||
assert_equal(expected_result, results[colname])
|
||||
|
||||
|
||||
class PopulateInitialWorkspaceTestCase(WithConstantInputs, ZiplineTestCase):
|
||||
class PopulateInitialWorkspaceTestCase(WithConstantInputs, CatalystTestCase):
|
||||
|
||||
@parameter_space(window_length=[3, 5], pipeline_length=[5, 10])
|
||||
def test_populate_initial_workspace(self, window_length, pipeline_length):
|
||||
@@ -1503,7 +1503,7 @@ class PopulateInitialWorkspaceTestCase(WithConstantInputs, ZiplineTestCase):
|
||||
|
||||
|
||||
class ChunkedPipelineTestCase(WithEquityPricingPipelineEngine,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
PIPELINE_START_DATE = Timestamp('2006-01-05', tz='UTC')
|
||||
END_DATE = Timestamp('2006-12-29', tz='UTC')
|
||||
|
||||
@@ -24,7 +24,7 @@ from catalyst.pipeline.loaders.utils import (
|
||||
normalize_timestamp_to_query_time,
|
||||
previous_event_indexer,
|
||||
)
|
||||
from catalyst.testing import check_arrays, ZiplineTestCase
|
||||
from catalyst.testing import check_arrays, CatalystTestCase
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAssetFinder,
|
||||
WithTradingSessions,
|
||||
@@ -148,7 +148,7 @@ def make_events(add_nulls):
|
||||
return pd.concat(event_frames, ignore_index=True)
|
||||
|
||||
|
||||
class EventIndexerTestCase(ZiplineTestCase):
|
||||
class EventIndexerTestCase(CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def init_class_fixtures(cls):
|
||||
@@ -269,7 +269,7 @@ class EventIndexerTestCase(ZiplineTestCase):
|
||||
|
||||
class EventsLoaderEmptyTestCase(WithAssetFinder,
|
||||
WithTradingSessions,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2014-01-01')
|
||||
END_DATE = pd.Timestamp('2014-01-30')
|
||||
|
||||
@@ -351,7 +351,7 @@ class EventsLoaderEmptyTestCase(WithAssetFinder,
|
||||
|
||||
class EventsLoaderTestCase(WithAssetFinder,
|
||||
WithTradingSessions,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2014-01-01')
|
||||
END_DATE = pd.Timestamp('2014-01-30')
|
||||
@@ -586,7 +586,7 @@ class BlazeEventsLoaderTestCase(EventsLoaderTestCase):
|
||||
)
|
||||
|
||||
|
||||
class EventLoaderUtilsTestCase(ZiplineTestCase):
|
||||
class EventLoaderUtilsTestCase(CatalystTestCase):
|
||||
# These cases test the following:
|
||||
# 1. Shuffling timestamps in DST/EST produces the correct normalized
|
||||
# timestamps
|
||||
|
||||
@@ -40,7 +40,7 @@ from catalyst.testing import (
|
||||
parameter_space,
|
||||
permute_rows,
|
||||
)
|
||||
from catalyst.testing.fixtures import ZiplineTestCase
|
||||
from catalyst.testing.fixtures import CatalystTestCase
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.numpy_utils import (
|
||||
categorical_dtype,
|
||||
@@ -1246,7 +1246,7 @@ class TestWindowSafety(TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestPostProcessAndToWorkSpaceValue(ZiplineTestCase):
|
||||
class TestPostProcessAndToWorkSpaceValue(CatalystTestCase):
|
||||
@parameter_space(dtype_=(float64_dtype, datetime64ns_dtype))
|
||||
def test_reversability(self, dtype_):
|
||||
class F(Factor):
|
||||
|
||||
@@ -37,7 +37,7 @@ from catalyst.pipeline.filters import (
|
||||
StaticAssets,
|
||||
StaticSids,
|
||||
)
|
||||
from catalyst.testing import parameter_space, permute_rows, ZiplineTestCase
|
||||
from catalyst.testing import parameter_space, permute_rows, CatalystTestCase
|
||||
from catalyst.testing.fixtures import WithSeededRandomPipelineEngine
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.numpy_utils import float64_dtype, int64_dtype
|
||||
@@ -841,7 +841,7 @@ class SidFactor(CustomFactor):
|
||||
|
||||
|
||||
class SpecificAssetsTestCase(WithSeededRandomPipelineEngine,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
ASSET_FINDER_EQUITY_SIDS = tuple(range(10))
|
||||
|
||||
@@ -887,7 +887,7 @@ class SpecificAssetsTestCase(WithSeededRandomPipelineEngine,
|
||||
)
|
||||
|
||||
|
||||
class TestPostProcessAndToWorkSpaceValue(ZiplineTestCase):
|
||||
class TestPostProcessAndToWorkSpaceValue(CatalystTestCase):
|
||||
def test_reversability(self):
|
||||
class F(Filter):
|
||||
inputs = ()
|
||||
|
||||
@@ -60,7 +60,7 @@ from catalyst.testing.fixtures import (
|
||||
WithAdjustmentReader,
|
||||
WithBcolzEquityDailyBarReaderFromCSVs,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
|
||||
@@ -84,7 +84,7 @@ def rolling_vwap(df, length):
|
||||
return Series(out, index=df.index)
|
||||
|
||||
|
||||
class ClosesOnly(WithDataPortal, ZiplineTestCase):
|
||||
class ClosesOnly(WithDataPortal, CatalystTestCase):
|
||||
sids = 1, 2, 3
|
||||
START_DATE = pd.Timestamp('2014-01-01', tz='utc')
|
||||
END_DATE = pd.Timestamp('2014-02-01', tz='utc')
|
||||
@@ -355,7 +355,7 @@ class MockDailyBarSpotReader(object):
|
||||
|
||||
class PipelineAlgorithmTestCase(WithBcolzEquityDailyBarReaderFromCSVs,
|
||||
WithAdjustmentReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
AAPL = 1
|
||||
MSFT = 2
|
||||
BRK_A = 3
|
||||
|
||||
@@ -40,7 +40,7 @@ from catalyst.pipeline.loaders.earnings_estimates import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAdjustmentReader,
|
||||
WithTradingSessions,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.testing.predicates import assert_equal, assert_raises_regex
|
||||
from catalyst.testing.predicates import assert_frame_equal
|
||||
@@ -113,7 +113,7 @@ def create_expected_df_for_factor_compute(start_date,
|
||||
|
||||
class WithEstimates(WithTradingSessions, WithAdjustmentReader):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.loader and cls.events as class
|
||||
CatalystTestCase mixin providing cls.loader and cls.events as class
|
||||
level fixtures.
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ class WithEstimates(WithTradingSessions, WithAdjustmentReader):
|
||||
|
||||
class WithOneDayPipeline(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.events as a class level fixture and
|
||||
CatalystTestCase mixin providing cls.events as a class level fixture and
|
||||
defining a test for all inheritors to use.
|
||||
|
||||
Attributes
|
||||
@@ -246,7 +246,7 @@ class WithOneDayPipeline(WithEstimates):
|
||||
assert_frame_equal(results, self.expected_out)
|
||||
|
||||
|
||||
class PreviousWithOneDayPipeline(WithOneDayPipeline, ZiplineTestCase):
|
||||
class PreviousWithOneDayPipeline(WithOneDayPipeline, CatalystTestCase):
|
||||
"""
|
||||
Tests that previous quarter loader correctly breaks if an incorrect
|
||||
number of quarters is passed.
|
||||
@@ -271,7 +271,7 @@ class PreviousWithOneDayPipeline(WithOneDayPipeline, ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class NextWithOneDayPipeline(WithOneDayPipeline, ZiplineTestCase):
|
||||
class NextWithOneDayPipeline(WithOneDayPipeline, CatalystTestCase):
|
||||
"""
|
||||
Tests that next quarter loader correctly breaks if an incorrect
|
||||
number of quarters is passed.
|
||||
@@ -308,7 +308,7 @@ dummy_df = pd.DataFrame({SID_FIELD_NAME: 0},
|
||||
|
||||
class WithWrongLoaderDefinition(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.events as a class level fixture and
|
||||
CatalystTestCase mixin providing cls.events as a class level fixture and
|
||||
defining a test for all inheritors to use.
|
||||
|
||||
Attributes
|
||||
@@ -372,7 +372,7 @@ class WithWrongLoaderDefinition(WithEstimates):
|
||||
|
||||
|
||||
class PreviousWithWrongNumQuarters(WithWrongLoaderDefinition,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
"""
|
||||
Tests that previous quarter loader correctly breaks if an incorrect
|
||||
number of quarters is passed.
|
||||
@@ -383,7 +383,7 @@ class PreviousWithWrongNumQuarters(WithWrongLoaderDefinition,
|
||||
|
||||
|
||||
class NextWithWrongNumQuarters(WithWrongLoaderDefinition,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
"""
|
||||
Tests that next quarter loader correctly breaks if an incorrect
|
||||
number of quarters is passed.
|
||||
@@ -398,7 +398,7 @@ options = ["split_adjustments_loader",
|
||||
"split_adjusted_asof"]
|
||||
|
||||
|
||||
class WrongSplitsLoaderDefinition(WithEstimates, ZiplineTestCase):
|
||||
class WrongSplitsLoaderDefinition(WithEstimates, CatalystTestCase):
|
||||
"""
|
||||
Test class that tests that loaders break correctly when incorrectly
|
||||
instantiated.
|
||||
@@ -436,7 +436,7 @@ class WrongSplitsLoaderDefinition(WithEstimates, ZiplineTestCase):
|
||||
|
||||
class WithEstimatesTimeZero(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.events as a class level fixture and
|
||||
CatalystTestCase mixin providing cls.events as a class level fixture and
|
||||
defining a test for all inheritors to use.
|
||||
|
||||
Attributes
|
||||
@@ -622,7 +622,7 @@ class WithEstimatesTimeZero(WithEstimates):
|
||||
sid_estimates)
|
||||
|
||||
|
||||
class NextEstimate(WithEstimatesTimeZero, ZiplineTestCase):
|
||||
class NextEstimate(WithEstimatesTimeZero, CatalystTestCase):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
return NextEarningsEstimatesLoader(events, columns)
|
||||
@@ -662,7 +662,7 @@ class BlazeNextEstimateLoaderTestCase(NextEstimate):
|
||||
)
|
||||
|
||||
|
||||
class PreviousEstimate(WithEstimatesTimeZero, ZiplineTestCase):
|
||||
class PreviousEstimate(WithEstimatesTimeZero, CatalystTestCase):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
return PreviousEarningsEstimatesLoader(events, columns)
|
||||
@@ -703,7 +703,7 @@ class BlazePreviousEstimateLoaderTestCase(PreviousEstimate):
|
||||
|
||||
class WithEstimateMultipleQuarters(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin providing cls.events, cls.make_expected_out as
|
||||
CatalystTestCase mixin providing cls.events, cls.make_expected_out as
|
||||
class-level fixtures and self.test_multiple_qtrs_requested as a test.
|
||||
|
||||
Attributes
|
||||
@@ -797,7 +797,7 @@ class WithEstimateMultipleQuarters(WithEstimates):
|
||||
|
||||
|
||||
class NextEstimateMultipleQuarters(
|
||||
WithEstimateMultipleQuarters, ZiplineTestCase
|
||||
WithEstimateMultipleQuarters, CatalystTestCase
|
||||
):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
@@ -854,7 +854,7 @@ class BlazeNextEstimateMultipleQuarters(NextEstimateMultipleQuarters):
|
||||
|
||||
class PreviousEstimateMultipleQuarters(
|
||||
WithEstimateMultipleQuarters,
|
||||
ZiplineTestCase
|
||||
CatalystTestCase
|
||||
):
|
||||
|
||||
@classmethod
|
||||
@@ -903,7 +903,7 @@ class BlazePreviousEstimateMultipleQuarters(PreviousEstimateMultipleQuarters):
|
||||
|
||||
class WithVaryingNumEstimates(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin providing fixtures and a test to ensure that we
|
||||
CatalystTestCase mixin providing fixtures and a test to ensure that we
|
||||
have the correct overwrites when the event date changes. We want to make
|
||||
sure that if we have a quarter with an event date that gets pushed back,
|
||||
we don't start overwriting for the next quarter early. Likewise,
|
||||
@@ -973,7 +973,7 @@ class WithVaryingNumEstimates(WithEstimates):
|
||||
|
||||
class PreviousVaryingNumEstimates(
|
||||
WithVaryingNumEstimates,
|
||||
ZiplineTestCase
|
||||
CatalystTestCase
|
||||
):
|
||||
def assert_compute(self, estimate, today):
|
||||
if today == pd.Timestamp('2015-01-13', tz='utc'):
|
||||
@@ -1003,7 +1003,7 @@ class BlazePreviousVaryingNumEstimates(PreviousVaryingNumEstimates):
|
||||
|
||||
class NextVaryingNumEstimates(
|
||||
WithVaryingNumEstimates,
|
||||
ZiplineTestCase
|
||||
CatalystTestCase
|
||||
):
|
||||
|
||||
def assert_compute(self, estimate, today):
|
||||
@@ -1034,7 +1034,7 @@ class BlazeNextVaryingNumEstimates(NextVaryingNumEstimates):
|
||||
|
||||
class WithEstimateWindows(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin providing fixures and a test to test running a
|
||||
CatalystTestCase mixin providing fixures and a test to test running a
|
||||
Pipeline with an estimates loader over differently-sized windows.
|
||||
|
||||
Attributes
|
||||
@@ -1198,7 +1198,7 @@ class WithEstimateWindows(WithEstimates):
|
||||
)
|
||||
|
||||
|
||||
class PreviousEstimateWindows(WithEstimateWindows, ZiplineTestCase):
|
||||
class PreviousEstimateWindows(WithEstimateWindows, CatalystTestCase):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
return PreviousEarningsEstimatesLoader(events, columns)
|
||||
@@ -1279,7 +1279,7 @@ class BlazePreviousEstimateWindows(PreviousEstimateWindows):
|
||||
return BlazePreviousEstimatesLoader(bz.data(events), columns)
|
||||
|
||||
|
||||
class NextEstimateWindows(WithEstimateWindows, ZiplineTestCase):
|
||||
class NextEstimateWindows(WithEstimateWindows, CatalystTestCase):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
return NextEarningsEstimatesLoader(events, columns)
|
||||
@@ -1394,7 +1394,7 @@ class BlazeNextEstimateWindows(NextEstimateWindows):
|
||||
|
||||
class WithSplitAdjustedWindows(WithEstimateWindows):
|
||||
"""
|
||||
ZiplineTestCase mixin providing fixures and a test to test running a
|
||||
CatalystTestCase mixin providing fixures and a test to test running a
|
||||
Pipeline with an estimates loader over differently-sized windows and with
|
||||
split adjustments.
|
||||
"""
|
||||
@@ -1572,7 +1572,7 @@ class WithSplitAdjustedWindows(WithEstimateWindows):
|
||||
|
||||
|
||||
class PreviousWithSplitAdjustedWindows(WithSplitAdjustedWindows,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
return PreviousSplitAdjustedEarningsEstimatesLoader(
|
||||
@@ -1726,7 +1726,7 @@ class BlazePreviousWithSplitAdjustedWindows(PreviousWithSplitAdjustedWindows):
|
||||
)
|
||||
|
||||
|
||||
class NextWithSplitAdjustedWindows(WithSplitAdjustedWindows, ZiplineTestCase):
|
||||
class NextWithSplitAdjustedWindows(WithSplitAdjustedWindows, CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
@@ -1951,7 +1951,7 @@ class BlazeNextWithSplitAdjustedWindows(NextWithSplitAdjustedWindows):
|
||||
|
||||
class WithSplitAdjustedMultipleEstimateColumns(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin for having multiple estimate columns that are
|
||||
CatalystTestCase mixin for having multiple estimate columns that are
|
||||
split-adjusted to make sure that adjustments are applied correctly.
|
||||
|
||||
Attributes
|
||||
@@ -2136,7 +2136,7 @@ class WithSplitAdjustedMultipleEstimateColumns(WithEstimates):
|
||||
|
||||
|
||||
class PreviousWithSplitAdjustedMultipleEstimateColumns(
|
||||
WithSplitAdjustedMultipleEstimateColumns, ZiplineTestCase
|
||||
WithSplitAdjustedMultipleEstimateColumns, CatalystTestCase
|
||||
):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
@@ -2218,7 +2218,7 @@ class BlazePreviousWithMultipleEstimateColumns(
|
||||
|
||||
|
||||
class NextWithSplitAdjustedMultipleEstimateColumns(
|
||||
WithSplitAdjustedMultipleEstimateColumns, ZiplineTestCase
|
||||
WithSplitAdjustedMultipleEstimateColumns, CatalystTestCase
|
||||
):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
@@ -2295,7 +2295,7 @@ class BlazeNextWithMultipleEstimateColumns(
|
||||
|
||||
class WithAdjustmentBoundaries(WithEstimates):
|
||||
"""
|
||||
ZiplineTestCase mixin providing class-level attributes, methods,
|
||||
CatalystTestCase mixin providing class-level attributes, methods,
|
||||
and a test to make sure that when the split-adjusted-asof-date is not
|
||||
strictly within the date index, we can still apply adjustments correctly.
|
||||
|
||||
@@ -2470,7 +2470,7 @@ class WithAdjustmentBoundaries(WithEstimates):
|
||||
|
||||
|
||||
class PreviousWithAdjustmentBoundaries(WithAdjustmentBoundaries,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
return partial(PreviousSplitAdjustedEarningsEstimatesLoader,
|
||||
@@ -2612,7 +2612,7 @@ class BlazePreviousWithAdjustmentBoundaries(PreviousWithAdjustmentBoundaries):
|
||||
|
||||
|
||||
class NextWithAdjustmentBoundaries(WithAdjustmentBoundaries,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
@classmethod
|
||||
def make_loader(cls, events, columns):
|
||||
return partial(NextSplitAdjustedEarningsEstimatesLoader,
|
||||
@@ -2720,7 +2720,7 @@ class BlazeNextWithAdjustmentBoundaries(NextWithAdjustmentBoundaries):
|
||||
split_adjusted_column_names=['estimate'])
|
||||
|
||||
|
||||
class QuarterShiftTestCase(ZiplineTestCase):
|
||||
class QuarterShiftTestCase(CatalystTestCase):
|
||||
"""
|
||||
This tests, in isolation, quarter calculation logic for shifting quarters
|
||||
backwards/forwards from a starting point.
|
||||
|
||||
@@ -31,12 +31,12 @@ from catalyst.testing import (
|
||||
)
|
||||
from catalyst.testing.fixtures import (
|
||||
WithSeededRandomPipelineEngine,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.numpy_utils import datetime64ns_dtype
|
||||
|
||||
|
||||
class SliceTestCase(WithSeededRandomPipelineEngine, ZiplineTestCase):
|
||||
class SliceTestCase(WithSeededRandomPipelineEngine, CatalystTestCase):
|
||||
sids = ASSET_FINDER_EQUITY_SIDS = Int64Index([1, 2, 3])
|
||||
START_DATE = Timestamp('2015-01-31', tz='UTC')
|
||||
END_DATE = Timestamp('2015-03-01', tz='UTC')
|
||||
|
||||
@@ -42,7 +42,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithSeededRandomPipelineEngine,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.numpy_utils import (
|
||||
bool_dtype,
|
||||
@@ -51,7 +51,7 @@ from catalyst.utils.numpy_utils import (
|
||||
)
|
||||
|
||||
|
||||
class StatisticalBuiltInsTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
class StatisticalBuiltInsTestCase(WithTradingEnvironment, CatalystTestCase):
|
||||
sids = ASSET_FINDER_EQUITY_SIDS = Int64Index([1, 2, 3])
|
||||
START_DATE = Timestamp('2015-01-31', tz='UTC')
|
||||
END_DATE = Timestamp('2015-03-01', tz='UTC')
|
||||
@@ -388,7 +388,7 @@ class StatisticalBuiltInsTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
|
||||
|
||||
class StatisticalMethodsTestCase(WithSeededRandomPipelineEngine,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
sids = ASSET_FINDER_EQUITY_SIDS = Int64Index([1, 2, 3])
|
||||
START_DATE = Timestamp('2015-01-31', tz='UTC')
|
||||
END_DATE = Timestamp('2015-03-01', tz='UTC')
|
||||
|
||||
@@ -21,7 +21,7 @@ from catalyst.pipeline.factors.equity import (
|
||||
AnnualizedVolatility,
|
||||
)
|
||||
from catalyst.testing import parameter_space
|
||||
from catalyst.testing.fixtures import ZiplineTestCase
|
||||
from catalyst.testing.fixtures import CatalystTestCase
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from .base import BasePipelineTestCase
|
||||
|
||||
@@ -115,7 +115,7 @@ class BollingerBandsTestCase(BasePipelineTestCase):
|
||||
self.assertIs(upper, bbands.upper)
|
||||
|
||||
|
||||
class AroonTestCase(ZiplineTestCase):
|
||||
class AroonTestCase(CatalystTestCase):
|
||||
window_length = 10
|
||||
nassets = 5
|
||||
dtype = [('down', 'f8'), ('up', 'f8')]
|
||||
@@ -148,7 +148,7 @@ class AroonTestCase(ZiplineTestCase):
|
||||
assert_equal(out, expected_out)
|
||||
|
||||
|
||||
class TestFastStochasticOscillator(ZiplineTestCase):
|
||||
class TestFastStochasticOscillator(CatalystTestCase):
|
||||
"""
|
||||
Test the Fast Stochastic Oscillator
|
||||
"""
|
||||
@@ -218,7 +218,7 @@ class TestFastStochasticOscillator(ZiplineTestCase):
|
||||
assert_equal(out, expected_out_k, array_decimal=6)
|
||||
|
||||
|
||||
class IchimokuKinkoHyoTestCase(ZiplineTestCase):
|
||||
class IchimokuKinkoHyoTestCase(CatalystTestCase):
|
||||
def test_ichimoku_kinko_hyo(self):
|
||||
window_length = 52
|
||||
today = pd.Timestamp('2014', tz='utc')
|
||||
@@ -334,7 +334,7 @@ class IchimokuKinkoHyoTestCase(ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestRateOfChangePercentage(ZiplineTestCase):
|
||||
class TestRateOfChangePercentage(CatalystTestCase):
|
||||
@parameterized.expand([
|
||||
('constant', [2.] * 10, 0.0),
|
||||
('step', [2.] + [1.] * 9, -50.0),
|
||||
@@ -358,7 +358,7 @@ class TestRateOfChangePercentage(ZiplineTestCase):
|
||||
assert_equal(out, np.full((len(assets),), expected))
|
||||
|
||||
|
||||
class TestLinearWeightedMovingAverage(ZiplineTestCase):
|
||||
class TestLinearWeightedMovingAverage(CatalystTestCase):
|
||||
def test_wma1(self):
|
||||
wma1 = LinearWeightedMovingAverage(
|
||||
inputs=(USEquityPricing.close,),
|
||||
@@ -390,7 +390,7 @@ class TestLinearWeightedMovingAverage(ZiplineTestCase):
|
||||
assert_equal(out, np.array([30., 31., 32., 33., 34.]))
|
||||
|
||||
|
||||
class TestTrueRange(ZiplineTestCase):
|
||||
class TestTrueRange(CatalystTestCase):
|
||||
|
||||
def test_tr_basic(self):
|
||||
tr = TrueRange()
|
||||
@@ -407,7 +407,7 @@ class TestTrueRange(ZiplineTestCase):
|
||||
assert_equal(out, np.full((3,), 2.))
|
||||
|
||||
|
||||
class MovingAverageConvergenceDivergenceTestCase(ZiplineTestCase):
|
||||
class MovingAverageConvergenceDivergenceTestCase(CatalystTestCase):
|
||||
|
||||
def expected_ewma(self, data_df, window):
|
||||
# Comment copied from `test_engine.py`:
|
||||
@@ -532,7 +532,7 @@ class MovingAverageConvergenceDivergenceTestCase(ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class AnnualizedVolatilityTestCase(ZiplineTestCase):
|
||||
class AnnualizedVolatilityTestCase(CatalystTestCase):
|
||||
"""
|
||||
Test Annualized Volatility
|
||||
"""
|
||||
|
||||
@@ -34,7 +34,7 @@ from catalyst.pipeline.factors import RecarrayField
|
||||
from catalyst.pipeline.sentinels import NotSpecified
|
||||
from catalyst.pipeline.term import AssetExists, Slice
|
||||
from catalyst.testing import parameter_space
|
||||
from catalyst.testing.fixtures import WithTradingSessions, ZiplineTestCase
|
||||
from catalyst.testing.fixtures import WithTradingSessions, CatalystTestCase
|
||||
from catalyst.testing.predicates import (
|
||||
assert_equal,
|
||||
assert_raises,
|
||||
@@ -155,7 +155,7 @@ def to_dict(l):
|
||||
return dict(zip(map(str, range(len(l))), l))
|
||||
|
||||
|
||||
class DependencyResolutionTestCase(WithTradingSessions, ZiplineTestCase):
|
||||
class DependencyResolutionTestCase(WithTradingSessions, CatalystTestCase):
|
||||
|
||||
TRADING_CALENDAR_STRS = ('NYSE',)
|
||||
START_DATE = pd.Timestamp('2014-01-02', tz='UTC')
|
||||
|
||||
@@ -55,7 +55,7 @@ from catalyst.testing import (
|
||||
)
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAdjustmentReader,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
# Test calendar ranges over the month of June 2015
|
||||
@@ -258,7 +258,7 @@ DIVIDENDS_EXPECTED = DataFrame(
|
||||
|
||||
|
||||
class USEquityPricingLoaderTestCase(WithAdjustmentReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = TEST_CALENDAR_START
|
||||
END_DATE = TEST_CALENDAR_STOP
|
||||
asset_ids = 1, 2, 3
|
||||
|
||||
@@ -18,7 +18,7 @@ import pandas as pd
|
||||
import catalyst.finance.risk as risk
|
||||
from catalyst.utils import factory
|
||||
|
||||
from catalyst.testing.fixtures import WithTradingEnvironment, ZiplineTestCase
|
||||
from catalyst.testing.fixtures import WithTradingEnvironment, CatalystTestCase
|
||||
|
||||
from catalyst.finance.trading import SimulationParameters
|
||||
|
||||
@@ -30,7 +30,7 @@ BENCHMARK = [BENCHMARK_BASE] * 251
|
||||
DECIMAL_PLACES = 8
|
||||
|
||||
|
||||
class TestRisk(WithTradingEnvironment, ZiplineTestCase):
|
||||
class TestRisk(WithTradingEnvironment, CatalystTestCase):
|
||||
|
||||
def init_instance_fixtures(self):
|
||||
super(TestRisk, self).init_instance_fixtures()
|
||||
|
||||
@@ -22,7 +22,7 @@ import catalyst.finance.risk as risk
|
||||
from catalyst.utils import factory
|
||||
|
||||
from catalyst.finance.trading import SimulationParameters
|
||||
from catalyst.testing.fixtures import WithTradingEnvironment, ZiplineTestCase
|
||||
from catalyst.testing.fixtures import WithTradingEnvironment, CatalystTestCase
|
||||
|
||||
from catalyst.finance.risk.period import RiskMetricsPeriod
|
||||
|
||||
@@ -34,7 +34,7 @@ BENCHMARK = [BENCHMARK_BASE] * 251
|
||||
DECIMAL_PLACES = 8
|
||||
|
||||
|
||||
class TestRisk(WithTradingEnvironment, ZiplineTestCase):
|
||||
class TestRisk(WithTradingEnvironment, CatalystTestCase):
|
||||
|
||||
def init_instance_fixtures(self):
|
||||
super(TestRisk, self).init_instance_fixtures()
|
||||
@@ -232,6 +232,28 @@ class TestRisk(WithTradingEnvironment, ZiplineTestCase):
|
||||
# The sortino ratio is calculated by a empyrical function so testing
|
||||
# of period sortino ratios will be limited to determine if the value is
|
||||
# numerical. This tests for its existence and format.
|
||||
|
||||
# This test needs a different result set that, with some
|
||||
# negative results, otherwise fails in a legitimate way.
|
||||
|
||||
RETURNS = (np.random.rand(251) * 0.1) - 0.05
|
||||
|
||||
self.algo_returns = factory.create_returns_from_list(
|
||||
RETURNS,
|
||||
self.sim_params
|
||||
)
|
||||
|
||||
self.metrics = risk.RiskReport(
|
||||
self.algo_returns,
|
||||
self.sim_params,
|
||||
benchmark_returns=self.benchmark_returns,
|
||||
trading_calendar=self.trading_calendar,
|
||||
treasury_curves=self.env.treasury_curves,
|
||||
)
|
||||
|
||||
for x in self.metrics.month_periods:
|
||||
print (type(x.sortino))
|
||||
|
||||
np.testing.assert_equal(
|
||||
all(isinstance(x.sortino, float)
|
||||
for x in self.metrics.month_periods),
|
||||
|
||||
+52
-52
@@ -109,7 +109,7 @@ from catalyst.testing.fixtures import (
|
||||
WithSimParams,
|
||||
WithTradingEnvironment,
|
||||
WithTmpDir,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.test_algorithms import (
|
||||
access_account_in_init,
|
||||
@@ -190,7 +190,7 @@ import catalyst.utils.factory as factory
|
||||
_multiprocess_can_split_ = False
|
||||
|
||||
|
||||
class TestRecordAlgorithm(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
class TestRecordAlgorithm(WithSimParams, WithDataPortal, CatalystTestCase):
|
||||
ASSET_FINDER_EQUITY_SIDS = 133,
|
||||
|
||||
def test_record_incr(self):
|
||||
@@ -210,7 +210,7 @@ class TestRecordAlgorithm(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
class TestMiscellaneousAPI(WithLogger,
|
||||
WithSimParams,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='UTC')
|
||||
END_DATE = pd.Timestamp('2006-01-04', tz='UTC')
|
||||
@@ -819,7 +819,7 @@ def log_nyse_close(context, data):
|
||||
class TestTransformAlgorithm(WithLogger,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-06', tz='utc')
|
||||
|
||||
@@ -1092,7 +1092,7 @@ def before_trading_start(context, data):
|
||||
class TestPositions(WithLogger,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-06', tz='utc')
|
||||
SIM_PARAMS_CAPITAL_BASE = 1000
|
||||
@@ -1225,7 +1225,7 @@ class TestPositions(WithLogger,
|
||||
|
||||
class TestBeforeTradingStart(WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2016-01-06', tz='utc')
|
||||
END_DATE = pd.Timestamp('2016-01-07', tz='utc')
|
||||
SIM_PARAMS_CAPITAL_BASE = 10000
|
||||
@@ -1578,7 +1578,7 @@ class TestBeforeTradingStart(WithDataPortal,
|
||||
class TestAlgoScript(WithLogger,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-12-31', tz='utc')
|
||||
DATA_PORTAL_USE_MINUTE_DATA = False
|
||||
@@ -2331,7 +2331,7 @@ def handle_data(context, data):
|
||||
class TestCapitalChanges(WithLogger,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
sids = 0, 1
|
||||
|
||||
@@ -2339,16 +2339,16 @@ class TestCapitalChanges(WithLogger,
|
||||
def make_equity_info(cls):
|
||||
data = make_simple_equity_info(
|
||||
cls.sids,
|
||||
pd.Timestamp('2006-01-03', tz='UTC'),
|
||||
pd.Timestamp('2006-01-09', tz='UTC'),
|
||||
pd.Timestamp('2016-01-03', tz='UTC'),
|
||||
pd.Timestamp('2016-01-09', tz='UTC'),
|
||||
)
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def make_equity_minute_bar_data(cls):
|
||||
minutes = cls.trading_calendar.minutes_in_range(
|
||||
pd.Timestamp('2006-01-03', tz='UTC'),
|
||||
pd.Timestamp('2006-01-09', tz='UTC')
|
||||
pd.Timestamp('2016-01-03', tz='UTC'),
|
||||
pd.Timestamp('2016-01-09', tz='UTC')
|
||||
)
|
||||
return trades_by_sid_to_dfs(
|
||||
{
|
||||
@@ -2366,8 +2366,8 @@ class TestCapitalChanges(WithLogger,
|
||||
@classmethod
|
||||
def make_equity_daily_bar_data(cls):
|
||||
days = cls.trading_calendar.sessions_in_range(
|
||||
pd.Timestamp('2006-01-03', tz='UTC'),
|
||||
pd.Timestamp('2006-01-09', tz='UTC')
|
||||
pd.Timestamp('2016-01-03', tz='UTC'),
|
||||
pd.Timestamp('2016-01-09', tz='UTC')
|
||||
)
|
||||
return trades_by_sid_to_dfs(
|
||||
{
|
||||
@@ -2387,12 +2387,12 @@ class TestCapitalChanges(WithLogger,
|
||||
])
|
||||
def test_capital_changes_daily_mode(self, change_type, value):
|
||||
sim_params = factory.create_simulation_parameters(
|
||||
start=pd.Timestamp('2006-01-03', tz='UTC'),
|
||||
end=pd.Timestamp('2006-01-09', tz='UTC')
|
||||
start=pd.Timestamp('2016-01-03', tz='UTC'),
|
||||
end=pd.Timestamp('2016-01-09', tz='UTC')
|
||||
)
|
||||
|
||||
capital_changes = {
|
||||
pd.Timestamp('2006-01-06', tz='UTC'):
|
||||
pd.Timestamp('2016-01-06', tz='UTC'):
|
||||
{'type': change_type, 'value': value}
|
||||
}
|
||||
|
||||
@@ -2429,7 +2429,7 @@ def order_stuff(context, data):
|
||||
self.assertEqual(len(capital_change_packets), 1)
|
||||
self.assertEqual(
|
||||
capital_change_packets[0],
|
||||
{'date': pd.Timestamp('2006-01-06', tz='UTC'),
|
||||
{'date': pd.Timestamp('2016-01-06', tz='UTC'),
|
||||
'type': 'cash',
|
||||
'target': 153000.0 if change_type == 'target' else None,
|
||||
'delta': 50000.0})
|
||||
@@ -2532,23 +2532,23 @@ def order_stuff(context, data):
|
||||
|
||||
self.assertEqual(
|
||||
algo.capital_change_deltas,
|
||||
{pd.Timestamp('2006-01-06', tz='UTC'): 50000.0}
|
||||
{pd.Timestamp('2016-01-06', tz='UTC'): 50000.0}
|
||||
)
|
||||
|
||||
@parameterized.expand([
|
||||
('interday_target', [('2006-01-04', 2388.0)]),
|
||||
('interday_delta', [('2006-01-04', 1000.0)]),
|
||||
('intraday_target', [('2006-01-04 17:00', 2186.0),
|
||||
('2006-01-04 18:00', 2806.0)]),
|
||||
('intraday_delta', [('2006-01-04 17:00', 500.0),
|
||||
('2006-01-04 18:00', 500.0)]),
|
||||
('interday_target', [('2016-01-04', 2388.0)]),
|
||||
('interday_delta', [('2016-01-04', 1000.0)]),
|
||||
('intraday_target', [('2016-01-04 17:00', 2186.0),
|
||||
('2016-01-04 18:00', 2806.0)]),
|
||||
('intraday_delta', [('2016-01-04 17:00', 500.0),
|
||||
('2016-01-04 18:00', 500.0)]),
|
||||
])
|
||||
def test_capital_changes_minute_mode_daily_emission(self, change, values):
|
||||
change_loc, change_type = change.split('_')
|
||||
|
||||
sim_params = factory.create_simulation_parameters(
|
||||
start=pd.Timestamp('2006-01-03', tz='UTC'),
|
||||
end=pd.Timestamp('2006-01-05', tz='UTC'),
|
||||
start=pd.Timestamp('2016-01-03', tz='UTC'),
|
||||
end=pd.Timestamp('2016-01-05', tz='UTC'),
|
||||
data_frequency='minute',
|
||||
capital_base=1000.0
|
||||
)
|
||||
@@ -2692,29 +2692,29 @@ def order_stuff(context, data):
|
||||
if change_loc == 'interday':
|
||||
self.assertEqual(
|
||||
algo.capital_change_deltas,
|
||||
{pd.Timestamp('2006-01-04', tz='UTC'): 1000.0}
|
||||
{pd.Timestamp('2016-01-04', tz='UTC'): 1000.0}
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
algo.capital_change_deltas,
|
||||
{pd.Timestamp('2006-01-04 17:00', tz='UTC'): 500.0,
|
||||
pd.Timestamp('2006-01-04 18:00', tz='UTC'): 500.0}
|
||||
{pd.Timestamp('2016-01-04 17:00', tz='UTC'): 500.0,
|
||||
pd.Timestamp('2016-01-04 18:00', tz='UTC'): 500.0}
|
||||
)
|
||||
|
||||
@parameterized.expand([
|
||||
('interday_target', [('2006-01-04', 2388.0)]),
|
||||
('interday_delta', [('2006-01-04', 1000.0)]),
|
||||
('intraday_target', [('2006-01-04 17:00', 2186.0),
|
||||
('2006-01-04 18:00', 2806.0)]),
|
||||
('intraday_delta', [('2006-01-04 17:00', 500.0),
|
||||
('2006-01-04 18:00', 500.0)]),
|
||||
('interday_target', [('2016-01-04', 2388.0)]),
|
||||
('interday_delta', [('2016-01-04', 1000.0)]),
|
||||
('intraday_target', [('2016-01-04 17:00', 2186.0),
|
||||
('2016-01-04 18:00', 2806.0)]),
|
||||
('intraday_delta', [('2016-01-04 17:00', 500.0),
|
||||
('2016-01-04 18:00', 500.0)]),
|
||||
])
|
||||
def test_capital_changes_minute_mode_minute_emission(self, change, values):
|
||||
change_loc, change_type = change.split('_')
|
||||
|
||||
sim_params = factory.create_simulation_parameters(
|
||||
start=pd.Timestamp('2006-01-03', tz='UTC'),
|
||||
end=pd.Timestamp('2006-01-05', tz='UTC'),
|
||||
start=pd.Timestamp('2016-01-03', tz='UTC'),
|
||||
end=pd.Timestamp('2016-01-05', tz='UTC'),
|
||||
data_frequency='minute',
|
||||
emission_rate='minute',
|
||||
capital_base=1000.0
|
||||
@@ -2933,20 +2933,20 @@ def order_stuff(context, data):
|
||||
if change_loc == 'interday':
|
||||
self.assertEqual(
|
||||
algo.capital_change_deltas,
|
||||
{pd.Timestamp('2006-01-04', tz='UTC'): 1000.0}
|
||||
{pd.Timestamp('2016-01-04', tz='UTC'): 1000.0}
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
algo.capital_change_deltas,
|
||||
{pd.Timestamp('2006-01-04 17:00', tz='UTC'): 500.0,
|
||||
pd.Timestamp('2006-01-04 18:00', tz='UTC'): 500.0}
|
||||
{pd.Timestamp('2016-01-04 17:00', tz='UTC'): 500.0,
|
||||
pd.Timestamp('2016-01-04 18:00', tz='UTC'): 500.0}
|
||||
)
|
||||
|
||||
|
||||
class TestGetDatetime(WithLogger,
|
||||
WithSimParams,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
SIM_PARAMS_DATA_FREQUENCY = 'minute'
|
||||
START_DATE = to_utc('2014-01-02 9:31')
|
||||
END_DATE = to_utc('2014-01-03 9:31')
|
||||
@@ -2994,7 +2994,7 @@ class TestGetDatetime(WithLogger,
|
||||
self.assertFalse(algo.first_bar)
|
||||
|
||||
|
||||
class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
class TestTradingControls(WithSimParams, WithDataPortal, CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-06', tz='utc')
|
||||
|
||||
@@ -3468,7 +3468,7 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
algo.run(data_portal)
|
||||
|
||||
|
||||
class TestAccountControls(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
class TestAccountControls(WithDataPortal, WithSimParams, CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-06', tz='utc')
|
||||
|
||||
@@ -3616,7 +3616,7 @@ class TestAccountControls(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
# format(i, actual_position, expected_positions[i]))
|
||||
|
||||
|
||||
class TestFutureFlip(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
class TestFutureFlip(WithDataPortal, WithSimParams, CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-09', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-10', tz='utc')
|
||||
sid, = ASSET_FINDER_EQUITY_SIDS = (1,)
|
||||
@@ -3677,7 +3677,7 @@ class TestFutureFlip(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
format(i, actual_position, expected_positions[i]))
|
||||
|
||||
|
||||
class TestFuturesAlgo(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
class TestFuturesAlgo(WithDataPortal, WithSimParams, CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2016-01-06', tz='utc')
|
||||
END_DATE = pd.Timestamp('2016-01-07', tz='utc')
|
||||
FUTURE_MINUTE_BAR_START_DATE = pd.Timestamp('2016-01-05', tz='UTC')
|
||||
@@ -3879,7 +3879,7 @@ class TestFuturesAlgo(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
self.assertEqual(txn['price'], expected_price)
|
||||
|
||||
|
||||
class TestTradingAlgorithm(WithTradingEnvironment, ZiplineTestCase):
|
||||
class TestTradingAlgorithm(WithTradingEnvironment, CatalystTestCase):
|
||||
def test_analyze_called(self):
|
||||
self.perf_ref = None
|
||||
|
||||
@@ -3907,7 +3907,7 @@ class TestTradingAlgorithm(WithTradingEnvironment, ZiplineTestCase):
|
||||
|
||||
class TestOrderCancelation(WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2016-01-05', tz='utc')
|
||||
END_DATE = pd.Timestamp('2016-01-07', tz='utc')
|
||||
@@ -4100,7 +4100,7 @@ class TestOrderCancelation(WithDataPortal,
|
||||
self.assertFalse(log_catcher.has_warnings)
|
||||
|
||||
|
||||
class TestEquityAutoClose(WithTradingEnvironment, WithTmpDir, ZiplineTestCase):
|
||||
class TestEquityAutoClose(WithTradingEnvironment, WithTmpDir, CatalystTestCase):
|
||||
"""
|
||||
Tests if delisted equities are properly removed from a portfolio holding
|
||||
positions in said equities.
|
||||
@@ -4661,7 +4661,7 @@ class TestEquityAutoClose(WithTradingEnvironment, WithTmpDir, ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
|
||||
class TestOrderAfterDelist(WithTradingEnvironment, CatalystTestCase):
|
||||
start = pd.Timestamp('2016-01-05', tz='utc')
|
||||
day_1 = pd.Timestamp('2016-01-06', tz='utc')
|
||||
day_4 = pd.Timestamp('2016-01-11', tz='utc')
|
||||
@@ -4756,7 +4756,7 @@ class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
|
||||
self.assertEqual(expected_message, w.message)
|
||||
|
||||
|
||||
class AlgoInputValidationTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
class AlgoInputValidationTestCase(WithTradingEnvironment, CatalystTestCase):
|
||||
|
||||
def test_reject_passing_both_api_methods_and_script(self):
|
||||
script = dedent(
|
||||
@@ -4787,7 +4787,7 @@ class AlgoInputValidationTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestPanelData(WithTradingEnvironment, ZiplineTestCase):
|
||||
class TestPanelData(WithTradingEnvironment, CatalystTestCase):
|
||||
|
||||
@parameterized.expand([
|
||||
('daily',
|
||||
|
||||
@@ -17,7 +17,7 @@ from catalyst.testing.fixtures import (
|
||||
WithCreateBarData,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.catalyst_warnings import ZiplineDeprecationWarning
|
||||
|
||||
@@ -133,7 +133,7 @@ def handle_data(context, data):
|
||||
class TestAPIShim(WithCreateBarData,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
):
|
||||
START_DATE = pd.Timestamp("2016-01-05", tz='UTC')
|
||||
END_DATE = pd.Timestamp("2016-01-28", tz='UTC')
|
||||
|
||||
@@ -80,7 +80,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAssetFinder,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
WithTradingCalendars,
|
||||
)
|
||||
from catalyst.utils.range import range
|
||||
@@ -345,7 +345,7 @@ class AssetTestCase(TestCase):
|
||||
'a' < self.asset3
|
||||
|
||||
|
||||
class TestFuture(WithAssetFinder, ZiplineTestCase):
|
||||
class TestFuture(WithAssetFinder, CatalystTestCase):
|
||||
@classmethod
|
||||
def make_futures_info(cls):
|
||||
return pd.DataFrame.from_dict(
|
||||
@@ -458,7 +458,7 @@ class TestFuture(WithAssetFinder, ZiplineTestCase):
|
||||
TestFuture.asset_finder.lookup_future_symbol('XXX99')
|
||||
|
||||
|
||||
class AssetFinderTestCase(WithTradingCalendars, ZiplineTestCase):
|
||||
class AssetFinderTestCase(WithTradingCalendars, CatalystTestCase):
|
||||
asset_finder_type = AssetFinder
|
||||
|
||||
def write_assets(self, **kwargs):
|
||||
@@ -1395,7 +1395,7 @@ class AssetFinderTestCase(WithTradingCalendars, ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestAssetDBVersioning(ZiplineTestCase):
|
||||
class TestAssetDBVersioning(CatalystTestCase):
|
||||
|
||||
def init_instance_fixtures(self):
|
||||
super(TestAssetDBVersioning, self).init_instance_fixtures()
|
||||
@@ -1533,7 +1533,7 @@ class TestAssetDBVersioning(ZiplineTestCase):
|
||||
assert_equal(expected_data, actual_data)
|
||||
|
||||
|
||||
class TestVectorizedSymbolLookup(WithAssetFinder, ZiplineTestCase):
|
||||
class TestVectorizedSymbolLookup(WithAssetFinder, CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def make_equity_info(cls):
|
||||
|
||||
@@ -38,7 +38,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithCreateBarData,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
from catalyst.utils.calendars.trading_calendar import days_at_time
|
||||
@@ -108,7 +108,7 @@ class WithBarDataChecks(object):
|
||||
class TestMinuteBarData(WithCreateBarData,
|
||||
WithBarDataChecks,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2016-01-05', tz='UTC')
|
||||
END_DATE = ASSET_FINDER_EQUITY_END_DATE = pd.Timestamp(
|
||||
'2016-01-07',
|
||||
@@ -730,7 +730,7 @@ class TestMinuteBarData(WithCreateBarData,
|
||||
|
||||
class TestMinuteBarDataFuturesCalendar(WithCreateBarData,
|
||||
WithBarDataChecks,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2016-01-05', tz='UTC')
|
||||
END_DATE = ASSET_FINDER_EQUITY_END_DATE = pd.Timestamp(
|
||||
@@ -857,7 +857,7 @@ class TestMinuteBarDataFuturesCalendar(WithCreateBarData,
|
||||
class TestDailyBarData(WithCreateBarData,
|
||||
WithBarDataChecks,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2016-01-05', tz='UTC')
|
||||
END_DATE = ASSET_FINDER_EQUITY_END_DATE = pd.Timestamp(
|
||||
'2016-01-11',
|
||||
|
||||
@@ -32,12 +32,12 @@ from catalyst.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
WithTradingCalendars,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
|
||||
class TestBenchmark(WithDataPortal, WithSimParams, WithTradingCalendars,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-12-29', tz='utc')
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ from catalyst.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
WithBcolzFutureMinuteBarReader,
|
||||
WithSimParams,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
WithBcolzFutureMinuteBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
START_DATE = pd.Timestamp('2015-01-05', tz='UTC')
|
||||
END_DATE = pd.Timestamp('2016-10-19', tz='UTC')
|
||||
@@ -1285,7 +1285,7 @@ def record_current_contract(algo, data):
|
||||
|
||||
|
||||
class OrderedContractsTestCase(WithAssetFinder,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def make_root_symbols_info(self):
|
||||
|
||||
@@ -27,7 +27,7 @@ from catalyst.data.minute_bars import (
|
||||
)
|
||||
from catalyst.testing import parameter_space
|
||||
from catalyst.testing.fixtures import (
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
WithTradingSessions,
|
||||
WithDataPortal,
|
||||
alias,
|
||||
@@ -38,7 +38,7 @@ from catalyst.utils.numpy_utils import float64_dtype
|
||||
|
||||
class DataPortalTestBase(WithDataPortal,
|
||||
WithTradingSessions,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
ASSET_FINDER_EQUITY_SIDS = (1, 2)
|
||||
START_DATE = pd.Timestamp('2016-08-01')
|
||||
|
||||
@@ -22,7 +22,7 @@ import pandas as pd
|
||||
from catalyst import examples
|
||||
from catalyst.data.bundles import register, unregister
|
||||
from catalyst.testing import test_resource_path
|
||||
from catalyst.testing.fixtures import WithTmpDir, ZiplineTestCase
|
||||
from catalyst.testing.fixtures import WithTmpDir, CatalystTestCase
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.cache import dataframe_cache
|
||||
from catalyst.utils.paths import update_modified_time
|
||||
@@ -34,7 +34,7 @@ _multiprocess_can_split_ = False
|
||||
matplotlib.use('Agg')
|
||||
|
||||
|
||||
class ExamplesTests(WithTmpDir, ZiplineTestCase):
|
||||
class ExamplesTests(WithTmpDir, CatalystTestCase):
|
||||
# some columns contain values with unique ids that will not be the same
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -22,14 +22,14 @@ from catalyst.test_algorithms import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
DEFAULT_TIMEOUT = 15 # seconds
|
||||
EXTENDED_TIMEOUT = 90
|
||||
|
||||
|
||||
class ExceptionTestCase(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
class ExceptionTestCase(WithDataPortal, WithSimParams, CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
START_DATE = pd.Timestamp('2006-01-07', tz='utc')
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ from catalyst.finance.execution import (
|
||||
)
|
||||
from catalyst.testing.fixtures import (
|
||||
WithLogger,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
|
||||
class ExecutionStyleTestCase(WithLogger, ZiplineTestCase):
|
||||
class ExecutionStyleTestCase(WithLogger, CatalystTestCase):
|
||||
"""
|
||||
Tests for catalyst ExecutionStyle classes.
|
||||
"""
|
||||
@@ -96,62 +96,62 @@ class ExecutionStyleTestCase(WithLogger, ZiplineTestCase):
|
||||
self.assertEqual(style.get_stop_price(True), None)
|
||||
self.assertEqual(style.get_stop_price(False), None)
|
||||
|
||||
@parameterized.expand(EXPECTED_PRICE_ROUNDING)
|
||||
def test_limit_order_prices(self,
|
||||
price,
|
||||
expected_limit_buy_or_stop_sell,
|
||||
expected_limit_sell_or_stop_buy):
|
||||
"""
|
||||
Test price getters for the LimitOrder class.
|
||||
"""
|
||||
style = LimitOrder(price)
|
||||
|
||||
self.assertEqual(expected_limit_buy_or_stop_sell,
|
||||
style.get_limit_price(True))
|
||||
self.assertEqual(expected_limit_sell_or_stop_buy,
|
||||
style.get_limit_price(False))
|
||||
|
||||
self.assertEqual(None, style.get_stop_price(True))
|
||||
self.assertEqual(None, style.get_stop_price(False))
|
||||
|
||||
@parameterized.expand(EXPECTED_PRICE_ROUNDING)
|
||||
def test_stop_order_prices(self,
|
||||
price,
|
||||
expected_limit_buy_or_stop_sell,
|
||||
expected_limit_sell_or_stop_buy):
|
||||
"""
|
||||
Test price getters for StopOrder class. Note that the expected rounding
|
||||
direction for stop prices is the reverse of that for limit prices.
|
||||
"""
|
||||
style = StopOrder(price)
|
||||
|
||||
self.assertEqual(None, style.get_limit_price(False))
|
||||
self.assertEqual(None, style.get_limit_price(True))
|
||||
|
||||
self.assertEqual(expected_limit_buy_or_stop_sell,
|
||||
style.get_stop_price(False))
|
||||
self.assertEqual(expected_limit_sell_or_stop_buy,
|
||||
style.get_stop_price(True))
|
||||
|
||||
@parameterized.expand(EXPECTED_PRICE_ROUNDING)
|
||||
def test_stop_limit_order_prices(self,
|
||||
price,
|
||||
expected_limit_buy_or_stop_sell,
|
||||
expected_limit_sell_or_stop_buy):
|
||||
"""
|
||||
Test price getters for StopLimitOrder class. Note that the expected
|
||||
rounding direction for stop prices is the reverse of that for limit
|
||||
prices.
|
||||
"""
|
||||
|
||||
style = StopLimitOrder(price, price + 1)
|
||||
|
||||
self.assertEqual(expected_limit_buy_or_stop_sell,
|
||||
style.get_limit_price(True))
|
||||
self.assertEqual(expected_limit_sell_or_stop_buy,
|
||||
style.get_limit_price(False))
|
||||
|
||||
self.assertEqual(expected_limit_buy_or_stop_sell + 1,
|
||||
style.get_stop_price(False))
|
||||
self.assertEqual(expected_limit_sell_or_stop_buy + 1,
|
||||
style.get_stop_price(True))
|
||||
# @parameterized.expand(EXPECTED_PRICE_ROUNDING)
|
||||
# def test_limit_order_prices(self,
|
||||
# price,
|
||||
# expected_limit_buy_or_stop_sell,
|
||||
# expected_limit_sell_or_stop_buy):
|
||||
# """
|
||||
# Test price getters for the LimitOrder class.
|
||||
# """
|
||||
# style = LimitOrder()
|
||||
#
|
||||
# # self.assertEqual(expected_limit_buy_or_stop_sell,
|
||||
# # style.get_limit_price(True))
|
||||
# # self.assertEqual(expected_limit_sell_or_stop_buy,
|
||||
# # style.get_limit_price(False))
|
||||
#
|
||||
# self.assertEqual(None, style.get_stop_price(True))
|
||||
# self.assertEqual(None, style.get_stop_price(False))
|
||||
#
|
||||
# # @parameterized.expand(EXPECTED_PRICE_ROUNDING)
|
||||
# def test_stop_order_prices(self,
|
||||
# price,
|
||||
# expected_limit_buy_or_stop_sell,
|
||||
# expected_limit_sell_or_stop_buy):
|
||||
# """
|
||||
# Test price getters for StopOrder class. Note that the expected rounding
|
||||
# direction for stop prices is the reverse of that for limit prices.
|
||||
# """
|
||||
# style = StopOrder(price)
|
||||
#
|
||||
# self.assertEqual(None, style.get_limit_price(False))
|
||||
# self.assertEqual(None, style.get_limit_price(True))
|
||||
#
|
||||
# # self.assertEqual(expected_limit_buy_or_stop_sell,
|
||||
# # style.get_stop_price(False))
|
||||
# # self.assertEqual(expected_limit_sell_or_stop_buy,
|
||||
# # style.get_stop_price(True))
|
||||
#
|
||||
# # @parameterized.expand(EXPECTED_PRICE_ROUNDING)
|
||||
# def test_stop_limit_order_prices(self,
|
||||
# price,
|
||||
# expected_limit_buy_or_stop_sell,
|
||||
# expected_limit_sell_or_stop_buy):
|
||||
# """
|
||||
# Test price getters for StopLimitOrder class. Note that the expected
|
||||
# rounding direction for stop prices is the reverse of that for limit
|
||||
# prices.
|
||||
# """
|
||||
#
|
||||
# style = StopLimitOrder(price, price + 1)
|
||||
#
|
||||
# self.assertEqual(expected_limit_buy_or_stop_sell,
|
||||
# style.get_limit_price(True))
|
||||
# self.assertEqual(expected_limit_sell_or_stop_buy,
|
||||
# style.get_limit_price(False))
|
||||
#
|
||||
# self.assertEqual(expected_limit_buy_or_stop_sell + 1,
|
||||
# style.get_stop_price(False))
|
||||
# self.assertEqual(expected_limit_sell_or_stop_buy + 1,
|
||||
# style.get_stop_price(True))
|
||||
|
||||
@@ -26,7 +26,7 @@ from catalyst.testing import FetcherDataPortal
|
||||
from catalyst.testing.fixtures import (
|
||||
WithResponses,
|
||||
WithSimParams,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from .resources.fetcher_inputs.fetcher_test_data import (
|
||||
AAPL_CSV_DATA,
|
||||
@@ -45,7 +45,7 @@ from .resources.fetcher_inputs.fetcher_test_data import (
|
||||
|
||||
class FetcherTestCase(WithResponses,
|
||||
WithSimParams,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def make_equity_info(cls):
|
||||
|
||||
@@ -46,7 +46,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithLogger,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
import catalyst.utils.factory as factory
|
||||
@@ -59,10 +59,10 @@ _multiprocess_can_split_ = False
|
||||
|
||||
class FinanceTestCase(WithLogger,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
ASSET_FINDER_EQUITY_SIDS = 1, 2, 133
|
||||
start = START_DATE = pd.Timestamp('2006-01-01', tz='utc')
|
||||
end = END_DATE = pd.Timestamp('2006-12-31', tz='utc')
|
||||
start = START_DATE = pd.Timestamp('2016-01-01', tz='utc')
|
||||
end = END_DATE = pd.Timestamp('2016-12-31', tz='utc')
|
||||
|
||||
def init_instance_fixtures(self):
|
||||
super(FinanceTestCase, self).init_instance_fixtures()
|
||||
@@ -236,7 +236,7 @@ class FinanceTestCase(WithLogger,
|
||||
data_portal = DataPortal(
|
||||
env.asset_finder, self.trading_calendar,
|
||||
first_trading_day=equity_minute_reader.first_trading_day,
|
||||
equity_minute_reader=equity_minute_reader,
|
||||
minute_reader=equity_minute_reader,
|
||||
)
|
||||
else:
|
||||
sim_params = factory.create_simulation_parameters(
|
||||
@@ -267,7 +267,7 @@ class FinanceTestCase(WithLogger,
|
||||
data_portal = DataPortal(
|
||||
env.asset_finder, self.trading_calendar,
|
||||
first_trading_day=equity_daily_reader.first_trading_day,
|
||||
equity_daily_reader=equity_daily_reader,
|
||||
daily_reader=equity_daily_reader,
|
||||
)
|
||||
|
||||
if "default_slippage" not in params or \
|
||||
@@ -403,7 +403,7 @@ class FinanceTestCase(WithLogger,
|
||||
|
||||
class TradingEnvironmentTestCase(WithLogger,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
"""
|
||||
Tests for date management utilities in catalyst.finance.trading.
|
||||
"""
|
||||
|
||||
@@ -37,7 +37,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithCreateBarData,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
alias,
|
||||
)
|
||||
|
||||
@@ -530,7 +530,7 @@ MINUTE_FIELD_INFO = {
|
||||
}
|
||||
|
||||
|
||||
class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
class MinuteEquityHistoryTestCase(WithHistory, CatalystTestCase):
|
||||
|
||||
EQUITY_DAILY_BAR_SOURCE_FROM_MINUTE = True
|
||||
DATA_PORTAL_FIRST_TRADING_DAY = alias('TRADING_START_DT')
|
||||
@@ -1598,7 +1598,7 @@ class NoPrefetchMinuteEquityHistoryTestCase(MinuteEquityHistoryTestCase):
|
||||
DATA_PORTAL_DAILY_HISTORY_PREFETCH = 0
|
||||
|
||||
|
||||
class DailyEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
class DailyEquityHistoryTestCase(WithHistory, CatalystTestCase):
|
||||
CREATE_BARDATA_DATA_FREQUENCY = 'daily'
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -6,7 +6,7 @@ import numpy as np
|
||||
from toolz import take
|
||||
|
||||
from catalyst.lib.labelarray import LabelArray
|
||||
from catalyst.testing import check_arrays, parameter_space, ZiplineTestCase
|
||||
from catalyst.testing import check_arrays, parameter_space, CatalystTestCase
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.compat import unicode
|
||||
|
||||
@@ -31,7 +31,7 @@ def all_ufuncs():
|
||||
return (f for f in vars(np).values() if isinstance(f, ufunc_type))
|
||||
|
||||
|
||||
class LabelArrayTestCase(ZiplineTestCase):
|
||||
class LabelArrayTestCase(CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def init_class_fixtures(cls):
|
||||
|
||||
@@ -22,7 +22,7 @@ from catalyst.data.us_equity_pricing import PanelBarReader
|
||||
from catalyst.testing import ExplodingObject
|
||||
from catalyst.testing.fixtures import (
|
||||
WithAssetFinder,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
|
||||
@@ -99,7 +99,7 @@ class WithPanelBarReader(WithAssetFinder):
|
||||
|
||||
|
||||
class TestPanelDailyBarReader(WithPanelBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
FREQUENCY = 'daily'
|
||||
|
||||
@@ -110,7 +110,7 @@ class TestPanelDailyBarReader(WithPanelBarReader,
|
||||
|
||||
|
||||
class TestPanelMinuteBarReader(WithPanelBarReader,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
FREQUENCY = 'minute'
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ from catalyst.testing.fixtures import (
|
||||
WithSimParams,
|
||||
WithTmpDir,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
|
||||
@@ -264,7 +264,7 @@ def setup_env_data(env, sim_params, sids, futures_sids=[]):
|
||||
env.write_data(futures_data=futures_data)
|
||||
|
||||
|
||||
class TestSplitPerformance(WithSimParams, WithTmpDir, ZiplineTestCase):
|
||||
class TestSplitPerformance(WithSimParams, WithTmpDir, CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-04', tz='utc')
|
||||
SIM_PARAMS_CAPITAL_BASE = 10e3
|
||||
@@ -402,7 +402,7 @@ class TestSplitPerformance(WithSimParams, WithTmpDir, ZiplineTestCase):
|
||||
|
||||
class TestDividendPerformance(WithSimParams,
|
||||
WithInstanceTmpDir,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
START_DATE = pd.Timestamp('2006-01-03', tz='utc')
|
||||
END_DATE = pd.Timestamp('2006-01-10', tz='utc')
|
||||
ASSET_FINDER_EQUITY_SIDS = 1, 2
|
||||
@@ -1030,7 +1030,7 @@ class TestDividendPerformanceHolidayStyle(TestDividendPerformance):
|
||||
|
||||
class TestPositionPerformance(WithInstanceTmpDir,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
def create_environment_stuff(self,
|
||||
num_days=4,
|
||||
@@ -1951,7 +1951,7 @@ shares in position"
|
||||
|
||||
class TestPositionTracker(WithTradingEnvironment,
|
||||
WithInstanceTmpDir,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
ASSET_FINDER_EQUITY_SIDS = 1, 2
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -18,7 +18,7 @@ from catalyst.finance.asset_restrictions import (
|
||||
from catalyst.testing import parameter_space
|
||||
from catalyst.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ ALLOWED = RESTRICTION_STATES.ALLOWED
|
||||
MINUTE = pd.Timedelta(minutes=1)
|
||||
|
||||
|
||||
class RestrictionsTestCase(WithDataPortal, ZiplineTestCase):
|
||||
class RestrictionsTestCase(WithDataPortal, CatalystTestCase):
|
||||
|
||||
ASSET_FINDER_EQUITY_SIDS = 1, 2, 3
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithLogger,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils import factory
|
||||
from catalyst.utils.security_list import (
|
||||
@@ -84,7 +84,7 @@ class IterateRLAlgo(TradingAlgorithm):
|
||||
|
||||
class SecurityListTestCase(WithLogger,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def init_class_fixtures(cls):
|
||||
|
||||
@@ -19,7 +19,7 @@ from catalyst.testing import (
|
||||
from catalyst.testing.fixtures import (
|
||||
WithConstantEquityMinuteBarData,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.testing.slippage import TestingSlippage
|
||||
from catalyst.utils.numpy_utils import bool_dtype
|
||||
@@ -123,7 +123,7 @@ class TestMakeBooleanArray(TestCase):
|
||||
|
||||
class TestTestingSlippage(WithConstantEquityMinuteBarData,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
ASSET_FINDER_EQUITY_SYMBOLS = ('A',)
|
||||
ASSET_FINDER_EQUITY_SIDS = (1,)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ from catalyst.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
WithSimParams,
|
||||
WithTradingEnvironment,
|
||||
ZiplineTestCase,
|
||||
CatalystTestCase,
|
||||
)
|
||||
from catalyst.utils import factory
|
||||
from catalyst.testing.core import FakeDataPortal
|
||||
@@ -53,7 +53,7 @@ class BeforeTradingAlgorithm(TradingAlgorithm):
|
||||
FREQUENCIES = {'daily': 0, 'minute': 1} # daily is less frequent than minute
|
||||
|
||||
|
||||
class TestTradeSimulation(WithTradingEnvironment, ZiplineTestCase):
|
||||
class TestTradeSimulation(WithTradingEnvironment, CatalystTestCase):
|
||||
|
||||
def fake_minutely_benchmark(self, dt):
|
||||
return 0.01
|
||||
@@ -115,7 +115,7 @@ class BeforeTradingStartsOnlyClock(object):
|
||||
|
||||
class TestBeforeTradingStartSimulationDt(WithSimParams,
|
||||
WithDataPortal,
|
||||
ZiplineTestCase):
|
||||
CatalystTestCase):
|
||||
|
||||
def test_bts_simulation_dt(self):
|
||||
code = """
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from pandas import Timestamp
|
||||
from nose_parameterized import parameterized
|
||||
|
||||
from catalyst.testing import ZiplineTestCase
|
||||
from catalyst.testing import CatalystTestCase
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
from catalyst.utils.date_utils import compute_date_range_chunks
|
||||
|
||||
@@ -13,7 +13,7 @@ def T(s):
|
||||
return Timestamp(s, tz='UTC')
|
||||
|
||||
|
||||
class TestDateUtils(ZiplineTestCase):
|
||||
class TestDateUtils(CatalystTestCase):
|
||||
|
||||
@classmethod
|
||||
def init_class_fixtures(cls):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from catalyst.testing.fixtures import ZiplineTestCase
|
||||
from catalyst.testing.fixtures import CatalystTestCase
|
||||
from catalyst.testing.predicates import (
|
||||
assert_equal,
|
||||
assert_is,
|
||||
@@ -31,7 +31,7 @@ class D(object):
|
||||
return 'D.delegate'
|
||||
|
||||
|
||||
class ComposeTypesTestCase(ZiplineTestCase):
|
||||
class ComposeTypesTestCase(CatalystTestCase):
|
||||
|
||||
def test_identity(self):
|
||||
assert_is(
|
||||
@@ -67,7 +67,7 @@ class N(type):
|
||||
return super(N, mcls).__new__(mcls, name, bases, dict_)
|
||||
|
||||
|
||||
class WithMetaclassesTestCase(ZiplineTestCase):
|
||||
class WithMetaclassesTestCase(CatalystTestCase):
|
||||
def test_with_metaclasses_no_subclasses(self):
|
||||
class E(with_metaclasses((M, N))):
|
||||
pass
|
||||
|
||||
@@ -3,7 +3,7 @@ Tests for catalyst/utils/pandas_utils.py
|
||||
"""
|
||||
import pandas as pd
|
||||
|
||||
from catalyst.testing import parameter_space, ZiplineTestCase
|
||||
from catalyst.testing import parameter_space, CatalystTestCase
|
||||
from catalyst.testing.predicates import assert_equal
|
||||
from catalyst.utils.pandas_utils import (
|
||||
categorical_df_concat,
|
||||
@@ -11,7 +11,7 @@ from catalyst.utils.pandas_utils import (
|
||||
)
|
||||
|
||||
|
||||
class TestNearestUnequalElements(ZiplineTestCase):
|
||||
class TestNearestUnequalElements(CatalystTestCase):
|
||||
|
||||
@parameter_space(tz=['UTC', 'US/Eastern'], __fail_fast=True)
|
||||
def test_nearest_unequal_elements(self, tz):
|
||||
@@ -86,7 +86,7 @@ class TestNearestUnequalElements(ZiplineTestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestCatDFConcat(ZiplineTestCase):
|
||||
class TestCatDFConcat(CatalystTestCase):
|
||||
|
||||
def test_categorical_df_concat(self):
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from catalyst.testing import ZiplineTestCase
|
||||
from catalyst.testing import CatalystTestCase
|
||||
from catalyst.utils.sharedoc import copydoc
|
||||
|
||||
|
||||
class TestSharedoc(ZiplineTestCase):
|
||||
class TestSharedoc(CatalystTestCase):
|
||||
|
||||
def test_copydoc(self):
|
||||
def original_docstring_function():
|
||||
|
||||
Reference in New Issue
Block a user