Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/crypto_strategies/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
"btc_weight": 0.30,
"trend_weight": 0.70,
"dynamic_mode": True,
"dynamic_regime_off_cut": 0.50,
"smart_multiplier_enabled": True,
"cycle_indicator_enabled": True,
"zscore_exit_enabled": True,
Expand Down
1 change: 1 addition & 0 deletions src/crypto_strategies/entrypoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ def evaluate_crypto_equity_combo(ctx: StrategyContext) -> StrategyDecision:
btc_weight=float(config.get("btc_weight", 0.30)),
trend_weight=float(config.get("trend_weight", 0.70)),
dynamic_mode=bool(config.get("dynamic_mode", True)),
dynamic_regime_off_cut=float(config.get("dynamic_regime_off_cut", 0.50)),
smart_multiplier_enabled=bool(config.get("smart_multiplier_enabled", True)),
cycle_indicator_enabled=bool(config.get("cycle_indicator_enabled", True)),
zscore_exit_enabled=bool(config.get("zscore_exit_enabled", True)),
Expand Down
1 change: 1 addition & 0 deletions src/crypto_strategies/manifests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
"btc_weight": 0.30,
"trend_weight": 0.70,
"dynamic_mode": True,
"dynamic_regime_off_cut": 0.50,
"smart_multiplier_enabled": True,
"cycle_indicator_enabled": True,
"zscore_exit_enabled": True,
Expand Down
8 changes: 6 additions & 2 deletions src/crypto_strategies/strategies/crypto_equity_combo.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ def build_target_weights(
btc_weight: float = DEFAULT_BTC_WEIGHT,
trend_weight: float = DEFAULT_TREND_WEIGHT,
dynamic_mode: bool = True,
dynamic_regime_off_cut: float = DYNAMIC_REGIME_OFF_CUT,
translator=None,
**kwargs: Any,
) -> tuple[dict[str, float], dict[str, object]]:
Expand Down Expand Up @@ -243,9 +244,11 @@ def build_target_weights(
regime_off = not btc_snapshot.get("regime_on", True)

if dynamic_mode and regime_off:
effective_btc = btc_weight + trend_weight * DYNAMIC_REGIME_OFF_CUT
effective_trend = trend_weight * (1.0 - DYNAMIC_REGIME_OFF_CUT)
regime_off_cut = _clamp_ratio(dynamic_regime_off_cut, default=DYNAMIC_REGIME_OFF_CUT)
effective_btc = btc_weight + trend_weight * regime_off_cut
effective_trend = trend_weight * (1.0 - regime_off_cut)
else:
regime_off_cut = 0.0
effective_btc = btc_weight
effective_trend = trend_weight

Expand Down Expand Up @@ -292,6 +295,7 @@ def build_target_weights(
"trend_weight": effective_trend,
"base_btc_weight": btc_weight,
"base_trend_weight": trend_weight,
"dynamic_regime_off_cut": regime_off_cut,
},
"btc_leg": {"weights": btc_weights, **btc_leg_metadata},
"trend_leg": {"weights": trend_weights, **trend_metadata},
Expand Down
31 changes: 31 additions & 0 deletions tests/test_crypto_equity_combo.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,37 @@ def test_trend_leg_honors_rotation_refresh_lock(self) -> None:
self.assertEqual(positive_weights, {"ETHUSDT"})
self.assertEqual(set(metadata["trend_leg"]["weights"]), {"ETHUSDT"})

def test_dynamic_regime_off_cut_is_configurable(self) -> None:
"""Regime-off cut should be configurable while keeping the 50% default."""
_, default_metadata = build_target_weights(
prices={"BTCUSDT": 60000.0},
indicators_map={},
universe_snapshot=[],
benchmark_snapshot={"regime_on": False},
portfolio={"total_equity": 100000.0, "buying_power": 1000.0},
btc_weight=0.30,
trend_weight=0.70,
smart_multiplier_enabled=False,
)
_, custom_metadata = build_target_weights(
prices={"BTCUSDT": 60000.0},
indicators_map={},
universe_snapshot=[],
benchmark_snapshot={"regime_on": False},
portfolio={"total_equity": 100000.0, "buying_power": 1000.0},
btc_weight=0.30,
trend_weight=0.70,
dynamic_regime_off_cut=0.30,
smart_multiplier_enabled=False,
)

self.assertAlmostEqual(default_metadata["combo"]["btc_weight"], 0.65)
self.assertAlmostEqual(default_metadata["combo"]["trend_weight"], 0.35)
self.assertAlmostEqual(default_metadata["combo"]["dynamic_regime_off_cut"], 0.50)
self.assertAlmostEqual(custom_metadata["combo"]["btc_weight"], 0.51)
self.assertAlmostEqual(custom_metadata["combo"]["trend_weight"], 0.49)
self.assertAlmostEqual(custom_metadata["combo"]["dynamic_regime_off_cut"], 0.30)

def test_compute_signals_returns_tuple(self) -> None:
"""compute_signals should return a 5-tuple with weights, signal_desc, cash_residual, status_desc, metadata."""
prices = {"BTCUSDT": 60000.0}
Expand Down
Loading