This commit is contained in:
2026-01-09 15:48:31 +08:00
parent e32eabbf95
commit c8e189e9c7
28 changed files with 2795 additions and 0 deletions

176
trade/binance_positions.go Normal file
View File

@@ -0,0 +1,176 @@
package trade
import (
"context"
"fmt"
"log"
"math"
"strings"
"git.apinb.com/bsm-sdk/core/utils"
"git.apinb.com/quant/strategy/internal/impl"
"git.apinb.com/quant/strategy/internal/models"
"github.com/vmihailenco/msgpack/v5"
)
var (
PositionsTotal int = 0
)
func GetPosSummary(PlanKeyName string) []string {
cacheBytes, err := impl.RedisService.Client.Get(impl.RedisService.Ctx, PlanKeyName+".PosSummary").Bytes()
if err != nil {
return nil
}
data := strings.Split(string(cacheBytes), ",")
// log.Println("GetPosSummary", PlanKeyName+".PosSummary", data)
if len(data) == 0 {
return nil
}
return data
}
func GetPosSymbols(PlanKeyName string) []string {
cacheBytes, err := impl.RedisService.Client.Get(impl.RedisService.Ctx, PlanKeyName+".PosSummary").Bytes()
if err != nil {
return nil
}
data := strings.Split(string(cacheBytes), ",")
if len(data) == 0 {
return nil
}
symbols := make([]string, 0)
for _, row := range data {
symbols = append(symbols, strings.Split(row, ".")[0])
}
return utils.ArrayRemoveRepeatString(symbols)
}
func ExistsPosition(PlanKeyName string, symbol string) PositionStatus {
data := GetPosSummary(PlanKeyName)
if len(data) == 0 {
return NoPositions
}
LongExists := utils.ArrayInString(symbol+".LONG", data)
ShortExists := utils.ArrayInString(symbol+".SHORT", data)
if LongExists && ShortExists {
return BothPositions
}
//log.Println("ExistsPosition", symbol, LongExists, ShortExists)
return GetPositionStats(LongExists, ShortExists)
}
func GetPositions(PlanKeyName string, symbol string) ([]*models.QuantOrders, error) {
cacheBytes, err := impl.RedisService.Client.Get(impl.RedisService.Ctx, PlanKeyName+".PosOrders").Bytes()
if err != nil {
return nil, err
}
var orders map[string][]*models.QuantOrders
err = msgpack.Unmarshal(cacheBytes, &orders)
if err != nil {
return nil, err
}
if _, ok := orders[symbol]; !ok {
return nil, fmt.Errorf("%s %s Not Found Position", PlanKeyName, symbol)
}
return orders[symbol], nil
}
func RefreshPositions(p *Spec) (map[string][]*models.QuantOrders, error) {
orders := make(map[string][]*models.QuantOrders, 0)
var summary []string
switch p.Api.Exchange {
case "BINANCE":
summary, orders = p.BinanceClient.GetPositions()
break
case "BITGET":
//summary, orders, _ := GetPositions_Bitget(api)
break
default:
return nil, fmt.Errorf("Not Found Exchange", p.Api.Exchange)
}
// 统地订单数量
PositionsTotal = len(summary)
if PositionsTotal == 0 {
impl.RedisService.Client.Del(impl.RedisService.Ctx, p.PlanKeyName+".PosSummary").Result()
impl.RedisService.Client.Del(impl.RedisService.Ctx, p.PlanKeyName+".PosOrders").Result()
return nil, nil
}
_, err := impl.RedisService.Client.Set(impl.RedisService.Ctx, p.PlanKeyName+".PosSummary", strings.Join(summary, ","), 0).Result()
if err != nil {
return nil, err
}
// 序列化为 MessagePack
ordersPack, _ := msgpack.Marshal(orders)
_, err = impl.RedisService.Client.Set(impl.RedisService.Ctx, p.PlanKeyName+".PosOrders", ordersPack, 0).Result()
if err != nil {
return nil, err
}
return orders, nil
}
func (bn *BinanceClient) GetPositions() ([]string, map[string][]*models.QuantOrders) {
data, err := bn.Futures.NewGetPositionRiskV3Service().Do(context.Background())
if err != nil {
log.Println("GetPositions_Binance", err)
return nil, nil
}
// jsonBytes, _ := json.Marshal(data)
// fmt.Println(string(jsonBytes))
positionData := make(map[string][]*models.QuantOrders, 0)
var PositionSummary []string
for _, row := range data {
amt := utils.String2Float64(row.PositionAmt)
if amt == 0 {
continue
}
side := strings.ToUpper(row.PositionSide)
PositionSummary = append(PositionSummary, row.Symbol+"."+side)
record := &models.QuantOrders{
Symbol: row.Symbol,
Side: side,
OpenPrice: utils.String2Float64(row.BreakEvenPrice), // 开仓成本价
Volume: math.Abs(utils.String2Float64(row.PositionAmt)), // 交易币成交数量
MarginSize: utils.String2Float64(row.InitialMargin), // 计价币成交数量
Status: "1", // 成交
}
/* TODO: 暂时不计算包括成本的均价
// 总成本 = (开仓价格 *开仓数量)+手续费
decOpenPrice := decimal.NewFromFloat(record.OpenPrice)
decVolume := decimal.NewFromFloat(record.Volume)
decFee := decimal.NewFromFloat(record.Fee)
totalCost := decOpenPrice.Mul(decVolume)
totalCost = totalCost.Add(decFee)
// 均价 = 总成本 / 总开仓数量
record.AvgPrice, _ = totalCost.Div(decVolume).Float64()
*/
positionData[row.Symbol] = append(positionData[row.Symbol], record)
}
return PositionSummary, positionData
}