Fix SpreadWindow: use netProfit() for fee accuracy, track real peak

- Replace manual spread-fee calc with netProfit() call — matches
  scanner's exact fee model (round-trip 0.07%)
- Each direction gets correct buy/sell fee pairing
- Add PeakNet field to record true max during window, not close-time value
- Log peak net with +/- sign instead of approx symbol
This commit is contained in:
jackyu66git
2026-05-03 20:28:27 +08:00
parent 8ae85750b5
commit 5cfb6afa55
+20 -12
View File
@@ -128,6 +128,7 @@ type SpreadWindow struct {
Coin string Coin string
Direction string // "BG->HL" or "HL->BG" Direction string // "BG->HL" or "HL->BG"
Since time.Time Since time.Time
PeakNet float64 // highest netProfit % observed during this window
} }
type SpreadWindowTracker struct { type SpreadWindowTracker struct {
@@ -151,18 +152,22 @@ func (swt *SpreadWindowTracker) Tick(snap map[string]map[string]float64, thresho
continue continue
} }
// Check both directions // Check both directions — use netProfit() for exact fee model match
for _, dir := range []struct { // BG→HL: buy BG (Bitget 0.020%), sell HL (HL 0.015%)
name string // HL→BG: buy HL (HL 0.015%), sell BG (Bitget 0.020%)
low float64 type dirCheck struct {
high float64 name string
}{ buyPrice float64
{"BG->HL", bgP, hlP}, sellPrice float64
{"HL->BG", hlP, bgP}, buyFee float64
sellFee float64
}
for _, dir := range []dirCheck{
{"BG->HL", bgP, hlP, makerFees[ExBitget], makerFees[ExHyperLiquid]},
{"HL->BG", hlP, bgP, makerFees[ExHyperLiquid], makerFees[ExBitget]},
} { } {
key := coin.Name + ":" + dir.name key := coin.Name + ":" + dir.name
spread := (dir.high - dir.low) / dir.low * 100 netSpr := netProfit(dir.buyPrice, dir.sellPrice, dir.buyFee, dir.sellFee)
netSpr := spread - (makerFees[ExBitget] + makerFees[ExHyperLiquid]) // rough net
w, exists := swt.windows[key] w, exists := swt.windows[key]
if netSpr >= threshold { if netSpr >= threshold {
@@ -171,14 +176,17 @@ func (swt *SpreadWindowTracker) Tick(snap map[string]map[string]float64, thresho
Coin: coin.Name, Coin: coin.Name,
Direction: dir.name, Direction: dir.name,
Since: now, Since: now,
PeakNet: netSpr,
} }
} else if netSpr > w.PeakNet {
w.PeakNet = netSpr
} }
} else { } else {
if exists { if exists {
dur := now.Sub(w.Since) dur := now.Sub(w.Since)
if dur > 100*time.Millisecond { if dur > 100*time.Millisecond {
log.Printf("[SpreadWindow] %s %s exceeded threshold for %v (peak net≈%.4f%%)", log.Printf("[SpreadWindow] %s %s exceeded threshold for %v (peak net=%+.4f%%)",
w.Coin, w.Direction, dur.Round(time.Millisecond), netSpr) w.Coin, w.Direction, dur.Round(time.Millisecond), w.PeakNet)
} }
delete(swt.windows, key) delete(swt.windows, key)
} }