Stage 07.4.3.9 — Position flip flow
This commit is contained in:
@@ -29,8 +29,8 @@ class ExecutionEngine:
|
||||
if state.decision_status != "READY" or not state.is_signal_ready:
|
||||
return ExecutionDecision("NONE", False, "Сигнал ещё не готов к execution.")
|
||||
|
||||
if self._should_close_position(state):
|
||||
return self._close_position(state)
|
||||
if self._should_flip_position(state):
|
||||
return self._flip_position(state)
|
||||
|
||||
if state.last_signal == "BUY":
|
||||
return self._open_position_if_empty(state=state, side="LONG", action="OPEN_LONG")
|
||||
@@ -102,6 +102,87 @@ class ExecutionEngine:
|
||||
|
||||
return ExecutionDecision(action, True, f"Paper ENTRY {side} открыта.")
|
||||
|
||||
def _flip_position(self, state: AutoTradeState) -> ExecutionDecision:
|
||||
position = type(self)._position
|
||||
|
||||
if position.side == "NONE":
|
||||
self._sync_state_from_position(state)
|
||||
return ExecutionDecision("NONE", False, "Нет позиции для flip.")
|
||||
|
||||
new_side = self._target_side_from_signal(state.last_signal)
|
||||
if new_side is None:
|
||||
return ExecutionDecision("NONE", False, "Нет направления для flip.")
|
||||
|
||||
try:
|
||||
ticker = ExchangeService().get_price(state.symbol)
|
||||
flip_price = ticker.price
|
||||
except Exception as exc:
|
||||
return ExecutionDecision("NONE", False, f"Ошибка получения цены для flip: {exc}")
|
||||
|
||||
now = self._now_time()
|
||||
pnl = self._calculate_pnl(flip_price)
|
||||
new_size = self._calculate_position_size(state)
|
||||
|
||||
old_side = position.side
|
||||
old_entry_price = position.entry_price
|
||||
old_size = position.size
|
||||
old_leverage = position.leverage
|
||||
old_opened_at = position.opened_at
|
||||
|
||||
type(self)._position = PositionState(
|
||||
side=new_side,
|
||||
symbol=state.symbol,
|
||||
entry_price=flip_price,
|
||||
size=new_size,
|
||||
leverage=state.leverage,
|
||||
unrealized_pnl_usd=0.0,
|
||||
opened_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
|
||||
self._sync_state_from_position(state)
|
||||
|
||||
payload = {
|
||||
"execution_type": "FLIP",
|
||||
"action": f"FLIP_{old_side}_TO_{new_side}",
|
||||
"symbol": state.symbol,
|
||||
"old_side": old_side,
|
||||
"new_side": new_side,
|
||||
"side": new_side,
|
||||
"entry_price": old_entry_price,
|
||||
"exit_price": flip_price,
|
||||
"new_entry_price": flip_price,
|
||||
"old_size": old_size,
|
||||
"new_size": new_size,
|
||||
"size": new_size,
|
||||
"old_leverage": old_leverage,
|
||||
"leverage": state.leverage,
|
||||
"pnl": pnl,
|
||||
"signal": state.last_signal,
|
||||
"confidence": state.last_signal_confidence,
|
||||
"repeat_count": state.last_signal_repeat_count,
|
||||
"reason": state.last_signal_reason,
|
||||
"opened_at": old_opened_at,
|
||||
"closed_at": now,
|
||||
"new_opened_at": now,
|
||||
}
|
||||
|
||||
JournalService().log_ui_info(
|
||||
event_type="paper_position_flipped",
|
||||
message=f"Paper FLIP выполнен: {old_side} → {new_side} {state.symbol}",
|
||||
screen="auto",
|
||||
action="paper_execution",
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
EventBus.emit("paper_position_flipped", payload)
|
||||
|
||||
return ExecutionDecision(
|
||||
f"FLIP_{old_side}_TO_{new_side}",
|
||||
True,
|
||||
f"Paper FLIP выполнен: {old_side} → {new_side}.",
|
||||
)
|
||||
|
||||
def _close_position(self, state: AutoTradeState) -> ExecutionDecision:
|
||||
position = type(self)._position
|
||||
|
||||
@@ -151,7 +232,7 @@ class ExecutionEngine:
|
||||
|
||||
return ExecutionDecision("CLOSE", True, "Paper EXIT выполнена.")
|
||||
|
||||
def _should_close_position(self, state: AutoTradeState) -> bool:
|
||||
def _should_flip_position(self, state: AutoTradeState) -> bool:
|
||||
position = type(self)._position
|
||||
|
||||
if position.side == "NONE":
|
||||
@@ -165,6 +246,15 @@ class ExecutionEngine:
|
||||
|
||||
return False
|
||||
|
||||
def _target_side_from_signal(self, signal: str | None) -> str | None:
|
||||
if signal == "BUY":
|
||||
return "LONG"
|
||||
|
||||
if signal == "SELL":
|
||||
return "SHORT"
|
||||
|
||||
return None
|
||||
|
||||
def _update_unrealized_pnl(self, state: AutoTradeState) -> None:
|
||||
position = type(self)._position
|
||||
|
||||
|
||||
Reference in New Issue
Block a user