177 lines
4.6 KiB
Go
177 lines
4.6 KiB
Go
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
|
|
}
|