Egis Dev Journal — 05/02

Topic: Funding fees, invariants, and making the system honest

Today was one of those days where the system didn’t “break” loudly — it failed silently. And that’s always the more dangerous kind.

What I worked on

1. Funding fee pipeline (deep dive)

I focused heavily on the funding fee collector, because Admin metrics were clearly wrong:

  • Funding fees stopped appearing after 2026-02-02 08:00 UTC
  • Admin showed 0 despite positions being open and Binance UI showing fees

I built an audit command that answered hard questions instead of guessing:

  • When was the last funding fee actually recorded?
  • How many 8h funding windows are missing?
  • Which bots and symbols were affected?
  • Were positions open (including closed ones now) during those windows?

This immediately gave clarity:

  • Scheduler was still running.
  • Commands exited cleanly.
  • Positions (open + closed) were correctly selected.
  • But no new funding rows were written.

That shifted the investigation from “logic bug” to data-source mismatch.


2. Closed positions are now first-class citizens

I fixed the funding sync logic so it no longer only considers currently open positions.

Instead, positions are selected by time overlap:

  • opened_at < window_end
  • closed_at IS NULL OR closed_at > window_start

The logs now explicitly prove that closed positions are included per window, with sample IDs.

This removed a huge blind spot in historical funding accounting.


3. Proved the bug is not in Egis core logic

With detailed logs (run_id, windows, position counts, fetched rows), I could say with confidence:

  • ❌ Not a DB issue
  • ❌ Not a timezone bug
  • ❌ Not a duration / lifecycle bug
  • ❌ Not a negative-fee filter bug
  • ❌ Not deleted-bot exclusion

The system was honest — it said:

“Bots synced, positions selected, but API returned no new funding records.”

That honesty is a win.


4. Found the real problem: Binance API usage

Incremental mode revealed a critical clue:

  • Binance API does return funding fees
  • But only old ones (duplicated at 2026-02-02 08:00 UTC)
  • Range mode never fetched beyond that

This strongly pointed to:

  • Cursor (last_synced_at) still clamping requests
  • Or incorrect Binance endpoint / params / pagination

You then confirmed something important:

Binance UI clearly shows funding fees on 2026-02-05

That was the final nail — Egis was asking Binance wrong, not Binance lacking data.


5. Anchored on a known-good solution (Samir)

Instead of continuing to guess, I decided to stop reinventing.

There is already a working Binance funding fee implementation in another project (Samir).

So the plan is now clear and grounded:

  • Read that implementation line by line
  • Match endpoint, params, pagination, time units, and behavior
  • Port that logic into Egis
  • Make range mode ignore cursors when explicitly requested

This is a shift from “debugging” to aligning with a proven reference.


6. Admin UX & structure improvements (parallel work)

While deep in backend work, I also cleaned up Admin UX and navigation:

  • Grouped menus logically:
    • Trading → Positions, Fees, Signal Hooks
    • Insights → Strategy Report, Execution Quality, Average Metrics
  • Sidebar now behaves like a proper accordion:
    • One group open at a time
  • Sub-menus align cleanly and share background with main menu
  • Dashboard cards are now consistently linkable
  • Fees and money values are formatted everywhere as $0.00
  • Average Metrics page gained:
    • Proper daily averages
    • shadcn date picker
    • Presets: 7 / 30 / 90 days

All of this reinforces the same theme: calm, honest, explainable Admin UX.


What I learned

  1. “No data” is a signal, not a failureIf the system can prove it tried correctly, silence becomes actionable information.
  2. Audit commands are more valuable than fixesThe audit command paid for itself immediately. It turned uncertainty into facts.
  3. Never fight an exchange API blindBinance APIs are full of edge cases (account type, incomeType, pagination, cursors).If you already have a working implementation — reuse it.
  4. Cursors must never override explicit intentIf a user says “sync this date”, the system must obey — cursors are helpers, not masters.
  5. Correctness beats speedIt’s slower to build proof and logs, but it prevents weeks of phantom bugs later.

Status at end of day

  • Egis core logic is sound
  • Funding fee bug is isolated to Binance API integration
  • Next step is mechanical and safe: follow the Samir solution
  • Admin UI is cleaner, calmer, and more truthful than before

Today wasn’t about adding features —

it was about making the system incapable of lying.

And that’s real progress.