Files
exchange_go/services/binanceservice/futuresjudgeservice.go
2025-02-18 15:40:45 +08:00

396 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"
"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
}
//判断是否有已触发交易对
count, _ := GetSymbolTriggerCount(db, v.Symbol, 2)
if count > 0 {
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)
if err := futApi.OrderPlaceLoop(db, params, 3); err != nil {
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 =? AND status ='0'", preOrder.Id).Update("status", "1").Error; err != nil {
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)
if len(reduceVal) == 0 {
return
}
db := GetDBConnection()
futApi := FutRestApi{}
setting, err := GetSystemSetting(db)
if err != nil {
log.Error("获取系统设置失败")
return
}
for _, item := range reduceVal {
reduceOrder := ReduceListItem{}
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
log.Error("反序列化失败")
continue
}
if reduceOrder.Symbol == trade.Coin+trade.Currency {
orderPrice := reduceOrder.Price
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
//买入
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)) {
FuturesReduceTrigger(db, reduceOrder, futApi, setting, key, item)
}
}
}
}
// 触发合约减仓
func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, key, item string) {
tradeSet, _ := GetTradeSet(reduceOrder.Symbol, 1)
if tradeSet.LastPrice == "" {
return
}
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)
return
} else if ok {
defer lock.Release()
takeOrder := DbModels.LinePreOrder{}
if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type =1", reduceOrder.Pid).Find(&takeOrder).Error; err != nil {
log.Error("查询止盈单失败")
return
}
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
if !hasrecord {
log.Debug("减仓缓存中不存在", item)
return
}
apiInfo, _ := GetApiInfo(takeOrder.ApiId)
if apiInfo.Id == 0 {
log.Error("现货减仓 查询api用户不存在")
return
}
err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
if err != nil {
log.Error("合约止盈撤单失败", err)
return
}
price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
num := reduceOrder.Num.Truncate(int32(tradeSet.AmountDigit))
var positionSide string
if reduceOrder.Side == "BUY" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
}
// params := FutOrderPlace{
// ApiId: reduceOrder.ApiId,
// Side: reduceOrder.Side,
// OrderType: "STOP",
// Symbol: reduceOrder.Symbol,
// Price: price,
// StopPrice: price,
// Quantity: num,
// NewClientOrderId: reduceOrder.OrderSn,
// }
if err := futApi.ClosePositionLoop(reduceOrder.Symbol, reduceOrder.OrderSn, num, reduceOrder.Side, positionSide, apiInfo, "LIMIT", "0", price, 3); err != nil {
log.Errorf("合约减仓挂单失败 id%s err:%v", reduceOrder.Id, err)
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)
}
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Errorf("合约减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err)
}
} else {
log.Error("获取锁失败")
}
}
// 判断现货加仓
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, _ := GetSystemSetting(db)
tradeSet, _ := GetTradeSet(v.Symbol, 0)
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
}
price := v.Price.Truncate(int32(tradeSet.PriceDigit))
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,
Quantity: num.Truncate(int32(tradeSet.AmountDigit)),
NewClientOrderId: v.OrderSn,
}
preOrderVal, _ := sonic.MarshalString(&v)
if err := futApi.OrderPlaceLoop(db, params, 3); err != nil {
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 =? AND status ='0'", preOrder.Id).Update("status", "1").Error; err != nil {
log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
}
return
} else {
log.Error("获取锁失败")
return
}
}