Commit Graph
11 Commits
Author SHA1 Message Date
jackyu66git 8ae85750b5 Add SpreadWindowTracker to measure opportunity duration
- New SpreadWindowTracker in types.go watches BG↔HL spread for all
  tracked coins, both directions
- Logs duration when spread stays above trade threshold then converges
- Wired into main loop after each scan tick
- Filters sub-100ms windows as noise
2026-05-03 20:24:36 +08:00
jackyu66git 505137bcb0 feat(web): trade detail modal with prices, fees, timestamps
- Click any trade row in history table to open detail modal
- Modal shows 6 sections: 概览, 时间, 价差, 手续费, 多仓, 空仓
- Entries and exits displayed with 6 decimal precision
- Fee entry/exit and total fee displayed
- Open/close timestamps with full date-time format
- Duration, scale count, total amount, exit reason
- Orders sub-table if available
- Escape key and overlay click to close
2026-05-03 18:59:28 +08:00
jackyu66git 02b74f1ec0 decouple display from trading: snapMu + RefreshSnapshot/ReadSnapshot
- Add snapMu RWMutex + positionsSnapshot to Trader
- RefreshSnapshot() called from main loop after Tick() — acquires
  t.mu briefly, stores deep copy under snapMu
- ReadSnapshot() returns snapshot copy under snapMu.RLock — never
  touches t.mu, zero contention with trading path
- Dashboard + handleStatus + hourly summary + status log all
  use ReadSnapshot() instead of GetPositionsCopy()
- Trading path (Tick/TryEntry/executeEntry/checkExit/checkScaleIn)
  never blocked by display reads
- Snapshot is at most 1 tick behind live state — acceptable delay
2026-05-03 18:35:47 +08:00
jackyu66git b08d8490fc fix: data race, scale-in PnL, nonce mutex, dead code, hourly check
- 🔴 Data race: Add GetPositionsCopy() returning deep copies (no shared
  ArbPosition pointers). Use it in dashboard broadcastLoop + handleStatus.
- 🟡 Scale-in PnL: Track LongEntryPrices/ShortEntryPrices on ArbPosition,
  compute weighted average (harmonic mean) at exit for accurate PnL.
- 🟢 CalcNetProfit: Delete dead code from exchange/helpers.go.
- 🟢 HL nonce: Add sync.Mutex around lastNonce++ (thread safety).
- 🟢 Hourly check: Change from 5-second window to minute window.
- 🟢 ExitPrice: Test mode closeLeg already handled by checkExit.
2026-05-03 18:31:14 +08:00
jackyu66git 931855e1f5 Cleanup: remove dead code after P3 refactor 2026-05-03 18:13:11 +08:00
jackyu66git beb3611778 Phase 3: Real-time enhancements
P3-1: Scan optimization — only BG↔HL (50+ pair combos → 2)
P3-2: Real-time spread chart — spreadHistory ring buffer +
      /api/spread-history endpoint + Chart.js spread chart
P3-3: Live position PnL — positions SSE now includes
      estimated current profit/loss + current spread
P3-4: Real-time trade events — trader.OnTradeEvent callback
      fires SSE 'trade_open' / 'trade_close' immediately
P3-5: Connection status monitoring — tracks last update time
      per exchange, broadcast via stats.connections + /api/connections

Frontend: spread chart card, PnL column in positions,
          connection status dots in stats bar,
          green/red border flash on trade events
2026-05-03 18:05:19 +08:00
jackyu66git 277c34c3bd Remove Aevo exchange (retired)
- Delete exchange/aevo.go (AevoWS, aevoTickerMsg, aevoTickerData, etc.)
- scanner.go: remove ExAevo constant, fee rates, scan pair entries, shortName mapping
- main.go: remove aevoSymbols collection loop and TrackedSymbol usage
2026-05-03 17:50:42 +08:00
jackyu66git eb74495470 Fix 8 bugs from code review
B#1 — sigCh shared across goroutines, SIGINT unreliable
  → context.WithCancel: main loop cancels ctx on SIGINT,
    4 WS goroutines select on ctx.Done() instead of shared sigCh

B#3 — restoreOpenPositions missing LastScaleAt
  → Set LastScaleAt = tr.OpenedAt on restore so scale-in cooldown works

B#4 — dYdX heartbeat goroutine leaks on reconnect
  → Added stopHeartbeat chan + heartbeatMu mutex; close old channel
    before spawning new heartbeat goroutine

B#5 — GetBitgetSize fmt.Sprintf rounds up, may exceed amountUSD
  → Added math.Floor(sz*multiplier)/multiplier before format to round
    DOWN to nearest valid step size for every coin

B#6 — netProfit and CalcNetProfit duplicate formula
  → scanner.go netProfit now delegates to exchange.CalcNetProfit

B#7 — Aevo Run callback only 2 params, incompatible with startExchange
  → Changed to 4-arg callback func(coin, price, bid, ask) with bid=ask=0

B#8 — parseFloat uses fmt.Sscanf (slow, locale-sensitive)
  → Replaced with strconv.ParseFloat

B#9 — dYdX receives hlSymbols instead of its own symbol list
  → Added dydxSymbols var, built from c.HL like other exchanges
2026-05-03 17:48:06 +08:00
jackyu66git c2489614a7 Phase 2: Web dashboard with SSE real-time push
- dashboard.go: SSE hub + HTTP server + price history ring buffer
- static.go: //go:embed for static files
- web/static/index.html: Full dashboard HTML (6 panels)
- web/static/app.js: SSE client, Chart.js price chart, live table updates
- web/static/style.css: GitHub-style dark theme
- main.go: Start dashboard on :8888 + wire price recording + scan results

Dashboard features:
  - Real-time price table (6 coins × 4 exchanges)
  - Arbitrage opportunities table
  - Open positions view
  - Historical trades table (from SQLite)
  - Chart.js price chart with coin/exchange selector
  - Stats summary (total/converged/diverged/flat)
  - 🚫 Zero external Go dependencies (Chart.js loaded from CDN)
2026-05-03 17:38:26 +08:00
jackyu66git b09314f317 Phase 1: SQLite persistence layer
- Add modernc.org/sqlite (pure Go, no CGO)
- db/ package: trades, orders, config_log tables + CRUD
- Trade persistence: every closed trade saved to SQLite
- Restart recovery: open positions restored from DB
- Automatic migration on startup
2026-05-03 17:28:08 +08:00
jackyu66git 719f0a061d Initial commit 2026-05-03 16:54:36 +08:00