Fix entry latency: synchronous execution with scan-time prices
- Remove goroutine in TryEntry (executeEntry now synchronous) - Use opp.BuyPrice/SellPrice directly instead of re-reading from store - Keep lightweight direction sanity check (0.1% tolerance) - executeEntry returns bool for call chain consistency
This commit is contained in:
@@ -258,39 +258,37 @@ func (t *Trader) TryEntry(opp *ArbOpportunity, store *PriceStore, notifier *Noti
|
|||||||
}
|
}
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
|
||||||
go t.executeEntry(opp, store, notifier)
|
return t.executeEntry(opp, store, notifier)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trader) executeEntry(opp *ArbOpportunity, store *PriceStore, notifier *Notifier) {
|
// executeEntry places both legs using the scan-time prices from ArbOpportunity.
|
||||||
|
// Synchronous — runs in the scanner tick to avoid WS price movement between
|
||||||
|
// detection and execution.
|
||||||
|
func (t *Trader) executeEntry(opp *ArbOpportunity, store *PriceStore, notifier *Notifier) bool {
|
||||||
|
// Use scan-time prices directly to avoid WS jitter killing the entry
|
||||||
|
bgP, hlP := opp.BuyPrice, opp.SellPrice
|
||||||
|
if opp.BuyEx == ExHyperLiquid {
|
||||||
|
bgP, hlP = opp.SellPrice, opp.BuyPrice
|
||||||
|
}
|
||||||
|
if bgP <= 0 || hlP <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick sanity check: spread direction hasn't completely reversed
|
||||||
|
// Use a relaxed check (not full re-read) since WS prices move constantly
|
||||||
snap := store.GetAll()
|
snap := store.GetAll()
|
||||||
exMap := snap[opp.Coin]
|
exMap := snap[opp.Coin]
|
||||||
if exMap == nil {
|
if exMap != nil {
|
||||||
return
|
currBg := exMap[ExBitget]
|
||||||
|
currHl := exMap[ExHyperLiquid]
|
||||||
|
if currBg > 0 && currHl > 0 {
|
||||||
|
if opp.BuyEx == ExBitget && currHl <= currBg*0.999 {
|
||||||
|
return false // reversed beyond small tolerance
|
||||||
}
|
}
|
||||||
bgP := exMap[ExBitget]
|
if opp.BuyEx == ExHyperLiquid && currBg <= currHl*0.999 {
|
||||||
hlP := exMap[ExHyperLiquid]
|
return false
|
||||||
if bgP <= 0 || hlP <= 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue #1: reProfit must use actual direction — CalcNetProfit auto-swaps on reversal!
|
|
||||||
var reProfit float64
|
|
||||||
if opp.BuyEx == ExBitget {
|
|
||||||
reProfit = netProfit(bgP, hlP, makerFees[ExBitget], makerFees[ExHyperLiquid])
|
|
||||||
} else {
|
|
||||||
reProfit = netProfit(hlP, bgP, makerFees[ExHyperLiquid], makerFees[ExBitget])
|
|
||||||
}
|
}
|
||||||
if reProfit < t.cfg.TradeThreshold {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue #1: Verify spread direction hasn't flipped since scan
|
|
||||||
if opp.BuyEx == ExBitget && hlP <= bgP {
|
|
||||||
return // reversed: HL no longer more expensive than BG
|
|
||||||
}
|
|
||||||
if opp.BuyEx == ExHyperLiquid && bgP <= hlP {
|
|
||||||
return // reversed: BG no longer more expensive than HL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := &ArbPosition{
|
pos := &ArbPosition{
|
||||||
@@ -337,13 +335,13 @@ func (t *Trader) executeEntry(opp *ArbOpportunity, store *PriceStore, notifier *
|
|||||||
// Execute both legs
|
// Execute both legs
|
||||||
if err := t.placeOrder(pos.LongLeg, "buy", store); err != "" {
|
if err := t.placeOrder(pos.LongLeg, "buy", store); err != "" {
|
||||||
t.cleanup(pos.Coin)
|
t.cleanup(pos.Coin)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
time.Sleep(300 * time.Millisecond)
|
time.Sleep(300 * time.Millisecond)
|
||||||
if err := t.placeOrder(pos.ShortLeg, "sell", store); err != "" {
|
if err := t.placeOrder(pos.ShortLeg, "sell", store); err != "" {
|
||||||
t.closeLeg(pos.LongLeg)
|
t.closeLeg(pos.LongLeg)
|
||||||
t.cleanup(pos.Coin)
|
t.cleanup(pos.Coin)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
pos.LastScaleAt = time.Now()
|
pos.LastScaleAt = time.Now()
|
||||||
@@ -374,6 +372,7 @@ func (t *Trader) executeEntry(opp *ArbOpportunity, store *PriceStore, notifier *
|
|||||||
"time": time.Now().Format("15:04:05"),
|
"time": time.Now().Format("15:04:05"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkScaleIn adds more position when spread widens further.
|
// checkScaleIn adds more position when spread widens further.
|
||||||
|
|||||||
Reference in New Issue
Block a user