1暂时提交
This commit is contained in:
@ -479,3 +479,13 @@ func GetOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 回去反单默认配置
|
||||
func GetReverseSetting(db *gorm.DB) (DbModels.LineReverseSetting, error) {
|
||||
var setting DbModels.LineReverseSetting
|
||||
if err := db.Model(&DbModels.LineReverseSetting{}).First(&setting).Error; err != nil {
|
||||
return setting, err
|
||||
}
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
@ -599,6 +599,49 @@ func (e FutRestApi) GetHoldeData(apiInfo *DbModels.LineApiUser, symbol, side str
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取合约 持仓价格、数量
|
||||
// symbol:交易对
|
||||
// positionSide:持仓方向
|
||||
// holdeData:持仓数据
|
||||
func (e FutRestApi) GetPositionData(apiInfo *DbModels.LineApiUser, symbol, positionSide string, holdeData *HoldeData) error {
|
||||
opts := retryhelper.DefaultRetryOptions()
|
||||
opts.RetryableErrFn = func(err error) bool {
|
||||
if strings.Contains(err.Error(), "LOT_SIZE") {
|
||||
return false
|
||||
}
|
||||
//重试
|
||||
return true
|
||||
}
|
||||
|
||||
holdes, err := retryhelper.RetryWithResult(func() ([]PositionRisk, error) {
|
||||
return e.GetPositionV3(apiInfo, symbol)
|
||||
}, opts)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range holdes {
|
||||
positionAmount, _ := decimal.NewFromString(item.PositionAmt)
|
||||
if (positionSide == "LONG" && item.PositionSide == "BOTH" && positionAmount.Cmp(decimal.Zero) > 0) || item.PositionSide == positionSide { //多
|
||||
holdeData.AveragePrice, _ = decimal.NewFromString(item.EntryPrice)
|
||||
holdeData.TotalQuantity = positionAmount.Abs()
|
||||
continue
|
||||
} else if (positionSide == "SHORT" && item.PositionSide == "BOTH" && positionAmount.Cmp(decimal.Zero) < 0) || item.PositionSide == positionSide { //空
|
||||
holdeData.AveragePrice, _ = decimal.NewFromString(item.EntryPrice)
|
||||
holdeData.TotalQuantity = positionAmount.Abs()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if holdeData.AveragePrice.Cmp(decimal.Zero) == 0 {
|
||||
holdesVal, _ := sonic.MarshalString(&holdes)
|
||||
log.Error("均价错误 symbol:", symbol, " 数据:", holdesVal)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取代币持仓信息
|
||||
func getSymbolHolde(e FutRestApi, apiInfo *DbModels.LineApiUser, symbol string, side string, holdeData *HoldeData) ([]PositionRisk, error) {
|
||||
holdes, err := e.GetPositionV3(apiInfo, symbol)
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
@ -108,26 +109,36 @@ func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interfac
|
||||
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 {
|
||||
needReverseOrder, closePosition, err1 := e.savePosition(&mainOrder, &apiInfo, true, false)
|
||||
|
||||
if err1 != nil {
|
||||
return true, err1
|
||||
}
|
||||
|
||||
e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, false, false)
|
||||
if needReverseOrder {
|
||||
e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, false, closePosition)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
needReverseOrder, closePosition, err1 := e.savePosition(&mainOrder, &apiInfo, true, true)
|
||||
|
||||
if err1 != nil {
|
||||
e.Log.Errorf("保存主订单失败: %v", err1)
|
||||
return true, err1
|
||||
}
|
||||
e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, true, closePosition)
|
||||
|
||||
if needReverseOrder {
|
||||
e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, true, closePosition)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return true, errors.New("不支持的订单类型")
|
||||
}
|
||||
return true, nil
|
||||
} else if apiInfo.Subordinate == "2" {
|
||||
e.changeOrderStatus(3, orderSn, mapData)
|
||||
|
||||
symbol, err := maphelper.GetString(mapData, "s")
|
||||
|
||||
if err != nil {
|
||||
@ -153,20 +164,19 @@ func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interfac
|
||||
TotalNum: totalNum,
|
||||
Side: side,
|
||||
Price: price,
|
||||
FinalPrice: price,
|
||||
}
|
||||
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 {
|
||||
if _, _, err1 := e.savePosition(&mainOrder, &apiInfo, false, false); err1 != nil {
|
||||
return true, err1
|
||||
}
|
||||
}
|
||||
case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
|
||||
case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SrgetELL":
|
||||
if mainOrder.Category == 0 {
|
||||
closePosition := maphelper.GetBool(mapData, "R")
|
||||
if err1 := e.savePosition(&mainOrder, 0, false, true, closePosition); err1 != nil {
|
||||
if _, _, err1 := e.savePosition(&mainOrder, &apiInfo, false, true); err1 != nil {
|
||||
return true, err1
|
||||
}
|
||||
}
|
||||
@ -298,17 +308,23 @@ func (e *ReverseService) SaveMainOrder(mapData map[string]interface{}, apiInfo D
|
||||
}
|
||||
|
||||
// 更新仓位信息
|
||||
// apiInfo: 当前下单api信息
|
||||
// return
|
||||
// neeReverseOrder: 是否需要反单
|
||||
// closePosition: true=平仓, false=减仓
|
||||
func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, reverseApiId int, isMain, reducePosition, closePosition bool) error {
|
||||
// err error 错误信息
|
||||
func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, isMain, reducePosition bool) (bool, bool, error) {
|
||||
position := DbModels.LineReversePosition{}
|
||||
positionSide := reverseOrder.PositionSide
|
||||
side := reverseOrder.Side
|
||||
totalNum := reverseOrder.TotalNum
|
||||
positionSide := order.PositionSide
|
||||
side := order.Side
|
||||
totalNum := order.TotalNum
|
||||
closePosition := false
|
||||
needReverseOrder := false
|
||||
|
||||
symbol, err1 := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reverseOrder.Symbol, 1)
|
||||
symbol, err1 := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
|
||||
|
||||
if err1 != nil {
|
||||
e.Log.Errorf("获取交易对失败 symbol:%s err:%v", reverseOrder.Symbol, err1)
|
||||
e.Log.Errorf("获取交易对失败 symbol:%s err:%v", order.Symbol, err1)
|
||||
}
|
||||
|
||||
var querySql string
|
||||
@ -316,12 +332,15 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
|
||||
//如果是主单,存储仓位则是反单的持仓方向
|
||||
if isMain {
|
||||
if reverseOrder.PositionSide == "LONG" {
|
||||
if order.PositionSide == "LONG" {
|
||||
positionSide = "SHORT"
|
||||
} else {
|
||||
positionSide = "LONG"
|
||||
}
|
||||
|
||||
//减仓 判断是否为平仓
|
||||
closePosition = e.getClosePosition(reducePosition, apiInfo, order, order.PositionSide, isMain)
|
||||
|
||||
if !reducePosition {
|
||||
//反单止盈止损方向相反
|
||||
if side == "SELL" {
|
||||
@ -330,14 +349,14 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
side = "SELL"
|
||||
}
|
||||
|
||||
position.ReverseApiId = reverseApiId
|
||||
position.ReverseApiId = apiInfo.ReverseApiId
|
||||
position.Side = side
|
||||
position.ApiId = reverseOrder.ApiId
|
||||
position.Symbol = reverseOrder.Symbol
|
||||
position.ApiId = order.ApiId
|
||||
position.Symbol = order.Symbol
|
||||
position.Status = 1
|
||||
position.ReverseStatus = 0
|
||||
position.PositionSide = positionSide
|
||||
position.AveragePrice = reverseOrder.FinalPrice
|
||||
position.AveragePrice = order.FinalPrice
|
||||
position.PositionNo = snowflakehelper.GetOrderNo()
|
||||
}
|
||||
querySql = "api_id =? and position_side =? and symbol =? and status =1"
|
||||
@ -354,6 +373,8 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
}
|
||||
} else {
|
||||
querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"
|
||||
//减仓 判断是否为平仓
|
||||
closePosition = e.getClosePosition(reducePosition, apiInfo, order, order.PositionSide, isMain)
|
||||
|
||||
if closePosition {
|
||||
totalNum = decimal.Zero
|
||||
@ -366,10 +387,11 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
}
|
||||
|
||||
var averagePrice decimal.Decimal
|
||||
var remainQuantity decimal.Decimal
|
||||
|
||||
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||
err1 := tx.Model(&position).Where(querySql,
|
||||
reverseOrder.ApiId, positionSide, reverseOrder.Symbol).First(&position).Error
|
||||
order.ApiId, positionSide, order.Symbol).First(&position).Error
|
||||
|
||||
if err1 != nil {
|
||||
//主单仓位不存在,创建新仓位
|
||||
@ -397,12 +419,12 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
|
||||
//加仓
|
||||
if !reducePosition {
|
||||
totalPrice = totalPrice.Add(reverseOrder.Price.Mul(reverseOrder.TotalNum))
|
||||
totalAmount = totalAmount.Add(reverseOrder.TotalNum)
|
||||
totalPrice = totalPrice.Add(order.Price.Mul(order.TotalNum))
|
||||
totalAmount = totalAmount.Add(order.TotalNum)
|
||||
} else if reducePosition && !closePosition {
|
||||
//只减仓
|
||||
totalPrice = totalPrice.Sub(reverseOrder.Price.Mul(reverseOrder.TotalNum))
|
||||
totalAmount = totalAmount.Sub(reverseOrder.TotalNum)
|
||||
totalPrice = totalPrice.Sub(order.Price.Mul(order.TotalNum))
|
||||
totalAmount = totalAmount.Sub(order.TotalNum)
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,7 +433,7 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
averagePrice = position.AveragePrice
|
||||
} else {
|
||||
if position.ReverseAveragePrice.IsZero() {
|
||||
averagePrice = reverseOrder.Price
|
||||
averagePrice = order.Price
|
||||
} else {
|
||||
averagePrice = position.ReverseAveragePrice
|
||||
}
|
||||
@ -431,7 +453,7 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
}
|
||||
|
||||
//关联订单的仓位id
|
||||
if err2 := tx.Exec("UPDATE line_reverse_order set position_id=@positionId where id=@orderId and position_id = 0", sql.Named("positionId", position.Id), sql.Named("orderId", reverseOrder.Id)).Error; err2 != nil {
|
||||
if err2 := tx.Exec("UPDATE line_reverse_order set position_id=@positionId where id=@orderId and position_id = 0", sql.Named("positionId", position.Id), sql.Named("orderId", order.Id)).Error; err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
@ -440,16 +462,63 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
||||
return dbResult.Error
|
||||
}
|
||||
|
||||
reverseOrder.PositionId = position.Id
|
||||
order.PositionId = position.Id
|
||||
if dbResult.RowsAffected == 0 {
|
||||
e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, reverseOrder)
|
||||
e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, order)
|
||||
return errors.New("没有找到对应的持仓信息")
|
||||
}
|
||||
|
||||
if reducePosition && !isMain {
|
||||
remainQuantity = position.ReverseAmount.Sub(totalNum)
|
||||
} else if !isMain {
|
||||
remainQuantity = position.ReverseAmount.Add(totalNum)
|
||||
}
|
||||
|
||||
//主单且对手单没有平仓
|
||||
if isMain && position.ReverseStatus != 2 {
|
||||
needReverseOrder = true
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
if !isMain && !closePosition {
|
||||
e.doDefaultTakeStop(order, apiInfo, remainQuantity)
|
||||
} else if !isMain && closePosition {
|
||||
//取消剩余的委托
|
||||
e.DoCancelTakeAndStop(order.Symbol, position.PositionSide, apiInfo)
|
||||
}
|
||||
|
||||
return needReverseOrder, closePosition, err
|
||||
}
|
||||
|
||||
// 获取是否为平仓状态
|
||||
func (e *ReverseService) getClosePosition(reducePosition bool, apiInfo *DbModels.LineApiUser, order *DbModels.LineReverseOrder, positionSide string, isMain bool) bool {
|
||||
closePosition := false
|
||||
|
||||
if reducePosition {
|
||||
futApi := FutRestApi{}
|
||||
holdData := HoldeData{}
|
||||
err := futApi.GetPositionData(apiInfo, order.Symbol, positionSide, &holdData)
|
||||
|
||||
if err != nil {
|
||||
e.Log.Errorf("获取剩余持仓信息失败 symbol:%s err:%v", order.Symbol, err)
|
||||
lastPosition := DbModels.LineReversePosition{}
|
||||
|
||||
if err2 := e.Orm.Model(&DbModels.LineReversePosition{}).Where("position_side =? and symbol =? and status =1", positionSide, order.Symbol).First(&lastPosition).Error; err2 != nil {
|
||||
e.Log.Errorf("获取上一次持仓信息失败 symbol:%s err:%v", order.Symbol, err2)
|
||||
} else if isMain && lastPosition.Amount.Cmp(order.TotalNum) <= 0 {
|
||||
//如果剩余仓位小于等于
|
||||
closePosition = true
|
||||
} else if !isMain && lastPosition.ReverseAmount.Cmp(order.TotalNum) <= 0 {
|
||||
closePosition = true
|
||||
}
|
||||
} else if holdData.TotalQuantity.IsZero() {
|
||||
closePosition = true
|
||||
}
|
||||
}
|
||||
|
||||
return closePosition
|
||||
}
|
||||
|
||||
// 反向下单
|
||||
@ -556,8 +625,11 @@ func (e *ReverseService) DoAddReverseOrder(mainOrder *DbModels.LineReverseOrder,
|
||||
|
||||
//反向下单百分比
|
||||
proportion = proportion.Div(decimal.NewFromInt(100)).Truncate(4)
|
||||
|
||||
amount = mainOrder.TotalNum.Mul(proportion).Truncate(int32(symbol.AmountDigit))
|
||||
|
||||
logger.Info("反向下单比例 %d ,原始数量:%d,反向下单数量:%d", proportion, mainOrder.TotalNum, amount)
|
||||
|
||||
if amount.Cmp(decimal.Zero) <= 0 {
|
||||
e.Log.Errorf("计算数量失败 symbol:%s custom:%s 数量小于0", mainOrder.Symbol, mainOrder.OrderSn)
|
||||
return errors.New("计算数量失败")
|
||||
@ -620,6 +692,24 @@ func (e *ReverseService) DoCancelTakeProfitBatch(symbol, positionSide, side stri
|
||||
return nil
|
||||
}
|
||||
|
||||
// 取消止盈和止损单
|
||||
func (e *ReverseService) DoCancelTakeAndStop(symbol, positionSide string, apiInfo *DbModels.LineApiUser) error {
|
||||
var orderSns []string
|
||||
|
||||
e.Orm.Model(&DbModels.LineReverseOrder{}).Where("symbol =? and position_side =? and status =2", symbol, positionSide).Pluck("order_sn", &orderSns)
|
||||
|
||||
if len(orderSns) > 0 {
|
||||
futApi := FutRestApi{}
|
||||
|
||||
if err := futApi.CancelBatchFutOrderLoop(*apiInfo, symbol, orderSns); err != nil {
|
||||
e.Log.Errorf("币安撤单失败 symbol:%s custom:%v :%v", symbol, orderSns, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取止盈止损订单
|
||||
// symbol: 交易对
|
||||
// positionSide: 持仓方向
|
||||
@ -681,41 +771,92 @@ func (e *ReverseService) DoBianceOrder(order *DbModels.LineReverseOrder, apiInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理默认止盈止损
|
||||
// order: 订单信息
|
||||
// apiInfo: api信息
|
||||
// Optimized Reverse Order Handling
|
||||
// File: reverse_order_handler.go
|
||||
|
||||
func (e *ReverseService) doDefaultTakeStop(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, totalNum decimal.Decimal) error {
|
||||
if totalNum.LessThanOrEqual(decimal.Zero) {
|
||||
return nil
|
||||
}
|
||||
|
||||
orders, err := e.getActiveReverseOrders(order.PositionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(orders) == 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
setting, err := GetReverseSetting(e.Orm)
|
||||
if err != nil {
|
||||
e.Log.Errorf("获取反单设置失败:%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
|
||||
if err != nil {
|
||||
e.Log.Errorf("获取交易对信息失败:%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
side := e.getOppositeSide(order.Side)
|
||||
lastPrice, _ := decimal.NewFromString(symbol.LastPrice)
|
||||
now := time.Now()
|
||||
|
||||
types := []struct {
|
||||
Enabled bool
|
||||
CheckTypes []string
|
||||
OrderType string
|
||||
Ratio decimal.Decimal
|
||||
IsTakeProfit bool
|
||||
}{
|
||||
{!setting.TakeProfitRatio.IsZero(), []string{"TAKE_PROFIT_MARKET", "TAKE_PROFIT"}, "TAKE_PROFIT_MARKET", setting.TakeProfitRatio, true},
|
||||
{!setting.StopLossRatio.IsZero(), []string{"STOP_MARKET", "STOP"}, "STOP_MARKET", setting.StopLossRatio, false},
|
||||
}
|
||||
|
||||
for _, t := range types {
|
||||
if t.Enabled && !e.hasOrderType(orders, t.CheckTypes...) {
|
||||
price := e.calculatePrice(order.PositionSide, order.FinalPrice, t.Ratio, t.IsTakeProfit)
|
||||
err := e.createReverseOrder(CreateOrderParams{
|
||||
Order: order,
|
||||
ApiInfo: apiInfo,
|
||||
Symbol: &symbol,
|
||||
Side: side,
|
||||
OrderType: t.OrderType,
|
||||
Price: price,
|
||||
TotalNum: totalNum,
|
||||
Now: now,
|
||||
LastPrice: lastPrice,
|
||||
Close: true,
|
||||
PositionId: order.PositionId,
|
||||
})
|
||||
if err != nil {
|
||||
e.Log.Errorf("止盈止损下单失败:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 重下止盈止损
|
||||
// mapData: 主单止盈止损回调
|
||||
// mapData:
|
||||
func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser, symbol *models.TradeSet) error {
|
||||
orderType := 0 //订单类型 1-止盈 2-止损
|
||||
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"
|
||||
}
|
||||
|
||||
close := maphelper.GetBool(*mapData, "cp")
|
||||
stopPrice := maphelper.GetDecimal(*mapData, "sp")
|
||||
if stopPrice.IsZero() {
|
||||
@ -723,12 +864,19 @@ func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orde
|
||||
return err
|
||||
}
|
||||
apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)
|
||||
|
||||
if err != nil {
|
||||
e.Log.Errorf("根据主单api获取反单api失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
return err
|
||||
}
|
||||
|
||||
side = e.getOppositeSide(side)
|
||||
if positionSide == "LONG" {
|
||||
positionSide = "SHORT"
|
||||
} else {
|
||||
positionSide = "LONG"
|
||||
}
|
||||
|
||||
var orderType int
|
||||
switch ot {
|
||||
case "STOP_MARKET", "STOP":
|
||||
orderType = 2
|
||||
@ -739,26 +887,19 @@ func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orde
|
||||
}
|
||||
|
||||
var reversePosition DbModels.LineReversePosition
|
||||
|
||||
e.Orm.Model(&reversePosition).
|
||||
Where("symbol =? and reverse_api_id =? and position_side =? and reverse_status =1", symbol.GetSymbol(), apiInfo.Id, positionSide).
|
||||
First(&reversePosition)
|
||||
|
||||
if reversePosition.Id == 0 {
|
||||
e.Log.Errorf("获取反单持仓失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
return err
|
||||
}
|
||||
|
||||
mainPercent := decimal.NewFromInt(1)
|
||||
|
||||
if !stopPrice.IsZero() && !reversePosition.AveragePrice.IsZero() {
|
||||
mainPercent = stopPrice.Div(reversePosition.AveragePrice)
|
||||
mainPercent = (mainPercent.Sub(decimal.NewFromInt(1))).Abs().Truncate(4)
|
||||
}
|
||||
mainPercent := stopPrice.Div(reversePosition.AveragePrice)
|
||||
mainPercent = (mainPercent.Sub(decimal.NewFromInt(1))).Abs().Truncate(4)
|
||||
|
||||
var percent decimal.Decimal
|
||||
switch {
|
||||
//做多止损
|
||||
case orderType == 2 && positionSide == "LONG", orderType == 1 && positionSide == "SHORT":
|
||||
percent = decimal.NewFromInt(1).Sub(mainPercent)
|
||||
case orderType == 2 && positionSide == "SHORT", orderType == 1 && positionSide == "LONG":
|
||||
@ -770,55 +911,286 @@ func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orde
|
||||
now := time.Now()
|
||||
price := reversePosition.AveragePrice.Mul(percent).Truncate(int32(symbol.PriceDigit))
|
||||
lastPrice, _ := decimal.NewFromString(symbol.LastPrice)
|
||||
newOrder := DbModels.LineReverseOrder{
|
||||
PositionId: reversePosition.Id,
|
||||
OrderSn: helper.GetOrderNo(),
|
||||
OrderType: orderType,
|
||||
Status: 1,
|
||||
Price: price,
|
||||
TotalNum: reversePosition.TotalReverseAmount,
|
||||
Symbol: symbol.GetSymbol(),
|
||||
Side: side,
|
||||
PositionSide: positionSide,
|
||||
FollowOrderSn: orderSn,
|
||||
Type: ot,
|
||||
SignPrice: lastPrice,
|
||||
Category: 1,
|
||||
ApiId: apiInfo.Id,
|
||||
IsAddPosition: 2,
|
||||
TriggerTime: &now,
|
||||
BuyPrice: reversePosition.TotalReverseAmount.Mul(price).Truncate(int32(symbol.PriceDigit)),
|
||||
}
|
||||
|
||||
if err1 := e.Orm.Create(&newOrder).Error; err1 != nil {
|
||||
e.Log.Errorf("保存反单止盈止损失败 symbol:%s custom:%s :%v", symbol, orderSn, err1)
|
||||
return err1
|
||||
}
|
||||
params := FutOrderPlace{
|
||||
ApiId: apiInfo.Id,
|
||||
Symbol: symbol.GetSymbol(),
|
||||
PositionSide: positionSide,
|
||||
Side: side,
|
||||
OrderType: ot,
|
||||
Quantity: reversePosition.TotalReverseAmount,
|
||||
Price: price,
|
||||
StopPrice: price,
|
||||
Profit: price,
|
||||
NewClientOrderId: newOrder.OrderSn,
|
||||
ClosePosition: close,
|
||||
}
|
||||
futApiV2 := FuturesResetV2{Service: e.Service}
|
||||
err = futApiV2.OrderPlaceLoop(&apiInfo, params)
|
||||
return e.createReverseOrder(CreateOrderParams{
|
||||
ApiInfo: &apiInfo,
|
||||
Symbol: symbol,
|
||||
Side: side,
|
||||
OrderType: ot,
|
||||
Price: price,
|
||||
TotalNum: reversePosition.TotalReverseAmount,
|
||||
Now: now,
|
||||
LastPrice: lastPrice,
|
||||
Close: close,
|
||||
PositionId: reversePosition.Id,
|
||||
OrderSn: orderSn,
|
||||
PositionSide: positionSide,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *ReverseService) getActiveReverseOrders(positionId int) ([]DbModels.LineReverseOrder, error) {
|
||||
var orders []DbModels.LineReverseOrder
|
||||
err := e.Orm.Model(&DbModels.LineReverseOrder{}).
|
||||
Where("position_id =? and status =2", positionId).
|
||||
Select("id,position_side,side,type").Find(&orders).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", symbol.GetSymbol(), orderSn, err)
|
||||
e.Log.Errorf("获取订单信息失败:%v", err)
|
||||
}
|
||||
return orders, err
|
||||
}
|
||||
|
||||
if err1 := e.Orm.Model(&newOrder).Updates(map[string]interface{}{"status": 8, "remark": err.Error(), "updated_at": time.Now()}).Error; err1 != nil {
|
||||
e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", newOrder.Symbol, newOrder.OrderSn, err1)
|
||||
func (e *ReverseService) getOppositeSide(side string) string {
|
||||
if side == "SELL" {
|
||||
return "BUY"
|
||||
}
|
||||
return "SELL"
|
||||
}
|
||||
|
||||
func (e *ReverseService) hasOrderType(orders []DbModels.LineReverseOrder, types ...string) bool {
|
||||
typeSet := make(map[string]bool)
|
||||
for _, t := range types {
|
||||
typeSet[t] = true
|
||||
}
|
||||
for _, o := range orders {
|
||||
if typeSet[o.Type] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ReverseService) calculatePrice(positionSide string, base decimal.Decimal, ratio decimal.Decimal, isTakeProfit bool) decimal.Decimal {
|
||||
adjust := decimal.NewFromInt(100)
|
||||
if positionSide == "LONG" {
|
||||
if isTakeProfit {
|
||||
return base.Mul(adjust.Add(ratio).Div(adjust)).Truncate(4)
|
||||
}
|
||||
return base.Mul(adjust.Sub(ratio).Div(adjust)).Truncate(4)
|
||||
}
|
||||
if isTakeProfit {
|
||||
return base.Mul(adjust.Sub(ratio).Div(adjust)).Truncate(4)
|
||||
}
|
||||
return base.Mul(adjust.Add(ratio).Div(adjust)).Truncate(4)
|
||||
}
|
||||
|
||||
type CreateOrderParams struct {
|
||||
ApiInfo *DbModels.LineApiUser
|
||||
Order *DbModels.LineReverseOrder
|
||||
Symbol *models.TradeSet
|
||||
Side string
|
||||
OrderType string
|
||||
Price decimal.Decimal
|
||||
TotalNum decimal.Decimal
|
||||
Now time.Time
|
||||
LastPrice decimal.Decimal
|
||||
Close bool
|
||||
PositionId int
|
||||
OrderSn string
|
||||
PositionSide string
|
||||
}
|
||||
|
||||
func (e *ReverseService) createReverseOrder(params CreateOrderParams) error {
|
||||
orderSn := params.OrderSn
|
||||
if orderSn == "" {
|
||||
orderSn = helper.GetOrderNo()
|
||||
}
|
||||
if params.PositionSide == "" && params.Order != nil {
|
||||
params.PositionSide = params.Order.PositionSide
|
||||
}
|
||||
newOrder := DbModels.LineReverseOrder{
|
||||
PositionId: params.PositionId,
|
||||
OrderSn: orderSn,
|
||||
OrderType: getOrderType(params.OrderType),
|
||||
Status: 1,
|
||||
Price: params.Price,
|
||||
TotalNum: params.TotalNum,
|
||||
Symbol: params.Symbol.GetSymbol(),
|
||||
Side: params.Side,
|
||||
PositionSide: params.PositionSide,
|
||||
FollowOrderSn: params.OrderSn,
|
||||
Type: params.OrderType,
|
||||
SignPrice: params.LastPrice,
|
||||
Category: 1,
|
||||
ApiId: params.ApiInfo.Id,
|
||||
IsAddPosition: 2,
|
||||
TriggerTime: ¶ms.Now,
|
||||
BuyPrice: params.TotalNum.Mul(params.Price).Truncate(int32(params.Symbol.PriceDigit)),
|
||||
}
|
||||
|
||||
if err := e.Orm.Create(&newOrder).Error; err != nil {
|
||||
e.Log.Errorf("保存反单止盈止损失败 symbol:%s custom:%s :%v", params.Symbol.GetSymbol(), newOrder.OrderSn, err)
|
||||
return err
|
||||
}
|
||||
|
||||
e.DoCancelTakeProfitBatch(symbol.GetSymbol(), positionSide, side, orderType, &apiInfo)
|
||||
futApiV2 := FuturesResetV2{Service: e.Service}
|
||||
params2 := FutOrderPlace{
|
||||
ApiId: params.ApiInfo.Id,
|
||||
Symbol: params.Symbol.GetSymbol(),
|
||||
PositionSide: newOrder.PositionSide,
|
||||
Side: newOrder.Side,
|
||||
OrderType: newOrder.Type,
|
||||
Quantity: newOrder.TotalNum,
|
||||
Price: newOrder.Price,
|
||||
StopPrice: newOrder.Price,
|
||||
Profit: newOrder.Price,
|
||||
NewClientOrderId: newOrder.OrderSn,
|
||||
ClosePosition: params.Close,
|
||||
}
|
||||
err := futApiV2.OrderPlaceLoop(params.ApiInfo, params2)
|
||||
if err != nil {
|
||||
e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", params.Symbol.GetSymbol(), newOrder.OrderSn, err)
|
||||
e.Orm.Model(&newOrder).Updates(map[string]interface{}{"status": 8, "remark": err.Error(), "updated_at": time.Now()})
|
||||
return err
|
||||
}
|
||||
e.DoCancelTakeProfitBatch(params.Symbol.GetSymbol(), newOrder.PositionSide, newOrder.Side, newOrder.OrderType, params.ApiInfo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOrderType(t string) int {
|
||||
if t == "TAKE_PROFIT_MARKET" || t == "TAKE_PROFIT" {
|
||||
return 1
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
// // 重下止盈止损
|
||||
// // mapData: 主单止盈止损回调
|
||||
// func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser, symbol *models.TradeSet) error {
|
||||
// orderType := 0 //订单类型 1-止盈 2-止损
|
||||
// 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"
|
||||
// }
|
||||
|
||||
// close := maphelper.GetBool(*mapData, "cp")
|
||||
// stopPrice := maphelper.GetDecimal(*mapData, "sp")
|
||||
// if stopPrice.IsZero() {
|
||||
// e.Log.Errorf("获取止盈止损单触发价失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
// return err
|
||||
// }
|
||||
// apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)
|
||||
|
||||
// if err != nil {
|
||||
// e.Log.Errorf("根据主单api获取反单api失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
// return err
|
||||
// }
|
||||
|
||||
// switch ot {
|
||||
// case "STOP_MARKET", "STOP":
|
||||
// orderType = 2
|
||||
// case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
|
||||
// orderType = 1
|
||||
// default:
|
||||
// return fmt.Errorf("不支持的订单类型 ot:%s", ot)
|
||||
// }
|
||||
|
||||
// var reversePosition DbModels.LineReversePosition
|
||||
|
||||
// e.Orm.Model(&reversePosition).
|
||||
// Where("symbol =? and reverse_api_id =? and position_side =? and reverse_status =1", symbol.GetSymbol(), apiInfo.Id, positionSide).
|
||||
// First(&reversePosition)
|
||||
|
||||
// if reversePosition.Id == 0 {
|
||||
// e.Log.Errorf("获取反单持仓失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
// return err
|
||||
// }
|
||||
|
||||
// mainPercent := decimal.NewFromInt(1)
|
||||
|
||||
// if !stopPrice.IsZero() && !reversePosition.AveragePrice.IsZero() {
|
||||
// mainPercent = stopPrice.Div(reversePosition.AveragePrice)
|
||||
// mainPercent = (mainPercent.Sub(decimal.NewFromInt(1))).Abs().Truncate(4)
|
||||
// }
|
||||
|
||||
// var percent decimal.Decimal
|
||||
// switch {
|
||||
// //做多止损
|
||||
// case orderType == 2 && positionSide == "LONG", orderType == 1 && positionSide == "SHORT":
|
||||
// percent = decimal.NewFromInt(1).Sub(mainPercent)
|
||||
// case orderType == 2 && positionSide == "SHORT", orderType == 1 && positionSide == "LONG":
|
||||
// percent = decimal.NewFromInt(1).Add(mainPercent)
|
||||
// default:
|
||||
// return fmt.Errorf("不支持的订单类型 ot:%s, ps:%s", ot, positionSide)
|
||||
// }
|
||||
|
||||
// now := time.Now()
|
||||
// price := reversePosition.AveragePrice.Mul(percent).Truncate(int32(symbol.PriceDigit))
|
||||
// lastPrice, _ := decimal.NewFromString(symbol.LastPrice)
|
||||
// newOrder := DbModels.LineReverseOrder{
|
||||
// PositionId: reversePosition.Id,
|
||||
// OrderSn: helper.GetOrderNo(),
|
||||
// OrderType: orderType,
|
||||
// Status: 1,
|
||||
// Price: price,
|
||||
// TotalNum: reversePosition.TotalReverseAmount,
|
||||
// Symbol: symbol.GetSymbol(),
|
||||
// Side: side,
|
||||
// PositionSide: positionSide,
|
||||
// FollowOrderSn: orderSn,
|
||||
// Type: ot,
|
||||
// SignPrice: lastPrice,
|
||||
// Category: 1,
|
||||
// ApiId: apiInfo.Id,
|
||||
// IsAddPosition: 2,
|
||||
// TriggerTime: &now,
|
||||
// BuyPrice: reversePosition.TotalReverseAmount.Mul(price).Truncate(int32(symbol.PriceDigit)),
|
||||
// }
|
||||
|
||||
// if err1 := e.Orm.Create(&newOrder).Error; err1 != nil {
|
||||
// e.Log.Errorf("保存反单止盈止损失败 symbol:%s custom:%s :%v", symbol, orderSn, err1)
|
||||
// return err1
|
||||
// }
|
||||
// params := FutOrderPlace{
|
||||
// ApiId: apiInfo.Id,
|
||||
// Symbol: symbol.GetSymbol(),
|
||||
// PositionSide: newOrder.PositionSide,
|
||||
// Side: newOrder.Side,
|
||||
// OrderType: ot,
|
||||
// Quantity: newOrder.TotalNum,
|
||||
// Price: price,
|
||||
// StopPrice: price,
|
||||
// Profit: price,
|
||||
// NewClientOrderId: newOrder.OrderSn,
|
||||
// ClosePosition: close,
|
||||
// }
|
||||
// futApiV2 := FuturesResetV2{Service: e.Service}
|
||||
// err = futApiV2.OrderPlaceLoop(&apiInfo, params)
|
||||
|
||||
// if err != nil {
|
||||
// e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", symbol.GetSymbol(), orderSn, err)
|
||||
|
||||
// if err1 := e.Orm.Model(&newOrder).Updates(map[string]interface{}{"status": 8, "remark": err.Error(), "updated_at": time.Now()}).Error; err1 != nil {
|
||||
// e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", newOrder.Symbol, newOrder.OrderSn, err1)
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
|
||||
// e.DoCancelTakeProfitBatch(symbol.GetSymbol(), newOrder.PositionSide, newOrder.Side, newOrder.OrderType, &apiInfo)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user