mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 01:48:22 +08:00
799d8b9c2e
- Convert all time.Time fields to int64 Unix milliseconds (UTC) - Add PostgreSQL migration to convert timestamp columns to bigint - Reduce Binance sync window from 7 days to 24 hours - Fix dashboard trader name visibility (add nofx-text-main color) - Add position value column to history table - Remove hardcoded API keys from test files
104 lines
3.1 KiB
Go
104 lines
3.1 KiB
Go
package trader
|
|
|
|
import (
|
|
"fmt"
|
|
"nofx/logger"
|
|
"nofx/market"
|
|
"nofx/store"
|
|
"time"
|
|
)
|
|
|
|
// CreatePositionSnapshot gets current real positions from exchange and creates snapshot positions
|
|
// This function will:
|
|
// 1. Delete all OPEN old positions from database
|
|
// 2. Get current real positions from exchange
|
|
// 3. Create a "snapshot" record for each real position
|
|
func CreatePositionSnapshot(traderID, exchangeID, exchangeType string, trader Trader, st *store.Store) error {
|
|
logger.Infof("📸 Creating position snapshot for trader %s (%s)...", traderID, exchangeType)
|
|
|
|
positionStore := st.Position()
|
|
|
|
// Step 1: Delete all OPEN positions
|
|
logger.Infof("🗑️ Deleting all OPEN positions from database...")
|
|
if err := positionStore.DeleteAllOpenPositions(traderID); err != nil {
|
|
return fmt.Errorf("failed to delete open positions: %w", err)
|
|
}
|
|
logger.Infof("✅ Deleted all OPEN positions")
|
|
|
|
// Step 2: Get current positions from exchange
|
|
logger.Infof("📡 Fetching current positions from exchange...")
|
|
positions, err := trader.GetPositions()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get positions from exchange: %w", err)
|
|
}
|
|
|
|
if len(positions) == 0 {
|
|
logger.Infof("✅ No open positions on exchange, snapshot complete")
|
|
return nil
|
|
}
|
|
|
|
logger.Infof("📥 Found %d positions on exchange", len(positions))
|
|
|
|
// Step 3: Create snapshot record for each position
|
|
nowMs := time.Now().UnixMilli()
|
|
createdCount := 0
|
|
|
|
for _, posMap := range positions {
|
|
// Parse position data
|
|
rawSymbol, _ := posMap["symbol"].(string)
|
|
symbol := market.Normalize(rawSymbol)
|
|
sideStr, _ := posMap["side"].(string)
|
|
positionAmt, _ := posMap["positionAmt"].(float64)
|
|
entryPrice, _ := posMap["entryPrice"].(float64)
|
|
markPrice, _ := posMap["markPrice"].(float64)
|
|
leverage, _ := posMap["leverage"].(float64)
|
|
|
|
// Skip positions with 0 quantity
|
|
if positionAmt == 0 {
|
|
continue
|
|
}
|
|
|
|
// Determine position side
|
|
side := "LONG"
|
|
if sideStr == "short" {
|
|
side = "SHORT"
|
|
}
|
|
|
|
// Use current mark price as entry price (approximation)
|
|
// If entryPrice is 0, use markPrice
|
|
if entryPrice == 0 {
|
|
entryPrice = markPrice
|
|
}
|
|
|
|
snapshotPosition := &store.TraderPosition{
|
|
TraderID: traderID,
|
|
ExchangeID: exchangeID,
|
|
ExchangeType: exchangeType,
|
|
ExchangePositionID: fmt.Sprintf("snapshot_%s_%s_%d", symbol, side, nowMs),
|
|
Symbol: symbol,
|
|
Side: side,
|
|
Quantity: positionAmt,
|
|
EntryPrice: entryPrice,
|
|
EntryOrderID: "snapshot", // Mark as snapshot
|
|
EntryTime: nowMs,
|
|
Leverage: int(leverage),
|
|
Status: "OPEN",
|
|
Source: "snapshot", // Mark source as snapshot
|
|
CreatedAt: nowMs,
|
|
UpdatedAt: nowMs,
|
|
}
|
|
|
|
if err := positionStore.CreateOpenPosition(snapshotPosition); err != nil {
|
|
logger.Infof(" ⚠️ Failed to create snapshot position for %s %s: %v", symbol, side, err)
|
|
continue
|
|
}
|
|
|
|
logger.Infof(" ✅ Created snapshot: %s %s %.6f @ %.2f (leverage: %dx)",
|
|
symbol, side, positionAmt, entryPrice, int(leverage))
|
|
createdCount++
|
|
}
|
|
|
|
logger.Infof("✅ Position snapshot complete: %d positions created", createdCount)
|
|
return nil
|
|
}
|