This commit is contained in:
2025-03-11 15:51:40 +08:00
parent ddba8e4ce2
commit ffb12cdb64
5 changed files with 80 additions and 38 deletions

View File

@ -437,13 +437,15 @@ func (req LineBatchAddPreOrderReq) CheckParams() error {
return errors.New("止盈价格不正确")
}
if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 {
if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 &&
v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 &&
(v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) {
return errors.New("止盈后止盈价格不正确")
}
if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 {
return errors.New("止盈后止损价格不正确")
}
// if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 {
// return errors.New("止盈后止损价格不正确")
// }
}
return nil
@ -582,6 +584,7 @@ type ClosePosition struct {
Rate string `json:"rate"` //限价平仓百分比
CloseType int `json:"close_type"` //现货平仓=1 合约平仓=2
ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type
Side string `json:"side"` //SELL=卖出(空) BUY=买入(多)
}
func (m *ClosePosition) CheckParams() error {

View File

@ -443,8 +443,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
if req.PricePattern == "percentage" {
AddOrder.Rate = req.Price
orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10%
priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1
orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10%
priceRate := utility.SafeDiv(orderPrice, (decimal.NewFromInt(100))) //下单价除100 =0.1
if strings.ToUpper(req.Site) == "BUY" { //购买方向
//实际下单价格
@ -486,12 +486,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
//div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365
//在换算成对应交易对对应的价值
//LTCBTC --> LTCUSDT == LTCUSDT -- 100.502
div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
div := utility.SafeDiv(utility.SafeDiv(tickerPrice, decimal.NewFromInt(1)), uTickerPrice)
//计算下单数量
AddOrder.Num = buyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String()
AddOrder.Num = utility.SafeDiv(buyPrice, div).Truncate(int32(tradeSet.AmountDigit)).String()
} else {
fromString, _ := decimal.NewFromString(AddOrder.Price)
AddOrder.Num = buyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String()
AddOrder.Num = utility.SafeDiv(buyPrice, fromString).Truncate(int32(tradeSet.AmountDigit)).String()
}
if utility.StringToFloat64(AddOrder.Num) < tradeSet.MinQty {
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 小于最小下单数量", id, req.Symbol))
@ -534,11 +534,14 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
StopLossRatio: req.ReduceStopLossRatio,
}
mainPrice := utility.StringToDecimal(AddOrder.Price)
mainAmount := buyPrice.Div(mainPrice)
mainAmount := utility.SafeDiv(buyPrice, mainPrice)
defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit))
defultExt2.TotalBefore = defultExt.TotalAfter
defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit))
defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2)
// if decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Cmp(decimal.Zero) > 0 {
defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(utility.SafeDiv(req.ReduceNumRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit))
defultExt2.ReTakeRatio = utility.SafeDiv(req.ReducePriceRatio, utility.SafeDiv(decimal.NewFromInt(100).Sub(req.ReduceNumRatio), decimal.NewFromInt(100))).Truncate(2)
preOrderExts = append(preOrderExts, defultExt)
preOrderExts = append(preOrderExts, defultExt2)
@ -644,9 +647,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
profitOrder.MainId = AddOrder.Id
if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 {
numPercent := req.ProfitNumRatio.Div(decimal.NewFromInt(100))
numPercent := utility.SafeDiv(req.ProfitNumRatio, decimal.NewFromInt(100))
profitOrder.Num = utility.StrToDecimal(profitOrder.Num).Mul(numPercent).Truncate(int32(tradeSet.AmountDigit)).String()
}
tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder)
@ -668,10 +670,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 {
if strings.ToUpper(req.Site) == "BUY" {
stopOrder.Site = "SELL"
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
} else {
stopOrder.Site = "BUY"
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
}
stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
stopOrder.Pid = AddOrder.Id
@ -807,12 +809,12 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx
data.Price = dataPrice.String()
if v.AddPositionType == 1 {
buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(v.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(2)
data.Num = buyPrice.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String()
buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(utility.SafeDiv(v.AddPositionVal, decimal.NewFromInt(100))).Truncate(2)
data.Num = utility.SafeDiv(buyPrice, dataPrice).Truncate(int32(tradeSet.AmountDigit)).String()
data.BuyPrice = buyPrice.String()
} else {
data.BuyPrice = v.AddPositionVal.Truncate(2).String()
data.Num = v.AddPositionVal.Truncate(2).Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String()
data.Num = utility.SafeDiv(v.AddPositionVal.Truncate(2), dataPrice).Truncate(int32(tradeSet.AmountDigit)).String()
}
return data
@ -886,7 +888,7 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO
if ext.TakeProfitNumRatio.Cmp(decimal.Zero) <= 0 || ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) >= 0 {
profitOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String()
} else {
profitOrder.Num = ext.TotalAfter.Mul(ext.TakeProfitNumRatio).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)).String()
profitOrder.Num = ext.TotalAfter.Mul(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String()
}
// 止盈需要累加之前的亏损
@ -969,7 +971,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line
}
if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100)))
percent := decimal.NewFromInt(1).Sub(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100)))
num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit))
}
@ -1473,7 +1475,7 @@ func (e *LinePreOrder) GetFutSpotOrderInfo(req dto.ManuallyCover, symbol, orderT
func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPrice decimal.Decimal, amountDigit int, notUsdt bool, symbolInfo models.LineSymbol) (decimal.Decimal, error) {
var amt decimal.Decimal
if req.CoverType == 1 {
decimalValue := utility.StringToDecimal(req.Value).Div(decimal.NewFromInt(100))
decimalValue := utility.SafeDiv(utility.StringToDecimal(req.Value), decimal.NewFromInt(100))
amt = totalNum.Mul(decimalValue)
} else {
decimalValue := utility.StringToDecimal(req.Value)
@ -1495,8 +1497,8 @@ func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPric
for _, symbolMap := range tickerSymbolMaps {
if symbolMap.Symbol == strings.ToUpper(symbolInfo.QuoteAsset+"USDT") {
uTickerPrice, _ := decimal.NewFromString(symbolMap.Price)
div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
amt = decimalValue.Div(div)
div := utility.SafeDiv(tickerPrice, decimal.NewFromInt(1).Div(uTickerPrice))
amt = utility.SafeDiv(decimalValue, div)
break
}
}
@ -1571,7 +1573,7 @@ func (e *LinePreOrder) SpotClosePosition(position *dto.ClosePosition, errs *[]er
"newClientOrderId": utility.Int64ToString(snowflakehelper.GetOrderId()),
"timeInForce": "GTC",
}
price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
paramsMaps["price"] = price.String()
} else {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 下单数量小于最小下单数量", position.ApiId)))
@ -1630,9 +1632,27 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err
//查询已经开仓的合约交易对
var futList []models.LinePreOrder
if position.Symbol == "" {
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId).Find(&futList)
query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId)
switch strings.ToUpper(position.Side) {
case "BUY":
query = query.Where("site = 'BUY'")
case "SELL":
query = query.Where("site = 'SELL'")
}
query.Find(&futList)
} else {
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol).Find(&futList)
query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol)
switch strings.ToUpper(position.Side) {
case "BUY":
query = query.Where("side = 'BUY'")
case "SELL":
query = query.Where("side = 'SELL'")
}
query.Find(&futList)
}
if len(futList) <= 0 {
*errs = append(*errs, fmt.Errorf("api_id:%d 没有可平仓的交易对", position.ApiId))
@ -1677,10 +1697,10 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err
if list.Site == "BUY" && risk.PositionSide == "LONG" { //做多
//根据仓位数量去平多
orderSide = "SELL"
price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
} else if list.Site == "SELL" && risk.PositionSide == "SHORT" {
orderSide = "BUY"
price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
}
if price.LessThanOrEqual(decimal.Zero) {
@ -1726,6 +1746,9 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err
//撤销合约的委托(根据方向撤)
orderSns, _ := binanceservice.GetOpenOrderSns(e.Orm, []int{list.Id})
if len(orderSns) > 0 {
logger.Infof("平仓 取消 订单id %v 订单号: %v", orderSns)
}
api.CancelBatchFutOrder(apiUserInfo, list.Symbol, orderSns)
// api.CancelAllFutOrder(apiUserInfo, list.Symbol)
//side=BUY&positionSide=LONG是开多

View File

@ -87,3 +87,16 @@ func DiscardDecimal(value decimal.Decimal, discardDigits int32) decimal.Decimal
return value
}
// SafeDiv 安全除法
// dividend: 被除数
// divisor: 除数
func SafeDiv(dividend, divisor decimal.Decimal) decimal.Decimal {
var result decimal.Decimal
if dividend.Cmp(decimal.Zero) != 0 && divisor.Cmp(decimal.Zero) != 0 {
result = dividend.Div(divisor)
}
return result
}

View File

@ -303,12 +303,12 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn)
removePosition(db, preOrder)
futApi := FutRestApi{}
apiUserInfo, _ := GetApiInfo(preOrder.ApiId)
if apiUserInfo.Id > 0 {
if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil {
logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err)
mainIds := []int{preOrder.MainId}
if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil {
logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err)
}
}

View File

@ -1,17 +1,19 @@
package scriptservice
import (
"github.com/bytedance/sonic"
log "github.com/go-admin-team/go-admin-core/logger"
sysservice "github.com/go-admin-team/go-admin-core/sdk/service"
"go-admin/app/admin/models"
"go-admin/app/admin/service"
"go-admin/app/admin/service/dto"
"go-admin/common/const/rediskey"
"go-admin/common/helper"
"gorm.io/gorm"
"go-admin/pkg/utility"
"strings"
"sync"
"github.com/bytedance/sonic"
log "github.com/go-admin-team/go-admin-core/logger"
sysservice "github.com/go-admin-team/go-admin-core/sdk/service"
"gorm.io/gorm"
)
type PreOrder struct {
@ -23,8 +25,9 @@ func (receiver *PreOrder) AddOrder(orm *gorm.DB) {
var wg sync.WaitGroup
for i := 1; i <= GoroutineNum; i++ {
wg.Add(1)
go workerWithLock(orm, &wg)
utility.SafeGo(func() {
workerWithLock(orm, &wg)
})
}
wg.Wait()
}