1、订单增加策略模板

This commit is contained in:
2025-03-31 15:37:29 +08:00
parent 9ca1cd9a19
commit 0b95e32655
6 changed files with 145 additions and 35 deletions

View File

@ -9,38 +9,40 @@ import (
type LinePreOrder struct {
models.Model
ExchangeType string `json:"exchangeType" gorm:"type:varchar(20);comment:交易所类型 (字典 exchange_type"`
Pid int `json:"pid" gorm:"type:int unsigned;omitempty;comment:pid"`
MainId int `json:"mainId" gorm:"type:int;comment:主单id"`
ApiId int `json:"apiId" gorm:"type:varchar(255);omitempty;comment:api用户"`
GroupId string `json:"groupId" gorm:"type:int unsigned;omitempty;comment:交易对组id"`
Symbol string `json:"symbol" gorm:"type:varchar(255);omitempty;comment:交易对"`
QuoteSymbol string `json:"quoteSymbol" gorm:"type:varchar(255);omitempty;comment:计较货币"`
SignPrice string `json:"signPrice" gorm:"type:decimal(18,8);omitempty;comment:对标价"`
SignPriceU decimal.Decimal `json:"signPriceU" gorm:"type:decimal(18,8);omitempty;comment:交易对对标U的行情价"`
SignPriceType string `json:"signPriceType" gorm:"type:enum('new','tall','low','mixture','entrust','add');omitempty;comment:对标价类型: new=最新价格;tall=24小时最高;low=24小时最低;mixture=标记价;entrust=委托实价;add=补仓"`
Rate string `json:"rate" gorm:"type:decimal(18,2);omitempty;comment:下单百分比"`
Price string `json:"price" gorm:"type:decimal(18,8);omitempty;comment:触发价格"`
Num string `json:"num" gorm:"type:decimal(18,8);omitempty;comment:购买数量"`
BuyPrice string `json:"buyPrice" gorm:"type:decimal(18,8);omitempty;comment:购买金额"`
SymbolType int `json:"symbolType" gorm:"type:int;comment:交易对类型:1=现货;2=合约"`
OrderCategory int `json:"orderCategory" gorm:"type:int;comment:订单类型: 1=主单 2=对冲单 3-加仓单"`
Site string `json:"site" gorm:"type:enum('BUY','SELL');omitempty;comment:购买方向:BUY=买;SELL=卖"`
OrderSn string `json:"orderSn" gorm:"type:varchar(255);omitempty;comment:订单号"`
OrderType int `json:"orderType" gorm:"type:int;omitempty;comment:订单类型:0=主单 1=止盈 2=止损 3=平仓 4=减仓"`
Desc string `json:"desc" gorm:"type:text;omitempty;comment:订单描述"`
Status int `json:"status" gorm:"type:int;omitempty;comment:是否被触发:0=待触发;1=已触发;2=下单失败;4=已取消;5=委托中;6=已成交;9=已平仓"`
CoverType int `json:"coverType" gorm:"type:int unsigned;omitempty;comment:对冲类型 1= 现货对合约 2=合约对合约 3 合约对现货"`
ExpireTime time.Time `json:"expireTime" gorm:"comment:过期时间"`
MainOrderType string `json:"mainOrderType" gorm:"type:enum;comment:第一笔(主单类型) 限价LIMIT市价(MARKET)"`
LossAmount decimal.Decimal `json:"lossAmount" gorm:"type:decimal(18,8);comment:亏损金额U"`
TriggerTime *time.Time `json:"triggerTime" gorm:"type:datetime;comment:触发时间"`
Child []LinePreOrder `json:"child" gorm:"-"`
ApiName string `json:"api_name" gorm:"->"`
ChildNum int64 `json:"child_num" gorm:"->"`
AddPositionStatus int `json:"add_position_status" gorm:"->"`
ReduceStatus int `json:"reduce_status" gorm:"->"`
Childs []LinePreOrder `json:"childs" gorm:"foreignKey:pid;references:id"`
ExchangeType string `json:"exchangeType" gorm:"type:varchar(20);comment:交易所类型 (字典 exchange_type"`
StrategyTemplateType int `json:"strategyTemplateType" gorm:"type:tinyint;comment:策略类型 0-无 1-波段涨跌幅"`
StrategyTemplateId int `json:"strategyTemplateId" gorm:"type:bigint;comment:策略模板id"`
Pid int `json:"pid" gorm:"type:int unsigned;omitempty;comment:pid"`
MainId int `json:"mainId" gorm:"type:int;comment:主单id"`
ApiId int `json:"apiId" gorm:"type:varchar(255);omitempty;comment:api用户"`
GroupId string `json:"groupId" gorm:"type:int unsigned;omitempty;comment:交易对组id"`
Symbol string `json:"symbol" gorm:"type:varchar(255);omitempty;comment:交易对"`
QuoteSymbol string `json:"quoteSymbol" gorm:"type:varchar(255);omitempty;comment:计较货币"`
SignPrice string `json:"signPrice" gorm:"type:decimal(18,8);omitempty;comment:对标价"`
SignPriceU decimal.Decimal `json:"signPriceU" gorm:"type:decimal(18,8);omitempty;comment:交易对对标U的行情价"`
SignPriceType string `json:"signPriceType" gorm:"type:enum('new','tall','low','mixture','entrust','add');omitempty;comment:对标价类型: new=最新价格;tall=24小时最高;low=24小时最低;mixture=标记价;entrust=委托实价;add=补仓"`
Rate string `json:"rate" gorm:"type:decimal(18,2);omitempty;comment:下单百分比"`
Price string `json:"price" gorm:"type:decimal(18,8);omitempty;comment:触发价格"`
Num string `json:"num" gorm:"type:decimal(18,8);omitempty;comment:购买数量"`
BuyPrice string `json:"buyPrice" gorm:"type:decimal(18,8);omitempty;comment:购买金额"`
SymbolType int `json:"symbolType" gorm:"type:int;comment:交易对类型:1=现货;2=合约"`
OrderCategory int `json:"orderCategory" gorm:"type:int;comment:订单类型: 1=主单 2=对冲单 3-加仓单"`
Site string `json:"site" gorm:"type:enum('BUY','SELL');omitempty;comment:购买方向:BUY=买;SELL=卖"`
OrderSn string `json:"orderSn" gorm:"type:varchar(255);omitempty;comment:订单"`
OrderType int `json:"orderType" gorm:"type:int;omitempty;comment:订单类型:0=主单 1=止盈 2=止损 3=平仓 4=减仓"`
Desc string `json:"desc" gorm:"type:text;omitempty;comment:订单描述"`
Status int `json:"status" gorm:"type:int;omitempty;comment:是否被触发:0=待触发;1=已触发;2=下单失败;4=已取消;5=委托中;6=已成交;9=已平仓"`
CoverType int `json:"coverType" gorm:"type:int unsigned;omitempty;comment:对冲类型 1= 现货对合约 2=合约对合约 3 合约对现货"`
ExpireTime time.Time `json:"expireTime" gorm:"comment:过期时间"`
MainOrderType string `json:"mainOrderType" gorm:"type:enum;comment:第一笔(主单类型) 限价LIMIT市价(MARKET)"`
LossAmount decimal.Decimal `json:"lossAmount" gorm:"type:decimal(18,8);comment:亏损金额U"`
TriggerTime *time.Time `json:"triggerTime" gorm:"type:datetime;comment:触发时间"`
Child []LinePreOrder `json:"child" gorm:"-"`
ApiName string `json:"api_name" gorm:"->"`
ChildNum int64 `json:"child_num" gorm:"->"`
AddPositionStatus int `json:"add_position_status" gorm:"->"`
ReduceStatus int `json:"reduce_status" gorm:"->"`
Childs []LinePreOrder `json:"childs" gorm:"foreignKey:pid;references:id"`
// LinePreOrder 线上预埋单\
models.ModelTime
models.ControlBy

View File

@ -186,6 +186,8 @@ func (s *LinePreOrderDeleteReq) GetId() interface{} {
type LineAddPreOrderReq struct {
ExchangeType string `json:"exchange_type" vd:"len($)>0"` //交易所类型
StrategyTemplateType int `json:"strategy_template_type"` //策略类型 0-无 1-波段涨跌幅
StrategyTemplateId int `json:"strategy_template_id"` //策略id
OrderType int `json:"order_type"` //订单类型
Symbol string `json:"symbol"` //交易对
ApiUserId string `json:"api_id" ` //下单用户
@ -283,6 +285,10 @@ func (req LineAddPreOrderReq) Valid() error {
return errors.New("主单减仓价格百分比不能为空")
}
if req.StrategyTemplateType == 1 && req.StrategyTemplateId == 0 {
return errors.New("请选择策略")
}
if req.ReduceNumRatio.IsZero() {
return errors.New("主单减仓数量百分比不能为空")
}
@ -367,6 +373,8 @@ type LineTreeOrder struct {
// LineBatchAddPreOrderReq 批量添加订单请求参数
type LineBatchAddPreOrderReq struct {
ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type
StrategyTemplateType int `json:"strategy_template_type"` //策略类型 0-无 1-波段涨跌幅
StrategyTemplateId int `json:"strategy_template_id"` //策略id
SymbolType int `json:"symbol_type"` //主单交易对类型 0-现货 1-合约
OrderType int `json:"order_type"` //订单类型
SymbolGroupId string `json:"symbol_group_id"` //交易对组id
@ -404,6 +412,10 @@ func (req LineBatchAddPreOrderReq) CheckParams() error {
return errors.New("请选择交易所")
}
if req.StrategyTemplateType == 1 && req.StrategyTemplateId == 0 {
return errors.New("请选择策略")
}
if req.ApiUserId == "" {
return errors.New("选择下单用户")
}

View File

@ -74,6 +74,13 @@ const (
AveRequestToken = "ave_request_token" // AVE请求token
)
const (
//现货最后成交价 sort set {exchangeType,symbol}
SpotTickerLastPrice = "spot_ticker_last_price:%s:%s"
//合约最后成交价 sort set {exchangeType,symbol}
FutureTickerLastPrice = "fut_ticker_last_price:%s:%s"
)
// 用户下单
const (
MemberShipPre = "member_ship_pre:%v" //用户开通会员预下单 单价缓存{payable_amount}

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
"math"
"reflect"
"strconv"
"time"
@ -609,7 +610,7 @@ func (e *RedisHelper) SignelAdd(key string, score float64, member string) error
_, err := e.client.ZRemRangeByScore(e.ctx, key, scoreStr, scoreStr).Result()
if err != nil {
fmt.Printf("删除score失败", err.Error())
fmt.Printf("删除score失败,err:%s", err.Error())
}
_, err = e.client.ZAdd(e.ctx, key, &redis.Z{
Score: score,
@ -642,6 +643,46 @@ func (e *RedisHelper) DelSortSet(key, member string) error {
return e.client.ZRem(e.ctx, key, member).Err()
}
// RemoveBeforeScore 移除 Sorted Set 中分数小于等于指定值的数据
// key: Sorted Set 的键
// score: 分数上限,所有小于等于此分数的元素将被移除
// 返回值: 移除的元素数量和可能的错误
func (e *RedisHelper) RemoveBeforeScore(key string, score float64) (int64, error) {
if key == "" {
return 0, errors.New("key 不能为空")
}
if math.IsNaN(score) || math.IsInf(score, 0) {
return 0, errors.New("score 必须是有效数字")
}
// 使用 ZRemRangeByScore 移除数据
count, err := e.client.ZRemRangeByScore(e.ctx, key, "-inf", strconv.FormatFloat(score, 'f', -1, 64)).Result()
if err != nil {
return 0, fmt.Errorf("移除 Sorted Set 数据失败, key: %s, score: %f, err: %v", key, score, err)
}
return count, nil
}
// GetNextAfterScore 获取指定分数及之后的第一条数据(包含指定分数)
func (e *RedisHelper) GetNextAfterScore(key string, score float64) (string, error) {
// 使用 ZRangeByScore 获取大于等于 score 的第一条数据
zs, err := e.client.ZRangeByScoreWithScores(e.ctx, key, &redis.ZRangeBy{
Min: fmt.Sprintf("%f", score), // 包含指定分数
Max: "+inf", // 上限为正无穷
Offset: 0, // 从第 0 条开始
Count: 1, // 只取 1 条
}).Result()
if err != nil {
return "", fmt.Errorf("获取数据失败: %v", err)
}
if len(zs) == 0 {
return "", nil // 没有符合条件的元素
}
return zs[0].Member.(string), nil
}
/*
获取sort set 所有数据
*/

View File

@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"go-admin/common/const/rediskey"
"go-admin/common/global"
"go-admin/common/helper"
"go-admin/config"
@ -15,6 +16,7 @@ import (
"go-admin/models"
log "github.com/go-admin-team/go-admin-core/logger"
"github.com/shopspring/decimal"
"github.com/bytedance/sonic"
"go.uber.org/zap"
@ -133,7 +135,19 @@ func handleTickerAllMessage(msg []byte) {
// log.Debug(symbol, "ticker@all ws处理失败", err)
continue
}
tradeSet.LastPrice = utility.StringFloat64Cut(data["c"].(string), int32(tradeSet.PriceDigit))
var utcTime int64
var lastPrice decimal.Decimal
lastPriceKey := fmt.Sprintf(rediskey.FutureTickerLastPrice, global.EXCHANGE_BINANCE, symbol)
if e, ok := data["E"]; ok {
utcTime = utility.ToInt64(e)
}
if e, ok := data["c"]; ok {
lastPrice = utility.StrToDecimal(e.(string)).Truncate(int32(tradeSet.PriceDigit))
}
tradeSet.LastPrice = lastPrice.String()
tradeSet.OpenPrice = utility.StrToFloatCut(data["o"].(string), int32(tradeSet.PriceDigit))
tradeSet.HighPrice = utility.StringFloat64Cut(data["h"].(string), int32(tradeSet.PriceDigit))
tradeSet.LowPrice = utility.StringFloat64Cut(data["l"].(string), int32(tradeSet.PriceDigit))
@ -147,6 +161,16 @@ func handleTickerAllMessage(msg []byte) {
log.Error(symbol, "ticker@all ws处理失败", err)
}
}
//行情存储时间
lastUtc := utcTime - 1000*60*60
if _, err := helper.DefaultRedis.RemoveBeforeScore(lastPriceKey, float64(lastUtc)); err != nil {
log.Errorf("移除 合约交易对:%s %d之前的最新成交价失败,err:%v", symbol, lastUtc, err)
}
if err := helper.DefaultRedis.AddSortSet(lastPriceKey, float64(utcTime), lastPrice.String()); err != nil {
log.Errorf("添加 合约交易对:%s %d之前的最新成交价失败,err:%v", symbol, lastUtc, err)
}
}
if len(trades) > 0 {

View File

@ -204,6 +204,7 @@ func handleTickerMessage(msg []byte) {
}
key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BINANCE, symbolName)
lastPriceKey := fmt.Sprintf(rediskey.SpotTickerLastPrice, global.EXCHANGE_BINANCE, symbolName)
tcVal, _ := helper.DefaultRedis.GetString(key)
tradeSet := models.TradeSet{}
if err := sonic.UnmarshalString(tcVal, &tradeSet); err != nil {
@ -212,7 +213,19 @@ func handleTickerMessage(msg []byte) {
}
continue
}
tradeSet.LastPrice = utility.StringFloat64Cut(dataMap["c"].(string), int32(tradeSet.PriceDigit))
var utcTime int64
var lastPrice decimal.Decimal
if e, ok := dataMap["E"]; ok {
utcTime = utility.ToInt64(e)
}
if e, ok := dataMap["c"]; ok {
lastPrice = utility.StrToDecimal(e.(string)).Truncate(int32(tradeSet.PriceDigit))
}
tradeSet.LastPrice = lastPrice.String()
tradeSet.OpenPrice = utility.StrToFloatCut(dataMap["o"].(string), int32(tradeSet.PriceDigit))
tradeSet.HighPrice = utility.StringFloat64Cut(dataMap["h"].(string), int32(tradeSet.PriceDigit))
tradeSet.LowPrice = utility.StringFloat64Cut(dataMap["l"].(string), int32(tradeSet.PriceDigit))
@ -236,6 +249,17 @@ func handleTickerMessage(msg []byte) {
log.Error("redis保存交易对失败", tradeSet.Coin, tradeSet.Currency)
}
}
//行情存储时间
lastUtc := utcTime - 1000*60*60
if _, err := helper.DefaultRedis.RemoveBeforeScore(lastPriceKey, float64(lastUtc)); err != nil {
log.Errorf("移除 现货交易对:%s %d之前的最新成交价失败,err:%v", symbolName, lastUtc, err)
}
if err := helper.DefaultRedis.AddSortSet(lastPriceKey, float64(utcTime), lastPrice.String()); err != nil {
log.Errorf("添加 现货交易对:%s %d之前的最新成交价失败,err:%v", symbolName, lastUtc, err)
}
}
//判断触发现货下单