1撤销限价后 市价分单
This commit is contained in:
@ -52,11 +52,11 @@ func (e LineApiUser) GetPage(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
for i, apiUser := range list {
|
||||
if apiUser.UserId <= 0 {
|
||||
list[i].OpenStatus = 1
|
||||
}
|
||||
}
|
||||
// for i, apiUser := range list {
|
||||
// if apiUser.UserId <= 0 {
|
||||
// list[i].OpenStatus = 1
|
||||
// }
|
||||
// }
|
||||
|
||||
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||
}
|
||||
|
||||
225
app/admin/apis/line_api_user_group.go
Normal file
225
app/admin/apis/line_api_user_group.go
Normal file
@ -0,0 +1,225 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"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/response"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
)
|
||||
|
||||
type LineApiUserGroup struct {
|
||||
api.Api
|
||||
}
|
||||
|
||||
// GetPage 获取api用户分组列表
|
||||
// @Summary 获取api用户分组列表
|
||||
// @Description 获取api用户分组列表
|
||||
// @Tags api用户分组
|
||||
// @Param groupName query string false "分组名称"
|
||||
// @Param pageSize query int false "页条数"
|
||||
// @Param pageIndex query int false "页码"
|
||||
// @Success 200 {object} response.Response{data=response.Page{list=[]models.LineApiUserGroup}} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-api-user-group [get]
|
||||
// @Security Bearer
|
||||
func (e LineApiUserGroup) GetPage(c *gin.Context) {
|
||||
req := dto.LineApiUserGroupGetPageReq{}
|
||||
s := service.LineApiUserGroup{}
|
||||
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
|
||||
}
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
list := make([]models.LineApiUserGroup, 0)
|
||||
var count int64
|
||||
|
||||
err = s.GetPage(&req, p, &list, &count)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取api用户分组失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||
}
|
||||
|
||||
// Get 获取api用户分组
|
||||
// @Summary 获取api用户分组
|
||||
// @Description 获取api用户分组
|
||||
// @Tags api用户分组
|
||||
// @Param id path int false "id"
|
||||
// @Success 200 {object} response.Response{data=models.LineApiUserGroup} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-api-user-group/{id} [get]
|
||||
// @Security Bearer
|
||||
func (e LineApiUserGroup) Get(c *gin.Context) {
|
||||
req := dto.LineApiUserGroupGetReq{}
|
||||
s := service.LineApiUserGroup{}
|
||||
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
|
||||
}
|
||||
var object models.LineApiUserGroup
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
err = s.Get(&req, p, &object)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取api用户分组失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(object, "查询成功")
|
||||
}
|
||||
|
||||
// Insert 创建api用户分组
|
||||
// @Summary 创建api用户分组
|
||||
// @Description 创建api用户分组
|
||||
// @Tags api用户分组
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param data body dto.LineApiUserGroupInsertReq true "data"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
||||
// @Router /api/v1/line-api-user-group [post]
|
||||
// @Security Bearer
|
||||
func (e LineApiUserGroup) Insert(c *gin.Context) {
|
||||
req := dto.LineApiUserGroupInsertReq{}
|
||||
s := service.LineApiUserGroup{}
|
||||
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.SetCreateBy(user.GetUserId(c))
|
||||
|
||||
if err := req.Valid(); err != nil {
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Insert(&req)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("创建api用户分组失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(req.GetId(), "创建成功")
|
||||
}
|
||||
|
||||
// Update 修改api用户分组
|
||||
// @Summary 修改api用户分组
|
||||
// @Description 修改api用户分组
|
||||
// @Tags api用户分组
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param id path int true "id"
|
||||
// @Param data body dto.LineApiUserGroupUpdateReq true "body"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
||||
// @Router /api/v1/line-api-user-group/{id} [put]
|
||||
// @Security Bearer
|
||||
func (e LineApiUserGroup) Update(c *gin.Context) {
|
||||
req := dto.LineApiUserGroupUpdateReq{}
|
||||
s := service.LineApiUserGroup{}
|
||||
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)
|
||||
|
||||
if err := req.Valid(); err != nil {
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Update(&req, p)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("修改api用户分组失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(req.GetId(), "修改成功")
|
||||
}
|
||||
|
||||
// Delete 删除api用户分组
|
||||
// @Summary 删除api用户分组
|
||||
// @Description 删除api用户分组
|
||||
// @Tags api用户分组
|
||||
// @Param data body dto.LineApiUserGroupDeleteReq true "body"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
||||
// @Router /api/v1/line-api-user-group [delete]
|
||||
// @Security Bearer
|
||||
func (e LineApiUserGroup) Delete(c *gin.Context) {
|
||||
s := service.LineApiUserGroup{}
|
||||
req := dto.LineApiUserGroupDeleteReq{}
|
||||
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("删除api用户分组失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(req.GetId(), "删除成功")
|
||||
}
|
||||
|
||||
func (e LineApiUserGroup) GetOptions(c *gin.Context) {
|
||||
s := service.LineApiUserGroup{}
|
||||
req := dto.LineApiUserGroupGetOptionsReq{}
|
||||
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
|
||||
}
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
var datas []dto.LineApiUserGroupOptions
|
||||
if err = s.GetOptions(&req, &datas, p); err != nil {
|
||||
e.Error(500, err, "")
|
||||
return
|
||||
}
|
||||
e.OK(datas, "查询成功")
|
||||
}
|
||||
@ -296,23 +296,27 @@ func (e LinePreOrder) AddPreOrder(c *gin.Context) {
|
||||
req.SetCreateBy(userId)
|
||||
errs := make([]error, 0)
|
||||
errStr := make([]string, 0)
|
||||
var msg string
|
||||
var tickerSymbol string
|
||||
if req.SymbolType == global.SYMBOL_SPOT {
|
||||
tickerSymbol = helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
|
||||
} else {
|
||||
tickerSymbol = helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val()
|
||||
}
|
||||
s.AddPreOrder(&req, p, &errs, tickerSymbol)
|
||||
if err := s.AddPreOrderCheck(&req, p, &errs, tickerSymbol); err != nil {
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
//e.Logger.Error(err)
|
||||
for _, err2 := range errs {
|
||||
errStr = append(errStr, err2.Error())
|
||||
}
|
||||
e.Error(500, nil, strings.Join(errStr, ","))
|
||||
return
|
||||
|
||||
msg = strings.Join(errStr, ",")
|
||||
}
|
||||
e.OK(nil, "操作成功")
|
||||
|
||||
e.OK(msg, "操作成功")
|
||||
}
|
||||
|
||||
// // 手动加仓
|
||||
@ -368,22 +372,26 @@ func (e LinePreOrder) BatchAddOrder(c *gin.Context) {
|
||||
errs := make([]error, 0)
|
||||
errStr := make([]string, 0)
|
||||
userId := user.GetUserId(c)
|
||||
|
||||
var msg string
|
||||
if userId <= 0 {
|
||||
e.Error(500, nil, "用户不存在")
|
||||
return
|
||||
}
|
||||
req.SetCreateBy(userId)
|
||||
s.AddBatchPreOrder(&req, p, &errs)
|
||||
if err := s.AddBatchPreOrder(&req, p, &errs); err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
//e.Logger.Error(err)
|
||||
for _, err2 := range errs {
|
||||
errStr = append(errStr, err2.Error())
|
||||
}
|
||||
e.Error(500, nil, strings.Join(errStr, ","))
|
||||
return
|
||||
msg = strings.Join(errStr, ",")
|
||||
}
|
||||
e.OK(nil, "操作成功")
|
||||
e.OK(msg, "操作成功")
|
||||
}
|
||||
|
||||
// QuickAddPreOrder 模板快速下单
|
||||
@ -409,22 +417,27 @@ func (e LinePreOrder) QuickAddPreOrder(c *gin.Context) {
|
||||
errs := make([]error, 0)
|
||||
errStr := make([]string, 0)
|
||||
userId := user.GetUserId(c)
|
||||
var msg string
|
||||
|
||||
if userId <= 0 {
|
||||
e.Error(500, nil, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
err = s.QuickAddPreOrder(&req, p, userId, &errs)
|
||||
if err := s.QuickAddPreOrder(&req, p, userId, &errs); err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
//e.Logger.Error(err)
|
||||
for _, err2 := range errs {
|
||||
errStr = append(errStr, err2.Error())
|
||||
}
|
||||
e.Error(500, nil, strings.Join(errStr, ","))
|
||||
return
|
||||
|
||||
msg = strings.Join(errStr, ",")
|
||||
}
|
||||
e.OK(nil, "操作成功")
|
||||
e.OK(msg, "操作成功")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -113,6 +113,7 @@ func (e SysConfig) Insert(c *gin.Context) {
|
||||
e.Error(500, err, "创建失败")
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(req.GetId(), "创建成功")
|
||||
}
|
||||
|
||||
|
||||
31
app/admin/models/line_api_user_group.go
Normal file
31
app/admin/models/line_api_user_group.go
Normal file
@ -0,0 +1,31 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"go-admin/common/models"
|
||||
)
|
||||
|
||||
type LineApiUserGroup struct {
|
||||
models.Model
|
||||
|
||||
ExchangeType string `json:"exchangeType" gorm:"type:varchar(30);comment:交易所"`
|
||||
GroupName string `json:"groupName" gorm:"type:varchar(30);comment:分组名称"`
|
||||
UserId string `json:"userId" gorm:"type:text;comment:用户ids 逗号分割"`
|
||||
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"`
|
||||
UserIds []int `json:"userIds" gorm:"-"`
|
||||
Count int `json:"count" gorm:"-"`
|
||||
models.ModelTime
|
||||
models.ControlBy
|
||||
}
|
||||
|
||||
func (LineApiUserGroup) TableName() string {
|
||||
return "line_api_user_group"
|
||||
}
|
||||
|
||||
func (e *LineApiUserGroup) Generate() models.ActiveRecord {
|
||||
o := *e
|
||||
return &o
|
||||
}
|
||||
|
||||
func (e *LineApiUserGroup) GetId() interface{} {
|
||||
return e.Id
|
||||
}
|
||||
@ -40,6 +40,7 @@ type LinePreOrder struct {
|
||||
ChildNum int64 `json:"child_num" gorm:"->"`
|
||||
AddPositionStatus int `json:"add_position_status" gorm:"->"`
|
||||
ReduceStatus int `json:"reduce_status" gorm:"->"`
|
||||
Childs []LinePreOrder `json:"childs" gorm:"foreignKey:pid;references:id"`
|
||||
// LinePreOrder 线上预埋单\
|
||||
models.ModelTime
|
||||
models.ControlBy
|
||||
|
||||
29
app/admin/router/line_api_user_group.go
Normal file
29
app/admin/router/line_api_user_group.go
Normal file
@ -0,0 +1,29 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
|
||||
"go-admin/app/admin/apis"
|
||||
"go-admin/common/actions"
|
||||
"go-admin/common/middleware"
|
||||
)
|
||||
|
||||
func init() {
|
||||
routerCheckRole = append(routerCheckRole, registerLineApiUserGroupRouter)
|
||||
}
|
||||
|
||||
// registerLineApiUserGroupRouter
|
||||
func registerLineApiUserGroupRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||
api := apis.LineApiUserGroup{}
|
||||
r := v1.Group("/line-api-user-group").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
||||
{
|
||||
r.GET("", actions.PermissionAction(), api.GetPage)
|
||||
r.GET("/:id", actions.PermissionAction(), api.Get)
|
||||
r.POST("", api.Insert)
|
||||
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
||||
r.DELETE("", api.Delete)
|
||||
|
||||
r.GET("/options", actions.PermissionAction(), api.GetOptions)
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import (
|
||||
type LineApiUserGetPageReq struct {
|
||||
dto.Pagination `search:"-"`
|
||||
ExchangeType string `form:"exchangeType" search:"-"`
|
||||
OpenStatus *int `form:"openStatus" search:"-" comment:"开启状态 0-关闭 1-开启"`
|
||||
LineApiUserOrder
|
||||
}
|
||||
|
||||
|
||||
151
app/admin/service/dto/line_api_user_group.go
Normal file
151
app/admin/service/dto/line_api_user_group.go
Normal file
@ -0,0 +1,151 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/dto"
|
||||
common "go-admin/common/models"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LineApiUserGroupGetPageReq struct {
|
||||
dto.Pagination `search:"-"`
|
||||
GroupName string `form:"groupName" search:"type:contains;column:group_name;table:line_api_user_group" comment:"分组名称"`
|
||||
LineApiUserGroupOrder
|
||||
}
|
||||
|
||||
type LineApiUserGroupOrder struct {
|
||||
Id string `form:"idOrder" search:"type:order;column:id;table:line_api_user_group"`
|
||||
GroupName string `form:"groupNameOrder" search:"type:order;column:group_name;table:line_api_user_group"`
|
||||
UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:line_api_user_group"`
|
||||
Status string `form:"statusOrder" search:"type:order;column:status;table:line_api_user_group"`
|
||||
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:line_api_user_group"`
|
||||
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:line_api_user_group"`
|
||||
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_api_user_group"`
|
||||
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_api_user_group"`
|
||||
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_api_user_group"`
|
||||
}
|
||||
|
||||
func (m *LineApiUserGroupGetPageReq) GetNeedSearch() interface{} {
|
||||
return *m
|
||||
}
|
||||
|
||||
type LineApiUserGroupInsertReq struct {
|
||||
Id int `json:"-" comment:"组件"` // 组件
|
||||
ExchangeType string `json:"exchangeType" comment:"交易类型"`
|
||||
|
||||
GroupName string `json:"groupName" comment:"分组名称"`
|
||||
UserId string `json:"userId" comment:"用户ids 逗号分割"`
|
||||
UserIds []int `json:"userIds" comment:"用户ids"`
|
||||
Status int `json:"status" comment:"状态 1-启用 2-禁用"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineApiUserGroupInsertReq) Valid() error {
|
||||
if s.ExchangeType == "" {
|
||||
return errors.New("交易类型不能为空")
|
||||
}
|
||||
|
||||
if s.GroupName == "" {
|
||||
return errors.New("分组名称不能为空")
|
||||
}
|
||||
|
||||
if len(s.UserIds) == 0 {
|
||||
return errors.New("用户不能为空")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LineApiUserGroupInsertReq) Generate(model *models.LineApiUserGroup) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
strIds := make([]string, len(s.UserIds))
|
||||
for i, id := range s.UserIds {
|
||||
strIds[i] = strconv.Itoa(id)
|
||||
}
|
||||
model.GroupName = s.GroupName
|
||||
model.ExchangeType = s.ExchangeType
|
||||
model.UserId = strings.Join(strIds, ",")
|
||||
model.Status = s.Status
|
||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||
}
|
||||
|
||||
func (s *LineApiUserGroupInsertReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
type LineApiUserGroupUpdateReq struct {
|
||||
Id int `uri:"id" comment:"组件"` // 组件
|
||||
ExchangeType string `json:"exchangeType" comment:"交易类型"`
|
||||
GroupName string `json:"groupName" comment:"分组名称"`
|
||||
UserId string `json:"userId" comment:"用户ids 逗号分割"`
|
||||
UserIds []int `json:"userIds" comment:"用户ids"`
|
||||
Status int `json:"status" comment:"状态 1-启用 2-禁用"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineApiUserGroupUpdateReq) Valid() error {
|
||||
if s.ExchangeType == "" {
|
||||
return errors.New("交易类型不能为空")
|
||||
}
|
||||
|
||||
if s.GroupName == "" {
|
||||
return errors.New("分组名称不能为空")
|
||||
}
|
||||
|
||||
if len(s.UserIds) == 0 {
|
||||
return errors.New("用户不能为空")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (s *LineApiUserGroupUpdateReq) Generate(model *models.LineApiUserGroup) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
strIds := make([]string, len(s.UserIds))
|
||||
for i, id := range s.UserIds {
|
||||
strIds[i] = strconv.Itoa(id)
|
||||
}
|
||||
model.GroupName = s.GroupName
|
||||
model.ExchangeType = s.ExchangeType
|
||||
model.UserId = strings.Join(strIds, ",")
|
||||
model.Status = s.Status
|
||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||
}
|
||||
|
||||
func (s *LineApiUserGroupUpdateReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineApiUserGroupGetReq 功能获取请求参数
|
||||
type LineApiUserGroupGetReq struct {
|
||||
Id int `uri:"id"`
|
||||
}
|
||||
|
||||
func (s *LineApiUserGroupGetReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineApiUserGroupDeleteReq 功能删除请求参数
|
||||
type LineApiUserGroupDeleteReq struct {
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
|
||||
func (s *LineApiUserGroupDeleteReq) GetId() interface{} {
|
||||
return s.Ids
|
||||
}
|
||||
|
||||
type LineApiUserGroupGetOptionsReq struct {
|
||||
ExchangeType string `form:"exchangeType"`
|
||||
}
|
||||
|
||||
type LineApiUserGroupOptions struct {
|
||||
Id int `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Value []int `json:"value"`
|
||||
Disabled bool `json:"disabled"`
|
||||
}
|
||||
@ -187,6 +187,8 @@ type LineAddPreOrderReq struct {
|
||||
OrderType int `json:"order_type"` //订单类型
|
||||
Symbol string `json:"symbol"` //交易对
|
||||
ApiUserId string `json:"api_id" ` //下单用户
|
||||
ApiIdType int `json:"api_id_type"` //下单用户类型
|
||||
ApiUserGroupId int `json:"api_user_group_id"` //下单用户组
|
||||
Site string `json:"site" ` //购买方向
|
||||
BuyPrice string `json:"buy_price" vd:"$>0"` //购买金额 U
|
||||
PricePattern string `json:"price_pattern"` //价格模式
|
||||
@ -368,6 +370,8 @@ type LineBatchAddPreOrderReq struct {
|
||||
SymbolGroupId string `json:"symbol_group_id"` //交易对组id
|
||||
Symbol string `json:"symbol"` //交易对
|
||||
ApiUserId string `json:"api_id"` //下单用户
|
||||
ApiIdType int `json:"api_id_type"` //下单用户类型
|
||||
ApiUserGroupId int `json:"api_user_group_id"` //下单用户组
|
||||
Site string `json:"site"` //购买方向
|
||||
BuyPrice string `json:"buy_price"` //购买金额 U
|
||||
PricePattern string `json:"price_pattern"` //价格模式
|
||||
|
||||
@ -40,6 +40,10 @@ func (e *LineApiUser) GetPage(c *dto.LineApiUserGetPageReq, p *actions.DataPermi
|
||||
query.Where("exchange_type = ?", c.ExchangeType)
|
||||
}
|
||||
|
||||
if c.OpenStatus != nil {
|
||||
query.Where("open_status = ?", c.OpenStatus)
|
||||
}
|
||||
|
||||
err = query.
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
@ -188,6 +192,7 @@ func OpenUserBinanceWebsocket(item models.LineApiUser) {
|
||||
func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermission) error {
|
||||
var err error
|
||||
var data = models.LineApiUser{}
|
||||
|
||||
e.Orm.Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
@ -195,7 +200,11 @@ func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermiss
|
||||
|
||||
c.Generate(&data)
|
||||
|
||||
db := e.Orm.Save(&data)
|
||||
updateGroups, err := e.GetUpdateGroups(c.Id)
|
||||
|
||||
//事务
|
||||
err = e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||
db := tx.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineApiUserService Save error:%s \r\n", err)
|
||||
return err
|
||||
@ -204,6 +213,19 @@ func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermiss
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
|
||||
if len(updateGroups) > 0 {
|
||||
if err := tx.Save(&updateGroups).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.saveCache(data)
|
||||
|
||||
//旧key和新的key不一样,则关闭旧的websocket
|
||||
@ -224,6 +246,24 @@ func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermiss
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LineApiUser) GetUpdateGroups(apiUserId int) ([]models.LineApiUserGroup, error) {
|
||||
updateGroups := make([]models.LineApiUserGroup, 0)
|
||||
var groups []models.LineApiUserGroup
|
||||
if err := e.Orm.Model(models.LineApiUserGroup{}).Where(" concat(',',user_id,',') like ? ", fmt.Sprintf("%%,%v,%%", apiUserId)).Find(&groups).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delId := fmt.Sprintf(",%v,", apiUserId)
|
||||
for _, group := range groups {
|
||||
|
||||
userId := strings.ReplaceAll(("," + group.UserId + ","), delId, "")
|
||||
group.UserId = strings.Trim(userId, ",")
|
||||
|
||||
updateGroups = append(updateGroups, group)
|
||||
}
|
||||
return updateGroups, nil
|
||||
}
|
||||
|
||||
// Remove 删除LineApiUser
|
||||
func (e *LineApiUser) Remove(d *dto.LineApiUserDeleteReq, p *actions.DataPermission) error {
|
||||
var data models.LineApiUser
|
||||
@ -347,3 +387,30 @@ func (e *LineApiUser) GetMainUser(req *dto.GetMainUserReq, list *[]models.LineAp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitCache 初始化缓存
|
||||
func (e *LineApiUser) InitCache() error {
|
||||
var items []models.LineApiUser
|
||||
|
||||
if err := e.Orm.Model(&models.LineApiUser{}).Find(&items).Error; err != nil {
|
||||
e.Log.Errorf("LineApiUserService error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
e.saveCache(item)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetActiveApis 获取激活的api
|
||||
func (e *LineApiUser) GetActiveApis(apiIds []int) ([]int, error) {
|
||||
result := make([]int, 0)
|
||||
|
||||
if err := e.Orm.Model(&models.LineApiUser{}).Where("open_status = 1 and id IN ?", apiIds).Pluck("id", &result).Error; err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
165
app/admin/service/line_api_user_group.go
Normal file
165
app/admin/service/line_api_user_group.go
Normal file
@ -0,0 +1,165 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
cDto "go-admin/common/dto"
|
||||
"go-admin/pkg/utility"
|
||||
)
|
||||
|
||||
type LineApiUserGroup struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// GetPage 获取LineApiUserGroup列表
|
||||
func (e *LineApiUserGroup) GetPage(c *dto.LineApiUserGroupGetPageReq, p *actions.DataPermission, list *[]models.LineApiUserGroup, count *int64) error {
|
||||
var err error
|
||||
var data models.LineApiUserGroup
|
||||
|
||||
err = e.Orm.Model(&data).
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineApiUserGroupService GetPage error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for index := range *list {
|
||||
userIds := strings.Split((*list)[index].UserId, ",")
|
||||
var filteredUserIds []string
|
||||
for _, id := range userIds {
|
||||
if id != "" {
|
||||
filteredUserIds = append(filteredUserIds, id)
|
||||
}
|
||||
}
|
||||
|
||||
(*list)[index].Count = len(filteredUserIds)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取LineApiUserGroup对象
|
||||
func (e *LineApiUserGroup) Get(d *dto.LineApiUserGroupGetReq, p *actions.DataPermission, model *models.LineApiUserGroup) error {
|
||||
var data models.LineApiUserGroup
|
||||
|
||||
err := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
First(model, d.GetId()).Error
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = errors.New("查看对象不存在或无权查看")
|
||||
e.Log.Errorf("Service GetLineApiUserGroup error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
e.Log.Errorf("db error:%s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
userIds := []int{}
|
||||
|
||||
for _, userId := range strings.Split(model.UserId, ",") {
|
||||
if userId == "" {
|
||||
continue
|
||||
}
|
||||
userIds = append(userIds, utility.ToInt(userId))
|
||||
}
|
||||
model.UserIds = userIds
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert 创建LineApiUserGroup对象
|
||||
func (e *LineApiUserGroup) Insert(c *dto.LineApiUserGroupInsertReq) error {
|
||||
var err error
|
||||
var data models.LineApiUserGroup
|
||||
c.Generate(&data)
|
||||
err = e.Orm.Create(&data).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineApiUserGroupService Insert error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update 修改LineApiUserGroup对象
|
||||
func (e *LineApiUserGroup) Update(c *dto.LineApiUserGroupUpdateReq, p *actions.DataPermission) error {
|
||||
var err error
|
||||
var data = models.LineApiUserGroup{}
|
||||
e.Orm.Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
c.Generate(&data)
|
||||
|
||||
db := e.Orm.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineApiUserGroupService Save error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove 删除LineApiUserGroup
|
||||
func (e *LineApiUserGroup) Remove(d *dto.LineApiUserGroupDeleteReq, p *actions.DataPermission) error {
|
||||
var data models.LineApiUserGroup
|
||||
|
||||
db := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).Delete(&data, d.GetId())
|
||||
if err := db.Error; err != nil {
|
||||
e.Log.Errorf("Service RemoveLineApiUserGroup error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权删除该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LineApiUserGroup) GetOptions(req *dto.LineApiUserGroupGetOptionsReq, data *[]dto.LineApiUserGroupOptions, p *actions.DataPermission) error {
|
||||
var err error
|
||||
var items []models.LineApiUserGroup
|
||||
|
||||
e.Orm.Model(&models.LineApiUserGroup{}).
|
||||
Where("exchange_type =?", req.ExchangeType).
|
||||
Scopes(
|
||||
actions.Permission(models.LineApiUserGroup{}.TableName(), p),
|
||||
).Find(&items)
|
||||
|
||||
for _, item := range items {
|
||||
ids := []int{}
|
||||
userIds := strings.Split(item.UserId, ",")
|
||||
|
||||
for _, id := range userIds {
|
||||
val, _ := strconv.Atoi(id)
|
||||
ids = append(ids, val)
|
||||
}
|
||||
|
||||
*data = append(*data, dto.LineApiUserGroupOptions{
|
||||
Label: item.GroupName,
|
||||
Id: item.Id,
|
||||
Value: ids,
|
||||
Disabled: item.Status != 1,
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -374,9 +374,34 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddPreOrder 单个添加
|
||||
func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataPermission, errs *[]error, tickerSymbol string) error {
|
||||
// 单个新增订单
|
||||
func (e *LinePreOrder) AddPreOrderCheck(req *dto.LineAddPreOrderReq, p *actions.DataPermission, errs *[]error, tickerSymbol string) error {
|
||||
apiUserService := LineApiUser{Service: e.Service}
|
||||
apiIds := []int{}
|
||||
apiUserIds := strings.Split(req.ApiUserId, ",")
|
||||
for _, v := range apiUserIds {
|
||||
apiId, _ := strconv.Atoi(v)
|
||||
apiIds = append(apiIds, apiId)
|
||||
}
|
||||
|
||||
activeApiIds, _ := apiUserService.GetActiveApis(apiIds)
|
||||
|
||||
if len(activeApiIds) == 0 {
|
||||
*errs = append(*errs, errors.New("api用户不存在或未激活"))
|
||||
} else {
|
||||
for _, item := range apiIds {
|
||||
if !utility.ContainsInt(activeApiIds, item) {
|
||||
*errs = append(*errs, fmt.Errorf("api用户未激活:%v", item))
|
||||
}
|
||||
}
|
||||
e.AddPreOrder(req, activeApiIds, p, errs, tickerSymbol)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddPreOrder 单个添加
|
||||
func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, apiUserIds []int, p *actions.DataPermission, errs *[]error, tickerSymbol string) error {
|
||||
if req.SaveTemplate == "2" || req.SaveTemplate == "1" { //2 = 只保存模板 1= 保存模板并下单
|
||||
var templateLog dto.LineAddPreOrderReq
|
||||
copier.Copy(&templateLog, req)
|
||||
@ -412,7 +437,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
|
||||
for _, id := range apiUserIds {
|
||||
if req.Site == "SELL" && req.SymbolType == global.SYMBOL_SPOT {
|
||||
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 现货不支持卖出操作", id, req.Symbol))
|
||||
*errs = append(*errs, fmt.Errorf("api_id:%v 获取交易对:%s 现货不支持卖出操作", id, req.Symbol))
|
||||
continue
|
||||
}
|
||||
var AddOrder models.LinePreOrder
|
||||
@ -500,7 +525,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
continue
|
||||
}
|
||||
AddOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
|
||||
AddOrder.ApiId, _ = strconv.Atoi(id)
|
||||
AddOrder.ApiId = id
|
||||
AddOrder.Symbol = req.Symbol
|
||||
AddOrder.QuoteSymbol = symbolInfo.QuoteAsset
|
||||
AddOrder.Pid = 0
|
||||
@ -651,14 +676,20 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
profitOrder.Rate = "0"
|
||||
} else {
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
profitOrder.Site = "SELL"
|
||||
// profitOrder.Site = "SELL"
|
||||
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 + utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
profitOrder.Site = "BUY"
|
||||
// profitOrder.Site = "BUY"
|
||||
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 - utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
profitOrder.Rate = req.Profit
|
||||
}
|
||||
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
profitOrder.Site = "SELL"
|
||||
} else {
|
||||
profitOrder.Site = "BUY"
|
||||
}
|
||||
profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
|
||||
profitOrder.Pid = AddOrder.Id
|
||||
profitOrder.OrderType = 1
|
||||
@ -696,15 +727,21 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
stopOrder.Rate = "0"
|
||||
} else {
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
stopOrder.Site = "SELL"
|
||||
// stopOrder.Site = "SELL"
|
||||
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
stopOrder.Site = "BUY"
|
||||
// stopOrder.Site = "BUY"
|
||||
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
|
||||
stopOrder.Rate = req.ReducePriceRatio.String()
|
||||
}
|
||||
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
stopOrder.Site = "SELL"
|
||||
} else {
|
||||
stopOrder.Site = "BUY"
|
||||
}
|
||||
stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
|
||||
stopOrder.Pid = AddOrder.Id
|
||||
stopOrder.MainId = AddOrder.Id
|
||||
@ -1078,10 +1115,34 @@ func (e *LinePreOrder) CheckRepeatOrder(symbolType int, apiUserId, site, baseCoi
|
||||
|
||||
// AddBatchPreOrder 批量添加
|
||||
func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p *actions.DataPermission, errs *[]error) error {
|
||||
// apiIds := []int{}
|
||||
// apiUserIds := strings.Split(batchReq.ApiUserId, ",")
|
||||
// apiUserService := LineApiUser{Service: e.Service}
|
||||
|
||||
// for _, v := range apiUserIds {
|
||||
// apiId, _ := strconv.Atoi(v)
|
||||
// apiIds = append(apiIds, apiId)
|
||||
// }
|
||||
|
||||
// activeIds, err := apiUserService.GetActiveApis(apiIds)
|
||||
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if len(activeIds) == 0 {
|
||||
// return errors.New("没有可用的api")
|
||||
// }
|
||||
|
||||
// for _, v := range apiIds {
|
||||
// if !utility.ContainsInt(activeIds, v) {
|
||||
// *errs = append(*errs, errors.New("api_id:"+strconv.Itoa(v)+"不可用"))
|
||||
// }
|
||||
// }
|
||||
|
||||
if batchReq.SaveTemplate == "2" || batchReq.SaveTemplate == "1" { //2 = 只保存模板 1= 保存模板并下单
|
||||
var templateLog dto.LineBatchAddPreOrderReq
|
||||
copier.Copy(&templateLog, batchReq)
|
||||
//templateLog = *batchReq
|
||||
templateLog.SaveTemplate = "0"
|
||||
templateLog.TemplateName = ""
|
||||
marshal, _ := sonic.Marshal(templateLog)
|
||||
@ -1112,14 +1173,15 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
}
|
||||
|
||||
//脚本次数
|
||||
if batchReq.OrderNum > 0 {
|
||||
// if batchReq.OrderNum > 0 {
|
||||
apiUserIds := strings.Split(batchReq.ApiUserId, ",")
|
||||
var tickerSymbol string
|
||||
if batchReq.SymbolType == global.SYMBOL_SPOT {
|
||||
tickerSymbol = helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
|
||||
} else {
|
||||
tickerSymbol = helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val()
|
||||
}
|
||||
apiUserIds := strings.Split(batchReq.ApiUserId, ",")
|
||||
|
||||
if batchReq.Script == "1" {
|
||||
//scriptLogs := make([]models.LinePreScript, 0)
|
||||
logParams := *batchReq
|
||||
@ -1129,8 +1191,9 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
logParams.SaveTemplate = "0"
|
||||
logParams.TemplateName = ""
|
||||
logParams.Script = ""
|
||||
logParams.ApiUserId = id
|
||||
marshal, _ := sonic.Marshal(logParams)
|
||||
log.ApiId = int64(utility.StringToInt(id))
|
||||
log.ApiId = utility.ToInt64(id)
|
||||
log.ScriptNum = int64(j)
|
||||
log.ScriptParams = string(marshal)
|
||||
log.AdminId = 0
|
||||
@ -1166,7 +1229,6 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
req.ProfitTpSlPriceRatio = batchReq.ProfitTpSlPriceRatio
|
||||
req.Ext = batchReq.Ext
|
||||
req.SymbolType = batchReq.SymbolType
|
||||
// req.StopPrice = batchReq.StopPrice
|
||||
req.ReducePriceRatio = batchReq.ReducePriceRatio
|
||||
req.PriceType = batchReq.PriceType
|
||||
req.CoverType = batchReq.CoverType
|
||||
@ -1177,15 +1239,15 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
req.ReduceTakeProfitRatio = batchReq.ReduceTakeProfitRatio
|
||||
req.CreateBy = batchReq.CreateBy
|
||||
|
||||
e.AddPreOrder(&req, p, errs, tickerSymbol)
|
||||
e.AddPreOrderCheck(&req, p, errs, tickerSymbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
*errs = append(*errs, errors.New("请选择运行次数"))
|
||||
return nil
|
||||
}
|
||||
// } else {
|
||||
// *errs = append(*errs, errors.New("请选择运行次数"))
|
||||
// return nil
|
||||
// }
|
||||
}
|
||||
|
||||
// QuickAddPreOrder 模板快速下单
|
||||
@ -1204,7 +1266,8 @@ func (e *LinePreOrder) QuickAddPreOrder(quickReq *dto.QuickAddPreOrderReq, p *ac
|
||||
} else {
|
||||
tickerSymbol = helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val()
|
||||
}
|
||||
err := e.AddPreOrder(&addPreOrderParams, p, errs, tickerSymbol)
|
||||
err := e.AddPreOrderCheck(&addPreOrderParams, p, errs, tickerSymbol)
|
||||
// err := e.AddPreOrder(&addPreOrderParams, p, errs, tickerSymbol)
|
||||
if err != nil {
|
||||
*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 生成订单失败", addPreOrderParams.ApiUserId, addPreOrderParams.Symbol))
|
||||
continue
|
||||
|
||||
@ -26,7 +26,7 @@ func (e *LineSymbolGroup) GetPage(c *dto.LineSymbolGroupGetPageReq, p *actions.D
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
// actions.Permission(data.TableName(), p),
|
||||
)
|
||||
if c.ExchangeType != "" {
|
||||
query = query.Where("exchange_type = ?", c.ExchangeType)
|
||||
|
||||
@ -718,6 +718,7 @@ func (e *LineUser) ResetPassword(req *dto.LineUserResetPwdReq) int {
|
||||
func (e *LineUser) OpenStatus(req *dto.OpenStatusReq, userId int) int {
|
||||
var apiUser models.LineApiUser
|
||||
user := models.LineUser{}
|
||||
apiUserService := LineApiUser{Service: e.Service}
|
||||
e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser)
|
||||
|
||||
if apiUser.ApiSecret == "" || apiUser.ApiKey == "" {
|
||||
@ -752,10 +753,31 @@ func (e *LineUser) OpenStatus(req *dto.OpenStatusReq, userId int) int {
|
||||
}
|
||||
}
|
||||
|
||||
err := e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Update("open_status", req.Status).Error
|
||||
apiUser.OpenStatus = int64(req.Status)
|
||||
updateGroups, _ := apiUserService.GetUpdateGroups(apiUser.Id)
|
||||
|
||||
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||
if err := e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Update("open_status", req.Status).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Save(&updateGroups).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return statuscode.ServerError
|
||||
}
|
||||
|
||||
val, _ := sonic.MarshalString(apiUser)
|
||||
|
||||
if val != "" {
|
||||
if err := helper.DefaultRedis.SetString(fmt.Sprintf(rediskey.API_USER, userId), val); err != nil {
|
||||
logger.Error("保存api user redis 失败", err)
|
||||
}
|
||||
}
|
||||
|
||||
return statuscode.OK
|
||||
}
|
||||
|
||||
@ -2,11 +2,15 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/const/rediskey"
|
||||
cDto "go-admin/common/dto"
|
||||
"go-admin/common/helper"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
)
|
||||
|
||||
@ -59,6 +63,13 @@ func (e *SysConfig) Insert(c *dto.SysConfigControl) error {
|
||||
e.Log.Errorf("Service InsertSysConfig error:%s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
key := fmt.Sprintf(rediskey.SysConfigKey, c.ConfigKey)
|
||||
content, _ := sonic.MarshalString(data)
|
||||
|
||||
if content != "" {
|
||||
helper.DefaultRedis.SetString(key, content)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -76,7 +87,13 @@ func (e *SysConfig) Update(c *dto.SysConfigControl) error {
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
|
||||
key := fmt.Sprintf(rediskey.SysConfigKey, c.ConfigKey)
|
||||
content, _ := sonic.MarshalString(model)
|
||||
|
||||
if content != "" {
|
||||
helper.DefaultRedis.SetString(key, content)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -133,7 +150,6 @@ func (e *SysConfig) UpdateForSet(c *[]dto.GetSetSysConfigReq) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -143,6 +159,9 @@ func (e *SysConfig) UpdateForSet(c *[]dto.GetSetSysConfigReq) error {
|
||||
func (e *SysConfig) Remove(d *dto.SysConfigDeleteReq) error {
|
||||
var err error
|
||||
var data models.SysConfig
|
||||
keys := make([]string, 0)
|
||||
|
||||
e.Orm.Model(&data).Where("id IN ?", d.Ids).Pluck("config_key", &keys)
|
||||
|
||||
db := e.Orm.Delete(&data, d.Ids)
|
||||
if err = db.Error; err != nil {
|
||||
@ -153,6 +172,14 @@ func (e *SysConfig) Remove(d *dto.SysConfigDeleteReq) error {
|
||||
err = errors.New("无权删除该数据")
|
||||
return err
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
cacheKey := fmt.Sprintf(rediskey.SysConfigKey, key)
|
||||
if err = helper.DefaultRedis.DeleteString(cacheKey); err != nil {
|
||||
e.Log.Errorf("Service RemoveSysConfig error:%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -2,11 +2,12 @@ package jobs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
models2 "go-admin/app/jobs/models"
|
||||
"time"
|
||||
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
models2 "go-admin/app/jobs/models"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
@ -40,6 +41,12 @@ type ExecJob struct {
|
||||
}
|
||||
|
||||
func (e *ExecJob) Run() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Errorf("脚本任务失败:%v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
startTime := time.Now()
|
||||
var obj = jobList[e.InvokeTarget]
|
||||
if obj == nil {
|
||||
|
||||
193
app/jobs/jobs.go
193
app/jobs/jobs.go
@ -26,6 +26,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/driver/mysql"
|
||||
|
||||
@ -158,7 +159,6 @@ func (t AutoPlaceOrder) Exec(arg interface{}) error {
|
||||
}
|
||||
|
||||
func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error {
|
||||
|
||||
str := time.Now().Format(timeFormat) + " [INFO] JobCore ClearLogJob exec success"
|
||||
defer logger.Info(str)
|
||||
var db *gorm.DB
|
||||
@ -188,7 +188,10 @@ func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error {
|
||||
defer lock.Release()
|
||||
limitOrderTimeoutDuration := utility.StringAsInt64(resp.ConfigValue)
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
err := db.Model(&models.LinePreOrder{}).Where("status = '5' AND main_order_type = 'LIMIT' AND order_type in ('4') AND updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).Find(&orders).Error
|
||||
err := db.Model(&models.LinePreOrder{}).
|
||||
Where("status = '5' AND main_order_type = 'LIMIT' AND order_type in ('4') AND updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).
|
||||
Preload("Childs").
|
||||
Find(&orders).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -228,6 +231,7 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
|
||||
for i := 0; i < 3; i++ {
|
||||
err = spotApi.CancelOpenOrderByOrderSn(apiUserinfo, order.Symbol, order.OrderSn)
|
||||
if err == nil || strings.Contains(err.Error(), "该交易对没有订单") {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -237,11 +241,12 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
|
||||
} else {
|
||||
var remainingQuantity decimal.Decimal
|
||||
spotOrder, err := spotApi.GetOrderByOrderSnLoop(order.Symbol, order.OrderSn, apiUserinfo, 4)
|
||||
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 0)
|
||||
|
||||
if err == nil {
|
||||
origQty := utility.StrToDecimal(spotOrder.OrigQuoteOrderQty)
|
||||
origQty := utility.StrToDecimal(spotOrder.OrigQty)
|
||||
excuteQty := utility.StrToDecimal(spotOrder.ExecutedQty)
|
||||
remainingQuantity = origQty.Sub(excuteQty).Abs()
|
||||
remainingQuantity = origQty.Sub(excuteQty).Abs().Truncate(int32(tradeSet.AmountDigit))
|
||||
}
|
||||
|
||||
if remainingQuantity.Cmp(decimal.Zero) <= 0 {
|
||||
@ -249,12 +254,53 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
|
||||
return nil
|
||||
}
|
||||
|
||||
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 0)
|
||||
maxMarketQty := decimal.NewFromFloat(tradeSet.MarketMaxQty).Truncate(int32(tradeSet.AmountDigit))
|
||||
|
||||
// if config.ApplicationConfig.Mode == "dev" {
|
||||
// maxMarketQty = decimal.NewFromFloat(10)
|
||||
// }
|
||||
|
||||
if remainingQuantity.Cmp(maxMarketQty) > 0 && maxMarketQty.Cmp(decimal.Zero) > 0 {
|
||||
multiple := remainingQuantity.Div(maxMarketQty).Abs().IntPart()
|
||||
remainder := remainingQuantity.Mod(maxMarketQty)
|
||||
saveOrders := make([]models.LinePreOrder, 0)
|
||||
|
||||
desc := fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn)
|
||||
|
||||
// 创建 multiple 个订单
|
||||
for i := 0; i < int(multiple); i++ {
|
||||
saveOrders = append(saveOrders, createNewOrder(order, maxMarketQty, desc))
|
||||
}
|
||||
|
||||
// 处理余数
|
||||
if remainder.Cmp(decimal.Zero) > 0 {
|
||||
saveOrders = append(saveOrders, createNewOrder(order, remainder.Truncate(int32(tradeSet.AmountDigit)), desc))
|
||||
}
|
||||
|
||||
if err := db.Create(&saveOrders).Error; err != nil {
|
||||
logger.Errorf("市价订单拆分后保存失败,err:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for index := range saveOrders {
|
||||
newNum := utility.StrToDecimal(saveOrders[index].Num)
|
||||
|
||||
if err := newSpotOrderClosePosition(saveOrders[index], newNum, spotApi, db); err != nil {
|
||||
logger.Errorf("市价订单拆分后保存失败,err:", err)
|
||||
}
|
||||
|
||||
if index == len(saveOrders)-1 {
|
||||
orderCopy := saveOrders[index] // 复制数据
|
||||
|
||||
binanceservice.HandleSpotMarketSliceTakeProfit(db, orderCopy, order.Id, apiUserinfo, tradeSet)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newClientOrderId := snowflakehelper.GetOrderId()
|
||||
|
||||
order.Num = remainingQuantity.Truncate(int32(tradeSet.AmountDigit)).String()
|
||||
order.Desc = fmt.Sprintf("取消限价单,重下市价单源订单号:%s ", order.OrderSn)
|
||||
order.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
|
||||
order.OrderSn = utility.Int64ToString(newClientOrderId)
|
||||
order.MainOrderType = "MARKET"
|
||||
err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn, "main_order_type": order.MainOrderType}).Error
|
||||
|
||||
@ -262,6 +308,18 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
|
||||
logger.Error(fmt.Sprintf("生成新市价单失败 err:%+v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
if err := newSpotOrderClosePosition(order, remainingQuantity, spotApi, db); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 现货市价单平仓
|
||||
func newSpotOrderClosePosition(order models.LinePreOrder, remainingQuantity decimal.Decimal, spotApi binanceservice.SpotRestApi, db *gorm.DB) error {
|
||||
params := binanceservice.OrderPlacementService{
|
||||
ApiId: order.ApiId,
|
||||
Symbol: order.Symbol,
|
||||
@ -271,9 +329,9 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
|
||||
Price: utility.StringToDecimal(order.Price),
|
||||
StopPrice: utility.StrToDecimal(order.Price),
|
||||
Quantity: remainingQuantity,
|
||||
NewClientOrderId: utility.Int64ToString(newClientOrderId),
|
||||
NewClientOrderId: order.OrderSn,
|
||||
}
|
||||
if err := spotApi.OrderPlace(db, params); err != nil {
|
||||
if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil {
|
||||
logger.Error(fmt.Sprintf("重新下市价单失败 err:%+v", err))
|
||||
err := db.Model(&order).Updates(map[string]interface{}{"status": "2", "desc": order.Desc + "err:" + err.Error()}).Error
|
||||
|
||||
@ -282,8 +340,6 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -293,6 +349,7 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
|
||||
for i := 0; i < 3; i++ {
|
||||
err := futApi.CancelFutOrder(apiUserinfo, order.Symbol, order.OrderSn)
|
||||
if err == nil || strings.Contains(err.Error(), "该交易对没有订单") {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -302,11 +359,12 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
|
||||
} else {
|
||||
var remainingQuantity decimal.Decimal
|
||||
spotOrder, err := futApi.GetOrderByOrderSnLoop(order.Symbol, order.OrderSn, apiUserinfo, 4)
|
||||
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 1)
|
||||
|
||||
if err == nil {
|
||||
origQty := utility.StrToDecimal(spotOrder.OrigQty)
|
||||
excuteQty := utility.StrToDecimal(spotOrder.ExecutedQty)
|
||||
remainingQuantity = origQty.Sub(excuteQty).Abs()
|
||||
remainingQuantity = origQty.Sub(excuteQty).Abs().Truncate(int32(tradeSet.AmountDigit))
|
||||
}
|
||||
|
||||
if remainingQuantity.Cmp(decimal.Zero) <= 0 {
|
||||
@ -314,21 +372,98 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
|
||||
return nil
|
||||
}
|
||||
|
||||
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 0)
|
||||
qty := decimal.NewFromFloat(tradeSet.MarketMaxQty)
|
||||
maxMarketQty := qty.Truncate(int32(tradeSet.AmountDigit))
|
||||
|
||||
// if config.ApplicationConfig.Mode == "dev" {
|
||||
// maxMarketQty = decimal.NewFromFloat(10)
|
||||
// }
|
||||
|
||||
//数量超过最大数量,则拆单
|
||||
if remainingQuantity.Cmp(maxMarketQty) > 0 && maxMarketQty.Cmp(decimal.Zero) > 0 {
|
||||
multiple := remainingQuantity.Div(maxMarketQty).Abs().IntPart()
|
||||
remainder := remainingQuantity.Mod(maxMarketQty)
|
||||
saveOrders := make([]models.LinePreOrder, 0)
|
||||
|
||||
desc := fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn)
|
||||
|
||||
// 创建 multiple 个订单
|
||||
for i := 0; i < int(multiple); i++ {
|
||||
saveOrders = append(saveOrders, createNewOrder(order, maxMarketQty, desc))
|
||||
}
|
||||
|
||||
// 处理余数
|
||||
if remainder.Cmp(decimal.Zero) > 0 {
|
||||
saveOrders = append(saveOrders, createNewOrder(order, remainder.Truncate(int32(tradeSet.AmountDigit)), desc))
|
||||
}
|
||||
|
||||
if err := db.Create(&saveOrders).Error; err != nil {
|
||||
logger.Errorf("市价订单拆分后保存失败,err:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for index := range saveOrders {
|
||||
newNum := utility.StrToDecimal(saveOrders[index].Num)
|
||||
|
||||
if err := newOrderClosePosition(saveOrders[index], futApi, newNum.Truncate(int32(tradeSet.AmountDigit)), apiUserinfo, db); err != nil {
|
||||
logger.Errorf("市价单拆分后下单失败 orderSn:%s, err:%s", saveOrders[index].OrderSn, err)
|
||||
}
|
||||
|
||||
if index == len(saveOrders)-1 {
|
||||
orderCopy := saveOrders[index] // 复制数据
|
||||
binanceservice.HandleMarketSliceTakeProfit(db, orderCopy, order.Id, apiUserinfo, tradeSet)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newClientOrderId := snowflakehelper.GetOrderId()
|
||||
order.Num = remainingQuantity.Truncate(int32(tradeSet.AmountDigit)).String()
|
||||
order.Desc = fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn)
|
||||
order.OrderSn = utility.Int64ToString(newClientOrderId)
|
||||
|
||||
// var newOrder models.LinePreOrder
|
||||
// copier.Copy(&newOrder, order)
|
||||
// newOrder.Id = 0
|
||||
// newOrder.OrderSn = utility.Int64ToString(newClientOrderId)
|
||||
// newOrder.CreatedAt = time.Now()
|
||||
// newOrder.MainOrderType = "MARKET"
|
||||
// err = db.Model(&models.LinePreOrder{}).Create(&newOrder).Error
|
||||
err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn}).Error
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("生成合约新市价单失败 err:%+v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = newOrderClosePosition(order, futApi, remainingQuantity, apiUserinfo, db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 复制订单
|
||||
func createNewOrder(order models.LinePreOrder, num decimal.Decimal, desc string) models.LinePreOrder {
|
||||
newOrder := models.LinePreOrder{}
|
||||
copier.Copy(&newOrder, order)
|
||||
|
||||
newOrder.Id = 0
|
||||
newOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
|
||||
newOrder.Num = num.String()
|
||||
newOrder.Desc = desc
|
||||
newOrder.MainOrderType = "MARKET"
|
||||
newOrder.Status = 0
|
||||
|
||||
// 重新创建 Childs,并正确赋值 `Pid`
|
||||
var newChilds []models.LinePreOrder
|
||||
for _, child := range order.Childs {
|
||||
newChild := child
|
||||
newChild.Id = 0
|
||||
newChild.Pid = 0
|
||||
newChild.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
|
||||
newChild.CreatedAt = time.Now()
|
||||
newChilds = append(newChilds, newChild)
|
||||
}
|
||||
newOrder.Childs = newChilds // 重新赋值子订单,避免浅拷贝问题
|
||||
|
||||
return newOrder
|
||||
}
|
||||
|
||||
// 新市价单
|
||||
func newOrderClosePosition(order models.LinePreOrder, futApi binanceservice.FutRestApi, remainingQuantity decimal.Decimal, apiUserinfo models.LineApiUser, db *gorm.DB) error {
|
||||
var positionSide string
|
||||
|
||||
if order.Site == "BUY" {
|
||||
@ -336,23 +471,6 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
|
||||
} else {
|
||||
positionSide = "LONG"
|
||||
}
|
||||
err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn}).Error
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("生成合约新市价单失败 err:%+v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// params := binanceservice.FutOrderPlace{
|
||||
// ApiId: order.ApiId,
|
||||
// Symbol: order.Symbol,
|
||||
// Side: order.Site,
|
||||
// Quantity: remainingQuantity,
|
||||
// Price: utility.StringToDecimal(order.Price),
|
||||
// SideType: "MARKET",
|
||||
// OpenOrder: 0,
|
||||
// OrderType: "MARKET",
|
||||
// NewClientOrderId: utility.Int64ToString(newClientOrderId),
|
||||
// }
|
||||
|
||||
if err := futApi.ClosePositionLoop(order.Symbol, order.OrderSn, remainingQuantity, order.Site, positionSide, apiUserinfo, "MARKET", "0", decimal.Zero, 3); err != nil {
|
||||
logger.Error(fmt.Sprintf("重新下合约市价单失败 err:%+v", err))
|
||||
@ -362,8 +480,7 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l ListenSymbol) Exec(arg interface{}) error {
|
||||
|
||||
159
app/jobs/jobs_test.go
Normal file
159
app/jobs/jobs_test.go
Normal file
@ -0,0 +1,159 @@
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/helper"
|
||||
"go-admin/services/binanceservice"
|
||||
"testing"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 测试现货限价转市价订单
|
||||
func TestSpotLimitTransferJob(t *testing.T) {
|
||||
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
|
||||
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||
helper.InitLockRedisConn("127.0.0.1:6379", "", "3")
|
||||
|
||||
job := LimitOrderTimeoutDuration{}
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
err := db.Model(&models.LinePreOrder{}).
|
||||
Where("order_sn =?", "393609596205268992").
|
||||
Preload("Childs").
|
||||
Find(&orders).Error
|
||||
// job.Exec([]string{})
|
||||
|
||||
config.ApplicationConfig.Mode = "dev"
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("获取订单失败 %v", err)
|
||||
}
|
||||
|
||||
for _, order := range orders {
|
||||
apiInfo, err := binanceservice.GetApiInfo(49)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("获取api信息失败 %v", err)
|
||||
return
|
||||
}
|
||||
spotApi := binanceservice.SpotRestApi{}
|
||||
err = job.ReSpotOrderPlace(db, order, apiInfo, spotApi)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("下单失败 %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
// 测试限价转市价订单
|
||||
func TestLimitTransferJob(t *testing.T) {
|
||||
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
|
||||
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||
helper.InitLockRedisConn("127.0.0.1:6379", "", "3")
|
||||
|
||||
job := LimitOrderTimeoutDuration{}
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
err := db.Model(&models.LinePreOrder{}).
|
||||
Where("order_sn =?", "393573282378416128").
|
||||
Preload("Childs").
|
||||
Find(&orders).Error
|
||||
// job.Exec([]string{})
|
||||
|
||||
config.ApplicationConfig.Mode = "dev"
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("获取订单失败 %v", err)
|
||||
}
|
||||
|
||||
for _, order := range orders {
|
||||
apiInfo, err := binanceservice.GetApiInfo(49)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("获取api信息失败 %v", err)
|
||||
return
|
||||
}
|
||||
futApi := binanceservice.FutRestApi{}
|
||||
err = job.ReFutOrderPlace(db, order, apiInfo, futApi)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("下单失败 %v", err)
|
||||
}
|
||||
}
|
||||
select {}
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||
gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
|
||||
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||
helper.InitLockRedisConn("127.0.0.1:6379", "", "3")
|
||||
|
||||
futApi := binanceservice.FutRestApi{}
|
||||
// params := binanceservice.FutOrderPlace{
|
||||
// Symbol: "ADAUSDT",
|
||||
// ApiId: 49,
|
||||
// Side: ,
|
||||
// }
|
||||
apiInfo, err := binanceservice.GetApiInfo(49)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("获取api信息失败 %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = futApi.ClosePositionLoop("ADAUSDT", "393573282378416128", decimal.NewFromInt(21), "SELL", "LONG", apiInfo, "LIMIT", "0", decimal.NewFromFloat(0.76), 3)
|
||||
|
||||
fmt.Printf("报错 %v", err)
|
||||
}
|
||||
|
||||
// 测试现货下单
|
||||
func TestReduceSpot(t *testing.T) {
|
||||
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
|
||||
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||
helper.InitLockRedisConn("127.0.0.1:6379", "", "3")
|
||||
|
||||
spotApi := binanceservice.SpotRestApi{}
|
||||
// params := binanceservice.FutOrderPlace{
|
||||
// Symbol: "ADAUSDT",
|
||||
// ApiId: 49,
|
||||
// Side: ,
|
||||
// }
|
||||
apiInfo, err := binanceservice.GetApiInfo(49)
|
||||
fmt.Sprintf("%v", apiInfo)
|
||||
|
||||
// if err != nil {
|
||||
// fmt.Printf("获取api信息失败 %v", err)
|
||||
// return
|
||||
// }
|
||||
|
||||
err = spotApi.CancelOpenOrderByOrderSn(apiInfo, "ADAUSDT", "393609596188491776")
|
||||
|
||||
fmt.Printf("取消报错 %v", err)
|
||||
|
||||
params := binanceservice.OrderPlacementService{
|
||||
ApiId: 49,
|
||||
Side: "SELL",
|
||||
Type: "LIMIT",
|
||||
TimeInForce: "GTC",
|
||||
Symbol: "ADAUSDT",
|
||||
Price: decimal.NewFromFloat(0.76),
|
||||
Quantity: decimal.NewFromInt(21),
|
||||
NewClientOrderId: "393609596205268992",
|
||||
}
|
||||
err = spotApi.OrderPlaceLoop(db, params, 3)
|
||||
|
||||
fmt.Printf("报错 %v", err)
|
||||
}
|
||||
@ -44,6 +44,9 @@ const (
|
||||
SpotCallBack = "spot_callback:%s" //现货回调 {ordersn}
|
||||
FutCallBack = "fut_callback:%s" //合约回调 {ordersn}
|
||||
|
||||
FutReducecCallback = "fut_reduce_callback:%v_%s" //合约减仓回调 {apiid,symbol}
|
||||
SpotReduceCallback = "spot_reduce_callback:%v_%s" //现货减仓回调 {apiid,symbol}
|
||||
|
||||
//需要清理键值---------BEGIN---------------
|
||||
|
||||
SpotStopLossList = "spot_stoploss_list:%s" //现货止损待触发列表 {交易所类型code}
|
||||
|
||||
6
common/const/rediskey/sys_config.go
Normal file
6
common/const/rediskey/sys_config.go
Normal file
@ -0,0 +1,6 @@
|
||||
package rediskey
|
||||
|
||||
const (
|
||||
// SysConfigKey 系统配置 {configKey}
|
||||
SysConfigKey = "sys_config:%s"
|
||||
)
|
||||
@ -9,13 +9,13 @@ import (
|
||||
"go-admin/config"
|
||||
"go-admin/pkg/httputils"
|
||||
"go-admin/services/binanceservice"
|
||||
"go-admin/services/cacheservice"
|
||||
"go-admin/services/futureservice"
|
||||
"go-admin/services/scriptservice"
|
||||
"os"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"github.com/robfig/cron/v3"
|
||||
"gorm.io/gorm"
|
||||
@ -29,12 +29,20 @@ func BusinessInit(db *gorm.DB) {
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
//初始化参数配置
|
||||
cacheservice.InitConfigCache(db)
|
||||
|
||||
//初始化订单配置
|
||||
binanceservice.ResetSystemSetting(db)
|
||||
lineApiUser := service.LineApiUser{}
|
||||
lineApiUser.Orm = db
|
||||
if err := lineApiUser.InitCache(); err != nil {
|
||||
logger.Errorf("初始化api用户失败:%v", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
//币安 现货交易对
|
||||
SpotCurrencyInit()
|
||||
|
||||
//币安 合约交易对
|
||||
FuturesInit()
|
||||
|
||||
@ -101,7 +109,7 @@ func loadApiUser(db *gorm.DB) error {
|
||||
users := make([]models.LineApiUser, 0)
|
||||
|
||||
if err := db.Model(&models.LineApiUser{}).Where("open_status =1").Find(&users).Error; err != nil {
|
||||
log.Error("loadApiUser:", err)
|
||||
logger.Error("loadApiUser:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -111,7 +119,7 @@ func loadApiUser(db *gorm.DB) error {
|
||||
|
||||
if val != "" {
|
||||
if err := helper.DefaultRedis.SetString(key, val); err != nil {
|
||||
log.Error("loadApiUser 保存redis:", err)
|
||||
logger.Error("loadApiUser 保存redis:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,7 +127,7 @@ func loadApiUser(db *gorm.DB) error {
|
||||
groups := make([]models.LineApiGroup, 0)
|
||||
|
||||
if err := db.Model(&models.LineApiGroup{}).Find(&groups).Error; err != nil {
|
||||
log.Error("loadApiGroup:", err)
|
||||
logger.Error("loadApiGroup:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ settings:
|
||||
|
||||
# redis 配置
|
||||
redis:
|
||||
addr: "192.168.1.12:6379"
|
||||
addr: "127.0.0.1:6379"
|
||||
password: ""
|
||||
db: 2
|
||||
# 雪花算法设备id
|
||||
|
||||
@ -28,8 +28,10 @@ type TradeSet struct {
|
||||
LowPrice string `json:"low"` //24小时最低价
|
||||
Volume string `json:"volume"` //24小时成数量
|
||||
QuoteVolume string `json:"quote"` //24小时成交金额
|
||||
MinQty float64 `json:"minQty"` //最小交易数量
|
||||
MaxQty float64 `json:"maxQty"` //最大交易数量
|
||||
MinQty float64 `json:"minQty"` //限价单最小交易数量
|
||||
MaxQty float64 `json:"maxQty"` //限价单最大交易数量
|
||||
MarketMaxQty float64 `json:"marketQty"` //市价单最大交易数量
|
||||
MarketMinQty float64 `json:"marketMinQty"` //市价单最小交易数量
|
||||
MaxNotional string `json:"MaxNotional` //最大名义价值
|
||||
MinNotional string `json:"MinNotional` //最大名义价值
|
||||
E int64 `json:"-"` //推送时间
|
||||
|
||||
54
services/binanceservice/futures_callback.go
Normal file
54
services/binanceservice/futures_callback.go
Normal file
@ -0,0 +1,54 @@
|
||||
package binanceservice
|
||||
|
||||
import (
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/cacheservice"
|
||||
"go-admin/services/positionservice"
|
||||
"time"
|
||||
|
||||
models2 "go-admin/models"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// HandleMarketSliceTakeProfit
|
||||
// @Description: 限价拆分为市价后处理止盈止损
|
||||
func HandleMarketSliceTakeProfit(db *gorm.DB, orderCopy models.LinePreOrder, extOrderId int, apiUserInfo models.LineApiUser, tradeSet models2.TradeSet) {
|
||||
HandleNextFuturesReduce(db, apiUserInfo, orderCopy, tradeSet)
|
||||
|
||||
//延时执行
|
||||
time.AfterFunc(15*time.Second, func() {
|
||||
utility.SafeGo(func() {
|
||||
orderExt := models.LinePreOrderExt{}
|
||||
db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt)
|
||||
positionService := positionservice.BinancePositionManagement{}
|
||||
var side = ""
|
||||
|
||||
if orderCopy.OrderType != 0 {
|
||||
if orderCopy.Site == "BUY" {
|
||||
side = "SELL"
|
||||
} else {
|
||||
side = "BUY"
|
||||
}
|
||||
|
||||
} else {
|
||||
side = orderCopy.Site
|
||||
}
|
||||
|
||||
positionData, err := positionService.GetPosition(orderCopy.ApiId, orderCopy.SymbolType, orderCopy.ExchangeType, orderCopy.Symbol, side)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("获取持仓信息失败,err:", err)
|
||||
} else {
|
||||
sysConfig := cacheservice.GetConfigCacheByKey(db, "market_take_profit_ratio")
|
||||
stopSysConfig := cacheservice.GetConfigCacheByKey(db, "market_stop_loss_ratio")
|
||||
marketTakeProfitRatio := utility.StrToDecimal(sysConfig.ConfigValue)
|
||||
marketStopLossRatio := utility.StrToDecimal(stopSysConfig.ConfigValue)
|
||||
|
||||
FutTakeProfit(db, &orderCopy, apiUserInfo, tradeSet, positionData, orderExt, marketTakeProfitRatio, marketStopLossRatio)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -226,6 +226,11 @@ func GetAndReloadSymbols(data *map[string]models.TradeSet) error {
|
||||
tradeSet.MinBuyVal = utility.StringAsFloat(*filter.MinPrice)
|
||||
case "LOT_SIZE":
|
||||
tradeSet.AmountDigit = utility.GetPrecision(*filter.StepSize)
|
||||
tradeSet.MaxQty = utility.StringAsFloat(*filter.MaxQty)
|
||||
tradeSet.MinQty = utility.StringAsFloat(*filter.MinQty)
|
||||
case "MARKET_LOT_SIZE":
|
||||
tradeSet.MarketMaxQty = utility.StringAsFloat(*filter.MaxQty)
|
||||
tradeSet.MarketMinQty = utility.StringAsFloat(*filter.MinQty)
|
||||
case "MIN_NOTIONAL":
|
||||
tradeSet.MinNotional = *filter.Notional
|
||||
case "MAX_NOTIONAL":
|
||||
|
||||
@ -11,12 +11,14 @@ import (
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
models2 "go-admin/models"
|
||||
"go-admin/models/positiondto"
|
||||
"go-admin/pkg/utility"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@ -148,26 +150,53 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
return
|
||||
}
|
||||
|
||||
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutReducecCallback, preOrder.ApiId, preOrder.Symbol), 120, 20, 100*time.Millisecond)
|
||||
|
||||
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||
log.Error("获取锁失败", err)
|
||||
return
|
||||
} else if ok {
|
||||
defer lock.Release()
|
||||
positionData := savePosition(db, preOrder)
|
||||
|
||||
//市价单就跳出 市价减仓不设止盈止损
|
||||
if preOrder.MainOrderType == "MARKET" {
|
||||
return
|
||||
}
|
||||
|
||||
//亏损大于0 重新计算比例
|
||||
FutTakeProfit(db, preOrder, apiUserInfo, tradeSet, positionData, orderExt, decimal.Zero, decimal.Zero)
|
||||
//处理下一个减仓
|
||||
HandleNextFuturesReduce(db, apiUserInfo, *preOrder, tradeSet)
|
||||
}
|
||||
}
|
||||
|
||||
// 减仓处理止盈止损
|
||||
func FutTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder, apiUserInfo DbModels.LineApiUser, tradeSet models2.TradeSet,
|
||||
positionData positiondto.PositionDto, orderExt DbModels.LinePreOrderExt, manualTakeRatio, manualStopRatio decimal.Decimal) bool {
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status = 0", preOrder.Id).Find(&orders).Error; err != nil {
|
||||
logger.Errorf("handleMainReduceFilled 获取待触发订单失败,订单号:%s", preOrder.OrderSn)
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet)
|
||||
price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit))
|
||||
futApi := FutRestApi{}
|
||||
mainId := preOrder.Id
|
||||
|
||||
if preOrder.MainId > 0 {
|
||||
mainId = preOrder.MainId
|
||||
}
|
||||
|
||||
for _, v := range orders {
|
||||
if v.OrderType == 1 {
|
||||
//亏损大于0 重新计算比例
|
||||
if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 {
|
||||
//手动设置百分比
|
||||
if manualTakeRatio.Cmp(decimal.Zero) > 0 {
|
||||
v.Rate = manualTakeRatio.String()
|
||||
percentag := manualTakeRatio.Div(decimal.NewFromInt(100))
|
||||
|
||||
if positionData.PositionSide == "LONG" {
|
||||
v.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
v.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
} else if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 {
|
||||
percentag := positionData.TotalLoss.Div(totalNum).Div(price).Mul(decimal.NewFromInt(100))
|
||||
percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2)
|
||||
v.Rate = percentag.String()
|
||||
@ -182,9 +211,32 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
|
||||
processFutTakeProfitOrder(db, futApi, v, totalNum)
|
||||
} else if v.OrderType == 2 {
|
||||
if manualStopRatio.Cmp(decimal.Zero) > 0 {
|
||||
v.Rate = manualStopRatio.String()
|
||||
percentag := manualStopRatio.Div(decimal.NewFromInt(100))
|
||||
|
||||
if positionData.PositionSide == "LONG" {
|
||||
v.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
v.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
}
|
||||
processFutStopLossOrder(db, v, utility.StrToDecimal(v.Price), totalNum)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 处理下一个待触发
|
||||
func HandleNextFuturesReduce(db *gorm.DB, apiUserInfo DbModels.LineApiUser, preOrdedr DbModels.LinePreOrder, tradeSet models2.TradeSet) {
|
||||
mainId := preOrdedr.Id
|
||||
|
||||
if preOrdedr.MainId > 0 {
|
||||
mainId = preOrdedr.MainId
|
||||
}
|
||||
|
||||
totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, &preOrdedr, tradeSet)
|
||||
nextFuturesReduceTrigger(db, mainId, totalNum, tradeSet)
|
||||
}
|
||||
|
||||
@ -307,7 +359,7 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
|
||||
if apiUserInfo.Id > 0 {
|
||||
mainIds := []int{preOrder.MainId}
|
||||
if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil {
|
||||
if err := CancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil {
|
||||
logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err)
|
||||
}
|
||||
}
|
||||
@ -328,7 +380,7 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
|
||||
if apiUserInfo.Id > 0 {
|
||||
mainIds := []int{preOrder.MainId}
|
||||
if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil {
|
||||
if err := CancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil {
|
||||
logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err)
|
||||
}
|
||||
}
|
||||
@ -356,7 +408,7 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
|
||||
if apiUserInfo.Id > 0 {
|
||||
mainIds := []int{preOrder.MainId}
|
||||
if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil {
|
||||
if err := CancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil {
|
||||
logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err)
|
||||
}
|
||||
}
|
||||
@ -508,7 +560,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd
|
||||
switch order.OrderType {
|
||||
case 1: // 止盈
|
||||
//亏损大于0 重新计算比例
|
||||
if first && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 {
|
||||
if first && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 && preOrder.SignPriceType != "mixture" {
|
||||
percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100))
|
||||
percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2)
|
||||
order.Rate = percentag.String()
|
||||
@ -591,14 +643,14 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr
|
||||
|
||||
// 批量取消订单
|
||||
|
||||
err = cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, changeMainOrderStatus)
|
||||
err = CancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, changeMainOrderStatus)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 根据mainid 取消订单
|
||||
// @changeMainOrderStatus 是否更新主单状态
|
||||
func cancelMainOrders(mainIds []int, db *gorm.DB, apiUserInfo DbModels.LineApiUser, symbol string, changeMainOrderStatus bool) error {
|
||||
func CancelMainOrders(mainIds []int, db *gorm.DB, apiUserInfo DbModels.LineApiUser, symbol string, changeMainOrderStatus bool) error {
|
||||
if len(mainIds) > 0 {
|
||||
orderSns, err := GetOpenOrderSns(db, mainIds)
|
||||
if err != nil {
|
||||
|
||||
63
services/binanceservice/spot_callback.go
Normal file
63
services/binanceservice/spot_callback.go
Normal file
@ -0,0 +1,63 @@
|
||||
package binanceservice
|
||||
|
||||
import (
|
||||
"go-admin/app/admin/models"
|
||||
models2 "go-admin/models"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/cacheservice"
|
||||
"go-admin/services/positionservice"
|
||||
"time"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// HandleMarketSliceTakeProfit
|
||||
// @Description: 限价拆分为市价后处理止盈止损
|
||||
func HandleSpotMarketSliceTakeProfit(db *gorm.DB, orderCopy models.LinePreOrder, extOrderId int, apiUserInfo models.LineApiUser, tradeSet models2.TradeSet) {
|
||||
mainId := orderCopy.Id
|
||||
|
||||
if orderCopy.MainId > 0 {
|
||||
mainId = orderCopy.MainId
|
||||
}
|
||||
totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, &orderCopy, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet)
|
||||
totalNum = totalNum.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit))
|
||||
nextSpotReduceTrigger(db, mainId, totalNum, tradeSet)
|
||||
|
||||
//延时执行
|
||||
time.AfterFunc(15*time.Second, func() {
|
||||
utility.SafeGo(func() {
|
||||
orderExt := models.LinePreOrderExt{}
|
||||
db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt)
|
||||
positionService := positionservice.BinancePositionManagement{}
|
||||
var side = ""
|
||||
|
||||
if orderCopy.OrderType != 0 {
|
||||
if orderCopy.Site == "BUY" {
|
||||
side = "SELL"
|
||||
} else {
|
||||
side = "BUY"
|
||||
}
|
||||
|
||||
} else {
|
||||
side = orderCopy.Site
|
||||
}
|
||||
|
||||
positionData, err := positionService.GetPosition(orderCopy.ApiId, orderCopy.SymbolType, orderCopy.ExchangeType, orderCopy.Symbol, side)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("获取持仓信息失败,err:", err)
|
||||
} else {
|
||||
sysConfig := cacheservice.GetConfigCacheByKey(db, "market_take_profit_ratio")
|
||||
stopSysConfig := cacheservice.GetConfigCacheByKey(db, "market_stop_loss_ratio")
|
||||
marketTakeProfitRatio := utility.StrToDecimal(sysConfig.ConfigValue)
|
||||
marketStopLossRatio := utility.StrToDecimal(stopSysConfig.ConfigValue)
|
||||
totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, &orderCopy, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet)
|
||||
totalNum = totalNum.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit))
|
||||
|
||||
SpotTakeProfit(db, &orderCopy, totalNum, positionData, orderExt, tradeSet, marketTakeProfitRatio, marketStopLossRatio)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -20,6 +20,7 @@ import (
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@ -173,14 +174,19 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
|
||||
orderExt := models.LinePreOrderExt{}
|
||||
positionData := savePosition(db, preOrder)
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
// rate := utility.StringAsFloat(preOrder.Rate)
|
||||
|
||||
if err := db.Model(&DbModels.LinePreOrderStatus{}).Where("order_id =? ", preOrder.MainId).Update("reduce_status", 1).Error; err != nil {
|
||||
logger.Errorf("handleMainReduceFilled 更新主单减仓状态失败,订单号:%s", preOrder.OrderSn)
|
||||
}
|
||||
db.Model(&orderExt).Where("order_id =?", preOrder.Id).Find(&orderExt)
|
||||
|
||||
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 {
|
||||
log.Error("获取锁失败", err)
|
||||
return
|
||||
} else if ok {
|
||||
defer lock.Release()
|
||||
// 100%减仓 终止流程
|
||||
if orderExt.AddPositionVal.Cmp(decimal.NewFromInt(100)) >= 0 {
|
||||
//缓存
|
||||
@ -194,13 +200,34 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
return
|
||||
}
|
||||
|
||||
//市价单跳出
|
||||
if preOrder.MainOrderType == "MARKET" {
|
||||
return
|
||||
}
|
||||
|
||||
totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet)
|
||||
totalNum = totalNum.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit))
|
||||
//亏损大于0 重新计算比例
|
||||
SpotTakeProfit(db, preOrder, totalNum, positionData, orderExt, tradeSet, decimal.Zero, decimal.Zero)
|
||||
|
||||
mainId := preOrder.Id
|
||||
|
||||
if preOrder.MainId > 0 {
|
||||
mainId = preOrder.MainId
|
||||
}
|
||||
|
||||
nextSpotReduceTrigger(db, mainId, totalNum, tradeSet)
|
||||
}
|
||||
}
|
||||
|
||||
func SpotTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder, totalNum decimal.Decimal,
|
||||
positionData positiondto.PositionDto, orderExt DbModels.LinePreOrderExt, tradeSet models2.TradeSet, manualTakeRatio, manualStopRatio decimal.Decimal) bool {
|
||||
price := utility.StrToDecimal(preOrder.Price)
|
||||
orders := make([]models.LinePreOrder, 0)
|
||||
|
||||
if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status=0", preOrder.Id).Find(&orders).Error; err != nil {
|
||||
logger.Errorf("获取减仓单止盈止损失败 err:%v", err)
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
spotApi := SpotRestApi{}
|
||||
@ -209,8 +236,17 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
orders[index].Num = totalNum.String()
|
||||
|
||||
if orders[index].OrderType == 1 {
|
||||
//亏损大于0 重新计算比例
|
||||
if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 {
|
||||
//手动设置百分比
|
||||
if manualTakeRatio.Cmp(decimal.Zero) > 0 {
|
||||
orders[index].Rate = manualTakeRatio.String()
|
||||
percentag := manualTakeRatio.Div(decimal.NewFromInt(100))
|
||||
|
||||
if positionData.PositionSide == "LONG" {
|
||||
orders[index].Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
orders[index].Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
} else if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 {
|
||||
percentag := positionData.TotalLoss.Div(totalNum).Div(price).Mul(decimal.NewFromInt(100))
|
||||
percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2)
|
||||
orders[index].Rate = percentag.String()
|
||||
@ -220,17 +256,20 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
|
||||
processTakeProfitOrder(db, spotApi, orders[index])
|
||||
} else if orders[index].OrderType == 2 {
|
||||
if manualStopRatio.Cmp(decimal.Zero) > 0 {
|
||||
orders[index].Rate = manualStopRatio.String()
|
||||
percentag := manualStopRatio.Div(decimal.NewFromInt(100))
|
||||
|
||||
if positionData.PositionSide == "LONG" {
|
||||
orders[index].Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
orders[index].Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
}
|
||||
processStopLossOrder(orders[index])
|
||||
}
|
||||
}
|
||||
|
||||
mainId := preOrder.Id
|
||||
|
||||
if preOrder.MainId > 0 {
|
||||
mainId = preOrder.MainId
|
||||
}
|
||||
|
||||
nextSpotReduceTrigger(db, mainId, totalNum, tradeSet)
|
||||
return false
|
||||
}
|
||||
|
||||
// 缓存下一个减仓单
|
||||
@ -751,7 +790,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
|
||||
switch order.OrderType {
|
||||
case 1: // 止盈
|
||||
//亏损大于0 重新计算比例
|
||||
if fist && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 {
|
||||
if fist && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 && preOrder.SignPriceType != "mixture" {
|
||||
percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100))
|
||||
|
||||
if fist {
|
||||
|
||||
@ -43,6 +43,9 @@ func GetSpotSymbols() (map[string]models.TradeSet, []string, error) {
|
||||
tradeSet.AmountDigit = utility.GetPrecision(filter.StepSize)
|
||||
tradeSet.MinQty = utility.StringAsFloat(filter.MinQty)
|
||||
tradeSet.MaxQty = utility.StringAsFloat(filter.MaxQty)
|
||||
case "MARKET_LOT_SIZE":
|
||||
tradeSet.MarketMinQty = utility.StringAsFloat(filter.MinQty)
|
||||
tradeSet.MarketMinQty = utility.StringAsFloat(filter.MaxQty)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
61
services/cacheservice/config_service.go
Normal file
61
services/cacheservice/config_service.go
Normal file
@ -0,0 +1,61 @@
|
||||
package cacheservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/helper"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 加载config缓存
|
||||
func InitConfigCache(db *gorm.DB) {
|
||||
datas := []models.SysConfig{}
|
||||
db.Model(&models.SysConfig{}).Find(&datas)
|
||||
for _, item := range datas {
|
||||
cacheKey := fmt.Sprintf(rediskey.SysConfigKey, item.ConfigKey)
|
||||
|
||||
content, err := sonic.Marshal(item)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(content) > 0 {
|
||||
helper.DefaultRedis.SetString(cacheKey, item.ConfigValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 sys config缓存
|
||||
func GetConfigCacheByKey(db *gorm.DB, key string) models.SysConfig {
|
||||
cacheKey := fmt.Sprintf(rediskey.SysConfigKey, key)
|
||||
val, _ := helper.DefaultRedis.GetString(key)
|
||||
result := models.SysConfig{}
|
||||
|
||||
if val == "" {
|
||||
sonic.Unmarshal([]byte(val), &result)
|
||||
}
|
||||
|
||||
if result.Id == 0 {
|
||||
if err := db.Model(&result).Where("config_key = ?", key).First(&result).Error; err != nil {
|
||||
logger.Errorf("GetConfigCacheByKey %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if result.Id > 0 {
|
||||
content, err := sonic.Marshal(result)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("GetConfigCacheByKey %s", err.Error())
|
||||
} else {
|
||||
helper.DefaultRedis.SetString(cacheKey, string(content))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user