From c2821ab77b7d16bff82c73d0ba0eac4ac9cf4d82 Mon Sep 17 00:00:00 2001 From: Victor Grau Serrat Date: Mon, 27 Nov 2017 15:43:57 -0700 Subject: [PATCH 1/6] DOC: added example_algo: Dual Moving Average Crossover --- catalyst/examples/dual_moving_average.py | 153 +++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 catalyst/examples/dual_moving_average.py diff --git a/catalyst/examples/dual_moving_average.py b/catalyst/examples/dual_moving_average.py new file mode 100644 index 00000000..b724ec24 --- /dev/null +++ b/catalyst/examples/dual_moving_average.py @@ -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 subgraph: 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 subgraph: 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 subgraph: 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)) + + # 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), + ) \ No newline at end of file From d07e0edd88f035593eb13d5ecc847c004f97fe16 Mon Sep 17 00:00:00 2001 From: fredfortier Date: Mon, 27 Nov 2017 17:54:34 -0500 Subject: [PATCH 2/6] DOC: 0.3.9 release notes --- docs/source/releases.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/source/releases.rst b/docs/source/releases.rst index b178ec58..67f32498 100644 --- a/docs/source/releases.rst +++ b/docs/source/releases.rst @@ -2,6 +2,20 @@ Release Notes ============= +Version 0.3.9 +^^^^^^^^^^^^^ +**Release Date**: 2017-11-14 + +Bug Fixes +~~~~~~~~~ + +- Improved cash display in running stats (:issue:`80`) +- Added capital_base parameter to live mode to limit cash (:issue:`79`) +- Fixed sortino warning issues (:issue:`77`) +- Adjusted computation of last candle of data.history (:issue:`71`) +- Added support for csv ingestion (:issue:`65`) + + Version 0.3.8 ^^^^^^^^^^^^^ **Release Date**: 2017-11-14 From a61b22b821a86995b30358437f2bcbb26abf394f Mon Sep 17 00:00:00 2001 From: fredfortier Date: Mon, 27 Nov 2017 17:55:22 -0500 Subject: [PATCH 3/6] DOC: 0.3.9 release notes --- docs/source/releases.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/source/releases.rst b/docs/source/releases.rst index 67f32498..2695c309 100644 --- a/docs/source/releases.rst +++ b/docs/source/releases.rst @@ -9,11 +9,14 @@ Version 0.3.9 Bug Fixes ~~~~~~~~~ -- Improved cash display in running stats (:issue:`80`) -- Added capital_base parameter to live mode to limit cash (:issue:`79`) - 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 From a52e201f866f01eeb7ab50d40531168054acce87 Mon Sep 17 00:00:00 2001 From: Victor Grau Serrat Date: Mon, 27 Nov 2017 21:10:17 -0700 Subject: [PATCH 4/6] DOC: improved dual_moving_average.py example algo --- catalyst/examples/dual_moving_average.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/catalyst/examples/dual_moving_average.py b/catalyst/examples/dual_moving_average.py index b724ec24..a73461f1 100644 --- a/catalyst/examples/dual_moving_average.py +++ b/catalyst/examples/dual_moving_average.py @@ -1,10 +1,10 @@ import numpy as np import pandas as pd from logbook import Logger -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt from catalyst import run_algorithm -from catalyst.api import (order, record, symbol, order_target_percent, +from catalyst.api import (order, record, symbol, order_target_percent, get_open_orders) from catalyst.exchange.stats_utils import extract_transactions @@ -18,7 +18,7 @@ def initialize(context): def handle_data(context, data): - # define the windows for the moving averages + # define the windows for the moving averages short_window = 50 long_window = 200 @@ -27,11 +27,11 @@ def handle_data(context, data): if context.i < long_window: return - # Compute moving averages calling data.history() for each + # 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', + 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() @@ -79,7 +79,7 @@ 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 subgraph: Plot portfolio value using base_currency + # First chart: Plot portfolio value using base_currency ax1 = plt.subplot(411) perf.loc[:, ['portfolio_value']].plot(ax=ax1) ax1.legend_.remove() @@ -87,7 +87,7 @@ def analyze(context, perf): start, end = ax1.get_ylim() ax1.yaxis.set_ticks(np.arange(start, end, (end-start)/5)) - # Second subgraph: Plot asset price, moving averages and buys/sells + # 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() @@ -119,7 +119,7 @@ def analyze(context, perf): label='' ) - # Third subgraph: Compare percentage change between our portfolio + # 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) @@ -128,7 +128,7 @@ def analyze(context, perf): start, end = ax3.get_ylim() ax3.yaxis.set_ticks(np.arange(start, end, (end-start)/5)) - # Plot our cash + # Fourth chart: Plot our cash ax4 = plt.subplot(414, sharex=ax1) perf.cash.plot(ax=ax4) ax4.set_ylabel('Cash\n({})'.format(base_currency)) From dfcfe5a370eed23c095b938306285496eeafb232 Mon Sep 17 00:00:00 2001 From: fredfortier Date: Tue, 28 Nov 2017 01:44:46 -0500 Subject: [PATCH 5/6] merging from develop --- catalyst/examples/mean_reversion_simple.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst/examples/mean_reversion_simple.py b/catalyst/examples/mean_reversion_simple.py index 6d4922e1..75e9f2ea 100644 --- a/catalyst/examples/mean_reversion_simple.py +++ b/catalyst/examples/mean_reversion_simple.py @@ -248,7 +248,7 @@ if __name__ == '__main__': # 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', + data_frequency='daily', initialize=initialize, handle_data=handle_data, analyze=analyze, From ffd1bc07cc973a9c3655bed0ed6b05ec21f44f2c Mon Sep 17 00:00:00 2001 From: Victor Grau Serrat Date: Tue, 28 Nov 2017 11:22:41 -0700 Subject: [PATCH 6/6] DOC: making example algos consistent with doc website --- catalyst/examples/buy_and_hodl.py | 25 ++++++++++--------------- catalyst/examples/buy_btc_simple.py | 5 +++-- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/catalyst/examples/buy_and_hodl.py b/catalyst/examples/buy_and_hodl.py index 536de1cd..74b04238 100644 --- a/catalyst/examples/buy_and_hodl.py +++ b/catalyst/examples/buy_and_hodl.py @@ -15,15 +15,11 @@ # 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 import run_algorithm -from catalyst.api import ( - order_target_value, - symbol, - record, - cancel_order, - get_open_orders, -) +from catalyst.api import (order_target_value, symbol, record, + cancel_order, get_open_orders, ) def initialize(context): @@ -78,15 +74,14 @@ 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]] @@ -126,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) @@ -142,13 +137,13 @@ def analyze(context=None, results=None): if __name__ == '__main__': run_algorithm( capital_base=10000, - data_frequency='minute', + 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('2017-11-01', utc=True), - end=pd.to_datetime('2017-11-10', utc=True), + start=pd.to_datetime('2015-03-01', utc=True), + end=pd.to_datetime('2017-10-31', utc=True), ) diff --git a/catalyst/examples/buy_btc_simple.py b/catalyst/examples/buy_btc_simple.py index ef64513b..5e4ebd57 100644 --- a/catalyst/examples/buy_btc_simple.py +++ b/catalyst/examples/buy_btc_simple.py @@ -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: