624 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			624 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package binanceservice
 | |
| 
 | |
| import (
 | |
| 	"database/sql"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	DbModels "go-admin/app/admin/models"
 | |
| 	"go-admin/common/global"
 | |
| 	"go-admin/common/helper"
 | |
| 	"go-admin/pkg/maphelper"
 | |
| 	"go-admin/services/cacheservice"
 | |
| 	"strconv"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/go-admin-team/go-admin-core/sdk/service"
 | |
| 	"github.com/shopspring/decimal"
 | |
| 	"gorm.io/gorm"
 | |
| )
 | |
| 
 | |
| type ReverseService struct {
 | |
| 	service.Service
 | |
| }
 | |
| 
 | |
| // SaveMainOrder 保存订单信息
 | |
| // mapData: 下单数据
 | |
| // status: 订单状态
 | |
| // orderSn: 自定义订单号
 | |
| func (e *ReverseService) handleSimpleStatusChange(status string, orderSn string, mapData map[string]interface{}) (bool, error) {
 | |
| 	statusMap := map[string]int{
 | |
| 		"NEW":              2,
 | |
| 		"CANCELED":         6,
 | |
| 		"EXPIRED":          7,
 | |
| 		"EXPIRED_IN_MATCH": 7,
 | |
| 	}
 | |
| 	if newStatus, ok := statusMap[status]; ok {
 | |
| 		_ = e.changeOrderStatus(newStatus, orderSn, mapData)
 | |
| 		return false, nil
 | |
| 	}
 | |
| 	return false, fmt.Errorf("不支持的订单状态 %s", status)
 | |
| }
 | |
| 
 | |
| // ReverseOrder 反向下单
 | |
| // apiKey: api key
 | |
| // mapData: 下单数据
 | |
| // return apiInfo, bool
 | |
| // bool: true=需要反单, false=不需要反单
 | |
| func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interface{}) (bool, error) {
 | |
| 	apiInfo := GetApiInfoByKey(apiKey)
 | |
| 
 | |
| 	if apiInfo.Id == 0 {
 | |
| 		e.Log.Errorf("获取apiInfo失败 %s", apiKey)
 | |
| 		return false, nil
 | |
| 	}
 | |
| 
 | |
| 	status, _ := maphelper.GetString(mapData, "X")
 | |
| 	orderSn, err := maphelper.GetString(mapData, "c")
 | |
| 	if err != nil {
 | |
| 		return true, err
 | |
| 	}
 | |
| 	ot, err := maphelper.GetString(mapData, "ot")
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return true, err
 | |
| 	}
 | |
| 
 | |
| 	switch status {
 | |
| 	case "NEW", "CANCELED", "EXPIRED", "EXPIRED_IN_MATCH":
 | |
| 		result, err := e.handleSimpleStatusChange(status, orderSn, mapData)
 | |
| 
 | |
| 		//如果是 新开止盈止损 需要取消反单的止盈止损之后重下反单止盈止损
 | |
| 		if status == "NEW" && (ot == "TAKE_PROFIT_MARKET" || ot == "STOP_LOSS_MARKET" || ot == "TAKE_PROFIT_LIMIT" || ot == "STOP_LOSS_LIMIT") &&
 | |
| 			apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {
 | |
| 
 | |
| 			if err := e.ReTakeOrStopOrder(&mapData, orderSn, &apiInfo); err != nil {
 | |
| 				return true, err
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return result, err
 | |
| 	case "FILLED":
 | |
| 		if apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {
 | |
| 			reverseApiInfo, err := GetApiInfo(apiInfo.ReverseApiId)
 | |
| 			if err != nil {
 | |
| 				e.Log.Errorf("获取反向api信息失败 reverseApiId:%d, err:%v", apiInfo.ReverseApiId, err)
 | |
| 				return true, err
 | |
| 			}
 | |
| 
 | |
| 			mainOrder, err := e.SaveMainOrder(mapData, apiInfo)
 | |
| 			if err != nil {
 | |
| 				return false, err
 | |
| 			}
 | |
| 
 | |
| 			switch {
 | |
| 			case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
 | |
| 				if mainOrder.Category == 0 {
 | |
| 					if err1 := e.savePosition(&mainOrder, reverseApiInfo.Id, true, false, false); err1 != nil {
 | |
| 						return true, err1
 | |
| 					}
 | |
| 
 | |
| 					e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, false, false)
 | |
| 				}
 | |
| 			case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
 | |
| 				if mainOrder.Category == 0 {
 | |
| 					closePosition := maphelper.GetBool(mapData, "R")
 | |
| 					if err1 := e.savePosition(&mainOrder, reverseApiInfo.Id, true, true, closePosition); err1 != nil {
 | |
| 						e.Log.Errorf("保存主订单失败: %v", err1)
 | |
| 						return true, err1
 | |
| 					}
 | |
| 					e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, true, closePosition)
 | |
| 				}
 | |
| 			default:
 | |
| 				return true, errors.New("不支持的订单类型")
 | |
| 			}
 | |
| 			return true, nil
 | |
| 		} else if apiInfo.Subordinate == "2" {
 | |
| 			symbol, err := maphelper.GetString(mapData, "s")
 | |
| 
 | |
| 			if err != nil {
 | |
| 				return true, err
 | |
| 			}
 | |
| 			positionSide, err := maphelper.GetString(mapData, "ps")
 | |
| 
 | |
| 			if err != nil {
 | |
| 				return true, err
 | |
| 			}
 | |
| 			side, err := maphelper.GetString(mapData, "S")
 | |
| 			if err != nil {
 | |
| 				return true, err
 | |
| 			}
 | |
| 
 | |
| 			totalNum := maphelper.GetDecimal(mapData, "z")
 | |
| 
 | |
| 			mainOrder := DbModels.LineReverseOrder{
 | |
| 				ApiId:        apiInfo.Id,
 | |
| 				Symbol:       symbol,
 | |
| 				PositionSide: positionSide,
 | |
| 				TotalNum:     totalNum,
 | |
| 				Side:         side,
 | |
| 			}
 | |
| 			e.changeOrderStatus(3, orderSn, mapData)
 | |
| 
 | |
| 			switch {
 | |
| 			case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
 | |
| 				if mainOrder.Category == 0 {
 | |
| 					if err1 := e.savePosition(&mainOrder, 0, false, false, false); err1 != nil {
 | |
| 						return true, err1
 | |
| 					}
 | |
| 				}
 | |
| 			case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
 | |
| 				if mainOrder.Category == 0 {
 | |
| 					closePosition := maphelper.GetBool(mapData, "R")
 | |
| 					if err1 := e.savePosition(&mainOrder, 0, false, true, closePosition); err1 != nil {
 | |
| 						return true, err1
 | |
| 					}
 | |
| 				}
 | |
| 			default:
 | |
| 				return true, errors.New("不支持的订单类型")
 | |
| 			}
 | |
| 		}
 | |
| 	default:
 | |
| 		return false, fmt.Errorf("不支持的订单状态 %s", status)
 | |
| 	}
 | |
| 
 | |
| 	return false, nil
 | |
| }
 | |
| 
 | |
| // 修改订单状态
 | |
| // status: 订单状态 1-待下单 2-已下单 3-已成交 6-已取消 7-已过期
 | |
| func (e *ReverseService) changeOrderStatus(status int, orderSn string, mapData map[string]interface{}) error {
 | |
| 	data := map[string]interface{}{"status": status, "updated_at": time.Now()}
 | |
| 
 | |
| 	if status == 3 {
 | |
| 		now := time.Now()
 | |
| 		if orderId, ok := mapData["i"].(float64); ok {
 | |
| 			data["order_id"] = orderId
 | |
| 		}
 | |
| 
 | |
| 		if ap, ok := mapData["ap"].(bool); ok {
 | |
| 			data["final_price"] = ap
 | |
| 		}
 | |
| 
 | |
| 		if num, ok := mapData["z"].(string); ok {
 | |
| 			data["total_num"], _ = decimal.NewFromString(num)
 | |
| 		}
 | |
| 
 | |
| 		data["trigger_time"] = &now
 | |
| 	}
 | |
| 
 | |
| 	db := e.Orm.Model(&DbModels.LineReverseOrder{}).
 | |
| 		Where("order_sn =? and status != 3", orderSn).
 | |
| 		Updates(data)
 | |
| 
 | |
| 	if db.Error != nil {
 | |
| 		e.Log.Errorf("修改订单状态失败 orderSn:%s, err:%v", orderSn, db.Error)
 | |
| 	}
 | |
| 
 | |
| 	if db.RowsAffected == 0 {
 | |
| 		e.Log.Errorf("修改订单状态失败 orderSn:%s, 未找到订单", orderSn)
 | |
| 	}
 | |
| 	return db.Error
 | |
| }
 | |
| 
 | |
| // 先保存主单,持仓信息 必须用双向持仓!
 | |
| func (e *ReverseService) SaveMainOrder(mapData map[string]interface{}, apiInfo DbModels.LineApiUser) (DbModels.LineReverseOrder, error) {
 | |
| 	now := time.Now()
 | |
| 	symbol, err := maphelper.GetString(mapData, "s")
 | |
| 	var reverseOrder DbModels.LineReverseOrder
 | |
| 	if err != nil {
 | |
| 		return reverseOrder, err
 | |
| 	}
 | |
| 
 | |
| 	orderSn, err := maphelper.GetString(mapData, "c")
 | |
| 	if err != nil {
 | |
| 		return reverseOrder, err
 | |
| 	}
 | |
| 
 | |
| 	side, err := maphelper.GetString(mapData, "S")
 | |
| 	if err != nil {
 | |
| 		return reverseOrder, err
 | |
| 	}
 | |
| 
 | |
| 	positionSide, err := maphelper.GetString(mapData, "ps")
 | |
| 	if err != nil {
 | |
| 		return reverseOrder, err
 | |
| 	}
 | |
| 
 | |
| 	mainType, _ := maphelper.GetString(mapData, "ot")
 | |
| 
 | |
| 	reverseOrder = DbModels.LineReverseOrder{
 | |
| 		ApiId:         apiInfo.Id,
 | |
| 		Category:      0,
 | |
| 		OrderSn:       orderSn,
 | |
| 		TriggerTime:   &now,
 | |
| 		Status:        3,
 | |
| 		Symbol:        symbol,
 | |
| 		FollowOrderSn: "",
 | |
| 		Type:          mainType,
 | |
| 		Price:         maphelper.GetDecimal(mapData, "p"),
 | |
| 		FinalPrice:    maphelper.GetDecimal(mapData, "ap"),
 | |
| 		TotalNum:      maphelper.GetDecimal(mapData, "z"),
 | |
| 		Side:          side,
 | |
| 		PositionSide:  positionSide,
 | |
| 	}
 | |
| 
 | |
| 	reverseOrder.PriceU = reverseOrder.Price
 | |
| 
 | |
| 	if !reverseOrder.Price.IsZero() && !reverseOrder.TotalNum.IsZero() {
 | |
| 		reverseOrder.BuyPrice = reverseOrder.Price.Mul(reverseOrder.TotalNum)
 | |
| 	}
 | |
| 
 | |
| 	if id, err := maphelper.GetFloat64(mapData, "i"); err == nil {
 | |
| 		reverseOrder.OrderId = strconv.FormatFloat(id, 'f', -1, 64)
 | |
| 	}
 | |
| 
 | |
| 	orderType, _ := maphelper.GetString(mapData, "ot")
 | |
| 	switch orderType {
 | |
| 	case "LIMIT", "MARKET":
 | |
| 		reverseOrder.OrderType = 0
 | |
| 	case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
 | |
| 		reverseOrder.OrderType = 1
 | |
| 	case "STOP_MARKET", "STOP", "TRAILING_STOP_MARKET":
 | |
| 		reverseOrder.OrderType = 2
 | |
| 	default:
 | |
| 		return reverseOrder, fmt.Errorf("不支持的订单类型: %s", orderType)
 | |
| 	}
 | |
| 
 | |
| 	if reverseOrder.PositionSide == "BOTH" {
 | |
| 		return reverseOrder, errors.New("不支持的持仓类型,必须为双向持仓")
 | |
| 	}
 | |
| 
 | |
| 	if err := e.Orm.Create(&reverseOrder).Error; err != nil {
 | |
| 		e.Log.Errorf("保存主单失败:%v", err)
 | |
| 		return reverseOrder, err
 | |
| 	}
 | |
| 
 | |
| 	return reverseOrder, nil
 | |
| }
 | |
| 
 | |
| // 更新仓位信息
 | |
| // closePosition: true=平仓, false=减仓
 | |
| func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, reverseApiId int, isMain, reducePosition, closePosition bool) error {
 | |
| 	position := DbModels.LineReversePosition{}
 | |
| 	positionSide := reverseOrder.PositionSide
 | |
| 	side := reverseOrder.Side
 | |
| 	totalNum := reverseOrder.TotalNum
 | |
| 
 | |
| 	var querySql string
 | |
| 	sqlStr := ""
 | |
| 
 | |
| 	//如果是主单,存储仓位则是反单的持仓方向
 | |
| 	if isMain {
 | |
| 		if reverseOrder.PositionSide == "LONG" {
 | |
| 			positionSide = "SHORT"
 | |
| 		} else {
 | |
| 			positionSide = "LONG"
 | |
| 		}
 | |
| 
 | |
| 		if !reducePosition {
 | |
| 			//反单止盈止损方向相反
 | |
| 			if side == "SELL" {
 | |
| 				side = "BUY"
 | |
| 			} else {
 | |
| 				side = "SELL"
 | |
| 			}
 | |
| 
 | |
| 			position.ReverseApiId = reverseApiId
 | |
| 			position.Side = side
 | |
| 			position.ApiId = reverseOrder.ApiId
 | |
| 			position.Symbol = reverseOrder.Symbol
 | |
| 			position.Status = 1
 | |
| 			position.ReverseStatus = 0
 | |
| 			position.PositionSide = positionSide
 | |
| 		}
 | |
| 		querySql = "api_id =? and position_side =? and symbol =? and status =1"
 | |
| 
 | |
| 		//平仓
 | |
| 		if closePosition {
 | |
| 			totalNum = decimal.Zero
 | |
| 			sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2 where id =@id and status!=2 "
 | |
| 		} else if reducePosition {
 | |
| 			//只减仓
 | |
| 			sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now() where id =@id and status!=2 "
 | |
| 		} else {
 | |
| 			sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now() where id =@id and status!=2"
 | |
| 		}
 | |
| 	} else {
 | |
| 		querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"
 | |
| 
 | |
| 		if closePosition {
 | |
| 			totalNum = decimal.Zero
 | |
| 			sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2 where id =@id and reverse_status !=2"
 | |
| 		} else if reducePosition {
 | |
| 			sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now() where id =@id and reverse_status !=2"
 | |
| 		} else {
 | |
| 			sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1 where id =@id and reverse_status !=2"
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	err := e.Orm.Transaction(func(tx *gorm.DB) error {
 | |
| 		err1 := tx.Model(&position).Where(querySql,
 | |
| 			reverseOrder.ApiId, positionSide, reverseOrder.Symbol).First(&position).Error
 | |
| 
 | |
| 		if err1 != nil {
 | |
| 			//主单仓位不存在,创建新仓位
 | |
| 			if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition {
 | |
| 
 | |
| 				if err2 := tx.Create(&position).Error; err2 != nil {
 | |
| 					return err2
 | |
| 				}
 | |
| 			} else {
 | |
| 				return err1
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id))
 | |
| 		if dbResult.Error != nil {
 | |
| 			return dbResult.Error
 | |
| 		}
 | |
| 
 | |
| 		if dbResult.RowsAffected == 0 {
 | |
| 			e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, reverseOrder)
 | |
| 			return errors.New("没有找到对应的持仓信息")
 | |
| 		}
 | |
| 
 | |
| 		return nil
 | |
| 	})
 | |
| 
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // 反向下单
 | |
| // mainOrder: 主单信息
 | |
| // reverseApiId: 反向apiId
 | |
| // orderProportion: 反向下单比例
 | |
| // reducePosition: 是否减仓
 | |
| // closePosition: 是否平仓
 | |
| func (e *ReverseService) DoAddReverseOrder(mainOrder *DbModels.LineReverseOrder, reverseApiInfo *DbModels.LineApiUser, orderProportion decimal.Decimal, reducePosition, closePosition bool) error {
 | |
| 	order := DbModels.LineReverseOrder{}
 | |
| 	order.ApiId = reverseApiInfo.Id
 | |
| 	order.Category = 1
 | |
| 	order.OrderSn = helper.GetOrderNo()
 | |
| 	order.FollowOrderSn = mainOrder.OrderSn
 | |
| 	order.Symbol = mainOrder.Symbol
 | |
| 	order.OrderType = 0
 | |
| 
 | |
| 	switch mainOrder.PositionSide {
 | |
| 	case "LONG":
 | |
| 		order.PositionSide = "SHORT"
 | |
| 	case "SHORT":
 | |
| 		order.PositionSide = "LONG"
 | |
| 	}
 | |
| 
 | |
| 	switch mainOrder.Side {
 | |
| 	case "SELL":
 | |
| 		order.Side = "BUY"
 | |
| 	case "BUY":
 | |
| 		order.Side = "SELL"
 | |
| 	default:
 | |
| 		return fmt.Errorf("不支持的订单类型 side:%s", mainOrder.Side)
 | |
| 	}
 | |
| 
 | |
| 	if reducePosition && closePosition {
 | |
| 		order.OrderType = 4
 | |
| 	} else if reducePosition {
 | |
| 		order.OrderType = 3
 | |
| 	}
 | |
| 
 | |
| 	symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, mainOrder.Symbol, 1)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		e.Log.Errorf("获取交易对信息失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	setting, err := cacheservice.GetReverseSetting(e.Orm)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		e.Log.Errorf("获取反单设置失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	signPrice, _ := decimal.NewFromString(symbol.LastPrice)
 | |
| 	price := signPrice.Truncate(int32(symbol.PriceDigit))
 | |
| 
 | |
| 	if price.Cmp(decimal.Zero) <= 0 {
 | |
| 		e.Log.Errorf("获取最新价格失败 symbol:%s custom:%s 单价小于0", mainOrder.Symbol, mainOrder.OrderSn)
 | |
| 		return errors.New("获取最新价格失败")
 | |
| 	}
 | |
| 
 | |
| 	var percent decimal.Decimal
 | |
| 	switch {
 | |
| 	case order.PositionSide == "LONG" && order.Side == "BUY", order.PositionSide == "SHORT" && order.Side == "BUY":
 | |
| 		percent = decimal.NewFromInt(100).Add(setting.ReversePremiumRatio)
 | |
| 	case order.PositionSide == "SHORT" && order.Side == "SELL", order.PositionSide == "LONG" && order.Side == "SELL":
 | |
| 		percent = decimal.NewFromInt(100).Sub(setting.ReversePremiumRatio)
 | |
| 	default:
 | |
| 		return fmt.Errorf("不支持的订单类型 ps:%s, side:%s", order.PositionSide, order.Side)
 | |
| 	}
 | |
| 
 | |
| 	percent = percent.Div(decimal.NewFromInt(100)).Truncate(4)
 | |
| 	//计算溢价单价
 | |
| 	price = price.Mul(percent).Truncate(int32(symbol.PriceDigit))
 | |
| 	var amount decimal.Decimal
 | |
| 
 | |
| 	//平仓单直接卖出全部数量
 | |
| 	if closePosition {
 | |
| 		var position DbModels.LineReversePosition
 | |
| 		if err1 := e.Orm.Model(position).
 | |
| 			Where("position_side =? and reverse_api_id =? and  reverse_status =1", order.PositionSide, order.ApiId).
 | |
| 			Select("reverse_amount").
 | |
| 			Find(&position).Error; err1 != nil {
 | |
| 			e.Log.Errorf("获取剩余仓位失败 symbol:%s custom:%s :%v", order.Symbol, order.OrderSn, err1)
 | |
| 
 | |
| 			if len(err1.Error()) < 255 {
 | |
| 				order.Remark = err1.Error()
 | |
| 			} else {
 | |
| 				order.Remark = err1.Error()[:254]
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		amount = position.ReverseAmount
 | |
| 
 | |
| 		if amount.IsZero() {
 | |
| 			order.Remark = "没有持仓数量"
 | |
| 		}
 | |
| 	} else {
 | |
| 		proportion := decimal.NewFromInt(100)
 | |
| 
 | |
| 		if orderProportion.Cmp(decimal.Zero) > 0 {
 | |
| 			proportion = orderProportion
 | |
| 		}
 | |
| 
 | |
| 		//反向下单百分比
 | |
| 		proportion = proportion.Div(decimal.NewFromInt(100)).Truncate(2)
 | |
| 		amount = mainOrder.BuyPrice.Mul(proportion).Div(price).Truncate(int32(symbol.AmountDigit))
 | |
| 
 | |
| 		if amount.Cmp(decimal.Zero) <= 0 {
 | |
| 			e.Log.Errorf("计算数量失败 symbol:%s custom:%s 数量小于0", mainOrder.Symbol, mainOrder.OrderSn)
 | |
| 			return errors.New("计算数量失败")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	order.TotalNum = amount
 | |
| 	order.Price = price
 | |
| 	order.PriceU = price
 | |
| 	order.BuyPrice = amount.Mul(price).Truncate(int32(symbol.PriceDigit))
 | |
| 	order.Status = 1
 | |
| 	order.Type = setting.ReverseOrderType
 | |
| 	order.SignPrice = signPrice
 | |
| 
 | |
| 	if order.Remark != "" {
 | |
| 		order.Status = 8
 | |
| 	}
 | |
| 
 | |
| 	if err := e.Orm.Model(&order).Create(&order).Error; err != nil {
 | |
| 		e.Log.Errorf("保存反单失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if order.Status == 1 {
 | |
| 		e.DoBianceOrder(&order, reverseApiInfo, &setting, reducePosition, closePosition)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // 处理币安订单
 | |
| // order: 反单信息
 | |
| // apiInfo: 币安api信息
 | |
| func (e *ReverseService) DoBianceOrder(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, setting *DbModels.LineReverseSetting, reducePosition, closePosition bool) error {
 | |
| 	futApiV2 := FuturesResetV2{Service: e.Service}
 | |
| 	orderType := setting.ReverseOrderType
 | |
| 
 | |
| 	if orderType == "" {
 | |
| 		orderType = "LIMIT"
 | |
| 	}
 | |
| 
 | |
| 	params := FutOrderPlace{
 | |
| 		ApiId:            apiInfo.Id,
 | |
| 		Symbol:           order.Symbol,
 | |
| 		PositionSide:     order.PositionSide,
 | |
| 		Side:             order.Side,
 | |
| 		OrderType:        orderType,
 | |
| 		Quantity:         order.TotalNum,
 | |
| 		Price:            order.Price,
 | |
| 		NewClientOrderId: order.OrderSn,
 | |
| 	}
 | |
| 
 | |
| 	err := futApiV2.OrderPlaceLoop(apiInfo, params)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", order.Symbol, order.OrderSn, err)
 | |
| 		remark := err.Error()
 | |
| 
 | |
| 		if len(remark) > 255 {
 | |
| 			remark = remark[:254]
 | |
| 		}
 | |
| 
 | |
| 		if err1 := e.Orm.Model(&order).Where("id =? and status !=3", order.Id).Updates(map[string]interface{}{"status": 8, "remark": remark, "updated_at": time.Now()}).Error; err1 != nil {
 | |
| 			e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", err1)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // 重下止盈止损
 | |
| // mapData: 主单止盈止损回调
 | |
| func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser) error {
 | |
| 	symbol, err := maphelper.GetString(*mapData, "s")
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	side, err := maphelper.GetString(*mapData, "S")
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	ot, err := maphelper.GetString(*mapData, "ot")
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	//反单止盈止损方向相反
 | |
| 	if side == "SELL" {
 | |
| 		side = "BUY"
 | |
| 	} else {
 | |
| 		side = "SELL"
 | |
| 	}
 | |
| 
 | |
| 	positionSide, err := maphelper.GetString(*mapData, "ps")
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if positionSide == "LONG" {
 | |
| 		positionSide = "SHORT"
 | |
| 	} else {
 | |
| 		positionSide = "LONG"
 | |
| 	}
 | |
| 
 | |
| 	apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		e.Log.Errorf("根据主单api获取反单api失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	var reverseOrder DbModels.LineReverseOrder
 | |
| 	e.Orm.Model(&DbModels.LineReverseOrder{}).
 | |
| 		Where("symbol =? and api_id =? and position_side =? and side = ? and status =1", symbol, mainApiInfo.ReverseApiId, positionSide, side).
 | |
| 		First(&reverseOrder)
 | |
| 
 | |
| 	//取消旧止盈止损
 | |
| 	if reverseOrder.OrderSn != "" {
 | |
| 		futApi := FutRestApi{}
 | |
| 		err := futApi.CancelFutOrderRetry(apiInfo, symbol, reverseOrder.OrderSn)
 | |
| 
 | |
| 		if err != nil {
 | |
| 			e.Log.Errorf("币安撤单失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	params := FutOrderPlace{
 | |
| 		ApiId:            apiInfo.Id,
 | |
| 		Symbol:           symbol,
 | |
| 		PositionSide:     positionSide,
 | |
| 		Side:             side,
 | |
| 		OrderType:        ot,
 | |
| 		Quantity:         reverseOrder.TotalNum,
 | |
| 		Price:            reverseOrder.Price,
 | |
| 		NewClientOrderId: reverseOrder.OrderSn,
 | |
| 	}
 | |
| 	futApiV2 := FuturesResetV2{Service: e.Service}
 | |
| 	futApiV2.OrderPlace(&apiInfo, params)
 | |
| 	return nil
 | |
| }
 |