1、阶段减仓
This commit is contained in:
@ -11,6 +11,7 @@ type LineReduceStrategyItem struct {
|
|||||||
|
|
||||||
ReduceStrategyId int `json:"reduceStrategyId" gorm:"type:bigint;comment:减仓策略id"`
|
ReduceStrategyId int `json:"reduceStrategyId" gorm:"type:bigint;comment:减仓策略id"`
|
||||||
LossPercent decimal.Decimal `json:"lossPercent" gorm:"type:decimal(10,2);comment:亏损百分比"`
|
LossPercent decimal.Decimal `json:"lossPercent" gorm:"type:decimal(10,2);comment:亏损百分比"`
|
||||||
|
QuantityPercent decimal.Decimal `json:"quantityPercent" gorm:"type:decimal(10,2);comment:减仓数量百分比"`
|
||||||
OrderType string `json:"orderType" gorm:"type:varchar(20);comment:订单类型 LIMIT-限价 MARKET-市价"`
|
OrderType string `json:"orderType" gorm:"type:varchar(20);comment:订单类型 LIMIT-限价 MARKET-市价"`
|
||||||
ReduceStrategy LineReduceStrategy `json:"reduceStrategy" gorm:"foreignKey:ReduceStrategyId;"`
|
ReduceStrategy LineReduceStrategy `json:"reduceStrategy" gorm:"foreignKey:ReduceStrategyId;"`
|
||||||
models.ModelTime
|
models.ModelTime
|
||||||
|
|||||||
@ -3,7 +3,8 @@ package dto
|
|||||||
import "github.com/shopspring/decimal"
|
import "github.com/shopspring/decimal"
|
||||||
|
|
||||||
type LineOrderReduceStrategyResp struct {
|
type LineOrderReduceStrategyResp struct {
|
||||||
OrderId int `json:"orderId"`
|
MainId int `json:"mainId" comment:"主单id"`
|
||||||
|
OrderId int `json:"orderId" comment:"减仓单id"`
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
Side string `json:"side" comment:"BUY SELL"`
|
Side string `json:"side" comment:"BUY SELL"`
|
||||||
Items []LineOrderReduceStrategyRespItem `json:"items"`
|
Items []LineOrderReduceStrategyRespItem `json:"items"`
|
||||||
@ -12,6 +13,7 @@ type LineOrderReduceStrategyResp struct {
|
|||||||
// 减仓节点
|
// 减仓节点
|
||||||
type LineOrderReduceStrategyRespItem struct {
|
type LineOrderReduceStrategyRespItem struct {
|
||||||
Price decimal.Decimal `json:"p" comment:"下单价"`
|
Price decimal.Decimal `json:"p" comment:"下单价"`
|
||||||
|
Num decimal.Decimal `json:"n" comment:"下单数量"`
|
||||||
TriggerPrice decimal.Decimal `json:"t" comment:"触发价"`
|
TriggerPrice decimal.Decimal `json:"t" comment:"触发价"`
|
||||||
LossPercent decimal.Decimal `json:"l" comment:"亏损百分比"`
|
LossPercent decimal.Decimal `json:"l" comment:"亏损百分比"`
|
||||||
OrderType string `json:"o" comment:"订单类型 LIMIT-限价 MARKET-市价"`
|
OrderType string `json:"o" comment:"订单类型 LIMIT-限价 MARKET-市价"`
|
||||||
|
|||||||
@ -56,7 +56,7 @@ func (s *LineReduceStrategyInsertReq) Valid() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if index > 0 && item.LossPercent.Cmp(s.Items[index].LossPercent) <= 0 {
|
if index > 0 && item.LossPercent.Cmp(s.Items[index-1].LossPercent) <= 0 {
|
||||||
return errors.New("亏损比例必须递增")
|
return errors.New("亏损比例必须递增")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,6 +75,7 @@ func (s *LineReduceStrategyInsertReq) Generate(model *models.LineReduceStrategy)
|
|||||||
strategyItem := models.LineReduceStrategyItem{}
|
strategyItem := models.LineReduceStrategyItem{}
|
||||||
strategyItem.OrderType = item.OrderType
|
strategyItem.OrderType = item.OrderType
|
||||||
strategyItem.LossPercent = item.LossPercent
|
strategyItem.LossPercent = item.LossPercent
|
||||||
|
strategyItem.QuantityPercent = item.QuantityPercent
|
||||||
|
|
||||||
model.Items = append(model.Items, strategyItem)
|
model.Items = append(model.Items, strategyItem)
|
||||||
}
|
}
|
||||||
@ -105,6 +106,7 @@ func (s *LineReduceStrategyUpdateReq) Generate(model *models.LineReduceStrategy)
|
|||||||
strategyItem := models.LineReduceStrategyItem{}
|
strategyItem := models.LineReduceStrategyItem{}
|
||||||
strategyItem.OrderType = item.OrderType
|
strategyItem.OrderType = item.OrderType
|
||||||
strategyItem.LossPercent = item.LossPercent
|
strategyItem.LossPercent = item.LossPercent
|
||||||
|
strategyItem.QuantityPercent = item.QuantityPercent
|
||||||
|
|
||||||
model.Items = append(model.Items, strategyItem)
|
model.Items = append(model.Items, strategyItem)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,9 @@ func (m *LineReduceStrategyItemGetPageReq) GetNeedSearch() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LineReduceStrategyItem struct {
|
type LineReduceStrategyItem struct {
|
||||||
LossPercent decimal.Decimal `json:"lossPercent" comment:"止损百分比"`
|
LossPercent decimal.Decimal `json:"lossPercent" comment:"止损百分比"`
|
||||||
OrderType string `json:"orderType" comment:"订单类型 LIMIT-限价 MARKET-市价"`
|
QuantityPercent decimal.Decimal `json:"quantityPercent" comment:"数量百分比"`
|
||||||
|
OrderType string `json:"orderType" comment:"订单类型 LIMIT-限价 MARKET-市价"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LineReduceStrategyItem) Valid() error {
|
func (s *LineReduceStrategyItem) Valid() error {
|
||||||
@ -46,6 +47,14 @@ func (s *LineReduceStrategyItem) Valid() error {
|
|||||||
return errors.New("百分比不能大于等于100")
|
return errors.New("百分比不能大于等于100")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.QuantityPercent.Cmp(decimal.Zero) <= 0 {
|
||||||
|
return errors.New("百分比不能小于等于0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.QuantityPercent.Cmp(decimal.NewFromInt(100)) >= 0 {
|
||||||
|
return errors.New("数量百分比不能大于等于100")
|
||||||
|
}
|
||||||
|
|
||||||
keys := []string{"LIMIT", "MARKET"}
|
keys := []string{"LIMIT", "MARKET"}
|
||||||
|
|
||||||
if !utility.ContainsStr(keys, s.OrderType) {
|
if !utility.ContainsStr(keys, s.OrderType) {
|
||||||
|
|||||||
@ -1042,6 +1042,7 @@ func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrder
|
|||||||
// 构建止盈、止盈止损
|
// 构建止盈、止盈止损
|
||||||
func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) {
|
func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) {
|
||||||
orders := make([]models.LinePreOrder, 0)
|
orders := make([]models.LinePreOrder, 0)
|
||||||
|
mainId := preOrder.Id
|
||||||
var side string
|
var side string
|
||||||
|
|
||||||
if (preOrder.OrderType != 0 && strings.ToUpper(preOrder.Site) == "BUY") || (preOrder.OrderType == 0 && strings.ToUpper(preOrder.Site) == "SELL") {
|
if (preOrder.OrderType != 0 && strings.ToUpper(preOrder.Site) == "BUY") || (preOrder.OrderType == 0 && strings.ToUpper(preOrder.Site) == "SELL") {
|
||||||
@ -1050,6 +1051,10 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO
|
|||||||
side = "SELL"
|
side = "SELL"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if preOrder.MainId > 0 {
|
||||||
|
mainId = preOrder.MainId
|
||||||
|
}
|
||||||
|
|
||||||
if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 {
|
if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 {
|
||||||
// 止盈单
|
// 止盈单
|
||||||
profitOrder := models.LinePreOrder{}
|
profitOrder := models.LinePreOrder{}
|
||||||
@ -1060,11 +1065,7 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO
|
|||||||
profitOrder.Pid = preOrder.Id
|
profitOrder.Pid = preOrder.Id
|
||||||
profitOrder.OrderType = 1
|
profitOrder.OrderType = 1
|
||||||
profitOrder.Status = 0
|
profitOrder.Status = 0
|
||||||
profitOrder.MainId = preOrder.Id
|
profitOrder.MainId = mainId
|
||||||
|
|
||||||
if preOrder.MainId > 0 {
|
|
||||||
profitOrder.MainId = preOrder.MainId
|
|
||||||
}
|
|
||||||
profitOrder.BuyPrice = "0"
|
profitOrder.BuyPrice = "0"
|
||||||
profitOrder.Site = side
|
profitOrder.Site = side
|
||||||
|
|
||||||
@ -1090,7 +1091,7 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO
|
|||||||
lossOrder.Pid = preOrder.Id
|
lossOrder.Pid = preOrder.Id
|
||||||
lossOrder.OrderType = 2
|
lossOrder.OrderType = 2
|
||||||
lossOrder.Status = 0
|
lossOrder.Status = 0
|
||||||
lossOrder.MainId = preOrder.MainId
|
lossOrder.MainId = mainId
|
||||||
lossOrder.BuyPrice = "0"
|
lossOrder.BuyPrice = "0"
|
||||||
lossOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String()
|
lossOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String()
|
||||||
lossOrder.Rate = ext.StopLossRatio.Truncate(2).String()
|
lossOrder.Rate = ext.StopLossRatio.Truncate(2).String()
|
||||||
@ -1611,6 +1612,10 @@ func (e *LinePreOrder) ClearAll() error {
|
|||||||
"futures_reduce_list",
|
"futures_reduce_list",
|
||||||
"spot_reduce_strategy_list",
|
"spot_reduce_strategy_list",
|
||||||
"fut_reduce_strategy_list",
|
"fut_reduce_strategy_list",
|
||||||
|
"future_position",
|
||||||
|
"spot_position",
|
||||||
|
"strategy_spot_order_list",
|
||||||
|
"strategy_fut_order_list",
|
||||||
}
|
}
|
||||||
err = helper.DefaultRedis.DeleteKeysByPrefix(prefixs...)
|
err = helper.DefaultRedis.DeleteKeysByPrefix(prefixs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -26,9 +26,9 @@ const (
|
|||||||
PreFutOrderList = "_PreFutOrderList_:%s" // 待触发的订单集合 {交易所类型 exchange_type}
|
PreFutOrderList = "_PreFutOrderList_:%s" // 待触发的订单集合 {交易所类型 exchange_type}
|
||||||
|
|
||||||
//策略现货订单集合 {交易所类型 exchange_type}
|
//策略现货订单集合 {交易所类型 exchange_type}
|
||||||
StrategySpotOrderList = "strategy_spot_order_list_%s"
|
StrategySpotOrderList = "strategy_spot_order_list:%s"
|
||||||
//策略合约订单集合 {交易所类型 exchange_type}
|
//策略合约订单集合 {交易所类型 exchange_type}
|
||||||
StrategyFutOrderList = "strategy_fut_order_list_%s"
|
StrategyFutOrderList = "strategy_fut_order_list:%s"
|
||||||
|
|
||||||
API_USER = "api_user:%v" // api用户
|
API_USER = "api_user:%v" // api用户
|
||||||
SystemSetting = "system_setting" //系统设置
|
SystemSetting = "system_setting" //系统设置
|
||||||
@ -51,6 +51,11 @@ const (
|
|||||||
//波段合约触发{apiuserid|symbol}
|
//波段合约触发{apiuserid|symbol}
|
||||||
StrategyFutTriggerLock = "strategy_fut_trigger_l:%v_%s"
|
StrategyFutTriggerLock = "strategy_fut_trigger_l:%v_%s"
|
||||||
|
|
||||||
|
//减仓波段合约触发 {apiuserid|symbol}
|
||||||
|
ReduceStrategyFutTriggerLock = "reduce_strategy_fut_trigger_l:%v_%s"
|
||||||
|
//减仓波段现货触发 {apiuserid|symbol}
|
||||||
|
ReduceStrategySpotTriggerLock = "reduce_strategy_spot_trigger_l:%v_%s"
|
||||||
|
|
||||||
SpotCallBack = "spot_callback:%s" //现货回调 {ordersn}
|
SpotCallBack = "spot_callback:%s" //现货回调 {ordersn}
|
||||||
FutCallBack = "fut_callback:%s" //合约回调 {ordersn}
|
FutCallBack = "fut_callback:%s" //合约回调 {ordersn}
|
||||||
|
|
||||||
|
|||||||
63
services/binanceservice/futures_judge_service_test.go
Normal file
63
services/binanceservice/futures_judge_service_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package binanceservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go-admin/common/const/rediskey"
|
||||||
|
"go-admin/common/global"
|
||||||
|
"go-admin/common/helper"
|
||||||
|
"go-admin/models"
|
||||||
|
"go-admin/services/cacheservice"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bytedance/sonic"
|
||||||
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFutureJudge(t *testing.T) {
|
||||||
|
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||||
|
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
|
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||||
|
helper.InitLockRedisConn("127.0.0.1:6379", "", "2")
|
||||||
|
// tradeSet := models.TradeSet{
|
||||||
|
// Coin: "ADA",
|
||||||
|
// Currency: "USDT",
|
||||||
|
// LastPrice: "0.516",
|
||||||
|
// }
|
||||||
|
|
||||||
|
key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE)
|
||||||
|
item := `{"id":4,"apiId":49,"mainId":1,"pid":1,"symbol":"ADAUSDT","price":"0.5417","side":"SELL","num":"13","orderSn":"397659701065547776"}`
|
||||||
|
reduceOrder := ReduceListItem{}
|
||||||
|
futApi := FutRestApi{}
|
||||||
|
setting, err := cacheservice.GetSystemSetting(db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("获取系统设置失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
|
||||||
|
logger.Error("反序列化失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// JudgeFuturesReduce(tradeSet)
|
||||||
|
FuturesReduceTrigger(db, reduceOrder, futApi, setting, key, item, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试减仓后减仓触发
|
||||||
|
func TestFutureReduceReduce(t *testing.T) {
|
||||||
|
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||||
|
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
|
sdk.Runtime.SetDb("default", db)
|
||||||
|
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||||
|
helper.InitLockRedisConn("127.0.0.1:6379", "", "2")
|
||||||
|
tradeSet := models.TradeSet{
|
||||||
|
Coin: "ADA",
|
||||||
|
Currency: "USDT",
|
||||||
|
LastPrice: "0.5307",
|
||||||
|
}
|
||||||
|
|
||||||
|
// JudgeFuturesReduce(tradeSet)
|
||||||
|
JudgeFuturesReduce(tradeSet)
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"go-admin/common/global"
|
"go-admin/common/global"
|
||||||
"go-admin/common/helper"
|
"go-admin/common/helper"
|
||||||
"go-admin/models"
|
"go-admin/models"
|
||||||
|
"go-admin/pkg/utility"
|
||||||
"go-admin/services/cacheservice"
|
"go-admin/services/cacheservice"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -161,30 +162,83 @@ func JudgeFuturesReduce(trade models.TradeSet) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reduceOrder := ReduceListItem{}
|
||||||
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
||||||
//减仓单减仓策略
|
//减仓单减仓策略
|
||||||
orderReduceVal, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE))
|
reduceReduceListKey := fmt.Sprintf(rediskey.FutOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
||||||
|
orderReduceVal, _ := helper.DefaultRedis.HGetAllFields(reduceReduceListKey)
|
||||||
reduceOrderStrategy := dto.LineOrderReduceStrategyResp{}
|
reduceOrderStrategy := dto.LineOrderReduceStrategyResp{}
|
||||||
|
|
||||||
for _, item := range orderReduceVal {
|
for _, item := range orderReduceVal {
|
||||||
sonic.Unmarshal([]byte(item), &reduceOrderStrategy)
|
sonic.Unmarshal([]byte(item), &reduceOrderStrategy)
|
||||||
|
|
||||||
for _, item2 := range reduceOrderStrategy.Items {
|
for index, item2 := range reduceOrderStrategy.Items {
|
||||||
if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency {
|
if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency && !item2.Actived {
|
||||||
//买入
|
//买入
|
||||||
if item2.TriggerPrice.Cmp(decimal.Zero) > 0 &&
|
if item2.TriggerPrice.Cmp(decimal.Zero) > 0 &&
|
||||||
tradePrice.Cmp(decimal.Zero) > 0 &&
|
tradePrice.Cmp(decimal.Zero) > 0 &&
|
||||||
((strings.ToUpper(reduceOrderStrategy.Side) == "SELL" && item2.TriggerPrice.Cmp(tradePrice) >= 0) ||
|
((strings.ToUpper(reduceOrderStrategy.Side) == "SELL" && item2.TriggerPrice.Cmp(tradePrice) >= 0) ||
|
||||||
(strings.ToUpper(reduceOrderStrategy.Side) == "BUY" && item2.TriggerPrice.Cmp(tradePrice) <= 0)) {
|
(strings.ToUpper(reduceOrderStrategy.Side) == "BUY" && item2.TriggerPrice.Cmp(tradePrice) <= 0)) {
|
||||||
//todo 生成订单并触发
|
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.ReduceStrategyFutTriggerLock, reduceOrder.ApiId, reduceOrder.Symbol), 50, 15, 100*time.Millisecond)
|
||||||
// SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item)
|
|
||||||
|
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||||
|
log.Error("获取锁失败", err)
|
||||||
|
return
|
||||||
|
} else if ok {
|
||||||
|
defer lock.Release()
|
||||||
|
hasrecord, _ := helper.DefaultRedis.IsElementInList(reduceReduceListKey, item)
|
||||||
|
|
||||||
|
if !hasrecord {
|
||||||
|
log.Debug("减仓缓存中不存在", item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
order, err := CreateReduceReduceOrder(db, reduceOrderStrategy.OrderId, item2.Price, item2.Num, trade.AmountDigit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%d 生成订单失败", reduceOrderStrategy.OrderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
reduceOrder.ApiId = order.ApiId
|
||||||
|
reduceOrder.Id = order.Id
|
||||||
|
reduceOrder.Pid = order.Pid
|
||||||
|
reduceOrder.MainId = order.MainId
|
||||||
|
reduceOrder.Symbol = order.Symbol
|
||||||
|
reduceOrder.Side = reduceOrderStrategy.Side
|
||||||
|
reduceOrder.OrderSn = order.OrderSn
|
||||||
|
reduceOrder.Price = item2.Price
|
||||||
|
reduceOrder.Num = item2.Num
|
||||||
|
//下单成功修改策略节点状态
|
||||||
|
if FuturesReduceTrigger(db, reduceOrder, futApi, setting, reduceReduceListKey, item, true) {
|
||||||
|
reduceOrderStrategy.Items[index].Actived = true
|
||||||
|
allActive := true
|
||||||
|
orderId := utility.IntToString(reduceOrderStrategy.OrderId)
|
||||||
|
|
||||||
|
for _, item3 := range reduceOrderStrategy.Items {
|
||||||
|
if !item3.Actived {
|
||||||
|
allActive = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allActive {
|
||||||
|
if err := helper.DefaultRedis.HDelField(reduceReduceListKey, orderId); err != nil {
|
||||||
|
log.Errorf("删除redis reduceReduceListKey失败 %s", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str, _ := sonic.MarshalString(reduceOrderStrategy)
|
||||||
|
if err := helper.DefaultRedis.HSetField(reduceReduceListKey, orderId, str); err != nil {
|
||||||
|
log.Errorf("更新redis reduceReduceListKey失败 %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range reduceVal {
|
for _, item := range reduceVal {
|
||||||
reduceOrder := ReduceListItem{}
|
|
||||||
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
|
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
|
||||||
log.Error("反序列化失败")
|
log.Error("反序列化失败")
|
||||||
continue
|
continue
|
||||||
@ -198,52 +252,54 @@ func JudgeFuturesReduce(trade models.TradeSet) {
|
|||||||
((strings.ToUpper(reduceOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) >= 0) ||
|
((strings.ToUpper(reduceOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) >= 0) ||
|
||||||
(strings.ToUpper(reduceOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) <= 0)) {
|
(strings.ToUpper(reduceOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) <= 0)) {
|
||||||
|
|
||||||
FuturesReduceTrigger(db, reduceOrder, futApi, setting, key, item)
|
FuturesReduceTrigger(db, reduceOrder, futApi, setting, key, item, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 触发合约减仓
|
// 触发合约减仓
|
||||||
func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, key, item string) {
|
// isStrategy 是否是策略减仓
|
||||||
|
func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, key, item string, isStrategy bool) bool {
|
||||||
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reduceOrder.Symbol, 1)
|
tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reduceOrder.Symbol, 1)
|
||||||
|
result := true
|
||||||
|
|
||||||
if tradeSet.LastPrice == "" {
|
if tradeSet.LastPrice == "" {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond)
|
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond)
|
||||||
|
|
||||||
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||||
log.Error("获取锁失败", err)
|
log.Error("获取锁失败", err)
|
||||||
return
|
return false
|
||||||
} else if ok {
|
} else if ok {
|
||||||
defer lock.Release()
|
defer lock.Release()
|
||||||
takeOrders := make([]DbModels.LinePreOrder, 0)
|
takeOrders := make([]DbModels.LinePreOrder, 0)
|
||||||
if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil {
|
if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type IN (1,2,4) AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil {
|
||||||
log.Error("查询止盈单失败")
|
log.Error("查询止盈单失败")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
|
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
|
||||||
|
|
||||||
if !hasrecord {
|
if !hasrecord {
|
||||||
log.Debug("减仓缓存中不存在", item)
|
log.Debug("减仓缓存中不存在", item)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
apiInfo, _ := GetApiInfo(reduceOrder.ApiId)
|
apiInfo, _ := GetApiInfo(reduceOrder.ApiId)
|
||||||
|
|
||||||
if apiInfo.Id == 0 {
|
if apiInfo.Id == 0 {
|
||||||
log.Error("现货减仓 查询api用户不存在")
|
log.Error("现货减仓 查询api用户不存在")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
for _, takeOrder := range takeOrders {
|
for _, takeOrder := range takeOrders {
|
||||||
err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
|
err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("合约止盈撤单失败", err)
|
log.Error("撤单失败", err)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +314,7 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := futApi.ClosePositionLoop(reduceOrder.Symbol, reduceOrder.OrderSn, num, reduceOrder.Side, positionSide, apiInfo, "LIMIT", "0", price, 3); err != nil {
|
if err := futApi.ClosePositionLoop(reduceOrder.Symbol, reduceOrder.OrderSn, num, reduceOrder.Side, positionSide, apiInfo, "LIMIT", "0", price, 3); err != nil {
|
||||||
|
result = false
|
||||||
log.Errorf("合约减仓挂单失败 id:%s err:%v", reduceOrder.Id, err)
|
log.Errorf("合约减仓挂单失败 id:%s err:%v", reduceOrder.Id, err)
|
||||||
|
|
||||||
if err2 := db.Model(&DbModels.LinePreOrder{}).
|
if err2 := db.Model(&DbModels.LinePreOrder{}).
|
||||||
@ -276,15 +333,22 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
//处理减仓单减仓策略
|
//处理减仓单减仓策略
|
||||||
CacheOrderStrategyAndReCreate(db, reduceOrder, 2, tradeSet, setting)
|
if err := CacheOrderStrategyAndReCreate(db, reduceOrder, 2, tradeSet, setting); err != nil {
|
||||||
|
log.Errorf("合约减仓策略处理失败 id:%s err:%v", reduceOrder.Id, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
|
if !isStrategy {
|
||||||
log.Errorf("合约减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err)
|
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
|
||||||
|
log.Errorf("合约减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error("获取锁失败")
|
log.Error("获取锁失败")
|
||||||
|
result = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断合约加仓
|
// 判断合约加仓
|
||||||
|
|||||||
@ -163,9 +163,9 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
|||||||
positionData := savePosition(db, preOrder)
|
positionData := savePosition(db, preOrder)
|
||||||
|
|
||||||
//市价单就跳出 市价减仓不设止盈止损
|
//市价单就跳出 市价减仓不设止盈止损
|
||||||
if preOrder.MainOrderType == "MARKET" {
|
// if preOrder.MainOrderType == "MARKET" {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
//亏损大于0 重新计算比例
|
//亏损大于0 重新计算比例
|
||||||
FutTakeProfit(db, preOrder, apiUserInfo, tradeSet, positionData, orderExt, decimal.Zero, decimal.Zero)
|
FutTakeProfit(db, preOrder, apiUserInfo, tradeSet, positionData, orderExt, decimal.Zero, decimal.Zero)
|
||||||
@ -435,6 +435,8 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) {
|
|||||||
stoploss := dto.StopLossRedisList{}
|
stoploss := dto.StopLossRedisList{}
|
||||||
addPosition := AddPositionList{}
|
addPosition := AddPositionList{}
|
||||||
reduce := ReduceListItem{}
|
reduce := ReduceListItem{}
|
||||||
|
//移除减仓后减仓策略
|
||||||
|
RemoveReduceReduceCacheByMainId(mainId, 2)
|
||||||
|
|
||||||
//止损缓存
|
//止损缓存
|
||||||
for _, v := range stoplossVal {
|
for _, v := range stoplossVal {
|
||||||
@ -598,7 +600,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processFutStopLossOrder(db, order, price, num)
|
processFutStopLossOrder(db, order, utility.StrToDecimal(order.Price), num)
|
||||||
// case 4: // 减仓
|
// case 4: // 减仓
|
||||||
// processFutReduceOrder(order, price, num)
|
// processFutReduceOrder(order, price, num)
|
||||||
}
|
}
|
||||||
@ -700,9 +702,13 @@ func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *model
|
|||||||
// order.Num = num.String()
|
// order.Num = num.String()
|
||||||
// } else
|
// } else
|
||||||
|
|
||||||
if first && (order.OrderCategory == 1 || order.OrderCategory == 3) && order.OrderType == 1 && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 {
|
//止盈止损重算数量
|
||||||
|
if first && (order.OrderCategory == 1 || order.OrderCategory == 3) && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 {
|
||||||
// 计算止盈数量
|
// 计算止盈数量
|
||||||
num = num.Mul(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
|
if order.OrderType == 1 {
|
||||||
|
num = num.Mul(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
|
||||||
|
}
|
||||||
|
|
||||||
order.Num = num.String()
|
order.Num = num.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
package binanceservice
|
package binanceservice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go-admin/app/admin/models"
|
"go-admin/app/admin/models"
|
||||||
DbModels "go-admin/app/admin/models"
|
DbModels "go-admin/app/admin/models"
|
||||||
|
"go-admin/pkg/utility"
|
||||||
|
"go-admin/pkg/utility/snowflakehelper"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-admin-team/go-admin-core/logger"
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@ -170,3 +175,68 @@ func GetChildTpOrder(db *gorm.DB, pid int) (int, error) {
|
|||||||
|
|
||||||
return int(count), nil
|
return int(count), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建减仓后减仓单
|
||||||
|
func CreateReduceReduceOrder(db *gorm.DB, pid int, price, num decimal.Decimal, amountDigit int) (models.LinePreOrder, error) {
|
||||||
|
var preOrder models.LinePreOrder
|
||||||
|
var result models.LinePreOrder
|
||||||
|
var ext models.LinePreOrderExt
|
||||||
|
|
||||||
|
if err := db.Model(&models.LinePreOrder{}).Preload("Childs").Where("id =? ", pid).First(&preOrder).Error; err != nil {
|
||||||
|
return preOrder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Model(&models.LinePreOrderExt{}).Where("order_id =? ", pid).Find(&ext).Error; err != nil {
|
||||||
|
return preOrder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copier.Copy(&result, &preOrder)
|
||||||
|
|
||||||
|
result.Id = 0
|
||||||
|
result.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
|
||||||
|
result.Status = 0
|
||||||
|
result.CreatedAt = time.Now()
|
||||||
|
result.TriggerTime = nil
|
||||||
|
result.UpdatedAt = time.Now()
|
||||||
|
result.BuyPrice = decimal.Zero.String()
|
||||||
|
result.Price = price.String()
|
||||||
|
result.Num = num.String()
|
||||||
|
|
||||||
|
for index := range result.Childs {
|
||||||
|
result.Childs[index].Id = 0
|
||||||
|
result.Childs[index].OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
|
||||||
|
result.Childs[index].Status = 0
|
||||||
|
result.Childs[index].CreatedAt = time.Now()
|
||||||
|
result.Childs[index].TriggerTime = nil
|
||||||
|
result.Childs[index].UpdatedAt = time.Now()
|
||||||
|
result.Childs[index].BuyPrice = decimal.Zero.String()
|
||||||
|
var pricePercent decimal.Decimal
|
||||||
|
|
||||||
|
if result.Childs[index].OrderType == 1 && ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 {
|
||||||
|
// 减仓单卖出
|
||||||
|
if preOrder.Site == "SELL" {
|
||||||
|
pricePercent = decimal.NewFromInt(100).Add(ext.TakeProfitRatio).Div(decimal.NewFromInt(100))
|
||||||
|
} else {
|
||||||
|
pricePercent = decimal.NewFromInt(100).Sub(ext.TakeProfitRatio).Div(decimal.NewFromInt(100))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if result.Childs[index].OrderType == 2 && ext.StopLossRatio.Cmp(decimal.Zero) > 0 {
|
||||||
|
if preOrder.Site == "SELL" {
|
||||||
|
pricePercent = decimal.NewFromInt(100).Sub(ext.StopLossRatio).Div(decimal.NewFromInt(100))
|
||||||
|
} else {
|
||||||
|
pricePercent = decimal.NewFromInt(100).Add(ext.StopLossRatio).Div(decimal.NewFromInt(100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//重新计算止盈止损价
|
||||||
|
if pricePercent.Cmp(decimal.Zero) > 0 {
|
||||||
|
result.Childs[index].Price = price.Mul(pricePercent).Truncate(int32(amountDigit)).String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Create(&result).Error; err != nil {
|
||||||
|
return result, fmt.Errorf("复制减仓单失败:pid:%d err:%v", pid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"go-admin/common/global"
|
"go-admin/common/global"
|
||||||
"go-admin/common/helper"
|
"go-admin/common/helper"
|
||||||
models2 "go-admin/models"
|
models2 "go-admin/models"
|
||||||
|
"go-admin/pkg/utility"
|
||||||
|
|
||||||
"github.com/bytedance/sonic"
|
"github.com/bytedance/sonic"
|
||||||
"github.com/go-admin-team/go-admin-core/logger"
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
@ -19,7 +20,7 @@ import (
|
|||||||
// reduceOrder:原始减仓单
|
// reduceOrder:原始减仓单
|
||||||
// reduceOrderStrategy:减仓策略
|
// reduceOrderStrategy:减仓策略
|
||||||
func CacheOrderStrategyAndReCreate(db *gorm.DB, reduceOrder ReduceListItem, symbolType int, tradeSet models2.TradeSet, setting models.LineSystemSetting) error {
|
func CacheOrderStrategyAndReCreate(db *gorm.DB, reduceOrder ReduceListItem, symbolType int, tradeSet models2.TradeSet, setting models.LineSystemSetting) error {
|
||||||
reduceOrderStrategy := models.LineReduceStrategy{}
|
reduceOrderStrategy := models.LineOrderReduceStrategy{}
|
||||||
var key string
|
var key string
|
||||||
|
|
||||||
if symbolType == 1 {
|
if symbolType == 1 {
|
||||||
@ -28,21 +29,27 @@ func CacheOrderStrategyAndReCreate(db *gorm.DB, reduceOrder ReduceListItem, symb
|
|||||||
key = fmt.Sprintf(rediskey.FutOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
key = fmt.Sprintf(rediskey.FutOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.Model(&reduceOrderStrategy).Where("order_id =?", reduceOrder.Id).Find(reduceOrderStrategy).Error; err != nil {
|
if err := db.Model(&reduceOrderStrategy).Where("order_id =?", reduceOrder.Id).Find(&reduceOrderStrategy).Error; err != nil {
|
||||||
logger.Errorf("获取减仓策略失败,err:%v", err)
|
logger.Errorf("获取减仓策略失败,err:%v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if reduceOrderStrategy.Id > 0 {
|
if reduceOrderStrategy.Id > 0 {
|
||||||
|
items := make([]models.LineReduceStrategyItem, 0)
|
||||||
strategyCache := dto.LineOrderReduceStrategyResp{
|
strategyCache := dto.LineOrderReduceStrategyResp{
|
||||||
OrderId: reduceOrder.Id,
|
OrderId: reduceOrder.Id,
|
||||||
|
Symbol: reduceOrder.Symbol,
|
||||||
|
MainId: reduceOrder.MainId,
|
||||||
|
Side: reduceOrder.Side,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range reduceOrderStrategy.Items {
|
sonic.Unmarshal([]byte(reduceOrderStrategy.ItemContent), &items)
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
var rate decimal.Decimal
|
var rate decimal.Decimal
|
||||||
var triggerRate decimal.Decimal
|
var triggerRate decimal.Decimal
|
||||||
|
|
||||||
if reduceOrder.Side == "BUY" {
|
if reduceOrder.Side == "SELL" {
|
||||||
rate = (decimal.NewFromInt(100).Sub(item.LossPercent)).Div(decimal.NewFromInt(100))
|
rate = (decimal.NewFromInt(100).Sub(item.LossPercent)).Div(decimal.NewFromInt(100))
|
||||||
|
|
||||||
if setting.ReduceEarlyTriggerPercent.Cmp(decimal.Zero) > 0 {
|
if setting.ReduceEarlyTriggerPercent.Cmp(decimal.Zero) > 0 {
|
||||||
@ -61,19 +68,26 @@ func CacheOrderStrategyAndReCreate(db *gorm.DB, reduceOrder ReduceListItem, symb
|
|||||||
}
|
}
|
||||||
price := reduceOrder.Price.Mul(rate).Truncate(int32(tradeSet.PriceDigit))
|
price := reduceOrder.Price.Mul(rate).Truncate(int32(tradeSet.PriceDigit))
|
||||||
triggerPrice := reduceOrder.Price.Mul(triggerRate).Truncate(int32(tradeSet.PriceDigit))
|
triggerPrice := reduceOrder.Price.Mul(triggerRate).Truncate(int32(tradeSet.PriceDigit))
|
||||||
|
num := reduceOrder.Num
|
||||||
|
|
||||||
|
//百分比大于0就重新计算 否则就是主减仓单数量
|
||||||
|
if item.QuantityPercent.Cmp(decimal.Zero) > 0 {
|
||||||
|
num = reduceOrder.Num.Mul(item.QuantityPercent.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
|
||||||
|
}
|
||||||
|
|
||||||
strategyCache.Items = append(strategyCache.Items, dto.LineOrderReduceStrategyRespItem{
|
strategyCache.Items = append(strategyCache.Items, dto.LineOrderReduceStrategyRespItem{
|
||||||
LossPercent: item.LossPercent,
|
LossPercent: item.LossPercent,
|
||||||
OrderType: item.OrderType,
|
OrderType: item.OrderType,
|
||||||
Price: price,
|
Price: price,
|
||||||
TriggerPrice: triggerPrice,
|
TriggerPrice: triggerPrice,
|
||||||
|
Num: num,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
str, _ := sonic.MarshalString(reduceOrderStrategy)
|
str, _ := sonic.MarshalString(strategyCache)
|
||||||
|
|
||||||
if str != "" {
|
if str != "" {
|
||||||
if err := helper.DefaultRedis.SetString(key, str); err != nil {
|
if err := helper.DefaultRedis.HSetField(key, utility.IntToString(reduceOrder.Id), str); err != nil {
|
||||||
logger.Errorf("减仓单缓存减仓策略,err:%v", err)
|
logger.Errorf("减仓单缓存减仓策略,err:%v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,3 +146,33 @@ func ReduceCallBack(db *gorm.DB, preOrder *models.LinePreOrder) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 移除减仓后减仓策略
|
||||||
|
// mainId 主单id
|
||||||
|
// symbolType 交易对类型
|
||||||
|
func RemoveReduceReduceCacheByMainId(mainId int, symbolType int) error {
|
||||||
|
var key string
|
||||||
|
switch 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.HGetAllFields(key)
|
||||||
|
cache := dto.LineOrderReduceStrategyResp{}
|
||||||
|
|
||||||
|
for _, v := range arrays {
|
||||||
|
sonic.Unmarshal([]byte(v), &cache)
|
||||||
|
|
||||||
|
if cache.MainId == mainId {
|
||||||
|
if err := helper.DefaultRedis.HDelField(key, utility.IntToString(cache.OrderId)); err != nil {
|
||||||
|
logger.Errorf("移除减仓单减仓策略失败redis err:%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -266,23 +266,76 @@ func JudgeSpotReduce(trade models.TradeSet) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reduceOrder := ReduceListItem{}
|
||||||
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
|
||||||
//减仓单减仓策略
|
//减仓单减仓策略
|
||||||
orderReduceVal, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE))
|
reduceReduceListKey := fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE)
|
||||||
|
orderReduceVal, _ := helper.DefaultRedis.GetAllList(reduceReduceListKey)
|
||||||
reduceOrderStrategy := dto.LineOrderReduceStrategyResp{}
|
reduceOrderStrategy := dto.LineOrderReduceStrategyResp{}
|
||||||
|
|
||||||
for _, item := range orderReduceVal {
|
for _, item := range orderReduceVal {
|
||||||
sonic.Unmarshal([]byte(item), &reduceOrderStrategy)
|
sonic.Unmarshal([]byte(item), &reduceOrderStrategy)
|
||||||
|
|
||||||
for _, item2 := range reduceOrderStrategy.Items {
|
for index, item2 := range reduceOrderStrategy.Items {
|
||||||
if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency {
|
if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency {
|
||||||
//买入
|
//买入
|
||||||
if strings.ToUpper(reduceOrderStrategy.Side) == "SELL" &&
|
if strings.ToUpper(reduceOrderStrategy.Side) == "SELL" &&
|
||||||
item2.TriggerPrice.Cmp(tradePrice) >= 0 &&
|
item2.TriggerPrice.Cmp(tradePrice) >= 0 &&
|
||||||
item2.TriggerPrice.Cmp(decimal.Zero) > 0 &&
|
item2.TriggerPrice.Cmp(decimal.Zero) > 0 &&
|
||||||
tradePrice.Cmp(decimal.Zero) > 0 {
|
tradePrice.Cmp(decimal.Zero) > 0 {
|
||||||
//todo 生成订单并触发
|
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.ReduceStrategySpotTriggerLock, reduceOrder.ApiId, reduceOrder.Symbol), 50, 15, 100*time.Millisecond)
|
||||||
// SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item)
|
|
||||||
|
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||||
|
log.Error("获取锁失败", err)
|
||||||
|
return
|
||||||
|
} else if ok {
|
||||||
|
defer lock.Release()
|
||||||
|
hasrecord, _ := helper.DefaultRedis.IsElementInList(reduceReduceListKey, item)
|
||||||
|
|
||||||
|
if !hasrecord {
|
||||||
|
log.Debug("减仓缓存中不存在", item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
order, err := CreateReduceReduceOrder(db, reduceOrderStrategy.OrderId, item2.Price, item2.Num, trade.AmountDigit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%d 生成订单失败", reduceOrderStrategy.OrderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
reduceOrder.ApiId = order.ApiId
|
||||||
|
reduceOrder.Id = order.Id
|
||||||
|
reduceOrder.Pid = order.Pid
|
||||||
|
reduceOrder.MainId = order.MainId
|
||||||
|
reduceOrder.Symbol = order.Symbol
|
||||||
|
reduceOrder.Side = reduceOrderStrategy.Side
|
||||||
|
reduceOrder.OrderSn = order.OrderSn
|
||||||
|
reduceOrder.Price = item2.Price
|
||||||
|
reduceOrder.Num = item2.Num
|
||||||
|
if SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item, true) {
|
||||||
|
reduceOrderStrategy.Items[index].Actived = true
|
||||||
|
allActive := true
|
||||||
|
orderId := utility.IntToString(reduceOrderStrategy.OrderId)
|
||||||
|
|
||||||
|
for _, item3 := range reduceOrderStrategy.Items {
|
||||||
|
if !item3.Actived {
|
||||||
|
allActive = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allActive {
|
||||||
|
if err := helper.DefaultRedis.HDelField(reduceReduceListKey, orderId); err != nil {
|
||||||
|
log.Errorf("删除redis reduceReduceListKey失败 %s", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str, _ := sonic.MarshalString(reduceOrderStrategy)
|
||||||
|
if err := helper.DefaultRedis.HSetField(reduceReduceListKey, orderId, str); err != nil {
|
||||||
|
log.Errorf("更新redis reduceReduceListKey失败 %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,7 +345,6 @@ func JudgeSpotReduce(trade models.TradeSet) {
|
|||||||
reduceVal, _ := helper.DefaultRedis.GetAllList(key)
|
reduceVal, _ := helper.DefaultRedis.GetAllList(key)
|
||||||
|
|
||||||
for _, item := range reduceVal {
|
for _, item := range reduceVal {
|
||||||
reduceOrder := ReduceListItem{}
|
|
||||||
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
|
if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
|
||||||
log.Error("反序列化失败")
|
log.Error("反序列化失败")
|
||||||
continue
|
continue
|
||||||
@ -306,52 +358,53 @@ func JudgeSpotReduce(trade models.TradeSet) {
|
|||||||
orderPrice.Cmp(decimal.Zero) > 0 &&
|
orderPrice.Cmp(decimal.Zero) > 0 &&
|
||||||
tradePrice.Cmp(decimal.Zero) > 0 {
|
tradePrice.Cmp(decimal.Zero) > 0 {
|
||||||
|
|
||||||
SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item)
|
SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 触发现货减仓
|
// 触发现货减仓
|
||||||
func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRestApi, setting DbModels.LineSystemSetting, key, item string) {
|
func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRestApi, setting DbModels.LineSystemSetting, key, item string, isStrategy bool) bool {
|
||||||
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reduceOrder.Symbol, 0)
|
tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reduceOrder.Symbol, 0)
|
||||||
|
result := true
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("获取交易设置失败")
|
log.Error("获取交易设置失败")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond)
|
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond)
|
||||||
|
|
||||||
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||||
log.Error("获取锁失败", err)
|
log.Error("获取锁失败", err)
|
||||||
return
|
return false
|
||||||
} else if ok {
|
} else if ok {
|
||||||
defer lock.Release()
|
defer lock.Release()
|
||||||
takeOrders := make([]DbModels.LinePreOrder, 0)
|
takeOrders := make([]DbModels.LinePreOrder, 0)
|
||||||
if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil {
|
if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type IN (1,2,4) AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil {
|
||||||
log.Error("查询止盈单失败")
|
log.Error("查询止盈单失败")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
|
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
|
||||||
|
|
||||||
if !hasrecord {
|
if !hasrecord {
|
||||||
log.Debug("减仓缓存中不存在", item)
|
log.Debug("减仓缓存中不存在", item)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
apiInfo, _ := GetApiInfo(reduceOrder.ApiId)
|
apiInfo, _ := GetApiInfo(reduceOrder.ApiId)
|
||||||
|
|
||||||
if apiInfo.Id == 0 {
|
if apiInfo.Id == 0 {
|
||||||
log.Error("现货减仓 查询api用户不存在")
|
log.Error("现货减仓 查询api用户不存在")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, takeOrder := range takeOrders {
|
for _, takeOrder := range takeOrders {
|
||||||
err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
|
err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("现货止盈撤单失败", err)
|
log.Error("现货撤单失败", err)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
|
price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
|
||||||
@ -368,6 +421,7 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil {
|
if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil {
|
||||||
|
result = false
|
||||||
log.Errorf("现货减仓挂单失败 id:%s err:%v", reduceOrder.Id, err)
|
log.Errorf("现货减仓挂单失败 id:%s err:%v", reduceOrder.Id, err)
|
||||||
|
|
||||||
if err2 := db.Model(&DbModels.LinePreOrder{}).
|
if err2 := db.Model(&DbModels.LinePreOrder{}).
|
||||||
@ -387,15 +441,21 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest
|
|||||||
}
|
}
|
||||||
|
|
||||||
//处理减仓单减仓策略
|
//处理减仓单减仓策略
|
||||||
CacheOrderStrategyAndReCreate(db, reduceOrder, 1, tradeSet, setting)
|
if err := CacheOrderStrategyAndReCreate(db, reduceOrder, 1, tradeSet, setting); err != nil {
|
||||||
|
log.Errorf("现货减仓 处理减仓策略失败 id:%v err:%v", reduceOrder.Id, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
|
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
|
||||||
|
result = false
|
||||||
log.Errorf("现货减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err)
|
log.Errorf("现货减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error("获取锁失败")
|
log.Error("获取锁失败")
|
||||||
|
result = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断现货加仓
|
// 判断现货加仓
|
||||||
|
|||||||
@ -550,6 +550,9 @@ func removeSpotLossAndAddPosition(mainId int, orderSn string) {
|
|||||||
addPosition := AddPositionList{}
|
addPosition := AddPositionList{}
|
||||||
reduce := ReduceListItem{}
|
reduce := ReduceListItem{}
|
||||||
|
|
||||||
|
//移除减仓后减仓策略
|
||||||
|
RemoveReduceReduceCacheByMainId(mainId, 1)
|
||||||
|
|
||||||
//止损缓存
|
//止损缓存
|
||||||
for _, v := range stoplossVal {
|
for _, v := range stoplossVal {
|
||||||
sonic.Unmarshal([]byte(v), &stoploss)
|
sonic.Unmarshal([]byte(v), &stoploss)
|
||||||
|
|||||||
Reference in New Issue
Block a user