Stage 07.4.4.1.14 — Execution refactoring and runtime semantics
This commit is contained in:
@@ -2,22 +2,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
#import math
|
||||
#from dataclasses import dataclass
|
||||
#from datetime import datetime
|
||||
|
||||
#from src.core.event_bus import EventBus
|
||||
#from src.integrations.exchange.service import ExchangeService
|
||||
from src.trading.auto.state import AutoTradeState
|
||||
from src.trading.execution.models import ExecutionDecision
|
||||
#from src.trading.journal.service import JournalService
|
||||
from src.trading.position.state import PositionState
|
||||
#from src.core.numbers import safe_float
|
||||
#from src.core.types import NumericLike
|
||||
from src.trading.execution.pricing import ExecutionPricingMixin
|
||||
from src.trading.execution.position_runtime import ExecutionPositionRuntimeMixin
|
||||
from src.trading.execution.position_intelligence import ExecutionPositionIntelligenceMixin
|
||||
from src.trading.execution.position_exit_decision import ExecutionPositionExitDecisionMixin
|
||||
from src.trading.execution.position_protection import ExecutionPositionProtectionMixin
|
||||
from src.trading.execution.supervisor import ExecutionSupervisorMixin
|
||||
from src.trading.execution.sizing import ExecutionSizingMixin
|
||||
@@ -27,6 +17,17 @@ from src.trading.execution.position_actions import ExecutionPositionActionsMixin
|
||||
from src.trading.execution.runtime_actions import ExecutionRuntimeActionsMixin
|
||||
from src.trading.execution.calculations import ExecutionCalculationsMixin
|
||||
from src.trading.execution.resets import ExecutionResetsMixin
|
||||
from src.trading.execution.constants import (
|
||||
EXECUTION_ACTION_NONE,
|
||||
EXECUTION_ACTION_OPEN_LONG,
|
||||
EXECUTION_ACTION_OPEN_SHORT,
|
||||
EXECUTION_DECISION_READY,
|
||||
EXECUTION_STATUS_RUNNING,
|
||||
POSITION_SIDE_LONG,
|
||||
POSITION_SIDE_SHORT,
|
||||
SIGNAL_BUY,
|
||||
SIGNAL_SELL,
|
||||
)
|
||||
|
||||
|
||||
class ExecutionEngine(
|
||||
@@ -34,7 +35,7 @@ class ExecutionEngine(
|
||||
ExecutionResetsMixin,
|
||||
ExecutionPricingMixin,
|
||||
ExecutionPositionRuntimeMixin,
|
||||
ExecutionPositionIntelligenceMixin,
|
||||
ExecutionPositionExitDecisionMixin,
|
||||
ExecutionSizingMixin,
|
||||
ExecutionPositionActionsMixin,
|
||||
ExecutionPositionProtectionMixin,
|
||||
@@ -45,11 +46,11 @@ class ExecutionEngine(
|
||||
):
|
||||
_position = PositionState()
|
||||
_size_precision = 5
|
||||
_min_flip_confidence = 0.75
|
||||
_min_flip_repeat_count = 3
|
||||
_min_flip_hold_seconds = 60
|
||||
_min_flip_confidence = 0.65
|
||||
_min_flip_repeat_count = 2
|
||||
_min_flip_hold_seconds = 20
|
||||
_flip_cooldown_seconds = 45
|
||||
_loss_flip_confidence = 0.9
|
||||
_loss_flip_confidence = 0.75
|
||||
_last_flip_block_key: str | None = None
|
||||
_runtime_action_cooldown_seconds = 30
|
||||
_last_runtime_action_key: str | None = None
|
||||
@@ -61,7 +62,6 @@ class ExecutionEngine(
|
||||
_max_execution_snapshot_age_seconds = 5
|
||||
|
||||
_degraded_market_block_states = {
|
||||
"HIGH_VOLATILITY",
|
||||
"CHAOTIC",
|
||||
"LIQUIDITY_VOID",
|
||||
}
|
||||
@@ -70,47 +70,81 @@ class ExecutionEngine(
|
||||
|
||||
_last_supervisor_block_key: str | None = None
|
||||
|
||||
# вернуть ExecutionDecision без выполнения торгового действия
|
||||
def _skip_execution(
|
||||
self,
|
||||
state: AutoTradeState,
|
||||
reason: str,
|
||||
) -> ExecutionDecision:
|
||||
state.last_execution_action = EXECUTION_ACTION_NONE
|
||||
state.last_execution_reason = reason
|
||||
|
||||
return ExecutionDecision(
|
||||
EXECUTION_ACTION_NONE,
|
||||
False,
|
||||
reason,
|
||||
)
|
||||
|
||||
def process(self, state: AutoTradeState) -> ExecutionDecision:
|
||||
# Synchronize runtime state
|
||||
self._sync_state_from_position(state)
|
||||
|
||||
if state.status != "RUNNING":
|
||||
return ExecutionDecision("NONE", False, "Execution доступен только в режиме RUNNING.")
|
||||
if state.status != EXECUTION_STATUS_RUNNING:
|
||||
return self._skip_execution(
|
||||
state,
|
||||
"Execution доступен только в режиме RUNNING.",
|
||||
)
|
||||
|
||||
self._update_unrealized_pnl(state)
|
||||
|
||||
# Emergency risk management
|
||||
risk_decision = self._risk_close_decision(state)
|
||||
if risk_decision is not None:
|
||||
return risk_decision
|
||||
|
||||
# Runtime position protection
|
||||
protection_decision = self._process_runtime_protection(state)
|
||||
if protection_decision is not None:
|
||||
return protection_decision
|
||||
|
||||
# Signal readiness validation
|
||||
if state.decision_status != EXECUTION_DECISION_READY or not state.is_signal_ready:
|
||||
reason = (
|
||||
f"Execution ожидает READY "
|
||||
f"(decision={state.decision_status}, "
|
||||
f"ready={state.is_signal_ready})."
|
||||
)
|
||||
|
||||
return self._skip_execution(
|
||||
state,
|
||||
reason,
|
||||
)
|
||||
|
||||
# Execution supervisor
|
||||
supervisor_decision = self._process_execution_supervisor(state)
|
||||
if supervisor_decision is not None:
|
||||
return supervisor_decision
|
||||
|
||||
if state.decision_status != "READY" or not state.is_signal_ready:
|
||||
return ExecutionDecision("NONE", False, "Сигнал ещё не готов к execution.")
|
||||
|
||||
# Existing position validation
|
||||
position = type(self)._position
|
||||
|
||||
# Не пытаемся повторно открыть позицию в ту же сторону.
|
||||
# Сигнал остаётся валидным для UI/Telegram, но execution не дублируется.
|
||||
if position.side == "LONG" and state.last_signal == "BUY":
|
||||
if position.side == POSITION_SIDE_LONG and state.last_signal == SIGNAL_BUY:
|
||||
return ExecutionDecision(
|
||||
"NONE",
|
||||
EXECUTION_ACTION_NONE,
|
||||
False,
|
||||
"Сигнал BUY совпадает с уже открытой LONG позицией.",
|
||||
)
|
||||
|
||||
if position.side == "SHORT" and state.last_signal == "SELL":
|
||||
if position.side == POSITION_SIDE_SHORT and state.last_signal == SIGNAL_SELL:
|
||||
return ExecutionDecision(
|
||||
"NONE",
|
||||
EXECUTION_ACTION_NONE,
|
||||
False,
|
||||
"Сигнал SELL совпадает с уже открытой SHORT позицией.",
|
||||
)
|
||||
|
||||
# Position flip
|
||||
if self._should_flip_position(state):
|
||||
flip_block_reason = self._flip_block_reason(state)
|
||||
|
||||
@@ -119,10 +153,22 @@ class ExecutionEngine(
|
||||
|
||||
return self._flip_position(state)
|
||||
|
||||
if state.last_signal == "BUY":
|
||||
return self._open_position_if_empty(state=state, side="LONG", action="OPEN_LONG")
|
||||
# New position opening
|
||||
if state.last_signal == SIGNAL_BUY:
|
||||
return self._open_position_if_empty(
|
||||
state=state,
|
||||
side=POSITION_SIDE_LONG,
|
||||
action=EXECUTION_ACTION_OPEN_LONG,
|
||||
)
|
||||
|
||||
if state.last_signal == "SELL":
|
||||
return self._open_position_if_empty(state=state, side="SHORT", action="OPEN_SHORT")
|
||||
if state.last_signal == SIGNAL_SELL:
|
||||
return self._open_position_if_empty(
|
||||
state=state,
|
||||
side=POSITION_SIDE_SHORT,
|
||||
action=EXECUTION_ACTION_OPEN_SHORT,
|
||||
)
|
||||
|
||||
return ExecutionDecision("NONE", False, "Нет торгового действия.")
|
||||
return self._skip_execution(
|
||||
state,
|
||||
"Нет торгового действия.",
|
||||
)
|
||||
Reference in New Issue
Block a user