Files
exchange_go/app/admin/service/line_pre_order.go
2025-03-12 16:56:37 +08:00

2179 lines
77 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"errors"
"fmt"
"go-admin/common/const/rediskey"
"go-admin/common/global"
"go-admin/common/helper"
models2 "go-admin/models"
"go-admin/models/positiondto"
"go-admin/pkg/utility"
"go-admin/pkg/utility/snowflakehelper"
"go-admin/services/binanceservice"
"sort"
"strconv"
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
"github.com/go-admin-team/go-admin-core/logger"
"github.com/go-admin-team/go-admin-core/sdk/service"
"gorm.io/gorm"
"go-admin/app/admin/models"
"go-admin/app/admin/service/dto"
"go-admin/common/actions"
cDto "go-admin/common/dto"
)
type LinePreOrder struct {
service.Service
}
// GetPage 获取LinePreOrder列表
func (e *LinePreOrder) GetPage(c *dto.LinePreOrderGetPageReq, p *actions.DataPermission, list *[]models.LinePreOrder, count *int64) error {
var err error
var data models.LinePreOrder
tx := e.Orm.Model(&data).
Scopes(
cDto.MakeCondition(c.GetNeedSearch()),
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
actions.Permission(data.TableName(), p),
).Where("pid = 0").Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").
Joins("JOIN line_pre_order_status on line_pre_order_status.order_sn = line_pre_order.order_sn").
Select("line_pre_order.*,line_api_user.api_name,line_pre_order_status.add_position_status,line_pre_order_status.reduce_status")
if c.AddPositionStatus >= 0 {
tx.Where("line_pre_order_status.add_position_status = ?", c.AddPositionStatus)
}
if c.HedgeStatus >= 0 {
tx.Where("line_pre_order_status.reduce_status = ?", c.HedgeStatus)
}
err = tx.
Find(list).Limit(-1).Offset(-1).
Count(count).Error
if err != nil {
e.Log.Errorf("LinePreOrderService GetPage error:%s \r\n", err)
return err
}
for i, order := range *list {
var childNum int64
e.Orm.Model(&models.LinePreOrder{}).Where("pid = ?", order.Id).Count(&childNum)
(*list)[i].ChildNum = childNum
}
return nil
}
// GetOrderPage 获取LinePreOrder列表
func (e *LinePreOrder) GetOrderPage(c *dto.LinePreOrderGetPageReq, p *actions.DataPermission, list *[]models.LinePreOrder, count *int64) error {
var err error
var data models.LinePreOrder
tx := e.Orm.Model(&data).
Scopes(
cDto.MakeCondition(c.GetNeedSearch()),
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
actions.Permission(data.TableName(), p),
).Where("pid = 0 AND status != '0'").Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").
Joins("JOIN line_pre_order_status on line_pre_order_status.order_id = line_pre_order.id").
Select("line_pre_order.*,line_api_user.api_name,line_pre_order_status.add_position_status,line_pre_order_status.reduce_status")
if c.AddPositionStatus >= 0 {
tx.Where("line_pre_order_status.add_position_status = ?", c.AddPositionStatus)
}
if c.HedgeStatus >= 0 {
tx.Where("line_pre_order_status.reduce_status = ?", c.HedgeStatus)
}
err = tx.
Find(list).Limit(-1).Offset(-1).
Count(count).Error
//err = e.Orm.Model(&data).
// Scopes(
// cDto.MakeCondition(c.GetNeedSearch()),
// cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
// actions.Permission(data.TableName(), p),
// ).Where("pid = 0 AND status != '0'").Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").Select("line_pre_order.*,line_api_user.api_name").
// Find(list).Limit(-1).Offset(-1).
// Count(count).Error
if err != nil {
e.Log.Errorf("LinePreOrderService GetChildPage error:%s \r\n", err)
return err
}
for i, order := range *list {
var childNum int64
e.Orm.Model(&models.LinePreOrder{}).Where("pid = ?", order.Id).Count(&childNum)
(*list)[i].ChildNum = childNum
}
return nil
}
// GetAllPage 获取LinePreOrder列表
func (e *LinePreOrder) GetAllPage(c *dto.LinePreOrderGetPageReq, p *actions.DataPermission, list *[]models.LinePreOrder, count *int64) error {
var err error
var data models.LinePreOrder
err = e.Orm.Model(&data).
Scopes(
cDto.MakeCondition(c.GetNeedSearch()),
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
actions.Permission(data.TableName(), p),
).
Find(list).Limit(-1).Offset(-1).
Count(count).Error
if err != nil {
e.Log.Errorf("LinePreOrderService GetPage error:%s \r\n", err)
return err
}
return nil
}
// GetChildList 获取子订单列表
func (e *LinePreOrder) GetChildList(req *dto.GetChildOrderReq, p *actions.DataPermission, order *[]models.LinePreOrder) error {
err := e.Orm.Model(&models.LinePreOrder{}).Where("pid = ?", req.Id).
Order("id asc").
Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").Select("line_pre_order.*,line_api_user.api_name").
Find(order).Error
if err != nil {
e.Log.Errorf("LinePreOrderService GetPage error:%s \r\n", err)
return err
}
ids := make([]int, 0)
for _, v := range *order {
ids = append(ids, v.Id)
}
if len(ids) > 0 {
var counts []dto.LinePreOrderChildCount
if err := e.Orm.Model(&models.LinePreOrder{}).Where("pid in ?", ids).Group("pid").Select("pid as id,count(*) as count").Find(&counts).Error; err != nil {
logger.Errorf("获取子订单数量失败 %v", err)
return nil
}
for index := range *order {
for _, v := range counts {
if v.Id == (*order)[index].Id {
(*order)[index].ChildNum = int64(v.Count)
}
}
}
}
return nil
}
// Get 获取LinePreOrder对象
func (e *LinePreOrder) Get(d *dto.LinePreOrderGetReq, p *actions.DataPermission, model *models.LinePreOrder) error {
var data models.LinePreOrder
err := e.Orm.Model(&data).
Scopes(
actions.Permission(data.TableName(), p),
).
First(model, d.GetId()).Error
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
err = errors.New("查看对象不存在或无权查看")
e.Log.Errorf("Service GetLinePreOrder error:%s \r\n", err)
return err
}
if err != nil {
e.Log.Errorf("db error:%s", err)
return err
}
return nil
}
// Insert 创建LinePreOrder对象
func (e *LinePreOrder) Insert(c *dto.LinePreOrderInsertReq) error {
var err error
var data models.LinePreOrder
c.Generate(&data)
err = e.Orm.Create(&data).Error
if err != nil {
e.Log.Errorf("LinePreOrderService Insert error:%s \r\n", err)
return err
}
return nil
}
// Update 修改LinePreOrder对象
func (e *LinePreOrder) Update(c *dto.LinePreOrderUpdateReq, p *actions.DataPermission) error {
var err error
var data = models.LinePreOrder{}
e.Orm.Scopes(
actions.Permission(data.TableName(), p),
).First(&data, c.GetId())
c.Generate(&data)
db := e.Orm.Save(&data)
if err = db.Error; err != nil {
e.Log.Errorf("LinePreOrderService Save error:%s \r\n", err)
return err
}
if db.RowsAffected == 0 {
return errors.New("无权更新该数据")
}
return nil
}
// Remove 删除LinePreOrder
func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermission) error {
var data models.LinePreOrder
var list []models.LinePreOrder
e.Orm.Model(&models.LinePreOrder{}).Where("id in ?", d.GetId()).Find(&list)
db := e.Orm.Model(&data).
Scopes(
actions.Permission(data.TableName(), p),
).Unscoped().Delete(&data, d.GetId())
if err := db.Error; err != nil {
e.Log.Errorf("Service RemoveLinePreOrder error:%s \r\n", err)
return err
}
ints := make([]int, 0)
apiIds := make([]int, 0)
if db.RowsAffected == 0 {
return errors.New("无权删除该数据")
}
positions := map[string]positiondto.LinePreOrderPositioinDelReq{}
addPosition := binanceservice.AddPositionList{}
reduceItem := binanceservice.ReduceListItem{}
futAddPosition := map[int]string{}
spotAddPosition := map[int]string{}
futReduces := map[int]string{}
spotRedces := map[int]string{}
futAddPositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE)
spotAddPositionKey := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE)
futReduceKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE)
spotReduceKey := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE)
futAddPositionVal, _ := helper.DefaultRedis.GetAllList(futAddPositionKey)
futReduceVal, _ := helper.DefaultRedis.GetAllList(spotAddPositionKey)
spotAddPositionVal, _ := helper.DefaultRedis.GetAllList(futReduceKey)
spotReduceVal, _ := helper.DefaultRedis.GetAllList(spotReduceKey)
for _, v := range futAddPositionVal {
sonic.Unmarshal([]byte(v), &addPosition)
if addPosition.MainId > 0 {
futAddPosition[addPosition.MainId] = v
}
}
for _, v := range spotAddPositionVal {
sonic.Unmarshal([]byte(v), &addPosition)
if addPosition.MainId > 0 {
spotAddPosition[addPosition.MainId] = v
}
}
for _, v := range futReduceVal {
sonic.Unmarshal([]byte(v), &reduceItem)
if reduceItem.MainId > 0 {
futReduces[reduceItem.MainId] = v
}
}
for _, v := range spotReduceVal {
sonic.Unmarshal([]byte(v), &reduceItem)
if reduceItem.MainId > 0 {
spotRedces[reduceItem.MainId] = v
}
}
//删除的缓存
for _, order := range list {
redisList := dto.PreOrderRedisList{
Id: order.Id,
Symbol: order.Symbol,
Price: order.Price,
Site: order.Site,
ApiId: order.ApiId,
OrderSn: order.OrderSn,
QuoteSymbol: order.QuoteSymbol,
}
if !utility.ContainsInt(apiIds, order.ApiId) {
apiIds = append(apiIds, order.ApiId)
}
//清除待加仓、待减仓
if val, ok := futAddPosition[order.Id]; ok {
helper.DefaultRedis.LRem(futAddPositionKey, val)
}
if val, ok := spotAddPosition[order.Id]; ok {
helper.DefaultRedis.LRem(spotAddPositionKey, val)
}
if val, ok := futReduces[order.Id]; ok {
helper.DefaultRedis.LRem(futReduceKey, val)
}
if val, ok := spotRedces[order.Id]; ok {
helper.DefaultRedis.LRem(spotReduceKey, val)
}
tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, fmt.Sprintf(global.TICKER_SPOT, order.ExchangeType, order.Symbol))
redisList.Price = utility.StringToDecimal(redisList.Price).Truncate(int32(tradeSet.PriceDigit)).String()
marshal, _ := sonic.Marshal(redisList)
if order.SymbolType == 1 {
listKey := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType)
helper.DefaultRedis.LRem(listKey, string(marshal))
} else {
listKey := fmt.Sprintf(rediskey.PreFutOrderList, order.ExchangeType)
helper.DefaultRedis.LRem(listKey, string(marshal))
}
//会影响持仓的
removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType)
if _, ok := positions[removeSymbolKey]; !ok {
positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{
ApiId: order.ApiId,
Symbol: order.Symbol,
ExchangeType: order.ExchangeType,
Side: order.Site,
SymbolType: order.SymbolType,
}
}
binanceservice.MainClosePositionClearCache(order.Id, order.SymbolType)
ints = append(ints, order.Id)
}
if len(ints) > 0 {
e.Orm.Model(&models.LinePreOrder{}).Where("main_id >0 AND main_id in ?", ints).Unscoped().Delete(&models.LinePreOrder{})
}
//清理仓位缓存
for _, v := range positions {
var count int64
e.Orm.Model(&models.LinePreOrder{}).
Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6",
v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count)
//没有已开仓的订单 直接清理仓位
if count == 0 {
var key string
if v.SymbolType == 1 {
key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side)
} else {
key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side)
}
helper.DefaultRedis.DeleteString(key)
}
}
return nil
}
// AddPreOrder 单个添加
func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataPermission, errs *[]error, tickerSymbol string) error {
apiUserIds := strings.Split(req.ApiUserId, ",")
if req.SaveTemplate == "2" || req.SaveTemplate == "1" { //2 = 只保存模板 1= 保存模板并下单
var templateLog dto.LineAddPreOrderReq
copier.Copy(&templateLog, req)
//templateLog := *req
templateLog.SaveTemplate = "0"
templateLog.TemplateName = ""
marshal, _ := sonic.Marshal(templateLog)
saveTemplateParams := models.LineOrderTemplateLogs{
Name: req.TemplateName,
UserId: 0,
Params: string(marshal),
Type: 1,
Switch: "0",
}
e.Orm.Model(&models.LineOrderTemplateLogs{}).Create(&saveTemplateParams)
}
if req.SaveTemplate == "2" {
return nil
}
//重新排序下跌比例(顺序)
sort.Slice(req.Ext, func(i, j int) bool {
return req.Ext[i].PriceRatio.Cmp(req.Ext[j].PriceRatio) < 0
})
var key string
if req.SymbolType == global.SYMBOL_SPOT {
key = fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, req.Symbol)
} else {
key = fmt.Sprintf(global.TICKER_FUTURES, req.ExchangeType, req.Symbol)
}
for _, id := range apiUserIds {
if req.Site == "SELL" && req.SymbolType == global.SYMBOL_SPOT {
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 现货不支持卖出操作", id, req.Symbol))
continue
}
var AddOrder models.LinePreOrder
var profitOrder models.LinePreOrder
var stopOrder models.LinePreOrder
//获取交易对
tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key)
tickerPrice := utility.StrToDecimal(tradeSet.LastPrice)
if tickerPrice.Equal(decimal.Zero) { //redis 没有这个值
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 交易行情出错", id, req.Symbol))
continue
}
AddOrder.ExchangeType = req.ExchangeType
AddOrder.OrderCategory = 1
AddOrder.SignPriceType = "new"
AddOrder.BuyPrice = req.BuyPrice //购买多少U
AddOrder.SymbolType = req.SymbolType //交易对类型1= 现货 2 = 合约
AddOrder.OrderType = req.OrderType //订单类型
AddOrder.CoverType = req.CoverType //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货
if req.ExpireHour == 0 { //过期时间默认为4个小时
req.ExpireHour = 4
}
AddOrder.ExpireTime = time.Now().Add(time.Duration(req.ExpireHour) * time.Hour) //过期时间
AddOrder.MainOrderType = req.MainOrderType
AddOrder.Site = req.Site
AddOrder.SignPrice = tickerPrice.String()
if req.PricePattern == "percentage" {
AddOrder.Rate = req.Price
orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10%
priceRate := utility.SafeDiv(orderPrice, (decimal.NewFromInt(100))) //下单价除100 =0.1
if strings.ToUpper(req.Site) == "BUY" { //购买方向
//实际下单价格
truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit))
AddOrder.Price = truncate.String()
} else {
truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit))
AddOrder.Price = truncate.String()
}
} else { //实际价格下单
AddOrder.Price = utility.StringToDecimal(req.Price).Truncate(int32(tradeSet.PriceDigit)).String()
AddOrder.SignPriceType = req.PricePattern
AddOrder.Rate = "0"
}
buyPrice := utility.StrToDecimal(req.BuyPrice) //购买多少U
var symbolInfo models.LineSymbol
e.Orm.Model(&models.LineSymbol{}).Where("type = ? AND symbol = ?", req.SymbolType, req.Symbol).Find(&symbolInfo)
//计算购买数量 判断是否是否是U本位
if symbolInfo.QuoteAsset != "USDT" { //不是U本位
//获取币本位兑换u的价格
ticker2 := models2.TradeSet{}
tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(symbolInfo.QuoteAsset+"USDT")))
if tickerVal == "" {
logger.Error("查询行情失败")
continue
}
err := sonic.Unmarshal([]byte(tickerVal), &ticker2)
if ticker2.LastPrice == "" {
logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(symbolInfo.QuoteAsset+"USDT"), err)
continue
}
//LTCBTC --> LTCUSDT
uTickerPrice, _ := decimal.NewFromString(ticker2.LastPrice) //94069
//换算成U
//div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365
//在换算成对应交易对对应的价值
//LTCBTC --> LTCUSDT == LTCUSDT -- 100.502
div := utility.SafeDiv(utility.SafeDiv(tickerPrice, decimal.NewFromInt(1)), uTickerPrice)
//计算下单数量
AddOrder.Num = utility.SafeDiv(buyPrice, div).Truncate(int32(tradeSet.AmountDigit)).String()
} else {
fromString, _ := decimal.NewFromString(AddOrder.Price)
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))
continue
}
AddOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
AddOrder.ApiId, _ = strconv.Atoi(id)
AddOrder.Symbol = req.Symbol
AddOrder.QuoteSymbol = symbolInfo.QuoteAsset
AddOrder.Pid = 0
AddOrder.GroupId = "0"
AddOrder.Status = 0
copier.Copy(&profitOrder, &AddOrder)
copier.Copy(&stopOrder, &AddOrder)
preOrderStatus := models.LinePreOrderStatus{}
preOrderStatus.OrderSn = AddOrder.OrderSn
//订单配置信息
preOrderExts := make([]models.LinePreOrderExt, 0)
defultExt := models.LinePreOrderExt{
AddType: 1, //主单等同于加仓0
AddPositionType: 1,
AddPositionVal: decimal.Zero,
OrderType: req.PriceType,
TakeProfitRatio: utility.StringToDecimal(req.Profit),
TakeProfitNumRatio: req.ProfitNumRatio,
TpTpPriceRatio: req.ProfitTpTpPriceRatio,
TpSlPriceRatio: req.ProfitTpSlPriceRatio,
}
//减仓单
defultExt2 := models.LinePreOrderExt{
AddType: 2,
OrderType: "LIMIT",
PriceRatio: req.ReducePriceRatio,
AddPositionType: 1,
AddPositionVal: req.ReduceNumRatio,
TakeProfitRatio: req.ReduceTakeProfitRatio,
TakeProfitNumRatio: decimal.NewFromInt(100), //减仓止盈默认100%
StopLossRatio: req.ReduceStopLossRatio,
}
mainPrice := utility.StringToDecimal(AddOrder.Price)
mainAmount := utility.SafeDiv(buyPrice, mainPrice)
defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit))
defultExt2.TotalBefore = defultExt.TotalAfter
// if decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Cmp(decimal.Zero) > 0 {
default2NumPercent := utility.SafeDiv(decimal.NewFromInt(100).Sub(req.ReduceNumRatio), decimal.NewFromInt(100))
defultExt2.TotalAfter = mainAmount.Mul(default2NumPercent).Truncate(int32(tradeSet.AmountDigit))
defultExt2.ReTakeRatio = utility.SafeDiv(req.ReducePriceRatio, default2NumPercent).Truncate(2)
preOrderExts = append(preOrderExts, defultExt)
preOrderExts = append(preOrderExts, defultExt2)
calculateResp := dto.CalculateBreakEvenRatioResp{}
mainParam := dto.CalculateBreakEevenRatioReq{
AddType: 2,
Price: mainPrice,
ExchangeType: req.ExchangeType,
Symbol: req.Symbol,
SymbolType: req.SymbolType,
BuyPrice: buyPrice,
LossBeginPercent: decimal.Zero,
LossEndPercent: req.ReducePriceRatio,
AddPositionType: 1,
AddPositionVal: req.ReduceNumRatio,
}
//计算减仓后
mainParam.LossEndPercent = req.ReducePriceRatio
mainParam.RemainingQuantity = mainAmount
e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet)
mainParam.RemainingQuantity = calculateResp.RemainingQuantity //mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU //buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit))
req.ReduceReTakeProfitRatio = calculateResp.Ratio
mainParam.LossBeginPercent = req.ReducePriceRatio
defultExt.ReTakeRatio = calculateResp.Ratio
for index, addPosition := range req.Ext {
ext := models.LinePreOrderExt{
AddType: addPosition.AddType,
OrderType: addPosition.OrderType,
PriceRatio: addPosition.PriceRatio,
TakeProfitRatio: addPosition.TakeProfitRatio,
TakeProfitNumRatio: addPosition.TakeProfitNumRatio,
StopLossRatio: addPosition.StopLossRatio,
TpTpPriceRatio: addPosition.TpTpPriceRatio,
TpSlPriceRatio: addPosition.TpSlPriceRatio,
AddPositionType: addPosition.AddPositionType,
AddPositionVal: addPosition.AddPositionVal,
}
mainParam.AddType = addPosition.AddType
mainParam.LossEndPercent = req.Ext[index].PriceRatio
mainParam.AddPositionType = req.Ext[index].AddPositionType
mainParam.AddPositionVal = req.Ext[index].AddPositionVal
if err := e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet); err != nil {
*errs = append(*errs, err)
return err
}
ext.TotalBefore = mainParam.RemainingQuantity //初始数量
ext.TotalAfter = calculateResp.RemainingQuantity //计算后数量
ext.ReTakeRatio = calculateResp.Ratio
mainParam.LossBeginPercent = addPosition.PriceRatio
mainParam.RemainingQuantity = calculateResp.RemainingQuantity
mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU
preOrderExts = append(preOrderExts, ext)
}
//事务添加
e.Orm.Transaction(func(tx *gorm.DB) error {
err := tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&AddOrder).Error
if err != nil {
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 生成订单失败", id, req.Symbol))
return err
}
preOrderStatus.OrderId = AddOrder.Id
//加仓、减仓状态
tx.Model(&models.LinePreOrderStatus{}).Create(&preOrderStatus)
preOrderExts[0].OrderId = AddOrder.Id
list := dto.PreOrderRedisList{
Id: AddOrder.Id,
Symbol: AddOrder.Symbol,
Price: AddOrder.Price,
Site: AddOrder.Site,
ApiId: AddOrder.ApiId,
OrderSn: AddOrder.OrderSn,
QuoteSymbol: AddOrder.QuoteSymbol,
}
marshal, _ := sonic.Marshal(&list)
var preKey string
if AddOrder.SymbolType == global.SYMBOL_SPOT {
preKey = fmt.Sprintf(rediskey.PreSpotOrderList, AddOrder.ExchangeType)
} else {
preKey = fmt.Sprintf(rediskey.PreFutOrderList, AddOrder.ExchangeType)
}
helper.DefaultRedis.LPushList(preKey, string(marshal))
//是否有止盈止损订单
if req.Profit != "" {
if strings.ToUpper(req.Site) == "BUY" {
profitOrder.Site = "SELL"
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 + utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
} else {
profitOrder.Site = "BUY"
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 - utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
}
profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
profitOrder.Pid = AddOrder.Id
profitOrder.OrderType = 1
profitOrder.Status = 0
profitOrder.Rate = req.Profit
profitOrder.MainId = AddOrder.Id
if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 {
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)
//不全部止盈的时候
if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 && req.ProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
reminQuantity := utility.StrToDecimal(AddOrder.Num).Sub(utility.StrToDecimal(profitOrder.Num))
childrens, err := makeTpOrder(&profitOrder, reminQuantity, req.ProfitTpTpPriceRatio, req.ProfitTpSlPriceRatio, &tradeSet)
if err != nil {
logger.Error("生成止盈后子订单失败")
return err
}
tx.Create(&childrens)
}
}
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(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(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
}
stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
stopOrder.Pid = AddOrder.Id
stopOrder.MainId = AddOrder.Id
stopOrder.OrderType = 4
stopOrder.Status = 0
stopOrder.BuyPrice = "0"
stopOrder.Rate = req.ReducePriceRatio.String()
stopNum := utility.StrToDecimal(AddOrder.Num).Mul(req.ReduceNumRatio.Div(decimal.NewFromInt(100)).Truncate(4))
stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String()
tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder)
preOrderExts[1].OrderId = stopOrder.Id
if req.ReduceNumRatio.Cmp(decimal.Zero) > 0 && req.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt2, tradeSet, false); err != nil {
logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err)
return err
} else if len(newOrders) > 0 {
if err := e.Orm.Create(&newOrders).Error; err != nil {
logger.Errorf("主单减仓保存止盈、减仓失败 err:%v", err)
return err
}
}
}
}
//添加止盈单
for index, v := range preOrderExts {
preOrderExts[index].MainOrderId = AddOrder.Id
if index < 2 {
// preOrderExts[index].OrderId = AddOrder.Id
continue
}
var newOrder models.LinePreOrder
if v.AddType == 1 {
newOrder = createPreAddPosition(&AddOrder, v, tradeSet)
} else if v.AddType == 2 {
newOrder = createPreReduceOrder(&AddOrder, v, tradeSet)
}
if newOrder.OrderSn == "" {
continue
}
if err := e.Orm.Create(&newOrder).Error; err != nil {
logger.Error("保存加仓单失败")
return err
}
preOrderExts[index].OrderId = newOrder.Id
//止盈、减仓
orders, err := makeFuturesTakeAndReduce(&newOrder, v, tradeSet)
if err != nil {
logger.Error("构造止盈、止损失败")
return err
}
if err := e.Orm.Create(&orders).Error; err != nil {
logger.Error("保存止盈、止损失败")
return err
}
for index := range orders {
//减仓单且 减仓比例大于0 小于100 就冲下止盈止损
if orders[index].OrderType == 1 && v.TakeProfitRatio.Cmp(decimal.Zero) > 0 && v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) < 0 {
reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet, true)
if err != nil {
logger.Error("生产止盈后止盈、减仓失败")
return err
}
if len(reduceChildOrders) == 0 {
continue
}
if err := e.Orm.Create(&reduceChildOrders).Error; err != nil {
logger.Error("报错止盈后止盈止损失败")
return err
}
}
}
}
err = tx.Model(&models.LinePreOrderExt{}).Create(&preOrderExts).Error
if err != nil {
return err
}
return nil
})
}
return nil
}
// 生成加仓单
func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderExt, tradeSet models2.TradeSet) models.LinePreOrder {
data := models.LinePreOrder{}
//主单类型
if v.AddPositionVal.Cmp(decimal.Zero) <= 0 {
logger.Errorf("预生成加仓单失败, 主订单号:%s 加仓数值不大于0", preOrder.OrderSn)
return data
}
price := utility.StrToDecimal(preOrder.Price)
copier.Copy(&data, preOrder)
data.Id = 0
data.Pid = preOrder.Id
data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
data.MainId = preOrder.Id
if preOrder.MainId > 0 {
data.MainId = preOrder.MainId
}
data.CreatedAt = time.Now()
data.MainOrderType = v.OrderType
data.Status = 0
data.OrderCategory = 3
data.Rate = v.PriceRatio.String()
var percentage decimal.Decimal
if data.Site == "BUY" {
percentage = decimal.NewFromInt(1).Sub(v.PriceRatio.Div(decimal.NewFromInt(100)))
} else {
percentage = decimal.NewFromInt(1).Add(v.PriceRatio.Div(decimal.NewFromInt(100)))
}
dataPrice := price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit))
data.Price = dataPrice.String()
if v.AddPositionType == 1 {
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 = utility.SafeDiv(v.AddPositionVal.Truncate(2), dataPrice).Truncate(int32(tradeSet.AmountDigit)).String()
}
return data
}
// 生成减仓单
func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) models.LinePreOrder {
var stopOrder models.LinePreOrder
//减仓单
if ext.PriceRatio.Cmp(decimal.Zero) > 0 {
copier.Copy(&stopOrder, preOrder)
stopOrder.Id = 0
stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
stopOrder.Pid = preOrder.Id
stopOrder.MainId = preOrder.Id
if preOrder.MainId > 0 {
stopOrder.MainId = preOrder.MainId
}
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()
if strings.ToUpper(preOrder.Site) == "BUY" {
stopOrder.Site = "SELL"
} else {
stopOrder.Site = "BUY"
}
binanceservice.SetPrice(&stopOrder, preOrder, tradeSet)
}
return stopOrder
}
// 构建止盈、止盈止损
func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) {
orders := make([]models.LinePreOrder, 0)
var side string
if (preOrder.OrderType != 0 && strings.ToUpper(preOrder.Site) == "BUY") || (preOrder.OrderType == 0 && strings.ToUpper(preOrder.Site) == "SELL") {
side = "BUY"
} else {
side = "SELL"
}
if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 {
// 止盈单
profitOrder := models.LinePreOrder{}
copier.Copy(&profitOrder, preOrder)
profitOrder.Id = 0
profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
profitOrder.Pid = preOrder.Id
profitOrder.OrderType = 1
profitOrder.Status = 0
profitOrder.MainId = preOrder.Id
if preOrder.MainId > 0 {
profitOrder.MainId = preOrder.MainId
}
profitOrder.BuyPrice = "0"
profitOrder.Site = side
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(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String()
}
// 止盈需要累加之前的亏损
profitOrder.Rate = ext.TakeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String()
binanceservice.SetPrice(&profitOrder, preOrder, tradeSet)
orders = append(orders, profitOrder)
}
if ext.StopLossRatio.Cmp(decimal.Zero) > 0 {
lossOrder := models.LinePreOrder{}
copier.Copy(&lossOrder, preOrder)
lossOrder.Id = 0
lossOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
lossOrder.Pid = preOrder.Id
lossOrder.OrderType = 2
lossOrder.Status = 0
lossOrder.MainId = preOrder.MainId
lossOrder.BuyPrice = "0"
lossOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String()
lossOrder.Rate = ext.StopLossRatio.Truncate(2).String()
lossOrder.Site = side
binanceservice.SetPrice(&lossOrder, preOrder, tradeSet)
orders = append(orders, lossOrder)
}
return orders, nil
}
// 构建止盈后止盈止损
// parentOrder 父订单
// remainQuantity 剩余数量
// tpPriceRatio 止盈价格比例
// slPriceRatio 止损价格比例
func makeTpOrder(parentOrder *models.LinePreOrder, reminQuantity decimal.Decimal, tpPriceRatio, slPriceRatio decimal.Decimal, tradeSet *models2.TradeSet) ([]models.LinePreOrder, error) {
result := make([]models.LinePreOrder, 0)
tp := models.LinePreOrder{}
sl := models.LinePreOrder{}
copier.Copy(&tp, parentOrder)
tp.Id = 0
tp.Pid = parentOrder.Id
tp.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
tp.OrderType = 1
tp.Status = 0
tp.Rate = tpPriceRatio.String()
tp.Num = reminQuantity.String()
binanceservice.SetPrice(&tp, parentOrder, *tradeSet)
result = append(result, tp)
if (slPriceRatio).Cmp(decimal.Zero) > 0 {
copier.Copy(&sl, parentOrder)
sl.Pid = parentOrder.Id
sl.Id = 0
sl.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
sl.OrderType = 2
sl.Num = reminQuantity.Truncate(int32(tradeSet.AmountDigit)).String()
sl.Rate = slPriceRatio.String()
binanceservice.SetPrice(&sl, parentOrder, *tradeSet)
result = append(result, sl)
}
return result, nil
}
// 构建减仓后止盈止损
// isTpTp 是否止盈后止盈止损
func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet, isTpTp bool) ([]models.LinePreOrder, error) {
orders := make([]models.LinePreOrder, 0)
num := ext.TotalAfter
var takeProfitRatio, slPriceRatio decimal.Decimal
if isTpTp {
takeProfitRatio = ext.TpTpPriceRatio
slPriceRatio = ext.TpSlPriceRatio
} else {
takeProfitRatio = ext.TakeProfitRatio
slPriceRatio = ext.StopLossRatio
}
if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
percent := decimal.NewFromInt(1).Sub(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100)))
num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit))
}
if takeProfitRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 {
takeProfitOrder := models.LinePreOrder{}
copier.Copy(&takeProfitOrder, parentOrder)
takeProfitOrder.Id = 0
takeProfitOrder.Pid = parentOrder.Id
takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
takeProfitOrder.Status = 0
takeProfitOrder.OrderType = 1
takeProfitOrder.SignPrice = parentOrder.Price
takeProfitOrder.CreatedAt = time.Now()
takeProfitOrder.BuyPrice = "0"
takeProfitOrder.MainOrderType = "LIMIT"
takeProfitOrder.Num = num.String()
//止盈需要累加之前的亏损
if isTpTp {
takeProfitOrder.Rate = takeProfitRatio.Truncate(2).String()
} else {
takeProfitOrder.Rate = takeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String()
}
takeProfitOrder.BuyPrice = "0"
binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet)
orders = append(orders, takeProfitOrder)
}
//有止损单
if slPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 {
var stoploss models.LinePreOrder
copier.Copy(&stoploss, parentOrder)
stoploss.Id = 0
stoploss.Pid = parentOrder.Id
stoploss.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
stoploss.Status = 0
stoploss.CreatedAt = time.Now()
stoploss.OrderType = 2
stoploss.SignPrice = parentOrder.Price
stoploss.BuyPrice = "0"
stoploss.Rate = slPriceRatio.String()
stoploss.MainOrderType = "LIMIT"
stoploss.Num = num.String()
stoploss.BuyPrice = "0"
binanceservice.SetPrice(&stoploss, parentOrder, tradeSet)
orders = append(orders, stoploss)
}
return orders, nil
}
// CheckRepeatOrder 检查重复下单 检查基础货币
func (e *LinePreOrder) CheckRepeatOrder(symbolType int, apiUserId, site, baseCoin string) int64 {
var count int64
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND pid=0 AND symbol like ? AND symbol_type = ? AND site = ? AND `status` IN (1,5,6)", apiUserId, baseCoin+"%", symbolType, site).Count(&count)
return count
}
// AddBatchPreOrder 批量添加
func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p *actions.DataPermission, errs *[]error) error {
if batchReq.SaveTemplate == "2" || batchReq.SaveTemplate == "1" { //2 = 只保存模板 1= 保存模板并下单
var templateLog dto.LineBatchAddPreOrderReq
copier.Copy(&templateLog, batchReq)
//templateLog = *batchReq
templateLog.SaveTemplate = "0"
templateLog.TemplateName = ""
marshal, _ := sonic.Marshal(templateLog)
saveTemplateParams := models.LineOrderTemplateLogs{
Name: batchReq.TemplateName,
UserId: 0,
Params: string(marshal),
Type: 2,
Switch: "0",
}
e.Orm.Model(&models.LineOrderTemplateLogs{}).Create(&saveTemplateParams)
}
if batchReq.SaveTemplate == "2" {
return nil
}
if batchReq.SymbolGroupId != "" {
var symbolGroupInfo models.LineSymbolGroup
e.Orm.Model(&models.LineSymbolGroup{}).Where("id = ?", utility.StringToInt(batchReq.SymbolGroupId)).Find(&symbolGroupInfo)
if symbolGroupInfo.Id <= 0 || symbolGroupInfo.Symbol == "" {
*errs = append(*errs, errors.New(fmt.Sprintf("选择的交易对组:%s不存在或交易对组的交易对为空", batchReq.SymbolGroupId)))
return nil
}
batchReq.Symbol = symbolGroupInfo.Symbol
}
//脚本次数
if batchReq.OrderNum > 0 {
var tickerSymbol string
if batchReq.SymbolType == global.SYMBOL_SPOT {
tickerSymbol = helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
} else {
tickerSymbol = helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val()
}
apiUserIds := strings.Split(batchReq.ApiUserId, ",")
if batchReq.Script == "1" {
//scriptLogs := make([]models.LinePreScript, 0)
logParams := *batchReq
for _, id := range apiUserIds {
for j := 1; j <= batchReq.OrderNum; j++ {
var log models.LinePreScript
logParams.SaveTemplate = "0"
logParams.TemplateName = ""
logParams.Script = ""
marshal, _ := sonic.Marshal(logParams)
log.ApiId = int64(utility.StringToInt(id))
log.ScriptNum = int64(j)
log.ScriptParams = string(marshal)
log.AdminId = 0
log.Status = "0"
//scriptLogs = append(scriptLogs, log)
err := e.Orm.Model(&models.LinePreScript{}).Create(&log).Error
if err != nil {
*errs = append(*errs, fmt.Errorf("记录脚本失败:%+v", err.Error()))
return nil
}
helper.DefaultRedis.RPushList(rediskey.PreOrderScriptList, utility.IntToString(log.Id))
}
}
return nil
}
for _, id := range apiUserIds {
for j := 0; j < batchReq.OrderNum; j++ {
symbols := strings.Split(batchReq.Symbol, ",")
for _, symbol := range symbols {
var req dto.LineAddPreOrderReq
req.ExchangeType = batchReq.ExchangeType
req.OrderType = batchReq.OrderType
req.Symbol = symbol
req.ApiUserId = id
req.Site = batchReq.Site
req.BuyPrice = batchReq.BuyPrice
req.PricePattern = batchReq.PricePattern
req.Price = batchReq.Price
req.Profit = batchReq.Profit
req.ProfitNumRatio = batchReq.ProfitNumRatio
req.ProfitTpTpPriceRatio = batchReq.ProfitTpTpPriceRatio
req.ProfitTpSlPriceRatio = batchReq.ProfitTpSlPriceRatio
req.Ext = batchReq.Ext
req.SymbolType = batchReq.SymbolType
// req.StopPrice = batchReq.StopPrice
req.ReducePriceRatio = batchReq.ReducePriceRatio
req.PriceType = batchReq.PriceType
req.CoverType = batchReq.CoverType
req.ExpireHour = batchReq.ExpireHour
req.MainOrderType = batchReq.MainOrderType
req.ReduceNumRatio = batchReq.ReduceNumRatio
req.ReduceStopLossRatio = batchReq.ReduceStopLossRatio
req.ReduceTakeProfitRatio = batchReq.ReduceTakeProfitRatio
e.AddPreOrder(&req, p, errs, tickerSymbol)
}
}
}
return nil
} else {
*errs = append(*errs, errors.New("请选择运行次数"))
return nil
}
}
// QuickAddPreOrder 模板快速下单
func (e *LinePreOrder) QuickAddPreOrder(quickReq *dto.QuickAddPreOrderReq, p *actions.DataPermission, errs *[]error) error {
templateLogs := make([]models.LineOrderTemplateLogs, 0)
e.Orm.Model(&models.LineOrderTemplateLogs{}).Where("id in ?", strings.Split(quickReq.Ids, ",")).Find(&templateLogs)
for _, log := range templateLogs {
//单独添加
if log.Type == 1 {
var addPreOrderParams dto.LineAddPreOrderReq
sonic.Unmarshal([]byte(log.Params), &addPreOrderParams)
var tickerSymbol string
if addPreOrderParams.OrderType == global.SYMBOL_SPOT {
tickerSymbol = helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
} else {
tickerSymbol = helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val()
}
err := e.AddPreOrder(&addPreOrderParams, p, errs, tickerSymbol)
if err != nil {
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 生成订单失败", addPreOrderParams.ApiUserId, addPreOrderParams.Symbol))
continue
}
}
//批量添加
if log.Type == 2 {
var batchAddPreOrder dto.LineBatchAddPreOrderReq
sonic.Unmarshal([]byte(log.Params), &batchAddPreOrder)
e.AddBatchPreOrder(&batchAddPreOrder, p, errs)
}
}
return nil
}
// Lever 设置杠杆
func (e *LinePreOrder) Lever(req *dto.LeverReq, p *actions.DataPermission, errs *[]error) {
users := make([]models.LineApiUser, 0)
err := e.Orm.Model(&models.LineApiUser{}).Where("id in ? AND exchange_type = ?", strings.Split(req.ApiUserIds, ","), req.ExchangeType).Find(&users).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
*errs = append(*errs, errors.New(fmt.Sprintf("设置杠杆失败:%+v", err.Error())))
return
}
var symbols string
if req.Symbol != "" {
symbols = req.Symbol
} else {
var symbolGroupInfo models.LineSymbolGroup
e.Orm.Model(&models.LineSymbolGroup{}).Where("id = ? AND exchange_type = ?", req.GroupId, req.ExchangeType).Find(&symbolGroupInfo)
symbols = symbolGroupInfo.Symbol
}
if req.IsAll == 1 {
lineSymbols := make([]models.LineSymbol, 0)
futSymbols := make([]string, 0)
e.Orm.Model(&models.LineSymbol{}).Where("type = '2' AND switch = '1' AND exchange_type = ?", req.ExchangeType).Find(&lineSymbols)
for _, symbol := range lineSymbols {
futSymbols = append(futSymbols, symbol.Symbol)
}
if len(futSymbols) != 0 {
symbols = strings.Join(futSymbols, ",")
}
}
if req.ExchangeType == global.EXCHANGE_BINANCE {
e.SetBinanceLever(req, users, symbols, errs)
}
return
}
// SetBinanceLever 设置币安杠杆
func (e *LinePreOrder) SetBinanceLever(req *dto.LeverReq, users []models.LineApiUser, symbols string, errs *[]error) {
for _, user := range users {
var client *helper.BinanceClient
if user.UserPass == "" {
client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "", user.IpAddress)
} else {
client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "socks5", user.UserPass+"@"+user.IpAddress)
}
//client.SendFuturesRequestAuth("/")
symbolsSlice := strings.Split(symbols, ",")
for _, s := range symbolsSlice {
params := map[string]string{
"leverage": utility.IntToString(req.Leverage),
"symbol": s,
}
resp, _, err := client.SendFuturesRequestAuth("/fapi/v1/leverage", "POST", params)
if err != nil {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 交易对:%s 设置杠杆失败:%+v", user.Id, s, err.Error())))
continue
}
var dataMap map[string]interface{}
if err := sonic.Unmarshal(resp, &dataMap); err != nil {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 交易对:%s 设置杠杆失败:%+v", user.Id, s, err.Error())))
continue
}
if _, ok := dataMap["leverage"]; !ok {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 交易对:%s 设置杠杆失败:%+v", user.Id, s, dataMap["message"])))
continue
}
}
}
}
// SetBinanceMarginType 设置币安保证金模式
func (e *LinePreOrder) SetBinanceMarginType(req *dto.MarginTypeReq, users []models.LineApiUser, symbols string, errs *[]error) {
for _, user := range users {
var client *helper.BinanceClient
if user.UserPass == "" {
client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "", user.IpAddress)
} else {
client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "socks5", user.UserPass+"@"+user.IpAddress)
}
//client.SendFuturesRequestAuth("/")
symbolsSlice := strings.Split(symbols, ",")
for _, s := range symbolsSlice {
params := map[string]string{
"marginType": req.MarginType,
"symbol": s,
}
resp, _, err := client.SendFuturesRequestAuth("/fapi/v1/marginType", "POST", params)
if err != nil {
*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, err.Error()))
continue
}
var dataMap map[string]interface{}
if err := sonic.Unmarshal(resp, &dataMap); err != nil {
*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, err.Error()))
continue
}
code, ok := dataMap["code"]
if !ok {
*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, dataMap["message"]))
continue
}
if code.(float64) != 200 {
*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, dataMap["message"]))
continue
}
}
}
}
// MarginType 设置仓位模式
func (e *LinePreOrder) MarginType(req *dto.MarginTypeReq, p *actions.DataPermission, errs *[]error) {
users := make([]models.LineApiUser, 0)
err := e.Orm.Model(&models.LineApiUser{}).Where("id in ? ", strings.Split(req.ApiUserIds, ",")).Find(&users).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
*errs = append(*errs, errors.New(fmt.Sprintf("设置杠杆失败:%+v", err.Error())))
return
}
var symbols string
if req.Symbol != "" {
symbols = req.Symbol
} else {
var symbolGroupInfo models.LineSymbolGroup
e.Orm.Model(&models.LineSymbolGroup{}).Where("id = ?", req.GroupId).Find(&symbolGroupInfo)
symbols = symbolGroupInfo.Symbol
}
if req.IsAll == 1 {
lineSymbols := make([]models.LineSymbol, 0)
futSymbols := make([]string, 0)
e.Orm.Model(&models.LineSymbol{}).Where("type = '2' AND switch = '1'").Find(&lineSymbols)
for _, symbol := range lineSymbols {
futSymbols = append(futSymbols, symbol.Symbol)
}
if len(futSymbols) != 0 {
symbols = strings.Join(futSymbols, ",")
}
}
if req.ExchangeType == global.EXCHANGE_BINANCE {
e.SetBinanceMarginType(req, users, symbols, errs)
}
}
// CancelOpenOrder 取消委托
func (e *LinePreOrder) CancelOpenOrder(req *dto.CancelOpenOrderReq, errs *[]error) {
newClientOrderIdList := make([]string, 0)
var apiUserInfo models.LineApiUser
e.Orm.Model(&models.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo)
futApi := binanceservice.FutRestApi{}
spotApi := binanceservice.SpotRestApi{}
//取消指定订单号的委托
if req.OrderSn != "" && req.Symbol != "" {
var err error
var orderInfo models.LinePreOrder
e.Orm.Model(&models.LinePreOrder{}).Where("symbol = ? AND order_sn = ? AND status = '5'", req.Symbol, req.OrderSn).Find(&orderInfo)
if orderInfo.Id <= 0 {
*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", "未找到可以取消的委托"))
return
}
if req.OrderType == 1 { //现货
err = spotApi.CancelOpenOrderByOrderSn(apiUserInfo, req.Symbol, req.OrderSn)
} else {
newClientOrderIdList = append(newClientOrderIdList, req.OrderSn)
err = futApi.CancelBatchFutOrder(apiUserInfo, req.Symbol, newClientOrderIdList)
}
if err != nil {
*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", err.Error()))
return
}
} else {
var orderList []models.LinePreOrder
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND main_id = 0 status = '5'", req.ApiId).Find(&orderList)
if len(orderList) <= 0 {
*errs = append(*errs, fmt.Errorf("没有可撤销的委托"))
return
}
for _, order := range orderList {
if order.SymbolType == global.SYMBOL_SPOT {
err := spotApi.CancelOpenOrderByOrderSn(apiUserInfo, req.Symbol, req.OrderSn)
if err != nil {
*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", err.Error()))
continue
}
} else {
err := futApi.CancelBatchFutOrder(apiUserInfo, order.Symbol, []string{order.OrderSn})
if err != nil {
*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", err.Error()))
continue
}
}
}
}
}
// ClearAll 一键清除数据
func (e *LinePreOrder) ClearAll() error {
_, err := helper.DefaultRedis.BatchDeleteKeys([]string{rediskey.PreSpotOrderList, rediskey.PreFutOrderList})
if err != nil {
e.Log.Errorf("Service RemoveLinePreOrder error:%s \r\n", err)
return err
}
prefixs := []string{
"spot_stoploss_list",
"futures_stoploss_list",
"spot_add_position_list",
"futures_add_position_list",
"api_user_hold",
"spot_trigger_lock",
"fut_trigger_lock",
"fut_trigger_stop_lock",
"spot_trigger_stop_lock",
"spot_addposition_trigger",
"fut_addposition_trigger",
"spot_hedge_close_position",
"futures_hedge_close_position",
"spot_callback",
"fut_callback",
"holde_a",
"holde_b",
"stop_loss_markt",
"_PreSpotOrderList_",
"_PreFutOrderList_",
"spot_reduce_list",
"futures_reduce_list",
}
err = helper.DefaultRedis.DeleteKeysByPrefix(prefixs...)
if err != nil {
e.Log.Errorf("Service RemoveLinePreOrder error:%s \r\n", err)
return err
}
e.Orm.Model(&models.LinePreOrder{}).Exec("TRUNCATE TABLE line_pre_order") //订单表
e.Orm.Model(&models.LinePreOrder{}).Exec("TRUNCATE TABLE line_pre_order_status") //订单拓展状态
e.Orm.Model(&models.LinePreOrder{}).Exec("TRUNCATE TABLE line_pre_order_ext") //订单拓展配置
return err
}
// GetTargetSymbol 获取目标交易对信息
func (e *LinePreOrder) GetTargetSymbol(symbol string, symbolType int) (string, bool, models.LineSymbol, error) {
var targetSymbol string
var notUsdt bool
var symbolInfo models.LineSymbol
// 处理非 USDT 交易对
if !strings.HasSuffix(symbol, "USDT") {
notUsdt = true
if err := e.Orm.Model(&models.LineSymbol{}).Where("symbol = ? AND type = ?", symbol, utility.IntToString(symbolType)).Find(&symbolInfo).Error; err != nil {
return "", false, models.LineSymbol{}, err
}
if symbolInfo.Id <= 0 {
return "", false, models.LineSymbol{}, fmt.Errorf("未找到交易对信息")
}
targetSymbol = symbolInfo.BaseAsset + "USDT"
} else {
targetSymbol = symbol
}
return targetSymbol, notUsdt, symbolInfo, nil
}
func (e *LinePreOrder) GetOrderInfo(req dto.ManuallyCover, symbol, orderType, site, status string) (models.LinePreOrder, error) {
var orderInfo models.LinePreOrder
if err := e.Orm.Model(models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND site = ? AND status = ?", req.ApiId, symbol, orderType, site, status).Find(&orderInfo).Error; err != nil {
return models.LinePreOrder{}, err
}
if orderInfo.Id <= 0 {
return models.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
}
return orderInfo, nil
}
func (e *LinePreOrder) GetFutOrderInfo(req dto.ManuallyCover, symbol, orderType, status string) (models.LinePreOrder, error) {
var orderInfo models.LinePreOrder
if err := e.Orm.Model(models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 2", req.ApiId, symbol, orderType, status).Find(&orderInfo).Error; err != nil {
return models.LinePreOrder{}, err
}
if orderInfo.Id <= 0 {
return models.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
}
return orderInfo, nil
}
// GetFutSpotOrderInfo 获取合约对现货的订单信息
func (e *LinePreOrder) GetFutSpotOrderInfo(req dto.ManuallyCover, symbol, orderType, status string) (models.LinePreOrder, error) {
var orderInfo models.LinePreOrder
if err := e.Orm.Model(models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 3", req.ApiId, symbol, orderType, status).Find(&orderInfo).Error; err != nil {
return models.LinePreOrder{}, err
}
if orderInfo.Id <= 0 {
return models.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
}
return orderInfo, nil
}
// CalculateAmount 计算加仓数量
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.SafeDiv(utility.StringToDecimal(req.Value), decimal.NewFromInt(100))
amt = totalNum.Mul(decimalValue)
} else {
decimalValue := utility.StringToDecimal(req.Value)
if notUsdt {
tickerSymbolMaps := make([]dto.Ticker, 0)
tickerSymbol := helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
if err := sonic.Unmarshal([]byte(tickerSymbol), &tickerSymbolMaps); err != nil {
return decimal.Zero, err
}
var tickerPrice decimal.Decimal
for _, symbolMap := range tickerSymbolMaps {
if symbolMap.Symbol == strings.ToUpper(symbolInfo.BaseAsset+"USDT") {
tickerPrice, _ = decimal.NewFromString(symbolMap.Price)
break
}
}
for _, symbolMap := range tickerSymbolMaps {
if symbolMap.Symbol == strings.ToUpper(symbolInfo.QuoteAsset+"USDT") {
uTickerPrice, _ := decimal.NewFromString(symbolMap.Price)
div := utility.SafeDiv(tickerPrice, decimal.NewFromInt(1).Div(uTickerPrice))
amt = utility.SafeDiv(decimalValue, div)
break
}
}
} else {
amt = decimalValue.Div(lastPrice)
}
}
return amt.Truncate(int32(amountDigit)), nil
}
// SpotClosePosition 现货单个交易对平仓
func (e *LinePreOrder) SpotClosePosition(position *dto.ClosePosition, errs *[]error) {
var apiUserInfo models.LineApiUser
e.Orm.Model(&models.LineApiUser{}).Where("id = ?", position.ApiId).Find(&apiUserInfo)
client := binanceservice.GetClient(&apiUserInfo)
resp, _, err := client.SendSpotAuth("/api/v3/account", "GET", map[string]interface{}{
"omitZeroBalances": true,
})
if err != nil {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 获取账户信息失败", position.ApiId)))
return
}
var balanceInfo binanceservice.SpotAccountInfo
sonic.Unmarshal(resp, &balanceInfo)
if len(balanceInfo.Balances) == 0 {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 没有可平仓的交易对", position.ApiId)))
}
//查询已经开仓的现货交易对
var spotList []models.LinePreOrder
if position.Symbol == "" { //全平
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol_type =1 AND status = 6 AND pid = 0 AND order_type = 0", position.ApiId).Find(&spotList)
} else {
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol_type =1 AND symbol = ? AND status = 6 AND pid = 0 AND order_type = 0", position.ApiId, position.Symbol).Find(&spotList)
}
if len(spotList) <= 0 {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 没有可平仓的交易对", position.ApiId)))
}
api := binanceservice.SpotRestApi{}
if len(spotList) == 0 {
*errs = append(*errs, errors.New("无仓可平"))
return
}
for _, list := range spotList {
for _, balance := range balanceInfo.Balances {
suffix := utility.ReplaceSuffix(list.Symbol, list.QuoteSymbol, "")
if utility.StringToDecimal(balance.Free).GreaterThan(decimal.Zero) && balance.Asset == suffix {
//锁仓数量大于0
if utility.StringToDecimal(balance.Locked).GreaterThan(decimal.Zero) {
//撤销之前的委托
client.SendSpotAuth("/api/v3/openOrders", "DELETE", map[string]string{
"symbol": list.Symbol,
})
}
key := fmt.Sprintf(global.TICKER_SPOT, list.ExchangeType, list.Symbol)
tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key)
total := utility.StringToDecimal(balance.Free).Add(utility.StringToDecimal(balance.Locked)).Truncate(int32(tradeSet.AmountDigit))
lastPrice := api.GetSpotSymbolLastPrice(list.Symbol)
var price decimal.Decimal
paramsMaps := make(map[string]string)
if total.GreaterThan(decimal.NewFromFloat(tradeSet.MinQty)) {
paramsMaps = map[string]string{
"symbol": list.Symbol,
"side": "SELL",
"quantity": total.String(),
"type": "LIMIT",
"newClientOrderId": utility.Int64ToString(snowflakehelper.GetOrderId()),
"timeInForce": "GTC",
}
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)))
continue
}
order := models.LinePreOrder{
ExchangeType: global.EXCHANGE_BINANCE,
Pid: list.Id,
MainId: list.Id,
ApiId: list.ApiId,
GroupId: "0",
Symbol: list.Symbol,
SymbolType: position.CloseType,
QuoteSymbol: list.QuoteSymbol,
SignPrice: lastPrice.String(),
SignPriceType: "new",
Rate: position.Rate,
Price: price.String(),
Num: total.String(),
BuyPrice: "0",
Site: "SELL",
OrderSn: paramsMaps["newClientOrderId"],
OrderType: 3,
Desc: "",
Status: 0,
CoverType: list.CoverType,
ExpireTime: time.Now().Add(time.Hour * 24 * 30),
MainOrderType: list.MainOrderType,
Child: nil,
OrderCategory: 1,
}
err := e.Orm.Model(&models.LinePreOrder{}).Create(&order).Error
if err != nil {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 生成订单失败:%s", position.ApiId, err.Error())))
continue
}
//下订单
_, _, err = client.SendSpotAuth("/api/v3/order", "POST", paramsMaps)
if err != nil {
*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 币安下订单失败:%s", position.ApiId, err.Error())))
continue
}
binanceservice.MainClosePositionClearCache(list.Id, list.SymbolType)
}
}
}
}
// FutClosePosition 合约平仓
func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]error) {
var apiUserInfo models.LineApiUser
e.Orm.Model(&models.LineApiUser{}).Where("id = ?", position.ApiId).Find(&apiUserInfo)
//client := binanceservice.GetClient(&apiUserInfo)
//查询已经开仓的合约交易对
var futList []models.LinePreOrder
if position.Symbol == "" {
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 {
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))
return
}
api := binanceservice.FutRestApi{}
if len(futList) == 0 {
*errs = append(*errs, errors.New("无仓可平"))
return
}
for _, list := range futList {
risks, err := api.GetPositionV3(&apiUserInfo, list.Symbol)
if err != nil {
*errs = append(*errs, fmt.Errorf("api_id:%d 获取仓位信息时 没有可平仓的交易对 err: %s", position.ApiId, err))
continue
}
key := fmt.Sprintf(global.TICKER_FUTURES, list.ExchangeType, list.Symbol)
tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key)
lastPrice := api.GetFutSymbolLastPrice(list.Symbol)
parentId := list.Id
if list.Pid > 0 {
parentId = list.Pid
}
for _, risk := range risks {
positionAmt := utility.StringToDecimal(risk.PositionAmt)
var riskSide string
var orderSide string
if positionAmt.GreaterThan(decimal.Zero) {
riskSide = "BUY"
} else {
riskSide = "SELL"
}
if riskSide == list.Site {
var price decimal.Decimal
if list.Site == "BUY" && risk.PositionSide == "LONG" { //做多
//根据仓位数量去平多
orderSide = "SELL"
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.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
}
if price.LessThanOrEqual(decimal.Zero) {
continue
}
if positionAmt.Abs().LessThanOrEqual(decimal.NewFromFloat(tradeSet.MinQty)) {
*errs = append(*errs, fmt.Errorf("api_id:%d 下单数量小于最小下单数量", position.ApiId))
continue
}
order := models.LinePreOrder{
Pid: parentId,
ExchangeType: list.ExchangeType,
ApiId: list.ApiId,
MainId: list.Id,
GroupId: "0",
Symbol: list.Symbol,
SymbolType: position.CloseType,
QuoteSymbol: list.QuoteSymbol,
SignPrice: lastPrice.String(),
SignPriceType: "new",
Rate: position.Rate,
Price: price.String(),
Num: positionAmt.Abs().String(),
BuyPrice: "0",
Site: orderSide,
OrderSn: utility.Int64ToString(snowflakehelper.GetOrderId()),
OrderType: 3,
Desc: "",
Status: 0,
CoverType: list.CoverType,
ExpireTime: time.Now().Add(24 * 30 * time.Hour),
MainOrderType: list.MainOrderType,
Child: nil,
OrderCategory: 1,
}
err = e.Orm.Model(&models.LinePreOrder{}).Create(&order).Error
if err != nil {
*errs = append(*errs, fmt.Errorf("api_id:%d 生成平仓单错误:%s", position.ApiId, err))
continue
}
//撤销合约的委托(根据方向撤)
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是开多
//side=SELL&positionSide=LONG是平多
//side=SELL&positionSide=SHORT是开空
//side=BUY&positionSide=SHORT是平空。
if orderSide != "" {
if orderSide == "BUY" { //平空
err = api.ClosePosition(list.Symbol, order.OrderSn, utility.StringToDecimal(order.Num), "BUY", "SHORT", apiUserInfo, "LIMIT", "0", price)
} else { // 平多
err = api.ClosePosition(list.Symbol, order.OrderSn, utility.StringToDecimal(order.Num), "SELL", "LONG", apiUserInfo, "LIMIT", "0", price)
}
if err != nil {
*errs = append(*errs, fmt.Errorf("api_id:%d 币安平仓单错误:%s", position.ApiId, err))
continue
}
// 主单平仓删除缓存
binanceservice.MainClosePositionClearCache(list.Id, list.SymbolType)
}
}
}
}
}
// ClearUnTriggered 清除待触发的交易对
func (e *LinePreOrder) ClearUnTriggered() error {
var orderLists []models.LinePreOrder
positions := map[string]positiondto.LinePreOrderPositioinDelReq{}
e.Orm.Model(&models.LinePreOrder{}).Where("main_id = 0 AND pid = 0 AND status = '0'").Find(&orderLists).Unscoped().Delete(&models.LinePreOrder{})
for _, order := range orderLists {
redisList := dto.PreOrderRedisList{
Id: order.Id,
Symbol: order.Symbol,
Price: order.Price,
Site: order.Site,
ApiId: order.ApiId,
OrderSn: order.OrderSn,
QuoteSymbol: order.QuoteSymbol,
}
tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, fmt.Sprintf(global.TICKER_SPOT, order.ExchangeType, order.Symbol))
redisList.Price = utility.StringToDecimal(redisList.Price).Truncate(int32(tradeSet.PriceDigit)).String()
marshal, _ := sonic.Marshal(redisList)
if order.SymbolType == 1 {
key := fmt.Sprintf(rediskey.PreFutOrderList, order.ExchangeType)
helper.DefaultRedis.LRem(key, string(marshal))
} else {
key := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType)
helper.DefaultRedis.LRem(key, string(marshal))
}
//会影响持仓的
removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType)
if _, ok := positions[removeSymbolKey]; !ok {
positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{
ApiId: order.ApiId,
Symbol: order.Symbol,
ExchangeType: order.ExchangeType,
Side: order.Site,
SymbolType: order.SymbolType,
}
}
e.Orm.Model(&models.LinePreOrder{}).Where("main_id = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{})
}
//清理仓位缓存
for _, v := range positions {
var count int64
e.Orm.Model(&models.LinePreOrder{}).
Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6",
v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count)
//没有已开仓的订单 直接清理仓位
if count == 0 {
var key string
if v.SymbolType == 1 {
key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side)
} else {
key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side)
}
helper.DefaultRedis.DeleteString(key)
}
}
return nil
}
func (e *LinePreOrder) QueryOrder(req *dto.QueryOrderReq) (res interface{}, err error) {
var apiUserInfo models.LineApiUser
e.Orm.Model(&models.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo)
var client *helper.BinanceClient
if apiUserInfo.UserPass == "" {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress)
} else {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress)
}
params := map[string]string{"symbol": req.Symbol, "origClientOrderId": req.OrderSn}
if req.OrderType == 1 { //现货
var resp dto.QuickAddPreOrderReq
auth, code, err := client.SendSpotAuth("/api/v3/order", "GET", params)
if code != 200 {
return nil, err
}
if err != nil {
return nil, err
}
sonic.Unmarshal(auth, &resp)
return resp, nil
}
if req.OrderType == 2 {
var resp dto.FutQueryOrderResp
auth, code, err := client.SendFuturesRequestAuth("/fapi/v1/order", "GET", params)
if code != 200 {
return nil, err
}
if err != nil {
return nil, err
}
sonic.Unmarshal(auth, &resp)
return resp, nil
}
return nil, err
}
func (e *LinePreOrder) QueryAiCoinPrice(req *dto.QueryAiCoinPriceReq) (models.LineDirection, error) {
var info models.LineDirection
err := e.Orm.Model(&models.LineDirection{}).Where("symbol = ?", req.Symbol).Find(&info).Error
return info, err
}
// 根据请求参数重新生成亏损回本止盈百分比
func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error {
var tradeSet models2.TradeSet
var tickerPrice decimal.Decimal
if req.SymbolType == 1 {
tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0)
} else {
tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1)
}
if tradeSet.LastPrice == "" {
return errors.New("获取不到交易对信息")
}
var price decimal.Decimal
tickerPrice = utility.StrToDecimal(tradeSet.LastPrice)
if req.PricePattern == "percentage" {
orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10%
priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1
if strings.ToUpper(req.Site) == "BUY" { //购买方向
//实际下单价格
price = tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit))
} else {
price = tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit))
}
} else { //实际价格下单
price = utility.StringToDecimal(req.Price).Truncate(int32(tradeSet.PriceDigit))
}
buyPrice := utility.StrToDecimal(req.BuyPrice)
mainAmount := buyPrice.Div(price).Truncate(int32(tradeSet.AmountDigit))
calculateResp := dto.CalculateBreakEvenRatioResp{}
lossBeginPercent := decimal.Zero
mainParam := dto.CalculateBreakEevenRatioReq{
Price: price,
ExchangeType: req.ExchangeType,
Symbol: req.Symbol,
SymbolType: req.SymbolType,
BuyPrice: buyPrice,
LossBeginPercent: lossBeginPercent,
LossEndPercent: req.ReducePriceRatio,
AddPositionType: 1,
AddPositionVal: req.ReduceNumRatio,
}
//计算减仓后
mainParam.LossEndPercent = req.ReducePriceRatio
mainParam.RemainingQuantity = mainAmount
mainParam.AddType = 2
e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet)
mainParam.RemainingQuantity = calculateResp.RemainingQuantity
mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU
req.ReduceReTakeProfitRatio = calculateResp.Ratio
lossBeginPercent = req.ReducePriceRatio
//顺序排序
sort.Slice(req.Ext, func(i, j int) bool {
return req.Ext[i].PriceRatio.Cmp(req.Ext[j].PriceRatio) < 0
})
for index := range req.Ext {
mainParam.AddType = req.Ext[index].AddType
mainParam.LossBeginPercent = lossBeginPercent
mainParam.LossEndPercent = req.Ext[index].PriceRatio
mainParam.AddPositionType = req.Ext[index].AddPositionType
mainParam.AddPositionVal = req.Ext[index].AddPositionVal
e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet)
req.Ext[index].ReTakeProfitRatio = calculateResp.Ratio
lossBeginPercent = req.Ext[index].PriceRatio
mainParam.RemainingQuantity = calculateResp.RemainingQuantity
mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU
}
return nil
}
// 计算亏损百分比
func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatioReq, data *dto.CalculateBreakEvenRatioResp, tradeSet models2.TradeSet) error {
if req.LossEndPercent.Cmp(req.LossBeginPercent) < 0 {
return errors.New("截至亏损百分比必须大于开始亏损百分比")
}
var addPositionBuyPrice decimal.Decimal
if req.AddType == 1 && req.AddPositionType == 1 {
addPositionBuyPrice = req.BuyPrice.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(2)
} else if req.AddType == 1 {
addPositionBuyPrice = req.AddPositionVal.Truncate(2)
}
var percentDiff decimal.Decimal
var reduceAmount decimal.Decimal
nowPrice := req.Price.Mul(decimal.NewFromInt(1).Sub(req.LossEndPercent.Div(decimal.NewFromInt(100)).Truncate(4))).Truncate(int32(tradeSet.PriceDigit))
addPositionAmount := addPositionBuyPrice.Div(nowPrice).Truncate(int32(tradeSet.AmountDigit))
//计算价格下跌价差
if req.LossEndPercent.Cmp(req.LossBeginPercent) > 0 {
percentDiff = req.LossEndPercent.Sub(req.LossBeginPercent).Abs()
}
totalAmount := req.RemainingQuantity.Add(addPositionAmount)
lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit))
//计算减仓数量
if req.AddType == 2 && req.AddPositionType == 1 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 {
reduceAmount = totalAmount.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit))
}
data.RemainingQuantity = totalAmount.Sub(reduceAmount)
data.TotalLossAmountU = lossAmountU.Add(req.TotalLossAmountU)
//计算百分比
if data.RemainingQuantity.Cmp(decimal.Zero) > 0 {
data.Ratio = data.TotalLossAmountU.Div(data.RemainingQuantity).Div(nowPrice).Mul(decimal.NewFromInt(100)).Truncate(2)
} else {
data.Ratio = decimal.Zero
}
return nil
}
// // 手动加仓
// func (e *LinePreOrder) AddPosition(req *dto.LinePreOrderAddPositionReq) error {
// lastPositionOrder := models.LinePreOrder{}
// var tradeSet models2.TradeSet
// if req.OrderType == 1 {
// tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0)
// } else if req.OrderType == 2 {
// tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1)
// } else {
// return fmt.Errorf("交易对:%s 订单类型错误", req.Symbol)
// }
// if tradeSet.LastPrice == "" {
// return fmt.Errorf("交易对:%s 交易对配置错误", req.Symbol)
// }
// if err := e.Orm.Model(&lastPositionOrder).Where("symbol =? AND status = 6 AND site =? AND api_id =? AND symbol_type =? AND exchange_type=?",
// req.Symbol, req.Site, req.ApiUserId, req.OrderType, req.ExchangeType).Error; err != nil {
// logger.Errorf("交易对:%s查询已开仓订单失败", req.Symbol)
// return fmt.Errorf("交易对:%s 没有已开仓的订单", req.Symbol)
// }
// ext := models.LinePreOrderExt{
// MainOrderId: lastPositionOrder.Id,
// TakeProfitRatio: req.Profit,
// ReducePriceRatio: req.ReducePriceRatio,
// ReduceNumRatio: req.ReduceNumRatio,
// ReduceTakeProfitRatio: req.ReduceTakeProfitRatio,
// ReduceStopLossRatio: req.ReduceStopLossRatio,
// AddPositionOrderType: req.AddPositionOrderType,
// AddPositionType: 2,
// AddPositionVal: req.BuyPrice,
// }
// addPosition := models.LinePreOrder{
// SignPrice: tradeSet.LastPrice,
// Pid: lastPositionOrder.Id,
// MainId: lastPositionOrder.Id,
// Symbol: req.Symbol,
// QuoteSymbol: tradeSet.Currency,
// SignPriceU: utility.StrToDecimal(tradeSet.LastPrice),
// ApiId: req.ApiUserId,
// Site: req.Site,
// ExchangeType: req.ExchangeType,
// OrderType: 0,
// OrderCategory: 3,
// BuyPrice: req.BuyPrice.String(),
// Status: 0,
// SymbolType: req.OrderType,
// MainOrderType: req.AddPositionOrderType,
// ExpireTime: time.Now().Add(4),
// OrderSn: utility.Int64ToString(snowflakehelper.GetOrderId()),
// }
// tickerPrice := utility.StrToDecimal(tradeSet.LastPrice)
// if req.PricePattern == "percentage" {
// addPosition.Rate = req.Price.String()
// priceRate := req.Price.Div(decimal.NewFromInt(100)) //下单价除100 =0.1
// if strings.ToUpper(req.Site) == "BUY" { //购买方向
// //实际下单价格
// truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit))
// addPosition.Price = truncate.String()
// } else {
// truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit))
// addPosition.Price = truncate.String()
// }
// } else { //实际价格下单
// addPosition.Price = req.Price.Truncate(int32(tradeSet.PriceDigit)).String()
// addPosition.SignPriceType = req.PricePattern
// addPosition.Rate = "0"
// }
// if tradeSet.Currency != "USDT" { //不是U本位
// //获取币本位兑换u的价格
// ticker2 := models2.TradeSet{}
// tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(tradeSet.Coin+"USDT")))
// if tickerVal == "" {
// logger.Error("查询行情失败")
// return fmt.Errorf("交易对:%s 获取u本位行情失败", req.Symbol)
// }
// err := sonic.Unmarshal([]byte(tickerVal), &ticker2)
// if ticker2.LastPrice == "" {
// logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(tradeSet.Coin+"USDT"), err)
// return fmt.Errorf("交易对:%s 获取u本位行情 反序列化失败", req.Symbol)
// }
// //LTCBTC --> LTCUSDT
// uTickerPrice := utility.StrToDecimal(ticker2.LastPrice) //94069
// //换算成U
// //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365
// //在换算成对应交易对对应的价值
// //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502
// div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
// //计算下单数量
// addPosition.Num = req.BuyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String()
// } else {
// fromString, _ := decimal.NewFromString(addPosition.Price)
// addPosition.Num = req.BuyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String()
// }
// //事务保存
// err := e.Orm.Transaction(func(tx *gorm.DB) error {
// //添加加仓单
// if err := tx.Create(&addPosition).Error; err != nil {
// return err
// }
// //止盈、减仓
// orders, err := makeFuturesTakeAndReduce(&addPosition, ext, tradeSet)
// if err != nil {
// logger.Error("构造加仓单止盈、减仓失败")
// return err
// }
// if err := e.Orm.Create(&orders).Error; err != nil {
// logger.Error("保存加仓单止盈、减仓失败")
// return err
// }
// //处理减仓单
// for index := range orders {
// //减仓单且 减仓比例大于0 小于100 就冲下止盈止损
// if orders[index].OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 && ext.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
// reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), ext, tradeSet)
// if err != nil {
// logger.Error("生产加仓单止盈、减仓失败")
// return err
// }
// if len(reduceChildOrders) == 0 {
// continue
// }
// if err := e.Orm.Create(&reduceChildOrders).Error; err != nil {
// logger.Error("报错减仓后止盈止损失败")
// return err
// }
// }
// }
// ext.OrderId = addPosition.Id
// if err := tx.Create(&ext).Error; err != nil {
// return err
// }
// return nil
// })
// if err != nil {
// logger.Errorf("交易对:%s 添加加仓订单失败", req.Symbol)
// return fmt.Errorf("交易对:%s 添加加仓订单失败", req.Symbol)
// }
// return nil
// }