Egis Dev – 15/02

Theme: Invariants over assumptions

Today wasn’t about building new features.

It was about protecting the system from itself.


1️⃣ Position truth vs. Exchange truth

The biggest lesson came from investigating Position 275.

  • Egis marked it CLOSED.
  • Binance (Demo) clearly showed it was still OPEN.
  • Sync logic said it should not be closed.
  • Yet somehow, it was.

That contradiction forced me to confront something uncomfortable:

A trading system is not allowed to “mostly” follow rules.

It must follow them absolutely.

I introduced and reinforced a hard invariant:

  • A position can only be CLOSED if Binance is flat for:
    • same exchange
    • same symbol
    • same positionSide (Hedge mode aware)

No local inference.

No guessing from orders.

No “probably closed”.

Binance flatness is the only gate.


2️⃣ The real danger: Exchange confusion

After adding Binance Real alongside Demo, subtle bugs emerged.

The 10-minute sync job became suspect.

We had to ensure:

  • Every sync is explicitly scoped by exchange.
  • No global filters.
  • No UI context leakage.
  • No symbol-only grouping.
  • No demo/real mixing in caches or in-memory maps.

This reinforced something important:

Multi-exchange systems fail silently when identity isn’t explicit.

From now on:

  • Exchange is part of every key.
  • positionKey = (exchange, account, bot, symbol, positionSide)
  • No implicit defaults.

3️⃣ Duplicate positions (276 / 277) – the Hedge Mode trap

Switching from single mode to Hedge Mode exposed a deeper flaw.

We discovered:

  • Position 276 closed (~$18)
  • Position 277 opened (~$20)
  • Binance had ONE position (~$38)

Egis split a single Binance position into two internal records.

That is a structural error.

The fix was not cosmetic.

We redefined the invariant:

One Binance position (per exchange + symbol + positionSide + bot)

must map to exactly one OPEN Egis Position.

No splits.

No accidental reopen.

No continuation into a new record.

Scaling in must attach to the same position.

That required:

  • Centralized position resolver
  • Concurrency-safe guard
  • Duplicate-prevention at creation boundary
  • Locking strategy to prevent race conditions
  • Tests simulating parallel signals

This was not a patch.

It was architectural correction.


4️⃣ Signal hooks must not control lifecycle

Signal Hook 957 reopened a previously closed position.

That was a red flag.

Signal intake logic must:

  • Place orders
  • Decide whether to ignore duplicates
  • Never mutate position lifecycle directly

Position open/close transitions belong to reconciliation logic only.

Signal hooks no longer:

  • Flip status
  • Null closed_at
  • “Repair” anything

They now respect the invariant:

If open position exists for (bot + symbol + side) → DO NOTHING.


5️⃣ Debugging visibility improvements

To prevent future blindness, I improved:

  • egis:debug:position-consistency
    • Now shows exchange identity clearly
    • Shows connection ID
    • Shows TP/SL open orders
    • Shows stopPrice for algo orders

The key insight:

Debug tools are not optional.

They are part of system design.

If debugging is vague, production behavior will be vague.


6️⃣ UI consistency fixes

On Admin > Position Detail:

  • Removed duplicated timeline rows.
  • Fixed estimated entry price logic.
  • Separated:
    • Estimated entry price (position context)
    • Trigger price (stopPrice for algo orders)

Algo orders must show their trigger levels (0.09777 / 0.0846),

not the position’s entry average (0.091348).

Precision matters.

Traders notice.


7️⃣ Average Metrics integrity

Found fee leakage into days with zero trades.

Root cause:

Fees were aggregated independently of the trade dataset.

Fix:

Fees must be derived strictly from the same trade scope as:

  • Trades count
  • Realised PnL
  • Avg duration

If trades = 0 → fees = 0.

No exceptions.


What I learned today

1. Invariants must be enforced at boundaries

Don’t rely on “expected behavior”.

Enforce it where state changes.

2. Hedge Mode changes everything

Single-mode mental models break under Hedge Mode.

positionSide becomes part of identity.

3. Exchange identity is not metadata — it is core logic

If exchange is not explicit everywhere, bugs will hide between environments.

4. Signal logic must never own state truth

Signals propose actions.

Reconciliation defines reality.

5. Debugging clarity equals system reliability

A good debug command prevents weeks of guesswork.


Today wasn’t glamorous.

No shiny feature.

But the system is now:

  • More deterministic
  • More invariant-driven
  • More resistant to race conditions
  • More exchange-safe

That’s real progress.

Egis is becoming less reactive…

and more mathematically strict.

And that’s exactly what a trading system should be.