1、合约止盈止损单在减仓单成交之后再取消
This commit is contained in:
		| @ -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() | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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:方向 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -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) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user