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
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -119,3 +120,69 @@ type ArbOpportunity struct {
|
||||
NetProfit float64 // percentage after fees
|
||||
GrossBasis float64 // raw price difference %
|
||||
}
|
||||
|
||||
// SpreadWindow tracks how long each coin's spread stays above threshold.
|
||||
// Used to measure the window of opportunity between threshold-crossing and
|
||||
// convergence — helps diagnose whether entry latency is a problem.
|
||||
type SpreadWindow struct {
|
||||
Coin string
|
||||
Direction string // "BG->HL" or "HL->BG"
|
||||
Since time.Time
|
||||
}
|
||||
|
||||
type SpreadWindowTracker struct {
|
||||
windows map[string]*SpreadWindow // key: "COIN:DIRECTION"
|
||||
}
|
||||
|
||||
func NewSpreadWindowTracker() *SpreadWindowTracker {
|
||||
return &SpreadWindowTracker{windows: make(map[string]*SpreadWindow)}
|
||||
}
|
||||
|
||||
func (swt *SpreadWindowTracker) Tick(snap map[string]map[string]float64, threshold float64) {
|
||||
now := time.Now()
|
||||
for _, coin := range TrackedCoins {
|
||||
exMap := snap[coin.Name]
|
||||
if exMap == nil {
|
||||
continue
|
||||
}
|
||||
bgP := exMap[ExBitget]
|
||||
hlP := exMap[ExHyperLiquid]
|
||||
if bgP <= 0 || hlP <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check both directions
|
||||
for _, dir := range []struct {
|
||||
name string
|
||||
low float64
|
||||
high float64
|
||||
}{
|
||||
{"BG->HL", bgP, hlP},
|
||||
{"HL->BG", hlP, bgP},
|
||||
} {
|
||||
key := coin.Name + ":" + dir.name
|
||||
spread := (dir.high - dir.low) / dir.low * 100
|
||||
netSpr := spread - (makerFees[ExBitget] + makerFees[ExHyperLiquid]) // rough net
|
||||
|
||||
w, exists := swt.windows[key]
|
||||
if netSpr >= threshold {
|
||||
if !exists {
|
||||
swt.windows[key] = &SpreadWindow{
|
||||
Coin: coin.Name,
|
||||
Direction: dir.name,
|
||||
Since: now,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if exists {
|
||||
dur := now.Sub(w.Since)
|
||||
if dur > 100*time.Millisecond {
|
||||
log.Printf("[SpreadWindow] %s %s exceeded threshold for %v (peak net≈%.4f%%)",
|
||||
w.Coin, w.Direction, dur.Round(time.Millisecond), netSpr)
|
||||
}
|
||||
delete(swt.windows, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user