2025-02-06 11:14:33 +08:00
package binanceservice
import (
"context"
2025-02-06 18:03:09 +08:00
"errors"
2025-02-06 11:14:33 +08:00
"fmt"
"go-admin/app/admin/models"
DbModels "go-admin/app/admin/models"
2025-02-06 18:03:09 +08:00
"go-admin/app/admin/service/dto"
2025-02-06 11:14:33 +08:00
"go-admin/common/const/rediskey"
2025-02-06 18:03:09 +08:00
"go-admin/common/global"
2025-02-06 11:14:33 +08:00
"go-admin/common/helper"
2025-02-11 14:49:16 +08:00
models2 "go-admin/models"
2025-02-28 18:27:52 +08:00
"go-admin/models/positiondto"
2025-02-06 11:14:33 +08:00
"go-admin/pkg/utility"
2025-04-03 18:32:23 +08:00
"go-admin/services/cacheservice"
2025-02-28 18:27:52 +08:00
"go-admin/services/positionservice"
2025-02-06 11:14:33 +08:00
"strconv"
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/go-admin-team/go-admin-core/logger"
2025-03-27 16:18:32 +08:00
log "github.com/go-admin-team/go-admin-core/logger"
2025-02-06 11:14:33 +08:00
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
/ *
订单回调
* /
func ChangeSpotOrder ( mapData map [ string ] interface { } ) {
// 检查订单号是否存在
orderSn , ok := mapData [ "c" ]
2025-02-08 14:05:57 +08:00
originOrderSn := mapData [ "C" ] //取消操作 代表原始订单号
2025-02-06 11:14:33 +08:00
if ! ok {
logger . Error ( "订单回调失败, 没有订单号" , mapData )
return
}
2025-02-11 18:03:30 +08:00
if originOrderSn != nil && originOrderSn != "" {
2025-02-08 14:05:57 +08:00
orderSn = originOrderSn
}
2025-02-06 11:14:33 +08:00
// 获取数据库连接
db := GetDBConnection ( )
if db == nil {
logger . Error ( "订单回调失败, 无法获取数据库连接" )
return
}
// 获取 Redis 锁
lockKey := fmt . Sprintf ( rediskey . SpotCallBack , orderSn )
lock := helper . NewRedisLock ( lockKey , 200 , 5 , 100 * time . Millisecond )
if err := acquireLock ( lock , orderSn ) ; err != nil {
return
}
defer lock . Release ( )
// 查询订单
preOrder , err := getPreOrder ( db , orderSn )
if err != nil {
logger . Error ( "订单回调失败, 查询订单失败:" , orderSn , " err:" , err )
return
}
// 解析订单状态
status , ok := mapData [ "X" ]
if ! ok {
logMapData ( mapData )
return
}
// 更新订单状态
2025-02-06 18:03:09 +08:00
orderStatus , reason := parseOrderStatus ( status , mapData )
2025-02-06 11:14:33 +08:00
if orderStatus == 0 {
logger . Error ( "订单回调失败,状态错误:" , orderSn , " status:" , status , " reason:" , reason )
return
}
if err := updateOrderStatus ( db , preOrder , orderStatus , reason , true , mapData ) ; err != nil {
logger . Error ( "修改订单状态失败:" , orderSn , " err:" , err )
return
}
// 根据订单类型和状态处理逻辑
handleOrderByType ( db , preOrder , orderStatus )
}
// 获取 Redis 锁
func acquireLock ( lock * helper . RedisLock , orderSn interface { } ) error {
acquired , err := lock . AcquireWait ( context . Background ( ) )
if err != nil {
logger . Error ( "订单回调失败, 获取锁失败:" , orderSn , " err:" , err )
return err
}
if ! acquired {
logger . Error ( "订单回调失败, 获取锁失败:" , orderSn )
return fmt . Errorf ( "failed to acquire lock" )
}
return nil
}
// 记录 mapData 数据
func logMapData ( mapData map [ string ] interface { } ) {
mapStr , _ := sonic . Marshal ( & mapData )
logger . Error ( "订单回调失败, 没有状态:" , string ( mapStr ) )
}
// 根据订单类型和状态处理逻辑
func handleOrderByType ( db * gorm . DB , preOrder * DbModels . LinePreOrder , orderStatus int ) {
switch {
// 主单成交
2025-02-06 18:03:09 +08:00
case preOrder . OrderType == 0 && orderStatus == 6 :
2025-02-06 11:14:33 +08:00
handleMainOrderFilled ( db , preOrder )
2025-02-10 18:21:44 +08:00
//主单减仓完毕
2025-02-14 09:43:49 +08:00
case preOrder . OrderType == 4 && orderStatus == 6 :
2025-02-10 18:21:44 +08:00
handleMainReduceFilled ( db , preOrder )
2025-02-06 11:14:33 +08:00
//主单取消
case preOrder . OrderType == 0 && preOrder . Pid == 0 && orderStatus == 4 :
2025-03-07 16:48:55 +08:00
handleMainOrderCancel ( db , preOrder , 1 )
2025-02-06 11:14:33 +08:00
// 止盈成交
2025-02-06 18:03:09 +08:00
case preOrder . OrderType == 1 && orderStatus == 6 :
2025-02-06 11:14:33 +08:00
handleSpotTakeProfitFilled ( db , preOrder )
2025-02-06 18:03:09 +08:00
//平仓单
case preOrder . OrderType == 3 && orderStatus == 6 :
2025-02-06 11:14:33 +08:00
handleMainOrderClosePosition ( db , preOrder )
2025-02-06 18:03:09 +08:00
//主单止损回调
2025-02-10 18:21:44 +08:00
case preOrder . OrderType == 2 && orderStatus == 6 :
2025-02-28 18:27:52 +08:00
removeSpotLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
removePosition ( db , preOrder )
2025-02-10 18:21:44 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =?" , preOrder . MainId ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Errorf ( "主单止损回调 订单号:%s 修改主单状态失败:%v" , preOrder . OrderSn , err )
}
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "main_id =? AND status =0" , preOrder . MainId ) . Update ( "status" , 4 ) . Error ; err != nil {
logger . Errorf ( "主单止损回调 订单号:%s 修改主单状态失败:%v" , preOrder . OrderSn , err )
}
2025-02-20 15:13:30 +08:00
spotApi := SpotRestApi { }
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
if apiUserInfo . Id > 0 {
req := CancelOpenOrdersReq {
Symbol : preOrder . Symbol ,
ApiId : preOrder . ApiId ,
}
if err := spotApi . CancelOpenOrders ( db , req ) ; err != nil {
logger . Errorf ( "止盈单成功 取消其它订单失败 订单号:%s:" , err )
}
}
2025-02-10 18:21:44 +08:00
}
}
// 主单减仓完毕
func handleMainReduceFilled ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
if apiUserInfo . Id == 0 {
logger . Errorf ( "handleMainReduceFilled 获取api信息失败,订单号:%s" , preOrder . OrderSn )
return
}
2025-04-03 18:32:23 +08:00
tradeSet , err := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , preOrder . Symbol , 0 )
2025-02-10 18:21:44 +08:00
if err != nil {
logger . Errorf ( "handleMainReduceFilled 获取交易对设置失败,订单号:%s" , preOrder . OrderSn )
return
}
2025-04-03 18:32:23 +08:00
//修改减仓单减仓策略状态
ReduceCallBack ( db , preOrder )
2025-02-28 18:27:52 +08:00
orderExt := models . LinePreOrderExt { }
positionData := savePosition ( db , preOrder )
2025-02-10 18:21:44 +08:00
2025-02-20 12:00:04 +08:00
if err := db . Model ( & DbModels . LinePreOrderStatus { } ) . Where ( "order_id =? " , preOrder . MainId ) . Update ( "reduce_status" , 1 ) . Error ; err != nil {
logger . Errorf ( "handleMainReduceFilled 更新主单减仓状态失败,订单号:%s" , preOrder . OrderSn )
}
2025-04-09 09:09:25 +08:00
//策略减仓单 获取主减仓单拓展信息
if preOrder . ReduceOrderId > 0 {
db . Model ( & orderExt ) . Where ( "order_id =?" , preOrder . ReduceOrderId ) . Find ( & orderExt )
} else {
db . Model ( & orderExt ) . Where ( "order_id =?" , preOrder . Id ) . Find ( & orderExt )
}
2025-02-20 12:00:04 +08:00
2025-03-27 16:18:32 +08:00
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotReduceCallback , preOrder . ApiId , preOrder . Symbol ) , 120 , 20 , 100 * time . Millisecond )
2025-02-28 18:27:52 +08:00
2025-03-27 16:18:32 +08:00
if ok , err := lock . AcquireWait ( context . Background ( ) ) ; err != nil {
log . Error ( "获取锁失败" , err )
2025-02-14 09:43:49 +08:00
return
2025-03-27 16:18:32 +08:00
} else if ok {
defer lock . Release ( )
// 100%减仓 终止流程
if orderExt . AddPositionVal . Cmp ( decimal . NewFromInt ( 100 ) ) >= 0 {
//缓存
removeSpotLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
removePosition ( db , preOrder )
ids := [ ] int { preOrder . MainId , preOrder . Pid }
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id IN ? AND status =6" , ids ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Info ( "100%减仓完毕,终结流程" )
}
return
}
//市价单跳出
if preOrder . MainOrderType == "MARKET" {
return
}
totalNum := getSpotPositionAvailableQuantity ( db , apiUserInfo , preOrder , tradeSet ) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet)
totalNum = totalNum . Mul ( decimal . NewFromFloat ( 0.998 ) ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
//亏损大于0 重新计算比例
SpotTakeProfit ( db , preOrder , totalNum , positionData , orderExt , tradeSet , decimal . Zero , decimal . Zero )
mainId := preOrder . Id
if preOrder . MainId > 0 {
mainId = preOrder . MainId
}
nextSpotReduceTrigger ( db , mainId , totalNum , tradeSet )
2025-02-14 09:43:49 +08:00
}
2025-03-27 16:18:32 +08:00
}
2025-02-18 09:59:51 +08:00
2025-03-27 16:18:32 +08:00
func SpotTakeProfit ( db * gorm . DB , preOrder * DbModels . LinePreOrder , totalNum decimal . Decimal ,
positionData positiondto . PositionDto , orderExt DbModels . LinePreOrderExt , tradeSet models2 . TradeSet , manualTakeRatio , manualStopRatio decimal . Decimal ) bool {
2025-02-28 18:27:52 +08:00
price := utility . StrToDecimal ( preOrder . Price )
2025-03-27 16:18:32 +08:00
orders := make ( [ ] models . LinePreOrder , 0 )
2025-02-14 09:43:49 +08:00
2025-02-18 09:59:51 +08:00
if err := db . Model ( & models . LinePreOrder { } ) . Where ( "pid =? AND order_type IN (1,2) AND status=0" , preOrder . Id ) . Find ( & orders ) . Error ; err != nil {
logger . Errorf ( "获取减仓单止盈止损失败 err:%v" , err )
2025-03-27 16:18:32 +08:00
return true
2025-02-14 09:43:49 +08:00
}
2025-02-10 18:21:44 +08:00
2025-02-14 09:43:49 +08:00
spotApi := SpotRestApi { }
2025-02-10 18:21:44 +08:00
2025-02-18 09:59:51 +08:00
for index := range orders {
2025-02-28 18:27:52 +08:00
orders [ index ] . Num = totalNum . String ( )
2025-02-18 09:59:51 +08:00
if orders [ index ] . OrderType == 1 {
2025-03-27 16:18:32 +08:00
//手动设置百分比
if manualTakeRatio . Cmp ( decimal . Zero ) > 0 {
orders [ index ] . Rate = manualTakeRatio . String ( )
percentag := manualTakeRatio . Div ( decimal . NewFromInt ( 100 ) )
if positionData . PositionSide == "LONG" {
orders [ index ] . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
} else {
orders [ index ] . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
} else if positionData . TotalLoss . Cmp ( decimal . Zero ) > 0 && orderExt . Id > 0 {
2025-03-03 17:35:41 +08:00
percentag := positionData . TotalLoss . Div ( totalNum ) . Div ( price ) . Mul ( decimal . NewFromInt ( 100 ) )
2025-02-28 18:27:52 +08:00
percentag = percentag . Add ( orderExt . TakeProfitRatio ) . Truncate ( 2 )
orders [ index ] . Rate = percentag . String ( )
percentag = percentag . Div ( decimal . NewFromInt ( 100 ) )
orders [ index ] . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
2025-02-18 09:59:51 +08:00
processTakeProfitOrder ( db , spotApi , orders [ index ] )
} else if orders [ index ] . OrderType == 2 {
2025-03-27 16:18:32 +08:00
if manualStopRatio . Cmp ( decimal . Zero ) > 0 {
orders [ index ] . Rate = manualStopRatio . String ( )
percentag := manualStopRatio . Div ( decimal . NewFromInt ( 100 ) )
if positionData . PositionSide == "LONG" {
orders [ index ] . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
} else {
orders [ index ] . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
}
2025-02-18 09:59:51 +08:00
processStopLossOrder ( orders [ index ] )
2025-02-15 18:38:58 +08:00
}
}
2025-03-27 16:18:32 +08:00
return false
2025-03-07 16:48:55 +08:00
}
// 缓存下一个减仓单
func nextSpotReduceTrigger ( db * gorm . DB , mainId int , totalNum decimal . Decimal , tradeSet models2 . TradeSet ) bool {
nextOrder := DbModels . LinePreOrder { }
nextExt := DbModels . LinePreOrderExt { }
// var percentag decimal.Decimal
2025-02-10 18:21:44 +08:00
2025-04-09 09:09:25 +08:00
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 {
2025-03-07 16:48:55 +08:00
logger . Errorf ( "获取下一个单失败 err:%v" , err )
return true
2025-02-10 18:21:44 +08:00
}
2025-03-10 11:13:08 +08:00
if err := db . Model ( & models . LinePreOrderExt { } ) . Where ( "order_id =? and add_type =2" , nextOrder . Id ) . First ( & nextExt ) . Error ; err != nil {
2025-03-07 16:48:55 +08:00
logger . Errorf ( "获取下一个单失败 err:%v" , err )
}
2025-02-10 18:21:44 +08:00
2025-03-07 16:48:55 +08:00
//移除缓存
key := fmt . Sprintf ( rediskey . SpotReduceList , global . EXCHANGE_BINANCE )
vals , _ := helper . DefaultRedis . GetAllList ( key )
item := ReduceListItem { }
for _ , val := range vals {
sonic . Unmarshal ( [ ] byte ( val ) , & item )
if item . MainId == mainId {
if _ , err := helper . DefaultRedis . LRem ( key , val ) ; err != nil {
logger . Errorf ( "减仓单 redis删除失败 main_id:%v err:%v" , mainId , err )
}
}
2025-02-10 18:21:44 +08:00
}
2025-03-07 16:48:55 +08:00
//减仓配置 且减仓比例大于0小于100
if nextExt . Id > 0 && nextExt . AddType == 2 && nextExt . AddPositionVal . Cmp ( decimal . Zero ) > 0 && nextExt . AddPositionVal . Cmp ( decimal . Zero ) < 100 {
num := totalNum . Mul ( nextExt . AddPositionVal . Div ( decimal . NewFromInt ( 100 ) ) ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
nextOrder . Num = num . String ( )
2025-03-10 11:13:08 +08:00
if err := db . Model ( & nextOrder ) . Update ( "num" , nextOrder . Num ) . Error ; err != nil {
logger . Errorf ( "更新减仓单数量失败 err:%v" , err )
}
2025-02-10 18:21:44 +08:00
}
2025-03-07 16:48:55 +08:00
//减仓待触发
processSpotReduceOrder ( nextOrder )
return false
2025-02-06 11:14:33 +08:00
}
2025-02-14 09:43:49 +08:00
// 获取主单可用数量
func getSpotPositionAvailableQuantity ( db * gorm . DB , apiUserInfo DbModels . LineApiUser , preOrder * DbModels . LinePreOrder , tradeSet models2 . TradeSet ) decimal . Decimal {
totalNum := getSpotTotalNum ( apiUserInfo , preOrder , tradeSet )
//biance查询失败 查系统内数量
if totalNum . Cmp ( decimal . Zero ) <= 0 {
var mainId int
if preOrder . MainId > 0 {
mainId = preOrder . MainId
} else {
mainId = preOrder . Id
}
totalNum = getInternalNum ( db , mainId )
}
return totalNum
}
// 获取现货剩余数量
func getSpotTotalNum ( apiUserInfo DbModels . LineApiUser , preOrder * DbModels . LinePreOrder , tradeSet models2 . TradeSet ) decimal . Decimal {
var totalNum decimal . Decimal
var err error
for x := 1 ; x < 4 ; x ++ {
totalNum , err = getSpotPositionNum ( apiUserInfo , preOrder , tradeSet )
if err == nil {
break
}
time . Sleep ( time . Millisecond * 150 )
}
if err != nil {
totalNum = utility . StrToDecimal ( preOrder . Num )
}
return totalNum
}
// 获取系统内剩余数量
func getInternalNum ( db * gorm . DB , mainId int ) decimal . Decimal {
var totalNum , totalSellNum decimal . Decimal
var data DbModels . LinePreOrder
if err := db . Model ( & data ) . Where ( "(main_id =? OR id =?) AND order_type =0 AND status =6" , mainId , mainId ) . Select ( "COALESCE(sum(num), 0)" ) . Find ( & totalNum ) . Error ; err != nil {
logger . Errorf ( "getSpotInternalNum 查询所有数量失败,mainId:%d err:%v" , mainId , err )
}
if err := db . Model ( & data ) . Where ( "main_id =? AND order_type =4 AND status =6" , mainId ) . Select ( "COALESCE(sum(num), 0)" ) . Find ( & totalSellNum ) . Error ; err != nil {
logger . Errorf ( "getSpotInternalNum 查询所有减仓数量失败,mainId:%d err:%v" , mainId , err )
}
return totalNum . Sub ( totalSellNum ) . Abs ( )
}
2025-02-11 14:49:16 +08:00
// 获取现货持仓数量
func getSpotPositionNum ( apiUserInfo DbModels . LineApiUser , preOrder * DbModels . LinePreOrder , tradeSet models2 . TradeSet ) ( decimal . Decimal , error ) {
client := GetClient ( & apiUserInfo )
resp , _ , err := client . SendSpotAuth ( "/api/v3/account" , "GET" , map [ string ] interface { } {
"omitZeroBalances" : true ,
} )
if err != nil {
logger . Errorf ( "api_id:%d 交易对:%s 查询用户信息失败:%v" , apiUserInfo . Id , preOrder . Symbol , err )
return decimal . Decimal { } , err
}
var balanceInfo SpotAccountInfo
sonic . Unmarshal ( resp , & balanceInfo )
suffix := utility . ReplaceSuffix ( preOrder . Symbol , preOrder . QuoteSymbol , "" )
var num decimal . Decimal
for _ , item := range balanceInfo . Balances {
if item . Asset == suffix {
if utility . StrToDecimal ( item . Free ) . Cmp ( decimal . Zero ) <= 0 {
logger . Error ( "handleMainReduceFilled 账户余额不足,交易对:%s 订单号:%s" , preOrder . Symbol , preOrder . OrderSn )
return decimal . Decimal { } , errors . New ( "账户余额不足" )
}
num = utility . StrToDecimal ( item . Free ) . Mul ( decimal . NewFromFloat ( 0.998 ) ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
break
}
}
return num , nil
}
2025-02-06 18:03:09 +08:00
// 主单取消
2025-03-07 16:48:55 +08:00
// symbolType 1:现货 2:合约
func handleMainOrderCancel ( db * gorm . DB , preOrder * DbModels . LinePreOrder , symboType int ) {
var preOrderKey string
if symboType == 1 {
preOrderKey = fmt . Sprintf ( rediskey . PreSpotOrderList , global . EXCHANGE_BINANCE )
} else {
preOrderKey = fmt . Sprintf ( rediskey . PreFutOrderList , global . EXCHANGE_BINANCE )
}
2025-02-06 18:03:09 +08:00
preSpotOrders , _ := helper . DefaultRedis . GetAllList ( preOrderKey )
preOrderCache := DbModels . LinePreOrder { }
for _ , v := range preSpotOrders {
if v == "" {
continue
}
sonic . Unmarshal ( [ ] byte ( v ) , & preOrderCache )
if preOrderCache . Pid == preOrder . Id {
2025-03-07 16:48:55 +08:00
var count int64
db . Model ( & models . LinePreOrder { } ) .
Where ( "api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6" ,
preOrder . ApiId , preOrder . Site , preOrder . Symbol , preOrder . SymbolType , preOrder . ExchangeType ) . Count ( & count )
2025-02-06 18:03:09 +08:00
2025-03-07 16:48:55 +08:00
if count == 0 {
_ , err := helper . DefaultRedis . LRem ( preOrderKey , v )
if err != nil {
logger . Errorf ( "订单回调失败, 回调订单号:%s 删除缓存失败:%v" , preOrder . OrderSn , err )
}
2025-02-06 18:03:09 +08:00
}
}
}
}
// 平仓单
2025-02-06 11:14:33 +08:00
func handleMainOrderClosePosition ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
2025-02-06 18:03:09 +08:00
//主单平仓
if preOrder . OrderCategory == 1 {
2025-02-10 18:21:44 +08:00
ids := [ ] int { preOrder . Pid , preOrder . MainId }
db . Transaction ( func ( tx * gorm . DB ) error {
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id IN ? AND status=6" , ids ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Errorf ( "平仓订单回调失败, 回调订单号:%s 更新主单失败:%v" , preOrder . OrderSn , err )
return err
}
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "main_id =? AND status=0" , preOrder . MainId ) . Update ( "status" , 4 ) . Error ; err != nil {
logger . Errorf ( "平仓订单回调失败, 回调订单号:%s 更新子单失败:%v" , preOrder . OrderSn , err )
return err
}
return nil
} )
2025-02-06 18:03:09 +08:00
}
2025-02-10 18:21:44 +08:00
2025-02-28 18:27:52 +08:00
removeSpotLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
removePosition ( db , preOrder )
2025-02-20 15:13:30 +08:00
spotApi := SpotRestApi { }
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
if apiUserInfo . Id > 0 {
req := CancelOpenOrdersReq {
Symbol : preOrder . Symbol ,
ApiId : preOrder . ApiId ,
}
if err := spotApi . CancelOpenOrders ( db , req ) ; err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "平仓单成功 取消其它订单失败 订单号:%s:" , err )
2025-02-20 15:13:30 +08:00
}
}
2025-02-06 11:14:33 +08:00
}
2025-02-06 18:03:09 +08:00
// 止盈成交
2025-02-06 11:14:33 +08:00
func handleSpotTakeProfitFilled ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
2025-03-06 18:16:35 +08:00
childCount , _ := GetChildTpOrder ( db , preOrder . Id )
2025-02-20 15:13:30 +08:00
2025-03-06 18:16:35 +08:00
if childCount > 0 {
extOrderId := preOrder . Pid //ext主单id
positionData := savePosition ( db , preOrder )
processTakeProfitAndStopLossOrders ( db , preOrder , & positionData , extOrderId , false )
} else {
removeSpotLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
2025-02-28 18:27:52 +08:00
2025-03-06 18:16:35 +08:00
spotApi := SpotRestApi { }
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
2025-02-10 18:21:44 +08:00
2025-03-06 18:16:35 +08:00
if apiUserInfo . Id > 0 {
req := CancelOpenOrdersReq {
Symbol : preOrder . Symbol ,
ApiId : preOrder . ApiId ,
}
if err := spotApi . CancelOpenOrders ( db , req ) ; err != nil {
logger . Errorf ( "止盈单成功 取消其它订单失败 订单号:%s:" , err )
}
2025-02-10 18:21:44 +08:00
}
2025-03-06 18:16:35 +08:00
removePosition ( db , preOrder )
2025-02-10 18:21:44 +08:00
2025-03-06 18:16:35 +08:00
db . Transaction ( func ( tx * gorm . DB ) error {
ids := [ ] int { preOrder . Pid , preOrder . MainId }
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id IN ? AND status =6 AND order_type=0" , ids ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Errorf ( "止盈订单回调失败, 回调订单号:%s 更新主单失败:%v" , preOrder . OrderSn , err )
2025-02-10 18:21:44 +08:00
2025-03-06 18:16:35 +08:00
return err
}
2025-02-06 18:03:09 +08:00
2025-03-06 18:16:35 +08:00
return nil
} )
}
2025-02-10 18:21:44 +08:00
}
2025-02-28 18:27:52 +08:00
// 移除仓位信息
func removePosition ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
positionMangement := positionservice . BinancePositionManagement { }
positionMangement . Orm = db
positionDelReq := positiondto . LinePreOrderPositioinDelReq {
ApiId : preOrder . ApiId ,
SymbolType : preOrder . SymbolType ,
Symbol : preOrder . Symbol ,
ExchangeType : preOrder . ExchangeType ,
}
if preOrder . Site == "BUY" {
positionDelReq . Side = "SELL"
} else {
positionDelReq . Side = "BUY"
}
positionMangement . RemovePosition ( & positionDelReq )
}
// 清理待加仓、待止损、待减仓缓存
func removeSpotLossAndAddPosition ( mainId int , orderSn string ) {
2025-02-06 18:03:09 +08:00
stoplossKey := fmt . Sprintf ( rediskey . SpotStopLossList , global . EXCHANGE_BINANCE )
stoplossVal , _ := helper . DefaultRedis . GetAllList ( stoplossKey )
2025-02-10 18:21:44 +08:00
addPositionKey := fmt . Sprintf ( rediskey . SpotAddPositionList , global . EXCHANGE_BINANCE )
addPositionVal , _ := helper . DefaultRedis . GetAllList ( addPositionKey )
2025-02-11 14:49:16 +08:00
reduceKey := fmt . Sprintf ( rediskey . SpotReduceList , global . EXCHANGE_BINANCE )
reduceVal , _ := helper . DefaultRedis . GetAllList ( reduceKey )
2025-02-10 18:21:44 +08:00
stoploss := dto . StopLossRedisList { }
addPosition := AddPositionList { }
2025-02-11 14:49:16 +08:00
reduce := ReduceListItem { }
2025-02-06 18:03:09 +08:00
2025-04-07 18:36:36 +08:00
//移除减仓后减仓策略
RemoveReduceReduceCacheByMainId ( mainId , 1 )
2025-02-11 14:49:16 +08:00
//止损缓存
2025-02-06 18:03:09 +08:00
for _ , v := range stoplossVal {
sonic . Unmarshal ( [ ] byte ( v ) , & stoploss )
2025-02-28 18:27:52 +08:00
if stoploss . MainId == mainId {
2025-02-06 18:03:09 +08:00
_ , err := helper . DefaultRedis . LRem ( stoplossKey , v )
2025-02-06 11:14:33 +08:00
2025-02-06 18:03:09 +08:00
if err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "订单回调失败, 回调订单号:%s 删除止损缓存失败:%v" , orderSn , err )
2025-02-10 18:21:44 +08:00
}
}
}
2025-02-11 14:49:16 +08:00
//加仓缓存
2025-02-10 18:21:44 +08:00
for _ , v := range addPositionVal {
sonic . Unmarshal ( [ ] byte ( v ) , & addPosition )
2025-02-28 18:27:52 +08:00
if addPosition . MainId == mainId {
2025-02-10 18:21:44 +08:00
_ , err := helper . DefaultRedis . LRem ( addPositionKey , v )
if err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v" , orderSn , err )
2025-02-06 18:03:09 +08:00
}
}
}
2025-02-11 14:49:16 +08:00
//减仓缓存
for _ , v := range reduceVal {
sonic . Unmarshal ( [ ] byte ( v ) , & reduce )
2025-02-28 18:27:52 +08:00
if reduce . MainId == mainId {
2025-02-11 14:49:16 +08:00
_ , err := helper . DefaultRedis . LRem ( reduceKey , v )
if err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v" , orderSn , err )
2025-02-11 14:49:16 +08:00
}
}
}
2025-02-06 11:14:33 +08:00
}
2025-02-06 18:03:09 +08:00
// 主单成交
2025-02-06 11:14:33 +08:00
func handleMainOrderFilled ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
2025-02-28 18:27:52 +08:00
//修改持仓信息
positionData := savePosition ( db , preOrder )
2025-02-20 12:00:04 +08:00
if preOrder . OrderCategory == 3 {
if err := db . Model ( & DbModels . LinePreOrderStatus { } ) . Where ( "order_id = ?" , preOrder . MainId ) . Update ( "add_position_status" , 1 ) . Error ; err != nil {
logger . Errorf ( "更新主单加仓状态失败, 主单号:%s, 错误信息:%v" , preOrder . MainId , err )
}
2025-02-28 18:27:52 +08:00
} else {
mainOrders , _ := getOpenPositionMainOrderId ( db , preOrder . Id , preOrder . ApiId , preOrder . SymbolType , preOrder . ExchangeType , preOrder . Symbol , preOrder . Site )
if len ( mainOrders ) > 0 {
2025-03-03 17:35:41 +08:00
mainIds := [ ] int { }
2025-02-28 18:27:52 +08:00
for _ , mainOrder := range mainOrders {
2025-03-03 17:35:41 +08:00
mainId := mainOrder . Id
if mainOrder . MainId > 0 {
mainId = mainOrder . MainId
}
removeSpotLossAndAddPosition ( mainId , mainOrder . OrderSn )
if ! utility . ContainsInt ( mainIds , mainId ) {
mainIds = append ( mainIds , mainId )
}
2025-02-28 18:27:52 +08:00
}
spotApi := SpotRestApi { }
err := spotApi . CancelOpenOrdersLoop ( db , CancelOpenOrdersReq { ApiId : preOrder . ApiId , Symbol : preOrder . Symbol } , 4 )
if err != nil {
logger . Errorf ( "取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v" , preOrder . Symbol , preOrder . MainId , err )
2025-03-03 17:35:41 +08:00
} else if len ( mainIds ) > 0 {
2025-03-04 15:34:32 +08:00
if err := db . Exec ( "UPDATE line_pre_order SET `status`=4,`desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6" , mainIds ) . Error ; err != nil {
2025-03-03 17:35:41 +08:00
logger . Errorf ( "新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v" , preOrder . MainId , err )
}
2025-02-28 18:27:52 +08:00
}
}
2025-03-07 16:48:55 +08:00
//加仓待触发
addPositionOrders := make ( [ ] DbModels . LinePreOrder , 0 )
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "main_id =? AND order_category=3 AND order_type=0 AND status=0" , preOrder . Id ) . Order ( "rate asc" ) . Find ( & addPositionOrders ) . Error ; err != nil {
logger . Errorf ( "handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v" , preOrder . OrderSn , err )
}
keySpotAddPosition := fmt . Sprintf ( rediskey . SpotAddPositionList , global . EXCHANGE_BINANCE )
for _ , addPositionOrder := range addPositionOrders {
addPositionData := AddPositionList {
Id : addPositionOrder . Id ,
OrderSn : addPositionOrder . OrderSn ,
MainId : addPositionOrder . MainId ,
Pid : addPositionOrder . Pid ,
Price : utility . StrToDecimal ( addPositionOrder . Price ) ,
ApiId : addPositionOrder . ApiId ,
Symbol : addPositionOrder . Symbol ,
Side : addPositionOrder . Site ,
SymbolType : addPositionOrder . SymbolType ,
}
addVal , err := sonic . MarshalString ( addPositionData )
if err != nil {
logger . Errorf ( "handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v" , preOrder . OrderSn , err )
return
}
if err := helper . DefaultRedis . RPushList ( keySpotAddPosition , addVal ) ; err != nil {
logger . Errorf ( "handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v" , preOrder . OrderSn , err )
}
}
2025-02-20 12:00:04 +08:00
}
2025-03-06 18:16:35 +08:00
processTakeProfitAndStopLossOrders ( db , preOrder , & positionData , preOrder . Id , true )
2025-03-07 16:48:55 +08:00
2025-02-06 11:14:33 +08:00
}
// 解析订单状态
2025-02-06 18:03:09 +08:00
// 5:委托中 6:完全成交 4:取消 9:已平仓
func parseOrderStatus ( status interface { } , mapData map [ string ] interface { } ) ( int , string ) {
2025-02-06 11:14:33 +08:00
reason , _ := mapData [ "r" ] . ( string )
if strings . ToLower ( reason ) == "none" {
reason = ""
}
switch status {
case "NEW" : // 未成交
return 5 , reason
case "FILLED" : // 完全成交
2025-02-06 18:03:09 +08:00
return 6 , reason
2025-03-10 11:13:08 +08:00
case "CANCELED" , "EXPIRED" , "EXPIRED_IN_MATCH" : // 取消
2025-02-06 11:14:33 +08:00
return 4 , reason
default :
return 0 , reason
}
}
// 更新订单状态
func updateOrderStatus ( db * gorm . DB , preOrder * models . LinePreOrder , status int , reason string , isSpot bool , mapData map [ string ] interface { } ) error {
params := map [ string ] interface { } { "status" : strconv . Itoa ( status ) , "desc" : reason }
switch isSpot {
case true :
total := decimal . Zero
totalAmount := decimal . Zero
if totalStr , ok := mapData [ "Z" ] . ( string ) ; ok {
total , _ = decimal . NewFromString ( totalStr )
}
if totalAmountStr , ok := mapData [ "z" ] . ( string ) ; ok {
totalAmount , _ = decimal . NewFromString ( totalAmountStr )
}
//主单 修改单价 和成交数量
if total . Cmp ( decimal . Zero ) > 0 && totalAmount . Cmp ( decimal . Zero ) > 0 {
2025-02-14 09:43:49 +08:00
num := totalAmount . Mul ( decimal . NewFromFloat ( 0.998 ) )
2025-02-06 11:14:33 +08:00
params [ "num" ] = num
params [ "price" ] = total . Div ( totalAmount )
preOrder . Num = num . String ( )
2025-02-08 14:05:57 +08:00
preOrder . Price = total . Div ( totalAmount ) . String ( )
2025-02-06 11:14:33 +08:00
}
case false :
status , _ := mapData [ "X" ] . ( string )
if status == "FILLED" {
num , _ := decimal . NewFromString ( mapData [ "z" ] . ( string ) )
params [ "num" ] = num . Mul ( decimal . NewFromFloat ( 0.998 ) ) . String ( )
2025-02-08 14:05:57 +08:00
price , _ := mapData [ "ap" ] . ( string )
2025-02-06 11:14:33 +08:00
2025-02-08 14:05:57 +08:00
params [ "price" ] = price
2025-02-06 11:14:33 +08:00
preOrder . Num = num . String ( )
2025-02-08 14:05:57 +08:00
preOrder . Price = price
2025-02-06 11:14:33 +08:00
}
}
return db . Model ( & DbModels . LinePreOrder { } ) . Where ( "order_sn = ? AND status < 6" , preOrder . OrderSn ) .
Updates ( params ) . Error
}
// 主单成交 处理止盈止损订单
2025-02-06 18:03:09 +08:00
// preOrder 主单
2025-02-28 18:27:52 +08:00
// positionData 持仓缓存信息
2025-03-06 18:16:35 +08:00
// fist 首次止盈止损
func processTakeProfitAndStopLossOrders ( db * gorm . DB , preOrder * models . LinePreOrder , positionData * positiondto . PositionDto , extOrderId int , fist bool ) {
2025-02-06 11:14:33 +08:00
orders := [ ] models . LinePreOrder { }
2025-04-03 18:32:23 +08:00
tradeSet , _ := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , preOrder . Symbol , 0 )
2025-03-07 16:48:55 +08:00
mainId := preOrder . Id
if preOrder . MainId > 0 {
mainId = preOrder . MainId
}
2025-02-11 14:49:16 +08:00
if tradeSet . Coin == "" {
logger . Error ( "获取交易对失败" )
return
}
2025-02-11 18:03:30 +08:00
apiInfo , err := GetApiInfo ( preOrder . ApiId )
if apiInfo . Id == 0 {
logger . Error ( "订单回调查询apiuserinfo失败 err:" , err )
return
}
2025-02-14 09:43:49 +08:00
if preOrder . OrderCategory == 3 {
if err := cancelSymbolTakeAndStop ( db , preOrder . MainId , preOrder . SymbolType ) ; err != nil {
logger . Errorf ( "取消止盈止损订单失败 orderSn:%s err:%v" , preOrder . OrderSn , err )
}
2025-02-11 18:03:30 +08:00
}
2025-02-14 09:43:49 +08:00
num := getSpotPositionAvailableQuantity ( db , apiInfo , preOrder , tradeSet )
2025-02-11 14:49:16 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) .
2025-02-18 15:40:45 +08:00
Where ( "pid = ? AND order_type > 0 AND status = '0' " , preOrder . Id ) .
2025-02-11 14:49:16 +08:00
Find ( & orders ) . Error ; err != nil && errors . Is ( err , gorm . ErrRecordNotFound ) {
2025-02-06 11:14:33 +08:00
logger . Error ( "订单回调查询止盈止损单失败:" , err )
return
}
2025-02-28 18:27:52 +08:00
price := utility . StrToDecimal ( preOrder . Price )
2025-02-06 11:14:33 +08:00
spotApi := SpotRestApi { }
2025-02-28 18:27:52 +08:00
orderExt := models . LinePreOrderExt { }
2025-03-06 18:16:35 +08:00
db . Model ( & orderExt ) . Where ( "order_id =?" , extOrderId ) . First ( & orderExt )
2025-03-10 11:13:08 +08:00
totalNum := num . Mul ( decimal . NewFromFloat ( 0.998 ) ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
num = totalNum
2025-02-06 11:14:33 +08:00
2025-03-10 11:13:08 +08:00
// 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))
// }
2025-03-07 16:48:55 +08:00
//止盈止损
2025-02-14 09:43:49 +08:00
for _ , order := range orders {
2025-03-07 16:48:55 +08:00
order . Num = num . String ( )
2025-02-11 18:03:30 +08:00
2025-03-21 20:44:35 +08:00
if fist && ( order . OrderCategory == 3 || order . OrderCategory == 1 ) && order . OrderType == 1 && orderExt . TakeProfitNumRatio . Cmp ( decimal . Zero ) > 0 && orderExt . TakeProfitNumRatio . Cmp ( decimal . NewFromInt ( 100 ) ) != 0 {
2025-03-06 18:16:35 +08:00
//主单第一次且止盈数量不是100% 止盈数量
order . Num = num . Mul ( orderExt . TakeProfitNumRatio . Div ( decimal . NewFromInt ( 100 ) ) ) . Truncate ( int32 ( tradeSet . AmountDigit ) ) . String ( )
2025-02-11 18:03:30 +08:00
}
if err := db . Model ( & order ) . Update ( "num" , order . Num ) . Error ; err != nil {
2025-02-06 18:03:09 +08:00
logger . Errorf ( "修改止盈止损数量失败 订单号:%s err:%v" , order . OrderSn , err )
}
2025-02-06 11:14:33 +08:00
switch order . OrderType {
case 1 : // 止盈
2025-02-28 18:27:52 +08:00
//亏损大于0 重新计算比例
2025-03-27 16:18:32 +08:00
if fist && positionData . TotalLoss . Cmp ( decimal . Zero ) > 0 && orderExt . Id > 0 && preOrder . SignPriceType != "mixture" {
2025-03-03 17:35:41 +08:00
percentag := positionData . TotalLoss . Div ( num ) . Div ( price ) . Mul ( decimal . NewFromInt ( 100 ) )
2025-03-06 18:16:35 +08:00
if fist {
percentag = percentag . Add ( orderExt . TakeProfitRatio ) . Truncate ( 2 )
}
2025-02-28 18:27:52 +08:00
order . Rate = percentag . String ( )
percentag = percentag . Div ( decimal . NewFromInt ( 100 ) )
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
2025-03-21 20:44:35 +08:00
} else if ! fist && orderExt . TpTpPriceRatio . Cmp ( decimal . Zero ) > 0 {
2025-03-06 18:16:35 +08:00
percentag := orderExt . TpTpPriceRatio
order . Rate = percentag . String ( )
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( percentag . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
2025-02-28 18:27:52 +08:00
}
2025-02-11 18:03:30 +08:00
processTakeProfitOrder ( db , spotApi , order )
2025-02-06 11:14:33 +08:00
case 2 : // 止损
2025-03-06 18:16:35 +08:00
if ! fist {
order . Rate = orderExt . TpSlPriceRatio . Truncate ( 2 ) . String ( )
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( orderExt . TpSlPriceRatio . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
2025-02-06 18:03:09 +08:00
processStopLossOrder ( order )
2025-03-07 16:48:55 +08:00
// case 4: //减仓
// processSpotReduceOrder(order)
2025-02-11 14:49:16 +08:00
}
}
2025-03-07 16:48:55 +08:00
//待触发减仓单
2025-03-10 11:13:08 +08:00
nextSpotReduceTrigger ( db , mainId , totalNum , tradeSet )
2025-02-11 14:49:16 +08:00
}
2025-02-14 09:43:49 +08:00
// 根据下单百分比计算价格
2025-02-18 09:59:51 +08:00
func SetPrice ( order * models . LinePreOrder , preOrder * models . LinePreOrder , tradeSet models2 . TradeSet ) {
2025-02-14 09:43:49 +08:00
orderType := order . OrderType
itemSide := order . Site
rate := utility . StrToDecimal ( order . Rate )
switch {
//做多止盈、做空止损或减仓
case ( orderType == 1 && itemSide == "SELL" ) , ( ( orderType == 2 || orderType == 4 ) && itemSide == "BUY" ) :
order . Price = utility . StrToDecimal ( preOrder . Price ) . Mul ( decimal . NewFromInt ( 1 ) . Add ( rate . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
//做多止损或减仓、做空止盈
case ( ( orderType == 2 || orderType == 4 ) && itemSide == "SELL" ) , ( orderType == 1 && itemSide == "BUY" ) :
order . Price = utility . StrToDecimal ( preOrder . Price ) . Mul ( decimal . NewFromInt ( 1 ) . Sub ( rate . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
}
2025-02-11 14:49:16 +08:00
// 现货减仓
2025-02-11 18:03:30 +08:00
func processSpotReduceOrder ( preOrder DbModels . LinePreOrder ) {
2025-02-11 14:49:16 +08:00
key := fmt . Sprintf ( rediskey . SpotReduceList , global . EXCHANGE_BINANCE )
item := ReduceListItem {
Id : preOrder . Id ,
ApiId : preOrder . ApiId ,
Pid : preOrder . Pid ,
MainId : preOrder . MainId ,
Price : utility . StrToDecimal ( preOrder . Price ) ,
2025-02-11 18:03:30 +08:00
Num : utility . StrToDecimal ( preOrder . Num ) ,
2025-02-11 14:49:16 +08:00
Side : preOrder . Site ,
Symbol : preOrder . Symbol ,
OrderSn : preOrder . OrderSn ,
}
itemVal , err := sonic . MarshalString ( & item )
if err != nil {
logger . Errorf ( "序列化失败 err:%v" , err )
return
}
if err := helper . DefaultRedis . RPushList ( key , itemVal ) ; err != nil {
logger . Errorf ( "减仓单写入缓存失败 err:%v" , err )
return
}
2025-02-06 11:14:33 +08:00
}
// 处理止盈订单
2025-02-11 18:03:30 +08:00
func processTakeProfitOrder ( db * gorm . DB , spotApi SpotRestApi , order models . LinePreOrder ) {
2025-04-03 18:32:23 +08:00
tradeSet , _ := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , order . Symbol , 0 )
2025-02-06 11:14:33 +08:00
if tradeSet . Coin == "" {
logger . Error ( "获取交易对失败" )
return
}
price , _ := decimal . NewFromString ( order . Price )
params := OrderPlacementService {
ApiId : order . ApiId ,
Symbol : order . Symbol ,
Side : order . Site ,
Price : price . Truncate ( int32 ( tradeSet . PriceDigit ) ) ,
2025-02-11 18:03:30 +08:00
Quantity : utility . StrToDecimal ( order . Num ) ,
2025-02-06 11:14:33 +08:00
Type : "TAKE_PROFIT_LIMIT" ,
TimeInForce : "GTC" ,
StopPrice : price . Truncate ( int32 ( tradeSet . PriceDigit ) ) ,
NewClientOrderId : order . OrderSn ,
}
2025-02-14 09:43:49 +08:00
err := spotApi . OrderPlaceLoop ( db , params , 3 )
2025-02-06 11:14:33 +08:00
if err != nil {
logger . Error ( "现货止盈下单失败:" , order . OrderSn , " err:" , err )
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id = ?" , order . Id ) .
2025-03-06 18:16:35 +08:00
Updates ( map [ string ] interface { } { "status" : "2" , "desc" : err . Error ( ) , "num" : params . Quantity , "rate" : order . Rate , "price" : order . Price } ) . Error ; err != nil {
2025-02-06 11:14:33 +08:00
logger . Error ( "现货止盈下单失败,更新状态失败:" , order . OrderSn , " err:" , err )
}
} else {
2025-02-28 18:27:52 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? " , order . Id ) .
2025-03-06 18:16:35 +08:00
Updates ( map [ string ] interface { } { "trigger_time" : time . Now ( ) , "rate" : order . Rate , "price" : order . Price , "num" : order . Num } ) . Error ; err != nil {
2025-02-22 11:24:08 +08:00
logger . Error ( "更新现货止盈单触发事件 ordersn:" , order . OrderSn )
}
2025-02-06 11:14:33 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id = ? and status ='0'" , order . Id ) .
2025-03-06 18:16:35 +08:00
Updates ( map [ string ] interface { } { "status" : "1" } ) . Error ; err != nil {
2025-02-06 11:14:33 +08:00
logger . Error ( "现货止盈下单成功,更新状态失败:" , order . OrderSn , " err:" , err )
}
}
}
// 处理止损订单
// order 止损单
2025-02-06 18:03:09 +08:00
func processStopLossOrder ( order models . LinePreOrder ) error {
price := utility . StrToDecimal ( order . Price )
stopLoss := dto . StopLossRedisList {
Id : order . Id ,
2025-02-15 18:38:58 +08:00
MainId : order . MainId ,
2025-02-06 18:03:09 +08:00
PId : order . Pid ,
ApiId : order . ApiId ,
OrderTye : order . OrderType ,
OrderCategory : order . OrderCategory ,
Price : price ,
SymbolType : order . SymbolType ,
Symbol : order . Symbol ,
Site : order . Site ,
}
2025-02-06 11:14:33 +08:00
2025-02-06 18:03:09 +08:00
stoplossVal , _ := sonic . MarshalString ( & stopLoss )
stoplossKey := fmt . Sprintf ( rediskey . SpotStopLossList , global . EXCHANGE_BINANCE )
2025-02-06 11:14:33 +08:00
2025-02-06 18:03:09 +08:00
if stoplossVal == "" {
return errors . New ( "stoplossVal is empty" )
}
2025-02-06 11:14:33 +08:00
2025-02-06 18:03:09 +08:00
if err := helper . DefaultRedis . RPushList ( stoplossKey , stoplossVal ) ; err != nil {
return err
2025-02-06 11:14:33 +08:00
}
2025-02-06 18:03:09 +08:00
return nil
2025-02-06 11:14:33 +08:00
}
2025-02-14 09:43:49 +08:00
// 循环取消订单
func CancelOpenOrderByOrderSnLoop ( apiInfo DbModels . LineApiUser , symbol string , orderSn string ) error {
spotApi := SpotRestApi { }
var err error
for x := 1 ; x <= 4 ; x ++ {
err = spotApi . CancelOpenOrderByOrderSn ( apiInfo , symbol , orderSn )
if err == nil || strings . Contains ( err . Error ( ) , "该交易对没有订单" ) {
err = nil
break
}
2025-03-10 11:13:08 +08:00
time . Sleep ( time . Millisecond * 300 )
2025-02-14 09:43:49 +08:00
}
return err
}
2025-02-06 11:14:33 +08:00
// NEW
// PENDING_NEW
// PARTIALLY_FILLED
// FILLED
// CANCELED
// PENDING_CANCEL
// REJECTED
// EXPIRED
// EXPIRED_IN_MATCH