1、反向下单 暂时提交
This commit is contained in:
@ -305,3 +305,26 @@ func (e LineApiUser) GetUnBindReverseApiUser(c *gin.Context) {
|
|||||||
|
|
||||||
e.OK(datas, "查询成功")
|
e.OK(datas, "查询成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取反单api用户选项
|
||||||
|
func (e LineApiUser) GetReverseApiOptions(c *gin.Context) {
|
||||||
|
req := dto.GetReverseApiOptionsReq{}
|
||||||
|
s := service.LineApiUser{}
|
||||||
|
err := e.MakeContext(c).
|
||||||
|
MakeOrm().
|
||||||
|
Bind(&req).
|
||||||
|
MakeService(&s.Service).
|
||||||
|
Errors
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Error(err)
|
||||||
|
e.Error(500, err, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list := make([]models.LineApiUser, 0)
|
||||||
|
err = s.GetReverseApiOptions(&req, &list)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("获取失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.OK(list, "操作成功")
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package apis
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||||
@ -193,6 +194,7 @@ func (e LinePriceLimit) Delete(c *gin.Context) {
|
|||||||
e.OK(req.GetId(), "删除成功")
|
e.OK(req.GetId(), "删除成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// aicoin数据同步
|
||||||
func (e LinePriceLimit) UpRange(c *gin.Context) {
|
func (e LinePriceLimit) UpRange(c *gin.Context) {
|
||||||
s := service.LinePriceLimit{}
|
s := service.LinePriceLimit{}
|
||||||
err := e.MakeContext(c).
|
err := e.MakeContext(c).
|
||||||
|
|||||||
@ -14,22 +14,27 @@ import (
|
|||||||
"go-admin/common/actions"
|
"go-admin/common/actions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LineReverseOrderExt struct {
|
type LineReversePosition struct {
|
||||||
api.Api
|
api.Api
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPage 获取反单拓展信息列表
|
// GetPage 获取反单管理-仓位列表
|
||||||
// @Summary 获取反单拓展信息列表
|
// @Summary 获取反单管理-仓位列表
|
||||||
// @Description 获取反单拓展信息列表
|
// @Description 获取反单管理-仓位列表
|
||||||
// @Tags 反单拓展信息
|
// @Tags 反单管理-仓位
|
||||||
|
// @Param apiId query int64 false "api_id"
|
||||||
|
// @Param reverseApiId query int64 false "反单api_id"
|
||||||
|
// @Param side query string false "买卖方向 BUY SELL"
|
||||||
|
// @Param positionSide query string false "持仓方向 LONG SHORT"
|
||||||
|
// @Param symbol query string false "交易对"
|
||||||
// @Param pageSize query int false "页条数"
|
// @Param pageSize query int false "页条数"
|
||||||
// @Param pageIndex query int false "页码"
|
// @Param pageIndex query int false "页码"
|
||||||
// @Success 200 {object} response.Response{data=response.Page{list=[]models.LineReverseOrderExt}} "{"code": 200, "data": [...]}"
|
// @Success 200 {object} response.Response{data=response.Page{list=[]models.LineReversePosition}} "{"code": 200, "data": [...]}"
|
||||||
// @Router /api/v1/line-reverse-order-ext [get]
|
// @Router /api/v1/line-reverse-position [get]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (e LineReverseOrderExt) GetPage(c *gin.Context) {
|
func (e LineReversePosition) GetPage(c *gin.Context) {
|
||||||
req := dto.LineReverseOrderExtGetPageReq{}
|
req := dto.LineReversePositionGetPageReq{}
|
||||||
s := service.LineReverseOrderExt{}
|
s := service.LineReversePosition{}
|
||||||
err := e.MakeContext(c).
|
err := e.MakeContext(c).
|
||||||
MakeOrm().
|
MakeOrm().
|
||||||
Bind(&req).
|
Bind(&req).
|
||||||
@ -42,29 +47,29 @@ func (e LineReverseOrderExt) GetPage(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := actions.GetPermissionFromContext(c)
|
p := actions.GetPermissionFromContext(c)
|
||||||
list := make([]models.LineReverseOrderExt, 0)
|
list := make([]models.LineReversePosition, 0)
|
||||||
var count int64
|
var count int64
|
||||||
|
|
||||||
err = s.GetPage(&req, p, &list, &count)
|
err = s.GetPage(&req, p, &list, &count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Error(500, err, fmt.Sprintf("获取反单拓展信息失败,\r\n失败信息 %s", err.Error()))
|
e.Error(500, err, fmt.Sprintf("获取反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get 获取反单拓展信息
|
// Get 获取反单管理-仓位
|
||||||
// @Summary 获取反单拓展信息
|
// @Summary 获取反单管理-仓位
|
||||||
// @Description 获取反单拓展信息
|
// @Description 获取反单管理-仓位
|
||||||
// @Tags 反单拓展信息
|
// @Tags 反单管理-仓位
|
||||||
// @Param id path int false "id"
|
// @Param id path int false "id"
|
||||||
// @Success 200 {object} response.Response{data=models.LineReverseOrderExt} "{"code": 200, "data": [...]}"
|
// @Success 200 {object} response.Response{data=models.LineReversePosition} "{"code": 200, "data": [...]}"
|
||||||
// @Router /api/v1/line-reverse-order-ext/{id} [get]
|
// @Router /api/v1/line-reverse-position/{id} [get]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (e LineReverseOrderExt) Get(c *gin.Context) {
|
func (e LineReversePosition) Get(c *gin.Context) {
|
||||||
req := dto.LineReverseOrderExtGetReq{}
|
req := dto.LineReversePositionGetReq{}
|
||||||
s := service.LineReverseOrderExt{}
|
s := service.LineReversePosition{}
|
||||||
err := e.MakeContext(c).
|
err := e.MakeContext(c).
|
||||||
MakeOrm().
|
MakeOrm().
|
||||||
Bind(&req).
|
Bind(&req).
|
||||||
@ -75,31 +80,31 @@ func (e LineReverseOrderExt) Get(c *gin.Context) {
|
|||||||
e.Error(500, err, err.Error())
|
e.Error(500, err, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var object models.LineReverseOrderExt
|
var object models.LineReversePosition
|
||||||
|
|
||||||
p := actions.GetPermissionFromContext(c)
|
p := actions.GetPermissionFromContext(c)
|
||||||
err = s.Get(&req, p, &object)
|
err = s.Get(&req, p, &object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Error(500, err, fmt.Sprintf("获取反单拓展信息失败,\r\n失败信息 %s", err.Error()))
|
e.Error(500, err, fmt.Sprintf("获取反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e.OK( object, "查询成功")
|
e.OK( object, "查询成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert 创建反单拓展信息
|
// Insert 创建反单管理-仓位
|
||||||
// @Summary 创建反单拓展信息
|
// @Summary 创建反单管理-仓位
|
||||||
// @Description 创建反单拓展信息
|
// @Description 创建反单管理-仓位
|
||||||
// @Tags 反单拓展信息
|
// @Tags 反单管理-仓位
|
||||||
// @Accept application/json
|
// @Accept application/json
|
||||||
// @Product application/json
|
// @Product application/json
|
||||||
// @Param data body dto.LineReverseOrderExtInsertReq true "data"
|
// @Param data body dto.LineReversePositionInsertReq true "data"
|
||||||
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
||||||
// @Router /api/v1/line-reverse-order-ext [post]
|
// @Router /api/v1/line-reverse-position [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (e LineReverseOrderExt) Insert(c *gin.Context) {
|
func (e LineReversePosition) Insert(c *gin.Context) {
|
||||||
req := dto.LineReverseOrderExtInsertReq{}
|
req := dto.LineReversePositionInsertReq{}
|
||||||
s := service.LineReverseOrderExt{}
|
s := service.LineReversePosition{}
|
||||||
err := e.MakeContext(c).
|
err := e.MakeContext(c).
|
||||||
MakeOrm().
|
MakeOrm().
|
||||||
Bind(&req).
|
Bind(&req).
|
||||||
@ -115,27 +120,27 @@ func (e LineReverseOrderExt) Insert(c *gin.Context) {
|
|||||||
|
|
||||||
err = s.Insert(&req)
|
err = s.Insert(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Error(500, err, fmt.Sprintf("创建反单拓展信息失败,\r\n失败信息 %s", err.Error()))
|
e.Error(500, err, fmt.Sprintf("创建反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e.OK(req.GetId(), "创建成功")
|
e.OK(req.GetId(), "创建成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 修改反单拓展信息
|
// Update 修改反单管理-仓位
|
||||||
// @Summary 修改反单拓展信息
|
// @Summary 修改反单管理-仓位
|
||||||
// @Description 修改反单拓展信息
|
// @Description 修改反单管理-仓位
|
||||||
// @Tags 反单拓展信息
|
// @Tags 反单管理-仓位
|
||||||
// @Accept application/json
|
// @Accept application/json
|
||||||
// @Product application/json
|
// @Product application/json
|
||||||
// @Param id path int true "id"
|
// @Param id path int true "id"
|
||||||
// @Param data body dto.LineReverseOrderExtUpdateReq true "body"
|
// @Param data body dto.LineReversePositionUpdateReq true "body"
|
||||||
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
||||||
// @Router /api/v1/line-reverse-order-ext/{id} [put]
|
// @Router /api/v1/line-reverse-position/{id} [put]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (e LineReverseOrderExt) Update(c *gin.Context) {
|
func (e LineReversePosition) Update(c *gin.Context) {
|
||||||
req := dto.LineReverseOrderExtUpdateReq{}
|
req := dto.LineReversePositionUpdateReq{}
|
||||||
s := service.LineReverseOrderExt{}
|
s := service.LineReversePosition{}
|
||||||
err := e.MakeContext(c).
|
err := e.MakeContext(c).
|
||||||
MakeOrm().
|
MakeOrm().
|
||||||
Bind(&req).
|
Bind(&req).
|
||||||
@ -151,23 +156,23 @@ func (e LineReverseOrderExt) Update(c *gin.Context) {
|
|||||||
|
|
||||||
err = s.Update(&req, p)
|
err = s.Update(&req, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Error(500, err, fmt.Sprintf("修改反单拓展信息失败,\r\n失败信息 %s", err.Error()))
|
e.Error(500, err, fmt.Sprintf("修改反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.OK( req.GetId(), "修改成功")
|
e.OK( req.GetId(), "修改成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 删除反单拓展信息
|
// Delete 删除反单管理-仓位
|
||||||
// @Summary 删除反单拓展信息
|
// @Summary 删除反单管理-仓位
|
||||||
// @Description 删除反单拓展信息
|
// @Description 删除反单管理-仓位
|
||||||
// @Tags 反单拓展信息
|
// @Tags 反单管理-仓位
|
||||||
// @Param data body dto.LineReverseOrderExtDeleteReq true "body"
|
// @Param data body dto.LineReversePositionDeleteReq true "body"
|
||||||
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
||||||
// @Router /api/v1/line-reverse-order-ext [delete]
|
// @Router /api/v1/line-reverse-position [delete]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (e LineReverseOrderExt) Delete(c *gin.Context) {
|
func (e LineReversePosition) Delete(c *gin.Context) {
|
||||||
s := service.LineReverseOrderExt{}
|
s := service.LineReversePosition{}
|
||||||
req := dto.LineReverseOrderExtDeleteReq{}
|
req := dto.LineReversePositionDeleteReq{}
|
||||||
err := e.MakeContext(c).
|
err := e.MakeContext(c).
|
||||||
MakeOrm().
|
MakeOrm().
|
||||||
Bind(&req).
|
Bind(&req).
|
||||||
@ -184,7 +189,7 @@ func (e LineReverseOrderExt) Delete(c *gin.Context) {
|
|||||||
|
|
||||||
err = s.Remove(&req, p)
|
err = s.Remove(&req, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Error(500, err, fmt.Sprintf("删除反单拓展信息失败,\r\n失败信息 %s", err.Error()))
|
e.Error(500, err, fmt.Sprintf("删除反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.OK( req.GetId(), "删除成功")
|
e.OK( req.GetId(), "删除成功")
|
||||||
@ -84,7 +84,7 @@ func (e LineReverseSetting) Get(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e.OK( object, "查询成功")
|
e.OK(object, "查询成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert 创建反单下单配置
|
// Insert 创建反单下单配置
|
||||||
@ -154,38 +154,5 @@ func (e LineReverseSetting) Update(c *gin.Context) {
|
|||||||
e.Error(500, err, fmt.Sprintf("修改反单下单配置失败,\r\n失败信息 %s", err.Error()))
|
e.Error(500, err, fmt.Sprintf("修改反单下单配置失败,\r\n失败信息 %s", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.OK( req.GetId(), "修改成功")
|
e.OK(req.GetId(), "修改成功")
|
||||||
}
|
|
||||||
|
|
||||||
// Delete 删除反单下单配置
|
|
||||||
// @Summary 删除反单下单配置
|
|
||||||
// @Description 删除反单下单配置
|
|
||||||
// @Tags 反单下单配置
|
|
||||||
// @Param data body dto.LineReverseSettingDeleteReq true "body"
|
|
||||||
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
|
||||||
// @Router /api/v1/line-reverse-setting [delete]
|
|
||||||
// @Security Bearer
|
|
||||||
func (e LineReverseSetting) Delete(c *gin.Context) {
|
|
||||||
s := service.LineReverseSetting{}
|
|
||||||
req := dto.LineReverseSettingDeleteReq{}
|
|
||||||
err := e.MakeContext(c).
|
|
||||||
MakeOrm().
|
|
||||||
Bind(&req).
|
|
||||||
MakeService(&s.Service).
|
|
||||||
Errors
|
|
||||||
if err != nil {
|
|
||||||
e.Logger.Error(err)
|
|
||||||
e.Error(500, err, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// req.SetUpdateBy(user.GetUserId(c))
|
|
||||||
p := actions.GetPermissionFromContext(c)
|
|
||||||
|
|
||||||
err = s.Remove(&req, p)
|
|
||||||
if err != nil {
|
|
||||||
e.Error(500, err, fmt.Sprintf("删除反单下单配置失败,\r\n失败信息 %s", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.OK( req.GetId(), "删除成功")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,26 +5,34 @@ import (
|
|||||||
|
|
||||||
"go-admin/common/models"
|
"go-admin/common/models"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LineReverseOrder struct {
|
type LineReverseOrder struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
PId int `json:"pId" gorm:"type:bigint;comment:主单id"`
|
ApiId int `json:"apiId" gorm:"type:bigint;comment:api id"`
|
||||||
OrderSn string `json:"orderSn" gorm:"type:varchar(50);comment:订单号"`
|
Category int `json:"category" gorm:"type:tinyint;comment:分类 0-原始订单 1-反单"`
|
||||||
|
|
||||||
|
OrderSn string `json:"orderSn" gorm:"type:varchar(50);comment:订单号 0-主单 1-止盈 2-止损"`
|
||||||
OrderId string `json:"orderId" gorm:"type:varchar(50);comment:币安订单号"`
|
OrderId string `json:"orderId" gorm:"type:varchar(50);comment:币安订单号"`
|
||||||
FollowOrderSn string `json:"followOrderSn" gorm:"type:varchar(50);comment:跟随币安订单号"`
|
FollowOrderSn string `json:"followOrderSn" gorm:"type:varchar(50);comment:跟随币安订单号"`
|
||||||
Symbol string `json:"symbol" gorm:"type:varchar(20);comment:交易对"`
|
Symbol string `json:"symbol" gorm:"type:varchar(20);comment:交易对"`
|
||||||
OrderType int `json:"orderType" gorm:"type:tinyint;comment:订单类型 0-主单 1-止损单 2-加仓 3-减仓"`
|
Type string `json:"type" gorm:"type:varchar(20);comment:交易类型"`
|
||||||
|
OrderType int `json:"orderType" gorm:"type:tinyint;comment:订单类型 0-主单 1-止盈 2-止损 3-减仓单 4-平仓单"`
|
||||||
BuyPrice decimal.Decimal `json:"buyPrice" gorm:"type:decimal(18,8);comment:购买金额"`
|
BuyPrice decimal.Decimal `json:"buyPrice" gorm:"type:decimal(18,8);comment:购买金额"`
|
||||||
Price decimal.Decimal `json:"price" gorm:"type:decimal(18,8);comment:委托价格"`
|
Price decimal.Decimal `json:"price" gorm:"type:decimal(18,8);comment:委托价格"`
|
||||||
|
TotalNum decimal.Decimal `json:"totalNum" gorm:"type:decimal(18,8);comment:总成交数量"`
|
||||||
PriceU decimal.Decimal `json:"priceU" gorm:"type:decimal(18,8);comment:委托价格(U)"`
|
PriceU decimal.Decimal `json:"priceU" gorm:"type:decimal(18,8);comment:委托价格(U)"`
|
||||||
FinalPrice decimal.Decimal `json:"finalPrice" gorm:"type:decimal(18,8);comment:实际成交价"`
|
FinalPrice decimal.Decimal `json:"finalPrice" gorm:"type:decimal(18,8);comment:实际成交价"`
|
||||||
PositionSide string `json:"positionSide" gorm:"type:varchar(10);comment:持仓方向 LONG-多 SHORT-空"`
|
PositionSide string `json:"positionSide" gorm:"type:varchar(10);comment:持仓方向 LONG-多 SHORT-空"`
|
||||||
Side string `json:"side" gorm:"type:varchar(10);comment:买卖方向 SELL-卖 BUY-买"`
|
Side string `json:"side" gorm:"type:varchar(10);comment:买卖方向 SELL-卖 BUY-买"`
|
||||||
SignPrice decimal.Decimal `json:"signPrice" gorm:"type:decimal(18,8);comment:行情价"`
|
SignPrice decimal.Decimal `json:"signPrice" gorm:"type:decimal(18,8);comment:行情价"`
|
||||||
TriggerTime time.Time `json:"triggerTime" gorm:"type:datetime;comment:触发时间"`
|
TriggerTime *time.Time `json:"triggerTime" gorm:"type:datetime;comment:触发时间"`
|
||||||
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-待下单 2-已下单 3-已成交 4-已平仓 5-已止损"`
|
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-待下单 2-已下单 3-已成交 6-已取消 7-已过期 8-已失败"`
|
||||||
|
IsAddPosition int `json:"isAddPosition" gorm:"type:tinyint;comment:是否增加仓位 1-否 2-是"`
|
||||||
|
IsReduce int `json:"isReduce" gorm:"type:tinyint;comment:是否减少仓位 1-否 2-是"`
|
||||||
|
Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
|
||||||
models.ModelTime
|
models.ModelTime
|
||||||
models.ControlBy
|
models.ControlBy
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"go-admin/common/models"
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
type LineReverseOrderExt struct {
|
|
||||||
models.Model
|
|
||||||
|
|
||||||
ReverseOrderId int `json:"reverseOrderId" gorm:"type:bigint;comment:下反单id"`
|
|
||||||
IsAddPosition int `json:"isAddPosition" gorm:"type:tinyint;comment:加仓状态 1-已加仓 2-未加仓"`
|
|
||||||
IsReduce int `json:"isReduce" gorm:"type:tinyint;comment:减仓状态 1-已减仓 2-未减仓"`
|
|
||||||
models.ModelTime
|
|
||||||
models.ControlBy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (LineReverseOrderExt) TableName() string {
|
|
||||||
return "line_reverse_order_ext"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *LineReverseOrderExt) Generate() models.ActiveRecord {
|
|
||||||
o := *e
|
|
||||||
return &o
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *LineReverseOrderExt) GetId() interface{} {
|
|
||||||
return e.Id
|
|
||||||
}
|
|
||||||
39
app/admin/models/line_reverse_position.go
Normal file
39
app/admin/models/line_reverse_position.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/common/models"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LineReversePosition struct {
|
||||||
|
models.Model
|
||||||
|
|
||||||
|
ApiId int `json:"apiId" gorm:"type:bigint;comment:api_id"`
|
||||||
|
TotalAmount decimal.Decimal `json:"totalAmount" gorm:"type:decimal(18,8);comment:总仓位"`
|
||||||
|
Amount decimal.Decimal `json:"amount" gorm:"type:decimal(18,8);comment:仓位"`
|
||||||
|
ReverseApiId int `json:"reverseApiId" gorm:"type:bigint;comment:反单api_id"`
|
||||||
|
TotalReverseAmount decimal.Decimal `json:"totalReverseAmount" gorm:"type:decimal(18,8);comment:总反单仓位"`
|
||||||
|
ReverseAmount decimal.Decimal `json:"reverseAmount" gorm:"type:decimal(18,8);comment:反单仓位"`
|
||||||
|
Side string `json:"side" gorm:"type:varchar(10);comment:买卖方向 BUY SELL"`
|
||||||
|
PositionSide string `json:"positionSide" gorm:"type:varchar(10);comment:持仓方向 LONG SHORT"`
|
||||||
|
Symbol string `json:"symbol" gorm:"type:varchar(20);comment:交易对"`
|
||||||
|
Status int `json:"status" gorm:"type:tinyint;comment:仓位状态 1-已开仓 2-已平仓"`
|
||||||
|
ReverseStatus int `json:"reverseStatus" gorm:"type:tinyint;comment:反单仓位状态 1-已开仓 2-已平仓"`
|
||||||
|
|
||||||
|
models.ModelTime
|
||||||
|
models.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (LineReversePosition) TableName() string {
|
||||||
|
return "line_reverse_position"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *LineReversePosition) Generate() models.ActiveRecord {
|
||||||
|
o := *e
|
||||||
|
return &o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *LineReversePosition) GetId() interface{} {
|
||||||
|
return e.Id
|
||||||
|
}
|
||||||
@ -9,9 +9,10 @@ import (
|
|||||||
type LineReverseSetting struct {
|
type LineReverseSetting struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
ReverseOrderType string `json:"reverseOrderType" gorm:"type:varchar(10);comment:反单下单类型 LIMIT-限价 MARKET-市价"`
|
ReverseOrderType string `json:"reverseOrderType" redis:"reverseOrderType" gorm:"type:varchar(10);comment:反单下单类型 LIMIT-限价 MARKET-市价"`
|
||||||
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" gorm:"type:decimal(10,2);comment:溢价百分比"`
|
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" redis:"reversePremiumRatio" gorm:"type:decimal(10,2);comment:溢价百分比"`
|
||||||
StopLossRatio decimal.Decimal `json:"stopLossRatio" gorm:"type:decimal(10,2);comment:止损百分比"`
|
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" redis:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"`
|
||||||
|
StopLossRatio decimal.Decimal `json:"stopLossRatio" redis:"stopLossRatio" gorm:"type:decimal(10,2);comment:止损百分比"`
|
||||||
models.ModelTime
|
models.ModelTime
|
||||||
models.ControlBy
|
models.ControlBy
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,5 +29,7 @@ func registerLineApiUserRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMi
|
|||||||
r.POST("getMainUser", api.GetMainUser) //获取获取主账号的用户
|
r.POST("getMainUser", api.GetMainUser) //获取获取主账号的用户
|
||||||
|
|
||||||
r.GET("unbind-reverse", api.GetUnBindReverseApiUser) //获取未绑定下反单用户
|
r.GET("unbind-reverse", api.GetUnBindReverseApiUser) //获取未绑定下反单用户
|
||||||
|
|
||||||
|
r.GET("reverse-options", api.GetReverseApiOptions) //获取可用反单api用户
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,13 +10,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
routerCheckRole = append(routerCheckRole, registerLineReverseOrderExtRouter)
|
routerCheckRole = append(routerCheckRole, registerLineReversePositionRouter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerLineReverseOrderExtRouter
|
// registerLineReversePositionRouter
|
||||||
func registerLineReverseOrderExtRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
func registerLineReversePositionRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||||
api := apis.LineReverseOrderExt{}
|
api := apis.LineReversePosition{}
|
||||||
r := v1.Group("/line-reverse-order-ext").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
r := v1.Group("/line-reverse-position").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
||||||
{
|
{
|
||||||
r.GET("", actions.PermissionAction(), api.GetPage)
|
r.GET("", actions.PermissionAction(), api.GetPage)
|
||||||
r.GET("/:id", actions.PermissionAction(), api.Get)
|
r.GET("/:id", actions.PermissionAction(), api.Get)
|
||||||
@ -5,8 +5,8 @@ import (
|
|||||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||||
|
|
||||||
"go-admin/app/admin/apis"
|
"go-admin/app/admin/apis"
|
||||||
"go-admin/common/middleware"
|
|
||||||
"go-admin/common/actions"
|
"go-admin/common/actions"
|
||||||
|
"go-admin/common/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -22,6 +22,6 @@ func registerLineReverseSettingRouter(v1 *gin.RouterGroup, authMiddleware *jwt.G
|
|||||||
r.GET("/:id", actions.PermissionAction(), api.Get)
|
r.GET("/:id", actions.PermissionAction(), api.Get)
|
||||||
r.POST("", api.Insert)
|
r.POST("", api.Insert)
|
||||||
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
||||||
r.DELETE("", api.Delete)
|
// r.DELETE("", api.Delete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,3 +219,8 @@ type UnBindReverseResp struct {
|
|||||||
Disabled bool `json:"disabled"`
|
Disabled bool `json:"disabled"`
|
||||||
ApiName string `json:"apiName"`
|
ApiName string `json:"apiName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetReverseApiOptionsReq struct {
|
||||||
|
Id int `json:"apiId" form:"id"`
|
||||||
|
ApiId int `json:"apiId" form:"apiId"`
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"go-admin/app/admin/models"
|
"go-admin/app/admin/models"
|
||||||
"go-admin/common/dto"
|
"go-admin/common/dto"
|
||||||
common "go-admin/common/models"
|
common "go-admin/common/models"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LineReverseOrderGetPageReq struct {
|
type LineReverseOrderGetPageReq struct {
|
||||||
@ -42,7 +44,6 @@ type LineReverseOrderOrder struct {
|
|||||||
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_reverse_order"`
|
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_reverse_order"`
|
||||||
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_reverse_order"`
|
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_reverse_order"`
|
||||||
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_reverse_order"`
|
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_reverse_order"`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *LineReverseOrderGetPageReq) GetNeedSearch() interface{} {
|
func (m *LineReverseOrderGetPageReq) GetNeedSearch() interface{} {
|
||||||
@ -71,9 +72,8 @@ type LineReverseOrderInsertReq struct {
|
|||||||
|
|
||||||
func (s *LineReverseOrderInsertReq) Generate(model *models.LineReverseOrder) {
|
func (s *LineReverseOrderInsertReq) 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.PId = s.PId
|
|
||||||
model.OrderSn = s.OrderSn
|
model.OrderSn = s.OrderSn
|
||||||
model.OrderId = s.OrderId
|
model.OrderId = s.OrderId
|
||||||
model.FollowOrderSn = s.FollowOrderSn
|
model.FollowOrderSn = s.FollowOrderSn
|
||||||
@ -86,7 +86,6 @@ 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 // 添加这而,需要记录是被谁创建的
|
||||||
}
|
}
|
||||||
@ -117,9 +116,9 @@ type LineReverseOrderUpdateReq struct {
|
|||||||
|
|
||||||
func (s *LineReverseOrderUpdateReq) Generate(model *models.LineReverseOrder) {
|
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.PId = s.PId
|
|
||||||
model.OrderSn = s.OrderSn
|
model.OrderSn = s.OrderSn
|
||||||
model.OrderId = s.OrderId
|
model.OrderId = s.OrderId
|
||||||
model.FollowOrderSn = s.FollowOrderSn
|
model.FollowOrderSn = s.FollowOrderSn
|
||||||
@ -132,7 +131,6 @@ 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 // 添加这而,需要记录是被谁更新的
|
||||||
}
|
}
|
||||||
@ -145,6 +143,7 @@ func (s *LineReverseOrderUpdateReq) GetId() interface{} {
|
|||||||
type LineReverseOrderGetReq struct {
|
type LineReverseOrderGetReq struct {
|
||||||
Id int `uri:"id"`
|
Id int `uri:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LineReverseOrderGetReq) GetId() interface{} {
|
func (s *LineReverseOrderGetReq) GetId() interface{} {
|
||||||
return s.Id
|
return s.Id
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,91 +0,0 @@
|
|||||||
package dto
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"go-admin/app/admin/models"
|
|
||||||
"go-admin/common/dto"
|
|
||||||
common "go-admin/common/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LineReverseOrderExtGetPageReq struct {
|
|
||||||
dto.Pagination `search:"-"`
|
|
||||||
LineReverseOrderExtOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
type LineReverseOrderExtOrder struct {
|
|
||||||
Id string `form:"idOrder" search:"type:order;column:id;table:line_reverse_order_ext"`
|
|
||||||
ReverseOrderId string `form:"reverseOrderIdOrder" search:"type:order;column:reverse_order_id;table:line_reverse_order_ext"`
|
|
||||||
IsAddPosition string `form:"isAddPositionOrder" search:"type:order;column:is_add_position;table:line_reverse_order_ext"`
|
|
||||||
IsReduce string `form:"isReduceOrder" search:"type:order;column:is_reduce;table:line_reverse_order_ext"`
|
|
||||||
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:line_reverse_order_ext"`
|
|
||||||
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:line_reverse_order_ext"`
|
|
||||||
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_reverse_order_ext"`
|
|
||||||
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_reverse_order_ext"`
|
|
||||||
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_reverse_order_ext"`
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *LineReverseOrderExtGetPageReq) GetNeedSearch() interface{} {
|
|
||||||
return *m
|
|
||||||
}
|
|
||||||
|
|
||||||
type LineReverseOrderExtInsertReq struct {
|
|
||||||
Id int `json:"-" comment:"主键id"` // 主键id
|
|
||||||
ReverseOrderId int `json:"reverseOrderId" comment:"下反单id"`
|
|
||||||
IsAddPosition int `json:"isAddPosition" comment:"加仓状态 1-已加仓 2-未加仓"`
|
|
||||||
IsReduce int `json:"isReduce" comment:"减仓状态 1-已减仓 2-未减仓"`
|
|
||||||
common.ControlBy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LineReverseOrderExtInsertReq) Generate(model *models.LineReverseOrderExt) {
|
|
||||||
if s.Id == 0 {
|
|
||||||
model.Model = common.Model{ Id: s.Id }
|
|
||||||
}
|
|
||||||
model.ReverseOrderId = s.ReverseOrderId
|
|
||||||
model.IsAddPosition = s.IsAddPosition
|
|
||||||
model.IsReduce = s.IsReduce
|
|
||||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LineReverseOrderExtInsertReq) GetId() interface{} {
|
|
||||||
return s.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
type LineReverseOrderExtUpdateReq struct {
|
|
||||||
Id int `uri:"id" comment:"主键id"` // 主键id
|
|
||||||
ReverseOrderId int `json:"reverseOrderId" comment:"下反单id"`
|
|
||||||
IsAddPosition int `json:"isAddPosition" comment:"加仓状态 1-已加仓 2-未加仓"`
|
|
||||||
IsReduce int `json:"isReduce" comment:"减仓状态 1-已减仓 2-未减仓"`
|
|
||||||
common.ControlBy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LineReverseOrderExtUpdateReq) Generate(model *models.LineReverseOrderExt) {
|
|
||||||
if s.Id == 0 {
|
|
||||||
model.Model = common.Model{ Id: s.Id }
|
|
||||||
}
|
|
||||||
model.ReverseOrderId = s.ReverseOrderId
|
|
||||||
model.IsAddPosition = s.IsAddPosition
|
|
||||||
model.IsReduce = s.IsReduce
|
|
||||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LineReverseOrderExtUpdateReq) GetId() interface{} {
|
|
||||||
return s.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
// LineReverseOrderExtGetReq 功能获取请求参数
|
|
||||||
type LineReverseOrderExtGetReq struct {
|
|
||||||
Id int `uri:"id"`
|
|
||||||
}
|
|
||||||
func (s *LineReverseOrderExtGetReq) GetId() interface{} {
|
|
||||||
return s.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
// LineReverseOrderExtDeleteReq 功能删除请求参数
|
|
||||||
type LineReverseOrderExtDeleteReq struct {
|
|
||||||
Ids []int `json:"ids"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LineReverseOrderExtDeleteReq) GetId() interface{} {
|
|
||||||
return s.Ids
|
|
||||||
}
|
|
||||||
117
app/admin/service/dto/line_reverse_position.go
Normal file
117
app/admin/service/dto/line_reverse_position.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/common/dto"
|
||||||
|
common "go-admin/common/models"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LineReversePositionGetPageReq struct {
|
||||||
|
dto.Pagination `search:"-"`
|
||||||
|
ApiId int64 `form:"apiId" search:"type:exact;column:api_id;table:line_reverse_position" comment:"api_id"`
|
||||||
|
ReverseApiId int64 `form:"reverseApiId" search:"type:exact;column:reverse_api_id;table:line_reverse_position" comment:"反单api_id"`
|
||||||
|
Side string `form:"side" search:"type:exact;column:side;table:line_reverse_position" comment:"买卖方向 BUY SELL"`
|
||||||
|
PositionSide string `form:"positionSide" search:"type:exact;column:position_side;table:line_reverse_position" comment:"持仓方向 LONG SHORT"`
|
||||||
|
Symbol string `form:"symbol" search:"type:contains;column:symbol;table:line_reverse_position" comment:"交易对"`
|
||||||
|
LineReversePositionOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineReversePositionOrder struct {
|
||||||
|
Id string `form:"idOrder" search:"type:order;column:id;table:line_reverse_position"`
|
||||||
|
ApiId string `form:"apiIdOrder" search:"type:order;column:api_id;table:line_reverse_position"`
|
||||||
|
ReverseApiId string `form:"reverseApiIdOrder" search:"type:order;column:reverse_api_id;table:line_reverse_position"`
|
||||||
|
ReverseAmount string `form:"reverseAmountOrder" search:"type:order;column:reverse_amount;table:line_reverse_position"`
|
||||||
|
Side string `form:"sideOrder" search:"type:order;column:side;table:line_reverse_position"`
|
||||||
|
PositionSide string `form:"positionSideOrder" search:"type:order;column:position_side;table:line_reverse_position"`
|
||||||
|
Symbol string `form:"symbolOrder" search:"type:order;column:symbol;table:line_reverse_position"`
|
||||||
|
Status string `form:"statusOrder" search:"type:order;column:status;table:line_reverse_position"`
|
||||||
|
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:line_reverse_position"`
|
||||||
|
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:line_reverse_position"`
|
||||||
|
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_reverse_position"`
|
||||||
|
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_reverse_position"`
|
||||||
|
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_reverse_position"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LineReversePositionGetPageReq) GetNeedSearch() interface{} {
|
||||||
|
return *m
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineReversePositionInsertReq struct {
|
||||||
|
Id int `json:"-" comment:"主键"` // 主键
|
||||||
|
ApiId int `json:"apiId" comment:"api_id"`
|
||||||
|
ReverseApiId int `json:"reverseApiId" comment:"反单api_id"`
|
||||||
|
ReverseAmount decimal.Decimal `json:"reverseAmount" comment:"反单仓位"`
|
||||||
|
Side string `json:"side" comment:"买卖方向 BUY SELL"`
|
||||||
|
PositionSide string `json:"positionSide" comment:"持仓方向 LONG SHORT"`
|
||||||
|
Symbol string `json:"symbol" comment:"交易对"`
|
||||||
|
Status int `json:"status" comment:"仓位状态 1-已开仓 2-已平仓"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LineReversePositionInsertReq) Generate(model *models.LineReversePosition) {
|
||||||
|
if s.Id == 0 {
|
||||||
|
model.Model = common.Model{Id: s.Id}
|
||||||
|
}
|
||||||
|
model.ApiId = s.ApiId
|
||||||
|
model.ReverseApiId = s.ReverseApiId
|
||||||
|
model.ReverseAmount = s.ReverseAmount
|
||||||
|
model.Side = s.Side
|
||||||
|
model.PositionSide = s.PositionSide
|
||||||
|
model.Symbol = s.Symbol
|
||||||
|
model.Status = s.Status
|
||||||
|
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LineReversePositionInsertReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineReversePositionUpdateReq struct {
|
||||||
|
Id int `uri:"id" comment:"主键"` // 主键
|
||||||
|
ApiId int `json:"apiId" comment:"api_id"`
|
||||||
|
ReverseApiId int `json:"reverseApiId" comment:"反单api_id"`
|
||||||
|
ReverseAmount decimal.Decimal `json:"reverseAmount" comment:"反单仓位"`
|
||||||
|
Side string `json:"side" comment:"买卖方向 BUY SELL"`
|
||||||
|
PositionSide string `json:"positionSide" comment:"持仓方向 LONG SHORT"`
|
||||||
|
Symbol string `json:"symbol" comment:"交易对"`
|
||||||
|
Status int `json:"status" comment:"仓位状态 1-已开仓 2-已平仓"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LineReversePositionUpdateReq) Generate(model *models.LineReversePosition) {
|
||||||
|
if s.Id == 0 {
|
||||||
|
model.Model = common.Model{Id: s.Id}
|
||||||
|
}
|
||||||
|
model.ApiId = s.ApiId
|
||||||
|
model.ReverseApiId = s.ReverseApiId
|
||||||
|
model.ReverseAmount = s.ReverseAmount
|
||||||
|
model.Side = s.Side
|
||||||
|
model.PositionSide = s.PositionSide
|
||||||
|
model.Symbol = s.Symbol
|
||||||
|
model.Status = s.Status
|
||||||
|
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LineReversePositionUpdateReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineReversePositionGetReq 功能获取请求参数
|
||||||
|
type LineReversePositionGetReq struct {
|
||||||
|
Id int `uri:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LineReversePositionGetReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineReversePositionDeleteReq 功能删除请求参数
|
||||||
|
type LineReversePositionDeleteReq struct {
|
||||||
|
Ids []int `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LineReversePositionDeleteReq) GetId() interface{} {
|
||||||
|
return s.Ids
|
||||||
|
}
|
||||||
@ -32,6 +32,8 @@ type LineReverseSettingInsertReq struct {
|
|||||||
Id int `json:"-" comment:"主键id"` // 主键id
|
Id int `json:"-" comment:"主键id"` // 主键id
|
||||||
ReverseOrderType string `json:"reverseOrderType" comment:"反单下单类型 LIMIT-限价 MARKET-市价"`
|
ReverseOrderType string `json:"reverseOrderType" comment:"反单下单类型 LIMIT-限价 MARKET-市价"`
|
||||||
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" comment:"溢价百分比"`
|
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" comment:"溢价百分比"`
|
||||||
|
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"`
|
||||||
|
StopLossRatio decimal.Decimal `json:"stopLossRatio" comment:"止损百分比"`
|
||||||
common.ControlBy
|
common.ControlBy
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +43,8 @@ func (s *LineReverseSettingInsertReq) Generate(model *models.LineReverseSetting)
|
|||||||
}
|
}
|
||||||
model.ReverseOrderType = s.ReverseOrderType
|
model.ReverseOrderType = s.ReverseOrderType
|
||||||
model.ReversePremiumRatio = s.ReversePremiumRatio
|
model.ReversePremiumRatio = s.ReversePremiumRatio
|
||||||
|
model.StopLossRatio = s.StopLossRatio
|
||||||
|
model.TakeProfitRatio = s.TakeProfitRatio
|
||||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +56,7 @@ type LineReverseSettingUpdateReq struct {
|
|||||||
Id int `uri:"id" comment:"主键id"` // 主键id
|
Id int `uri:"id" comment:"主键id"` // 主键id
|
||||||
ReverseOrderType string `json:"reverseOrderType" comment:"反单下单类型 LIMIT-限价 MARKET-市价"`
|
ReverseOrderType string `json:"reverseOrderType" comment:"反单下单类型 LIMIT-限价 MARKET-市价"`
|
||||||
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" comment:"溢价百分比"`
|
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" comment:"溢价百分比"`
|
||||||
|
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"`
|
||||||
StopLossRatio decimal.Decimal `json:"stopLossRatio" comment:"止损百分比"`
|
StopLossRatio decimal.Decimal `json:"stopLossRatio" comment:"止损百分比"`
|
||||||
common.ControlBy
|
common.ControlBy
|
||||||
}
|
}
|
||||||
@ -63,6 +68,7 @@ func (s *LineReverseSettingUpdateReq) Generate(model *models.LineReverseSetting)
|
|||||||
model.ReverseOrderType = s.ReverseOrderType
|
model.ReverseOrderType = s.ReverseOrderType
|
||||||
model.ReversePremiumRatio = s.ReversePremiumRatio
|
model.ReversePremiumRatio = s.ReversePremiumRatio
|
||||||
model.StopLossRatio = s.StopLossRatio
|
model.StopLossRatio = s.StopLossRatio
|
||||||
|
model.TakeProfitRatio = s.TakeProfitRatio
|
||||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"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"
|
||||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"go-admin/app/admin/models"
|
"go-admin/app/admin/models"
|
||||||
@ -25,6 +26,23 @@ type LineApiUser struct {
|
|||||||
service.Service
|
service.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取可以绑定的api列表
|
||||||
|
func (e LineApiUser) GetReverseApiOptions(req *dto.GetReverseApiOptionsReq, user *[]models.LineApiUser) error {
|
||||||
|
query := e.Orm.Model(models.LineApiUser{}).
|
||||||
|
Where("subordinate ='0' and id != ?", req.Id)
|
||||||
|
|
||||||
|
if req.ApiId > 0 {
|
||||||
|
query = query.Or(" id =?", req.ApiId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := query.Find(user).Error; err != nil {
|
||||||
|
e.Log.Errorf("LineApiUserService GetReverseApiOptions error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetPage 获取LineApiUser列表
|
// GetPage 获取LineApiUser列表
|
||||||
func (e *LineApiUser) GetPage(c *dto.LineApiUserGetPageReq, p *actions.DataPermission, list *[]models.LineApiUser, count *int64) error {
|
func (e *LineApiUser) GetPage(c *dto.LineApiUserGetPageReq, p *actions.DataPermission, list *[]models.LineApiUser, count *int64) error {
|
||||||
var err error
|
var err error
|
||||||
@ -107,7 +125,7 @@ func (e *LineApiUser) Insert(c *dto.LineApiUserInsertReq) error {
|
|||||||
return errors.New("反向api已被使用")
|
return errors.New("反向api已被使用")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err2 := tx.Model(models.LineApiUser{}).Where("id =?", c.ReverseApiId).Update("subordinate", 2).Error; err2 != nil {
|
if err2 := tx.Model(models.LineApiUser{}).Where("id =?", c.ReverseApiId).Update("subordinate", '2').Error; err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
data.Subordinate = "1"
|
data.Subordinate = "1"
|
||||||
@ -133,6 +151,9 @@ func (e *LineApiUser) Insert(c *dto.LineApiUserInsertReq) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err2 := e.CacheRelation(); err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +191,33 @@ func (e *LineApiUser) saveCache(data models.LineApiUser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 缓存关系
|
||||||
|
// cacheAll 是否缓存所有关系
|
||||||
|
func (e *LineApiUser) CacheRelation() error {
|
||||||
|
var datas *[]models.LineApiUser
|
||||||
|
cacheStrs := make([]string, 0)
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&models.LineApiUser{}).Where("subordinate ='1' and reverse_api_id >0 and open_status =1 and reverse_status =1").Find(&datas).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, data := range *datas {
|
||||||
|
cacheStrs = append(cacheStrs, fmt.Sprintf("%d:%d", data.Id, data.ReverseApiId))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cacheStrs) > 0 {
|
||||||
|
if err := helper.DefaultRedis.SetListCache(rediskey.ApiReverseRelation, 0, cacheStrs...); err != nil {
|
||||||
|
e.Log.Errorf("设置缓存失败 err:%v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := helper.DefaultRedis.DeleteString(rediskey.ApiReverseRelation); err != nil {
|
||||||
|
e.Log.Errorf("删除缓存失败 err:%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
打开用户websocket订阅
|
打开用户websocket订阅
|
||||||
*/
|
*/
|
||||||
@ -292,6 +340,10 @@ func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermiss
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err2 := e.CacheRelation(); err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,10 +390,14 @@ func (e *LineApiUser) Remove(d *dto.LineApiUserDeleteReq, p *actions.DataPermiss
|
|||||||
|
|
||||||
_, err := helper.DefaultRedis.BatchDeleteKeys(delKeys)
|
_, err := helper.DefaultRedis.BatchDeleteKeys(delKeys)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil && !errors.Is(err, redis.Nil) {
|
||||||
e.Log.Error("批量删除api_user key失败", err)
|
e.Log.Error("批量删除api_user key失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err2 := e.CacheRelation(); err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,14 +12,14 @@ import (
|
|||||||
cDto "go-admin/common/dto"
|
cDto "go-admin/common/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LineReverseOrderExt struct {
|
type LineReversePosition struct {
|
||||||
service.Service
|
service.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPage 获取LineReverseOrderExt列表
|
// GetPage 获取LineReversePosition列表
|
||||||
func (e *LineReverseOrderExt) GetPage(c *dto.LineReverseOrderExtGetPageReq, p *actions.DataPermission, list *[]models.LineReverseOrderExt, count *int64) error {
|
func (e *LineReversePosition) GetPage(c *dto.LineReversePositionGetPageReq, p *actions.DataPermission, list *[]models.LineReversePosition, count *int64) error {
|
||||||
var err error
|
var err error
|
||||||
var data models.LineReverseOrderExt
|
var data models.LineReversePosition
|
||||||
|
|
||||||
err = e.Orm.Model(&data).
|
err = e.Orm.Model(&data).
|
||||||
Scopes(
|
Scopes(
|
||||||
@ -30,15 +30,15 @@ func (e *LineReverseOrderExt) GetPage(c *dto.LineReverseOrderExtGetPageReq, p *a
|
|||||||
Find(list).Limit(-1).Offset(-1).
|
Find(list).Limit(-1).Offset(-1).
|
||||||
Count(count).Error
|
Count(count).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("LineReverseOrderExtService GetPage error:%s \r\n", err)
|
e.Log.Errorf("LineReversePositionService GetPage error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get 获取LineReverseOrderExt对象
|
// Get 获取LineReversePosition对象
|
||||||
func (e *LineReverseOrderExt) Get(d *dto.LineReverseOrderExtGetReq, p *actions.DataPermission, model *models.LineReverseOrderExt) error {
|
func (e *LineReversePosition) Get(d *dto.LineReversePositionGetReq, p *actions.DataPermission, model *models.LineReversePosition) error {
|
||||||
var data models.LineReverseOrderExt
|
var data models.LineReversePosition
|
||||||
|
|
||||||
err := e.Orm.Model(&data).
|
err := e.Orm.Model(&data).
|
||||||
Scopes(
|
Scopes(
|
||||||
@ -47,7 +47,7 @@ func (e *LineReverseOrderExt) Get(d *dto.LineReverseOrderExtGetReq, p *actions.D
|
|||||||
First(model, d.GetId()).Error
|
First(model, d.GetId()).Error
|
||||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
err = errors.New("查看对象不存在或无权查看")
|
err = errors.New("查看对象不存在或无权查看")
|
||||||
e.Log.Errorf("Service GetLineReverseOrderExt error:%s \r\n", err)
|
e.Log.Errorf("Service GetLineReversePosition error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -57,23 +57,23 @@ func (e *LineReverseOrderExt) Get(d *dto.LineReverseOrderExtGetReq, p *actions.D
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert 创建LineReverseOrderExt对象
|
// Insert 创建LineReversePosition对象
|
||||||
func (e *LineReverseOrderExt) Insert(c *dto.LineReverseOrderExtInsertReq) error {
|
func (e *LineReversePosition) Insert(c *dto.LineReversePositionInsertReq) error {
|
||||||
var err error
|
var err error
|
||||||
var data models.LineReverseOrderExt
|
var data models.LineReversePosition
|
||||||
c.Generate(&data)
|
c.Generate(&data)
|
||||||
err = e.Orm.Create(&data).Error
|
err = e.Orm.Create(&data).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("LineReverseOrderExtService Insert error:%s \r\n", err)
|
e.Log.Errorf("LineReversePositionService Insert error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 修改LineReverseOrderExt对象
|
// Update 修改LineReversePosition对象
|
||||||
func (e *LineReverseOrderExt) Update(c *dto.LineReverseOrderExtUpdateReq, p *actions.DataPermission) error {
|
func (e *LineReversePosition) Update(c *dto.LineReversePositionUpdateReq, p *actions.DataPermission) error {
|
||||||
var err error
|
var err error
|
||||||
var data = models.LineReverseOrderExt{}
|
var data = models.LineReversePosition{}
|
||||||
e.Orm.Scopes(
|
e.Orm.Scopes(
|
||||||
actions.Permission(data.TableName(), p),
|
actions.Permission(data.TableName(), p),
|
||||||
).First(&data, c.GetId())
|
).First(&data, c.GetId())
|
||||||
@ -81,7 +81,7 @@ func (e *LineReverseOrderExt) Update(c *dto.LineReverseOrderExtUpdateReq, p *act
|
|||||||
|
|
||||||
db := e.Orm.Save(&data)
|
db := e.Orm.Save(&data)
|
||||||
if err = db.Error; err != nil {
|
if err = db.Error; err != nil {
|
||||||
e.Log.Errorf("LineReverseOrderExtService Save error:%s \r\n", err)
|
e.Log.Errorf("LineReversePositionService Save error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if db.RowsAffected == 0 {
|
if db.RowsAffected == 0 {
|
||||||
@ -90,16 +90,16 @@ func (e *LineReverseOrderExt) Update(c *dto.LineReverseOrderExtUpdateReq, p *act
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove 删除LineReverseOrderExt
|
// Remove 删除LineReversePosition
|
||||||
func (e *LineReverseOrderExt) Remove(d *dto.LineReverseOrderExtDeleteReq, p *actions.DataPermission) error {
|
func (e *LineReversePosition) Remove(d *dto.LineReversePositionDeleteReq, p *actions.DataPermission) error {
|
||||||
var data models.LineReverseOrderExt
|
var data models.LineReversePosition
|
||||||
|
|
||||||
db := e.Orm.Model(&data).
|
db := e.Orm.Model(&data).
|
||||||
Scopes(
|
Scopes(
|
||||||
actions.Permission(data.TableName(), p),
|
actions.Permission(data.TableName(), p),
|
||||||
).Delete(&data, d.GetId())
|
).Delete(&data, d.GetId())
|
||||||
if err := db.Error; err != nil {
|
if err := db.Error; err != nil {
|
||||||
e.Log.Errorf("Service RemoveLineReverseOrderExt error:%s \r\n", err)
|
e.Log.Errorf("Service RemoveLineReversePosition error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if db.RowsAffected == 0 {
|
if db.RowsAffected == 0 {
|
||||||
@ -9,7 +9,9 @@ import (
|
|||||||
"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"
|
||||||
|
"go-admin/common/const/rediskey"
|
||||||
cDto "go-admin/common/dto"
|
cDto "go-admin/common/dto"
|
||||||
|
"go-admin/common/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LineReverseSetting struct {
|
type LineReverseSetting struct {
|
||||||
@ -67,6 +69,26 @@ func (e *LineReverseSetting) Insert(c *dto.LineReverseSettingInsertReq) error {
|
|||||||
e.Log.Errorf("LineReverseSettingService Insert error:%s \r\n", err)
|
e.Log.Errorf("LineReverseSettingService Insert error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = e.SaveCache(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存数据
|
||||||
|
func (e *LineReverseSetting) SaveCache(data models.LineReverseSetting) error {
|
||||||
|
if data.Id == 0 {
|
||||||
|
if err := e.Orm.First(&data).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := helper.DefaultRedis.SetHashWithTags(rediskey.ReverseSetting, &data); err != nil {
|
||||||
|
e.Log.Errorf("redis error:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +109,11 @@ func (e *LineReverseSetting) Update(c *dto.LineReverseSettingUpdateReq, p *actio
|
|||||||
if db.RowsAffected == 0 {
|
if db.RowsAffected == 0 {
|
||||||
return errors.New("无权更新该数据")
|
return errors.New("无权更新该数据")
|
||||||
}
|
}
|
||||||
|
if err = helper.DefaultRedis.SetHashWithTags(rediskey.ReverseSetting, data); err != nil {
|
||||||
|
e.Log.Errorf("redis error:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,11 @@ func run() error {
|
|||||||
clearLogJob(db, ctx)
|
clearLogJob(db, ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//自动重启websocket
|
||||||
|
utility.SafeGo(func() {
|
||||||
|
reconnect(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
|
// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
|
||||||
quit := make(chan os.Signal, 1)
|
quit := make(chan os.Signal, 1)
|
||||||
signal.Notify(quit, os.Interrupt)
|
signal.Notify(quit, os.Interrupt)
|
||||||
@ -133,3 +138,33 @@ func clearLogJob(db *gorm.DB, ctx context.Context) {
|
|||||||
fileservice.ClearLogs(db)
|
fileservice.ClearLogs(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定时重连websocket
|
||||||
|
func reconnect(ctx context.Context) error {
|
||||||
|
ticker := time.NewTicker(time.Hour * 1)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
case <-ticker.C:
|
||||||
|
serverinit.RestartConnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartDeadCheck 假死检测
|
||||||
|
func StartDeadCheck(ctx context.Context) {
|
||||||
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
serverinit.DeadCheck()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
23
common/const/binancecode/event.go
Normal file
23
common/const/binancecode/event.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package binancecode
|
||||||
|
|
||||||
|
// NEW
|
||||||
|
// CANCELED 已撤
|
||||||
|
// CALCULATED 订单 ADL 或爆仓
|
||||||
|
// EXPIRED 订单失效
|
||||||
|
// TRADE 交易
|
||||||
|
// AMENDMENT 订单修改
|
||||||
|
|
||||||
|
const (
|
||||||
|
//新开单
|
||||||
|
EVENT_NEW = "NEW"
|
||||||
|
//已撤单
|
||||||
|
EVENT_CANCELED = "CANCELED"
|
||||||
|
//订单 ADL 或爆仓
|
||||||
|
EVENT_CALCULATED = "CALCULATED"
|
||||||
|
//订单失效
|
||||||
|
EVENT_EXPIRED = "EXPIRED"
|
||||||
|
//交易中
|
||||||
|
EVENT_TRADE = "TRADE"
|
||||||
|
//订单修改
|
||||||
|
EVENT_AMENDMENT = "AMENDMENT"
|
||||||
|
)
|
||||||
20
common/const/binancecode/order_type.go
Normal file
20
common/const/binancecode/order_type.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package binancecode
|
||||||
|
|
||||||
|
const (
|
||||||
|
//限价单
|
||||||
|
ORDER_TYPE_LIMIT = "LIMIT"
|
||||||
|
//市价单
|
||||||
|
ORDER_TYPE_MARKET = "MARKET"
|
||||||
|
//止损限价单
|
||||||
|
ORDER_TYPE_STOP = "STOP"
|
||||||
|
//止损市价单
|
||||||
|
ORDER_TYPE_STOP_MARKET = "STOP_MARKET"
|
||||||
|
//止盈限价单
|
||||||
|
ORDER_TYPE_TAKE_PROFIT = "TAKE_PROFIT"
|
||||||
|
//止盈市价单
|
||||||
|
ORDER_TYPE_TAKE_PROFIT_MARKET = "TAKE_PROFIT_MARKET"
|
||||||
|
//跟踪止损单
|
||||||
|
ORDER_TYPE_TRAILING_STOP_MARKET = "TRAILING_STOP_MARKET"
|
||||||
|
//爆仓
|
||||||
|
ORDER_TYPE_LIQUIDATION = "LIQUIDATION"
|
||||||
|
)
|
||||||
6
common/const/binancecode/side.go
Normal file
6
common/const/binancecode/side.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package binancecode
|
||||||
|
|
||||||
|
const (
|
||||||
|
SideBuy = "BUY"
|
||||||
|
SideSell = "SELL"
|
||||||
|
)
|
||||||
6
common/const/rediskey/api_user.go
Normal file
6
common/const/rediskey/api_user.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package rediskey
|
||||||
|
|
||||||
|
const (
|
||||||
|
//主单api和反单关系 List {apiId}:{reverseApiId}
|
||||||
|
ApiReverseRelation = "api_reverse_relation"
|
||||||
|
)
|
||||||
11
common/const/rediskey/reverse_position.go
Normal file
11
common/const/rediskey/reverse_position.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package rediskey
|
||||||
|
|
||||||
|
const (
|
||||||
|
//反单仓位管理 {apiKey} hash key
|
||||||
|
ReversePositionKey = "reverse_position:%s"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
//反单配置 hash {order_type} {take_profit_ratio} {stop_loss_ratio} {reverse_premium_ratio}
|
||||||
|
ReverseSetting = "reverse_setting"
|
||||||
|
)
|
||||||
@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/bytedance/sonic"
|
"github.com/bytedance/sonic"
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RedisHelper 结构体封装了 Redis 客户端及上下文
|
// RedisHelper 结构体封装了 Redis 客户端及上下文
|
||||||
@ -460,44 +461,85 @@ func (r *RedisHelper) SetNX(key string, value interface{}, expiration time.Durat
|
|||||||
return result == "OK", nil
|
return result == "OK", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFieldsFromStruct(obj interface{}) map[string]interface{} {
|
// SetHashWithTags 改进版:支持 struct 或 map 输入
|
||||||
|
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
||||||
|
var fields map[string]interface{}
|
||||||
|
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 {
|
||||||
|
return fmt.Errorf("从结构体获取字段失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := r.client.HSet(r.ctx, key, fields)
|
||||||
|
return cmd.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFieldsFromStruct 改进版:处理指针并进行类型检查,返回 error
|
||||||
|
func getFieldsFromStruct(obj interface{}) (map[string]interface{}, error) {
|
||||||
fields := make(map[string]interface{})
|
fields := make(map[string]interface{})
|
||||||
val := reflect.ValueOf(obj)
|
val := reflect.ValueOf(obj)
|
||||||
typ := reflect.TypeOf(obj)
|
|
||||||
|
// 如果 obj 是指针,则解引用
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保我们正在处理的是一个结构体
|
||||||
|
if val.Kind() != reflect.Struct {
|
||||||
|
return nil, fmt.Errorf("期望一个结构体或结构体指针,但得到的是 %s", val.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := val.Type() // 在解引用后获取类型
|
||||||
|
|
||||||
for i := 0; i < val.NumField(); i++ {
|
for i := 0; i < val.NumField(); i++ {
|
||||||
field := typ.Field(i)
|
field := typ.Field(i)
|
||||||
tag := field.Tag.Get("redis")
|
tag := field.Tag.Get("redis") // 获取 redis tag
|
||||||
if tag != "" {
|
if tag != "" {
|
||||||
fieldVal := val.Field(i)
|
fieldVal := val.Field(i)
|
||||||
if fieldVal.Kind() == reflect.Slice || fieldVal.Kind() == reflect.Map {
|
// 检查字段是否可导出,不可导出的字段无法直接通过 Interface() 访问
|
||||||
|
if !fieldVal.CanInterface() {
|
||||||
|
continue // 跳过不可导出字段
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fieldVal.Kind() {
|
||||||
|
case reflect.Slice, reflect.Map:
|
||||||
// 处理切片或映射类型
|
// 处理切片或映射类型
|
||||||
// 对于切片,使用索引作为字段名
|
|
||||||
if fieldVal.Kind() == reflect.Slice {
|
if fieldVal.Kind() == reflect.Slice {
|
||||||
for j := 0; j < fieldVal.Len(); j++ {
|
for j := 0; j < fieldVal.Len(); j++ {
|
||||||
elem := fieldVal.Index(j).Interface()
|
elem := fieldVal.Index(j).Interface()
|
||||||
fields[fmt.Sprintf("%s_%d", tag, j)] = elem
|
fields[fmt.Sprintf("%s_%d", tag, j)] = elem
|
||||||
}
|
}
|
||||||
} else if fieldVal.Kind() == reflect.Map {
|
} else if fieldVal.Kind() == reflect.Map {
|
||||||
// 对于映射,使用键作为字段名
|
|
||||||
for _, key := range fieldVal.MapKeys() {
|
for _, key := range fieldVal.MapKeys() {
|
||||||
elem := fieldVal.MapIndex(key).Interface()
|
elem := fieldVal.MapIndex(key).Interface()
|
||||||
fields[fmt.Sprintf("%s_%v", tag, key.Interface())] = elem
|
fields[fmt.Sprintf("%s_%v", tag, key.Interface())] = elem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
if fieldVal.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||||
|
if decVal, ok := fieldVal.Interface().(decimal.Decimal); ok {
|
||||||
|
// 将 decimal.Decimal 直接转换为字符串来保留精度
|
||||||
|
fields[tag] = decVal.String()
|
||||||
} else {
|
} else {
|
||||||
|
// 理论上不应该发生,但作为回退
|
||||||
|
fields[tag] = fieldVal.Interface()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fields[tag] = fieldVal.Interface()
|
||||||
|
}
|
||||||
|
default:
|
||||||
fields[tag] = fieldVal.Interface()
|
fields[tag] = fieldVal.Interface()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return fields, nil
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
|
||||||
fields := getFieldsFromStruct(obj)
|
|
||||||
_, err := r.client.HSet(r.ctx, key, fields).Result()
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HSetField 设置哈希中的一个字段
|
// HSetField 设置哈希中的一个字段
|
||||||
@ -543,6 +585,82 @@ func (r *RedisHelper) HGetAllFields(key string) (map[string]string, error) {
|
|||||||
return fields, nil
|
return fields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HGetAsObject 获取哈希中所有字段的值并反序列化为对象
|
||||||
|
func (r *RedisHelper) HGetAsObject(key string, out interface{}) error {
|
||||||
|
data, err := r.HGetAllFields(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(out)
|
||||||
|
if val.Kind() != reflect.Ptr || val.IsNil() {
|
||||||
|
return fmt.Errorf("output must be a non-nil pointer to a struct")
|
||||||
|
}
|
||||||
|
val = val.Elem()
|
||||||
|
typ := val.Type()
|
||||||
|
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
field := typ.Field(i)
|
||||||
|
tag := field.Tag.Get("redis")
|
||||||
|
if tag == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
strVal, ok := data[tag]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldVal := val.Field(i)
|
||||||
|
if !fieldVal.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fieldVal.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
fieldVal.SetString(strVal)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil {
|
||||||
|
fieldVal.SetInt(intVal)
|
||||||
|
}
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
if uintVal, err := strconv.ParseUint(strVal, 10, 64); err == nil {
|
||||||
|
fieldVal.SetUint(uintVal)
|
||||||
|
}
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
if floatVal, err := strconv.ParseFloat(strVal, 64); err == nil {
|
||||||
|
fieldVal.SetFloat(floatVal)
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
if boolVal, err := strconv.ParseBool(strVal); err == nil {
|
||||||
|
fieldVal.SetBool(boolVal)
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
// 针对 time.Time 特别处理
|
||||||
|
if fieldVal.Type() == reflect.TypeOf(time.Time{}) {
|
||||||
|
if t, err := time.Parse(time.RFC3339, strVal); err == nil {
|
||||||
|
fieldVal.Set(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
} else if fieldVal.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||||
|
if t, err := decimal.NewFromString(strVal); err == nil {
|
||||||
|
fieldVal.Set(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 其他 struct 尝试用 json 反序列化
|
||||||
|
ptr := reflect.New(fieldVal.Type()).Interface()
|
||||||
|
if err := sonic.Unmarshal([]byte(strVal), ptr); err == nil {
|
||||||
|
fieldVal.Set(reflect.ValueOf(ptr).Elem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice, reflect.Map:
|
||||||
|
ptr := reflect.New(fieldVal.Type()).Interface()
|
||||||
|
if err := sonic.Unmarshal([]byte(strVal), ptr); err == nil {
|
||||||
|
fieldVal.Set(reflect.ValueOf(ptr).Elem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// HDelField 删除哈希中的某个字段
|
// HDelField 删除哈希中的某个字段
|
||||||
func (r *RedisHelper) HDelField(key, field string) error {
|
func (r *RedisHelper) HDelField(key, field string) error {
|
||||||
_, err := r.client.HDel(r.ctx, key, field).Result()
|
_, err := r.client.HDel(r.ctx, key, field).Result()
|
||||||
|
|||||||
@ -30,6 +30,24 @@ func BusinessInit(db *gorm.DB) {
|
|||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lineApiService := service.LineApiUser{}
|
||||||
|
lineApiService.Orm = db
|
||||||
|
lineApiService.Log = logger.NewHelper(sdk.Runtime.GetLogger()).WithFields(map[string]interface{}{})
|
||||||
|
|
||||||
|
if err := lineApiService.CacheRelation(); err != nil {
|
||||||
|
lineApiService.Log.Errorf("初始化api关系失败 err:%v", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
reverseSettingService := service.LineReverseSetting{}
|
||||||
|
reverseSettingService.Orm = db
|
||||||
|
reverseSettingService.Log = lineApiService.Log
|
||||||
|
|
||||||
|
if err := reverseSettingService.SaveCache(models.LineReverseSetting{}); err != nil {
|
||||||
|
reverseSettingService.Log.Errorf("初始化反单设置失败 err:%v", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
//初始化参数配置
|
//初始化参数配置
|
||||||
cacheservice.InitConfigCache(db)
|
cacheservice.InitConfigCache(db)
|
||||||
//初始化可缓存价格交易对
|
//初始化可缓存价格交易对
|
||||||
|
|||||||
@ -84,3 +84,44 @@ func UserSubscribeInit(orm *gorm.DB, ctx context.Context) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重启连接
|
||||||
|
func RestartConnect() error {
|
||||||
|
spotSockets := excservice.SpotSockets
|
||||||
|
futuresSockets := excservice.FutureSockets
|
||||||
|
timeOut := 22 * time.Hour
|
||||||
|
|
||||||
|
for _, item := range spotSockets {
|
||||||
|
//超过22小时,重新连接
|
||||||
|
if time.Since(item.ConnectTime) > timeOut {
|
||||||
|
if err := item.ReplaceConnection(); err != nil {
|
||||||
|
log.Errorf("现货重启连接失败 key:%s,error:%s", item.GetKey(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range futuresSockets {
|
||||||
|
//超过22小时,重新连接
|
||||||
|
if time.Since(item.ConnectTime) > timeOut {
|
||||||
|
if err := item.ReplaceConnection(); err != nil {
|
||||||
|
log.Errorf("合约重启连接失败 key:%s,error:%s", item.GetKey(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 假死重启
|
||||||
|
func DeadCheck() {
|
||||||
|
spotSockets := excservice.SpotSockets
|
||||||
|
futuresSockets := excservice.FutureSockets
|
||||||
|
|
||||||
|
for _, item := range spotSockets {
|
||||||
|
item.DeadCheck()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range futuresSockets {
|
||||||
|
item.DeadCheck()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
46
pkg/maphelper/maphelper.go
Normal file
46
pkg/maphelper/maphelper.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package maphelper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetString(m map[string]interface{}, key string) (string, error) {
|
||||||
|
val, ok := m[key]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("参数错误,缺少字段 %s", key)
|
||||||
|
}
|
||||||
|
strVal, ok := val.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("字段 %s 类型错误", key)
|
||||||
|
}
|
||||||
|
return strVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFloat64(m map[string]interface{}, key string) (float64, error) {
|
||||||
|
val, ok := m[key]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("参数错误,缺少字段 %s", key)
|
||||||
|
}
|
||||||
|
f, ok := val.(float64)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("字段 %s 类型错误", key)
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBool(m map[string]interface{}, key string) bool {
|
||||||
|
if val, ok := m[key].(bool); ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDecimal(m map[string]interface{}, key string) decimal.Decimal {
|
||||||
|
if val, ok := m[key].(string); ok {
|
||||||
|
d, _ := decimal.NewFromString(val)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
54
pkg/retryhelper/retryhelper.go
Normal file
54
pkg/retryhelper/retryhelper.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package retryhelper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RetryOptions 定义重试的配置项
|
||||||
|
type RetryOptions struct {
|
||||||
|
MaxRetries int // 最大重试次数
|
||||||
|
InitialInterval time.Duration // 初始重试间隔
|
||||||
|
MaxInterval time.Duration // 最大重试间隔
|
||||||
|
BackoffFactor float64 // 指数退避的增长因子
|
||||||
|
RetryableErrFn func(error) bool // 用于判断是否为可重试错误的函数(可选)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultRetryOptions() RetryOptions {
|
||||||
|
return RetryOptions{
|
||||||
|
MaxRetries: 3,
|
||||||
|
InitialInterval: 150 * time.Millisecond,
|
||||||
|
MaxInterval: 3 * time.Second,
|
||||||
|
BackoffFactor: 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry 重试通用函数(用于没有返回值的函数)
|
||||||
|
func Retry(op func() error, opts RetryOptions) error {
|
||||||
|
_, err := RetryWithResult(func() (struct{}, error) {
|
||||||
|
return struct{}{}, op()
|
||||||
|
}, opts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryWithResult 重试通用函数(用于带返回值的函数)
|
||||||
|
func RetryWithResult[T any](op func() (T, error), opts RetryOptions) (result T, err error) {
|
||||||
|
interval := opts.InitialInterval
|
||||||
|
for attempt := 0; attempt <= opts.MaxRetries; attempt++ {
|
||||||
|
result, err = op()
|
||||||
|
if err == nil {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.RetryableErrFn != nil && !opts.RetryableErrFn(err) {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if attempt < opts.MaxRetries {
|
||||||
|
time.Sleep(interval)
|
||||||
|
interval = time.Duration(math.Min(float64(opts.MaxInterval), float64(interval)*opts.BackoffFactor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, fmt.Errorf("retry failed after %d attempts, last error: %w", opts.MaxRetries+1, err)
|
||||||
|
}
|
||||||
@ -1,15 +0,0 @@
|
|||||||
package binanceservice
|
|
||||||
|
|
||||||
import "github.com/go-admin-team/go-admin-core/sdk/service"
|
|
||||||
|
|
||||||
type BinanceReverseFuturesService struct {
|
|
||||||
service.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
func (e *BinanceReverseFuturesService) DoReverse() error {
|
|
||||||
apiInfo, ok := ShouldReverse(apiKey)
|
|
||||||
//TODO: 实现反向开仓逻辑
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package binanceservice
|
|
||||||
|
|
||||||
import "github.com/go-admin-team/go-admin-core/sdk/service"
|
|
||||||
|
|
||||||
type BinanceReverseSpotService struct {
|
|
||||||
service.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
// JudgeApiUser 判断是否需要
|
|
||||||
func (e *BinanceReverseSpotService) JudgeApiUser() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
package binanceservice
|
|
||||||
|
|
||||||
import DbModels "go-admin/app/admin/models"
|
|
||||||
|
|
||||||
// ShouldReverse 判断是否需要反单
|
|
||||||
// return apiInfo, bool
|
|
||||||
func ShouldReverse(apiKey string) (DbModels.LineApiUser, bool) {
|
|
||||||
// TODO: 实现判断是否需要反单的逻辑
|
|
||||||
apiInfo := GetApiInfoByKey(apiKey)
|
|
||||||
|
|
||||||
if apiInfo.ReverseStatus == 1 && apiInfo.ReverseApiId > 0 {
|
|
||||||
return apiInfo, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return DbModels.LineApiUser{}, false
|
|
||||||
}
|
|
||||||
114
services/binanceservice/futures_reset_v2.go
Normal file
114
services/binanceservice/futures_reset_v2.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package binanceservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
DbModels "go-admin/app/admin/models"
|
||||||
|
"go-admin/pkg/retryhelper"
|
||||||
|
|
||||||
|
"github.com/bytedance/sonic"
|
||||||
|
log "github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FuturesResetV2 struct {
|
||||||
|
service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带重试机制的合约下单
|
||||||
|
func (e *FuturesResetV2) OrderPlaceLoop(apiUserInfo *DbModels.LineApiUser, params FutOrderPlace) 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.OrderPlace(apiUserInfo, params)
|
||||||
|
}, opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合约下单
|
||||||
|
func (e *FuturesResetV2) OrderPlace(apiUserInfo *DbModels.LineApiUser, params FutOrderPlace) error {
|
||||||
|
if err := params.CheckParams(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
orderType := strings.ToUpper(params.OrderType)
|
||||||
|
side := strings.ToUpper(params.Side)
|
||||||
|
|
||||||
|
paramsMaps := map[string]string{
|
||||||
|
"symbol": params.Symbol,
|
||||||
|
"side": side,
|
||||||
|
"quantity": params.Quantity.String(),
|
||||||
|
"type": orderType,
|
||||||
|
"newClientOrderId": params.NewClientOrderId,
|
||||||
|
"positionSide": params.PositionSide,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置市价和限价等类型的参数
|
||||||
|
switch orderType {
|
||||||
|
case "LIMIT":
|
||||||
|
paramsMaps["price"] = params.Price.String()
|
||||||
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
|
case "TAKE_PROFIT_MARKET":
|
||||||
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
|
paramsMaps["stopprice"] = params.Profit.String()
|
||||||
|
paramsMaps["workingType"] = "MARK_PRICE"
|
||||||
|
case "TAKE_PROFIT":
|
||||||
|
paramsMaps["price"] = params.Price.String()
|
||||||
|
paramsMaps["stopprice"] = params.Profit.String()
|
||||||
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
|
case "STOP_MARKET":
|
||||||
|
paramsMaps["stopprice"] = params.StopPrice.String()
|
||||||
|
paramsMaps["workingType"] = "MARK_PRICE"
|
||||||
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
|
case "STOP":
|
||||||
|
paramsMaps["price"] = params.Price.String()
|
||||||
|
paramsMaps["stopprice"] = params.StopPrice.String()
|
||||||
|
paramsMaps["workingType"] = "MARK_PRICE"
|
||||||
|
paramsMaps["timeInForce"] = "GTC"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 API 信息和发送下单请求
|
||||||
|
client := GetClient(apiUserInfo)
|
||||||
|
_, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return parseOrderError(err, paramsMaps, statusCode, apiUserInfo)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拆出的错误解析函数
|
||||||
|
func parseOrderError(err error, paramsMaps map[string]string, statusCode int, apiUserInfo *DbModels.LineApiUser) error {
|
||||||
|
var dataMap map[string]interface{}
|
||||||
|
|
||||||
|
if err2 := sonic.Unmarshal([]byte(err.Error()), &dataMap); err2 == nil {
|
||||||
|
if code, ok := dataMap["code"]; ok {
|
||||||
|
paramsVal, _ := sonic.MarshalString(¶msMaps)
|
||||||
|
log.Error("下单失败 参数:", paramsVal)
|
||||||
|
errContent := FutErrorMaps[code.(float64)]
|
||||||
|
if errContent == "" {
|
||||||
|
errContent = err.Error()
|
||||||
|
}
|
||||||
|
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], errContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊处理保证金不足
|
||||||
|
if strings.Contains(err.Error(), "Margin is insufficient.") {
|
||||||
|
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], FutErrorMaps[-2019])
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("api_id:%d 交易对:%s statusCode:%v 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], statusCode, err.Error())
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"go-admin/models/binancedto"
|
"go-admin/models/binancedto"
|
||||||
"go-admin/models/futuresdto"
|
"go-admin/models/futuresdto"
|
||||||
"go-admin/pkg/httputils"
|
"go-admin/pkg/httputils"
|
||||||
|
"go-admin/pkg/retryhelper"
|
||||||
"go-admin/pkg/utility"
|
"go-admin/pkg/utility"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -715,6 +716,15 @@ func (e FutRestApi) ClosePosition(symbol string, orderSn string, quantity decima
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 带重试机制的取消订单
|
||||||
|
func (e FutRestApi) CancelFutOrderRetry(apiUserInfo DbModels.LineApiUser, symbol string, newClientOrderId string) error {
|
||||||
|
opts := retryhelper.DefaultRetryOptions()
|
||||||
|
|
||||||
|
return retryhelper.Retry(func() error {
|
||||||
|
return e.CancelFutOrder(apiUserInfo, symbol, newClientOrderId)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
// CancelFutOrder 通过单个订单号取消合约委托
|
// CancelFutOrder 通过单个订单号取消合约委托
|
||||||
// symbol 交易对
|
// symbol 交易对
|
||||||
// newClientOrderId 系统自定义订单号
|
// newClientOrderId 系统自定义订单号
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import (
|
|||||||
/*
|
/*
|
||||||
修改订单信息
|
修改订单信息
|
||||||
*/
|
*/
|
||||||
func ChangeFutureOrder(mapData map[string]interface{}) {
|
func ChangeFutureOrder(mapData map[string]interface{}, apiKey string) {
|
||||||
// 检查订单号是否存在
|
// 检查订单号是否存在
|
||||||
orderSn, ok := mapData["c"]
|
orderSn, ok := mapData["c"]
|
||||||
originOrderSn := mapData["C"] //取消操作 代表原始订单号
|
originOrderSn := mapData["C"] //取消操作 代表原始订单号
|
||||||
@ -61,34 +61,45 @@ func ChangeFutureOrder(mapData map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
defer lock.Release()
|
defer lock.Release()
|
||||||
|
|
||||||
// 查询订单
|
//反单逻辑
|
||||||
preOrder, err := getPreOrder(db, orderSn)
|
reverseService := ReverseService{}
|
||||||
|
reverseService.Orm = db
|
||||||
|
reverseService.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
_, err = reverseService.ReverseOrder(apiKey, mapData)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("合约订单回调失败,查询订单失败:", orderSn, " err:", err)
|
logger.Errorf("合约订单回调失败,反单失败:%v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析订单状态
|
// 以前的下单逻辑
|
||||||
status, ok := mapData["X"].(string)
|
// 查询订单
|
||||||
if !ok {
|
// preOrder, err := getPreOrder(db, orderSn)
|
||||||
mapStr, _ := sonic.Marshal(&mapData)
|
// if err != nil {
|
||||||
logger.Error("订单回调失败,没有状态:", string(mapStr))
|
// logger.Error("合约订单回调失败,查询订单失败:", orderSn, " err:", err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// 更新订单状态
|
|
||||||
orderStatus, reason := parseOrderStatus(status, mapData)
|
|
||||||
|
|
||||||
if orderStatus == 0 {
|
// // 解析订单状态
|
||||||
logger.Error("订单回调失败,状态错误:", orderSn, " status:", status, " reason:", reason)
|
// status, ok := mapData["X"].(string)
|
||||||
return
|
// if !ok {
|
||||||
}
|
// mapStr, _ := sonic.Marshal(&mapData)
|
||||||
|
// logger.Error("订单回调失败,没有状态:", string(mapStr))
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// // 更新订单状态
|
||||||
|
// orderStatus, reason := parseOrderStatus(status, mapData)
|
||||||
|
|
||||||
if err := updateOrderStatus(db, preOrder, orderStatus, reason, true, mapData); err != nil {
|
// if orderStatus == 0 {
|
||||||
logger.Error("修改订单状态失败:", orderSn, " err:", err)
|
// logger.Error("订单回调失败,状态错误:", orderSn, " status:", status, " reason:", reason)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
handleFutOrderByType(db, preOrder, orderStatus)
|
// if err := updateOrderStatus(db, preOrder, orderStatus, reason, true, mapData); err != nil {
|
||||||
|
// logger.Error("修改订单状态失败:", orderSn, " err:", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// handleFutOrderByType(db, preOrder, orderStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合约回调
|
// 合约回调
|
||||||
|
|||||||
@ -64,6 +64,7 @@ type FutOrderPlace struct {
|
|||||||
ApiId int `json:"api_id"` //api用户id
|
ApiId int `json:"api_id"` //api用户id
|
||||||
Symbol string `json:"symbol"` //合约交易对
|
Symbol string `json:"symbol"` //合约交易对
|
||||||
Side string `json:"side"` //购买方向
|
Side string `json:"side"` //购买方向
|
||||||
|
PositionSide string `json:"position_side"` //持仓方向
|
||||||
Quantity decimal.Decimal `json:"quantity"` //数量
|
Quantity decimal.Decimal `json:"quantity"` //数量
|
||||||
Price decimal.Decimal `json:"price"` //限价单价
|
Price decimal.Decimal `json:"price"` //限价单价
|
||||||
SideType string `json:"side_type"` //现价或者市价
|
SideType string `json:"side_type"` //现价或者市价
|
||||||
|
|||||||
623
services/binanceservice/reverse_service.go
Normal file
623
services/binanceservice/reverse_service.go
Normal file
@ -0,0 +1,623 @@
|
|||||||
|
package binanceservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
DbModels "go-admin/app/admin/models"
|
||||||
|
"go-admin/common/global"
|
||||||
|
"go-admin/common/helper"
|
||||||
|
"go-admin/pkg/maphelper"
|
||||||
|
"go-admin/services/cacheservice"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReverseService struct {
|
||||||
|
service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveMainOrder 保存订单信息
|
||||||
|
// mapData: 下单数据
|
||||||
|
// status: 订单状态
|
||||||
|
// orderSn: 自定义订单号
|
||||||
|
func (e *ReverseService) handleSimpleStatusChange(status string, orderSn string, mapData map[string]interface{}) (bool, error) {
|
||||||
|
statusMap := map[string]int{
|
||||||
|
"NEW": 2,
|
||||||
|
"CANCELED": 6,
|
||||||
|
"EXPIRED": 7,
|
||||||
|
"EXPIRED_IN_MATCH": 7,
|
||||||
|
}
|
||||||
|
if newStatus, ok := statusMap[status]; ok {
|
||||||
|
_ = e.changeOrderStatus(newStatus, orderSn, mapData)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("不支持的订单状态 %s", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseOrder 反向下单
|
||||||
|
// apiKey: api key
|
||||||
|
// mapData: 下单数据
|
||||||
|
// return apiInfo, bool
|
||||||
|
// bool: true=需要反单, false=不需要反单
|
||||||
|
func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interface{}) (bool, error) {
|
||||||
|
apiInfo := GetApiInfoByKey(apiKey)
|
||||||
|
|
||||||
|
if apiInfo.Id == 0 {
|
||||||
|
e.Log.Errorf("获取apiInfo失败 %s", apiKey)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _ := maphelper.GetString(mapData, "X")
|
||||||
|
orderSn, err := maphelper.GetString(mapData, "c")
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
ot, err := maphelper.GetString(mapData, "ot")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case "NEW", "CANCELED", "EXPIRED", "EXPIRED_IN_MATCH":
|
||||||
|
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") &&
|
||||||
|
apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {
|
||||||
|
|
||||||
|
if err := e.ReTakeOrStopOrder(&mapData, orderSn, &apiInfo); err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
case "FILLED":
|
||||||
|
if apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {
|
||||||
|
reverseApiInfo, err := GetApiInfo(apiInfo.ReverseApiId)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取反向api信息失败 reverseApiId:%d, err:%v", apiInfo.ReverseApiId, err)
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mainOrder, err := e.SaveMainOrder(mapData, apiInfo)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
|
||||||
|
if mainOrder.Category == 0 {
|
||||||
|
if err1 := e.savePosition(&mainOrder, reverseApiInfo.Id, true, false, false); err1 != nil {
|
||||||
|
return true, err1
|
||||||
|
}
|
||||||
|
|
||||||
|
e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, false, false)
|
||||||
|
}
|
||||||
|
case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
|
||||||
|
if mainOrder.Category == 0 {
|
||||||
|
closePosition := maphelper.GetBool(mapData, "R")
|
||||||
|
if err1 := e.savePosition(&mainOrder, reverseApiInfo.Id, true, true, closePosition); err1 != nil {
|
||||||
|
e.Log.Errorf("保存主订单失败: %v", err1)
|
||||||
|
return true, err1
|
||||||
|
}
|
||||||
|
e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, true, closePosition)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return true, errors.New("不支持的订单类型")
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
} else if apiInfo.Subordinate == "2" {
|
||||||
|
symbol, err := maphelper.GetString(mapData, "s")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
positionSide, err := maphelper.GetString(mapData, "ps")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
side, err := maphelper.GetString(mapData, "S")
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalNum := maphelper.GetDecimal(mapData, "z")
|
||||||
|
|
||||||
|
mainOrder := DbModels.LineReverseOrder{
|
||||||
|
ApiId: apiInfo.Id,
|
||||||
|
Symbol: symbol,
|
||||||
|
PositionSide: positionSide,
|
||||||
|
TotalNum: totalNum,
|
||||||
|
Side: side,
|
||||||
|
}
|
||||||
|
e.changeOrderStatus(3, orderSn, mapData)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
|
||||||
|
if mainOrder.Category == 0 {
|
||||||
|
if err1 := e.savePosition(&mainOrder, 0, false, false, false); err1 != nil {
|
||||||
|
return true, err1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
|
||||||
|
if mainOrder.Category == 0 {
|
||||||
|
closePosition := maphelper.GetBool(mapData, "R")
|
||||||
|
if err1 := e.savePosition(&mainOrder, 0, false, true, closePosition); err1 != nil {
|
||||||
|
return true, err1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return true, errors.New("不支持的订单类型")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("不支持的订单状态 %s", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改订单状态
|
||||||
|
// status: 订单状态 1-待下单 2-已下单 3-已成交 6-已取消 7-已过期
|
||||||
|
func (e *ReverseService) changeOrderStatus(status int, orderSn string, mapData map[string]interface{}) error {
|
||||||
|
data := map[string]interface{}{"status": status, "updated_at": time.Now()}
|
||||||
|
|
||||||
|
if status == 3 {
|
||||||
|
now := time.Now()
|
||||||
|
if orderId, ok := mapData["i"].(float64); ok {
|
||||||
|
data["order_id"] = orderId
|
||||||
|
}
|
||||||
|
|
||||||
|
if ap, ok := mapData["ap"].(bool); ok {
|
||||||
|
data["final_price"] = ap
|
||||||
|
}
|
||||||
|
|
||||||
|
if num, ok := mapData["z"].(string); ok {
|
||||||
|
data["total_num"], _ = decimal.NewFromString(num)
|
||||||
|
}
|
||||||
|
|
||||||
|
data["trigger_time"] = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
db := e.Orm.Model(&DbModels.LineReverseOrder{}).
|
||||||
|
Where("order_sn =? and status != 3", orderSn).
|
||||||
|
Updates(data)
|
||||||
|
|
||||||
|
if db.Error != nil {
|
||||||
|
e.Log.Errorf("修改订单状态失败 orderSn:%s, err:%v", orderSn, db.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.RowsAffected == 0 {
|
||||||
|
e.Log.Errorf("修改订单状态失败 orderSn:%s, 未找到订单", orderSn)
|
||||||
|
}
|
||||||
|
return db.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先保存主单,持仓信息 必须用双向持仓!
|
||||||
|
func (e *ReverseService) SaveMainOrder(mapData map[string]interface{}, apiInfo DbModels.LineApiUser) (DbModels.LineReverseOrder, error) {
|
||||||
|
now := time.Now()
|
||||||
|
symbol, err := maphelper.GetString(mapData, "s")
|
||||||
|
var reverseOrder DbModels.LineReverseOrder
|
||||||
|
if err != nil {
|
||||||
|
return reverseOrder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
orderSn, err := maphelper.GetString(mapData, "c")
|
||||||
|
if err != nil {
|
||||||
|
return reverseOrder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
side, err := maphelper.GetString(mapData, "S")
|
||||||
|
if err != nil {
|
||||||
|
return reverseOrder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
positionSide, err := maphelper.GetString(mapData, "ps")
|
||||||
|
if err != nil {
|
||||||
|
return reverseOrder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mainType, _ := maphelper.GetString(mapData, "ot")
|
||||||
|
|
||||||
|
reverseOrder = DbModels.LineReverseOrder{
|
||||||
|
ApiId: apiInfo.Id,
|
||||||
|
Category: 0,
|
||||||
|
OrderSn: orderSn,
|
||||||
|
TriggerTime: &now,
|
||||||
|
Status: 3,
|
||||||
|
Symbol: symbol,
|
||||||
|
FollowOrderSn: "",
|
||||||
|
Type: mainType,
|
||||||
|
Price: maphelper.GetDecimal(mapData, "p"),
|
||||||
|
FinalPrice: maphelper.GetDecimal(mapData, "ap"),
|
||||||
|
TotalNum: maphelper.GetDecimal(mapData, "z"),
|
||||||
|
Side: side,
|
||||||
|
PositionSide: positionSide,
|
||||||
|
}
|
||||||
|
|
||||||
|
reverseOrder.PriceU = reverseOrder.Price
|
||||||
|
|
||||||
|
if !reverseOrder.Price.IsZero() && !reverseOrder.TotalNum.IsZero() {
|
||||||
|
reverseOrder.BuyPrice = reverseOrder.Price.Mul(reverseOrder.TotalNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if id, err := maphelper.GetFloat64(mapData, "i"); err == nil {
|
||||||
|
reverseOrder.OrderId = strconv.FormatFloat(id, 'f', -1, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
orderType, _ := maphelper.GetString(mapData, "ot")
|
||||||
|
switch orderType {
|
||||||
|
case "LIMIT", "MARKET":
|
||||||
|
reverseOrder.OrderType = 0
|
||||||
|
case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
|
||||||
|
reverseOrder.OrderType = 1
|
||||||
|
case "STOP_MARKET", "STOP", "TRAILING_STOP_MARKET":
|
||||||
|
reverseOrder.OrderType = 2
|
||||||
|
default:
|
||||||
|
return reverseOrder, fmt.Errorf("不支持的订单类型: %s", orderType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reverseOrder.PositionSide == "BOTH" {
|
||||||
|
return reverseOrder, errors.New("不支持的持仓类型,必须为双向持仓")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.Orm.Create(&reverseOrder).Error; err != nil {
|
||||||
|
e.Log.Errorf("保存主单失败:%v", err)
|
||||||
|
return reverseOrder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return reverseOrder, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新仓位信息
|
||||||
|
// closePosition: true=平仓, false=减仓
|
||||||
|
func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, reverseApiId int, isMain, reducePosition, closePosition bool) error {
|
||||||
|
position := DbModels.LineReversePosition{}
|
||||||
|
positionSide := reverseOrder.PositionSide
|
||||||
|
side := reverseOrder.Side
|
||||||
|
totalNum := reverseOrder.TotalNum
|
||||||
|
|
||||||
|
var querySql string
|
||||||
|
sqlStr := ""
|
||||||
|
|
||||||
|
//如果是主单,存储仓位则是反单的持仓方向
|
||||||
|
if isMain {
|
||||||
|
if reverseOrder.PositionSide == "LONG" {
|
||||||
|
positionSide = "SHORT"
|
||||||
|
} else {
|
||||||
|
positionSide = "LONG"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reducePosition {
|
||||||
|
//反单止盈止损方向相反
|
||||||
|
if side == "SELL" {
|
||||||
|
side = "BUY"
|
||||||
|
} else {
|
||||||
|
side = "SELL"
|
||||||
|
}
|
||||||
|
|
||||||
|
position.ReverseApiId = reverseApiId
|
||||||
|
position.Side = side
|
||||||
|
position.ApiId = reverseOrder.ApiId
|
||||||
|
position.Symbol = reverseOrder.Symbol
|
||||||
|
position.Status = 1
|
||||||
|
position.ReverseStatus = 0
|
||||||
|
position.PositionSide = positionSide
|
||||||
|
}
|
||||||
|
querySql = "api_id =? and position_side =? and symbol =? and status =1"
|
||||||
|
|
||||||
|
//平仓
|
||||||
|
if closePosition {
|
||||||
|
totalNum = decimal.Zero
|
||||||
|
sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2 where id =@id and status!=2 "
|
||||||
|
} else if reducePosition {
|
||||||
|
//只减仓
|
||||||
|
sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now() where id =@id and status!=2 "
|
||||||
|
} else {
|
||||||
|
sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now() where id =@id and status!=2"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"
|
||||||
|
|
||||||
|
if closePosition {
|
||||||
|
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"
|
||||||
|
} else if reducePosition {
|
||||||
|
sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now() where id =@id and reverse_status !=2"
|
||||||
|
} 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
|
err1 := tx.Model(&position).Where(querySql,
|
||||||
|
reverseOrder.ApiId, positionSide, reverseOrder.Symbol).First(&position).Error
|
||||||
|
|
||||||
|
if err1 != nil {
|
||||||
|
//主单仓位不存在,创建新仓位
|
||||||
|
if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition {
|
||||||
|
|
||||||
|
if err2 := tx.Create(&position).Error; err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id))
|
||||||
|
if dbResult.Error != nil {
|
||||||
|
return dbResult.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbResult.RowsAffected == 0 {
|
||||||
|
e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, reverseOrder)
|
||||||
|
return errors.New("没有找到对应的持仓信息")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反向下单
|
||||||
|
// mainOrder: 主单信息
|
||||||
|
// reverseApiId: 反向apiId
|
||||||
|
// orderProportion: 反向下单比例
|
||||||
|
// reducePosition: 是否减仓
|
||||||
|
// closePosition: 是否平仓
|
||||||
|
func (e *ReverseService) DoAddReverseOrder(mainOrder *DbModels.LineReverseOrder, reverseApiInfo *DbModels.LineApiUser, orderProportion decimal.Decimal, reducePosition, closePosition bool) error {
|
||||||
|
order := DbModels.LineReverseOrder{}
|
||||||
|
order.ApiId = reverseApiInfo.Id
|
||||||
|
order.Category = 1
|
||||||
|
order.OrderSn = helper.GetOrderNo()
|
||||||
|
order.FollowOrderSn = mainOrder.OrderSn
|
||||||
|
order.Symbol = mainOrder.Symbol
|
||||||
|
order.OrderType = 0
|
||||||
|
|
||||||
|
switch mainOrder.PositionSide {
|
||||||
|
case "LONG":
|
||||||
|
order.PositionSide = "SHORT"
|
||||||
|
case "SHORT":
|
||||||
|
order.PositionSide = "LONG"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mainOrder.Side {
|
||||||
|
case "SELL":
|
||||||
|
order.Side = "BUY"
|
||||||
|
case "BUY":
|
||||||
|
order.Side = "SELL"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的订单类型 side:%s", mainOrder.Side)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reducePosition && closePosition {
|
||||||
|
order.OrderType = 4
|
||||||
|
} else if reducePosition {
|
||||||
|
order.OrderType = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, mainOrder.Symbol, 1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取交易对信息失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
setting, err := cacheservice.GetReverseSetting(e.Orm)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取反单设置失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
signPrice, _ := decimal.NewFromString(symbol.LastPrice)
|
||||||
|
price := signPrice.Truncate(int32(symbol.PriceDigit))
|
||||||
|
|
||||||
|
if price.Cmp(decimal.Zero) <= 0 {
|
||||||
|
e.Log.Errorf("获取最新价格失败 symbol:%s custom:%s 单价小于0", mainOrder.Symbol, mainOrder.OrderSn)
|
||||||
|
return errors.New("获取最新价格失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
var percent decimal.Decimal
|
||||||
|
switch {
|
||||||
|
case order.PositionSide == "LONG" && order.Side == "BUY", order.PositionSide == "SHORT" && order.Side == "BUY":
|
||||||
|
percent = decimal.NewFromInt(100).Add(setting.ReversePremiumRatio)
|
||||||
|
case order.PositionSide == "SHORT" && order.Side == "SELL", order.PositionSide == "LONG" && order.Side == "SELL":
|
||||||
|
percent = decimal.NewFromInt(100).Sub(setting.ReversePremiumRatio)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的订单类型 ps:%s, side:%s", order.PositionSide, order.Side)
|
||||||
|
}
|
||||||
|
|
||||||
|
percent = percent.Div(decimal.NewFromInt(100)).Truncate(4)
|
||||||
|
//计算溢价单价
|
||||||
|
price = price.Mul(percent).Truncate(int32(symbol.PriceDigit))
|
||||||
|
var amount decimal.Decimal
|
||||||
|
|
||||||
|
//平仓单直接卖出全部数量
|
||||||
|
if closePosition {
|
||||||
|
var position DbModels.LineReversePosition
|
||||||
|
if err1 := e.Orm.Model(position).
|
||||||
|
Where("position_side =? and reverse_api_id =? and reverse_status =1", order.PositionSide, order.ApiId).
|
||||||
|
Select("reverse_amount").
|
||||||
|
Find(&position).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("获取剩余仓位失败 symbol:%s custom:%s :%v", order.Symbol, order.OrderSn, err1)
|
||||||
|
|
||||||
|
if len(err1.Error()) < 255 {
|
||||||
|
order.Remark = err1.Error()
|
||||||
|
} else {
|
||||||
|
order.Remark = err1.Error()[:254]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amount = position.ReverseAmount
|
||||||
|
|
||||||
|
if amount.IsZero() {
|
||||||
|
order.Remark = "没有持仓数量"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
proportion := decimal.NewFromInt(100)
|
||||||
|
|
||||||
|
if orderProportion.Cmp(decimal.Zero) > 0 {
|
||||||
|
proportion = orderProportion
|
||||||
|
}
|
||||||
|
|
||||||
|
//反向下单百分比
|
||||||
|
proportion = proportion.Div(decimal.NewFromInt(100)).Truncate(2)
|
||||||
|
amount = mainOrder.BuyPrice.Mul(proportion).Div(price).Truncate(int32(symbol.AmountDigit))
|
||||||
|
|
||||||
|
if amount.Cmp(decimal.Zero) <= 0 {
|
||||||
|
e.Log.Errorf("计算数量失败 symbol:%s custom:%s 数量小于0", mainOrder.Symbol, mainOrder.OrderSn)
|
||||||
|
return errors.New("计算数量失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
order.TotalNum = amount
|
||||||
|
order.Price = price
|
||||||
|
order.PriceU = price
|
||||||
|
order.BuyPrice = amount.Mul(price).Truncate(int32(symbol.PriceDigit))
|
||||||
|
order.Status = 1
|
||||||
|
order.Type = setting.ReverseOrderType
|
||||||
|
order.SignPrice = signPrice
|
||||||
|
|
||||||
|
if order.Remark != "" {
|
||||||
|
order.Status = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&order).Create(&order).Error; err != nil {
|
||||||
|
e.Log.Errorf("保存反单失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if order.Status == 1 {
|
||||||
|
e.DoBianceOrder(&order, reverseApiInfo, &setting, reducePosition, closePosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理币安订单
|
||||||
|
// order: 反单信息
|
||||||
|
// apiInfo: 币安api信息
|
||||||
|
func (e *ReverseService) DoBianceOrder(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, setting *DbModels.LineReverseSetting, reducePosition, closePosition bool) error {
|
||||||
|
futApiV2 := FuturesResetV2{Service: e.Service}
|
||||||
|
orderType := setting.ReverseOrderType
|
||||||
|
|
||||||
|
if orderType == "" {
|
||||||
|
orderType = "LIMIT"
|
||||||
|
}
|
||||||
|
|
||||||
|
params := FutOrderPlace{
|
||||||
|
ApiId: apiInfo.Id,
|
||||||
|
Symbol: order.Symbol,
|
||||||
|
PositionSide: order.PositionSide,
|
||||||
|
Side: order.Side,
|
||||||
|
OrderType: orderType,
|
||||||
|
Quantity: order.TotalNum,
|
||||||
|
Price: order.Price,
|
||||||
|
NewClientOrderId: order.OrderSn,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := futApiV2.OrderPlaceLoop(apiInfo, params)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", order.Symbol, order.OrderSn, err)
|
||||||
|
remark := err.Error()
|
||||||
|
|
||||||
|
if len(remark) > 255 {
|
||||||
|
remark = remark[:254]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err1 := e.Orm.Model(&order).Where("id =? and status !=3", order.Id).Updates(map[string]interface{}{"status": 8, "remark": remark, "updated_at": time.Now()}).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", err1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重下止盈止损
|
||||||
|
// mapData: 主单止盈止损回调
|
||||||
|
func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser) error {
|
||||||
|
symbol, err := maphelper.GetString(*mapData, "s")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
side, err := maphelper.GetString(*mapData, "S")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ot, err := maphelper.GetString(*mapData, "ot")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//反单止盈止损方向相反
|
||||||
|
if side == "SELL" {
|
||||||
|
side = "BUY"
|
||||||
|
} else {
|
||||||
|
side = "SELL"
|
||||||
|
}
|
||||||
|
|
||||||
|
positionSide, err := maphelper.GetString(*mapData, "ps")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if positionSide == "LONG" {
|
||||||
|
positionSide = "SHORT"
|
||||||
|
} else {
|
||||||
|
positionSide = "LONG"
|
||||||
|
}
|
||||||
|
|
||||||
|
apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("根据主单api获取反单api失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var reverseOrder DbModels.LineReverseOrder
|
||||||
|
e.Orm.Model(&DbModels.LineReverseOrder{}).
|
||||||
|
Where("symbol =? and api_id =? and position_side =? and side = ? and status =1", symbol, mainApiInfo.ReverseApiId, positionSide, side).
|
||||||
|
First(&reverseOrder)
|
||||||
|
|
||||||
|
//取消旧止盈止损
|
||||||
|
if reverseOrder.OrderSn != "" {
|
||||||
|
futApi := FutRestApi{}
|
||||||
|
err := futApi.CancelFutOrderRetry(apiInfo, symbol, reverseOrder.OrderSn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("币安撤单失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := FutOrderPlace{
|
||||||
|
ApiId: apiInfo.Id,
|
||||||
|
Symbol: symbol,
|
||||||
|
PositionSide: positionSide,
|
||||||
|
Side: side,
|
||||||
|
OrderType: ot,
|
||||||
|
Quantity: reverseOrder.TotalNum,
|
||||||
|
Price: reverseOrder.Price,
|
||||||
|
NewClientOrderId: reverseOrder.OrderSn,
|
||||||
|
}
|
||||||
|
futApiV2 := FuturesResetV2{Service: e.Service}
|
||||||
|
futApiV2.OrderPlace(&apiInfo, params)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
1
services/binanceservice/reverse_service_test.go
Normal file
1
services/binanceservice/reverse_service_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package binanceservice
|
||||||
@ -21,7 +21,6 @@ import (
|
|||||||
|
|
||||||
"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"
|
||||||
log "github.com/go-admin-team/go-admin-core/logger"
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@ -29,7 +28,7 @@ import (
|
|||||||
/*
|
/*
|
||||||
订单回调
|
订单回调
|
||||||
*/
|
*/
|
||||||
func ChangeSpotOrder(mapData map[string]interface{}) {
|
func ChangeSpotOrder(mapData map[string]interface{}, apiKey string) {
|
||||||
// 检查订单号是否存在
|
// 检查订单号是否存在
|
||||||
orderSn, ok := mapData["c"]
|
orderSn, ok := mapData["c"]
|
||||||
originOrderSn := mapData["C"] //取消操作 代表原始订单号
|
originOrderSn := mapData["C"] //取消操作 代表原始订单号
|
||||||
@ -193,7 +192,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
|||||||
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotReduceCallback, preOrder.ApiId, preOrder.Symbol), 120, 20, 100*time.Millisecond)
|
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotReduceCallback, preOrder.ApiId, preOrder.Symbol), 120, 20, 100*time.Millisecond)
|
||||||
|
|
||||||
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||||
log.Error("获取锁失败", err)
|
logger.Error("获取锁失败", err)
|
||||||
return
|
return
|
||||||
} else if ok {
|
} else if ok {
|
||||||
defer lock.Release()
|
defer lock.Release()
|
||||||
|
|||||||
23
services/cacheservice/confg_server_test.go
Normal file
23
services/cacheservice/confg_server_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package cacheservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/common/helper"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetReverseSetting(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)
|
||||||
|
|
||||||
|
setting, err := GetReverseSetting(db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(setting)
|
||||||
|
}
|
||||||
@ -130,3 +130,22 @@ func ResetSystemSetting(db *gorm.DB) (models.LineSystemSetting, error) {
|
|||||||
}
|
}
|
||||||
return models.LineSystemSetting{}, nil
|
return models.LineSystemSetting{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取反单配置
|
||||||
|
func GetReverseSetting(db *gorm.DB) (models.LineReverseSetting, error) {
|
||||||
|
setting := models.LineReverseSetting{}
|
||||||
|
|
||||||
|
helper.DefaultRedis.HGetAsObject(rediskey.ReverseSetting, &setting)
|
||||||
|
|
||||||
|
if setting.ReverseOrderType == "" {
|
||||||
|
if err := db.Model(&setting).First(&setting).Error; err != nil {
|
||||||
|
logger.Error("获取反单配置失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := helper.DefaultRedis.SetHashWithTags(rediskey.ReverseSetting, &setting); err != nil {
|
||||||
|
logger.Error("redis添加反单配置失败", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import (
|
|||||||
- @msg 消息内容
|
- @msg 消息内容
|
||||||
- @listenType 订阅类型 0-现货 1-合约
|
- @listenType 订阅类型 0-现货 1-合约
|
||||||
*/
|
*/
|
||||||
func ReceiveListen(msg []byte, listenType int) (reconnect bool, err error) {
|
func ReceiveListen(msg []byte, listenType int, apiKey string) (reconnect bool, err error) {
|
||||||
var dataMap map[string]interface{}
|
var dataMap map[string]interface{}
|
||||||
err = sonic.Unmarshal(msg, &dataMap)
|
err = sonic.Unmarshal(msg, &dataMap)
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ func ReceiveListen(msg []byte, listenType int) (reconnect bool, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
utility.SafeGo(func() {
|
utility.SafeGo(func() {
|
||||||
binanceservice.ChangeSpotOrder(mapData)
|
binanceservice.ChangeSpotOrder(mapData, apiKey)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
var data futuresdto.OrderTradeUpdate
|
var data futuresdto.OrderTradeUpdate
|
||||||
@ -64,7 +64,7 @@ func ReceiveListen(msg []byte, listenType int) (reconnect bool, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
utility.SafeGo(func() {
|
utility.SafeGo(func() {
|
||||||
binanceservice.ChangeFutureOrder(data.OrderDetails)
|
binanceservice.ChangeFutureOrder(data.OrderDetails, apiKey)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
//订单更新
|
//订单更新
|
||||||
@ -72,9 +72,9 @@ func ReceiveListen(msg []byte, listenType int) (reconnect bool, err error) {
|
|||||||
log.Info("executionReport 推送:", string(msg))
|
log.Info("executionReport 推送:", string(msg))
|
||||||
|
|
||||||
if listenType == 0 { //现货
|
if listenType == 0 { //现货
|
||||||
binanceservice.ChangeSpotOrder(dataMap)
|
binanceservice.ChangeSpotOrder(dataMap, apiKey)
|
||||||
} else if listenType == 1 { //合约
|
} else if listenType == 1 { //合约
|
||||||
binanceservice.ChangeFutureOrder(dataMap)
|
binanceservice.ChangeFutureOrder(dataMap, apiKey)
|
||||||
} else {
|
} else {
|
||||||
log.Error("executionReport 不支持的订阅类型", strconv.Itoa(listenType))
|
log.Error("executionReport 不支持的订阅类型", strconv.Itoa(listenType))
|
||||||
}
|
}
|
||||||
@ -101,6 +101,7 @@ func ReceiveListen(msg []byte, listenType int) (reconnect bool, err error) {
|
|||||||
|
|
||||||
case "eventStreamTerminated":
|
case "eventStreamTerminated":
|
||||||
log.Info("账户数据流被终止 type:", getWsTypeName(listenType))
|
log.Info("账户数据流被终止 type:", getWsTypeName(listenType))
|
||||||
|
return true, nil
|
||||||
default:
|
default:
|
||||||
log.Info("未知事件 内容:", string(msg))
|
log.Info("未知事件 内容:", string(msg))
|
||||||
log.Info("未知事件", event)
|
log.Info("未知事件", event)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bytedance/sonic"
|
"github.com/bytedance/sonic"
|
||||||
@ -43,6 +44,8 @@ type BinanceWebSocketManager struct {
|
|||||||
mu sync.Mutex // 用于控制并发访问 isStopped
|
mu sync.Mutex // 用于控制并发访问 isStopped
|
||||||
cancelFunc context.CancelFunc
|
cancelFunc context.CancelFunc
|
||||||
listenKey string // 新增字段
|
listenKey string // 新增字段
|
||||||
|
reconnecting atomic.Bool // 防止重复重连
|
||||||
|
ConnectTime time.Time // 当前连接建立时间
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已有连接
|
// 已有连接
|
||||||
@ -72,6 +75,10 @@ func NewBinanceWebSocketManager(wsType int, apiKey, apiSecret, proxyType, proxyA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wm *BinanceWebSocketManager) GetKey() string {
|
||||||
|
return wm.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
func (wm *BinanceWebSocketManager) Start() {
|
func (wm *BinanceWebSocketManager) Start() {
|
||||||
utility.SafeGo(wm.run)
|
utility.SafeGo(wm.run)
|
||||||
// wm.run()
|
// wm.run()
|
||||||
@ -91,14 +98,32 @@ func (wm *BinanceWebSocketManager) Restart(apiKey, apiSecret, proxyType, proxyAd
|
|||||||
wm.isStopped = false
|
wm.isStopped = false
|
||||||
utility.SafeGo(wm.run)
|
utility.SafeGo(wm.run)
|
||||||
} else {
|
} else {
|
||||||
wm.reconnect <- struct{}{}
|
log.Warnf("调用restart")
|
||||||
|
wm.triggerReconnect(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return wm
|
return wm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 触发重连
|
||||||
|
func (wm *BinanceWebSocketManager) triggerReconnect(force bool) {
|
||||||
|
if force {
|
||||||
|
wm.reconnecting.Store(false) // 强制重置标志位
|
||||||
|
}
|
||||||
|
|
||||||
|
if wm.reconnecting.CompareAndSwap(false, true) {
|
||||||
|
log.Warnf("准备重连 key: %s wsType: %v", wm.apiKey, wm.wsType)
|
||||||
|
// 发送信号触发重连协程
|
||||||
|
select {
|
||||||
|
case wm.reconnect <- struct{}{}:
|
||||||
|
default:
|
||||||
|
// 防止阻塞,如果通道满了就跳过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
func Restart(wm *BinanceWebSocketManager) {
|
func Restart(wm *BinanceWebSocketManager) {
|
||||||
wm.reconnect <- struct{}{}
|
log.Warnf("调用restart")
|
||||||
|
wm.triggerReconnect(true)
|
||||||
}
|
}
|
||||||
func (wm *BinanceWebSocketManager) run() {
|
func (wm *BinanceWebSocketManager) run() {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@ -201,6 +226,8 @@ func (wm *BinanceWebSocketManager) connect(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 连接成功,更新连接时间
|
||||||
|
wm.ConnectTime = time.Now()
|
||||||
log.Info(fmt.Sprintf("已连接到 Binance %s WebSocket【%s】 key:%s", getWsTypeName(wm.wsType), wm.apiKey, listenKey))
|
log.Info(fmt.Sprintf("已连接到 Binance %s WebSocket【%s】 key:%s", getWsTypeName(wm.wsType), wm.apiKey, listenKey))
|
||||||
|
|
||||||
// Ping处理
|
// Ping处理
|
||||||
@ -222,10 +249,88 @@ func (wm *BinanceWebSocketManager) connect(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
// utility.SafeGoParam(wm.restartConnect, ctx)
|
|
||||||
utility.SafeGo(func() { wm.startListenKeyRenewal2(ctx) })
|
utility.SafeGo(func() { wm.startListenKeyRenewal2(ctx) })
|
||||||
utility.SafeGo(func() { wm.readMessages(ctx) })
|
utility.SafeGo(func() { wm.readMessages(ctx) })
|
||||||
utility.SafeGo(func() { wm.handleReconnect(ctx) })
|
utility.SafeGo(func() { wm.handleReconnect(ctx) })
|
||||||
|
utility.SafeGo(func() { wm.startPingLoop(ctx) })
|
||||||
|
// utility.SafeGo(func() { wm.startDeadCheck(ctx) })
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceConnection 创建新连接并关闭旧连接,实现无缝连接替换
|
||||||
|
func (wm *BinanceWebSocketManager) ReplaceConnection() error {
|
||||||
|
wm.mu.Lock()
|
||||||
|
if wm.isStopped {
|
||||||
|
wm.mu.Unlock()
|
||||||
|
return errors.New("WebSocket 已停止")
|
||||||
|
}
|
||||||
|
oldCtxCancel := wm.cancelFunc
|
||||||
|
oldConn := wm.ws
|
||||||
|
wm.mu.Unlock()
|
||||||
|
|
||||||
|
log.Infof("🔄 正在替换连接: %s", wm.apiKey)
|
||||||
|
|
||||||
|
// 步骤 1:先获取新的 listenKey 和连接
|
||||||
|
newListenKey, err := wm.getListenKey()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("获取新 listenKey 失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer, err := wm.getDialer()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newURL := fmt.Sprintf("%s/%s", wm.url, newListenKey)
|
||||||
|
newConn, _, err := dialer.Dial(newURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("新连接 Dial 失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤 2:创建新的上下文并启动协程
|
||||||
|
newCtx, newCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// 设置 ping handler
|
||||||
|
newConn.SetPingHandler(func(appData string) error {
|
||||||
|
log.Infof("收到 Ping(新连接) key:%s msg:%s", wm.apiKey, appData)
|
||||||
|
for x := 0; x < 5; x++ {
|
||||||
|
if err := newConn.WriteControl(websocket.PongMessage, []byte(appData), time.Now().Add(10*time.Second)); err != nil {
|
||||||
|
log.Errorf("Pong 失败 %d 次 err:%v", x, err)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
setLastTime(wm)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// 步骤 3:安全切换连接
|
||||||
|
wm.mu.Lock()
|
||||||
|
wm.ws = newConn
|
||||||
|
wm.listenKey = newListenKey
|
||||||
|
wm.ConnectTime = time.Now()
|
||||||
|
wm.cancelFunc = newCancel
|
||||||
|
wm.mu.Unlock()
|
||||||
|
|
||||||
|
log.Infof("✅ 替换连接成功: %s listenKey: %s", wm.apiKey, newListenKey)
|
||||||
|
|
||||||
|
// 步骤 4:启动新连接协程
|
||||||
|
go wm.startListenKeyRenewal2(newCtx)
|
||||||
|
go wm.readMessages(newCtx)
|
||||||
|
go wm.handleReconnect(newCtx)
|
||||||
|
go wm.startPingLoop(newCtx)
|
||||||
|
// go wm.startDeadCheck(newCtx)
|
||||||
|
|
||||||
|
// 步骤 5:关闭旧连接、取消旧协程
|
||||||
|
if oldCtxCancel != nil {
|
||||||
|
oldCtxCancel()
|
||||||
|
}
|
||||||
|
if oldConn != nil {
|
||||||
|
_ = oldConn.Close()
|
||||||
|
log.Infof("🔒 旧连接已关闭: %s", wm.apiKey)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -251,6 +356,7 @@ func setLastTime(wm *BinanceWebSocketManager) {
|
|||||||
if val != "" {
|
if val != "" {
|
||||||
helper.DefaultRedis.SetString(subKey, val)
|
helper.DefaultRedis.SetString(subKey, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *BinanceWebSocketManager) getDialer() (*websocket.Dialer, error) {
|
func (wm *BinanceWebSocketManager) getDialer() (*websocket.Dialer, error) {
|
||||||
@ -357,7 +463,8 @@ func (wm *BinanceWebSocketManager) readMessages(ctx context.Context) {
|
|||||||
_, msg, err := wm.ws.ReadMessage()
|
_, msg, err := wm.ws.ReadMessage()
|
||||||
if err != nil && strings.Contains(err.Error(), "websocket: close") {
|
if err != nil && strings.Contains(err.Error(), "websocket: close") {
|
||||||
if !wm.isStopped {
|
if !wm.isStopped {
|
||||||
wm.reconnect <- struct{}{}
|
log.Error("收到关闭消息", err.Error())
|
||||||
|
wm.triggerReconnect(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Error("websocket 关闭")
|
log.Error("websocket 关闭")
|
||||||
@ -375,8 +482,9 @@ func (wm *BinanceWebSocketManager) readMessages(ctx context.Context) {
|
|||||||
func (wm *BinanceWebSocketManager) handleOrderUpdate(msg []byte) {
|
func (wm *BinanceWebSocketManager) handleOrderUpdate(msg []byte) {
|
||||||
setLastTime(wm)
|
setLastTime(wm)
|
||||||
|
|
||||||
if reconnect, _ := ReceiveListen(msg, wm.wsType); reconnect {
|
if reconnect, _ := ReceiveListen(msg, wm.wsType, wm.apiKey); reconnect {
|
||||||
wm.reconnect <- struct{}{}
|
log.Errorf("收到重连请求")
|
||||||
|
wm.triggerReconnect(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,6 +555,7 @@ func (wm *BinanceWebSocketManager) handleReconnect(ctx context.Context) {
|
|||||||
retryCount++
|
retryCount++
|
||||||
|
|
||||||
if retryCount >= maxRetries {
|
if retryCount >= maxRetries {
|
||||||
|
wm.reconnecting.Store(false)
|
||||||
log.Error("重连失败次数过多,退出重连逻辑")
|
log.Error("重连失败次数过多,退出重连逻辑")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -455,50 +564,87 @@ func (wm *BinanceWebSocketManager) handleReconnect(ctx context.Context) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重连成功,清除标记
|
||||||
|
wm.reconnecting.Store(false)
|
||||||
|
retryCount = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 假死检测
|
||||||
|
func (wm *BinanceWebSocketManager) DeadCheck() {
|
||||||
|
subKey := fmt.Sprintf(global.USER_SUBSCRIBE, wm.apiKey)
|
||||||
|
val, _ := helper.DefaultRedis.GetString(subKey)
|
||||||
|
if val == "" {
|
||||||
|
log.Warnf("没有订阅信息,无法进行假死检测")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var data binancedto.UserSubscribeState
|
||||||
|
_ = sonic.Unmarshal([]byte(val), &data)
|
||||||
|
|
||||||
|
var lastTime *time.Time
|
||||||
|
if wm.wsType == 0 {
|
||||||
|
lastTime = data.SpotLastTime
|
||||||
|
} else {
|
||||||
|
lastTime = data.FuturesLastTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义最大静默时间(超出视为假死)
|
||||||
|
var timeout time.Duration
|
||||||
|
if wm.wsType == 0 {
|
||||||
|
timeout = 40 * time.Second // Spot 每 20s ping,40s 足够
|
||||||
|
} else {
|
||||||
|
timeout = 6 * time.Minute // Futures 每 3 分钟 ping
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastTime != nil && time.Since(*lastTime) > timeout {
|
||||||
|
log.Warnf("检测到假死连接 key:%s type:%v, 距离上次通信: %v, 触发重连", wm.apiKey, wm.wsType, time.Since(*lastTime))
|
||||||
|
wm.triggerReconnect(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主动心跳发送机制
|
||||||
|
func (wm *BinanceWebSocketManager) startPingLoop(ctx context.Context) {
|
||||||
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
if wm.isStopped {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := wm.ws.WriteMessage(websocket.PingMessage, []byte("ping"))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("主动 Ping Binance 失败:", err)
|
||||||
|
wm.triggerReconnect(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 定期删除listenkey 并重启ws
|
// 定期删除listenkey 并重启ws
|
||||||
func (wm *BinanceWebSocketManager) startListenKeyRenewal(ctx context.Context, listenKey string) {
|
// func (wm *BinanceWebSocketManager) startListenKeyRenewal(ctx context.Context, listenKey string) {
|
||||||
time.Sleep(30 * time.Minute)
|
// time.Sleep(30 * time.Minute)
|
||||||
|
|
||||||
select {
|
// select {
|
||||||
case <-ctx.Done():
|
// case <-ctx.Done():
|
||||||
return
|
// return
|
||||||
default:
|
// default:
|
||||||
if err := wm.deleteListenKey(listenKey); err != nil {
|
// if err := wm.deleteListenKey(listenKey); err != nil {
|
||||||
log.Error("Failed to renew listenKey: ,type:%v key: %s", wm.wsType, wm.apiKey, err)
|
// log.Error("Failed to renew listenKey: ,type:%v key: %s", wm.wsType, wm.apiKey, err)
|
||||||
} else {
|
// } else {
|
||||||
log.Debug("Successfully delete listenKey")
|
// log.Debug("Successfully delete listenKey")
|
||||||
wm.reconnect <- struct{}{}
|
// wm.triggerReconnect()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// ticker := time.NewTicker(5 * time.Minute)
|
// }
|
||||||
// defer ticker.Stop()
|
|
||||||
|
|
||||||
// for {
|
|
||||||
// select {
|
|
||||||
// case <-ticker.C:
|
|
||||||
// if wm.isStopped {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if err := wm.deleteListenKey(listenKey); err != nil {
|
|
||||||
// log.Error("Failed to renew listenKey: ,type:%v key: %s", wm.wsType, wm.apiKey, err)
|
|
||||||
// } else {
|
|
||||||
// log.Debug("Successfully delete listenKey")
|
|
||||||
// wm.reconnect <- struct{}{}
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// case <-ctx.Done():
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定时续期
|
// 定时续期
|
||||||
func (wm *BinanceWebSocketManager) startListenKeyRenewal2(ctx context.Context) {
|
func (wm *BinanceWebSocketManager) startListenKeyRenewal2(ctx context.Context) {
|
||||||
@ -527,49 +673,34 @@ func (wm *BinanceWebSocketManager) startListenKeyRenewal2(ctx context.Context) {
|
|||||||
/*
|
/*
|
||||||
删除listenkey
|
删除listenkey
|
||||||
*/
|
*/
|
||||||
func (wm *BinanceWebSocketManager) deleteListenKey(listenKey string) error {
|
// func (wm *BinanceWebSocketManager) deleteListenKey(listenKey string) error {
|
||||||
client, err := wm.createBinanceClient()
|
// client, err := wm.createBinanceClient()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
var resp []byte
|
// var resp []byte
|
||||||
|
|
||||||
switch wm.wsType {
|
// switch wm.wsType {
|
||||||
case 0:
|
// case 0:
|
||||||
path := fmt.Sprintf("/api/v3/userDataStream")
|
// path := fmt.Sprintf("/api/v3/userDataStream")
|
||||||
params := map[string]interface{}{
|
// params := map[string]interface{}{
|
||||||
"listenKey": listenKey,
|
// "listenKey": listenKey,
|
||||||
}
|
// }
|
||||||
resp, _, err = client.SendSpotRequestByKey(path, "DELETE", params)
|
// resp, _, err = client.SendSpotRequestByKey(path, "DELETE", params)
|
||||||
|
|
||||||
log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
// log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
||||||
case 1:
|
// case 1:
|
||||||
resp, _, err = client.SendFuturesRequestByKey("/fapi/v1/listenKey", "DELETE", nil)
|
// resp, _, err = client.SendFuturesRequestByKey("/fapi/v1/listenKey", "DELETE", nil)
|
||||||
log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
// log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
||||||
default:
|
// default:
|
||||||
return errors.New("unknown ws type")
|
// return errors.New("unknown ws type")
|
||||||
}
|
// }
|
||||||
|
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (wm *BinanceWebSocketManager) renewListenKey(listenKey string) error {
|
func (wm *BinanceWebSocketManager) renewListenKey(listenKey string) error {
|
||||||
// payloadParam := map[string]interface{}{
|
|
||||||
// "listenKey": listenKey,
|
|
||||||
// "apiKey": wm.apiKey,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// params := map[string]interface{}{
|
|
||||||
// "id": getUUID(),
|
|
||||||
// "method": "userDataStream.ping",
|
|
||||||
// "params": payloadParam,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if err := wm.ws.WriteJSON(params); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// wm.ws.WriteJSON()
|
|
||||||
client, err := wm.createBinanceClient()
|
client, err := wm.createBinanceClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user