Files
exchange_go/services/binanceservice/futuresjudgeservice.go

489 lines
16 KiB
Go
Raw Normal View History

2025-02-11 14:49:16 +08:00
package binanceservice
import (
"context"
"errors"
"fmt"
DbModels "go-admin/app/admin/models"
"go-admin/app/admin/service/dto"
"go-admin/common/const/rediskey"
"go-admin/common/global"
"go-admin/common/helper"
"go-admin/models"
2025-04-07 18:36:36 +08:00
"go-admin/pkg/utility"
"go-admin/services/cacheservice"
2025-02-11 14:49:16 +08:00
"strings"
"time"
"github.com/bytedance/sonic"
log "github.com/go-admin-team/go-admin-core/logger"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
/*
判断合约触发
*/
func JudgeFuturesPrice(tradeSet models.TradeSet) {
preOrderVal, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.PreFutOrderList, global.EXCHANGE_BINANCE))
db := GetDBConnection()
if len(preOrderVal) == 0 {
return
}
futApi := FutRestApi{}
for _, item := range preOrderVal {
preOrder := dto.PreOrderRedisList{}
if err := sonic.Unmarshal([]byte(item), &preOrder); err != nil {
log.Error("反序列化失败")
continue
}
if preOrder.Symbol != tradeSet.Coin+tradeSet.Currency {
continue
}
orderPrice, _ := decimal.NewFromString(preOrder.Price)
tradePrice, _ := decimal.NewFromString(tradeSet.LastPrice)
if orderPrice.Cmp(decimal.Zero) == 0 || tradePrice.Cmp(decimal.Zero) == 0 {
continue
}
//多
if (strings.ToUpper(preOrder.Site) == "BUY" && orderPrice.Cmp(tradePrice) >= 0) ||
(strings.ToUpper(preOrder.Site) == "SELL" && orderPrice.Cmp(tradePrice) <= 0) {
futTriggerOrder(db, &preOrder, item, futApi)
}
}
}
// 分布式锁下单
func futTriggerOrder(db *gorm.DB, v *dto.PreOrderRedisList, item string, futApi FutRestApi) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, v.ApiId, v.Symbol), 200, 5, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Debug("获取锁失败", err)
return
} else if ok {
defer lock.Release()
key := fmt.Sprintf(rediskey.PreFutOrderList, global.EXCHANGE_BINANCE)
preOrder := DbModels.LinePreOrder{}
if err := db.Where("id = ?", v.Id).First(&preOrder).Error; err != nil {
log.Error("获取预下单失败", err)
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Error("不存在待触发主单", item)
helper.DefaultRedis.LRem(key, item)
}
return
}
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
if !hasrecord {
log.Debug("预下单缓存中不存在", item)
return
}
price, _ := decimal.NewFromString(v.Price)
num, _ := decimal.NewFromString(preOrder.Num)
if price.Cmp(decimal.Zero) == 0 {
log.Error("价格不能为0")
return
}
params := FutOrderPlace{
ApiId: v.ApiId,
Symbol: v.Symbol,
Side: v.Site,
OrderType: preOrder.MainOrderType,
SideType: preOrder.MainOrderType,
Price: price,
Quantity: num,
NewClientOrderId: v.OrderSn,
}
preOrderVal, _ := sonic.MarshalString(&v)
2025-02-14 09:43:49 +08:00
if err := futApi.OrderPlaceLoop(db, params, 3); err != nil {
2025-02-11 14:49:16 +08:00
log.Error("下单失败", v.Symbol, " err:", err)
err := db.Model(&DbModels.LinePreOrder{}).Where("id =? and status='0'", preOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error
if err != nil {
log.Error("更新预下单状态失败")
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
}
return
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
}
if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", preOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
log.Error("更新预下单状态失败 ordersn:", preOrder.OrderSn, " status:1")
}
if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status ='0'", preOrder.Id).Updates(map[string]interface{}{"status": "1"}).Error; err != nil {
2025-02-11 14:49:16 +08:00
log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
}
return
} else {
log.Error("获取锁失败")
return
}
}
// 判断是否触发合约减仓
func JudgeFuturesReduce(trade models.TradeSet) {
key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE)
reduceVal, _ := helper.DefaultRedis.GetAllList(key)
db := GetDBConnection()
2025-02-11 18:03:30 +08:00
futApi := FutRestApi{}
setting, err := cacheservice.GetSystemSetting(db)
2025-02-11 14:49:16 +08:00
if err != nil {
log.Error("获取系统设置失败")
return
}
2025-04-07 18:36:36 +08:00
reduceOrder := ReduceListItem{}
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
//减仓单减仓策略
2025-04-07 18:36:36 +08:00
reduceReduceListKey := fmt.Sprintf(rediskey.FutOrderReduceStrategyList, global.EXCHANGE_BINANCE)
orderReduceVal, _ := helper.DefaultRedis.HGetAllFields(reduceReduceListKey)
reduceOrderStrategy := dto.LineOrderReduceStrategyResp{}
for _, item := range orderReduceVal {
sonic.Unmarshal([]byte(item), &reduceOrderStrategy)
2025-04-07 18:36:36 +08:00
for index, item2 := range reduceOrderStrategy.Items {
if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency && !item2.Actived {
//买入
if item2.TriggerPrice.Cmp(decimal.Zero) > 0 &&
tradePrice.Cmp(decimal.Zero) > 0 &&
((strings.ToUpper(reduceOrderStrategy.Side) == "SELL" && item2.TriggerPrice.Cmp(tradePrice) >= 0) ||
(strings.ToUpper(reduceOrderStrategy.Side) == "BUY" && item2.TriggerPrice.Cmp(tradePrice) <= 0)) {
2025-04-07 18:36:36 +08:00
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.ReduceStrategyFutTriggerLock, reduceOrder.ApiId, reduceOrder.Symbol), 50, 15, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Error("获取锁失败", err)
return
} else if ok {
defer lock.Release()
2025-04-09 09:09:25 +08:00
hasrecord, _ := helper.DefaultRedis.HExists(reduceReduceListKey, utility.IntToString(reduceOrderStrategy.OrderId), item)
2025-04-07 18:36:36 +08:00
if !hasrecord {
log.Debug("减仓缓存中不存在", item)
return
}
2025-04-09 09:09:25 +08:00
order, err := CreateReduceReduceOrder(db, reduceOrderStrategy.OrderId, item2.Price, item2.Num, trade.PriceDigit)
2025-04-07 18:36:36 +08:00
if err != nil {
log.Errorf("%d 生成订单失败", reduceOrderStrategy.OrderId)
}
reduceOrder.ApiId = order.ApiId
reduceOrder.Id = order.Id
reduceOrder.Pid = order.Pid
reduceOrder.MainId = order.MainId
reduceOrder.Symbol = order.Symbol
reduceOrder.Side = reduceOrderStrategy.Side
reduceOrder.OrderSn = order.OrderSn
reduceOrder.Price = item2.Price
reduceOrder.Num = item2.Num
//下单成功修改策略节点状态
2025-04-09 09:09:25 +08:00
if FuturesReduceTrigger(db, reduceOrder, futApi, setting, reduceReduceListKey, item, true, reduceOrderStrategy.OrderId) {
2025-04-07 18:36:36 +08:00
reduceOrderStrategy.Items[index].Actived = true
allActive := true
orderId := utility.IntToString(reduceOrderStrategy.OrderId)
for _, item3 := range reduceOrderStrategy.Items {
if !item3.Actived {
allActive = false
break
}
}
if allActive {
if err := helper.DefaultRedis.HDelField(reduceReduceListKey, orderId); err != nil {
log.Errorf("删除redis reduceReduceListKey失败 %s", err.Error())
}
} else {
str, _ := sonic.MarshalString(reduceOrderStrategy)
if err := helper.DefaultRedis.HSetField(reduceReduceListKey, orderId, str); err != nil {
log.Errorf("更新redis reduceReduceListKey失败 %s", err.Error())
}
}
}
}
}
}
}
}
2025-02-11 14:49:16 +08:00
for _, item := range reduceVal {
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
log.Error("反序列化失败")
continue
}
if reduceOrder.Symbol == trade.Coin+trade.Currency {
orderPrice := reduceOrder.Price
//买入
if orderPrice.Cmp(decimal.Zero) > 0 &&
tradePrice.Cmp(decimal.Zero) > 0 &&
((strings.ToUpper(reduceOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) >= 0) ||
(strings.ToUpper(reduceOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) <= 0)) {
2025-04-09 09:09:25 +08:00
FuturesReduceTrigger(db, reduceOrder, futApi, setting, key, item, false, 0)
2025-02-11 14:49:16 +08:00
}
}
}
}
// 触发合约减仓
2025-04-07 18:36:36 +08:00
// isStrategy 是否是策略减仓
2025-04-09 09:09:25 +08:00
// reduceId 父减仓单id
func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, key, item string, isStrategy bool, reduceId int) bool {
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reduceOrder.Symbol, 1)
2025-04-07 18:36:36 +08:00
result := true
2025-02-11 18:03:30 +08:00
if tradeSet.LastPrice == "" {
2025-04-07 18:36:36 +08:00
return false
2025-02-11 18:03:30 +08:00
}
2025-02-11 14:49:16 +08:00
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Error("获取锁失败", err)
2025-04-07 18:36:36 +08:00
return false
2025-02-11 14:49:16 +08:00
} else if ok {
defer lock.Release()
2025-03-10 11:13:08 +08:00
takeOrders := make([]DbModels.LinePreOrder, 0)
//只取消减仓单 止盈止损减仓成功后取消
if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type IN 4 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil {
2025-02-11 14:49:16 +08:00
log.Error("查询止盈单失败")
// return false
2025-02-11 14:49:16 +08:00
}
2025-04-09 09:09:25 +08:00
var hasrecord bool
if isStrategy {
hasrecord, _ = helper.DefaultRedis.HExists(key, utility.IntToString(reduceId), item)
} else {
hasrecord, _ = helper.DefaultRedis.IsElementInList(key, item)
}
2025-02-11 18:03:30 +08:00
if !hasrecord {
log.Debug("减仓缓存中不存在", item)
2025-04-07 18:36:36 +08:00
return false
2025-02-11 18:03:30 +08:00
}
2025-03-10 11:13:08 +08:00
apiInfo, _ := GetApiInfo(reduceOrder.ApiId)
2025-02-11 14:49:16 +08:00
if apiInfo.Id == 0 {
log.Error("现货减仓 查询api用户不存在")
2025-04-07 18:36:36 +08:00
return false
2025-02-11 14:49:16 +08:00
}
2025-03-10 11:13:08 +08:00
for _, takeOrder := range takeOrders {
err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
2025-02-11 14:49:16 +08:00
2025-03-10 11:13:08 +08:00
if err != nil {
2025-04-07 18:36:36 +08:00
log.Error("撤单失败", err)
return false
2025-03-10 11:13:08 +08:00
}
2025-02-11 14:49:16 +08:00
}
2025-02-14 09:43:49 +08:00
2025-02-11 14:49:16 +08:00
price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
2025-02-15 18:38:58 +08:00
num := reduceOrder.Num.Truncate(int32(tradeSet.AmountDigit))
2025-02-14 09:43:49 +08:00
var positionSide string
2025-02-11 14:49:16 +08:00
2025-02-14 09:43:49 +08:00
if reduceOrder.Side == "BUY" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
2025-02-11 14:49:16 +08:00
}
2025-02-14 09:43:49 +08:00
if err := futApi.ClosePositionLoop(reduceOrder.Symbol, reduceOrder.OrderSn, num, reduceOrder.Side, positionSide, apiInfo, "LIMIT", "0", price, 3); err != nil {
2025-04-07 18:36:36 +08:00
result = false
2025-02-11 14:49:16 +08:00
log.Errorf("合约减仓挂单失败 id%s err:%v", reduceOrder.Id, err)
2025-02-18 15:40:45 +08:00
if err2 := db.Model(&DbModels.LinePreOrder{}).
Where("id = ? AND status =0", reduceOrder.Id).
Updates(map[string]interface{}{"status": 2, "desc": err.Error()}).Error; err2 != nil {
log.Errorf("合约减仓更新状态失败 id%s err:%v", reduceOrder.Id, err2)
}
} else {
if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", reduceOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
log.Error("更新减仓单状态失败 ordersn:", reduceOrder.OrderSn, " status:1")
}
if err := db.Model(&DbModels.LinePreOrder{}).
Where("id = ? AND status =0", reduceOrder.Id).Updates(map[string]interface{}{"status": 1}).Error; err != nil {
log.Errorf("合约减仓更新状态失败 id%s err:%v", reduceOrder.Id, err)
}
//处理减仓单减仓策略
2025-04-07 18:36:36 +08:00
if err := CacheOrderStrategyAndReCreate(db, reduceOrder, 2, tradeSet, setting); err != nil {
log.Errorf("合约减仓策略处理失败 id%s err:%v", reduceOrder.Id, err)
}
2025-02-11 14:49:16 +08:00
}
2025-04-07 18:36:36 +08:00
if !isStrategy {
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Errorf("合约减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err)
}
2025-02-11 14:49:16 +08:00
}
} else {
log.Error("获取锁失败")
2025-04-07 18:36:36 +08:00
result = false
2025-02-11 14:49:16 +08:00
}
2025-04-07 18:36:36 +08:00
return result
2025-02-11 14:49:16 +08:00
}
// 判断合约加仓
2025-02-11 14:49:16 +08:00
func JudgeFutAddPosition(trade models.TradeSet) {
key := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE)
preOrderVal, _ := helper.DefaultRedis.GetAllList(key)
db := GetDBConnection()
if len(preOrderVal) == 0 {
return
}
futApi := FutRestApi{}
for _, item := range preOrderVal {
preOrder := AddPositionList{}
if err := sonic.Unmarshal([]byte(item), &preOrder); err != nil {
log.Error("反序列化失败")
continue
}
if preOrder.Symbol == trade.Coin+trade.Currency {
orderPrice := preOrder.Price
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
if orderPrice.Cmp(decimal.Zero) == 0 || tradePrice.Cmp(decimal.Zero) == 0 {
continue
}
//多
if (strings.ToUpper(preOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) >= 0) ||
(strings.ToUpper(preOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) <= 0) {
FutAddPositionTrigger(db, &preOrder, item, futApi)
}
}
}
}
// 合约加仓触发
func FutAddPositionTrigger(db *gorm.DB, v *AddPositionList, item string, futApi FutRestApi) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Error("获取锁失败", err)
return
} else if ok {
defer lock.Release()
setting, _ := cacheservice.GetSystemSetting(db)
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, v.Symbol, 1)
2025-02-11 14:49:16 +08:00
if tradeSet.LastPrice == "" {
log.Errorf("合约加仓触发 查询交易对失败 交易对:%s ordersn:%s", v.Symbol, v.OrderSn)
return
}
key := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE)
preOrder := DbModels.LinePreOrder{}
if err := db.Where("id = ?", v.Id).First(&preOrder).Error; err != nil {
log.Error("获取预下单失败", err)
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Error("不存在待触发加仓主单", item)
helper.DefaultRedis.LRem(key, item)
}
return
}
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
if !hasrecord {
log.Error("不存在待触发加仓主单", item)
return
}
2025-02-15 18:38:58 +08:00
price := v.Price.Truncate(int32(tradeSet.PriceDigit))
2025-02-11 14:49:16 +08:00
num, _ := decimal.NewFromString(preOrder.Num)
if setting.AddPositionPremium.Cmp(decimal.Zero) > 0 {
price = price.Mul(decimal.NewFromInt(1).Sub(setting.AddPositionPremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
}
params := FutOrderPlace{
ApiId: v.ApiId,
Symbol: v.Symbol,
Side: v.Side,
OrderType: "LIMIT",
Price: price,
2025-02-15 18:38:58 +08:00
Quantity: num.Truncate(int32(tradeSet.AmountDigit)),
2025-02-11 14:49:16 +08:00
NewClientOrderId: v.OrderSn,
}
preOrderVal, _ := sonic.MarshalString(&v)
2025-02-14 09:43:49 +08:00
if err := futApi.OrderPlaceLoop(db, params, 3); err != nil {
2025-02-11 14:49:16 +08:00
log.Error("下单失败", v.Symbol, " err:", err)
err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status =0", preOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error
if err != nil {
log.Error("下单失败后修改订单失败")
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
}
return
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
}
if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", preOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
}
if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status ='0'", preOrder.Id).Updates(map[string]interface{}{"status": "1"}).Error; err != nil {
2025-02-11 14:49:16 +08:00
log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
}
return
} else {
log.Error("获取锁失败")
return
}
}