diff --git a/catalyst/examples/buy_and_hodl.py b/catalyst/examples/buy_and_hodl.py index b2b6a7ec..e4146b3c 100644 --- a/catalyst/examples/buy_and_hodl.py +++ b/catalyst/examples/buy_and_hodl.py @@ -14,7 +14,9 @@ # 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. +import pandas as pd +from catalyst import run_algorithm from catalyst.api import ( order_target_value, symbol, @@ -23,6 +25,7 @@ from catalyst.api import ( get_open_orders, ) + def initialize(context): context.ASSET_NAME = 'BTC_USDT' context.TARGET_HODL_RATIO = 0.8 @@ -38,6 +41,7 @@ def initialize(context): context.i = 0 + def handle_data(context, data): context.i += 1 @@ -64,8 +68,8 @@ def handle_data(context, data): order_target_value( context.asset, target_hodl_value, - limit_price=price*1.1, - stop_price=price*0.9, + limit_price=price * 1.1, + stop_price=price * 0.9, ) record( @@ -76,6 +80,7 @@ def handle_data(context, data): leverage=context.account.leverage, ) + def analyze(context=None, results=None): import matplotlib.pyplot as plt @@ -134,4 +139,17 @@ def analyze(context=None, results=None): # Show the plot. plt.gcf().set_size_inches(18, 8) - plt.show() \ No newline at end of file + plt.show() + + +run_algorithm( + capital_base=10000, + data_frequency='minute', + initialize=initialize, + handle_data=handle_data, + analyze=analyze, + exchange_name='poloniex', + base_currency='usd', + start=pd.to_datetime('2017-10-1', utc=True), + end=pd.to_datetime('2017-11-10', utc=True), +) diff --git a/catalyst/examples/mean_reversion_simple.py b/catalyst/examples/mean_reversion_simple.py index 58e15006..9aa5ada3 100644 --- a/catalyst/examples/mean_reversion_simple.py +++ b/catalyst/examples/mean_reversion_simple.py @@ -1,14 +1,12 @@ # For this example, we're going to write a simple momentum script. When the # stock goes up quickly, we're going to buy; when it goes down quickly, we're # going to sell. Hopefully we'll ride the waves. -from datetime import timedelta import pandas as pd import talib # To run an algorithm in Catalyst, you need two functions: initialize and # handle_data. from logbook import Logger -from talib.common import MA_Type from catalyst import run_algorithm from catalyst.api import symbol, record, order_target_percent, \ @@ -17,10 +15,10 @@ from catalyst.api import symbol, record, order_target_percent, \ # In this example, Catalyst will create the `.catalyst/data/live_algos` # directory. If we stop and start the algorithm, Catalyst will resume its # state using the files included in the folder. -from catalyst.exchange.stats_utils import extract_transactions, trend_direction +from catalyst.exchange.stats_utils import extract_transactions -algo_namespace = 'mean_reversion_simple' -log = Logger(algo_namespace) +NAMESPACE = 'mean_reversion_simple' +log = Logger(NAMESPACE) def initialize(context): @@ -30,7 +28,7 @@ def initialize(context): # parameters or values you're going to use. # In our example, we're looking at Ether in USD Tether. - context.eth_btc = symbol('neo_usd') + context.neo_usd = symbol('neo_usd') context.base_price = None context.current_day = None @@ -50,14 +48,14 @@ def handle_data(context, data): context.current_day = today # We're computing the volume-weighted-average-price of the security - # defined above, in the context.eth_btc variable. For this example, we're + # defined above, in the context.neo_usd variable. For this example, we're # using three bars on the 15 min bars. # The frequency attribute determine the bar size. We use this convention # for the frequency alias: # http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases prices = data.history( - context.eth_btc, + context.neo_usd, fields='close', bar_count=50, frequency='15T' @@ -72,7 +70,7 @@ def handle_data(context, data): # We need a variable for the current price of the security to compare to # the average. Since we are requesting two fields, data.current() # returns a DataFrame with - current = data.current(context.eth_btc, fields=['close', 'volume']) + current = data.current(context.neo_usd, fields=['close', 'volume']) price = current['close'] # If base_price is not set, we use the current value. This is the @@ -101,19 +99,19 @@ def handle_data(context, data): # 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.eth_btc) + orders = get_open_orders(context.neo_usd) if len(orders) > 0: return # Exit if we cannot trade - if not data.can_trade(context.eth_btc): + if not data.can_trade(context.neo_usd): return # Another powerful built-in feature of the Catalyst backtester is the # portfolio object. The portfolio object tracks your positions, cash, # cost basis of specific holdings, and more. In this line, we calculate # how long or short our position is at this minute. - pos_amount = context.portfolio.positions[context.eth_btc].amount + pos_amount = context.portfolio.positions[context.neo_usd].amount if rsi[-1] <= 30 and pos_amount == 0: log.info( @@ -121,7 +119,7 @@ def handle_data(context, data): data.current_dt, price, rsi[-1] ) ) - order_target_percent(context.eth_btc, 1) + order_target_percent(context.neo_usd, 1) context.traded_today = True elif rsi[-1] >= 80 and pos_amount > 0: @@ -130,7 +128,7 @@ def handle_data(context, data): data.current_dt, price, rsi[-1] ) ) - order_target_percent(context.eth_btc, 0) + order_target_percent(context.neo_usd, 0) context.traded_today = True @@ -150,7 +148,7 @@ def analyze(context=None, perf=None): perf.loc[:, 'price'].plot(ax=ax2, label='Price') ax2.set_ylabel('{asset} ({base})'.format( - asset=context.eth_btc.symbol, base=base_currency + asset=context.neo_usd.symbol, base=base_currency )) transaction_df = extract_transactions(perf) @@ -218,10 +216,10 @@ def analyze(context=None, perf=None): if __name__ == '__main__': # The execution mode: backtest or live - MODE = 'backtest' + MODE = 'live' if MODE == 'backtest': - # catalyst run -f catalyst/examples/mean_reversion_simple.py -x poloniex -s 2017-7-1 -e 2017-7-31 -c usdt -n mean-reversion --data-frequency minute --capital-base 10000 + # 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 run_algorithm( capital_base=10000, data_frequency='minute', @@ -229,7 +227,7 @@ if __name__ == '__main__': handle_data=handle_data, analyze=analyze, exchange_name='bitfinex', - algo_namespace=algo_namespace, + algo_namespace=NAMESPACE, base_currency='usd', start=pd.to_datetime('2017-10-1', utc=True), end=pd.to_datetime('2017-11-10', utc=True), @@ -242,7 +240,7 @@ if __name__ == '__main__': analyze=analyze, exchange_name='bitfinex', live=True, - algo_namespace=algo_namespace, + algo_namespace=NAMESPACE, base_currency='usd', live_graph=True ) diff --git a/catalyst/examples/rsi_profit_target.py b/catalyst/examples/rsi_profit_target.py index a426c402..5e24f122 100644 --- a/catalyst/examples/rsi_profit_target.py +++ b/catalyst/examples/rsi_profit_target.py @@ -250,27 +250,27 @@ def analyze(context=None, results=None): pass -run_algorithm( - initialize=initialize, - handle_data=handle_data, - analyze=analyze, - exchange_name='bittrex', - live=True, - algo_namespace=algo_namespace, - base_currency='btc', - live_graph=False -) - -# Backtest # run_algorithm( -# capital_base=0.5, -# data_frequency='minute', # initialize=initialize, # handle_data=handle_data, # analyze=analyze, -# exchange_name='poloniex', +# exchange_name='bittrex', +# live=True, # algo_namespace=algo_namespace, # base_currency='btc', -# start=pd.to_datetime('2017-9-1', utc=True), -# end=pd.to_datetime('2017-10-1', utc=True), +# live_graph=False # ) + +# Backtest +run_algorithm( + capital_base=0.5, + data_frequency='minute', + initialize=initialize, + handle_data=handle_data, + analyze=analyze, + exchange_name='poloniex', + algo_namespace=algo_namespace, + base_currency='btc', + start=pd.to_datetime('2017-9-1', utc=True), + end=pd.to_datetime('2017-10-1', utc=True), +) diff --git a/catalyst/exchange/stats_utils.py b/catalyst/exchange/stats_utils.py index 1290f71f..e982de2a 100644 --- a/catalyst/exchange/stats_utils.py +++ b/catalyst/exchange/stats_utils.py @@ -30,14 +30,25 @@ def crossover(source, target): bool """ - if source[-1] is np.nan or source[-2] is np.nan \ - or target[-1] is np.nan or target[-2] is np.nan: - return False + if isinstance(target, numbers.Number): + if source[-1] is np.nan or source[-2] is np.nan \ + or target is np.nan: + return False + + if source[-1] >= target > source[-2]: + return True + else: + return False - if source[-1] > target[-1] and source[-2] < target[-2]: - return True else: - return False + if source[-1] is np.nan or source[-2] is np.nan \ + or target[-1] is np.nan or target[-2] is np.nan: + return False + + if source[-1] > target[-1] and source[-2] < target[-2]: + return True + else: + return False def crossunder(source, target): diff --git a/catalyst/support/issue_47.py b/catalyst/support/issue_47.py index 5341dd03..3ebc0bc7 100644 --- a/catalyst/support/issue_47.py +++ b/catalyst/support/issue_47.py @@ -1,6 +1,6 @@ """ Requires Catalyst version 0.3.0 or above -Tested on Catalyst version 0.3.2 +Tested on Catalyst version 0.3.3 These example aims to provide and easy way for users to learn how to collect data from the different exchanges. You simply need to specify the exchange and the market that you want to focus on. @@ -27,7 +27,7 @@ from catalyst.api import ( def initialize(context): context.i = -1 # counts the minutes context.exchange = 'poloniex' # must match the exchange specified in run_algorithm - context.base_currency = 'eth' # must match the base currency specified in run_algorithm + context.base_currency = 'btc' # must match the base currency specified in run_algorithm def handle_data(context, data): @@ -56,21 +56,21 @@ def handle_data(context, data): # 30 minute interval ohlcv data (the standard data required for candlestick or indicators/signals) # 30T means 30 minutes re-sampling of one minute data. change to your desire time interval. - open = fill(data.history(coin, 'open', bar_count=lookback, - frequency='1m')).resample('30T').first() + opened = fill(data.history(coin, 'open', bar_count=lookback, + frequency='30T')).values high = fill(data.history(coin, 'high', bar_count=lookback, - frequency='1m')).resample('30T').max() + frequency='30T')).values low = fill(data.history(coin, 'low', bar_count=lookback, - frequency='1m')).resample('30T').min() + frequency='30T')).values close = fill(data.history(coin, 'price', bar_count=lookback, - frequency='1m')).resample('30T').last() + frequency='30T')).values volume = fill(data.history(coin, 'volume', bar_count=lookback, - frequency='1m')).resample('30T').sum() + frequency='30T')).values # close[-1] is the equivalent to current price # displays the minute price for each pair every 30 minutes print( - today, pair, open[-1], high[-1], low[-1], close[-1], volume[-1]) + today, pair, opened[-1], high[-1], low[-1], close[-1], volume[-1]) # ---------------------------------------------------------------------------------------------------------- # -------------------------------------- Insert Your Strategy Here ----------------------------------------- @@ -82,7 +82,7 @@ def analyze(context=None, results=None): # Get the universe for a given exchange and a given base_currency market -# Example: Poloniex BTC Market +# Example: Poloniex btc Market def universe(context, lookback_date, current_date): json_symbols = get_exchange_symbols( context.exchange) # get all the pairs for the exchange @@ -103,7 +103,6 @@ def universe(context, lookback_date, current_date): universe_df = universe_df[universe_df.end_daily >= current_date] context.coins = symbols( *universe_df.symbol) # convert all the pairs to symbols - print(universe_df.head(), len(universe_df)) return universe_df.symbol.tolist() @@ -119,8 +118,8 @@ def fill(series): if __name__ == '__main__': - start_date = pd.to_datetime('2017-01-01', utc=True) - end_date = pd.to_datetime('2017-10-15', utc=True) + start_date = pd.to_datetime('2017-01-08', utc=True) + end_date = pd.to_datetime('2017-11-13', utc=True) performance = run_algorithm(start=start_date, end=end_date, capital_base=10000.0, @@ -129,7 +128,7 @@ if __name__ == '__main__': analyze=analyze, exchange_name='poloniex', data_frequency='minute', - base_currency='eth', + base_currency='btc', live=False, live_graph=False, algo_namespace='simple_universe') diff --git a/catalyst/utils/run_algo.py b/catalyst/utils/run_algo.py index c12dedc9..b4335311 100644 --- a/catalyst/utils/run_algo.py +++ b/catalyst/utils/run_algo.py @@ -1,4 +1,5 @@ import os +import re import sys import warnings from datetime import timedelta @@ -8,6 +9,8 @@ from time import sleep import click 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 @@ -167,10 +170,12 @@ def _run(handle_data, # 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'] == ''): + 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') ) + exchange=exchange_name.title(), + filename=os.path.join( + get_exchange_folder(exchange_name, environ), 'auth.json')) if exchange_name == 'bitfinex': exchanges[exchange_name] = Bitfinex( @@ -287,7 +292,7 @@ def _run(handle_data, algo_namespace=algo_namespace, live_graph=live_graph ) - else: + elif exchanges: # Removed the existing Poloniex fork to keep things simple # We can add back the complexity if required. @@ -317,6 +322,36 @@ def _run(handle_data, exchanges=exchanges ) + elif bundle is not None: + bundle_data = load( + bundle, + environ, + bundle_timestamp, + ) + + prefix, connstr = re.split( + r'sqlite:///', + str(bundle_data.asset_finder.engine.url), + maxsplit=1, + ) + if prefix: + raise ValueError( + "invalid url %r, must begin with 'sqlite:///'" % + str(bundle_data.asset_finder.engine.url), + ) + + env = TradingEnvironment(asset_db_path=connstr, environ=environ) + first_trading_day = \ + bundle_data.equity_minute_bar_reader.first_trading_day + + data = DataPortal( + env.asset_finder, open_calendar, + first_trading_day=first_trading_day, + equity_minute_reader=bundle_data.equity_minute_bar_reader, + equity_daily_reader=bundle_data.equity_daily_bar_reader, + adjustment_reader=bundle_data.adjustment_reader, + ) + perf = algorithm_class( namespace=namespace, env=env, @@ -486,7 +521,9 @@ def run_algorithm(initialize, -------- catalyst.data.bundles.bundles : The available data bundles. """ - load_extensions(default_extension, extensions, strict_extensions, environ) + load_extensions( + default_extension, extensions, strict_extensions, environ + ) # I'm not sure that we need this since the modified DataPortal # does not require extensions to be explicitly loaded. diff --git a/tests/exchange/test_bundle.py b/tests/exchange/test_bundle.py index 9492438e..3966a1e8 100644 --- a/tests/exchange/test_bundle.py +++ b/tests/exchange/test_bundle.py @@ -460,8 +460,8 @@ class TestExchangeBundle: def bundle_to_csv(self): exchange_name = 'poloniex' data_frequency = 'minute' - period = '2017-09' - symbol = 'eth_btc' + period = '2017-02' + symbol = 'lsk_eth' exchange = get_exchange(exchange_name) asset = exchange.get_asset(symbol)