Files
nofx/trader/kucoin/trader_positions.go
T
tinkle-community cb31782be4 refactor: split large files and clean up project structure
- Rename experience/ to telemetry/ for clarity
- Split 15+ large Go files (800-2200 lines) into focused modules:
  kernel/engine.go, backtest/runner.go, market/data.go, store/position.go,
  api/handler_trader.go, trader/auto_trader_grid.go, and 9 exchange traders
- Split frontend monoliths: types.ts, api.ts, AITradersPage.tsx, BacktestPage.tsx
  into domain-specific modules with barrel re-exports
- Remove stale files: screenshots, .yml.old, pyproject.toml
- Remove unused scripts/ and cmd/ directories
- Remove broken/outdated test files (network-dependent, stale expectations)
2026-03-12 12:53:57 +08:00

116 lines
3.3 KiB
Go

package kucoin
import (
"encoding/json"
"fmt"
"time"
)
// GetPositions gets all positions
func (t *KuCoinTrader) GetPositions() ([]map[string]interface{}, error) {
// Check cache
t.positionsCacheMutex.RLock()
if t.cachedPositions != nil && time.Since(t.positionsCacheTime) < t.cacheDuration {
t.positionsCacheMutex.RUnlock()
return t.cachedPositions, nil
}
t.positionsCacheMutex.RUnlock()
data, err := t.doRequest("GET", kucoinPositionPath, nil)
if err != nil {
return nil, fmt.Errorf("failed to get positions: %w", err)
}
var positions []struct {
Symbol string `json:"symbol"`
CurrentQty int64 `json:"currentQty"` // Position quantity (in lots, integer)
AvgEntryPrice float64 `json:"avgEntryPrice"` // Average entry price
MarkPrice float64 `json:"markPrice"` // Mark price
UnrealisedPnl float64 `json:"unrealisedPnl"` // Unrealized PnL
Leverage float64 `json:"leverage"` // Leverage setting
RealLeverage float64 `json:"realLeverage"` // Effective leverage (may be nil in cross mode)
LiquidationPrice float64 `json:"liquidationPrice"`// Liquidation price
Multiplier float64 `json:"multiplier"` // Contract multiplier
IsOpen bool `json:"isOpen"`
CrossMode bool `json:"crossMode"`
OpeningTimestamp int64 `json:"openingTimestamp"`
SettleCurrency string `json:"settleCurrency"`
}
if err := json.Unmarshal(data, &positions); err != nil {
return nil, fmt.Errorf("failed to parse position data: %w", err)
}
var result []map[string]interface{}
for _, pos := range positions {
if !pos.IsOpen || pos.CurrentQty == 0 {
continue
}
// Convert symbol format
symbol := t.convertSymbolBack(pos.Symbol)
// Determine side based on position quantity
// KuCoin: positive qty = long, negative qty = short
side := "long"
qty := pos.CurrentQty
if qty < 0 {
side = "short"
qty = -qty
}
// Convert lots to actual quantity using multiplier
// Position quantity = lots * multiplier
multiplier := pos.Multiplier
if multiplier == 0 {
multiplier = 0.001 // Default for BTC
}
positionAmt := float64(qty) * multiplier
// Determine margin mode
mgnMode := "isolated"
if pos.CrossMode {
mgnMode = "cross"
}
// Use Leverage field (setting), fallback to RealLeverage (effective), default to 10
leverage := pos.Leverage
if leverage == 0 {
leverage = pos.RealLeverage
}
if leverage == 0 {
leverage = 10 // Default leverage
}
posMap := map[string]interface{}{
"symbol": symbol,
"positionAmt": positionAmt,
"entryPrice": pos.AvgEntryPrice,
"markPrice": pos.MarkPrice,
"unRealizedProfit": pos.UnrealisedPnl,
"leverage": leverage,
"liquidationPrice": pos.LiquidationPrice,
"side": side,
"mgnMode": mgnMode,
"createdTime": pos.OpeningTimestamp,
}
result = append(result, posMap)
}
// Update cache
t.positionsCacheMutex.Lock()
t.cachedPositions = result
t.positionsCacheTime = time.Now()
t.positionsCacheMutex.Unlock()
return result, nil
}
// InvalidatePositionCache clears the position cache
func (t *KuCoinTrader) InvalidatePositionCache() {
t.positionsCacheMutex.Lock()
t.cachedPositions = nil
t.positionsCacheTime = time.Time{}
t.positionsCacheMutex.Unlock()
}