"""
Helper for building an algorithm trading and performance history
as a dictionary that can be reviewed during or after an
algorithm finishes running
"""
import analysis_engine.consts as ae_consts
import spylunking.log.setup_logging as log_utils
log = log_utils.build_colorized_logger(name=__name__)
[docs]def build_trade_history_entry(
ticker,
num_owned,
close,
balance,
commission,
date,
trade_type,
algo_start_price,
original_balance,
minute=None,
high=None,
low=None,
open_val=None,
volume=None,
ask=None,
bid=None,
today_high=None,
today_low=None,
today_open_val=None,
today_close=None,
today_volume=None,
stop_loss=None,
trailing_stop_loss=None,
buy_hold_units=None,
sell_hold_units=None,
spread_exp_date=None,
spread_id=None,
low_strike=None,
low_bid=None,
low_ask=None,
low_volume=None,
low_open_int=None,
low_delta=None,
low_gamma=None,
low_theta=None,
low_vega=None,
low_rho=None,
low_impl_vol=None,
low_intrinsic=None,
low_extrinsic=None,
low_theo_price=None,
low_theo_volatility=None,
low_max_covered=None,
low_exp_date=None,
high_strike=None,
high_bid=None,
high_ask=None,
high_volume=None,
high_open_int=None,
high_delta=None,
high_gamma=None,
high_theta=None,
high_vega=None,
high_rho=None,
high_impl_vol=None,
high_intrinsic=None,
high_extrinsic=None,
high_theo_price=None,
high_theo_volatility=None,
high_max_covered=None,
high_exp_date=None,
prev_balance=None,
prev_num_owned=None,
total_buys=None,
total_sells=None,
buy_triggered=None,
buy_strength=None,
buy_risk=None,
sell_triggered=None,
sell_strength=None,
sell_risk=None,
num_indicators_buy=None,
num_indicators_sell=None,
min_buy_indicators=None,
min_sell_indicators=None,
net_gain=None,
net_value=None,
ds_id=None,
note=None,
err=None,
entry_spread_dict=None,
version=1,
verbose=False):
"""build_trade_history_entry
Build a dictionary for tracking an algorithm profitability per ticker
and for ``TRADE_SHARES``, ``TRADE_VERTICAL_BULL_SPREAD``, or
``TRADE_VERTICAL_BEAR_SPREAD`` trading types.
.. note:: setting the ``minute`` is required to build
a minute-by-minute ``Trading History``
:param ticker: string ticker or symbol
:param num_owned: integer current owned
number of ``shares`` for this asset or number of
currently owned ``contracts`` for an options
spread.
:param close: float ``close`` price of the
underlying asset
:param balance: float amount of available capital
:param commission: float for commission costs
:param date: string trade date for that row usually
``COMMON_DATE_FORMAT`` (``YYYY-MM-DD``)
:param minute: optional - string for recording the minute
the trade was place, and the format is
``COMMON_TICK_DATE_FORMAT`` (``YYYY-MM-DD HH:MM:SS``)
this is optional if the algorithm is set up to
trade using a ``day`` value for timeseries.
:param trade_type: type of the trade - supported values:
``TRADE_SHARES``,
``TRADE_VERTICAL_BULL_SPREAD``,
``TRADE_VERTICAL_BEAR_SPREAD``
:param algo_start_price: float starting close/contract price
for this algo
:param original_balance: float starting original account
balance for this algo
:param high: optional - float underlying stock asset ``high`` price
:param low: optional - float underlying stock asset ``low`` price
:param open_val: optional - float underlying stock asset ``open`` price
:param volume: optional - integer underlying stock asset ``volume``
:param ask: optional - float ``ask`` price of the
stock (for buying ``shares``)
:param bid: optional - float ``bid`` price of the
stock (for selling ``shares``)
:param today_high: optional - float ``high`` from
the daily dataset (if available)
:param today_low: optional - float ``low`` from
the daily dataset (if available)
:param today_open_val: optional - float ``open`` from
the daily dataset (if available)
:param today_close: optional - float ``close`` from
the daily dataset (if available)
:param today_volume: optional - float ``volume`` from
the daily dataset (if available)
:param stop_loss: optional - float ``stop_loss`` price of the
stock/spread (for selling ``shares`` vs ``contracts``)
:param trailing_stop_loss: optional - float ``trailing_stop_loss``
price of the stock/spread (for selling ``shares`` vs ``contracts``)
:param buy_hold_units: optional - number of units
to hold buys - helps with algorithm tuning
:param sell_hold_units: optional - number of units
to hold sells - helps with algorithm tuning
:param spread_exp_date: optional - string spread contract
expiration date (``COMMON_DATE_FORMAT`` (``YYYY-MM-DD``)
:param spread_id: optional - spread identifier for reviewing
spread performances
:param low_strike: optional
- only for vertical bull/bear trade types
``low leg strike price`` of the spread
:param low_bid: optional
- only for vertical bull/bear trade types
``low leg bid`` of the spread
:param low_ask: optional
- only for vertical bull/bear trade types
``low leg ask`` of the spread
:param low_volume: optional
- only for vertical bull/bear trade types
``low leg volume`` of the spread
:param low_open_int: optional
- only for vertical bull/bear trade types
``low leg open interest`` of the spread
:param low_delta: optional
- only for vertical bull/bear trade types
``low leg delta`` of the spread
:param low_gamma: optional
- only for vertical bull/bear trade types
``low leg gamma`` of the spread
:param low_theta: optional
- only for vertical bull/bear trade types
``low leg theta`` of the spread
:param low_vega: optional
- only for vertical bull/bear trade types
``low leg vega`` of the spread
:param low_rho: optional
- only for vertical bull/bear trade types
``low leg rho`` of the spread
:param low_impl_vol: optional
- only for vertical bull/bear trade types
``low leg implied volatility`` of the spread
:param low_intrinsic: optional
- only for vertical bull/bear trade types
``low leg intrinsic`` of the spread
:param low_extrinsic: optional
- only for vertical bull/bear trade types
``low leg extrinsic`` of the spread
:param low_theo_price: optional
- only for vertical bull/bear trade types
``low leg theoretical price`` of the spread
:param low_theo_volatility: optional
- only for vertical bull/bear trade types
``low leg theoretical volatility`` of the spread
:param low_max_covered: optional
- only for vertical bull/bear trade types
``low leg max covered returns`` of the spread
:param low_exp_date: optional
- only for vertical bull/bear trade types
``low leg expiration date`` of the spread
:param high_strike: optional
- only for vertical bull/bear trade types
``high leg strike price`` of the spread
:param high_bid: optional
- only for vertical bull/bear trade types
``high leg bid`` of the spread
:param high_ask: optional
- only for vertical bull/bear trade types
``high leg ask`` of the spread
:param high_volume: optional
- only for vertical bull/bear trade types
``high leg volume`` of the spread
:param high_open_int: optional
- only for vertical bull/bear trade types
``high leg open interest`` of the spread
:param high_delta: optional
- only for vertical bull/bear trade types
``high leg delta`` of the spread
:param high_gamma: optional
- only for vertical bull/bear trade types
``high leg gamma`` of the spread
:param high_theta: optional
- only for vertical bull/bear trade types
``high leg theta`` of the spread
:param high_vega: optional
- only for vertical bull/bear trade types
``high leg vega`` of the spread
:param high_rho: optional
- only for vertical bull/bear trade types
``high leg rho`` of the spread
:param high_impl_vol: optional
- only for vertical bull/bear trade types
``high leg implied volatility`` of the spread
:param high_intrinsic: optional
- only for vertical bull/bear trade types
``high leg intrinsic`` of the spread
:param high_extrinsic: optional
- only for vertical bull/bear trade types
``high leg extrinsic`` of the spread
:param high_theo_price: optional
- only for vertical bull/bear trade types
``high leg theoretical price`` of the spread
:param high_theo_volatility: optional
- only for vertical bull/bear trade types
``high leg theoretical volatility`` of the spread
:param high_max_covered: optional
- only for vertical bull/bear trade types
``high leg max covered returns`` of the spread
:param high_exp_date: optional
- only for vertical bull/bear trade types
``high leg expiration date`` of the spread
:param prev_balance: optional - previous balance
for this algo
:param prev_num_owned: optional - previous num of
``shares`` or ``contracts``
:param total_buys: optional - total buy orders
for this algo
:param total_sells: optional - total sell orders
for this algo
:param buy_triggered: optional - bool
``buy`` conditions in the algorithm triggered
:param buy_strength: optional - float
custom strength/confidence rating for tuning
algorithm performance for desirable
sensitivity and specificity
:param buy_risk: optional - float
custom risk rating for tuning algorithm
peformance for avoiding custom risk for buy
conditions
:param sell_triggered: optional - bool
``sell`` conditions in the algorithm triggered
:param sell_strength: optional - float
custom strength/confidence rating for tuning
algorithm performance for desirable
sensitivity and specificity
:param sell_risk: optional - float
custom risk rating for tuning algorithm
peformance for avoiding custom risk for buy
conditions
:param num_indicators_buy: optional - integer
number of indicators the ``IndicatorProcessor``
processed and said to ``buy`` an asset
:param num_indicators_sell: optional - integer
number of indicators the ``IndicatorProcessor``
processed and said to ``sell`` an asset
:param min_buy_indicators: optional - integer
minimum number of indicators required to trigger
a ``buy`` order
:param min_sell_indicators: optional - integer
minimum number of indicators required to trigger
a ``sell`` order
net_gain=None,
net_value=None,
:param net_value: optional - float total value the algorithm
has left remaining since starting trading. this includes
the number of ``self.num_owned`` shares with the
``self.latest_close`` price included
:param net_gain: optional - float amount the algorithm has
made since starting including owned shares
with the ``self.latest_close`` price included
:param ds_id: optional - datset id for debugging
:param note: optional - string for tracking high level
testing notes on algorithm indicator ratings and
internal message passing during an algorithms's
``self.process`` method
:param err: optional - string for tracking errors
:param entry_spread_dict: optional - on exit spreads
the calculation of net gain can use the entry
spread to determine specific performance metrics
(work in progress)
:param version: optional - version tracking order history
:param verbose: optional - bool log each history node
(default is ``False``)
"""
status = ae_consts.NOT_RUN
algo_status = ae_consts.NOT_RUN
err = None
balance_net_gain = 0.0
breakeven_price = None
max_profit = None # only for option spreads
max_loss = None # only for option spreads
exp_date = None # only for option spreads
# latest price - start price of the algo
price_change_since_start = close - algo_start_price
if close:
if close < 0.01:
status = ae_consts.INVALID
history_dict = {
'ticker': ticker,
'algo_start_price': ae_consts.to_f(algo_start_price),
'algo_price_change': ae_consts.to_f(price_change_since_start),
'original_balance': ae_consts.to_f(original_balance),
'status': status,
'algo_status': algo_status,
'buy_now': buy_triggered,
'buy_strength': buy_strength,
'buy_risk': buy_risk,
'sell_now': sell_triggered,
'sell_strength': sell_strength,
'sell_risk': sell_risk,
'num_indicators_buy': num_indicators_buy,
'num_indicators_sell': num_indicators_sell,
'min_buy_indicators': min_buy_indicators,
'min_sell_indicators': min_sell_indicators,
'ds_id': ds_id,
'num_owned': num_owned,
'close': ae_consts.to_f(close),
'balance': ae_consts.to_f(balance),
'commission': ae_consts.to_f(commission),
'date': date,
'minute': minute,
'trade_type': trade_type,
'high': ae_consts.to_f(high),
'low': ae_consts.to_f(low),
'open': ae_consts.to_f(open_val),
'volume': volume,
'ask': ae_consts.to_f(ask),
'bid': ae_consts.to_f(bid),
'today_high': ae_consts.to_f(today_high),
'today_low': ae_consts.to_f(today_low),
'today_open_val': ae_consts.to_f(today_open_val),
'today_close': ae_consts.to_f(today_close),
'today_volume': ae_consts.to_f(today_volume),
'stop_loss': ae_consts.to_f(stop_loss),
'trailing_stop_loss': ae_consts.to_f(trailing_stop_loss),
'buy_hold_units': buy_hold_units,
'sell_hold_units': sell_hold_units,
'low_strike': low_strike,
'low_bid': ae_consts.to_f(low_bid),
'low_ask': ae_consts.to_f(low_ask),
'low_volume': low_volume,
'low_open_int': low_open_int,
'low_delta': low_delta,
'low_gamma': low_gamma,
'low_theta': low_theta,
'low_vega': low_vega,
'low_rho': low_rho,
'low_impl_vol': low_impl_vol,
'low_intrinsic': low_intrinsic,
'low_extrinsic': low_extrinsic,
'low_theo_price': low_theo_price,
'low_theo_volatility': low_theo_volatility,
'low_max_covered': low_max_covered,
'low_exp_date': low_exp_date,
'high_strike': high_strike,
'high_bid': ae_consts.to_f(high_bid),
'high_ask': ae_consts.to_f(high_ask),
'high_volume': high_volume,
'high_open_int': high_open_int,
'high_delta': high_delta,
'high_gamma': high_gamma,
'high_theta': high_theta,
'high_vega': high_vega,
'high_rho': high_rho,
'high_impl_vol': high_impl_vol,
'high_intrinsic': high_intrinsic,
'high_extrinsic': high_extrinsic,
'high_theo_price': high_theo_price,
'high_theo_volatility': high_theo_volatility,
'high_max_covered': high_max_covered,
'high_exp_date': high_exp_date,
'spread_id': spread_id,
'net_gain': net_gain,
'net_value': net_value,
'breakeven_price': breakeven_price,
'max_profit': ae_consts.to_f(max_profit),
'max_loss': ae_consts.to_f(max_loss),
'exp_date': exp_date,
'prev_balance': ae_consts.to_f(prev_balance),
'prev_num_owned': ae_consts.to_f(prev_num_owned),
'total_buys': total_buys,
'total_sells': total_sells,
'note': note,
'err': err,
'version': version
}
# evaluate if the algorithm is gaining
# cash over the test
if balance and original_balance:
# net change on the balance
# note this needs to be upgraded to
# support orders per ticker
# single tickers will work for v1
balance_net_gain = balance - original_balance
if balance_net_gain > 0.0:
algo_status = ae_consts.ALGO_PROFITABLE
else:
algo_status = ae_consts.ALGO_NOT_PROFITABLE
else:
history_dict['err'] = (
f'{ticker} ds_id={ds_id} missing balance={balance} and '
f'original_balance={original_balance}')
algo_status = ae_consts.ALGO_ERROR
# if starting balance and original_balance exist
# to determine algorithm trade profitability
# if there are no shares to sell then
# there's no current trade open
if num_owned and num_owned < 1:
status = ae_consts.TRADE_NO_SHARES_TO_SELL
else:
if close < 0.01:
history_dict['err'] = (
f'{ticker} ds_id={ds_id} close={close} must be greater '
f'than 0.01')
status = ae_consts.TRADE_ERROR
elif algo_start_price < 0.01:
history_dict['err'] = (
f'{ticker} ds_id={ds_id} '
f'algo_start_price={algo_start_price} must be greater '
f'than 0.01')
status = ae_consts.TRADE_ERROR
else:
price_net_gain = close - algo_start_price
if price_net_gain > 0.0:
status = ae_consts.TRADE_PROFITABLE
else:
status = ae_consts.TRADE_NOT_PROFITABLE
# if starting price when algo started and close exist
# determine if this trade profitability
# Assign calculated values:
history_dict['net_gain'] = net_gain
history_dict['balance_net_gain'] = balance_net_gain
history_dict['breakeven_price'] = breakeven_price
history_dict['max_profit'] = max_profit
history_dict['max_loss'] = max_loss
history_dict['exp_date'] = exp_date
# assign statuses
history_dict['status'] = status
history_dict['algo_status'] = algo_status
use_date = minute
if not use_date:
use_date = date
if verbose:
log.debug(
f'{ticker} ds_id={ds_id} {use_date} '
f'algo={ae_consts.get_status(status=history_dict["algo_status"])} '
f'trade={ae_consts.get_status(status=history_dict["status"])} '
f'history={ae_consts.ppj(history_dict)}')
return history_dict
# end of build_trade_history_entry