Skip to content

Commit

Permalink
Fixing issues with alpha model and new TVL size risk model (#1051)
Browse files Browse the repository at this point in the history
- Add safety asserts
- Allow empty normalised weights
  • Loading branch information
miohtama authored Oct 1, 2024
1 parent 9469cfa commit f7afb9a
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 18 deletions.
43 changes: 43 additions & 0 deletions tradeexecutor/strategy/alpha_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,30 @@ def get_debug_print(self) -> str:
print(f" Signal #{idx} {signal}", file=buf)
return buf.getvalue()

def has_any_signal(self) -> bool:
"""For this cycle, should we try to do any trades.
Any of the signals have non-zero value
:return:
True if alpha model should attempt to do some trades on this timestamp/cycle.
Trades could be still cancelled (zeroed out) by a risk model.
"""
return any([s for s in self.signals.values() if s.signal != 0])

def has_any_position(self) -> bool:
"""For this cycle, are we going to do any trades.
Some signals have :py:attr:`TradingSignal.position_target` set after the risk adjustments.
:return:
True if alpha model should attempt to do some trades on this timestamp/cycle.
Trades could be still cancelled (zeroed out) by a risk model.
"""
return any([s for s in self.signals.values() if s.position_target != 0])

def set_signal(
self,
pair: TradingPairIdentifier,
Expand Down Expand Up @@ -677,6 +701,8 @@ def _normalise_weights_size_risk(
# if all positions are size-risked down
s = self.signals[pair_id]

assert s.old_weight is not None, f"TradingSignal.old_weight is not available: {s} - remember to call AlphaModel.update_old_weights()"

assert s.raw_weight >= 0, "_normalise_weights_size_risk(): short or leverage not implemented"

concentration_capped_normal_weight = min(normal_weight, max_weight)
Expand All @@ -687,6 +713,14 @@ def _normalise_weights_size_risk(
asked_position_size
)

logger.info(
"Position size risk, pair: %s, asked: %s, accepted: %s, diagnostics: %s",
s.pair,
size_risk.asked_size,
size_risk.accepted_size,
size_risk.diagnostics_data,
)

s.position_size_risk = size_risk
s.position_target = size_risk.accepted_size
total_accetable_investments += size_risk.accepted_size
Expand Down Expand Up @@ -765,6 +799,15 @@ def normalise_weights(
investable_equity=investable_equity,
)

# Risk model zeroed out everything so something is likely wrong
if self.has_any_signal() and not self.has_any_position():
logger.warning(
"normalise_weights() at %s had signal, but refuses to have any position target, all positions zeroed out by risk model",
self.timestamp,
)
for s in self.signals.values():
logger.warning("Signal %s", s)

def assign_weights(self, method=weight_by_1_slash_n):
"""Convert raw signals to their portfolio weight counterparts.
Expand Down
12 changes: 7 additions & 5 deletions tradeexecutor/strategy/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1075,11 +1075,13 @@ def post_process_trade_decision(
If any of the trades is detected to have too much price impact.
"""

logger.info(
"post_process_trade_decision(): Post-processing %d trades, max_price_impact: %s",
len(trades),
max_price_impact,
)
if not execution_context.mode.is_backtesting():
# Too noisy for backtesting
logger.info(
"post_process_trade_decision(): Post-processing %d trades, max_price_impact: %s",
len(trades),
max_price_impact,
)

# TODO: Write a full logic here, only supports closing shorts now,
# assuming everything lending is short
Expand Down
20 changes: 8 additions & 12 deletions tradeexecutor/strategy/trading_strategy_universe.py
Original file line number Diff line number Diff line change
Expand Up @@ -1951,7 +1951,6 @@ def load_partial_data(
name: str | None = None,
candle_progress_bar_desc: str | None = None,
lending_candle_progress_bar_desc: str | None = None,

) -> Dataset:
"""Load pair data for given trading pairs.
Expand Down Expand Up @@ -2009,15 +2008,6 @@ def create_trading_universe(
:param time_bucket:
The candle time frame.
:param chain_id:
Which blockchain hosts our exchange
:param exchange_slug:
Which exchange hosts our trading pairs
:param exchange_slug:
Which exchange hosts our trading pairs
:param pairs:
List of trading pair tickers.
Expand All @@ -2027,11 +2017,14 @@ def create_trading_universe(
- Human-readable descriptions, see :py:attr:`tradingstrategy.pair.HumanReadableTradingPairDescription`.
- Direct :py:class:`pandas.DataFrame` of pairs.
:param lending_reserves:
:param lending_resserves:
Lending reserves for which you want to download the data.
Either list of lending pool descriptions or preloaded lending universe.
:param lending_candle_types:
What lending data columns to load
:param liquidity:
Set true to load liquidity data as well
Expand All @@ -2040,9 +2033,12 @@ def create_trading_universe(
If not given use `time_bucket`.
:param lending_reverses:
:param lending_reserves:
Set true to load lending reserve data as well
:lending_candle_types:
What lending data columns to load
:param stop_loss_time_bucket:
If set load stop loss trigger
data using this candle granularity.
Expand Down
4 changes: 4 additions & 0 deletions tradeexecutor/strategy/weighting.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ def normalise_weights(weights: Dict[PairInternalId, Weight]) -> Dict[PairInterna

total = sum(weights.values())
normalised_weights = {}
if total == 0:
# Avoid division by zero
return normalised_weights

for key, value in weights.items():
normalised_weights[key] = value / total

Expand Down

0 comments on commit f7afb9a

Please sign in to comment.