mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-01 13:09:32 +08:00
Optimizing algorithm initialization
This commit is contained in:
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user