1262 lines
45 KiB
Go
1262 lines
45 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/pkg/utility"
|
||
"go-admin/pkg/utility/snowflakehelper"
|
||
"go-admin/services/binanceservice"
|
||
"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
|
||
}
|
||
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)
|
||
//for _, order := range list {
|
||
//if order.Status != "0" {
|
||
// return errors.New(fmt.Sprintf("订单id %d 已被触发 无法被删除", order.Id))
|
||
//}
|
||
//}
|
||
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("无权删除该数据")
|
||
}
|
||
|
||
//删除的缓存
|
||
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)
|
||
}
|
||
|
||
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))
|
||
}
|
||
|
||
binanceservice.MainClosePositionClearCache(order.Id, order.CoverType)
|
||
|
||
ints = append(ints, order.Id)
|
||
}
|
||
|
||
if len(ints) > 0 {
|
||
e.Orm.Model(&models.LinePreOrder{}).Where("pid >0 AND pid in ?", ints).Unscoped().Delete(&models.LinePreOrder{})
|
||
}
|
||
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
|
||
}
|
||
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)
|
||
orderCount := e.CheckRepeatOrder(req.OrderType, id, req.Site, tradeSet.Coin)
|
||
if orderCount > 0 {
|
||
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol))
|
||
continue
|
||
}
|
||
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
|
||
if req.PricePattern == "percentage" {
|
||
AddOrder.Rate = req.Price
|
||
orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10%
|
||
priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1
|
||
AddOrder.SignPrice = tickerPrice.String()
|
||
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.SignPrice = req.Price
|
||
AddOrder.SignPriceType = "mixture"
|
||
AddOrder.Rate = "0"
|
||
}
|
||
buyPrice, _ := decimal.NewFromString(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 := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
|
||
//计算下单数量
|
||
AddOrder.Num = buyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String()
|
||
} else {
|
||
fromString, _ := decimal.NewFromString(AddOrder.Price)
|
||
AddOrder.Num = buyPrice.Div(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{
|
||
TakeProfitRatio: req.TakeProfitRatio,
|
||
ReducePriceRatio: req.ReducePriceRatio,
|
||
ReduceNumRatio: req.ReduceNumRatio,
|
||
ReduceTakeProfitRatio: req.ReduceTakeProfitRatio,
|
||
ReduceStopLossRatio: req.ReduceStopLossRatio,
|
||
}
|
||
preOrderExts = append(preOrderExts, defultExt)
|
||
|
||
for _, addPosition := range req.Ext {
|
||
ext := models.LinePreOrderExt{
|
||
TakeProfitRatio: addPosition.TakeProfitRatio,
|
||
ReducePriceRatio: addPosition.ReducePriceRatio,
|
||
ReduceNumRatio: addPosition.ReduceNumRatio,
|
||
ReduceTakeProfitRatio: addPosition.ReduceTakeProfitRatio,
|
||
ReduceStopLossRatio: addPosition.ReduceStopLossRatio,
|
||
AddPositionPriceRatio: addPosition.AddPositionPriceRatio,
|
||
AddPositionType: addPosition.AddPositionType,
|
||
AddPositionVal: addPosition.AddPositionVal,
|
||
}
|
||
|
||
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)
|
||
|
||
for index := range preOrderExts {
|
||
if index == 0 {
|
||
preOrderExts[index].OrderId = AddOrder.Id
|
||
}
|
||
|
||
preOrderExts[index].MainOrderId = 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
|
||
|
||
tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder)
|
||
}
|
||
|
||
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(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||
} else {
|
||
stopOrder.Site = "BUY"
|
||
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(req.ReducePriceRatio.Div(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.Rate = req.ReducePriceRatio.String()
|
||
|
||
tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder)
|
||
}
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// CheckRepeatOrder 检查重复下单 检查基础货币
|
||
func (e *LinePreOrder) CheckRepeatOrder(orderType int, apiUserId, site, baseCoin string) int64 {
|
||
var count int64
|
||
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol like ? AND order_type = ? AND site = ? AND `status` IN (1,5)", apiUserId, baseCoin+"%", orderType, 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.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 ? ", 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, ",")
|
||
}
|
||
}
|
||
|
||
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
|
||
}
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
// 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, ",")
|
||
}
|
||
}
|
||
|
||
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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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 pid = 0 status = '5' AND order_type = ?", req.ApiId, strconv.Itoa(req.OrderType)).Find(&orderList)
|
||
if len(orderList) <= 0 {
|
||
*errs = append(*errs, fmt.Errorf("没有可撤销的委托"))
|
||
return
|
||
}
|
||
for _, order := range orderList {
|
||
|
||
if order.OrderType == 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_",
|
||
}
|
||
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.StringToDecimal(req.Value).Div(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 := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
|
||
amt = decimalValue.Div(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 status = 6 AND pid = 0 AND order_type = 0", position.ApiId).Find(&spotList)
|
||
} else {
|
||
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? 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{}
|
||
|
||
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.StringToDecimal(position.Rate).Div(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,
|
||
ApiId: list.ApiId,
|
||
GroupId: "0",
|
||
Symbol: list.Symbol,
|
||
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,
|
||
}
|
||
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.CoverType)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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 == "" {
|
||
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND order_category=2 AND order_type =0", position.ApiId).Find(&futList)
|
||
} else {
|
||
e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND status = 6 AND order_category=2 AND order_type = 0", position.ApiId, position.Symbol).Find(&futList)
|
||
}
|
||
if len(futList) <= 0 {
|
||
*errs = append(*errs, fmt.Errorf("api_id:%d 没有可平仓的交易对", position.ApiId))
|
||
return
|
||
}
|
||
|
||
api := binanceservice.FutRestApi{}
|
||
|
||
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.StringToDecimal(position.Rate).Div(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.StringToDecimal(position.Rate).Div(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,
|
||
ApiId: list.ApiId,
|
||
GroupId: "0",
|
||
Symbol: list.Symbol,
|
||
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,
|
||
}
|
||
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
|
||
}
|
||
|
||
//撤销合约的委托
|
||
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.CoverType)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// ClearUnTriggered 清除待触发的交易对
|
||
func (e *LinePreOrder) ClearUnTriggered() error {
|
||
var orderLists []models.LinePreOrder
|
||
e.Orm.Model(&models.LinePreOrder{}).Where("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))
|
||
}
|
||
e.Orm.Model(&models.LinePreOrder{}).Where("pid = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{})
|
||
}
|
||
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
|
||
}
|