From 619fbfc6eadd1c77a0c80f37973abe74ee658db7 Mon Sep 17 00:00:00 2001 From: Victor Grau Serrat Date: Thu, 7 Dec 2017 22:32:17 -0700 Subject: [PATCH] DOC: PEP8 simple_universe.py & added to example_algos.html --- catalyst/examples/simple_universe.py | 141 ++++++++++++--------- docs/source/example-algos.rst | 176 +++++++++++++++++++++++++++ 2 files changed, 261 insertions(+), 56 deletions(-) diff --git a/catalyst/examples/simple_universe.py b/catalyst/examples/simple_universe.py index ff79c763..a0abc535 100644 --- a/catalyst/examples/simple_universe.py +++ b/catalyst/examples/simple_universe.py @@ -2,73 +2,105 @@ Requires Catalyst version 0.3.0 or above 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. -You will all see how to create a universe and filter it base on the exchange and the market you desire. +This example aims to provide an easy way for users to learn how to +collect data from any given exchange and select a subset of the available +currency pairs for trading. You simply need to specify the exchange and +the market (base_currency) that you want to focus on. You will then see +how to create a universe of assets, and filter it based the market you +desire. -The example prints out the closing price of all the pairs for a given market-exchange every 30 minutes. -The example also contains the ohlcv minute data for the past seven days which could be used to create indicators -Use this as the backbone to create your own trading strategies. +The example prints out the closing price of all the pairs for a given +market in a given exchange every 30 minutes. The example also contains +the OHLCV data with minute-resolution for the past seven days which +could be used to create indicators. Use this code as the backbone to +create your own trading strategy. + +The lookback_date variable is used to ensure data for a coin existed on +the lookback period specified. + +To run, execute the following two commands in a terminal (inside catalyst +environment). The first one retrieves all the pricing data needed for this +script to run (only needs to be run once), and the second one executes this +script with the parameters specified in the run_algorithm() call at the end +of the file: + +catalyst ingest-exchange -x bitfinex -f minute + +python simple_universe.py -Variables lookback date and date are used to ensure data for a coin existed on the lookback period specified. """ +from datetime import timedelta import numpy as np import pandas as pd -from datetime import timedelta + from catalyst import run_algorithm from catalyst.exchange.exchange_utils import get_exchange_symbols - -from catalyst.api import ( - symbols, -) +from catalyst.api import (symbols, ) def initialize(context): - context.i = -1 # counts the minutes - context.exchange = context.exchanges.values()[0].name.lower() # exchange name - context.base_currency = context.exchanges.values()[0].base_currency.lower() # market base currency + context.i = -1 # minute counter + context.exchange = context.exchanges.values()[0].name.lower() + context.base_currency = context.exchanges.values()[0].base_currency.lower() def handle_data(context, data): context.i += 1 lookback_days = 7 # 7 days - # current date formatted into a string - today = data.current_dt - date, time = today.strftime('%Y-%m-%d %H:%M:%S').split(' ') - lookback_date = today - timedelta(days=lookback_days) # subtract the amount of days specified in lookback - lookback_date = lookback_date.strftime('%Y-%m-%d %H:%M:%S').split(' ')[0] # get only the date as a string + # current date & time in each iteration formatted into a string + now = data.current_dt + date, time = now.strftime('%Y-%m-%d %H:%M:%S').split(' ') + lookback_date = now - timedelta(days=lookback_days) + # keep only the date as a string, discard the time + lookback_date = lookback_date.strftime('%Y-%m-%d %H:%M:%S').split(' ')[0] - # update universe everyday - new_day = 60 * 24 # assuming data_frequency='minute' - if not context.i % new_day: + one_day_in_minutes = 1440 # 60 * 24 assumes data_frequency='minute' + # update universe everyday at midnight + if not context.i % one_day_in_minutes: context.universe = universe(context, lookback_date, date) # get data every 30 minutes minutes = 30 - one_day_in_minutes = 1440 # 1440 assumes data_frequency='minute' - lookback = one_day_in_minutes / minutes * lookback_days # get N lookback_days of history data + # get lookback_days of history data: that is 'lookback' number of bins + lookback = one_day_in_minutes / minutes * lookback_days if not context.i % minutes and context.universe: # we iterate for every pair in the current universe for coin in context.coins: pair = str(coin.symbol) - # 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. - opened = fill(data.history(coin, 'open', bar_count=lookback, frequency='30T')).values - high = fill(data.history(coin, 'high', bar_count=lookback, frequency='30T')).values - low = fill(data.history(coin, 'low', bar_count=lookback, frequency='30T')).values - close = fill(data.history(coin, 'price', bar_count=lookback, frequency='30T')).values - volume = fill(data.history(coin, 'volume', bar_count=lookback, frequency='30T')).values + # Get 30 minute interval OHLCV data. This is the standard data + # required for candlestick or indicators/signals. Return Pandas + # DataFrames. 30T means 30-minute re-sampling of one minute data. + # Adjust it to your desired time interval as needed. + opened = fill(data.history(coin, 'open', + bar_count=lookback, frequency='30T')).values + high = fill(data.history(coin, 'high', + bar_count=lookback, frequency='30T')).values + low = fill(data.history(coin, 'low', + bar_count=lookback, frequency='30T')).values + close = fill(data.history(coin, 'price', + bar_count=lookback, frequency='30T')).values + volume = fill(data.history(coin, 'volume', + bar_count=lookback, frequency='30T')).values - # close[-1] is the equivalent to current price + # close[-1] is the last value in the set, which is the equivalent + # to current price (as in the most recent value) # displays the minute price for each pair every 30 minutes - print(today, pair, opened[-1], high[-1], low[-1], close[-1], volume[-1]) + print('{now}: {pair} -\tO:{o},\tH:{h},\tL:{c},\tC{c},\tV:{v}'.format( + now=now, + pair=pair, + o=opened[-1], + h=high[-1], + l=low[-1], + c=close[-1], + v=volume[-1], + )) - # ---------------------------------------------------------------------------------------------------------- - # -------------------------------------- Insert Your Strategy Here ----------------------------------------- - # ---------------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------- + # --------------- Insert Your Strategy Here ------------------- + # ------------------------------------------------------------- def analyze(context=None, results=None): @@ -78,23 +110,22 @@ def analyze(context=None, results=None): # Get the universe for a given exchange and a given base_currency 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 - universe_df = pd.DataFrame.from_dict(json_symbols).transpose().astype(str) # convert into a dataframe - universe_df['base_currency'] = universe_df.apply(lambda row: row.symbol.split('_')[1], - axis=1) - universe_df['market_currency'] = universe_df.apply(lambda row: row.symbol.split('_')[0], - axis=1) + # get all the pairs for the given exchange + json_symbols = get_exchange_symbols(context.exchange) + # convert into a DataFrame for easier processing + df = pd.DataFrame.from_dict(json_symbols).transpose().astype(str) + df['base_currency'] = df.apply(lambda row: row.symbol.split('_')[1],axis=1) + df['market_currency'] = df.apply(lambda row: row.symbol.split('_')[0],axis=1) - # Filter all the exchange pairs to only the ones for a give base currency - universe_df = universe_df[universe_df['base_currency'] == context.base_currency] + # Filter all the pairs to get only the ones for a given base_currency + df = df[df['base_currency'] == context.base_currency] # Filter all the pairs to ensure that pair existed in the current date range - universe_df = universe_df[universe_df.start_date < lookback_date] - universe_df = universe_df[universe_df.end_daily >= current_date] - context.coins = symbols(*universe_df.symbol) # convert all the pairs to symbols + df = df[df.start_date < lookback_date] + df = df[df.end_daily >= current_date] + context.coins = symbols(*df.symbol) # convert all the pairs to symbols - # print(universe_df.symbol.tolist()) - return universe_df.symbol.tolist() + return df.symbol.tolist() # Replace all NA, NAN or infinite values with its nearest value @@ -102,7 +133,9 @@ def fill(series): if isinstance(series, pd.Series): return series.replace([np.inf, -np.inf], np.nan).ffill().bfill() elif isinstance(series, np.ndarray): - return pd.Series(series).replace([np.inf, -np.inf], np.nan).ffill().bfill().values + return pd.Series(series).replace( + [np.inf, -np.inf], np.nan + ).ffill().bfill().values else: return series @@ -112,7 +145,7 @@ if __name__ == '__main__': end_date = pd.to_datetime('2017-11-13', utc=True) performance = run_algorithm(start=start_date, end=end_date, - capital_base=100.0, # amount of base_currency, not always in dollars unless usd + capital_base=100.0, # amount of base_currency initialize=initialize, handle_data=handle_data, analyze=analyze, @@ -123,7 +156,3 @@ if __name__ == '__main__': live_graph=False, algo_namespace='simple_universe') -""" -Run in Terminal (inside catalyst environment): -python simple_universe.py -""" diff --git a/docs/source/example-algos.rst b/docs/source/example-algos.rst index 550d8b58..ff5d5ea7 100644 --- a/docs/source/example-algos.rst +++ b/docs/source/example-algos.rst @@ -31,6 +31,11 @@ Overview `two-part video tutorial `_ to show how to get started in backtesting and live trading with Catalyst. +- :ref:`Simple Universe `: This code provides the 'universe' + of available trading pairs on a given exchange on any given day. You can use + this code to dynamically select which currency pairs you want to trade each + day of your strategy. This example does not make any trades. + - :ref:`Portfolio Optimization `: Use this code to execute a portfolio optimization model. This strategy will select the portfolio with the maximum Sharpe Ratio. The parameters are set to use 180 @@ -753,6 +758,177 @@ implemented after the video was recorded, which executes the orders at slighlty different prices, but resulting in significant changes in performance of our strategy. +.. _simple_universe: + +Simple Universe +~~~~~~~~~~~~~~~ + +Source code: `examples/simple_universe.py `_ + +This example aims to provide an easy way for users to learn how to +collect data from any given exchange and select a subset of the available +currency pairs for trading. You simply need to specify the exchange and +the market (base_currency) that you want to focus on. You will then see +how to create a universe of assets, and filter it based the market you +desire. + +The example prints out the closing price of all the pairs for a given +market in a given exchange every 30 minutes. The example also contains +the OHLCV data with minute-resolution for the past seven days which +could be used to create indicators. Use this code as the backbone to +create your own trading strategy. + +The lookback_date variable is used to ensure data for a coin existed on +the lookback period specified. + +To run, execute the following two commands in a terminal (inside catalyst +environment). The first one retrieves all the pricing data needed for this +script to run (only needs to be run once), and the second one executes this +script with the parameters specified in the run_algorithm() call at the end +of the file: + +.. code-block:: bash + + catalyst ingest-exchange -x bitfinex -f minute + +.. code-block:: bash + + python simple_universe.py + +Credits: This code was originally submitted by `Abner Ayala-Acevedo +`_. Thank you! + +.. code-block:: python + + from datetime import timedelta + + import numpy as np + import pandas as pd + + from catalyst import run_algorithm + from catalyst.exchange.exchange_utils import get_exchange_symbols + from catalyst.api import (symbols, ) + + + def initialize(context): + context.i = -1 # minute counter + context.exchange = context.exchanges.values()[0].name.lower() + context.base_currency = context.exchanges.values()[0].base_currency.lower() + + + def handle_data(context, data): + context.i += 1 + lookback_days = 7 # 7 days + + # current date & time in each iteration formatted into a string + now = data.current_dt + date, time = now.strftime('%Y-%m-%d %H:%M:%S').split(' ') + lookback_date = now - timedelta(days=lookback_days) + # keep only the date as a string, discard the time + lookback_date = lookback_date.strftime('%Y-%m-%d %H:%M:%S').split(' ')[0] + + one_day_in_minutes = 1440 # 60 * 24 assumes data_frequency='minute' + # update universe everyday at midnight + if not context.i % one_day_in_minutes: + context.universe = universe(context, lookback_date, date) + + # get data every 30 minutes + minutes = 30 + # get lookback_days of history data: that is 'lookback' number of bins + lookback = one_day_in_minutes / minutes * lookback_days + if not context.i % minutes and context.universe: + # we iterate for every pair in the current universe + for coin in context.coins: + pair = str(coin.symbol) + + # Get 30 minute interval OHLCV data. This is the standard data + # required for candlestick or indicators/signals. Return Pandas + # DataFrames. 30T means 30-minute re-sampling of one minute data. + # Adjust it to your desired time interval as needed. + opened = fill(data.history(coin, 'open', + bar_count=lookback, frequency='30T')).values + high = fill(data.history(coin, 'high', + bar_count=lookback, frequency='30T')).values + low = fill(data.history(coin, 'low', + bar_count=lookback, frequency='30T')).values + close = fill(data.history(coin, 'price', + bar_count=lookback, frequency='30T')).values + volume = fill(data.history(coin, 'volume', + bar_count=lookback, frequency='30T')).values + + # close[-1] is the last value in the set, which is the equivalent + # to current price (as in the most recent value) + # displays the minute price for each pair every 30 minutes + print('{now}: {pair} -\tO:{o},\tH:{h},\tL:{c},\tC{c},\tV:{v}'.format( + now=now, + pair=pair, + o=opened[-1], + h=high[-1], + l=low[-1], + c=close[-1], + v=volume[-1], + )) + + # ------------------------------------------------------------- + # --------------- Insert Your Strategy Here ------------------- + # ------------------------------------------------------------- + + + def analyze(context=None, results=None): + pass + + + # Get the universe for a given exchange and a given base_currency market + # Example: Poloniex BTC Market + def universe(context, lookback_date, current_date): + # get all the pairs for the given exchange + json_symbols = get_exchange_symbols(context.exchange) + # convert into a DataFrame for easier processing + df = pd.DataFrame.from_dict(json_symbols).transpose().astype(str) + df['base_currency'] = df.apply(lambda row: row.symbol.split('_')[1],axis=1) + df['market_currency'] = df.apply(lambda row: row.symbol.split('_')[0],axis=1) + + # Filter all the pairs to get only the ones for a given base_currency + df = df[df['base_currency'] == context.base_currency] + + # Filter all the pairs to ensure that pair existed in the current date range + df = df[df.start_date < lookback_date] + df = df[df.end_daily >= current_date] + context.coins = symbols(*df.symbol) # convert all the pairs to symbols + + return df.symbol.tolist() + + + # Replace all NA, NAN or infinite values with its nearest value + def fill(series): + if isinstance(series, pd.Series): + return series.replace([np.inf, -np.inf], np.nan).ffill().bfill() + elif isinstance(series, np.ndarray): + return pd.Series(series).replace( + [np.inf, -np.inf], np.nan + ).ffill().bfill().values + else: + return series + + + if __name__ == '__main__': + start_date = pd.to_datetime('2017-11-10', utc=True) + end_date = pd.to_datetime('2017-11-13', utc=True) + + performance = run_algorithm(start=start_date, end=end_date, + capital_base=100.0, # amount of base_currency + initialize=initialize, + handle_data=handle_data, + analyze=analyze, + exchange_name='bitfinex', + data_frequency='minute', + base_currency='btc', + live=False, + live_graph=False, + algo_namespace='simple_universe') + + + .. _portfolio_optimization: Portfolio Optimization