Polishing the algorithm

This commit is contained in:
Frederic Fortier
2017-08-20 15:38:59 -04:00
parent ef40339058
commit d3b06a083c
3 changed files with 75 additions and 61 deletions
+28 -44
View File
@@ -8,8 +8,7 @@ from catalyst.api import (
get_open_orders,
)
from catalyst.errors import ZiplineError
import matplotlib.pyplot as plt
import pyfolio as pf
import talib
algo_namespace = 'buy_the_dip_live'
log = Logger(algo_namespace)
@@ -17,12 +16,12 @@ log = Logger(algo_namespace)
def initialize(context):
log.info('initializing algo')
context.ASSET_NAME = 'EOS_USD'
context.TICK_SIZE = 1000.0
context.ASSET_NAME = 'IOT_USD'
context.asset = symbol(context.ASSET_NAME)
context.TARGET_POSITIONS = 100
context.BUY_INCREMENT = 1
context.TARGET_POSITIONS = 200
context.PROFIT_TARGET = 0.1
context.SLIPPAGE_ALLOWED = 0.02
context.retry_check_open_orders = 2
context.retry_update_portfolio = 2
@@ -32,28 +31,22 @@ def initialize(context):
def _handle_data(context, data):
# price_history = data.history(symbol('iot_usd'),
# fields='price',
# bar_count=20,
# frequency='1d'
# )
# ohlc = data.history([context.asset, symbol('iot_usd')],
# fields='price',
# bar_count=20,
# frequency='1d'
# )
ohlc = data.history(context.asset,
fields=['price', 'volume'],
bar_count=120,
frequency='1m'
)
# ohlc = data.history([context.asset, symbol('iot_usd')],
# fields=['price', 'volume'],
# bar_count=20,
# frequency='1d'
# )
prices = data.history(
context.asset,
fields='price',
bar_count=20,
frequency='15m'
)
rsi = talib.RSI(prices.values, timeperiod=14)[-1]
log.info('got rsi: {}'.format(rsi))
hist_price = ohlc['price']
# Buying more when RSI is low, this should lower our cost basis
if rsi <= 40:
buy_increment = 2
elif rsi <= 30:
buy_increment = 5
else:
buy_increment = 1
cash = context.portfolio.cash
log.info('base currency available: {cash}'.format(cash=cash))
@@ -66,7 +59,7 @@ def _handle_data(context, data):
log.info('skipping bar until all open orders execute')
return
if price * context.BUY_INCREMENT > cash:
if price * buy_increment > cash:
log.info('not enough base currency to consider buying')
return
@@ -83,12 +76,13 @@ def _handle_data(context, data):
)
if price < cost_basis:
is_buy = True
elif price > cost_basis * 1.1:
log.info('price higher than cost basis, taking profit')
elif price > cost_basis * (1 + context.PROFIT_TARGET) or rsi > 70:
profit = (price * position.amount) - (cost_basis * position.amount)
log.info('closing position, taking profit: {}'.format(profit))
order_target_percent(
asset=context.asset,
target=0,
limit_price=price * 0.95,
limit_price=price * (1 - context.SLIPPAGE_ALLOWED),
)
else:
log.info('no buy or sell opportunity found')
@@ -104,8 +98,8 @@ def _handle_data(context, data):
)
order(
asset=context.asset,
amount=context.BUY_INCREMENT,
limit_price=price * 1.1
amount=buy_increment,
limit_price=price * (1 + context.SLIPPAGE_ALLOWED)
)
record(
@@ -134,17 +128,7 @@ def handle_data(context, data):
def analyze(context, stats):
# pnl, = plt.plot(stats.index, stats['pnl'], '-',
# color='blue',
# linewidth=1.0,
# label='P&L',
# )
#
# plt.legend(handles=[pnl])
# plt.show()
returns, positions, transactions, gross_lev = \
pf.utils.extract_rets_pos_txn_from_zipline(stats)
log.info('the full stats:\n{}'.format(stats))
pass
+4
View File
@@ -226,8 +226,12 @@ class ExchangeTradingAlgorithm(TradingAlgorithm):
shorts_count=pos_stats.shorts_count,
)
# Merging cumulative risk
stats.update(tracker.cumulative_risk_metrics.to_dict())
# Merging latest recorded variables
stats.update(self.recorded_vars)
stats['positions'] = period.position_tracker.get_positions_list()
# we want the key to be absent, not just empty
+43 -17
View File
@@ -1,3 +1,4 @@
import re
import pytz
import six
import base64
@@ -279,17 +280,43 @@ class Bitfinex(Exchange):
def get_candles(self, data_frequency, assets,
end_dt=None, bar_count=None, limit=None):
"""
Retrieve OHLVC candles from Bitfinex
# TODO: support all available frequencies
start_dt = None
if data_frequency == 'minute' or data_frequency == '1m':
:param data_frequency:
:param assets:
:param end_dt:
:param bar_count:
:param limit:
:return:
Available Frequencies
---------------------
'1m', '5m', '15m', '30m', '1h', '3h', '6h', '12h', '1D', '7D', '14D',
'1M'
"""
freq_match = re.match(r'([0-9].*)(m|h|d)', data_frequency, re.M | re.I)
if freq_match:
number = int(freq_match.group(1))
unit = freq_match.group(2)
if unit == 'd':
converted_unit = 'D'
else:
converted_unit = unit
frequency = '{}{}'.format(number, converted_unit)
allowed_frequencies = ['1m', '5m', '15m', '30m', '1h', '3h', '6h',
'12h', '1D', '7D', '14D', '1M']
if frequency not in allowed_frequencies:
raise InvalidHistoryFrequencyError(
frequency=data_frequency
)
elif data_frequency == 'minute':
frequency = '1m'
if bar_count and end_dt:
start_dt = end_dt - timedelta(minutes=bar_count)
elif data_frequency == 'daily' or data_frequency == '1d':
elif data_frequency == 'daily':
frequency = '1D'
if bar_count and end_dt:
start_dt = end_dt - timedelta(days=bar_count)
else:
raise InvalidHistoryFrequencyError(
frequency=data_frequency
@@ -306,28 +333,26 @@ class Bitfinex(Exchange):
symbol=symbol
)
if start_dt and end_dt:
if bar_count:
is_list = True
url += '/hist?start={start}&end={end}'.format(
start=time.mktime(start_dt.timetuple()) * 1000,
end=time.mktime(end_dt.timetuple()) * 1000,
)
url += '/hist?limit={}'.format(int(bar_count))
else:
is_list = False
url += '/last'
try:
response = requests.get(url)
candles = response.json()
except Exception as e:
raise ExchangeRequestError(error=e)
if 'message' in candles:
if 'error' in response.content:
raise ExchangeRequestError(
error='Unable to retrieve candles: {}'.format(
candles['message'])
response.content)
)
candles = response.json()
def ohlc_from_candle(candle):
return dict(
open=candle[1],
@@ -342,7 +367,8 @@ class Bitfinex(Exchange):
if is_list:
ohlc_bars = []
for candle in candles:
# We can to list candles from old to new
for candle in reversed(candles):
ohlc = ohlc_from_candle(candle)
ohlc_bars.append(ohlc)