Files
nofx/trader/aster/trader_positions.go
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

122 lines
3.7 KiB
Go

package aster
import (
"encoding/json"
"fmt"
"nofx/logger"
"strconv"
"strings"
)
// GetPositions Get position information
func (t *AsterTrader) GetPositions() ([]map[string]interface{}, error) {
params := make(map[string]interface{})
body, err := t.request("GET", "/fapi/v3/positionRisk", params)
if err != nil {
return nil, err
}
var positions []map[string]interface{}
if err := json.Unmarshal(body, &positions); err != nil {
return nil, err
}
result := []map[string]interface{}{}
for _, pos := range positions {
posAmtStr, ok := pos["positionAmt"].(string)
if !ok {
continue
}
posAmt, _ := strconv.ParseFloat(posAmtStr, 64)
if posAmt == 0 {
continue // Skip empty positions
}
entryPrice, _ := strconv.ParseFloat(pos["entryPrice"].(string), 64)
markPrice, _ := strconv.ParseFloat(pos["markPrice"].(string), 64)
unRealizedProfit, _ := strconv.ParseFloat(pos["unRealizedProfit"].(string), 64)
leverageVal, _ := strconv.ParseFloat(pos["leverage"].(string), 64)
liquidationPrice, _ := strconv.ParseFloat(pos["liquidationPrice"].(string), 64)
// Determine direction (consistent with Binance)
side := "long"
if posAmt < 0 {
side = "short"
posAmt = -posAmt
}
// Return same field names as Binance
result = append(result, map[string]interface{}{
"symbol": pos["symbol"],
"side": side,
"positionAmt": posAmt,
"entryPrice": entryPrice,
"markPrice": markPrice,
"unRealizedProfit": unRealizedProfit,
"leverage": leverageVal,
"liquidationPrice": liquidationPrice,
})
}
return result, nil
}
// SetMarginMode Set margin mode
func (t *AsterTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
// Aster supports margin mode settings
// API format similar to Binance: CROSSED (cross margin) / ISOLATED (isolated margin)
marginType := "CROSSED"
if !isCrossMargin {
marginType = "ISOLATED"
}
params := map[string]interface{}{
"symbol": symbol,
"marginType": marginType,
}
// Use request method to call API
_, err := t.request("POST", "/fapi/v3/marginType", params)
if err != nil {
// Ignore error if it indicates no need to change
if strings.Contains(err.Error(), "No need to change") ||
strings.Contains(err.Error(), "Margin type cannot be changed") {
logger.Infof(" ✓ %s margin mode is already %s or cannot be changed due to existing positions", symbol, marginType)
return nil
}
// Detect multi-assets mode (error code -4168)
if strings.Contains(err.Error(), "Multi-Assets mode") ||
strings.Contains(err.Error(), "-4168") ||
strings.Contains(err.Error(), "4168") {
logger.Infof(" ⚠️ %s detected multi-assets mode, forcing cross margin mode", symbol)
logger.Infof(" 💡 Tip: To use isolated margin mode, please disable multi-assets mode on the exchange")
return nil
}
// Detect unified account API
if strings.Contains(err.Error(), "unified") ||
strings.Contains(err.Error(), "portfolio") ||
strings.Contains(err.Error(), "Portfolio") {
logger.Infof(" ❌ %s detected unified account API, cannot perform futures trading", symbol)
return fmt.Errorf("please use 'Spot & Futures Trading' API permission, not 'Unified Account API'")
}
logger.Infof(" ⚠️ Failed to set margin mode: %v", err)
// Don't return error, let trading continue
return nil
}
logger.Infof(" ✓ %s margin mode has been set to %s", symbol, marginType)
return nil
}
// SetLeverage Set leverage multiplier
func (t *AsterTrader) SetLeverage(symbol string, leverage int) error {
params := map[string]interface{}{
"symbol": symbol,
"leverage": leverage,
}
_, err := t.request("POST", "/fapi/v3/leverage", params)
return err
}