mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
cb31782be4
- 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)
116 lines
3.3 KiB
Go
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()
|
|
}
|