From d4c8e692a78e2646736dac2fb03e1633a23edf6a Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 11 Apr 2025 09:06:09 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=90=88=E7=BA=A6=E6=AD=A2=E7=9B=88?= =?UTF-8?q?=E6=AD=A2=E6=8D=9F=E5=8D=95=E5=9C=A8=E5=87=8F=E4=BB=93=E5=8D=95?= =?UTF-8?q?=E6=88=90=E4=BA=A4=E4=B9=8B=E5=90=8E=E5=86=8D=E5=8F=96=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/line_pre_order.go | 1 - common/const/rediskey/redis_key.go | 4 +- .../futures_judge_service_test.go | 2 +- services/binanceservice/futuresbinancerest.go | 44 -------- .../binanceservice/futuresjudgeservice.go | 5 +- services/binanceservice/futuresrest.go | 32 ++++++ services/binanceservice/orderservice.go | 2 +- .../binanceservice/strategy_order_service.go | 100 ++++++++++++++---- services/futureservice/binancemarket.go | 19 ++-- services/spotservice/binancemarket.go | 19 ++-- 10 files changed, 142 insertions(+), 86 deletions(-) diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index f3a8cd0..4a64b7b 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -1015,7 +1015,6 @@ func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrder stopOrder.OrderType = 4 stopOrder.Status = 0 - stopOrder.Rate = ext.PriceRatio.String() stopOrder.Num = ext.TotalAfter.Sub(ext.TotalBefore).Abs().Truncate(int32(tradeSet.AmountDigit)).String() stopOrder.BuyPrice = "0" stopOrder.Rate = ext.PriceRatio.String() diff --git a/common/const/rediskey/redis_key.go b/common/const/rediskey/redis_key.go index 0290e1e..dbd70f9 100644 --- a/common/const/rediskey/redis_key.go +++ b/common/const/rediskey/redis_key.go @@ -95,9 +95,9 @@ const ( ) const ( - //现货最后成交价 sort set {exchangeType,symbol} + //现货最后成交价 sort set key:={exchangeType,symbol} content:={utc:price} SpotTickerLastPrice = "spot_ticker_last_price:%s:%s" - //合约最后成交价 sort set {exchangeType,symbol} + //合约最后成交价 sort set {exchangeType,symbol} content:={utc:price} FutureTickerLastPrice = "fut_ticker_last_price:%s:%s" //允许缓存交易对价格的交易对 list diff --git a/services/binanceservice/futures_judge_service_test.go b/services/binanceservice/futures_judge_service_test.go index 5e198c6..2daf740 100644 --- a/services/binanceservice/futures_judge_service_test.go +++ b/services/binanceservice/futures_judge_service_test.go @@ -28,7 +28,7 @@ func TestFutureJudge(t *testing.T) { // } key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) - item := `{"id":50,"apiId":49,"mainId":47,"pid":47,"symbol":"ADAUSDT","price":"0.5936","side":"SELL","num":"12","orderSn":"397913127842217984"}` + item := `{"id":10,"apiId":49,"mainId":7,"pid":7,"symbol":"ADAUSDT","price":"0.6244","side":"BUY","num":"12","orderSn":"398690240274890752"}` reduceOrder := ReduceListItem{} futApi := FutRestApi{} setting, err := cacheservice.GetSystemSetting(db) diff --git a/services/binanceservice/futuresbinancerest.go b/services/binanceservice/futuresbinancerest.go index 147a943..bc53433 100644 --- a/services/binanceservice/futuresbinancerest.go +++ b/services/binanceservice/futuresbinancerest.go @@ -555,50 +555,6 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { return nil } -// ClosePositionB 平仓B对应的交易对 -// bApiUserInfo B 账户api-user信息 -// symbol 需要平仓的交易对 -// closeType 平仓模式 ALL = 全平 reduceOnly = 只减仓 -// func (e FutRestApi) ClosePositionB(orm *gorm.DB, bApiUserInfo *DbModels.LineApiUser, symbol string, closeType string) error { -// if bApiUserInfo == nil { -// return errors.New("缺失平仓账户信息") -// } -// if symbol == "" { -// return errors.New("缺失平仓交易对信息") -// } -// risks, err := e.GetPositionV3(bApiUserInfo, symbol) -// if err != nil { -// return err -// } - -// for _, risk := range risks { -// if risk.Symbol == strings.ToUpper(symbol) { -// //持仓数量 -// positionAmt, _ := decimal.NewFromString(risk.PositionAmt) -// side := "BUY" -// if positionAmt.GreaterThan(decimal.Zero) { -// side = "SELL" -// } -// var closeAmt decimal.Decimal -// if strings.ToUpper(closeType) == "ALL" { //全部平仓 -// closeAmt = positionAmt -// } else { -// //如果是只减仓的话 数量=仓位数量*比例 -// closeAmt = positionAmt.Mul(decimal.NewFromFloat(reduceOnlyRate)) -// } - -// err = e.ClosePosition(symbol, closeAmt, side, *bApiUserInfo, "MARKET", "", decimal.Zero) -// if err != nil { -// return err -// } -// orm.Model(&DbModels.LinePreOrder{}).Where("api_id = ? AND symbol = ?", bApiUserInfo.Id, symbol).Updates(map[string]interface{}{ -// "status": "6", -// }) -// } -// } -// return nil -// } - // 获取合约 持仓价格、数量 // symbol:交易对 // side:方向 diff --git a/services/binanceservice/futuresjudgeservice.go b/services/binanceservice/futuresjudgeservice.go index 935da5c..0c42fed 100644 --- a/services/binanceservice/futuresjudgeservice.go +++ b/services/binanceservice/futuresjudgeservice.go @@ -277,9 +277,10 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes } else if ok { defer lock.Release() takeOrders := make([]DbModels.LinePreOrder, 0) - if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type IN (1,2,4) AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil { + //只取消减仓单 止盈止损减仓成功后取消 + 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 { log.Error("查询止盈单失败") - return false + // return false } var hasrecord bool diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index c0c3ed1..cec7f4e 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -158,6 +158,8 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return } + //取消委托中的止盈止损 + cancelTakeProfitByReduce(db, apiUserInfo, preOrder.Symbol, preOrder.MainId, preOrder.SymbolType) 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 { @@ -179,6 +181,36 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } +// 减仓成功后取消止盈止损 +func cancelTakeProfitByReduce(db *gorm.DB, apiUserInfo DbModels.LineApiUser, symbol string, mainId int, symbolType int) { + orders, err := GetSymbolTakeAndStop(db, mainId, symbolType) + futApi := FutRestApi{} + + if err != nil { + logger.Errorf("mainId:%d 获取委托中的止盈止损失败:%v", mainId, err) + } + + orderSns := make([]string, 0) + + for _, v := range orders { + if v.OrderType != 1 && v.OrderType != 2 { + continue + } + + orderSns = append(orderSns, v.OrderSn) + } + + arr := utility.SplitSlice(orderSns, 10) + + for _, v := range arr { + err := futApi.CancelBatchFutOrder(apiUserInfo, symbol, v) + + if err != nil { + logger.Errorf("mainId:%d 取消止盈止损失败:%v", mainId, err) + } + } +} + // 减仓处理止盈止损 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 { diff --git a/services/binanceservice/orderservice.go b/services/binanceservice/orderservice.go index ae3d7ba..9b22840 100644 --- a/services/binanceservice/orderservice.go +++ b/services/binanceservice/orderservice.go @@ -134,7 +134,7 @@ func GetTotalLossAmount(db *gorm.DB, mainId int) (decimal.Decimal, error) { return totalLossAmountU, nil } -// 获取交易对的 委托中的止盈止损 +// 获取交易对的 委托中的止盈止损、减仓单 // mainId 主单id // symbolType 交易对类型 func GetSymbolTakeAndStop(db *gorm.DB, mainId int, symbolType int) ([]models.LinePreOrder, error) { diff --git a/services/binanceservice/strategy_order_service.go b/services/binanceservice/strategy_order_service.go index 0b9e504..4593a05 100644 --- a/services/binanceservice/strategy_order_service.go +++ b/services/binanceservice/strategy_order_service.go @@ -12,9 +12,11 @@ import ( models2 "go-admin/models" "go-admin/pkg/utility" "go-admin/services/cacheservice" + "strings" "time" "github.com/bytedance/sonic" + "github.com/go-admin-team/go-admin-core/logger" "github.com/go-admin-team/go-admin-core/sdk/service" "github.com/shopspring/decimal" ) @@ -95,8 +97,16 @@ func (e *BinanceStrategyOrderService) JudgeStrategy(order dto.StrategyOrderRedis } score := lastPrices[0].Score - startPrice := utility.StrToDecimal(beforePrice) - lastPrice := utility.StrToDecimal(lastPrices[0].Member.(string)) + var startPrice decimal.Decimal + var lastPrice decimal.Decimal + startPriceArgs := strings.Split(beforePrice, ":") + endPricecArgs := strings.Split(lastPrices[0].Member.(string), ":") + + if len(startPriceArgs) == 0 || len(endPricecArgs) == 0 { + return result, errors.New("获取交易对起止价格失败") + } + startPrice = utility.StrToDecimal(startPriceArgs[len(startPriceArgs)-1]) + lastPrice = utility.StrToDecimal(endPricecArgs[len(endPricecArgs)-1]) //时间差超过10s if (nowUtc-int64(score))/1000 > 10 { @@ -188,33 +198,81 @@ func (e *BinanceStrategyOrderService) RecalculateOrder(tradeSet models2.TradeSet mainOrder.SignPrice = lastPrice.String() mainOrder.Price = newPrice.String() mainOrder.Num = totalNum.String() + remainQuantity := totalNum for index := range mainOrder.Childs { - //主单止盈 - if mainOrder.Child[index].OrderType == 1 { - var ext models.LinePreOrderExt + var ext models.LinePreOrderExt + for _, v := range exts { + if v.OrderId == mainOrder.Child[index].Id { + ext = v + break + } + } + if ext.Id <= 0 { + logger.Errorf("子订单ext不存在 id:%d", mainOrder.Child[index].Id) + continue + } - for _, v := range exts { - if v.OrderId == mainOrder.Child[index].Id { - ext = v - break - } + //主单止盈、止损 + if mainOrder.Child[index].Pid == mainOrder.Child[index].MainId && (mainOrder.Child[index].OrderType == 1 || mainOrder.Child[index].OrderType == 2) { + var percent decimal.Decimal + + switch { + // 加价 + case mainOrder.Child[index].OrderType == 1 && mainOrder.Site == "BUY", mainOrder.Child[index].OrderType == 2 && mainOrder.Site == "SELL": + percent = decimal.NewFromInt(100).Add(ext.TakeProfitRatio) + //减价 + case mainOrder.Child[index].OrderType == 2 && mainOrder.Site == "BUY", mainOrder.Child[index].OrderType == 1 && mainOrder.Site == "SELL": + percent = decimal.NewFromInt(100).Sub(ext.StopLossRatio) } - if ext.Id > 0 { - var percent decimal.Decimal - //多 - if mainOrder.Site == "BUY" { - percent = decimal.NewFromInt(100).Add(ext.TakeProfitRatio) - } else { - percent = decimal.NewFromInt(100).Sub(ext.StopLossRatio) - } + childPrice := lastPrice.Mul(percent.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.PriceDigit)) + mainOrder.Child[index].Price = childPrice.String() + mainOrder.Child[index].Num = totalNum.String() - childPrice := lastPrice.Mul(percent.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.PriceDigit)) - mainOrder.Child[index].Price = childPrice.String() - } } else { //todo 重新计算 + lastNum := remainQuantity + + //过期时间 + if ext.ExpirateHour <= 0 { + mainOrder.Child[index].ExpireTime = time.Now().AddDate(10, 0, 0) + } else { + mainOrder.Child[index].ExpireTime = time.Now().Add(time.Hour * time.Duration(ext.ExpirateHour)) + } + + switch { + //加仓单 + case mainOrder.Child[index].OrderType == 1 && mainOrder.Child[index].OrderCategory == 3: + var percentage decimal.Decimal + + if mainOrder.Site == "BUY" { + percentage = decimal.NewFromInt(1).Sub(ext.PriceRatio.Div(decimal.NewFromInt(100))) + } else { + percentage = decimal.NewFromInt(1).Add(ext.PriceRatio.Div(decimal.NewFromInt(100))) + } + + dataPrice := utility.StrToDecimal(mainOrder.Price).Mul(percentage).Truncate(int32(tradeSet.PriceDigit)) + mainOrder.Child[index].Price = dataPrice.String() + + if ext.AddPositionType == 1 { + buyPrice := utility.StrToDecimal(mainOrder.BuyPrice).Mul(utility.SafeDiv(ext.AddPositionVal, decimal.NewFromInt(100))).Truncate(2) + mainOrder.Child[index].Num = utility.SafeDiv(buyPrice, dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + + } else { + mainOrder.Child[index].Num = utility.SafeDiv(ext.AddPositionVal.Truncate(2), dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + } + + //加库存 + lastNum = lastNum.Add(utility.StrToDecimal(mainOrder.Child[index].Num)) + //todo 计算子订单 + //减仓单 + case mainOrder.Child[index].OrderType == 4: + // todo 计算 + //减库存 + lastNum = lastNum.Add(utility.StrToDecimal(mainOrder.Child[index].Num)) + //todo 计算子订单 + } } } return nil diff --git a/services/futureservice/binancemarket.go b/services/futureservice/binancemarket.go index b8094e2..fd5afc9 100644 --- a/services/futureservice/binancemarket.go +++ b/services/futureservice/binancemarket.go @@ -162,14 +162,19 @@ func handleTickerAllMessage(msg []byte) { } } - //行情存储时间 - lastUtc := utcTime - 1000*60*60 - if _, err := helper.DefaultRedis.RemoveBeforeScore(lastPriceKey, float64(lastUtc)); err != nil { - log.Errorf("移除 合约交易对:%s %d之前的最新成交价失败,err:%v", symbol, lastUtc, err) - } + val, _ := helper.DefaultRedis.GetAllList(rediskey.CacheSymbolLastPrice) - if err := helper.DefaultRedis.AddSortSet(lastPriceKey, float64(utcTime), lastPrice.String()); err != nil { - log.Errorf("添加 合约交易对:%s %d之前的最新成交价失败,err:%v", symbol, lastUtc, err) + if utility.ContainsStr(val, symbol) { + //行情存储时间 + lastUtc := utcTime - 1000*60*60 + content := fmt.Sprintf("%d:%s", utcTime, lastPrice.String()) + if _, err := helper.DefaultRedis.RemoveBeforeScore(lastPriceKey, float64(lastUtc)); err != nil { + log.Errorf("移除 合约交易对:%s %d之前的最新成交价失败,err:%v", symbol, lastUtc, err) + } + + if err := helper.DefaultRedis.AddSortSet(lastPriceKey, float64(utcTime), content); err != nil { + log.Errorf("添加 合约交易对:%s %d之前的最新成交价失败,err:%v", symbol, lastUtc, err) + } } } diff --git a/services/spotservice/binancemarket.go b/services/spotservice/binancemarket.go index 8759ad1..267cdd4 100644 --- a/services/spotservice/binancemarket.go +++ b/services/spotservice/binancemarket.go @@ -250,15 +250,20 @@ func handleTickerMessage(msg []byte) { } } - //行情存储时间 - lastUtc := utcTime - 1000*60*60 + val, _ := helper.DefaultRedis.GetAllList(rediskey.CacheSymbolLastPrice) - if _, err := helper.DefaultRedis.RemoveBeforeScore(lastPriceKey, float64(lastUtc)); err != nil { - log.Errorf("移除 现货交易对:%s %d之前的最新成交价失败,err:%v", symbolName, lastUtc, err) - } + if utility.ContainsStr(val, symbolName) { + //行情存储时间 + lastUtc := utcTime - 1000*60*60 + content := fmt.Sprintf("%d:%s", utcTime, lastPrice.String()) - if err := helper.DefaultRedis.AddSortSet(lastPriceKey, float64(utcTime), lastPrice.String()); err != nil { - log.Errorf("添加 现货交易对:%s %d之前的最新成交价失败,err:%v", symbolName, lastUtc, err) + if _, err := helper.DefaultRedis.RemoveBeforeScore(lastPriceKey, float64(lastUtc)); err != nil { + log.Errorf("移除 现货交易对:%s %d之前的最新成交价失败,err:%v", symbolName, lastUtc, err) + } + + if err := helper.DefaultRedis.AddSortSet(lastPriceKey, float64(utcTime), content); err != nil { + log.Errorf("添加 现货交易对:%s %d之前的最新成交价失败,err:%v", symbolName, lastUtc, err) + } } }