Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions store/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,12 @@ func (s *PositionStore) GetOpenPositions(traderID string) ([]*TraderPosition, er
// GetOpenPositionBySymbol gets open position for specified symbol and direction
func (s *PositionStore) GetOpenPositionBySymbol(traderID, symbol, side string) (*TraderPosition, error) {
var pos TraderPosition
err := s.db.Where("trader_id = ? AND symbol = ? AND side = ? AND status = ?", traderID, symbol, side, "OPEN").

// Build side condition: match exact side OR "BOTH" (one-way mode positions)
// "BOTH" is used in one-way position mode where positions don't have a specific side
sideCondition := "(side = ? OR side = 'BOTH')"

err := s.db.Where("trader_id = ? AND symbol = ? AND "+sideCondition+" AND status = ?", traderID, symbol, side, "OPEN").
Order("entry_time DESC").
First(&pos).Error

Expand All @@ -295,7 +300,7 @@ func (s *PositionStore) GetOpenPositionBySymbol(traderID, symbol, side string) (
// Try without USDT suffix for backward compatibility
if strings.HasSuffix(symbol, "USDT") {
baseSymbol := strings.TrimSuffix(symbol, "USDT")
err = s.db.Where("trader_id = ? AND symbol = ? AND side = ? AND status = ?", traderID, baseSymbol, side, "OPEN").
err = s.db.Where("trader_id = ? AND symbol = ? AND "+sideCondition+" AND status = ?", traderID, baseSymbol, side, "OPEN").
Order("entry_time DESC").
First(&pos).Error
if err == nil {
Expand Down
24 changes: 19 additions & 5 deletions trader/bitget/order_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,32 @@ func (t *BitgetTrader) GetTrades(startTime time.Time, limit int) ([]BitgetTrade,
feeAsset = fill.FeeDetail[0].FeeCoin
}

// Determine order action based on side and tradeSide
// Bitget one-way mode: buy_single (open long), sell_single (close long)
// Bitget hedge mode: open + buy = open_long, close + sell = close_long
// Determine order action based on side, tradeSide, and profit
// Key insight: profit != 0 means it's a closing trade (realized PnL)
// profit == 0 means it's an opening trade (no PnL yet)
//
// Bitget one-way mode: buy_single/sell_single
// Bitget hedge mode: open/close + buy/sell
orderAction := "open_long"
side := strings.ToLower(fill.Side)
tradeSide := strings.ToLower(fill.TradeSide)
hasProfit := profit != 0 // Non-zero profit indicates closing trade

// One-way position mode (buy_single/sell_single)
if tradeSide == "buy_single" {
orderAction = "open_long"
// buy_single: could be open_long (no profit) or close_short (has profit)
if hasProfit {
orderAction = "close_short"
} else {
orderAction = "open_long"
}
} else if tradeSide == "sell_single" {
orderAction = "close_long"
// sell_single: could be open_short (no profit) or close_long (has profit)
if hasProfit {
orderAction = "close_long"
} else {
orderAction = "open_short"
}
} else if tradeSide == "open" {
// Hedge mode: open
if side == "buy" {
Expand Down