Skip to content

Atomic signal flip: opt-in flip_on_opposite_signal flag #561

@MDUYN

Description

@MDUYN

Summary

Add an opt-in flip_on_opposite_signal: bool = False flag on TradingStrategy so that when an OPEN_SHORT signal fires against an existing long position (or BUY against an existing short), the framework closes the current position and opens the opposite-side position atomically on the same bar.

Motivation

Today the framework enforces "one position per symbol" by silently rejecting opposite-side signals with signal_event.reason="already_in_position" / "open_short_position". For trend-following strategies that rely on clean trend flips (e.g. SuperTrend, MA crossover), this means:

  • The exit and re-entry happen on separate bars (or never, if a cooldown is configured).
  • Strategies miss the move during the gap.
  • The behaviour is silent — users only discover it by reading signal_events or visualising the chart.

This is the #1 friction point reported for short-enabled strategies.

Proposed design

  • New field on TradingStrategy: flip_on_opposite_signal: bool = False (default keeps current behaviour).
  • When True:
    • Vector engine (vector_backtest_service.py) — in the two rejection branches at ~lines 1140 / 1277, instead of emitting a rejection signal_event, close the existing position at the current bar's price and open the opposite-side position on the same bar.
    • Event engine — add a resolve_conflicts phase hook that rewrites opposite-side signals into [CLOSE, OPEN] order pairs.
  • Cooldown bookkeeping must skip the synthetic close so the new opposite-side open isn't blocked by its own flip.
  • Position sizing for the opposite side uses post-close cash (not pre-close).

Acceptance criteria

  • flip_on_opposite_signal field exists on TradingStrategy, defaults to False.
  • Default behaviour is byte-identical to v9.0 (verified by re-running full test suite).
  • Vector engine: when flag is True, a SHORT signal against an open long produces one SELL + one OPEN_SHORT order on the same bar.
  • Vector engine: when flag is True, a BUY signal against an open short produces one COVER + one BUY order on the same bar.
  • Event engine: same behaviour via the strategy phase pipeline.
  • Deterministic scenario tests added to tests/scenarios/test_deterministic_pnl_scenarios.py covering long→short flip and short→long flip on both engines (4 tests total).
  • Documentation updated in the strategy reference.

Non-goals

  • Does not allow simultaneous long+short on the same symbol (that's hedge mode — see #[hedge-mode-issue]).
  • Does not change Position / Trade data model.

Estimated scope

~half day. Opt-in, no breaking changes.

Release target

v9.1.0 (SemVer MINOR — additive, backward-compatible).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions