mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
Revert "fix(sync): handle close trades without matching open position"
This reverts commit 9c57134dfb.
This commit is contained in:
@@ -253,33 +253,6 @@ func (s *PositionStore) ClosePositionFully(id int64, exitPrice float64, exitOrde
|
||||
}).Error
|
||||
}
|
||||
|
||||
// GetSyntheticClosedPosition gets an existing synthetic CLOSED position (close_reason='sync_partial')
|
||||
// Used when merging multiple close trades that have no matching open position
|
||||
func (s *PositionStore) GetSyntheticClosedPosition(traderID, symbol, side string) (*TraderPosition, error) {
|
||||
var pos TraderPosition
|
||||
err := s.db.Where("trader_id = ? AND symbol = ? AND side = ? AND status = ? AND close_reason = ?",
|
||||
traderID, symbol, side, "CLOSED", "sync_partial").
|
||||
Order("exit_time DESC").
|
||||
First(&pos).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pos, nil
|
||||
}
|
||||
|
||||
// UpdateSyntheticPosition updates a synthetic CLOSED position with additional close trade data
|
||||
func (s *PositionStore) UpdateSyntheticPosition(id int64, entryQty, exitPrice, realizedPnL, fee float64, exitTimeMs int64) error {
|
||||
nowMs := time.Now().UTC().UnixMilli()
|
||||
return s.db.Model(&TraderPosition{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"entry_quantity": entryQty,
|
||||
"exit_price": exitPrice,
|
||||
"realized_pnl": realizedPnL,
|
||||
"fee": fee,
|
||||
"exit_time": exitTimeMs,
|
||||
"updated_at": nowMs,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// DeleteAllOpenPositions deletes all OPEN positions for a trader
|
||||
func (s *PositionStore) DeleteAllOpenPositions(traderID string) error {
|
||||
return s.db.Where("trader_id = ? AND status = ?", traderID, "OPEN").Delete(&TraderPosition{}).Error
|
||||
|
||||
@@ -107,58 +107,9 @@ func (pb *PositionBuilder) handleClose(
|
||||
}
|
||||
|
||||
if position == nil {
|
||||
// No OPEN position found - check for existing synthetic CLOSED position to merge into
|
||||
// This can happen when the position was opened before the sync window (>24h ago)
|
||||
// but closed during the sync window. Multiple close trades should merge together.
|
||||
existingSynthetic, _ := pb.positionStore.GetSyntheticClosedPosition(traderID, symbol, side)
|
||||
|
||||
nowMs := time.Now().UTC().UnixMilli()
|
||||
if existingSynthetic != nil {
|
||||
// Merge into existing synthetic position
|
||||
newEntryQty := existingSynthetic.EntryQuantity + quantity
|
||||
// Calculate weighted average exit price
|
||||
newExitPrice := (existingSynthetic.ExitPrice*existingSynthetic.EntryQuantity + price*quantity) / newEntryQty
|
||||
newExitPrice = math.Round(newExitPrice*100) / 100
|
||||
newPnL := existingSynthetic.RealizedPnL + realizedPnL
|
||||
newFee := existingSynthetic.Fee + fee
|
||||
|
||||
logger.Infof(" 📊 Merging into synthetic position: %s %s +%.4f @ %.2f (total: %.4f @ %.2f, pnl: %.2f)",
|
||||
symbol, side, quantity, price, newEntryQty, newExitPrice, newPnL)
|
||||
|
||||
return pb.positionStore.UpdateSyntheticPosition(existingSynthetic.ID, newEntryQty, newExitPrice, newPnL, newFee, tradeTimeMs)
|
||||
}
|
||||
|
||||
// Create new synthetic CLOSED position
|
||||
logger.Infof(" ⚠️ No matching open position for %s %s, creating synthetic CLOSED position", symbol, side)
|
||||
syntheticPosition := &TraderPosition{
|
||||
TraderID: traderID,
|
||||
ExchangeID: exchangeID,
|
||||
ExchangeType: exchangeType,
|
||||
ExchangePositionID: fmt.Sprintf("sync_closed_%s_%s_%d", symbol, side, tradeTimeMs),
|
||||
Symbol: symbol,
|
||||
Side: side,
|
||||
Quantity: 0, // Already closed
|
||||
EntryQuantity: quantity,
|
||||
EntryPrice: 0, // Unknown - opened before sync window
|
||||
EntryOrderID: "", // Unknown
|
||||
EntryTime: 0, // Unknown
|
||||
ExitPrice: price, // We know the exit price
|
||||
ExitOrderID: orderID,
|
||||
ExitTime: tradeTimeMs,
|
||||
RealizedPnL: realizedPnL,
|
||||
Fee: fee,
|
||||
Leverage: 1,
|
||||
Status: "CLOSED",
|
||||
CloseReason: "sync_partial", // Mark as partial data
|
||||
Source: "sync",
|
||||
CreatedAt: nowMs,
|
||||
UpdatedAt: nowMs,
|
||||
}
|
||||
if err := pb.positionStore.Create(syntheticPosition); err != nil {
|
||||
return fmt.Errorf("failed to create synthetic closed position: %w", err)
|
||||
}
|
||||
logger.Infof(" ✅ Created synthetic CLOSED position: %s %s qty=%.4f exit=%.2f pnl=%.2f",
|
||||
symbol, side, quantity, price, realizedPnL)
|
||||
// No open position found - just skip
|
||||
// This can happen if trades are processed out of order or database was cleared
|
||||
logger.Infof(" ⚠️ No matching open position for %s %s (orderID: %s), skipping", symbol, side, orderID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user