fix: 重启后加仓数据恢复 + scale_count/amount_usd 实时持久化

- restoreOpenPositions 从 orders 表 count 加仓次数,正确设置 ScaleLevels 和 AmountUSD
- 加仓时立即持久化 amount_usd 和 scale_count 到 trades 表(不等到平仓)
- PnL 图表支持单笔交易显示(原点+累计PnL连线)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jackyu66git
2026-05-05 03:19:26 +08:00
co-authored by Claude Opus 4.7
parent f97ac16b1c
commit 2f4d7869a9
3 changed files with 32 additions and 10 deletions
+20 -9
View File
@@ -3,6 +3,7 @@ package main
import (
"fmt"
"log"
"strconv"
"strings"
"sync"
"time"
@@ -939,6 +940,8 @@ func (t *Trader) checkScaleIn(pos *ArbPosition, bgP, hlP, diffPct float64, store
LongOrderID: &longOID, ShortOrderID: &shortOID,
CreatedAt: now,
})
// Persist updated amount_usd and scale_count immediately
t.db.UpdateTradeScale(pos.DBTradeID, pos.AmountUSD, pos.ScaleLevels)
}
log.Printf("[Trader] %s: Scale-in #%d executed | spread=%.4f%% (entry=%.4f%%) | total=$%.0f",
@@ -1274,11 +1277,12 @@ func (t *Trader) placeOrder(leg *PositionLeg, side string, store *PriceStore) (s
leg.OrderID = resp
log.Printf("[ExRes] HL %s %s: size=%s", side, leg.Coin, szStr)
// Parse actual fill price and estimate fee from HL response
fillPrice, _, parseErr := t.hyperliquid.ParseFillFromResponse(resp)
// Parse actual fill price, OID, and estimate fee from HL response
fillPrice, _, oid, parseErr := t.hyperliquid.ParseFillFromResponse(resp)
if parseErr == nil && fillPrice > 0 {
leg.EntryPrice = fillPrice
log.Printf("[Fill] HL %s %s: actual fillPrice=%.6f", side, leg.Coin, fillPrice)
leg.OrderID = strconv.FormatInt(oid, 10)
log.Printf("[Fill] HL %s %s: actual fillPrice=%.6f oid=%d", side, leg.Coin, fillPrice, oid)
}
fee, fetchErr := t.hyperliquid.EstimateFeeFromResponse(resp, takerFees[ExHyperLiquid])
if fetchErr != nil {
@@ -1531,11 +1535,12 @@ func (t *Trader) closeLeg(leg *PositionLeg) string {
log.Printf("[ExRes] HL close %s %s: size=%s → response=%s", side, leg.Coin, leg.Size, resp)
leg.OrderID = resp
// Parse actual fill price from HL close response
fillPrice, _, parseErr := t.hyperliquid.ParseFillFromResponse(resp)
// Parse actual fill price and OID from HL close response
fillPrice, _, oid, parseErr := t.hyperliquid.ParseFillFromResponse(resp)
if parseErr == nil && fillPrice > 0 {
leg.ExitPrice = fillPrice
log.Printf("[Fill] HL close %s: actual exitPrice=%.6f", leg.Coin, fillPrice)
leg.OrderID = strconv.FormatInt(oid, 10)
log.Printf("[Fill] HL close %s: actual exitPrice=%.6f oid=%d", leg.Coin, fillPrice, oid)
}
}
leg.Closed = true
@@ -1754,11 +1759,12 @@ func (t *Trader) placeOrderAt(leg *PositionLeg, side string, store *PriceStore,
}
leg.OrderID = oid
// Parse actual fill price from HL response
fillPrice, _, parseErr := t.hyperliquid.ParseFillFromResponse(oid)
// Parse actual fill price and OID from HL response
fillPrice, _, oidNum, parseErr := t.hyperliquid.ParseFillFromResponse(oid)
if parseErr == nil && fillPrice > 0 {
leg.EntryPrice = fillPrice
log.Printf("[Fill] HL scale %s %s: actual fillPrice=%.6f", side, leg.Coin, fillPrice)
leg.OrderID = strconv.FormatInt(oidNum, 10)
log.Printf("[Fill] HL scale %s %s: actual fillPrice=%.6f oid=%d", side, leg.Coin, fillPrice, oidNum)
}
// Estimate fee from HL response
@@ -1957,6 +1963,11 @@ func (t *Trader) restoreOpenPositions() {
if err == nil {
pos.LongEntryPrices = append(pos.LongEntryPrices, scaleLong...)
pos.ShortEntryPrices = append(pos.ShortEntryPrices, scaleShort...)
// Restore ScaleLevels from actual scale order count
if len(scaleLong) > 0 {
pos.ScaleLevels = len(scaleLong)
pos.AmountUSD = tr.AmountUSD * (1 + float64(pos.ScaleLevels))
}
// Refresh leg EntryPrice to reflect all scale levels
if len(pos.LongEntryPrices) > 1 {
pos.LongLeg.EntryPrice = weightedAvgPrice(pos.LongEntryPrices, t.cfg.TradeAmountUSD)