1、减仓策略
2、减仓后减仓节点(60%)
This commit is contained in:
@ -1,14 +1,12 @@
|
||||
package binanceservice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
DbModels "go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
"go-admin/models"
|
||||
"go-admin/models/positiondto"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/positionservice"
|
||||
@ -27,32 +25,6 @@ type AddPosition struct {
|
||||
Db *gorm.DB
|
||||
}
|
||||
|
||||
// 获取缓存交易对
|
||||
// symbolType 0-现货 1-合约
|
||||
func GetTradeSet(symbol string, symbolType int) (models.TradeSet, error) {
|
||||
result := models.TradeSet{}
|
||||
val := ""
|
||||
|
||||
switch symbolType {
|
||||
case 0:
|
||||
key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BINANCE, symbol)
|
||||
val, _ = helper.DefaultRedis.GetString(key)
|
||||
case 1:
|
||||
key := fmt.Sprintf(global.TICKER_FUTURES, global.EXCHANGE_BINANCE, symbol)
|
||||
val, _ = helper.DefaultRedis.GetString(key)
|
||||
}
|
||||
|
||||
if val != "" {
|
||||
if err := sonic.Unmarshal([]byte(val), &result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
} else {
|
||||
return result, errors.New("未找到交易对信息")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetRisk(futApi *FutRestApi, apiInfo *DbModels.LineApiUser, symbol string) []PositionRisk {
|
||||
for x := 0; x < 5; x++ {
|
||||
risks, _ := futApi.GetPositionV3(apiInfo, symbol)
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
"go-admin/models"
|
||||
"go-admin/services/cacheservice"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -151,19 +152,37 @@ func JudgeFuturesReduce(trade models.TradeSet) {
|
||||
key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE)
|
||||
reduceVal, _ := helper.DefaultRedis.GetAllList(key)
|
||||
|
||||
if len(reduceVal) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
db := GetDBConnection()
|
||||
futApi := FutRestApi{}
|
||||
setting, err := GetSystemSetting(db)
|
||||
setting, err := cacheservice.GetSystemSetting(db)
|
||||
|
||||
if err != nil {
|
||||
log.Error("获取系统设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
||||
//减仓单减仓策略
|
||||
orderReduceVal, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE))
|
||||
reduceOrderStrategy := dto.LineOrderReduceStrategyResp{}
|
||||
|
||||
for _, item := range orderReduceVal {
|
||||
sonic.Unmarshal([]byte(item), &reduceOrderStrategy)
|
||||
|
||||
for _, item2 := range reduceOrderStrategy.Items {
|
||||
if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency {
|
||||
//买入
|
||||
if item2.TriggerPrice.Cmp(decimal.Zero) > 0 &&
|
||||
tradePrice.Cmp(decimal.Zero) > 0 &&
|
||||
((strings.ToUpper(reduceOrderStrategy.Side) == "SELL" && item2.TriggerPrice.Cmp(tradePrice) >= 0) ||
|
||||
(strings.ToUpper(reduceOrderStrategy.Side) == "BUY" && item2.TriggerPrice.Cmp(tradePrice) <= 0)) {
|
||||
//todo 生成订单并触发
|
||||
// SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range reduceVal {
|
||||
reduceOrder := ReduceListItem{}
|
||||
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
|
||||
@ -173,7 +192,6 @@ func JudgeFuturesReduce(trade models.TradeSet) {
|
||||
|
||||
if reduceOrder.Symbol == trade.Coin+trade.Currency {
|
||||
orderPrice := reduceOrder.Price
|
||||
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
||||
//买入
|
||||
if orderPrice.Cmp(decimal.Zero) > 0 &&
|
||||
tradePrice.Cmp(decimal.Zero) > 0 &&
|
||||
@ -188,7 +206,7 @@ func JudgeFuturesReduce(trade models.TradeSet) {
|
||||
|
||||
// 触发合约减仓
|
||||
func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, key, item string) {
|
||||
tradeSet, _ := GetTradeSet(reduceOrder.Symbol, 1)
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reduceOrder.Symbol, 1)
|
||||
|
||||
if tradeSet.LastPrice == "" {
|
||||
return
|
||||
@ -239,17 +257,6 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes
|
||||
positionSide = "LONG"
|
||||
}
|
||||
|
||||
// params := FutOrderPlace{
|
||||
// ApiId: reduceOrder.ApiId,
|
||||
// Side: reduceOrder.Side,
|
||||
// OrderType: "STOP",
|
||||
// Symbol: reduceOrder.Symbol,
|
||||
// Price: price,
|
||||
// StopPrice: price,
|
||||
// Quantity: num,
|
||||
// NewClientOrderId: reduceOrder.OrderSn,
|
||||
// }
|
||||
|
||||
if err := futApi.ClosePositionLoop(reduceOrder.Symbol, reduceOrder.OrderSn, num, reduceOrder.Side, positionSide, apiInfo, "LIMIT", "0", price, 3); err != nil {
|
||||
log.Errorf("合约减仓挂单失败 id:%s err:%v", reduceOrder.Id, err)
|
||||
|
||||
@ -267,6 +274,9 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes
|
||||
Where("id = ? AND status =0", reduceOrder.Id).Updates(map[string]interface{}{"status": 1}).Error; err != nil {
|
||||
log.Errorf("合约减仓更新状态失败 id:%s err:%v", reduceOrder.Id, err)
|
||||
}
|
||||
|
||||
//处理减仓单减仓策略
|
||||
CacheOrderStrategyAndReCreate(db, reduceOrder, 2, tradeSet, setting)
|
||||
}
|
||||
|
||||
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
|
||||
@ -322,8 +332,8 @@ func FutAddPositionTrigger(db *gorm.DB, v *AddPositionList, item string, futApi
|
||||
} else if ok {
|
||||
defer lock.Release()
|
||||
|
||||
setting, _ := GetSystemSetting(db)
|
||||
tradeSet, _ := GetTradeSet(v.Symbol, 1)
|
||||
setting, _ := cacheservice.GetSystemSetting(db)
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, v.Symbol, 1)
|
||||
|
||||
if tradeSet.LastPrice == "" {
|
||||
log.Errorf("合约加仓触发 查询交易对失败 交易对:%s ordersn:%s", v.Symbol, v.OrderSn)
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
models2 "go-admin/models"
|
||||
"go-admin/models/positiondto"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/cacheservice"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -126,13 +127,15 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
return
|
||||
}
|
||||
|
||||
tradeSet, err := GetTradeSet(preOrder.Symbol, 1)
|
||||
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, preOrder.Symbol, 1)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("handleReduceFilled 获取交易对设置失败,订单号:%s", preOrder.OrderSn)
|
||||
return
|
||||
}
|
||||
|
||||
//修改减仓单减仓策略状态
|
||||
ReduceCallBack(db, preOrder)
|
||||
orderExt := models.LinePreOrderExt{}
|
||||
db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt)
|
||||
// rate := utility.StringAsFloat(orderExt.AddPositionVal)
|
||||
@ -473,7 +476,7 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) {
|
||||
// 处理主单成交,处理止盈、止损、减仓订单
|
||||
func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrderId int, first bool) {
|
||||
// 获取交易对配置和API信息
|
||||
tradeSet, err := GetTradeSet(preOrder.Symbol, 1)
|
||||
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, preOrder.Symbol, 1)
|
||||
mainId := preOrder.Id
|
||||
if err != nil || tradeSet.Coin == "" {
|
||||
logger.Errorf("获取交易对配置失败, 回调订单号:%s, 错误信息: %v", preOrder.OrderSn, err)
|
||||
@ -742,7 +745,7 @@ func processFutReduceOrder(order DbModels.LinePreOrder, price, num decimal.Decim
|
||||
// 处理止盈订单
|
||||
func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.LinePreOrder, num decimal.Decimal) {
|
||||
price, _ := decimal.NewFromString(order.Price)
|
||||
tradeSet, _ := GetTradeSet(order.Symbol, 1)
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
|
||||
|
||||
params := FutOrderPlace{
|
||||
ApiId: order.ApiId,
|
||||
@ -779,7 +782,7 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line
|
||||
// 处理止损订单
|
||||
// order 止损单
|
||||
func processFutStopLossOrder(db *gorm.DB, order models.LinePreOrder, price, num decimal.Decimal) error {
|
||||
tradeSet, _ := GetTradeSet(order.Symbol, 1)
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
|
||||
|
||||
params := FutOrderPlace{
|
||||
ApiId: order.ApiId,
|
||||
|
||||
134
services/binanceservice/reduce_order_strategy_service.go
Normal file
134
services/binanceservice/reduce_order_strategy_service.go
Normal file
@ -0,0 +1,134 @@
|
||||
package binanceservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
models2 "go-admin/models"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 缓存减仓节点和重新生成
|
||||
// reduceOrder:原始减仓单
|
||||
// reduceOrderStrategy:减仓策略
|
||||
func CacheOrderStrategyAndReCreate(db *gorm.DB, reduceOrder ReduceListItem, symbolType int, tradeSet models2.TradeSet, setting models.LineSystemSetting) error {
|
||||
reduceOrderStrategy := models.LineReduceStrategy{}
|
||||
var key string
|
||||
|
||||
if symbolType == 1 {
|
||||
key = fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
||||
} else {
|
||||
key = fmt.Sprintf(rediskey.FutOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
||||
}
|
||||
|
||||
if err := db.Model(&reduceOrderStrategy).Where("order_id =?", reduceOrder.Id).Find(reduceOrderStrategy).Error; err != nil {
|
||||
logger.Errorf("获取减仓策略失败,err:%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if reduceOrderStrategy.Id > 0 {
|
||||
strategyCache := dto.LineOrderReduceStrategyResp{
|
||||
OrderId: reduceOrder.Id,
|
||||
}
|
||||
|
||||
for _, item := range reduceOrderStrategy.Items {
|
||||
var rate decimal.Decimal
|
||||
var triggerRate decimal.Decimal
|
||||
|
||||
if reduceOrder.Side == "BUY" {
|
||||
rate = (decimal.NewFromInt(100).Sub(item.LossPercent)).Div(decimal.NewFromInt(100))
|
||||
|
||||
if setting.ReduceEarlyTriggerPercent.Cmp(decimal.Zero) > 0 {
|
||||
triggerRate = rate.Add(setting.ReduceEarlyTriggerPercent.Div(decimal.NewFromInt(100)))
|
||||
} else {
|
||||
triggerRate = rate
|
||||
}
|
||||
} else {
|
||||
rate = (decimal.NewFromInt(100).Add(item.LossPercent)).Div(decimal.NewFromInt(100))
|
||||
|
||||
if setting.ReduceEarlyTriggerPercent.Cmp(decimal.Zero) > 0 {
|
||||
triggerRate = rate.Sub(setting.ReduceEarlyTriggerPercent.Div(decimal.NewFromInt(100)))
|
||||
} else {
|
||||
triggerRate = rate
|
||||
}
|
||||
}
|
||||
price := reduceOrder.Price.Mul(rate).Truncate(int32(tradeSet.PriceDigit))
|
||||
triggerPrice := reduceOrder.Price.Mul(triggerRate).Truncate(int32(tradeSet.PriceDigit))
|
||||
|
||||
strategyCache.Items = append(strategyCache.Items, dto.LineOrderReduceStrategyRespItem{
|
||||
LossPercent: item.LossPercent,
|
||||
OrderType: item.OrderType,
|
||||
Price: price,
|
||||
TriggerPrice: triggerPrice,
|
||||
})
|
||||
}
|
||||
|
||||
str, _ := sonic.MarshalString(reduceOrderStrategy)
|
||||
|
||||
if str != "" {
|
||||
if err := helper.DefaultRedis.SetString(key, str); err != nil {
|
||||
logger.Errorf("减仓单缓存减仓策略,err:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据订单id获取订单减仓策略
|
||||
func GetReduceStrategyById(db *gorm.DB, orderId int) (models.LineOrderReduceStrategy, error) {
|
||||
result := models.LineOrderReduceStrategy{}
|
||||
|
||||
if err := db.Model(result).Where("order_id =?", orderId).Select("id", "order_id").First(&result).Error; err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 减仓单成功回调
|
||||
func ReduceCallBack(db *gorm.DB, preOrder *models.LinePreOrder) error {
|
||||
reduceOrderId := preOrder.Id
|
||||
var key string
|
||||
if preOrder.ReduceOrderId > 0 {
|
||||
reduceOrderId = preOrder.ReduceOrderId
|
||||
}
|
||||
|
||||
switch preOrder.SymbolType {
|
||||
case 1:
|
||||
key = fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
||||
case 2:
|
||||
key = fmt.Sprintf(rediskey.FutOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
||||
default:
|
||||
return fmt.Errorf("交易对类型错误")
|
||||
}
|
||||
|
||||
arrays, _ := helper.DefaultRedis.GetAllList(key)
|
||||
cache := dto.LineOrderReduceStrategyResp{}
|
||||
|
||||
for _, v := range arrays {
|
||||
sonic.Unmarshal([]byte(v), &cache)
|
||||
|
||||
if cache.OrderId == reduceOrderId {
|
||||
if _, err := helper.DefaultRedis.LRem(key, v); err != nil {
|
||||
logger.Errorf("移除减仓单减仓策略失败redis err:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
orderReduceStrategy, _ := GetReduceStrategyById(db, reduceOrderId)
|
||||
if orderReduceStrategy.Id > 0 {
|
||||
if err := db.Model(&orderReduceStrategy).Update("actived", 1).Error; err != nil {
|
||||
logger.Errorf("更新减仓单减仓策略状态失败 order_id:%d err:%v", orderReduceStrategy.OrderId, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"go-admin/common/helper"
|
||||
"go-admin/models"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/cacheservice"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -141,14 +142,14 @@ func JudgeSpotStopLoss(trade models.TradeSet) {
|
||||
|
||||
db := GetDBConnection()
|
||||
spotApi := SpotRestApi{}
|
||||
setting, err := GetSystemSetting(db)
|
||||
setting, err := cacheservice.GetSystemSetting(db)
|
||||
|
||||
if err != nil {
|
||||
log.Error("获取系统设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
tradeSet, err := GetTradeSet(trade.Coin+trade.Currency, 0)
|
||||
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, trade.Coin+trade.Currency, 0)
|
||||
|
||||
if err != nil {
|
||||
log.Error("获取交易设置失败")
|
||||
@ -256,21 +257,40 @@ func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi S
|
||||
// 判断是否触发现货减仓
|
||||
func JudgeSpotReduce(trade models.TradeSet) {
|
||||
key := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE)
|
||||
reduceVal, _ := helper.DefaultRedis.GetAllList(key)
|
||||
|
||||
if len(reduceVal) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
db := GetDBConnection()
|
||||
spotApi := SpotRestApi{}
|
||||
setting, err := GetSystemSetting(db)
|
||||
setting, err := cacheservice.GetSystemSetting(db)
|
||||
|
||||
if err != nil {
|
||||
log.Error("获取系统设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
||||
//减仓单减仓策略
|
||||
orderReduceVal, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE))
|
||||
reduceOrderStrategy := dto.LineOrderReduceStrategyResp{}
|
||||
|
||||
for _, item := range orderReduceVal {
|
||||
sonic.Unmarshal([]byte(item), &reduceOrderStrategy)
|
||||
|
||||
for _, item2 := range reduceOrderStrategy.Items {
|
||||
if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency {
|
||||
//买入
|
||||
if strings.ToUpper(reduceOrderStrategy.Side) == "SELL" &&
|
||||
item2.TriggerPrice.Cmp(tradePrice) >= 0 &&
|
||||
item2.TriggerPrice.Cmp(decimal.Zero) > 0 &&
|
||||
tradePrice.Cmp(decimal.Zero) > 0 {
|
||||
//todo 生成订单并触发
|
||||
// SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//减仓单
|
||||
reduceVal, _ := helper.DefaultRedis.GetAllList(key)
|
||||
|
||||
for _, item := range reduceVal {
|
||||
reduceOrder := ReduceListItem{}
|
||||
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
|
||||
@ -280,7 +300,6 @@ func JudgeSpotReduce(trade models.TradeSet) {
|
||||
|
||||
if reduceOrder.Symbol == trade.Coin+trade.Currency {
|
||||
orderPrice := reduceOrder.Price
|
||||
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
||||
//买入
|
||||
if strings.ToUpper(reduceOrder.Side) == "SELL" &&
|
||||
orderPrice.Cmp(tradePrice) >= 0 &&
|
||||
@ -295,7 +314,7 @@ func JudgeSpotReduce(trade models.TradeSet) {
|
||||
|
||||
// 触发现货减仓
|
||||
func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRestApi, setting DbModels.LineSystemSetting, key, item string) {
|
||||
tradeSet, err := GetTradeSet(reduceOrder.Symbol, 0)
|
||||
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reduceOrder.Symbol, 0)
|
||||
|
||||
if err != nil {
|
||||
log.Error("获取交易设置失败")
|
||||
@ -366,6 +385,9 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest
|
||||
Updates(map[string]interface{}{"status": 1}).Error; err != nil {
|
||||
log.Errorf("修改现货减仓状态失败 id:%s err:%v", reduceOrder.Id, err)
|
||||
}
|
||||
|
||||
//处理减仓单减仓策略
|
||||
CacheOrderStrategyAndReCreate(db, reduceOrder, 1, tradeSet, setting)
|
||||
}
|
||||
|
||||
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
|
||||
@ -414,8 +436,8 @@ func SpotAddPositionTrigger(db *gorm.DB, v *AddPositionList, item string, spotAp
|
||||
} else if ok {
|
||||
defer lock.Release()
|
||||
|
||||
setting, _ := GetSystemSetting(db)
|
||||
tradeSet, _ := GetTradeSet(v.Symbol, 0)
|
||||
setting, _ := cacheservice.GetSystemSetting(db)
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, v.Symbol, 0)
|
||||
|
||||
if tradeSet.LastPrice == "" {
|
||||
log.Errorf("现货加仓触发 查询交易对失败 交易对:%s ordersn:%s", v.Symbol, v.OrderSn)
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
models2 "go-admin/models"
|
||||
"go-admin/models/positiondto"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/cacheservice"
|
||||
"go-admin/services/positionservice"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -165,13 +166,16 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
return
|
||||
}
|
||||
|
||||
tradeSet, err := GetTradeSet(preOrder.Symbol, 0)
|
||||
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, preOrder.Symbol, 0)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("handleMainReduceFilled 获取交易对设置失败,订单号:%s", preOrder.OrderSn)
|
||||
return
|
||||
}
|
||||
|
||||
//修改减仓单减仓策略状态
|
||||
ReduceCallBack(db, preOrder)
|
||||
|
||||
orderExt := models.LinePreOrderExt{}
|
||||
positionData := savePosition(db, preOrder)
|
||||
|
||||
@ -731,7 +735,7 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r
|
||||
// fist 首次止盈止损
|
||||
func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto, extOrderId int, fist bool) {
|
||||
orders := []models.LinePreOrder{}
|
||||
tradeSet, _ := GetTradeSet(preOrder.Symbol, 0)
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, preOrder.Symbol, 0)
|
||||
mainId := preOrder.Id
|
||||
|
||||
if preOrder.MainId > 0 {
|
||||
@ -869,7 +873,7 @@ func processSpotReduceOrder(preOrder DbModels.LinePreOrder) {
|
||||
|
||||
// 处理止盈订单
|
||||
func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LinePreOrder) {
|
||||
tradeSet, _ := GetTradeSet(order.Symbol, 0)
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 0)
|
||||
|
||||
if tradeSet.Coin == "" {
|
||||
logger.Error("获取交易对失败")
|
||||
@ -941,44 +945,6 @@ func processStopLossOrder(order models.LinePreOrder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSystemSetting(db *gorm.DB) (models.LineSystemSetting, error) {
|
||||
key := fmt.Sprintf(rediskey.SystemSetting)
|
||||
val, _ := helper.DefaultRedis.GetString(key)
|
||||
setting := models.LineSystemSetting{}
|
||||
|
||||
if val != "" {
|
||||
sonic.UnmarshalString(val, &setting)
|
||||
}
|
||||
|
||||
if setting.Id > 0 {
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
setting, err = ResetSystemSetting(db)
|
||||
if err != nil {
|
||||
return setting, err
|
||||
}
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
func ResetSystemSetting(db *gorm.DB) (DbModels.LineSystemSetting, error) {
|
||||
setting := DbModels.LineSystemSetting{}
|
||||
if err := db.Model(&setting).First(&setting).Error; err != nil {
|
||||
return setting, err
|
||||
}
|
||||
|
||||
settVal, _ := sonic.MarshalString(&setting)
|
||||
|
||||
if settVal != "" {
|
||||
if err := helper.DefaultRedis.SetString(rediskey.SystemSetting, settVal); err != nil {
|
||||
logger.Error("redis添加系统设置失败", err)
|
||||
}
|
||||
}
|
||||
return DbModels.LineSystemSetting{}, nil
|
||||
}
|
||||
|
||||
// 循环取消订单
|
||||
func CancelOpenOrderByOrderSnLoop(apiInfo DbModels.LineApiUser, symbol string, orderSn string) error {
|
||||
spotApi := SpotRestApi{}
|
||||
|
||||
232
services/binanceservice/strategy_order_service.go
Normal file
232
services/binanceservice/strategy_order_service.go
Normal file
@ -0,0 +1,232 @@
|
||||
package binanceservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
models2 "go-admin/models"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/cacheservice"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type BinanceStrategyOrderService struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// 判断是否触发波段订单
|
||||
func (e *BinanceStrategyOrderService) TriggerStrategyOrder(exchangeType string) {
|
||||
//现货
|
||||
orderStrs, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.StrategyFutOrderList, exchangeType))
|
||||
e.DoJudge(orderStrs, 1, exchangeType)
|
||||
|
||||
//合约
|
||||
futOrdedrStrs, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.StrategyFutOrderList, exchangeType))
|
||||
e.DoJudge(futOrdedrStrs, 2, exchangeType)
|
||||
}
|
||||
|
||||
// 处理订单
|
||||
func (e *BinanceStrategyOrderService) DoJudge(orderStrs []string, symbolType int, exchangeType string) {
|
||||
for _, orderStr := range orderStrs {
|
||||
var lockKey string
|
||||
orderItem := dto.StrategyOrderRedisList{}
|
||||
err := sonic.Unmarshal([]byte(orderStr), &orderItem)
|
||||
|
||||
if err != nil || orderItem.Symbol == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if symbolType == 1 {
|
||||
lockKey = rediskey.StrategySpotTriggerLock
|
||||
} else {
|
||||
lockKey = rediskey.StrategyFutTriggerLock
|
||||
}
|
||||
|
||||
lock := helper.NewRedisLock(fmt.Sprintf(lockKey, orderItem.ApiId, orderItem.Symbol), 200, 50, 100*time.Millisecond)
|
||||
|
||||
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||
e.Log.Debug("获取锁失败", err)
|
||||
return
|
||||
} else if ok {
|
||||
defer lock.Release()
|
||||
|
||||
//判断是否符合条件
|
||||
success, err := e.JudgeStrategy(orderItem, 1, exchangeType)
|
||||
|
||||
if err != nil {
|
||||
e.Log.Errorf("order_id:%d err:%v", orderItem.Id, err)
|
||||
}
|
||||
|
||||
if success {
|
||||
e.TriggerOrder(orderItem, symbolType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否符合触发条件
|
||||
func (e *BinanceStrategyOrderService) JudgeStrategy(order dto.StrategyOrderRedisList, symbolType int, exchangeType string) (bool, error) {
|
||||
var symbolKey string
|
||||
result := false
|
||||
nowUtc := time.Now().UnixMilli()
|
||||
|
||||
switch symbolType {
|
||||
case 1:
|
||||
symbolKey = fmt.Sprintf(rediskey.SpotTickerLastPrice, exchangeType, order.Symbol)
|
||||
case 2:
|
||||
symbolKey = fmt.Sprintf(rediskey.FutureTickerLastPrice, exchangeType, order.Symbol)
|
||||
}
|
||||
|
||||
lastUtc := nowUtc - (int64(order.TimeSlotStart) * 60 * 1000)
|
||||
beforePrice, _ := helper.DefaultRedis.GetNextAfterScore(symbolKey, float64(lastUtc))
|
||||
lastPrices, _ := helper.DefaultRedis.GetLastSortSet(symbolKey)
|
||||
|
||||
if beforePrice == "" || len(lastPrices) == 0 {
|
||||
return result, errors.New("获取交易对起止价格失败")
|
||||
}
|
||||
|
||||
score := lastPrices[0].Score
|
||||
startPrice := utility.StrToDecimal(beforePrice)
|
||||
lastPrice := utility.StrToDecimal(lastPrices[0].Member.(string))
|
||||
|
||||
//时间差超过10s
|
||||
if (nowUtc-int64(score))/1000 > 10 {
|
||||
return result, fmt.Errorf("时间差超过 %ss", "10")
|
||||
}
|
||||
|
||||
if startPrice.Cmp(decimal.Zero) == 0 || lastPrice.Cmp(decimal.Zero) == 0 {
|
||||
return result, errors.New("获取交易对起止价格有一个为0")
|
||||
}
|
||||
|
||||
percentag := lastPrice.Div(startPrice).Sub(decimal.NewFromInt(1)).Truncate(6)
|
||||
|
||||
//价格没有变动
|
||||
if percentag.Cmp(decimal.Zero) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
//满足条件
|
||||
switch order.CompareType {
|
||||
case 1:
|
||||
result = percentag.Mul(decimal.NewFromInt(100)).Cmp(order.Percentag) > 0
|
||||
case 2:
|
||||
result = percentag.Mul(decimal.NewFromInt(100)).Cmp(order.Percentag) >= 0
|
||||
case 5:
|
||||
result = percentag.Mul(decimal.NewFromInt(100)).Cmp(order.Percentag) == 0
|
||||
default:
|
||||
return result, errors.New("没有对应的类型")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 触发委托单
|
||||
func (e *BinanceStrategyOrderService) TriggerOrder(order dto.StrategyOrderRedisList, symbolType int) error {
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
if err := e.Orm.Model(&models.LinePreOrder{}).Where("main_id =?", order.Id).Find(&orders).Error; err != nil {
|
||||
e.Log.Errorf("order_id:%d 获取委托单失败:%s", order.Id, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
setting, _ := cacheservice.GetSystemSetting(e.Orm)
|
||||
|
||||
if setting.Id == 0 {
|
||||
return errors.New("获取系统设置失败")
|
||||
}
|
||||
|
||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, symbolType)
|
||||
|
||||
if tradeSet.Coin == "" {
|
||||
return errors.New("获取交易对行情失败")
|
||||
}
|
||||
|
||||
lastPrice := utility.StrToDecimal(tradeSet.LastPrice)
|
||||
|
||||
if lastPrice.Cmp(decimal.Zero) <= 0 {
|
||||
return errors.New("最新成交价小于等于0")
|
||||
}
|
||||
|
||||
var mainOrder models.LinePreOrder
|
||||
|
||||
for _, v := range orders {
|
||||
if v.MainId == 0 {
|
||||
mainOrder = v
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
GetOrderByPid(&mainOrder, orders, mainOrder.Id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 重新计算订单单价、数量
|
||||
// tradeSet 交易对行情
|
||||
// mainOrder 主订单
|
||||
// setting 系统设置
|
||||
func (e *BinanceStrategyOrderService) RecalculateOrder(tradeSet models2.TradeSet, mainOrder *models.LinePreOrder, setting models.LineSystemSetting) error {
|
||||
exts := make([]models.LinePreOrderExt, 0)
|
||||
if err := e.Orm.Model(models.LinePreOrderExt{}).Where("main_id =?", mainOrder.Id).Find(&exts).Error; err != nil {
|
||||
return errors.New("获取拓展信息失败")
|
||||
}
|
||||
|
||||
lastPrice := utility.StrToDecimal(tradeSet.LastPrice)
|
||||
rate := utility.StrToDecimal(mainOrder.Rate)
|
||||
newPrice := lastPrice.Mul(decimal.NewFromInt(1).Add(rate.Div(decimal.NewFromInt(100)).Truncate(4))).Truncate(int32(tradeSet.PriceDigit))
|
||||
buyPrice := utility.StrToDecimal(mainOrder.BuyPrice)
|
||||
totalNum := buyPrice.Div(newPrice).Truncate(int32(tradeSet.AmountDigit))
|
||||
|
||||
mainOrder.SignPrice = lastPrice.String()
|
||||
mainOrder.Price = newPrice.String()
|
||||
mainOrder.Num = totalNum.String()
|
||||
|
||||
for index := range mainOrder.Childs {
|
||||
//主单止盈
|
||||
if mainOrder.Child[index].OrderType == 1 {
|
||||
var ext models.LinePreOrderExt
|
||||
|
||||
for _, v := range exts {
|
||||
if v.OrderId == mainOrder.Child[index].Id {
|
||||
ext = v
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ext.Id > 0 {
|
||||
var percent decimal.Decimal
|
||||
//多
|
||||
if mainOrder.Site == "BUY" {
|
||||
percent = decimal.NewFromInt(100).Add(ext.TakeProfitRatio)
|
||||
} else {
|
||||
percent = decimal.NewFromInt(100).Sub(ext.StopLossRatio)
|
||||
}
|
||||
|
||||
childPrice := lastPrice.Mul(percent.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.PriceDigit))
|
||||
mainOrder.Child[index].Price = childPrice.String()
|
||||
}
|
||||
} else {
|
||||
//todo 重新计算
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 递归订单树
|
||||
func GetOrderByPid(order *models.LinePreOrder, orders []models.LinePreOrder, pid int) {
|
||||
for _, v := range orders {
|
||||
if v.Pid == pid {
|
||||
GetOrderByPid(&v, orders, v.Id)
|
||||
|
||||
order.Childs = append(order.Childs, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user