1
This commit is contained in:
@ -10,6 +10,7 @@ import (
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
models2 "go-admin/models"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/pkg/utility/snowflakehelper"
|
||||
"strconv"
|
||||
@ -157,6 +158,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
}
|
||||
|
||||
price := utility.StrToDecimal(preOrder.Price)
|
||||
parentOrder, _ := GetOrderById(db, preOrder.Pid)
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
rate := utility.StringAsFloat(preOrder.Rate)
|
||||
ext := models.LinePreOrderExt{}
|
||||
@ -165,34 +167,11 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
|
||||
// 不是100%减仓 就需要挂止盈止损
|
||||
if rate < 100 {
|
||||
client := GetClient(&apiUserInfo)
|
||||
|
||||
resp, _, err := client.SendSpotAuth("/api/v3/account", "GET", map[string]interface{}{
|
||||
"omitZeroBalances": true,
|
||||
})
|
||||
|
||||
num, err := getSpotPositionNum(apiUserInfo, preOrder, tradeSet)
|
||||
if err != nil {
|
||||
logger.Errorf("api_id:%d 交易对:%s 查询用户信息失败:%v", apiUserInfo.Id, preOrder.Symbol, err)
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
num = utility.StrToDecimal(item.Free).Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
takeProfitOrder := models.LinePreOrder{}
|
||||
copier.Copy(&takeProfitOrder, &preOrder)
|
||||
takeProfitOrder.Id = 0
|
||||
@ -253,6 +232,17 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
}
|
||||
}
|
||||
|
||||
//计算实际亏损
|
||||
if parentOrder.Price != "" {
|
||||
parentPrice := utility.StrToDecimal(parentOrder.Price)
|
||||
reduceNum := utility.StrToDecimal(preOrder.Num)
|
||||
lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum).Truncate(2)
|
||||
|
||||
if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil {
|
||||
logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err)
|
||||
}
|
||||
}
|
||||
|
||||
//加仓待触发
|
||||
addPositionOrder := DbModels.LinePreOrder{}
|
||||
|
||||
@ -285,6 +275,38 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取现货持仓数量
|
||||
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
|
||||
}
|
||||
|
||||
// 主单取消
|
||||
func handleMainOrderCancel(preOrder *DbModels.LinePreOrder) {
|
||||
preOrderKey := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE)
|
||||
@ -359,9 +381,13 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) {
|
||||
stoplossVal, _ := helper.DefaultRedis.GetAllList(stoplossKey)
|
||||
addPositionKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE)
|
||||
addPositionVal, _ := helper.DefaultRedis.GetAllList(addPositionKey)
|
||||
reduceKey := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE)
|
||||
reduceVal, _ := helper.DefaultRedis.GetAllList(reduceKey)
|
||||
stoploss := dto.StopLossRedisList{}
|
||||
addPosition := AddPositionList{}
|
||||
reduce := ReduceListItem{}
|
||||
|
||||
//止损缓存
|
||||
for _, v := range stoplossVal {
|
||||
sonic.Unmarshal([]byte(v), &stoploss)
|
||||
if stoploss.MainId == preOrder.MainId {
|
||||
@ -373,6 +399,7 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) {
|
||||
}
|
||||
}
|
||||
|
||||
//加仓缓存
|
||||
for _, v := range addPositionVal {
|
||||
sonic.Unmarshal([]byte(v), &addPosition)
|
||||
if addPosition.MainId == preOrder.MainId {
|
||||
@ -383,6 +410,18 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//减仓缓存
|
||||
for _, v := range reduceVal {
|
||||
sonic.Unmarshal([]byte(v), &reduce)
|
||||
if reduce.MainId == preOrder.MainId {
|
||||
_, err := helper.DefaultRedis.LRem(reduceKey, v)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", preOrder.OrderSn, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 主单成交
|
||||
@ -517,14 +556,28 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r
|
||||
// preOrder 主单
|
||||
func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder) {
|
||||
orders := []models.LinePreOrder{}
|
||||
if err := db.Model(&DbModels.LinePreOrder{}).Where("pid = ? AND order_category = 1 AND order_type > 0 AND status = '0' ", preOrder.Id).Find(&orders).Error; err != nil {
|
||||
tradeSet, _ := GetTradeSet(preOrder.Symbol, 0)
|
||||
|
||||
if tradeSet.Coin == "" {
|
||||
logger.Error("获取交易对失败")
|
||||
return
|
||||
}
|
||||
|
||||
if err := db.Model(&DbModels.LinePreOrder{}).
|
||||
Where("pid = ? AND order_category = 1 AND order_type > 0 AND status = '0' ", preOrder.Id).
|
||||
Find(&orders).Error; err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Error("订单回调查询止盈止损单失败:", err)
|
||||
return
|
||||
} else if len(orders) == 0 && preOrder.OrderCategory == 3 {
|
||||
orders, err = makeSpotTakeAndReduce(preOrder, db, tradeSet, orders)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
spotApi := SpotRestApi{}
|
||||
num, _ := decimal.NewFromString(preOrder.Num)
|
||||
// num = num.Mul(decimal.NewFromFloat(0.998))
|
||||
|
||||
for i, order := range orders {
|
||||
if i >= 2 { // 最多处理 2 个订单
|
||||
@ -540,10 +593,123 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
|
||||
processTakeProfitOrder(db, spotApi, order, num)
|
||||
case 2: // 止损
|
||||
processStopLossOrder(order)
|
||||
case 4: //减仓
|
||||
processSpotReduceOrder(order, num)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建现货止盈、减仓单
|
||||
func makeSpotTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet, orders []DbModels.LinePreOrder) ([]DbModels.LinePreOrder, error) {
|
||||
ext := models.LinePreOrderExt{}
|
||||
apiInfo, err := GetApiInfo(preOrder.ApiId)
|
||||
price := utility.StrToDecimal(preOrder.Price)
|
||||
|
||||
if apiInfo.Id == 0 {
|
||||
logger.Error("订单回调查询apiuserinfo失败 err:", err)
|
||||
return nil, errors.New("订单回调查询apiuserinfo失败")
|
||||
}
|
||||
|
||||
if err := db.Model(&ext).Where("order_id = ?", preOrder.Id).First(&ext).Error; err != nil {
|
||||
logger.Error("订单回调查询止盈止损单扩展表失败:", err)
|
||||
return nil, errors.New("订单回调查询止盈止损单扩展表失败")
|
||||
}
|
||||
|
||||
totalLossAmountU, _ := GetTotalLossAmount(db, preOrder.MainId)
|
||||
num, err := getSpotPositionNum(apiInfo, preOrder, tradeSet)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("订单回调查询持仓数量失败:", err)
|
||||
return nil, errors.New("订单回调查询持仓数量失败")
|
||||
}
|
||||
|
||||
//止盈单
|
||||
if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 {
|
||||
profitOrder := models.LinePreOrder{}
|
||||
copier.Copy(&profitOrder, preOrder)
|
||||
|
||||
profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
|
||||
profitOrder.Pid = preOrder.Id
|
||||
profitOrder.OrderType = 1
|
||||
profitOrder.Status = 0
|
||||
profitOrder.Rate = ext.TakeProfitRatio.String()
|
||||
profitOrder.MainId = ext.MainOrderId
|
||||
profitOrder.Num = num.String()
|
||||
|
||||
//止盈需要累加之前的亏损
|
||||
if totalLossAmountU.Cmp(decimal.Zero) > 0 {
|
||||
profitOrder.Rate = totalLossAmountU.Div(num).Div(price).Sub(decimal.NewFromInt(1)).Abs().Add(ext.TakeProfitRatio).Truncate(2).String()
|
||||
} else {
|
||||
profitOrder.Rate = ext.TakeProfitRatio.String()
|
||||
}
|
||||
|
||||
orders = append(orders, profitOrder)
|
||||
}
|
||||
|
||||
//减仓单
|
||||
if ext.ReducePriceRatio.Cmp(decimal.Zero) > 0 {
|
||||
stopOrder := models.LinePreOrder{}
|
||||
copier.Copy(&stopOrder, preOrder)
|
||||
|
||||
stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
|
||||
stopOrder.Pid = preOrder.Id
|
||||
stopOrder.MainId = ext.MainOrderId
|
||||
stopOrder.OrderType = 4
|
||||
stopOrder.Status = 0
|
||||
stopOrder.Rate = ext.ReducePriceRatio.String()
|
||||
stopOrder.Num = num.String()
|
||||
|
||||
orders = append(orders, stopOrder)
|
||||
}
|
||||
|
||||
for index := range orders {
|
||||
if strings.ToUpper(preOrder.Site) == "BUY" {
|
||||
orders[index].Site = "SELL"
|
||||
orders[index].Price = utility.StrToDecimal(preOrder.Price).Mul(decimal.NewFromInt(1).Sub(ext.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
orders[index].Site = "BUY"
|
||||
orders[index].Price = utility.StrToDecimal(preOrder.Price).Mul(decimal.NewFromInt(1).Add(ext.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
}
|
||||
|
||||
if len(orders) > 0 {
|
||||
if err := db.Create(&orders).Error; err != nil {
|
||||
logger.Error("主单回调,创建止盈、减仓单失败")
|
||||
return orders, errors.New("主单回调,创建止盈、减仓单失败")
|
||||
}
|
||||
}
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
// 现货减仓
|
||||
func processSpotReduceOrder(preOrder DbModels.LinePreOrder, num decimal.Decimal) {
|
||||
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),
|
||||
Num: num,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 处理止盈订单
|
||||
func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LinePreOrder, num decimal.Decimal) {
|
||||
tradeSet, _ := GetTradeSet(order.Symbol, 0)
|
||||
|
||||
Reference in New Issue
Block a user