1、有更新,还没测完,暂时归档

This commit is contained in:
2025-11-05 16:26:21 +08:00
parent 643eab3496
commit bd7a6d3870
15 changed files with 536 additions and 296 deletions

View File

@ -19,6 +19,7 @@ import (
"time"
"github.com/bytedance/sonic"
"github.com/go-admin-team/go-admin-core/logger"
log "github.com/go-admin-team/go-admin-core/logger"
"github.com/shopspring/decimal"
"gorm.io/gorm"
@ -35,6 +36,8 @@ func JudgeFuturesPrice(tradeSet models.TradeSet) {
return
}
futApi := FutRestApi{}
orderService := orderservice.OrderService{}
orderService.Orm = db
for _, item := range preOrderVal {
preOrder := dto.PreOrderRedisList{}
@ -57,14 +60,32 @@ func JudgeFuturesPrice(tradeSet models.TradeSet) {
//多
if (strings.ToUpper(preOrder.Site) == "BUY" && orderPrice.Cmp(tradePrice) >= 0) ||
(strings.ToUpper(preOrder.Site) == "SELL" && orderPrice.Cmp(tradePrice) <= 0) {
futTriggerOrder(db, &preOrder, item, futApi)
apiInfo, _ := GetApiInfo(preOrder.ApiId)
if err := commonservice.JudgeWebsocketTimeout(apiInfo.ApiKey, 2); err != nil {
log.Errorf("合约行情订阅超时,apiKey:%s err:%v", apiInfo.ApiKey, err)
if err1 := orderService.ErrorTrigger(preOrder.Id, 2,
global.EXCHANGE_BINANCE, item,
fmt.Sprintf("行情触发失败,err:%v", err)); err1 != nil {
log.Error("触发失败", err1)
}
continue
} else {
futTriggerOrder(db, &preOrder, item, futApi)
}
}
}
}
// 分布式锁下单
// 分布式锁下单(合约预设主单触发)
// 参数说明:
// - db: 数据库连接,用于查询与更新预下单状态
// - v: 预下单信息(包含价格、数量、订单号等)
// - item: 从 Redis 列表读取的原始字符串,用于幂等删除
// - futApi: 合约下单 API 封装
// 逻辑:在 FutTrigger 锁下校验订单有效性,执行下单并更新状态,最后用原始 item 删除缓存,避免序列化差异导致删除失败。
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)
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, v.ApiId, v.Symbol), 200, 5, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Debug("获取锁失败", err)
@ -110,29 +131,21 @@ func futTriggerOrder(db *gorm.DB, v *dto.PreOrderRedisList, item string, futApi
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
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)
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Error("删除redis 预下单失败:", err)
}
return
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
if _, err := helper.DefaultRedis.LRem(key, item); 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 {
@ -303,7 +316,7 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder positiondto.ReduceListItem, f
apiInfo, _ := GetApiInfo(reduceOrder.ApiId)
if apiInfo.Id == 0 {
log.Error("现货减仓 查询api用户不存在")
log.Error("合约减仓 查询api用户不存在")
return false
}
for _, takeOrder := range takeOrders {
@ -399,6 +412,12 @@ func JudgeFutAddPosition(trade models.TradeSet) {
}
// 合约加仓触发
// 参数说明:
// - db: 数据库连接
// - v: 加仓触发信息
// - item: Redis 列表原始条目,用于精确删除
// - futApi: 合约下单 API 封装
// 逻辑:在 FutTrigger 锁下校验缓存与订单有效性,按系统加仓溢价调整价格,触发限价下单并删除原始缓存条目。
func FutAddPositionTrigger(db *gorm.DB, v *positiondto.AddPositionList, item string, futApi FutRestApi) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond)
@ -452,29 +471,21 @@ func FutAddPositionTrigger(db *gorm.DB, v *positiondto.AddPositionList, item str
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
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)
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Error("删除redis 预下单失败:", err)
}
return
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
if _, err := helper.DefaultRedis.LRem(key, item); 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 {
@ -490,3 +501,66 @@ func FutAddPositionTrigger(db *gorm.DB, v *positiondto.AddPositionList, item str
return
}
}
// 触发止盈单
// Deprecated 暂时不用了
func FuturesTakeTrigger(db *gorm.DB, spotApi *SpotRestApi, takeOrder dto.TakeProfitRedisList, key, item string, futApi FutRestApi) {
// tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, takeOrder.Symbol, 2)
// if err != nil {
// logger.Errorf("触发止盈单 查询交易对失败 交易对:%s ordersn:%s err:%v", takeOrder.Symbol, takeOrder.OrderSn, err)
// }
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, takeOrder.ApiId, takeOrder.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()
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
if !hasrecord {
log.Debug("止损缓存中不存在", item)
return
}
apiInfo, _ := GetApiInfo(takeOrder.ApiId)
if apiInfo.Id == 0 {
log.Error("现货止盈 查询api用户不存在")
return
}
// price := takeOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.StopLossPremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
// num := utility.StrToDecimal(stopOrder.Num).Truncate(int32(tradeSet.AmountDigit))
params := OrderPlacementService{
ApiId: takeOrder.ApiId,
Side: takeOrder.Site,
Type: "LIMIT",
TimeInForce: "GTC",
Symbol: takeOrder.Symbol,
// Price: price,
// Quantity: num,
NewClientOrderId: takeOrder.OrderSn,
}
if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil {
log.Errorf("现货止盈挂单失败 id%s err:%v", takeOrder.Id, err)
} else {
if err := db.Model(&DbModels.LinePreOrder{}).
Where("id = ? ", takeOrder.Id).
Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
log.Errorf("现货止盈更新状态失败 id%s err:%v", takeOrder.Id, err)
}
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Errorf("现货止盈 删除缓存失败 id:%v err:%v", takeOrder.Id, err)
}
} else {
logger.Errorf("触发止盈单 不存在待触发主单 %s", item)
}
}

View File

@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"go-admin/app/admin/models"
DbModels "go-admin/app/admin/models"
"go-admin/app/admin/service/dto"
"go-admin/common/const/rediskey"
@ -20,7 +20,6 @@ import (
"github.com/bytedance/sonic"
"github.com/go-admin-team/go-admin-core/logger"
log "github.com/go-admin-team/go-admin-core/logger"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
@ -137,7 +136,7 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
//修改减仓单减仓策略状态
ReduceCallBack(db, preOrder)
orderExt := models.LinePreOrderExt{}
orderExt := DbModels.LinePreOrderExt{}
//减仓策略单获取主减仓单的拓展信息
if preOrder.ReduceOrderId > 0 {
db.Model(&orderExt).Where("order_id =?", preOrder.ReduceOrderId).First(&orderExt)
@ -162,7 +161,7 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutReducecCallback, preOrder.ApiId, preOrder.Symbol), 120, 20, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Error("获取锁失败", err)
logger.Error("获取锁失败", err)
return
} else if ok {
defer lock.Release()
@ -183,8 +182,8 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
// 减仓处理止盈止损
func FutTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder, apiUserInfo DbModels.LineApiUser, tradeSet models2.TradeSet,
positionData positiondto.PositionDto, orderExt DbModels.LinePreOrderExt, manualTakeRatio, manualStopRatio decimal.Decimal) bool {
orders := make([]models.LinePreOrder, 0)
if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status = 0", preOrder.Id).Find(&orders).Error; err != nil {
orders := make([]DbModels.LinePreOrder, 0)
if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status = 0", preOrder.Id).Find(&orders).Error; err != nil {
logger.Errorf("handleMainReduceFilled 获取待触发订单失败,订单号:%s", preOrder.OrderSn)
return true
}
@ -254,12 +253,12 @@ func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal,
nextOrder := DbModels.LinePreOrder{}
nextExt := DbModels.LinePreOrderExt{}
if err := db.Model(&models.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0 AND reduce_order_id=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil {
if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0 AND reduce_order_id=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil {
logger.Errorf("获取下一个单失败 err:%v", err)
return
}
if err := db.Model(&models.LinePreOrderExt{}).Where("order_id =? and add_type =2", nextOrder.Id).First(&nextExt).Error; err != nil {
if err := db.Model(&DbModels.LinePreOrderExt{}).Where("order_id =? and add_type =2", nextOrder.Id).First(&nextExt).Error; err != nil {
logger.Errorf("获取下一个单失败 err:%v", err)
}
@ -482,7 +481,7 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) {
}
// 处理主单成交,处理止盈、止损、减仓订单
func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrderId int, first bool) {
func handleFutMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder, extOrderId int, first bool) {
// 获取交易对配置和API信息
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, preOrder.Symbol, 2)
mainId := preOrder.Id
@ -503,7 +502,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd
// 处理主单加仓
if preOrder.OrderCategory == 3 {
if err := handleMainOrderAddPosition(db, preOrder); err != nil {
if err = handleMainOrderAddPosition(db, preOrder); err != nil {
logger.Errorf("处理主单加仓失败, 主单号:%s, 错误信息: %v", preOrder.MainId, err)
return
}
@ -557,7 +556,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd
// 获取和保存持仓数据
positionData := savePosition(db, preOrder)
orderExt := models.LinePreOrderExt{}
orderExt := DbModels.LinePreOrderExt{}
db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt)
totalNum := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit))
price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit))
@ -616,7 +615,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd
}
// 处理主单加仓
func handleMainOrderAddPosition(db *gorm.DB, preOrder *models.LinePreOrder) error {
func handleMainOrderAddPosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) error {
// 更新加仓状态
if err := db.Model(&DbModels.LinePreOrderStatus{}).
Where("order_id = ?", preOrder.MainId).
@ -630,7 +629,7 @@ func handleMainOrderAddPosition(db *gorm.DB, preOrder *models.LinePreOrder) erro
// 取消主单相关订单
// changeMainOrderStatus 是否修改主单状态
func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder, changeMainOrderStatus bool) error {
func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *DbModels.LinePreOrder, changeMainOrderStatus bool) error {
mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site)
if err != nil {
return err
@ -689,8 +688,8 @@ func CancelMainOrders(mainIds []int, db *gorm.DB, apiUserInfo DbModels.LineApiUs
}
// 获取止盈止损订单
func getStopOrders(db *gorm.DB, preOrder *models.LinePreOrder) ([]models.LinePreOrder, error) {
var orders []models.LinePreOrder
func getStopOrders(db *gorm.DB, preOrder *DbModels.LinePreOrder) ([]DbModels.LinePreOrder, error) {
var orders []DbModels.LinePreOrder
if err := db.Model(&DbModels.LinePreOrder{}).
Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id).
Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
@ -700,7 +699,7 @@ func getStopOrders(db *gorm.DB, preOrder *models.LinePreOrder) ([]models.LinePre
}
// 更新订单数量
func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, ext *models.LinePreOrderExt, num decimal.Decimal, first bool, tradeSet models2.TradeSet) decimal.Decimal {
func updateOrderQuantity(db *gorm.DB, order DbModels.LinePreOrder, preOrder *DbModels.LinePreOrder, ext *DbModels.LinePreOrderExt, num decimal.Decimal, first bool, tradeSet models2.TradeSet) decimal.Decimal {
// 处理减仓比例
// if order.OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 {
// // 计算减仓数量
@ -755,7 +754,8 @@ func processFutReduceOrder(order DbModels.LinePreOrder, price, num decimal.Decim
}
// 处理止盈订单
func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.LinePreOrder, num decimal.Decimal) {
// Deprecated 请使用 processFutTakeProfitOrderNew
func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order DbModels.LinePreOrder, num decimal.Decimal) {
price, _ := decimal.NewFromString(order.Price)
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 2)
@ -774,7 +774,7 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line
if err != nil {
logger.Error("合约止盈下单失败:", order.OrderSn, " err:", err)
if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id).
if err = db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id).
Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity, "rate": order.Rate, "price": order.Price}).Error; err != nil {
logger.Error("合约止盈下单失败,更新状态失败:", order.OrderSn, " err:", err)
}
@ -791,9 +791,38 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line
}
}
// 写入待触发合约止盈单
func processFutTakeProfitOrderNew(db *gorm.DB, futApi FutRestApi, order DbModels.LinePreOrder, num decimal.Decimal) {
price, _ := decimal.NewFromString(order.Price)
takeOrder := dto.TakeProfitRedisList{
Id: order.Id,
ApiId: order.ApiId,
PId: order.Pid,
MainId: order.MainId,
Price: price,
Site: order.Site,
Symbol: order.Symbol,
SymbolType: order.SymbolType,
OrderCategory: order.OrderCategory,
OrderSn: order.OrderSn,
}
key := fmt.Sprintf(rediskey.FuturesTakeList, global.EXCHANGE_BINANCE)
val, err := sonic.MarshalString(&takeOrder)
if err != nil {
logger.Errorf("序列化失败 err:%v", err)
return
}
if err := helper.DefaultRedis.RPushList(key, val); err != nil {
logger.Errorf("合约止盈单写入缓存失败 err:%v", err)
return
}
}
// 处理止损订单
// order 止损单
func processFutStopLossOrder(db *gorm.DB, order models.LinePreOrder, price, num decimal.Decimal) error {
// Deprecated 请使用 processFutStopLossOrderNew
func processFutStopLossOrder(db *gorm.DB, order DbModels.LinePreOrder, price, num decimal.Decimal) error {
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 2)
params := FutOrderPlace{

View File

@ -56,9 +56,11 @@ func JudgeSpotPrice(trade models.TradeSet) {
apiInfo, _ := GetApiInfo(preOrder.ApiId)
if err := commonservice.JudgeWebsocketTimeout(apiInfo.ApiKey, 1); err != nil {
log.Errorf("现货行情订阅超时,apiKey:%s err:%v", err)
log.Errorf("现货行情订阅超时,apiKey:%s err:%v", apiInfo.ApiKey, err)
if err1 := orderService.ErrorTrigger(&preOrder, fmt.Sprintf("行情触发失败,err:%v", err)); err1 != nil {
if err1 := orderService.ErrorTrigger(preOrder.Id, 1,
global.EXCHANGE_BINANCE, item,
fmt.Sprintf("行情触发失败,err:%v", err)); err1 != nil {
log.Error("触发失败", err1)
}
continue
@ -70,9 +72,8 @@ func JudgeSpotPrice(trade models.TradeSet) {
}
}
// 分布式锁下单
// v 预下单信息
// item 预下单源文本
// 分布式锁下单(现货预设主单触发)
// v: 预下单信息item: Redis 原始条目(用于准确删除)
func SpotOrderLock(db *gorm.DB, v *dto.PreOrderRedisList, item string, spotApi SpotRestApi) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond)
@ -114,29 +115,21 @@ func SpotOrderLock(db *gorm.DB, v *dto.PreOrderRedisList, item string, spotApi S
Quantity: num,
NewClientOrderId: v.OrderSn,
}
preOrderVal, _ := sonic.MarshalString(&v)
if err := spotApi.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
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)
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Error("删除redis 预下单失败:", err)
}
return
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
if _, err := helper.DefaultRedis.LRem(key, item); 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 {
@ -214,7 +207,7 @@ func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi S
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
if !hasrecord {
log.Debug("减仓缓存中不存在", item)
log.Debug("止损缓存中不存在", item)
return
}
@ -225,17 +218,8 @@ func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi S
return
}
var err error
for x := 1; x <= 4; x++ {
err = spotApi.CancelOpenOrderByOrderSn(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
if err == nil || strings.Contains(err.Error(), "该交易对没有订单") {
err = nil
break
}
}
if err != nil {
// 使用统一的撤单重试封装,确保行为一致且高效
if err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn); err != nil {
log.Error("现货止损撤单失败", err)
return
}
@ -265,11 +249,9 @@ func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi S
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Errorf("现货止损 删除缓存失败 id:%v err:%v", stopOrder.Id, err)
}
} else {
log.Error("获取锁失败")
}
}
// 判断是否触发现货减仓
@ -310,7 +292,7 @@ func JudgeSpotReduce(trade models.TradeSet) {
return
} else if ok {
defer lock.Release()
hasrecord, _ := helper.DefaultRedis.HExists(reduceReduceListKey, utility.IntTostring(reduceOrderStrategy.OrderId), item)
hasrecord, _ := helper.DefaultRedis.HExists(reduceReduceListKey, utility.IntToString(reduceOrderStrategy.OrderId), item)
if !hasrecord {
log.Debug("减仓缓存中不存在", item)
@ -515,6 +497,8 @@ func JudgeSpotAddPosition(trade models.TradeSet) {
}
}
// 现货加仓触发
// v: 加仓触发信息item: Redis 原始条目(用于准确删除)
func SpotAddPositionTrigger(db *gorm.DB, v *positiondto.AddPositionList, item string, spotApi SpotRestApi) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond)
@ -569,8 +553,6 @@ func SpotAddPositionTrigger(db *gorm.DB, v *positiondto.AddPositionList, item st
Quantity: num.Truncate(int32(tradeSet.AmountDigit)),
NewClientOrderId: v.OrderSn,
}
preOrderVal, _ := sonic.MarshalString(&v)
if err := spotApi.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
@ -578,20 +560,14 @@ func SpotAddPositionTrigger(db *gorm.DB, v *positiondto.AddPositionList, item st
if err != nil {
log.Error("下单失败后修改订单失败")
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Error("删除redis 预下单失败:", err)
}
return
}
if preOrderVal != "" {
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
log.Error("删除redis 预下单失败:", err)
}
if _, err := helper.DefaultRedis.LRem(key, item); 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 {
@ -607,3 +583,71 @@ func SpotAddPositionTrigger(db *gorm.DB, v *positiondto.AddPositionList, item st
return
}
}
// 触发现货止盈
func SpotTakeProfitTrigger(db *gorm.DB, takeOrder dto.TakeProfitRedisList, spotApi SpotRestApi, setting DbModels.LineSystemSetting, tradeSet models.TradeSet, key string, item string) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, takeOrder.ApiId, takeOrder.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()
stopOrder := DbModels.LinePreOrder{}
if err := db.Model(&DbModels.LinePreOrder{}).
Where("pid =? AND order_type =2", takeOrder.PId).
Find(&stopOrder).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
}
// 使用统一的撤单重试封装,确保行为一致且高效
if err := CancelOpenOrderByOrderSnLoop(apiInfo, stopOrder.Symbol, stopOrder.OrderSn); err != nil {
log.Error("现货止盈撤单失败", err)
return
}
price := takeOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.StopLossPremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
num := utility.StrToDecimal(stopOrder.Num).Truncate(int32(tradeSet.AmountDigit))
params := OrderPlacementService{
ApiId: takeOrder.ApiId,
Side: takeOrder.Site,
Type: "TAKE_PROFIT_Market",
TimeInForce: "GTC",
Symbol: takeOrder.Symbol,
Price: price,
Quantity: num,
NewClientOrderId: takeOrder.OrderSn,
}
if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil {
log.Errorf("现货止盈挂单失败 id%s err:%v", takeOrder.Id, err)
} else {
if err := db.Model(&DbModels.LinePreOrder{}).
Where("id = ? ", takeOrder.Id).
Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
log.Errorf("现货止盈更新状态失败 id%s err:%v", takeOrder.Id, err)
}
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Errorf("现货止盈 删除缓存失败 id:%v err:%v", takeOrder.Id, err)
}
} else {
log.Error("获取锁失败")
}
}

View File

@ -785,9 +785,6 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
totalNum := num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit))
num = totalNum
// if orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
// num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
// }
//止盈止损
for _, order := range orders {
order.Num = num.String()
@ -828,8 +825,6 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
}
processStopLossOrder(order)
// case 4: //减仓
// processSpotReduceOrder(order)
}
}
@ -908,7 +903,7 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP
if err != nil {
logger.Error("现货止盈下单失败:", order.OrderSn, " err:", err)
if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id).
if err = db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id).
Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity, "rate": order.Rate, "price": order.Price}).Error; err != nil {
logger.Error("现货止盈下单失败,更新状态失败:", order.OrderSn, " err:", err)
}
@ -924,8 +919,47 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP
}
}
// 止盈 放到止盈缓存中
// Deprecated 暂时没用
func processTakeProfitOrderNew(db *gorm.DB, spotApi SpotRestApi, order models.LinePreOrder) error {
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
if tradeSet.Coin == "" {
logger.Error("获取交易对失败")
return errors.New("获取交易对失败")
}
price, _ := decimal.NewFromString(order.Price)
price = price.Truncate(int32(tradeSet.PriceDigit))
takeOrder := dto.TakeProfitRedisList{
Id: order.Id,
MainId: order.MainId,
PId: order.Pid,
ApiId: order.ApiId,
OrderTye: order.OrderType,
OrderCategory: order.OrderCategory,
Price: price,
SymbolType: order.SymbolType,
Symbol: order.Symbol,
Site: order.Site,
OrderSn: order.OrderSn,
}
takeVal, _ := sonic.MarshalString(&takeOrder)
takeKey := fmt.Sprintf(rediskey.SpotTakeList, global.EXCHANGE_BINANCE)
if takeVal == "" {
return errors.New("takeVal is empty")
}
if err := helper.DefaultRedis.RPushList(takeKey, takeVal); err != nil {
return err
}
return nil
}
// 处理止损订单
// order 止损单
func processStopLossOrder(order models.LinePreOrder) error {
price := utility.StrToDecimal(order.Price)
stopLoss := dto.StopLossRedisList{
@ -955,6 +989,52 @@ func processStopLossOrder(order models.LinePreOrder) error {
return nil
}
// 处理止损订单 直接下到交易所
// Deprecated: 现在不用,等止盈不挂到交易所得时候使用
func processStopLossOrderNew(db *gorm.DB, spotApi SpotRestApi, order models.LinePreOrder) error {
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
if tradeSet.Coin == "" {
logger.Error("获取交易对失败")
return errors.New("获取交易对失败")
}
price, _ := decimal.NewFromString(order.Price)
params := OrderPlacementService{
ApiId: order.ApiId,
Symbol: order.Symbol,
Side: order.Site,
Price: price.Truncate(int32(tradeSet.PriceDigit)),
Quantity: utility.StrToDecimal(order.Num),
Type: "STOP_LOSS_LIMIT",
TimeInForce: "GTC",
StopPrice: price.Truncate(int32(tradeSet.PriceDigit)),
NewClientOrderId: order.OrderSn,
}
err := spotApi.OrderPlaceLoop(db, params, 3)
if err != nil {
logger.Error("现货止损下单失败:", order.OrderSn, " err:", err)
if err = db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id).
Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity, "rate": order.Rate, "price": order.Price}).Error; err != nil {
logger.Error("现货止损下单失败,更新状态失败:", order.OrderSn, " err:", err)
}
} else {
if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id).
Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate, "price": order.Price, "num": order.Num}).Error; err != nil {
logger.Error("更新现货止损单触发事件 ordersn:", order.OrderSn)
}
if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id).
Updates(map[string]interface{}{"status": "1"}).Error; err != nil {
logger.Error("现货止损下单成功,更新状态失败:", order.OrderSn, " err:", err)
}
}
return nil
}
// 循环取消订单
func CancelOpenOrderByOrderSnLoop(apiInfo DbModels.LineApiUser, symbol string, orderSn string) error {
spotApi := SpotRestApi{}