2179 lines
77 KiB
Go
2179 lines
77 KiB
Go
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
|
||
// }
|