Allow exchange-minimum Kelly layers
This commit is contained in:
@@ -631,8 +631,11 @@ def _torch_forecast_entry_signal(
|
||||
if open_positions_for_symbol >= _dynamic_symbol_position_limit(settings):
|
||||
return Signal(symbol, "HOLD", 0.0, "torch_forecast: symbol position limit reached")
|
||||
|
||||
account_context = dict(account or {})
|
||||
account_context.setdefault("symbol", symbol)
|
||||
account_context.setdefault("open_positions_for_symbol", open_positions_for_symbol)
|
||||
stop_loss_percent = _clamp(settings.stop_loss_percent, 0.003, 0.08)
|
||||
sizing = _torch_forecast_position_sizing(settings, account, stop_loss_percent, forecast, symbol)
|
||||
sizing = _torch_forecast_position_sizing(settings, account_context, stop_loss_percent, forecast, symbol)
|
||||
position_notional = float(sizing["notional_usdt"])
|
||||
expected_return = _safe_float(forecast.get("expected_return_percent"), 0.0)
|
||||
probability_up = _safe_float(forecast.get("probability_up"), 0.5)
|
||||
@@ -1218,7 +1221,28 @@ def _kelly_position(
|
||||
bankroll = settings.starting_balance_usdt
|
||||
target_notional = max(0.0, bankroll * effective_fraction)
|
||||
open_symbol_exposure = _account_symbol_exposure(account, symbol)
|
||||
remaining_notional = max(0.0, target_notional - open_symbol_exposure)
|
||||
raw_remaining_notional = max(0.0, target_notional - open_symbol_exposure)
|
||||
exchange_min_entry = _account_exchange_min_entry(account, settings)
|
||||
remaining_notional = raw_remaining_notional
|
||||
effective_target_notional = target_notional
|
||||
layer_mode = False
|
||||
if (
|
||||
symbol
|
||||
and target_notional > 0
|
||||
and raw_remaining_notional < exchange_min_entry
|
||||
and exchange_min_entry > settings.min_position_usdt + 1e-9
|
||||
and _account_open_positions_for_symbol(account) > 0
|
||||
):
|
||||
room = min(
|
||||
max(0.0, settings.max_position_usdt),
|
||||
max(0.0, settings.max_symbol_exposure_usdt - open_symbol_exposure),
|
||||
max(0.0, settings.max_total_exposure_usdt - _account_total_exposure(account)),
|
||||
max(0.0, _safe_float((account or {}).get("cash"), settings.starting_balance_usdt) - settings.min_cash_reserve_usdt),
|
||||
)
|
||||
if room >= exchange_min_entry:
|
||||
remaining_notional = exchange_min_entry
|
||||
effective_target_notional = open_symbol_exposure + exchange_min_entry
|
||||
layer_mode = True
|
||||
return {
|
||||
"kelly_probability": round(probability, 4),
|
||||
"kelly_probability_source": probability_source,
|
||||
@@ -1231,9 +1255,13 @@ def _kelly_position(
|
||||
"kelly_effective_fraction": round(effective_fraction, 4),
|
||||
"kelly_bankroll_usdt": round(bankroll, 2),
|
||||
"kelly_target_notional_usdt": round(target_notional, 2),
|
||||
"kelly_effective_target_notional_usdt": round(effective_target_notional, 2),
|
||||
"kelly_open_symbol_exposure_usdt": round(open_symbol_exposure, 2),
|
||||
"kelly_raw_remaining_notional_usdt": round(raw_remaining_notional, 2),
|
||||
"kelly_remaining_notional_usdt": round(remaining_notional, 2),
|
||||
"kelly_notional_usdt": round(remaining_notional, 2),
|
||||
"kelly_exchange_min_entry_usdt": round(exchange_min_entry, 2),
|
||||
"kelly_layer_mode": layer_mode,
|
||||
}
|
||||
|
||||
|
||||
@@ -1251,6 +1279,28 @@ def _account_symbol_exposure(account: dict | None, symbol: str | None = None) ->
|
||||
return 0.0
|
||||
|
||||
|
||||
def _account_total_exposure(account: dict | None) -> float:
|
||||
if not isinstance(account, dict):
|
||||
return 0.0
|
||||
return max(0.0, _safe_float(account.get("exposure"), 0.0))
|
||||
|
||||
|
||||
def _account_open_positions_for_symbol(account: dict | None) -> int:
|
||||
if not isinstance(account, dict):
|
||||
return 0
|
||||
try:
|
||||
return max(0, int(account.get("open_positions_for_symbol", 0)))
|
||||
except (TypeError, ValueError):
|
||||
return 0
|
||||
|
||||
|
||||
def _account_exchange_min_entry(account: dict | None, settings: Settings) -> float:
|
||||
minimum = max(0.0, settings.min_position_usdt)
|
||||
if not isinstance(account, dict):
|
||||
return minimum
|
||||
return max(minimum, _safe_float(account.get("exchange_min_entry_usdt"), minimum))
|
||||
|
||||
|
||||
def _confidence_probability(final_score: float, min_signal_confidence: float) -> float:
|
||||
denominator = max(0.0001, 1.0 - min_signal_confidence)
|
||||
ratio = _clamp((final_score - min_signal_confidence) / denominator, 0.0, 1.0)
|
||||
|
||||
Reference in New Issue
Block a user