Skip to content

Commit

Permalink
Alpha model frozen position check (#1142)
Browse files Browse the repository at this point in the history
- Add frozen position check inside `generate_rebalance_trades_and_triggers` so we do not try to trade on a frozen position (increase/decrease)
-
  • Loading branch information
miohtama authored Jan 21, 2025
1 parent f68d95a commit 810cd4c
Show file tree
Hide file tree
Showing 11 changed files with 682 additions and 353 deletions.
2 changes: 1 addition & 1 deletion deps/web3-ethereum-defi
994 changes: 651 additions & 343 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ python = ">=3.11,<=3.12"
trading-strategy = {path = "deps/trading-strategy", develop = true}
# trading-strategy = "^0.22.6"
requests = "^2.27.1"
matplotlib = "^3.6.0"
matplotlib = ">=3.5"
jupyterlab = "^4.0.7"
pandas = "<3"
pandas-ta = "^0.3.14b" # Unmaintained, still stick to old Pandas
Expand Down
2 changes: 1 addition & 1 deletion tests/backtest/test_backtest_credit_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def test_backtest_credit_flow(
credit_positions = [p for p in portfolio.get_all_positions() if p.is_credit_supply()]
assert len(credit_positions) == 10
total_interest_gained = sum(p.get_total_profit_usd() for p in credit_positions)
assert total_interest_gained == pytest.approx(76.8234503286251)
assert total_interest_gained == pytest.approx(76.8234503286251, rel=0.01)

interest_metrics = calculate_credit_metrics(state)
assert isinstance(interest_metrics, pd.DataFrame)
1 change: 1 addition & 0 deletions tests/mainnet_fork/test_enzyme_credit_position.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def environment(
"PATH": os.environ["PATH"], # Needs forge
"ONE_DELTA": "true",
"MAX_CYCLES": "3", # Run decide_trades() 2 times
"MAX_DATA_DELAY_MINUTES": "36000",
}
return environment

Expand Down
7 changes: 5 additions & 2 deletions tradeexecutor/analysis/pair.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,17 @@ def display_strategy_universe(

if show_price:
try:
data["price"], data["price_at"] = strategy_universe.data_universe.candles.get_price_with_tolerance(
data["price"], data["last_price_at"] = strategy_universe.data_universe.candles.get_price_with_tolerance(
pair=pair.internal_id,
when=candle_now,
tolerance=tolerance,
)
candles = strategy_universe.data_universe.candles.get_candles_by_pair(pair.internal_id)
data["first_price_at"] = candles.index[0]
except CandleSampleUnavailable:
data["price"] = "<not avail>"
data["price_at"] = "-"
data["first_price_at"] = "-"
data["last_price_at"] = "-"

if show_volume:
try:
Expand Down
9 changes: 5 additions & 4 deletions tradeexecutor/backtest/grid_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,10 +451,11 @@ def __eq__(self, other):
return self.combination == other.combination

def __repr__(self) -> str:
cagr = self.get_cagr()
sharpe = self.get_sharpe()
max_drawdown = self.get_max_drawdown()
return f"<GridSearchResult\n {self.combination.get_all_parameters_label()}\n CAGR: {cagr*100:.2f}% Sharpe: {sharpe:.2f} Max drawdown:{max_drawdown*100:.2f}%\n>"
cagr = self.get_cagr() or 0
sharpe = self.get_sharpe() or 0
sortino = self.get_sortino() or 0
max_drawdown = self.get_max_drawdown() or 0
return f"<GridSearchResult\n {self.combination.get_all_parameters_label()}\n CAGR: {cagr*100:.2f}%, Sharpe: {sharpe:.2f}, Sortino: {sortino:.2f}, Max drawdown:{max_drawdown*100:.2f}%\n>"

def get_label(self) -> str:
"""Get name for this result for charts.
Expand Down
8 changes: 8 additions & 0 deletions tradeexecutor/backtest/optimiser.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,14 @@ def get_filtered_count(self) -> int:
"""
return len([r for r in self.results if r.filtered])

def find_best(self, sort_key: Callable, reverse=True) -> OptimiserSearchResult | None:
""""""
if len(self.results) == 0:
return None
sorted_results = self.results.sorted(sort_key, reverse=reverse)
return sorted_results[0]



class ObjectiveWrapper:
"""Middleware between Optimiser and TS frameworks.
Expand Down
2 changes: 2 additions & 0 deletions tradeexecutor/cli/commands/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ def start(
else:
name = "Unnamed backtest"

assert asset_management_mode, f"ASSET_MANAGEMENT_MODE must given, options are: {[member.name for member in AssetManagementMode]}"

if not log_level:
if asset_management_mode == AssetManagementMode.backtest:
log_level = logging.WARNING
Expand Down
6 changes: 6 additions & 0 deletions tradeexecutor/strategy/alpha_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,8 @@ def generate_rebalance_trades_and_triggers(
s.flags.add(TradingPairSignalFlags.max_adjust_too_small)
return []

frozen_pairs = {p.pair for p in position_manager.state.portfolio.frozen_positions.values()}

# TODO: Break this massive for if spagetti to sub-functions
for signal in self.iterate_signals():

Expand Down Expand Up @@ -1217,6 +1219,10 @@ def generate_rebalance_trades_and_triggers(
signal.normalised_weight,
dollar_diff)

if signal.pair in frozen_pairs:
logger.warning("Does not generate trades for a pair with frozen positions: %s", signal.pair)
continue

if individual_rebalance_min_threshold:
trade_size = abs(dollar_diff)

Expand Down

0 comments on commit 810cd4c

Please sign in to comment.