Optimizing algorithm initialization

This commit is contained in:
fredfortier
2017-09-03 13:05:18 -04:00
parent c5e1945558
commit bcb5fd2b14
5 changed files with 192 additions and 23 deletions
@@ -0,0 +1,155 @@
'''
This algorithm requires an additional library (ta-lib) beyond those required by catalyst.
Install it first by running:
$ pip install TA-Lib
If you get build errors like "fatal error: ta-lib/ta_libc.h: No such file or directory"
it typically means that it can't find the underlying TA-Lib library and needs to be installed.
See https://mrjbq7.github.io/ta-lib/install.html for instructions on how to install
the required dependencies.
'''
import talib
from logbook import Logger
from catalyst.api import (
order,
order_target_percent,
symbol,
record,
get_open_orders,
)
from catalyst.exchange.stats_utils import get_pretty_stats
algo_namespace = 'buy_low_sell_high_xrp'
log = Logger(algo_namespace)
def initialize(context):
log.info('initializing algo')
context.ASSET_NAME = 'XRP_USD'
context.asset = symbol(context.ASSET_NAME)
context.TARGET_POSITIONS = 5000
context.PROFIT_TARGET = 0.1
context.SLIPPAGE_ALLOWED = 0.05
context.retry_check_open_orders = 10
context.retry_update_portfolio = 10
context.retry_order = 5
context.errors = []
pass
def _handle_data(context, data):
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))
# Buying more when RSI is low, this should lower our cost basis
if rsi <= 30:
buy_increment = 50
elif rsi <= 40:
buy_increment = 20
elif rsi <= 70:
buy_increment = 5
else:
buy_increment = None
cash = context.portfolio.cash
log.info('base currency available: {cash}'.format(cash=cash))
price = data.current(context.asset, 'price')
log.info('got price {price}'.format(price=price))
record(
price=price,
rsi=rsi,
)
orders = get_open_orders(context.asset)
if orders:
log.info('skipping bar until all open orders execute')
return
is_buy = False
cost_basis = None
if context.asset in context.portfolio.positions:
position = context.portfolio.positions[context.asset]
cost_basis = position.cost_basis
log.info(
'found {amount} positions with cost basis {cost_basis}'.format(
amount=position.amount,
cost_basis=cost_basis
)
)
if position.amount >= context.TARGET_POSITIONS:
log.info('reached positions target: {}'.format(position.amount))
return
if price < cost_basis:
is_buy = True
elif position.amount > 0 and \
price > cost_basis * (1 + context.PROFIT_TARGET):
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 * (1 - context.SLIPPAGE_ALLOWED),
)
else:
log.info('no buy or sell opportunity found')
else:
is_buy = True
if is_buy:
if buy_increment is None:
log.info('the rsi is too high to consider buying {}'.format(rsi))
return
if price * buy_increment > cash:
log.info('not enough base currency to consider buying')
return
log.info(
'buying position cheaper than cost basis {} < {}'.format(
price,
cost_basis
)
)
order(
asset=context.asset,
amount=buy_increment,
limit_price=price * (1 + context.SLIPPAGE_ALLOWED)
)
def handle_data(context, data):
log.info('handling bar {}'.format(data.current_dt))
# try:
_handle_data(context, data)
# except Exception as e:
# log.warn('aborting the bar on error {}'.format(e))
# context.errors.append(e)
log.info('completed bar {}, total execution errors {}'.format(
data.current_dt,
len(context.errors)
))
if len(context.errors) > 0:
log.info('the errors:\n{}'.format(context.errors))
def analyze(context, stats):
log.info('the daily stats:\n{}'.format(get_pretty_stats(stats)))
pass
+6 -6
View File
@@ -61,7 +61,7 @@ class ExchangeTradingAlgorithm(TradingAlgorithm):
self.is_running = True
self.retry_check_open_orders = 5
self.retry_update_portfolio = 5
self.retry_synchronize_portfolio = 5
self.retry_get_open_orders = 5
self.retry_order = 2
self.retry_delay = 5
@@ -175,9 +175,9 @@ class ExchangeTradingAlgorithm(TradingAlgorithm):
def updated_account(self):
return self.exchange.account
def _update_portfolio(self, attempt_index=0):
def _synchronize_portfolio(self, attempt_index=0):
try:
self.exchange.update_portfolio()
self.exchange.synchronize_portfolio()
# Applying the updated last_sales_price to the positions
# in the performance tracker. This seems a bit redundant
@@ -195,9 +195,9 @@ class ExchangeTradingAlgorithm(TradingAlgorithm):
log.warn(
'update portfolio attempt {}: {}'.format(attempt_index, e)
)
if attempt_index < self.retry_update_portfolio:
if attempt_index < self.retry_synchronize_portfolio:
sleep(self.retry_delay)
self._update_portfolio(attempt_index + 1)
self._synchronize_portfolio(attempt_index + 1)
else:
raise ExchangePortfolioDataError(
data_type='update-portfolio',
@@ -293,7 +293,7 @@ class ExchangeTradingAlgorithm(TradingAlgorithm):
if not self.is_running:
return
self._update_portfolio()
self._synchronize_portfolio()
transactions = self._check_open_orders()
for transaction in transactions:
+3 -2
View File
@@ -25,8 +25,8 @@ class Bittrex(Exchange):
self.base_currency = base_currency
self._portfolio = portfolio
self.minute_writer=None
self.minute_reader=None
self.minute_writer = None
self.minute_reader = None
self.assets = dict()
self.load_assets()
@@ -77,6 +77,7 @@ class Bittrex(Exchange):
def get_balances(self):
try:
log.debug('retrieving wallet balances')
balances = self.api.getbalances()
except Exception as e:
raise ExchangeRequestError(error=e)
+4 -2
View File
@@ -52,7 +52,7 @@ class Exchange:
self._portfolio = ExchangePortfolio(
start_date=pd.Timestamp.utcnow()
)
self.update_portfolio()
self.synchronize_portfolio()
return self._portfolio
@@ -397,13 +397,14 @@ class Exchange:
df = pd.concat(series)
return df
def update_portfolio(self):
def synchronize_portfolio(self):
"""
Update the portfolio cash and position balances based on the
latest ticker prices.
:return:
"""
log.debug('synchronizing portfolio with exchange {}'.format(self.name))
balances = self.get_balances()
base_position_available = balances[self.base_currency] \
@@ -417,6 +418,7 @@ class Exchange:
portfolio = self._portfolio
portfolio.cash = base_position_available
log.debug('found base currency balance: {}'.format(portfolio.cash))
if portfolio.starting_cash is None:
portfolio.starting_cash = portfolio.cash
+24 -13
View File
@@ -44,8 +44,8 @@ from catalyst.exchange.asset_finder_exchange import AssetFinderExchange
from catalyst.exchange.exchange_portfolio import ExchangePortfolio
from catalyst.exchange.exchange_errors import (
ExchangeRequestError,
ExchangeRequestErrorTooManyAttempts
)
ExchangeRequestErrorTooManyAttempts,
BaseCurrencyNotFoundError)
from catalyst.exchange.exchange_utils import get_exchange_auth, \
get_algo_object
from logbook import Logger
@@ -193,7 +193,7 @@ def _run(handle_data,
if live and exchange is not None:
env = TradingEnvironment(
environ=environ,
exchange_tz="UTC",
exchange_tz='UTC',
asset_db_path=None
)
env.asset_finder = AssetFinderExchange(exchange)
@@ -206,32 +206,43 @@ def _run(handle_data,
)
choose_loader = None
def update_portfolio(attempt_index=0):
def fetch_capital_base(attempt_index=0):
"""
Fetch the portfolio for the exchange
We can't continue on error because it is required to bootstrap
the algorithm.
Fetch the base currency amount required to bootstrap
the algorithm against the exchange.
The algorithm cannot continue without this value.
:param attempt_index:
:return:
:return capital_base: the amount of base currency available for
trading
"""
try:
exchange.update_portfolio()
return exchange.portfolio
log.debug('retrieving capital base in {} to bootstrap '
'exchange {}'.format(base_currency, exchange_name))
balances = exchange.get_balances()
except ExchangeRequestError as e:
if attempt_index < 20:
sleep(5)
return update_portfolio(attempt_index + 1)
return fetch_capital_base(attempt_index + 1)
else:
raise ExchangeRequestErrorTooManyAttempts(
attempts=attempt_index,
error=e
)
portfolio = update_portfolio()
if base_currency in balances:
return balances[base_currency]
else:
raise BaseCurrencyNotFoundError(
base_currency=base_currency,
exchange=exchange_name
)
sim_params = create_simulation_parameters(
start=start,
end=end,
capital_base=portfolio.starting_cash,
capital_base=fetch_capital_base(),
emission_rate='minute',
data_frequency='minute'
)