1、临时提交 反向下单
This commit is contained in:
@ -47,7 +47,7 @@ func (e LineReversePosition) GetPage(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := actions.GetPermissionFromContext(c)
|
p := actions.GetPermissionFromContext(c)
|
||||||
list := make([]models.LineReversePosition, 0)
|
list := make([]dto.LineReversePositionListResp, 0)
|
||||||
var count int64
|
var count int64
|
||||||
|
|
||||||
err = s.GetPage(&req, p, &list, &count)
|
err = s.GetPage(&req, p, &list, &count)
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
type LineReverseOrder struct {
|
type LineReverseOrder struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
|
PositionId int `json:"positionId" gorm:"type:bigint;comment:仓位id"`
|
||||||
ApiId int `json:"apiId" gorm:"type:bigint;comment:api id"`
|
ApiId int `json:"apiId" gorm:"type:bigint;comment:api id"`
|
||||||
Category int `json:"category" gorm:"type:tinyint;comment:分类 0-原始订单 1-反单"`
|
Category int `json:"category" gorm:"type:tinyint;comment:分类 0-原始订单 1-反单"`
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
type LineReversePosition struct {
|
type LineReversePosition struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
|
PositionNo string `json:"positionNo" gorm:"type:varchar(18);comment:仓位编号"`
|
||||||
ApiId int `json:"apiId" gorm:"type:bigint;comment:api_id"`
|
ApiId int `json:"apiId" gorm:"type:bigint;comment:api_id"`
|
||||||
TotalAmount decimal.Decimal `json:"totalAmount" gorm:"type:decimal(18,8);comment:总仓位"`
|
TotalAmount decimal.Decimal `json:"totalAmount" gorm:"type:decimal(18,8);comment:总仓位"`
|
||||||
Amount decimal.Decimal `json:"amount" gorm:"type:decimal(18,8);comment:仓位"`
|
Amount decimal.Decimal `json:"amount" gorm:"type:decimal(18,8);comment:仓位"`
|
||||||
@ -20,7 +21,8 @@ type LineReversePosition struct {
|
|||||||
Symbol string `json:"symbol" gorm:"type:varchar(20);comment:交易对"`
|
Symbol string `json:"symbol" gorm:"type:varchar(20);comment:交易对"`
|
||||||
Status int `json:"status" gorm:"type:tinyint;comment:仓位状态 1-已开仓 2-已平仓"`
|
Status int `json:"status" gorm:"type:tinyint;comment:仓位状态 1-已开仓 2-已平仓"`
|
||||||
ReverseStatus int `json:"reverseStatus" gorm:"type:tinyint;comment:反单仓位状态 1-已开仓 2-已平仓"`
|
ReverseStatus int `json:"reverseStatus" gorm:"type:tinyint;comment:反单仓位状态 1-已开仓 2-已平仓"`
|
||||||
|
AveragePrice decimal.Decimal `json:"averagePrice" gorm:"type:decimal(18,8);comment:主单平均价格"`
|
||||||
|
ReverseAveragePrice decimal.Decimal `json:"reverseAveragePrice" gorm:"type:decimal(18,8);comment:反单平均价格"`
|
||||||
models.ModelTime
|
models.ModelTime
|
||||||
models.ControlBy
|
models.ControlBy
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ type LineReverseOrderGetPageReq struct {
|
|||||||
PositionSide string `form:"positionSide" search:"type:exact;column:position_side;table:line_reverse_order" comment:"持仓方向 LONG-多 SHORT-空"`
|
PositionSide string `form:"positionSide" search:"type:exact;column:position_side;table:line_reverse_order" comment:"持仓方向 LONG-多 SHORT-空"`
|
||||||
Side string `form:"side" search:"type:exact;column:side;table:line_reverse_order" comment:"买卖方向 SELL-卖 BUY-买"`
|
Side string `form:"side" search:"type:exact;column:side;table:line_reverse_order" comment:"买卖方向 SELL-卖 BUY-买"`
|
||||||
Status int `form:"status" search:"type:exact;column:status;table:line_reverse_order" comment:"状态 1-待下单 2-已下单 3-已成交 4-已平仓 5-已止损"`
|
Status int `form:"status" search:"type:exact;column:status;table:line_reverse_order" comment:"状态 1-待下单 2-已下单 3-已成交 4-已平仓 5-已止损"`
|
||||||
|
PositionId int `form:"positionId" search:"type:exact;column:position_id;table:line_reverse_order" comment:"持仓id"`
|
||||||
LineReverseOrderOrder
|
LineReverseOrderOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ func (m *LineReverseOrderGetPageReq) GetNeedSearch() interface{} {
|
|||||||
|
|
||||||
type LineReverseOrderInsertReq struct {
|
type LineReverseOrderInsertReq struct {
|
||||||
Id int `json:"-" comment:"主键id"` // 主键id
|
Id int `json:"-" comment:"主键id"` // 主键id
|
||||||
PId int `json:"pId" comment:"主单id"`
|
PositionId int `json:"positionId" comment:"仓位id"`
|
||||||
OrderSn string `json:"orderSn" comment:"订单号"`
|
OrderSn string `json:"orderSn" comment:"订单号"`
|
||||||
OrderId string `json:"orderId" comment:"币安订单号"`
|
OrderId string `json:"orderId" comment:"币安订单号"`
|
||||||
FollowOrderSn string `json:"followOrderSn" comment:"跟随币安订单号"`
|
FollowOrderSn string `json:"followOrderSn" comment:"跟随币安订单号"`
|
||||||
@ -86,6 +87,7 @@ func (s *LineReverseOrderInsertReq) Generate(model *models.LineReverseOrder) {
|
|||||||
model.PositionSide = s.PositionSide
|
model.PositionSide = s.PositionSide
|
||||||
model.Side = s.Side
|
model.Side = s.Side
|
||||||
model.SignPrice = s.SignPrice
|
model.SignPrice = s.SignPrice
|
||||||
|
model.TriggerTime = &s.TriggerTime
|
||||||
model.Status = s.Status
|
model.Status = s.Status
|
||||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||||
}
|
}
|
||||||
@ -96,7 +98,7 @@ func (s *LineReverseOrderInsertReq) GetId() interface{} {
|
|||||||
|
|
||||||
type LineReverseOrderUpdateReq struct {
|
type LineReverseOrderUpdateReq struct {
|
||||||
Id int `uri:"id" comment:"主键id"` // 主键id
|
Id int `uri:"id" comment:"主键id"` // 主键id
|
||||||
PId int `json:"pId" comment:"主单id"`
|
PositionId int `json:"positionId" comment:"仓位id"`
|
||||||
OrderSn string `json:"orderSn" comment:"订单号"`
|
OrderSn string `json:"orderSn" comment:"订单号"`
|
||||||
OrderId string `json:"orderId" comment:"币安订单号"`
|
OrderId string `json:"orderId" comment:"币安订单号"`
|
||||||
FollowOrderSn string `json:"followOrderSn" comment:"跟随币安订单号"`
|
FollowOrderSn string `json:"followOrderSn" comment:"跟随币安订单号"`
|
||||||
@ -118,7 +120,6 @@ func (s *LineReverseOrderUpdateReq) Generate(model *models.LineReverseOrder) {
|
|||||||
if s.Id == 0 {
|
if s.Id == 0 {
|
||||||
model.Model = common.Model{Id: s.Id}
|
model.Model = common.Model{Id: s.Id}
|
||||||
}
|
}
|
||||||
|
|
||||||
model.OrderSn = s.OrderSn
|
model.OrderSn = s.OrderSn
|
||||||
model.OrderId = s.OrderId
|
model.OrderId = s.OrderId
|
||||||
model.FollowOrderSn = s.FollowOrderSn
|
model.FollowOrderSn = s.FollowOrderSn
|
||||||
@ -131,6 +132,7 @@ func (s *LineReverseOrderUpdateReq) Generate(model *models.LineReverseOrder) {
|
|||||||
model.PositionSide = s.PositionSide
|
model.PositionSide = s.PositionSide
|
||||||
model.Side = s.Side
|
model.Side = s.Side
|
||||||
model.SignPrice = s.SignPrice
|
model.SignPrice = s.SignPrice
|
||||||
|
model.TriggerTime = &s.TriggerTime
|
||||||
model.Status = s.Status
|
model.Status = s.Status
|
||||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,3 +115,21 @@ type LineReversePositionDeleteReq struct {
|
|||||||
func (s *LineReversePositionDeleteReq) GetId() interface{} {
|
func (s *LineReversePositionDeleteReq) GetId() interface{} {
|
||||||
return s.Ids
|
return s.Ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LineReversePositionListResp struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
ApiName string `json:"apiName"`
|
||||||
|
ReverseApiName string `json:"reverseApiName"`
|
||||||
|
Amount decimal.Decimal `json:"amount"`
|
||||||
|
TotalAmount decimal.Decimal `json:"totalAmount"`
|
||||||
|
ReverseAmount decimal.Decimal `json:"reverseAmount"`
|
||||||
|
TotalReverseAmount decimal.Decimal `json:"totalReverseAmount"`
|
||||||
|
Side string `json:"side"`
|
||||||
|
PositionSide string `json:"positionSide"`
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
ReverseStatus int `json:"reverseStatus"`
|
||||||
|
AveragePrice decimal.Decimal `json:"averagePrice"`
|
||||||
|
ReverseAveragePrice decimal.Decimal `json:"reverseAveragePrice"`
|
||||||
|
CreatedAt string `json:"createdAt"`
|
||||||
|
}
|
||||||
|
|||||||
@ -4,12 +4,14 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"go-admin/app/admin/models"
|
"go-admin/app/admin/models"
|
||||||
"go-admin/app/admin/service/dto"
|
"go-admin/app/admin/service/dto"
|
||||||
"go-admin/common/actions"
|
"go-admin/common/actions"
|
||||||
cDto "go-admin/common/dto"
|
cDto "go-admin/common/dto"
|
||||||
|
"go-admin/pkg/utility"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LineReversePosition struct {
|
type LineReversePosition struct {
|
||||||
@ -17,9 +19,10 @@ type LineReversePosition struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPage 获取LineReversePosition列表
|
// GetPage 获取LineReversePosition列表
|
||||||
func (e *LineReversePosition) GetPage(c *dto.LineReversePositionGetPageReq, p *actions.DataPermission, list *[]models.LineReversePosition, count *int64) error {
|
func (e *LineReversePosition) GetPage(c *dto.LineReversePositionGetPageReq, p *actions.DataPermission, list *[]dto.LineReversePositionListResp, count *int64) error {
|
||||||
var err error
|
var err error
|
||||||
var data models.LineReversePosition
|
var data models.LineReversePosition
|
||||||
|
var datas []models.LineReversePosition
|
||||||
|
|
||||||
err = e.Orm.Model(&data).
|
err = e.Orm.Model(&data).
|
||||||
Scopes(
|
Scopes(
|
||||||
@ -27,12 +30,52 @@ func (e *LineReversePosition) GetPage(c *dto.LineReversePositionGetPageReq, p *a
|
|||||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||||
actions.Permission(data.TableName(), p),
|
actions.Permission(data.TableName(), p),
|
||||||
).
|
).
|
||||||
Find(list).Limit(-1).Offset(-1).
|
Find(&datas).Limit(-1).Offset(-1).
|
||||||
Count(count).Error
|
Count(count).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("LineReversePositionService GetPage error:%s \r\n", err)
|
e.Log.Errorf("LineReversePositionService GetPage error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userIds := make([]int, 0)
|
||||||
|
|
||||||
|
for _, item := range datas {
|
||||||
|
if !utility.ContainsInt(userIds, item.ApiId) {
|
||||||
|
userIds = append(userIds, item.ApiId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utility.ContainsInt(userIds, item.ReverseApiId) {
|
||||||
|
userIds = append(userIds, item.ReverseApiId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userMap := make(map[int]string, 0)
|
||||||
|
|
||||||
|
if len(userIds) > 0 {
|
||||||
|
var users []models.LineApiUser
|
||||||
|
e.Orm.Model(&models.LineApiUser{}).
|
||||||
|
Where("id IN (?)", userIds).
|
||||||
|
Find(&users)
|
||||||
|
|
||||||
|
for _, item := range users {
|
||||||
|
userMap[int(item.Id)] = item.ApiName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range datas {
|
||||||
|
var resp dto.LineReversePositionListResp
|
||||||
|
copier.Copy(&resp, &item)
|
||||||
|
|
||||||
|
if userName, ok := userMap[item.ApiId]; ok {
|
||||||
|
resp.ApiName = userName
|
||||||
|
}
|
||||||
|
|
||||||
|
if userName, ok := userMap[item.ReverseApiId]; ok {
|
||||||
|
resp.ReverseApiName = userName
|
||||||
|
}
|
||||||
|
|
||||||
|
*list = append(*list, resp)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -463,22 +463,13 @@ func (r *RedisHelper) SetNX(key string, value interface{}, expiration time.Durat
|
|||||||
|
|
||||||
// SetHashWithTags 改进版:支持 struct 或 map 输入
|
// SetHashWithTags 改进版:支持 struct 或 map 输入
|
||||||
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
||||||
var fields map[string]interface{}
|
fields, err := getFieldsFromStruct(obj)
|
||||||
var err error
|
|
||||||
|
|
||||||
// 1. 优先检查 obj 是否已经是一个 map[string]interface{}
|
|
||||||
if m, ok := obj.(map[string]interface{}); ok {
|
|
||||||
fields = m // 如果是,直接使用这个 map
|
|
||||||
} else {
|
|
||||||
// 2. 如果不是 map,则假设它是一个 struct,并尝试从 struct 中获取字段
|
|
||||||
fields, err = getFieldsFromStruct(obj) // getFieldsFromStruct 现在需要返回 error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("从结构体获取字段失败: %w", err)
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := r.client.HSet(r.ctx, key, fields)
|
_, err = r.client.HSet(r.ctx, key, fields).Result()
|
||||||
return cmd.Err()
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFieldsFromStruct 改进版:处理指针并进行类型检查,返回 error
|
// getFieldsFromStruct 改进版:处理指针并进行类型检查,返回 error
|
||||||
|
|||||||
@ -37,6 +37,10 @@ type TradeSet struct {
|
|||||||
E int64 `json:"-"` //推送时间
|
E int64 `json:"-"` //推送时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *TradeSet) GetSymbol() string {
|
||||||
|
return e.Coin + e.Currency
|
||||||
|
}
|
||||||
|
|
||||||
//CommissionType int `db:"commissiontype"` //手续费:1买,2卖,3双向
|
//CommissionType int `db:"commissiontype"` //手续费:1买,2卖,3双向
|
||||||
//DepositNum float64 `db:"depositnum" json:"depositnum"` //保证金规模(手)
|
//DepositNum float64 `db:"depositnum" json:"depositnum"` //保证金规模(手)
|
||||||
//ForceRate float64 `db:"forcerate" json:"forcerate"` //维持保证金率1%
|
//ForceRate float64 `db:"forcerate" json:"forcerate"` //维持保证金率1%
|
||||||
|
|||||||
@ -28,3 +28,7 @@ func init() {
|
|||||||
func GetOrderId() int64 {
|
func GetOrderId() int64 {
|
||||||
return snowNode.Generate().Int64()
|
return snowNode.Generate().Int64()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetOrderNo() string {
|
||||||
|
return fmt.Sprintf("%d", GetOrderId())
|
||||||
|
}
|
||||||
|
|||||||
@ -49,7 +49,6 @@ func (e *FuturesResetV2) OrderPlace(apiUserInfo *DbModels.LineApiUser, params Fu
|
|||||||
paramsMaps := map[string]string{
|
paramsMaps := map[string]string{
|
||||||
"symbol": params.Symbol,
|
"symbol": params.Symbol,
|
||||||
"side": side,
|
"side": side,
|
||||||
"quantity": params.Quantity.String(),
|
|
||||||
"type": orderType,
|
"type": orderType,
|
||||||
"newClientOrderId": params.NewClientOrderId,
|
"newClientOrderId": params.NewClientOrderId,
|
||||||
"positionSide": params.PositionSide,
|
"positionSide": params.PositionSide,
|
||||||
@ -64,6 +63,10 @@ func (e *FuturesResetV2) OrderPlace(apiUserInfo *DbModels.LineApiUser, params Fu
|
|||||||
paramsMaps["timeInForce"] = "GTC"
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
paramsMaps["stopprice"] = params.Profit.String()
|
paramsMaps["stopprice"] = params.Profit.String()
|
||||||
paramsMaps["workingType"] = "MARK_PRICE"
|
paramsMaps["workingType"] = "MARK_PRICE"
|
||||||
|
|
||||||
|
if params.ClosePosition {
|
||||||
|
paramsMaps["closePosition"] = "true"
|
||||||
|
}
|
||||||
case "TAKE_PROFIT":
|
case "TAKE_PROFIT":
|
||||||
paramsMaps["price"] = params.Price.String()
|
paramsMaps["price"] = params.Price.String()
|
||||||
paramsMaps["stopprice"] = params.Profit.String()
|
paramsMaps["stopprice"] = params.Profit.String()
|
||||||
@ -72,6 +75,10 @@ func (e *FuturesResetV2) OrderPlace(apiUserInfo *DbModels.LineApiUser, params Fu
|
|||||||
paramsMaps["stopprice"] = params.StopPrice.String()
|
paramsMaps["stopprice"] = params.StopPrice.String()
|
||||||
paramsMaps["workingType"] = "MARK_PRICE"
|
paramsMaps["workingType"] = "MARK_PRICE"
|
||||||
paramsMaps["timeInForce"] = "GTC"
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
|
|
||||||
|
if params.ClosePosition {
|
||||||
|
paramsMaps["closePosition"] = "true"
|
||||||
|
}
|
||||||
case "STOP":
|
case "STOP":
|
||||||
paramsMaps["price"] = params.Price.String()
|
paramsMaps["price"] = params.Price.String()
|
||||||
paramsMaps["stopprice"] = params.StopPrice.String()
|
paramsMaps["stopprice"] = params.StopPrice.String()
|
||||||
@ -79,6 +86,10 @@ func (e *FuturesResetV2) OrderPlace(apiUserInfo *DbModels.LineApiUser, params Fu
|
|||||||
paramsMaps["timeInForce"] = "GTC"
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//不是平仓
|
||||||
|
if !params.ClosePosition {
|
||||||
|
paramsMaps["quantity"] = params.Quantity.String()
|
||||||
|
}
|
||||||
// 获取 API 信息和发送下单请求
|
// 获取 API 信息和发送下单请求
|
||||||
client := GetClient(apiUserInfo)
|
client := GetClient(apiUserInfo)
|
||||||
_, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps)
|
_, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps)
|
||||||
@ -99,7 +110,7 @@ func parseOrderError(err error, paramsMaps map[string]string, statusCode int, ap
|
|||||||
log.Error("下单失败 参数:", paramsVal)
|
log.Error("下单失败 参数:", paramsVal)
|
||||||
errContent := FutErrorMaps[code.(float64)]
|
errContent := FutErrorMaps[code.(float64)]
|
||||||
if errContent == "" {
|
if errContent == "" {
|
||||||
errContent = err.Error()
|
errContent, _ = dataMap["msg"].(string)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], errContent)
|
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], errContent)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -784,6 +784,27 @@ func (e FutRestApi) CancelAllFutOrder(apiUserInfo DbModels.LineApiUser, symbol s
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 带重试的批量撤销订单
|
||||||
|
func (e FutRestApi) CancelBatchFutOrderLoop(apiUserInfo DbModels.LineApiUser, symbol string, newClientOrderIdList []string) error {
|
||||||
|
opts := retryhelper.DefaultRetryOptions()
|
||||||
|
opts.RetryableErrFn = func(err error) bool {
|
||||||
|
if strings.Contains(err.Error(), "LOT_SIZE") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
//重试
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retryhelper.Retry(func() error {
|
||||||
|
return e.CancelBatchFutOrder(apiUserInfo, symbol, newClientOrderIdList)
|
||||||
|
}, opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CancelBatchFutOrder 批量撤销订单
|
// CancelBatchFutOrder 批量撤销订单
|
||||||
// symbol 交易对
|
// symbol 交易对
|
||||||
// newClientOrderIdList 系统自定义的订单号, 最多支持10个订单
|
// newClientOrderIdList 系统自定义的订单号, 最多支持10个订单
|
||||||
|
|||||||
@ -70,36 +70,6 @@ func ChangeFutureOrder(mapData map[string]interface{}, apiKey string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("合约订单回调失败,反单失败:%v", err)
|
logger.Errorf("合约订单回调失败,反单失败:%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 以前的下单逻辑
|
|
||||||
// 查询订单
|
|
||||||
// preOrder, err := getPreOrder(db, orderSn)
|
|
||||||
// if err != nil {
|
|
||||||
// logger.Error("合约订单回调失败,查询订单失败:", orderSn, " err:", err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 解析订单状态
|
|
||||||
// status, ok := mapData["X"].(string)
|
|
||||||
// if !ok {
|
|
||||||
// mapStr, _ := sonic.Marshal(&mapData)
|
|
||||||
// logger.Error("订单回调失败,没有状态:", string(mapStr))
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// // 更新订单状态
|
|
||||||
// orderStatus, reason := parseOrderStatus(status, mapData)
|
|
||||||
|
|
||||||
// if orderStatus == 0 {
|
|
||||||
// logger.Error("订单回调失败,状态错误:", orderSn, " status:", status, " reason:", reason)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if err := updateOrderStatus(db, preOrder, orderStatus, reason, true, mapData); err != nil {
|
|
||||||
// logger.Error("修改订单状态失败:", orderSn, " err:", err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// handleFutOrderByType(db, preOrder, orderStatus)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合约回调
|
// 合约回调
|
||||||
|
|||||||
@ -73,6 +73,7 @@ type FutOrderPlace struct {
|
|||||||
StopPrice decimal.Decimal `json:"stopprice"` //止损价格
|
StopPrice decimal.Decimal `json:"stopprice"` //止损价格
|
||||||
OrderType string `json:"order_type"` //订单类型,市价或限价MARKET(市价单) TAKE_PROFIT_MARKET(市价止盈) TAKE_PROFIT(限价止盈) STOP (限价止损) STOP_MARKET(市价止损)
|
OrderType string `json:"order_type"` //订单类型,市价或限价MARKET(市价单) TAKE_PROFIT_MARKET(市价止盈) TAKE_PROFIT(限价止盈) STOP (限价止损) STOP_MARKET(市价止损)
|
||||||
NewClientOrderId string `json:"newClientOrderId"`
|
NewClientOrderId string `json:"newClientOrderId"`
|
||||||
|
ClosePosition bool `json:"closePosition"` //是否平仓
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s FutOrderPlace) CheckParams() error {
|
func (s FutOrderPlace) CheckParams() error {
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import (
|
|||||||
DbModels "go-admin/app/admin/models"
|
DbModels "go-admin/app/admin/models"
|
||||||
"go-admin/common/global"
|
"go-admin/common/global"
|
||||||
"go-admin/common/helper"
|
"go-admin/common/helper"
|
||||||
|
"go-admin/models"
|
||||||
"go-admin/pkg/maphelper"
|
"go-admin/pkg/maphelper"
|
||||||
|
"go-admin/pkg/utility/snowflakehelper"
|
||||||
"go-admin/services/cacheservice"
|
"go-admin/services/cacheservice"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -68,10 +70,23 @@ func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interfac
|
|||||||
result, err := e.handleSimpleStatusChange(status, orderSn, mapData)
|
result, err := e.handleSimpleStatusChange(status, orderSn, mapData)
|
||||||
|
|
||||||
//如果是 新开止盈止损 需要取消反单的止盈止损之后重下反单止盈止损
|
//如果是 新开止盈止损 需要取消反单的止盈止损之后重下反单止盈止损
|
||||||
if status == "NEW" && (ot == "TAKE_PROFIT_MARKET" || ot == "STOP_LOSS_MARKET" || ot == "TAKE_PROFIT_LIMIT" || ot == "STOP_LOSS_LIMIT") &&
|
if status == "NEW" && (ot == "TAKE_PROFIT_MARKET" || ot == "STOP_MARKET" || ot == "TAKE_PROFIT" || ot == "STOP") &&
|
||||||
apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {
|
apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {
|
||||||
|
symbolCode, err := maphelper.GetString(mapData, "s")
|
||||||
|
|
||||||
if err := e.ReTakeOrStopOrder(&mapData, orderSn, &apiInfo); err != nil {
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取symbolCode失败 symbol:%s err:%v", symbolCode, err)
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, symbolCode, 1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取symbol失败 symbol:%s err:%v", symbolCode, err)
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.ReTakeOrStopOrder(&mapData, orderSn, &apiInfo, &symbol); err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,6 +143,7 @@ func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interfac
|
|||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
price := maphelper.GetDecimal(mapData, "ap")
|
||||||
totalNum := maphelper.GetDecimal(mapData, "z")
|
totalNum := maphelper.GetDecimal(mapData, "z")
|
||||||
|
|
||||||
mainOrder := DbModels.LineReverseOrder{
|
mainOrder := DbModels.LineReverseOrder{
|
||||||
@ -136,6 +152,7 @@ func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interfac
|
|||||||
PositionSide: positionSide,
|
PositionSide: positionSide,
|
||||||
TotalNum: totalNum,
|
TotalNum: totalNum,
|
||||||
Side: side,
|
Side: side,
|
||||||
|
Price: price,
|
||||||
}
|
}
|
||||||
e.changeOrderStatus(3, orderSn, mapData)
|
e.changeOrderStatus(3, orderSn, mapData)
|
||||||
|
|
||||||
@ -175,8 +192,8 @@ func (e *ReverseService) changeOrderStatus(status int, orderSn string, mapData m
|
|||||||
data["order_id"] = orderId
|
data["order_id"] = orderId
|
||||||
}
|
}
|
||||||
|
|
||||||
if ap, ok := mapData["ap"].(bool); ok {
|
if ap, ok := mapData["ap"].(string); ok {
|
||||||
data["final_price"] = ap
|
data["final_price"], _ = decimal.NewFromString(ap)
|
||||||
}
|
}
|
||||||
|
|
||||||
if num, ok := mapData["z"].(string); ok {
|
if num, ok := mapData["z"].(string); ok {
|
||||||
@ -184,6 +201,10 @@ func (e *ReverseService) changeOrderStatus(status int, orderSn string, mapData m
|
|||||||
}
|
}
|
||||||
|
|
||||||
data["trigger_time"] = &now
|
data["trigger_time"] = &now
|
||||||
|
} else if status == 2 {
|
||||||
|
if orderId, ok := mapData["i"].(float64); ok {
|
||||||
|
data["order_id"] = orderId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db := e.Orm.Model(&DbModels.LineReverseOrder{}).
|
db := e.Orm.Model(&DbModels.LineReverseOrder{}).
|
||||||
@ -284,6 +305,12 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
|||||||
side := reverseOrder.Side
|
side := reverseOrder.Side
|
||||||
totalNum := reverseOrder.TotalNum
|
totalNum := reverseOrder.TotalNum
|
||||||
|
|
||||||
|
symbol, err1 := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, reverseOrder.Symbol, 1)
|
||||||
|
|
||||||
|
if err1 != nil {
|
||||||
|
e.Log.Errorf("获取交易对失败 symbol:%s err:%v", reverseOrder.Symbol, err1)
|
||||||
|
}
|
||||||
|
|
||||||
var querySql string
|
var querySql string
|
||||||
sqlStr := ""
|
sqlStr := ""
|
||||||
|
|
||||||
@ -310,6 +337,8 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
|||||||
position.Status = 1
|
position.Status = 1
|
||||||
position.ReverseStatus = 0
|
position.ReverseStatus = 0
|
||||||
position.PositionSide = positionSide
|
position.PositionSide = positionSide
|
||||||
|
position.AveragePrice = reverseOrder.FinalPrice
|
||||||
|
position.PositionNo = snowflakehelper.GetOrderNo()
|
||||||
}
|
}
|
||||||
querySql = "api_id =? and position_side =? and symbol =? and status =1"
|
querySql = "api_id =? and position_side =? and symbol =? and status =1"
|
||||||
|
|
||||||
@ -319,9 +348,9 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
|||||||
sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2 where id =@id and status!=2 "
|
sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2 where id =@id and status!=2 "
|
||||||
} else if reducePosition {
|
} else if reducePosition {
|
||||||
//只减仓
|
//只减仓
|
||||||
sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now() where id =@id and status!=2 "
|
sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now(),average_price=@averagePrice where id =@id and status!=2 "
|
||||||
} else {
|
} else {
|
||||||
sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now() where id =@id and status!=2"
|
sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now(),average_price=@averagePrice where id =@id and status!=2"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"
|
querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"
|
||||||
@ -330,12 +359,14 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
|||||||
totalNum = decimal.Zero
|
totalNum = decimal.Zero
|
||||||
sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2 where id =@id and reverse_status !=2"
|
sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2 where id =@id and reverse_status !=2"
|
||||||
} else if reducePosition {
|
} else if reducePosition {
|
||||||
sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now() where id =@id and reverse_status !=2"
|
sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now(),reverse_average_price=@averagePrice where id =@id and reverse_status !=2"
|
||||||
} else {
|
} else {
|
||||||
sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1 where id =@id and reverse_status !=2"
|
sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1,reverse_average_price=@averagePrice where id =@id and reverse_status !=2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var averagePrice decimal.Decimal
|
||||||
|
|
||||||
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
err1 := tx.Model(&position).Where(querySql,
|
err1 := tx.Model(&position).Where(querySql,
|
||||||
reverseOrder.ApiId, positionSide, reverseOrder.Symbol).First(&position).Error
|
reverseOrder.ApiId, positionSide, reverseOrder.Symbol).First(&position).Error
|
||||||
@ -347,16 +378,69 @@ func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, r
|
|||||||
if err2 := tx.Create(&position).Error; err2 != nil {
|
if err2 := tx.Create(&position).Error; err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
averagePrice = position.AveragePrice
|
||||||
} else {
|
} else {
|
||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var totalAmount decimal.Decimal
|
||||||
|
var totalPrice decimal.Decimal
|
||||||
|
if !position.Amount.IsZero() && !position.AveragePrice.IsZero() {
|
||||||
|
if isMain {
|
||||||
|
totalPrice = position.Amount.Mul(position.AveragePrice)
|
||||||
|
totalAmount = position.Amount
|
||||||
|
} else {
|
||||||
|
totalPrice = position.ReverseAmount.Mul(position.ReverseAveragePrice)
|
||||||
|
totalAmount = position.ReverseAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id))
|
//加仓
|
||||||
|
if !reducePosition {
|
||||||
|
totalPrice = totalPrice.Add(reverseOrder.Price.Mul(reverseOrder.TotalNum))
|
||||||
|
totalAmount = totalAmount.Add(reverseOrder.TotalNum)
|
||||||
|
} else if reducePosition && !closePosition {
|
||||||
|
//只减仓
|
||||||
|
totalPrice = totalPrice.Sub(reverseOrder.Price.Mul(reverseOrder.TotalNum))
|
||||||
|
totalAmount = totalAmount.Sub(reverseOrder.TotalNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalAmount.IsZero() || totalPrice.IsZero() {
|
||||||
|
if isMain {
|
||||||
|
averagePrice = position.AveragePrice
|
||||||
|
} else {
|
||||||
|
if position.ReverseAveragePrice.IsZero() {
|
||||||
|
averagePrice = reverseOrder.Price
|
||||||
|
} else {
|
||||||
|
averagePrice = position.ReverseAveragePrice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//平仓
|
||||||
|
if closePosition {
|
||||||
|
if isMain {
|
||||||
|
averagePrice = position.AveragePrice
|
||||||
|
} else {
|
||||||
|
averagePrice = position.ReverseAveragePrice
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
averagePrice = totalPrice.Div(totalAmount).Truncate(int32(symbol.PriceDigit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//关联订单的仓位id
|
||||||
|
if err2 := tx.Exec("UPDATE line_reverse_order set position_id=@positionId where id=@orderId and position_id = 0", sql.Named("positionId", position.Id), sql.Named("orderId", reverseOrder.Id)).Error; err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id), sql.Named("averagePrice", averagePrice))
|
||||||
if dbResult.Error != nil {
|
if dbResult.Error != nil {
|
||||||
return dbResult.Error
|
return dbResult.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reverseOrder.PositionId = position.Id
|
||||||
if dbResult.RowsAffected == 0 {
|
if dbResult.RowsAffected == 0 {
|
||||||
e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, reverseOrder)
|
e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, reverseOrder)
|
||||||
return errors.New("没有找到对应的持仓信息")
|
return errors.New("没有找到对应的持仓信息")
|
||||||
@ -471,8 +555,8 @@ func (e *ReverseService) DoAddReverseOrder(mainOrder *DbModels.LineReverseOrder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//反向下单百分比
|
//反向下单百分比
|
||||||
proportion = proportion.Div(decimal.NewFromInt(100)).Truncate(2)
|
proportion = proportion.Div(decimal.NewFromInt(100)).Truncate(4)
|
||||||
amount = mainOrder.BuyPrice.Mul(proportion).Div(price).Truncate(int32(symbol.AmountDigit))
|
amount = mainOrder.TotalNum.Mul(proportion).Truncate(int32(symbol.AmountDigit))
|
||||||
|
|
||||||
if amount.Cmp(decimal.Zero) <= 0 {
|
if amount.Cmp(decimal.Zero) <= 0 {
|
||||||
e.Log.Errorf("计算数量失败 symbol:%s custom:%s 数量小于0", mainOrder.Symbol, mainOrder.OrderSn)
|
e.Log.Errorf("计算数量失败 symbol:%s custom:%s 数量小于0", mainOrder.Symbol, mainOrder.OrderSn)
|
||||||
@ -487,6 +571,7 @@ func (e *ReverseService) DoAddReverseOrder(mainOrder *DbModels.LineReverseOrder,
|
|||||||
order.Status = 1
|
order.Status = 1
|
||||||
order.Type = setting.ReverseOrderType
|
order.Type = setting.ReverseOrderType
|
||||||
order.SignPrice = signPrice
|
order.SignPrice = signPrice
|
||||||
|
order.PositionId = mainOrder.PositionId
|
||||||
|
|
||||||
if order.Remark != "" {
|
if order.Remark != "" {
|
||||||
order.Status = 8
|
order.Status = 8
|
||||||
@ -498,12 +583,64 @@ func (e *ReverseService) DoAddReverseOrder(mainOrder *DbModels.LineReverseOrder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if order.Status == 1 {
|
if order.Status == 1 {
|
||||||
e.DoBianceOrder(&order, reverseApiInfo, &setting, reducePosition, closePosition)
|
err = e.DoBianceOrder(&order, reverseApiInfo, &setting, reducePosition, closePosition)
|
||||||
|
|
||||||
|
//Biance下单成共 且为平仓单时 取消止盈止损
|
||||||
|
if err == nil && closePosition {
|
||||||
|
e.DoCancelTakeProfitBatch(symbol.GetSymbol(), order.PositionSide, order.Side, -1, reverseApiInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取消止盈止损订单
|
||||||
|
// symbol: 交易对
|
||||||
|
// positionSide: 持仓方向
|
||||||
|
// side: 订单方向
|
||||||
|
// orderType: 订单类型 -1-全部 1-止盈 2-止损
|
||||||
|
// apiInfo: 下单api
|
||||||
|
func (e *ReverseService) DoCancelTakeProfitBatch(symbol, positionSide, side string, orderType int, apiInfo *DbModels.LineApiUser) error {
|
||||||
|
orderSns := e.GetTakeProfitBatch(symbol, positionSide, side, apiInfo.Id, orderType)
|
||||||
|
|
||||||
|
if len(orderSns) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(orderSns) > 0 {
|
||||||
|
futApi := FutRestApi{}
|
||||||
|
err := futApi.CancelBatchFutOrderLoop(*apiInfo, symbol, orderSns)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("币安撤单失败 symbol:%s custom:%v :%v", symbol, orderSns, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取止盈止损订单
|
||||||
|
// symbol: 交易对
|
||||||
|
// positionSide: 持仓方向
|
||||||
|
// side: 订单方向
|
||||||
|
// apiId: 币安apiId
|
||||||
|
// orderType: 订单类型 -1-全部 1-止盈 2-止损
|
||||||
|
func (e *ReverseService) GetTakeProfitBatch(symbol, positionSide, side string, apiId int, orderType int) []string {
|
||||||
|
var orderSns []string
|
||||||
|
orderTypes := []int{1, 2}
|
||||||
|
|
||||||
|
if orderType == 1 {
|
||||||
|
orderTypes = []int{1}
|
||||||
|
} else if orderType == 2 {
|
||||||
|
orderTypes = []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Orm.Model(&DbModels.LineReverseOrder{}).Where("symbol =? and position_side =? and side = ? and status =2 and order_type in ?", symbol, positionSide, side, orderTypes).Pluck("order_sn", &orderSns)
|
||||||
|
|
||||||
|
return orderSns
|
||||||
|
}
|
||||||
|
|
||||||
// 处理币安订单
|
// 处理币安订单
|
||||||
// order: 反单信息
|
// order: 反单信息
|
||||||
// apiInfo: 币安api信息
|
// apiInfo: 币安api信息
|
||||||
@ -546,13 +683,8 @@ func (e *ReverseService) DoBianceOrder(order *DbModels.LineReverseOrder, apiInfo
|
|||||||
|
|
||||||
// 重下止盈止损
|
// 重下止盈止损
|
||||||
// mapData: 主单止盈止损回调
|
// mapData: 主单止盈止损回调
|
||||||
func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser) error {
|
func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser, symbol *models.TradeSet) error {
|
||||||
symbol, err := maphelper.GetString(*mapData, "s")
|
orderType := 0 //订单类型 1-止盈 2-止损
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
side, err := maphelper.GetString(*mapData, "S")
|
side, err := maphelper.GetString(*mapData, "S")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -584,6 +716,12 @@ func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orde
|
|||||||
positionSide = "LONG"
|
positionSide = "LONG"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close := maphelper.GetBool(*mapData, "cp")
|
||||||
|
stopPrice := maphelper.GetDecimal(*mapData, "sp")
|
||||||
|
if stopPrice.IsZero() {
|
||||||
|
e.Log.Errorf("获取止盈止损单触发价失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)
|
apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -591,33 +729,96 @@ func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orde
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var reverseOrder DbModels.LineReverseOrder
|
switch ot {
|
||||||
e.Orm.Model(&DbModels.LineReverseOrder{}).
|
case "STOP_MARKET", "STOP":
|
||||||
Where("symbol =? and api_id =? and position_side =? and side = ? and status =1", symbol, mainApiInfo.ReverseApiId, positionSide, side).
|
orderType = 2
|
||||||
First(&reverseOrder)
|
case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
|
||||||
|
orderType = 1
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的订单类型 ot:%s", ot)
|
||||||
|
}
|
||||||
|
|
||||||
//取消旧止盈止损
|
var reversePosition DbModels.LineReversePosition
|
||||||
if reverseOrder.OrderSn != "" {
|
|
||||||
futApi := FutRestApi{}
|
|
||||||
err := futApi.CancelFutOrderRetry(apiInfo, symbol, reverseOrder.OrderSn)
|
|
||||||
|
|
||||||
if err != nil {
|
e.Orm.Model(&reversePosition).
|
||||||
e.Log.Errorf("币安撤单失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
Where("symbol =? and reverse_api_id =? and position_side =? and reverse_status =1", symbol.GetSymbol(), apiInfo.Id, positionSide).
|
||||||
|
First(&reversePosition)
|
||||||
|
|
||||||
|
if reversePosition.Id == 0 {
|
||||||
|
e.Log.Errorf("获取反单持仓失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainPercent := decimal.NewFromInt(1)
|
||||||
|
|
||||||
|
if !stopPrice.IsZero() && !reversePosition.AveragePrice.IsZero() {
|
||||||
|
mainPercent = stopPrice.Div(reversePosition.AveragePrice)
|
||||||
|
mainPercent = (mainPercent.Sub(decimal.NewFromInt(1))).Abs().Truncate(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var percent decimal.Decimal
|
||||||
|
switch {
|
||||||
|
//做多止损
|
||||||
|
case orderType == 2 && positionSide == "LONG", orderType == 1 && positionSide == "SHORT":
|
||||||
|
percent = decimal.NewFromInt(1).Sub(mainPercent)
|
||||||
|
case orderType == 2 && positionSide == "SHORT", orderType == 1 && positionSide == "LONG":
|
||||||
|
percent = decimal.NewFromInt(1).Add(mainPercent)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的订单类型 ot:%s, ps:%s", ot, positionSide)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
price := reversePosition.AveragePrice.Mul(percent).Truncate(int32(symbol.PriceDigit))
|
||||||
|
lastPrice, _ := decimal.NewFromString(symbol.LastPrice)
|
||||||
|
newOrder := DbModels.LineReverseOrder{
|
||||||
|
PositionId: reversePosition.Id,
|
||||||
|
OrderSn: helper.GetOrderNo(),
|
||||||
|
OrderType: orderType,
|
||||||
|
Status: 1,
|
||||||
|
Price: price,
|
||||||
|
TotalNum: reversePosition.TotalReverseAmount,
|
||||||
|
Symbol: symbol.GetSymbol(),
|
||||||
|
Side: side,
|
||||||
|
PositionSide: positionSide,
|
||||||
|
FollowOrderSn: orderSn,
|
||||||
|
Type: ot,
|
||||||
|
SignPrice: lastPrice,
|
||||||
|
Category: 1,
|
||||||
|
ApiId: apiInfo.Id,
|
||||||
|
IsAddPosition: 2,
|
||||||
|
TriggerTime: &now,
|
||||||
|
BuyPrice: reversePosition.TotalReverseAmount.Mul(price).Truncate(int32(symbol.PriceDigit)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err1 := e.Orm.Create(&newOrder).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("保存反单止盈止损失败 symbol:%s custom:%s :%v", symbol, orderSn, err1)
|
||||||
|
return err1
|
||||||
|
}
|
||||||
params := FutOrderPlace{
|
params := FutOrderPlace{
|
||||||
ApiId: apiInfo.Id,
|
ApiId: apiInfo.Id,
|
||||||
Symbol: symbol,
|
Symbol: symbol.GetSymbol(),
|
||||||
PositionSide: positionSide,
|
PositionSide: positionSide,
|
||||||
Side: side,
|
Side: side,
|
||||||
OrderType: ot,
|
OrderType: ot,
|
||||||
Quantity: reverseOrder.TotalNum,
|
Quantity: reversePosition.TotalReverseAmount,
|
||||||
Price: reverseOrder.Price,
|
Price: price,
|
||||||
NewClientOrderId: reverseOrder.OrderSn,
|
StopPrice: price,
|
||||||
|
Profit: price,
|
||||||
|
NewClientOrderId: newOrder.OrderSn,
|
||||||
|
ClosePosition: close,
|
||||||
}
|
}
|
||||||
futApiV2 := FuturesResetV2{Service: e.Service}
|
futApiV2 := FuturesResetV2{Service: e.Service}
|
||||||
futApiV2.OrderPlace(&apiInfo, params)
|
err = futApiV2.OrderPlaceLoop(&apiInfo, params)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", symbol.GetSymbol(), orderSn, err)
|
||||||
|
|
||||||
|
if err1 := e.Orm.Model(&newOrder).Updates(map[string]interface{}{"status": 8, "remark": err.Error(), "updated_at": time.Now()}).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", newOrder.Symbol, newOrder.OrderSn, err1)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.DoCancelTakeProfitBatch(symbol.GetSymbol(), positionSide, side, orderType, &apiInfo)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,34 @@
|
|||||||
package binanceservice
|
package binanceservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/common/helper"
|
||||||
|
"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 initConfig() *gorm.DB {
|
||||||
|
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")
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
func TestReverseOrder(t *testing.T) {
|
||||||
|
db := initConfig()
|
||||||
|
mapData := make(map[string]interface{})
|
||||||
|
content := `{"s":"ADAUSDT","c":"439301585084878848","S":"SELL","o":"LIMIT","f":"GTC","q":"8","p":"0.7781","ap":"0.7843","sp":"0","x":"TRADE","X":"FILLED","i":56468022240,"l":"8","z":"8","L":"0.7843","n":"0.0031372","N":"USDT","T":1753950025820,"t":1642816051,"b":"0","a":"0","m":false,"R":false,"wt":"CONTRACT_PRICE","ot":"LIMIT","ps":"SHORT","cp":false,"rp":"0","pP":false,"si":0,"ss":0,"V":"EXPIRE_MAKER","pm":"NONE","gtd":0}`
|
||||||
|
sonic.Unmarshal([]byte(content), &mapData)
|
||||||
|
|
||||||
|
service := ReverseService{}
|
||||||
|
service.Orm = db
|
||||||
|
service.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
|
||||||
|
service.ReverseOrder("c8ej2vxXzNUIlCjQCmU1iavK8LG78uZaXY3CIT4kz0PuhnXycg44HsVAbsqupHTw", mapData)
|
||||||
|
}
|
||||||
|
|||||||
@ -522,7 +522,7 @@ func (wm *BinanceWebSocketManager) Stop() {
|
|||||||
|
|
||||||
// 重连机制
|
// 重连机制
|
||||||
func (wm *BinanceWebSocketManager) handleReconnect(ctx context.Context) {
|
func (wm *BinanceWebSocketManager) handleReconnect(ctx context.Context) {
|
||||||
maxRetries := 5 // 最大重试次数
|
maxRetries := 100 // 最大重试次数
|
||||||
retryCount := 0
|
retryCount := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|||||||
Reference in New Issue
Block a user