From 32ba6262cceece643caee33409b157c68c45aa51 Mon Sep 17 00:00:00 2001
From: hucan <951870319@qq.com>
Date: Tue, 25 Feb 2025 16:57:59 +0800
Subject: [PATCH 01/13] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=BC=80=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/admin/apis/line_coinnetwork.go | 117 +++---
app/admin/apis/line_pre_order.go | 35 +-
app/admin/apis/member_withdrawal_log.go | 193 +++++++++
app/admin/fronted/invite_log.go | 46 +++
app/admin/fronted/line_user.go | 43 ++
app/admin/fronted/member_balance_log.go | 42 ++
app/admin/fronted/member_renwal_log.go | 40 +-
app/admin/fronted/member_withdrawal_log.go | 106 +++++
app/admin/models/line_coinnetwork.go | 1 +
app/admin/models/line_user.go | 1 +
app/admin/models/member_balance.go | 1 +
app/admin/models/member_balance_log.go | 28 +-
app/admin/models/member_renwa_log.go | 36 +-
app/admin/models/member_withdrawal_log.go | 40 ++
app/admin/router/invite_log.go | 21 +
app/admin/router/line_pre_order.go | 1 +
app/admin/router/line_user.go | 4 +-
app/admin/router/member_balance_log.go | 15 +-
app/admin/router/member_renwa_log.go | 16 +-
app/admin/router/member_withdrawal_log.go | 43 ++
app/admin/service/appservice/invite_log.go | 55 +++
app/admin/service/appservice/line_user.go | 205 ++++++++++
.../service/appservice/member_balance_log.go | 44 ++
.../service/appservice/member_renwal_log.go | 143 ++++++-
.../appservice/member_withdrawal_log.go | 185 +++++++++
app/admin/service/dto/line_coinnetwork.go | 4 +
app/admin/service/dto/line_pre_order.go | 57 +++
app/admin/service/dto/line_user.go | 16 +-
app/admin/service/dto/member_balance_log.go | 144 ++++---
app/admin/service/dto/member_renwa_log.go | 77 ++--
.../service/dto/member_withdrawal_log.go | 183 +++++++++
app/admin/service/line_coinnetwork.go | 14 +
app/admin/service/line_pre_order.go | 170 +++++++-
app/admin/service/member_balance.go | 62 +--
app/admin/service/member_renwa_log.go | 60 +--
app/admin/service/member_withdrawal_log.go | 109 +++++
app/jobs/examples.go | 18 +-
app/jobs/jobs.go | 38 +-
app/jobs/memberjobs.go | 16 +
app/jobs/trxJobs.go | 54 ++-
.../member_balance_change_source/dict.go | 7 +
.../dicts/member_renwal_log_status/dict.go | 10 +
.../member_withdrawal_log_status/dict.go | 16 +
common/const/rediskey/redis_key.go | 7 +
common/const/sysconfigkey/key.go | 8 +
.../sysservice/authservice/authentication.go | 379 +++++++++---------
common/status_code/code_11-12.go | 11 +
pkg/cryptohelper/inttostring/Inttoostr.go | 13 +
48 files changed, 2468 insertions(+), 466 deletions(-)
create mode 100644 app/admin/apis/member_withdrawal_log.go
create mode 100644 app/admin/fronted/invite_log.go
create mode 100644 app/admin/fronted/member_balance_log.go
create mode 100644 app/admin/fronted/member_withdrawal_log.go
create mode 100644 app/admin/models/member_withdrawal_log.go
create mode 100644 app/admin/router/invite_log.go
create mode 100644 app/admin/router/member_withdrawal_log.go
create mode 100644 app/admin/service/appservice/invite_log.go
create mode 100644 app/admin/service/appservice/line_user.go
create mode 100644 app/admin/service/appservice/member_balance_log.go
create mode 100644 app/admin/service/appservice/member_withdrawal_log.go
create mode 100644 app/admin/service/dto/member_withdrawal_log.go
create mode 100644 app/admin/service/member_withdrawal_log.go
create mode 100644 app/jobs/memberjobs.go
create mode 100644 common/const/dicts/member_balance_change_source/dict.go
create mode 100644 common/const/dicts/member_renwal_log_status/dict.go
create mode 100644 common/const/dicts/member_withdrawal_log_status/dict.go
create mode 100644 common/const/sysconfigkey/key.go
create mode 100644 common/status_code/code_11-12.go
diff --git a/app/admin/apis/line_coinnetwork.go b/app/admin/apis/line_coinnetwork.go
index 1a85256..c004d1b 100644
--- a/app/admin/apis/line_coinnetwork.go
+++ b/app/admin/apis/line_coinnetwork.go
@@ -1,9 +1,10 @@
package apis
import (
- "fmt"
+ "fmt"
"github.com/gin-gonic/gin"
+ "github.com/gin-gonic/gin/binding"
"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"
@@ -28,18 +29,18 @@ type LineCoinnetwork struct {
// @Router /api/v1/line-coinnetwork [get]
// @Security Bearer
func (e LineCoinnetwork) GetPage(c *gin.Context) {
- req := dto.LineCoinnetworkGetPageReq{}
- s := service.LineCoinnetwork{}
- 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 := dto.LineCoinnetworkGetPageReq{}
+ s := service.LineCoinnetwork{}
+ err := e.MakeContext(c).
+ MakeOrm().
+ Bind(&req, binding.Form, binding.Query).
+ 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.LineCoinnetwork, 0)
@@ -48,7 +49,7 @@ func (e LineCoinnetwork) GetPage(c *gin.Context) {
err = s.GetPage(&req, p, &list, &count)
if err != nil {
e.Error(500, err, fmt.Sprintf("获取【币种网络】失败,\r\n失败信息 %s", err.Error()))
- return
+ return
}
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
@@ -65,7 +66,7 @@ func (e LineCoinnetwork) GetPage(c *gin.Context) {
func (e LineCoinnetwork) Get(c *gin.Context) {
req := dto.LineCoinnetworkGetReq{}
s := service.LineCoinnetwork{}
- err := e.MakeContext(c).
+ err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
@@ -81,10 +82,10 @@ func (e LineCoinnetwork) Get(c *gin.Context) {
err = s.Get(&req, p, &object)
if err != nil {
e.Error(500, err, fmt.Sprintf("获取【币种网络】失败,\r\n失败信息 %s", err.Error()))
- return
+ return
}
- e.OK( object, "查询成功")
+ e.OK(object, "查询成功")
}
// Insert 创建【币种网络】
@@ -98,25 +99,25 @@ func (e LineCoinnetwork) Get(c *gin.Context) {
// @Router /api/v1/line-coinnetwork [post]
// @Security Bearer
func (e LineCoinnetwork) Insert(c *gin.Context) {
- req := dto.LineCoinnetworkInsertReq{}
- s := service.LineCoinnetwork{}
- 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 := dto.LineCoinnetworkInsertReq{}
+ s := service.LineCoinnetwork{}
+ 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))
err = s.Insert(&req)
if err != nil {
e.Error(500, err, fmt.Sprintf("创建【币种网络】失败,\r\n失败信息 %s", err.Error()))
- return
+ return
}
e.OK(req.GetId(), "创建成功")
@@ -134,27 +135,27 @@ func (e LineCoinnetwork) Insert(c *gin.Context) {
// @Router /api/v1/line-coinnetwork/{id} [put]
// @Security Bearer
func (e LineCoinnetwork) Update(c *gin.Context) {
- req := dto.LineCoinnetworkUpdateReq{}
- s := service.LineCoinnetwork{}
- 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 := dto.LineCoinnetworkUpdateReq{}
+ s := service.LineCoinnetwork{}
+ 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.Update(&req, p)
if err != nil {
e.Error(500, err, fmt.Sprintf("修改【币种网络】失败,\r\n失败信息 %s", err.Error()))
- return
+ return
}
- e.OK( req.GetId(), "修改成功")
+ e.OK(req.GetId(), "修改成功")
}
// Delete 删除【币种网络】
@@ -166,18 +167,18 @@ func (e LineCoinnetwork) Update(c *gin.Context) {
// @Router /api/v1/line-coinnetwork [delete]
// @Security Bearer
func (e LineCoinnetwork) Delete(c *gin.Context) {
- s := service.LineCoinnetwork{}
- req := dto.LineCoinnetworkDeleteReq{}
- 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
- }
+ s := service.LineCoinnetwork{}
+ req := dto.LineCoinnetworkDeleteReq{}
+ 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)
@@ -185,7 +186,7 @@ func (e LineCoinnetwork) Delete(c *gin.Context) {
err = s.Remove(&req, p)
if err != nil {
e.Error(500, err, fmt.Sprintf("删除【币种网络】失败,\r\n失败信息 %s", err.Error()))
- return
+ return
}
- e.OK( req.GetId(), "删除成功")
+ e.OK(req.GetId(), "删除成功")
}
diff --git a/app/admin/apis/line_pre_order.go b/app/admin/apis/line_pre_order.go
index 145687a..40a2c1f 100644
--- a/app/admin/apis/line_pre_order.go
+++ b/app/admin/apis/line_pre_order.go
@@ -113,11 +113,7 @@ func (e LinePreOrder) GetChildOrderList(c *gin.Context) {
}
p := actions.GetPermissionFromContext(c)
list := make([]models.LinePreOrder, 0)
- if err != nil {
- e.Logger.Error(err)
- e.Error(500, err, err.Error())
- return
- }
+
err = s.GetChildList(&req, p, &list)
if err != nil {
e.Error(500, err, fmt.Sprintf("获取子订单信息失败,\r\n失败信息 %s", err.Error()))
@@ -310,6 +306,35 @@ func (e LinePreOrder) AddPreOrder(c *gin.Context) {
e.OK(nil, "操作成功")
}
+// 手动加仓
+func (e LinePreOrder) AddPosition(c *gin.Context) {
+ s := service.LinePreOrder{}
+ req := dto.LinePreOrderAddPositionReq{}
+ 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
+ }
+
+ if err := req.Valid(); err != nil {
+ e.Error(500, err, err.Error())
+ return
+ }
+
+ err = s.AddPosition(&req)
+
+ if err != nil {
+ e.Error(500, nil, err.Error())
+ return
+ }
+ e.OK(nil, "操作成功")
+}
+
// BatchAddOrder 批量添加
func (e LinePreOrder) BatchAddOrder(c *gin.Context) {
s := service.LinePreOrder{}
diff --git a/app/admin/apis/member_withdrawal_log.go b/app/admin/apis/member_withdrawal_log.go
new file mode 100644
index 0000000..9ded828
--- /dev/null
+++ b/app/admin/apis/member_withdrawal_log.go
@@ -0,0 +1,193 @@
+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 MemberWithdrawalLog struct {
+ api.Api
+}
+
+// GetPage 获取用户提现记录列表
+// @Summary 获取用户提现记录列表
+// @Description 获取用户提现记录列表
+// @Tags 用户提现记录
+// @Param networkName query string false "网络名称"
+// @Param status query string false "提现状态(member_withdrawal_status)"
+// @Param pageSize query int false "页条数"
+// @Param pageIndex query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]models.MemberWithdrawalLog}} "{"code": 200, "data": [...]}"
+// @Router /api/v1/member-withdrawal-log [get]
+// @Security Bearer
+func (e MemberWithdrawalLog) GetPage(c *gin.Context) {
+ req := dto.MemberWithdrawalLogGetPageReq{}
+ s := service.MemberWithdrawalLog{}
+ 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.MemberWithdrawalLog, 0)
+ var count int64
+
+ err = s.GetPage(&req, p, &list, &count)
+ if err != nil {
+ e.Error(500, err, fmt.Sprintf("获取用户提现记录失败,\r\n失败信息 %s", err.Error()))
+ return
+ }
+
+ e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+
+// Get 获取用户提现记录
+// @Summary 获取用户提现记录
+// @Description 获取用户提现记录
+// @Tags 用户提现记录
+// @Param id path int false "id"
+// @Success 200 {object} response.Response{data=models.MemberWithdrawalLog} "{"code": 200, "data": [...]}"
+// @Router /api/v1/member-withdrawal-log/{id} [get]
+// @Security Bearer
+func (e MemberWithdrawalLog) Get(c *gin.Context) {
+ req := dto.MemberWithdrawalLogGetReq{}
+ s := service.MemberWithdrawalLog{}
+ 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.MemberWithdrawalLog
+
+ p := actions.GetPermissionFromContext(c)
+ err = s.Get(&req, p, &object)
+ if err != nil {
+ e.Error(500, err, fmt.Sprintf("获取用户提现记录失败,\r\n失败信息 %s", err.Error()))
+ return
+ }
+
+ e.OK( object, "查询成功")
+}
+
+// Insert 创建用户提现记录
+// @Summary 创建用户提现记录
+// @Description 创建用户提现记录
+// @Tags 用户提现记录
+// @Accept application/json
+// @Product application/json
+// @Param data body dto.MemberWithdrawalLogInsertReq true "data"
+// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
+// @Router /api/v1/member-withdrawal-log [post]
+// @Security Bearer
+func (e MemberWithdrawalLog) Insert(c *gin.Context) {
+ req := dto.MemberWithdrawalLogInsertReq{}
+ s := service.MemberWithdrawalLog{}
+ 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))
+
+ err = s.Insert(&req)
+ if err != nil {
+ e.Error(500, err, fmt.Sprintf("创建用户提现记录失败,\r\n失败信息 %s", err.Error()))
+ return
+ }
+
+ e.OK(req.GetId(), "创建成功")
+}
+
+// Update 修改用户提现记录
+// @Summary 修改用户提现记录
+// @Description 修改用户提现记录
+// @Tags 用户提现记录
+// @Accept application/json
+// @Product application/json
+// @Param id path int true "id"
+// @Param data body dto.MemberWithdrawalLogUpdateReq true "body"
+// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
+// @Router /api/v1/member-withdrawal-log/{id} [put]
+// @Security Bearer
+func (e MemberWithdrawalLog) Update(c *gin.Context) {
+ req := dto.MemberWithdrawalLogUpdateReq{}
+ s := service.MemberWithdrawalLog{}
+ 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.Update(&req, p)
+ if err != nil {
+ e.Error(500, err, fmt.Sprintf("修改用户提现记录失败,\r\n失败信息 %s", err.Error()))
+ return
+ }
+ e.OK( req.GetId(), "修改成功")
+}
+
+// Delete 删除用户提现记录
+// @Summary 删除用户提现记录
+// @Description 删除用户提现记录
+// @Tags 用户提现记录
+// @Param data body dto.MemberWithdrawalLogDeleteReq true "body"
+// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
+// @Router /api/v1/member-withdrawal-log [delete]
+// @Security Bearer
+func (e MemberWithdrawalLog) Delete(c *gin.Context) {
+ s := service.MemberWithdrawalLog{}
+ req := dto.MemberWithdrawalLogDeleteReq{}
+ 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(), "删除成功")
+}
diff --git a/app/admin/fronted/invite_log.go b/app/admin/fronted/invite_log.go
new file mode 100644
index 0000000..c049969
--- /dev/null
+++ b/app/admin/fronted/invite_log.go
@@ -0,0 +1,46 @@
+package fronted
+
+import (
+ "go-admin/app/admin/service/appservice"
+ "go-admin/app/admin/service/common"
+ "go-admin/app/admin/service/dto"
+
+ "github.com/gin-gonic/gin"
+ "github.com/go-admin-team/go-admin-core/sdk/api"
+)
+
+type InviteLog struct {
+ api.Api
+}
+
+// GetPage 获取InviteLog列表
+func (e InviteLog) GetPersonnalPage(c *gin.Context) {
+ s := appservice.InviteLog{}
+ req := dto.PersonnalInviteLogPageReq{}
+ 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
+ }
+
+ datas := make([]dto.InviteLogResp, 0)
+ req.UserId = common.GetUserId(c)
+
+ if req.UserId == 0 {
+ req.UserId = -1
+ }
+ var count int64
+ err = s.GetPage(&req, &datas, &count)
+
+ if err != nil {
+ e.Error(500, err, err.Error())
+ return
+ }
+
+ e.PageOK(datas, int(count), req.PageIndex, req.PageSize, "success")
+}
diff --git a/app/admin/fronted/line_user.go b/app/admin/fronted/line_user.go
index 8cc610a..e15e0c3 100644
--- a/app/admin/fronted/line_user.go
+++ b/app/admin/fronted/line_user.go
@@ -7,6 +7,7 @@ import (
"go-admin/app/admin/models/sysmodel"
"go-admin/app/admin/service"
"go-admin/app/admin/service/aduserdb"
+ "go-admin/app/admin/service/appservice"
"go-admin/app/admin/service/common"
"go-admin/app/admin/service/dto"
"go-admin/common/const/rediskey"
@@ -23,6 +24,7 @@ import (
"github.com/bytedance/sonic"
"github.com/gin-gonic/gin"
+ "github.com/go-admin-team/go-admin-core/logger"
"github.com/go-admin-team/go-admin-core/sdk/api"
"github.com/shopspring/decimal"
"gorm.io/gorm"
@@ -35,11 +37,15 @@ type LineUserApi struct {
// Register 用户注册
func (e LineUserApi) Register(c *gin.Context) {
s := service.LineUser{}
+ userAppService := appservice.LineUser{}
+ balanceService := service.MemberBalance{}
req := sysmodel.FrontedUserRegisterReq{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
+ MakeService(&userAppService.Service).
+ MakeService(&balanceService.Service).
Errors
if err != nil {
e.Logger.Error(err)
@@ -66,6 +72,10 @@ func (e LineUserApi) Register(c *gin.Context) {
e.Error(ok, nil, sysstatuscode.GetStatusCodeDescription(c, ok))
return
}
+
+ userAppService.SetDefaultRenwal(user)
+ balanceService.CreateDefaultBalance(user)
+
resp := map[string]interface{}{
"email": req.Email,
"mobile": req.Phone,
@@ -579,3 +589,36 @@ func (e LineUserApi) CallBack(c *gin.Context) {
e.OK(nil, "")
}
+
+// 退出登录
+func (e LineUserApi) Logout(c *gin.Context) {
+ err := e.MakeContext(c).
+ MakeOrm().
+ Errors
+ if err != nil {
+ e.Logger.Error(err)
+ e.Error(500, err, err.Error())
+ return
+ }
+
+ userId := common.GetUserId(c)
+ err = authservice.Logout(userId, 1)
+
+ if err != nil {
+ logger.Error("退出登录失败:", err.Error())
+ e.Error(statuscode.ServerError, nil, "")
+ }
+
+ e.OK(nil, "success")
+}
+
+// 查询个人资产
+func (e LineUserApi) GetProperty(c *gin.Context) {
+ err := e.MakeContext(c).
+ MakeOrm().
+ Errors
+ if err != nil {
+ e.Logger.Error(err)
+ e.Error(statuscode.ServerError, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.ServerError))
+ }
+}
diff --git a/app/admin/fronted/member_balance_log.go b/app/admin/fronted/member_balance_log.go
new file mode 100644
index 0000000..76fa1a3
--- /dev/null
+++ b/app/admin/fronted/member_balance_log.go
@@ -0,0 +1,42 @@
+package fronted
+
+import (
+ "go-admin/app/admin/service/appservice"
+ "go-admin/app/admin/service/common"
+ "go-admin/app/admin/service/dto"
+ "go-admin/common/service/sysservice/sysstatuscode"
+ statuscode "go-admin/common/status_code"
+
+ "github.com/gin-gonic/gin"
+ "github.com/go-admin-team/go-admin-core/sdk/api"
+)
+
+type MemberBalanceLog struct {
+ api.Api
+}
+
+// 分页查询
+func (e MemberBalanceLog) GetPage(c *gin.Context) {
+ s := appservice.MemberBalanceLog{}
+ req := dto.MemberBalanceLogPageAppReq{}
+ err := e.MakeContext(c).
+ MakeOrm().
+ Bind(&req).
+ MakeService(&s.Service).
+ Errors
+ if err != nil {
+ e.Logger.Error(err)
+ e.Error(500, err, "server error")
+ return
+ }
+ req.UserId = common.GetUserId(c)
+ data := make([]dto.MemberBalanceLogAppResp, 0)
+ var count int64
+ code := s.GetPage(&req, &data, &count)
+
+ if code != statuscode.OK {
+ e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
+ }
+
+ e.PageOK(data, int(count), req.PageIndex, req.PageSize, "success")
+}
diff --git a/app/admin/fronted/member_renwal_log.go b/app/admin/fronted/member_renwal_log.go
index c9fe881..5b78d62 100644
--- a/app/admin/fronted/member_renwal_log.go
+++ b/app/admin/fronted/member_renwal_log.go
@@ -19,14 +19,50 @@ type MemberRenwalLog struct {
func (e MemberRenwalLog) GetPage(c *gin.Context) {
s := appservice.MemberRenwalLog{}
req := dto.MemberRenwalLogPageAppReq{}
-
+ err := e.MakeContext(c).
+ MakeOrm().
+ Bind(&req).
+ MakeService(&s.Service).
+ Errors
+ if err != nil {
+ e.Logger.Error(err)
+ e.Error(500, err, "server error")
+ return
+ }
userId := common.GetUserId(c)
data := make([]dto.MemberRenwalLogResp, 0)
- code := s.GetPage(userId, &req, &data)
+ var count int64
+ code := s.GetPage(userId, &req, &data, &count)
if code != statuscode.OK {
e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
}
+ e.PageOK(data, int(count), req.PageIndex, req.PageSize, "success")
+}
+
+func (e MemberRenwalLog) Apply(c *gin.Context) {
+ s := appservice.MemberRenwalLog{}
+ req := dto.MemberRenwalCreateAppReq{}
+ err := e.MakeContext(c).
+ MakeOrm().
+ Bind(&req).
+ MakeService(&s.Service).
+ Errors
+ if err != nil {
+ e.Logger.Error(err)
+ e.Error(500, err, "server error")
+ return
+ }
+
+ req.UserId = common.GetUserId(c)
+ data := dto.MemberRenwalCreateAppResp{}
+ code := s.Renwal(&req, &data)
+
+ if code != statuscode.OK {
+ e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
+ return
+ }
+
e.OK(data, "success")
}
diff --git a/app/admin/fronted/member_withdrawal_log.go b/app/admin/fronted/member_withdrawal_log.go
new file mode 100644
index 0000000..3542270
--- /dev/null
+++ b/app/admin/fronted/member_withdrawal_log.go
@@ -0,0 +1,106 @@
+package fronted
+
+import (
+ "go-admin/app/admin/service/appservice"
+ "go-admin/app/admin/service/common"
+ "go-admin/app/admin/service/dto"
+ "go-admin/common/service/sysservice/sysstatuscode"
+ statuscode "go-admin/common/status_code"
+
+ "github.com/gin-gonic/gin"
+ "github.com/gin-gonic/gin/binding"
+ "github.com/go-admin-team/go-admin-core/sdk/api"
+)
+
+type MemberWithdrawalLog struct {
+ api.Api
+}
+
+// 分页查询
+func (e MemberWithdrawalLog) GetPage(c *gin.Context) {
+ s := appservice.MemberWithdrawalLog{}
+ req := dto.MemberWithdrawalLogGetPageAppReq{}
+ err := e.MakeContext(c).
+ MakeOrm().
+ Bind(&req, binding.Form, binding.Query).
+ MakeService(&s.Service).
+ Errors
+ if err != nil {
+ e.Logger.Error(err)
+ e.Error(500, err, err.Error())
+ return
+ }
+
+ var count int64
+ var list []dto.MemberWithdrawalLogResp
+
+ code := s.GetPage(&req, &list, &count)
+
+ if code != statuscode.OK {
+ e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
+ }
+
+ e.PageOK(list, int(count), req.PageIndex, req.PageSize, "success")
+}
+
+// 用户提现申请
+func (e MemberWithdrawalLog) Apply(c *gin.Context) {
+ s := appservice.MemberWithdrawalLog{}
+ req := dto.MemberWithdrawalLogApplyReq{}
+ 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
+ }
+
+ if code := req.Valid(); code != statuscode.OK {
+ e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
+ return
+ }
+
+ req.UserId = common.GetUserId(c)
+ code := s.Apply(&req)
+
+ if code != statuscode.OK {
+ e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
+ return
+ }
+
+ e.OK(nil, "success")
+}
+
+// 用户取消提现
+func (e MemberWithdrawalLog) Cancel(c *gin.Context) {
+ s := appservice.MemberWithdrawalLog{}
+ req := dto.MemberWithdrawalLogCancelReq{}
+ 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
+ }
+
+ if code := req.Valid(); code != statuscode.OK {
+ e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
+ return
+ }
+
+ userId := common.GetUserId(c)
+ code := s.Canceled(&req, userId)
+
+ if code != statuscode.OK {
+ e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code))
+ return
+ }
+
+ e.OK(nil, "success")
+}
diff --git a/app/admin/models/line_coinnetwork.go b/app/admin/models/line_coinnetwork.go
index 124cff6..e9c4116 100644
--- a/app/admin/models/line_coinnetwork.go
+++ b/app/admin/models/line_coinnetwork.go
@@ -16,6 +16,7 @@ type LineCoinnetwork struct {
UnlockTime int64 `json:"unlockTime" gorm:"type:int;comment:提现确认平均时间,单位分钟"`
Fee decimal.Decimal `json:"fee" gorm:"type:decimal(32,6);comment:网络手续费(百分比)"`
MinWithdrawal decimal.Decimal `json:"minWithdrawal" gorm:"type:decimal(32,6);comment:最小提现金额"`
+ Status int `json:"status" gorm:"type:int;comment:状态 1 启用 2 禁用"`
models.ModelTime
models.ControlBy
}
diff --git a/app/admin/models/line_user.go b/app/admin/models/line_user.go
index 66e7e1a..e1e9492 100644
--- a/app/admin/models/line_user.go
+++ b/app/admin/models/line_user.go
@@ -39,6 +39,7 @@ type LineUser struct {
Status string `json:"status" gorm:"type:varchar(30);comment:状态"`
Verification string `json:"verification" gorm:"type:varchar(255);comment:验证"`
LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;comment:登录时间"`
+ ExpirationTime *time.Time `json:"expirationTime" gorm:"type:timestamp;comment:过期时间"`
OpenStatus int `json:"open_status" gorm:"-"`
models.ModelTime
models.ControlBy
diff --git a/app/admin/models/member_balance.go b/app/admin/models/member_balance.go
index 964cc67..0a53119 100644
--- a/app/admin/models/member_balance.go
+++ b/app/admin/models/member_balance.go
@@ -9,6 +9,7 @@ import (
type MemberBalance struct {
models.Model
+ UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
TotalAmont decimal.Decimal `json:"totalAmont" gorm:"type:decimal(18,6);comment:总余额"`
FreeAmount decimal.Decimal `json:"freeAmount" gorm:"type:decimal(18,6);comment:可用余额"`
FrozenAmount decimal.Decimal `json:"frozenAmount" gorm:"type:decimal(18,6);comment:冻结金额"`
diff --git a/app/admin/models/member_balance_log.go b/app/admin/models/member_balance_log.go
index 5a30605..c74f41f 100644
--- a/app/admin/models/member_balance_log.go
+++ b/app/admin/models/member_balance_log.go
@@ -1,27 +1,27 @@
package models
import (
-
"go-admin/common/models"
+ "github.com/shopspring/decimal"
)
type MemberBalanceLog struct {
- models.Model
-
- UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
- ChangeSource string `json:"changeSource" gorm:"type:varchar(50);comment:变更来源 (member_change_source)"`
- ChangeType string `json:"changeType" gorm:"type:tinyint;comment:变更类别 1-收入 2-支出"`
- Amount string `json:"amount" gorm:"type:decimal(18,6);comment:变更金额"`
- BalanceBefore string `json:"balanceBefore" gorm:"type:decimal(18,6);comment:变更前余额"`
- BalanceAfter string `json:"balanceAfter" gorm:"type:decimal(18,6);comment:变更后余额"`
- Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
- models.ModelTime
- models.ControlBy
+ models.Model
+
+ UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
+ ChangeSource string `json:"changeSource" gorm:"type:varchar(50);comment:变更来源 (member_change_source)"`
+ ChangeType int `json:"changeType" gorm:"type:tinyint;comment:变更类别 1-收入 2-支出"`
+ Amount decimal.Decimal `json:"amount" gorm:"type:decimal(18,6);comment:变更金额"`
+ BalanceBefore decimal.Decimal `json:"balanceBefore" gorm:"type:decimal(18,6);comment:变更前余额"`
+ BalanceAfter decimal.Decimal `json:"balanceAfter" gorm:"type:decimal(18,6);comment:变更后余额"`
+ Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
+ models.ModelTime
+ models.ControlBy
}
func (MemberBalanceLog) TableName() string {
- return "member_balance_log"
+ return "member_balance_log"
}
func (e *MemberBalanceLog) Generate() models.ActiveRecord {
@@ -31,4 +31,4 @@ func (e *MemberBalanceLog) Generate() models.ActiveRecord {
func (e *MemberBalanceLog) GetId() interface{} {
return e.Id
-}
\ No newline at end of file
+}
diff --git a/app/admin/models/member_renwa_log.go b/app/admin/models/member_renwa_log.go
index fcc2b63..b4b20d7 100644
--- a/app/admin/models/member_renwa_log.go
+++ b/app/admin/models/member_renwa_log.go
@@ -1,30 +1,34 @@
package models
import (
- "time"
+ "time"
"go-admin/common/models"
+ "github.com/shopspring/decimal"
)
type MemberRenwaLog struct {
- models.Model
-
- RenwalId string `json:"renwalId" gorm:"type:bigint;comment:套餐id"`
- RenwalName string `json:"renwalName" gorm:"type:varchar(255);comment:续期套餐名称"`
- RenwalDuration string `json:"renwalDuration" gorm:"type:int;comment:续期时长(天数)"`
- Status string `json:"status" gorm:"type:varchar(255);comment:订单状态(member_renwal_log_status)"`
- PayableAmount string `json:"payableAmount" gorm:"type:decimal(18,6);comment:应付金额"`
- ActualPaymentAmount string `json:"actualPaymentAmount" gorm:"type:decimal(18,6);comment:实付金额"`
- FromAddress string `json:"fromAddress" gorm:"type:varchar(255);comment:付款地址"`
- Coin string `json:"coin" gorm:"type:varchar(255);comment:代币"`
- PaymentTime time.Time `json:"paymentTime" gorm:"type:datetime;comment:支付时间"`
- models.ModelTime
- models.ControlBy
+ models.Model
+
+ RenwalId int `json:"renwalId" gorm:"type:bigint;comment:套餐id"`
+ UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
+ RenwalName string `json:"renwalName" gorm:"type:varchar(255);comment:续期套餐名称"`
+ RenwalDuration int `json:"renwalDuration" gorm:"type:int;comment:续期时长(天数)"`
+ Status string `json:"status" gorm:"type:varchar(255);comment:订单状态(member_renwal_log_status)"`
+ PayableAmount decimal.Decimal `json:"payableAmount" gorm:"type:decimal(18,6);comment:应付金额"`
+ ActualPaymentAmount decimal.Decimal `json:"actualPaymentAmount" gorm:"type:decimal(18,6);comment:实付金额"`
+ FromAddress string `json:"fromAddress" gorm:"type:varchar(255);comment:付款地址"`
+ Coin string `json:"coin" gorm:"type:varchar(255);comment:代币"`
+ PaymentTime *time.Time `json:"paymentTime" gorm:"type:datetime;comment:支付时间"`
+ Hash string `json:"hash" gorm:"type:varchar(255);comment:交易hash"`
+ ExpirationTime time.Time `json:"expirationTime" gorm:"type:datetime;comment:到期时间"`
+ models.ModelTime
+ models.ControlBy
}
func (MemberRenwaLog) TableName() string {
- return "member_renwa_log"
+ return "member_renwa_log"
}
func (e *MemberRenwaLog) Generate() models.ActiveRecord {
@@ -34,4 +38,4 @@ func (e *MemberRenwaLog) Generate() models.ActiveRecord {
func (e *MemberRenwaLog) GetId() interface{} {
return e.Id
-}
\ No newline at end of file
+}
diff --git a/app/admin/models/member_withdrawal_log.go b/app/admin/models/member_withdrawal_log.go
new file mode 100644
index 0000000..3165ef5
--- /dev/null
+++ b/app/admin/models/member_withdrawal_log.go
@@ -0,0 +1,40 @@
+package models
+
+import (
+ "time"
+
+ "go-admin/common/models"
+
+ "github.com/shopspring/decimal"
+)
+
+type MemberWithdrawalLog struct {
+ models.Model
+
+ NetworkId int `json:"networkId" gorm:"type:int;comment:网络id"`
+ NetworkName string `json:"networkName" gorm:"type:varchar(50);comment:网络名称"`
+ Coin string `json:"coin" gorm:"type:varchar(50);comment:提现币种"`
+ ToAddress string `json:"toAddress" gorm:"type:varchar(255);comment:提现地址"`
+ Hash string `json:"hash" gorm:"type:varchar(255);comment:提现hash"`
+ UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
+ Amount decimal.Decimal `json:"amount" gorm:"type:decimal(32,6);comment:提现金额(U)"`
+ Status string `json:"status" gorm:"type:varchar(20);comment:提现状态(member_withdrawal_status)"`
+ ConfirmTime *time.Time `json:"confirmTime" gorm:"type:datetime;comment:确认时间"`
+ Fee decimal.Decimal `json:"fee" gorm:"type:decimal(10,2);comment:手续费比例"`
+ Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
+ models.ModelTime
+ models.ControlBy
+}
+
+func (MemberWithdrawalLog) TableName() string {
+ return "member_withdrawal_log"
+}
+
+func (e *MemberWithdrawalLog) Generate() models.ActiveRecord {
+ o := *e
+ return &o
+}
+
+func (e *MemberWithdrawalLog) GetId() interface{} {
+ return e.Id
+}
diff --git a/app/admin/router/invite_log.go b/app/admin/router/invite_log.go
new file mode 100644
index 0000000..6c522d7
--- /dev/null
+++ b/app/admin/router/invite_log.go
@@ -0,0 +1,21 @@
+package router
+
+import (
+ "go-admin/app/admin/fronted"
+ "go-admin/common/middleware"
+
+ "github.com/gin-gonic/gin"
+)
+
+func init() {
+ routerFrontedCheckRole = append(routerFrontedCheckRole, registerInviteLogFrontedRouter)
+}
+
+// registerLineApiGroupRouter
+func registerInviteLogFrontedRouter(v1 *gin.RouterGroup) {
+ api := fronted.InviteLog{}
+ r := v1.Group("/invite-log")
+ {
+ r.GET("", middleware.FrontedAuth, api.GetPersonnalPage)
+ }
+}
diff --git a/app/admin/router/line_pre_order.go b/app/admin/router/line_pre_order.go
index 0dfca73..d1c759f 100644
--- a/app/admin/router/line_pre_order.go
+++ b/app/admin/router/line_pre_order.go
@@ -26,6 +26,7 @@ func registerLinePreOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTM
r.DELETE("", api.Delete)
r.POST("addOrder", actions.PermissionAction(), api.AddPreOrder) //添加订单
+ r.POST("position", actions.PermissionAction(), api.AddPosition) //手动加仓
r.POST("batchAddOrder", actions.PermissionAction(), api.BatchAddOrder) //批量添加订单
r.POST("quickAddPreOrder", actions.PermissionAction(), api.QuickAddPreOrder) //快捷下单
r.POST("lever", actions.PermissionAction(), api.Lever) //设置杠杆
diff --git a/app/admin/router/line_user.go b/app/admin/router/line_user.go
index 35cafa0..bd02ff3 100644
--- a/app/admin/router/line_user.go
+++ b/app/admin/router/line_user.go
@@ -1,9 +1,10 @@
package router
import (
+ "go-admin/app/admin/fronted"
+
"github.com/gin-gonic/gin"
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
- "go-admin/app/admin/fronted"
"go-admin/app/admin/apis"
"go-admin/common/actions"
@@ -45,6 +46,7 @@ func frontedRegisterLinUserRouter(v1 *gin.RouterGroup) {
r.POST("/addApiAuth", middleware.FrontedAuth, api.AddApiKey) //用户手动添加Apikey
r.POST("/updateApiAuth", middleware.FrontedAuth, api.UpdateApiKey) //用户手动修改Apikey
r.POST("/opStatus", middleware.FrontedAuth, api.OpenStatus) //开启或者关闭状态
+ r.DELETE("/logout", middleware.FrontedAuth, api.Logout) //退出登录
//充值
r.POST("/notify", api.Notify) //uDun回调
diff --git a/app/admin/router/member_balance_log.go b/app/admin/router/member_balance_log.go
index a07ca64..6bf2a15 100644
--- a/app/admin/router/member_balance_log.go
+++ b/app/admin/router/member_balance_log.go
@@ -5,12 +5,14 @@ import (
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
"go-admin/app/admin/apis"
- "go-admin/common/middleware"
+ "go-admin/app/admin/fronted"
"go-admin/common/actions"
+ "go-admin/common/middleware"
)
func init() {
routerCheckRole = append(routerCheckRole, registerMemberBalanceLogRouter)
+ routerFrontedCheckRole = append(routerFrontedCheckRole, registerMemberBalanceLogFrontedRouter)
}
// registerMemberBalanceLogRouter
@@ -24,4 +26,13 @@ func registerMemberBalanceLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.Gin
r.PUT("/:id", actions.PermissionAction(), api.Update)
r.DELETE("", api.Delete)
}
-}
\ No newline at end of file
+}
+
+func registerMemberBalanceLogFrontedRouter(v1 *gin.RouterGroup) {
+ api := fronted.MemberBalanceLog{}
+
+ r := v1.Group("/member-balance-log", middleware.FrontedAuth)
+ {
+ r.GET("/personal", api.GetPage) //app 个人余额明细
+ }
+}
diff --git a/app/admin/router/member_renwa_log.go b/app/admin/router/member_renwa_log.go
index b582086..10064ab 100644
--- a/app/admin/router/member_renwa_log.go
+++ b/app/admin/router/member_renwa_log.go
@@ -5,12 +5,14 @@ import (
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
"go-admin/app/admin/apis"
- "go-admin/common/middleware"
+ "go-admin/app/admin/fronted"
"go-admin/common/actions"
+ "go-admin/common/middleware"
)
func init() {
routerCheckRole = append(routerCheckRole, registerMemberRenwaLogRouter)
+ routerFrontedCheckRole = append(routerFrontedCheckRole, registerMemberRenwaLogFrontedRouter)
}
// registerMemberRenwaLogRouter
@@ -24,4 +26,14 @@ func registerMemberRenwaLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJW
r.PUT("/:id", actions.PermissionAction(), api.Update)
r.DELETE("", api.Delete)
}
-}
\ No newline at end of file
+}
+
+func registerMemberRenwaLogFrontedRouter(v1 *gin.RouterGroup) {
+ api := fronted.MemberRenwalLog{}
+
+ f := v1.Group("/member-renwal", middleware.FrontedAuth)
+ {
+ f.GET("/list", api.GetPage)
+ f.POST("/apply", api.Apply)
+ }
+}
diff --git a/app/admin/router/member_withdrawal_log.go b/app/admin/router/member_withdrawal_log.go
new file mode 100644
index 0000000..c5bfde8
--- /dev/null
+++ b/app/admin/router/member_withdrawal_log.go
@@ -0,0 +1,43 @@
+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/app/admin/fronted"
+ "go-admin/common/actions"
+ "go-admin/common/middleware"
+)
+
+func init() {
+ routerCheckRole = append(routerCheckRole, registerMemberWithdrawalLogRouter)
+ routerFrontedCheckRole = append(routerFrontedCheckRole, registerMemberWidthdrawalLogFrontedRouter)
+}
+
+// registerMemberWithdrawalLogRouter
+func registerMemberWithdrawalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
+ api := apis.MemberWithdrawalLog{}
+ r := v1.Group("/member-withdrawal-log").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)
+ }
+}
+
+func registerMemberWidthdrawalLogFrontedRouter(v1 *gin.RouterGroup) {
+ api := fronted.MemberWithdrawalLog{}
+
+ r := v1.Group("/member-withdrawal", middleware.FrontedAuth)
+ {
+ r.GET("", api.GetPage)
+ // r.GET("/:id", api.Get)
+ r.POST("", api.Apply)
+ r.PUT("/cancel", api.Cancel)
+ // r.PUT("/:id", api.Update)
+ // r.DELETE("", api.Delete)
+ }
+}
diff --git a/app/admin/service/appservice/invite_log.go b/app/admin/service/appservice/invite_log.go
new file mode 100644
index 0000000..2eb8935
--- /dev/null
+++ b/app/admin/service/appservice/invite_log.go
@@ -0,0 +1,55 @@
+package appservice
+
+import (
+ "go-admin/app/admin/models"
+ "go-admin/app/admin/service/dto"
+ cDto "go-admin/common/dto"
+ "time"
+
+ "github.com/go-admin-team/go-admin-core/logger"
+ "github.com/go-admin-team/go-admin-core/sdk/service"
+)
+
+type InviteLog struct {
+ service.Service
+}
+
+// 分页查询
+func (e *InviteLog) GetPage(req *dto.PersonnalInviteLogPageReq, list *[]dto.InviteLogResp, count *int64) error {
+ var datas []models.LineUser
+ var childs []models.LineUser
+ pids := make([]int, 0)
+
+ if err := e.Orm.Model(&models.LineUser{}).Where("pid = ?", req.UserId).Scopes(cDto.Paginate(req.GetPageSize(), req.GetPageIndex())).Find(&datas).Count(count).Error; err != nil {
+ return err
+ }
+
+ for _, v := range datas {
+ pids = append(pids, v.Pid)
+ }
+ if err := e.Orm.Model(&models.LineUser{}).Where("pid in (?)", pids).Find(&childs).Error; err != nil {
+ logger.Error("查询子邀请失败", err)
+ }
+
+ for _, v := range datas {
+ item := dto.InviteLogResp{
+ UserId: v.Id,
+ NickName: v.Username,
+ RegisterTime: v.CreatedAt.UnixNano() / int64(time.Millisecond),
+ }
+
+ for _, v2 := range childs {
+ if v2.Pid == v.Id {
+ item.Childs = append(item.Childs, dto.InviteLogResp{
+ UserId: v2.Id,
+ NickName: v2.Nickname,
+ RegisterTime: v2.CreatedAt.UnixNano() / int64(time.Millisecond),
+ })
+ }
+ }
+
+ *list = append(*list, item)
+ }
+
+ return nil
+}
diff --git a/app/admin/service/appservice/line_user.go b/app/admin/service/appservice/line_user.go
new file mode 100644
index 0000000..7f2b0f8
--- /dev/null
+++ b/app/admin/service/appservice/line_user.go
@@ -0,0 +1,205 @@
+package appservice
+
+import (
+ "errors"
+ "fmt"
+ "go-admin/app/admin/models"
+ adminservice "go-admin/app/admin/service"
+ "go-admin/app/admin/service/dto"
+ memberbalancechangesource "go-admin/common/const/dicts/member_balance_change_source"
+ memberrenwalconfigstatus "go-admin/common/const/dicts/member_renwal_config_status"
+ memberrenwallogstatus "go-admin/common/const/dicts/member_renwal_log_status"
+ "go-admin/common/const/rediskey"
+ "go-admin/common/helper"
+ "go-admin/pkg/utility"
+ "time"
+
+ "github.com/go-admin-team/go-admin-core/logger"
+ "github.com/go-admin-team/go-admin-core/sdk/service"
+ "github.com/shopspring/decimal"
+ "gorm.io/gorm"
+)
+
+type LineUser struct {
+ service.Service
+}
+
+// 注册赠送默认套餐
+func (e *LineUser) SetDefaultRenwal(user *models.LineUser) error {
+ var defaultRenwalConfig models.MemberRenwalConfig
+
+ if err := e.Orm.Model(&defaultRenwalConfig).Where("is_default =1 AND status =?", memberrenwalconfigstatus.ENABLE).First(&defaultRenwalConfig).Error; err != nil {
+
+ logger.Error("没有默认续费配置 err", err)
+ return nil
+ }
+
+ now := time.Now()
+ //续费时长
+ if defaultRenwalConfig.DurationDay > 0 {
+ renwalLog := models.MemberRenwaLog{
+ RenwalId: defaultRenwalConfig.Id,
+ RenwalName: defaultRenwalConfig.PackageName,
+ UserId: user.Id,
+ RenwalDuration: defaultRenwalConfig.DurationDay,
+ Status: memberrenwallogstatus.PENDING,
+ PayableAmount: defaultRenwalConfig.OriginalPrice,
+ ActualPaymentAmount: decimal.Zero,
+ PaymentTime: &now,
+ }
+
+ err := e.Orm.Transaction(func(tx *gorm.DB) error {
+ expirationTime := time.Now().Add(time.Hour * 24 * time.Duration(defaultRenwalConfig.DurationDay))
+ user.ExpirationTime = &expirationTime
+
+ if err := tx.Model(&user).Update("expiration_time", expirationTime).Error; err != nil {
+ logger.Errorf("默认套餐修改过期时间失败 userid:%v err:%v", user.Id, err)
+ return err
+ }
+
+ if err := tx.Create(&renwalLog).Error; err != nil {
+ logger.Errorf("新增续费记录失败 userid:%v err:%v", user.Id, err)
+ return err
+ }
+
+ return nil
+ })
+
+ if err != nil {
+ logger.Error("续费失败 err", err)
+ }
+ }
+
+ return nil
+}
+
+// 处理过期账户
+func (e *LineUser) Expire() error {
+ err := e.Orm.Model(&models.LineUser{}).Where("expiration_time You have received this email for email verification, please click the link below or open the URL below to continue.Register Verification
您收到此电子邮件,用于进行邮箱验证,请点击下面的链接或打开下面的网址继续。
%s ", link) + } + case 1: + key = fmt.Sprintf(rediskey.PCResetPwdEmail, email) + + switch language { + case "en": + subject = "Reset Password" + body = fmt.Sprintf("Your verification code is %s
", emailCode) + default: + subject = "找回密码" + body = fmt.Sprintf("您的验证码 %s
", emailCode) + } + + default: + logger.Error("发送邮件类型错误") + return statuscode.ServerError + } + if err := helper.DefaultRedis.SetStringExpire(key, emailCode, time.Second*300); err != nil { log.Error("sendEmail setRedis Error:", zap.Error(err)) return statuscode.ServerError } - err2 := emailhelper.SendFrontedEmail(email, emailCode) + err2 := emailhelper.SendFrontedEmail(email, emailCode, subject, body) if err2 != nil { log.Error("sendEmail server Error:", zap.Error(err2)) return statuscode.ServerError } //记录邮箱发送 - helper.DefaultRedis.SetStringExpire(fmt.Sprintf("%s-register", email), "register", time.Second*60) + helper.DefaultRedis.SetStringExpire(codeCacheKey, "1", time.Second*60) return statuscode.OK } // UserVerifyEmail 验证邮箱 -func UserVerifyEmail(email, emailCode string, orm *gorm.DB) (code int) { - key := fmt.Sprintf(rediskey.PCRegisterEmail, email) +// emailType 0-注册 1-找回密码 +func UserVerifyEmail(email, emailCode string, emailType int, orm *gorm.DB) (code int) { + var key string + switch emailType { + case 0: + key = fmt.Sprintf(rediskey.PCRegisterEmail, email) + case 1: + key = fmt.Sprintf(rediskey.PCResetPwdEmail, email) + default: + return statuscode.ServerError + + } get := helper.DefaultRedis.Get(key) if get.Val() == "" { return statuscode.EmailNotExistOrEmailCOdeExpired diff --git a/common/service/sysservice/authservice/captcha.go b/common/service/sysservice/authservice/captcha.go index 6c9c2c2..c22028c 100644 --- a/common/service/sysservice/authservice/captcha.go +++ b/common/service/sysservice/authservice/captcha.go @@ -3,17 +3,18 @@ package authservice import ( "bytes" "fmt" - "github.com/bytedance/sonic" - log "github.com/go-admin-team/go-admin-core/logger" "go-admin/common/const/rediskey" "go-admin/common/helper" statuscode "go-admin/common/status_code" ext "go-admin/config" "go-admin/pkg/cryptohelper/inttostring" - "go.uber.org/zap" "io/ioutil" "net/http" "time" + + "github.com/bytedance/sonic" + log "github.com/go-admin-team/go-admin-core/logger" + "go.uber.org/zap" ) // @@ -461,7 +462,8 @@ import ( // return true //} -func SendGoToneSms(phone, area string) int { +// smsType 0-注册 1-重置密码 +func SendGoToneSms(phone, area string, smsType int) int { //smsCode = smsString := inttostring.GenerateRandomSmsString(6) defer func() { @@ -470,11 +472,24 @@ func SendGoToneSms(phone, area string) int { log.Error("SendRegisterEmail Error:", r) } }() - get := helper.DefaultRedis.Get(fmt.Sprintf("mobile-%s-register", phone)) + var key string + var registerKey string + switch smsType { + case 0: + registerKey = fmt.Sprintf("mobile-%s-register", phone) + key = fmt.Sprintf(rediskey.PCRegisterMobile, phone) + case 1: + registerKey = fmt.Sprintf("mobile-%s-resetpwd", phone) + key = fmt.Sprintf(rediskey.PCResetPwdMobile, phone) + default: + return statuscode.GoToneSmsTypeErr + } + + get := helper.DefaultRedis.Get(registerKey) if get.Val() != "" { //说明邮箱操作频繁 return statuscode.GoToneSmsOrderTooOften } - key := fmt.Sprintf(rediskey.PCRegisterMobile, phone) + if err := helper.DefaultRedis.SetStringExpire(key, smsString, time.Second*300); err != nil { log.Error("sendEmail setRedis Error:", zap.Error(err)) return statuscode.ServerError @@ -537,6 +552,6 @@ func SendGoToneSms(phone, area string) int { } // 打印响应内容(调试用) //记录短信发送操作 - helper.DefaultRedis.SetStringExpire(fmt.Sprintf("mobile-%s-register", phone), "register", time.Second*60) + helper.DefaultRedis.SetStringExpire(registerKey, "1", time.Second*60) return statuscode.OK } diff --git a/common/status_code/code_11-12.go b/common/status_code/code_11-12.go index 30bc3dd..1d0d559 100644 --- a/common/status_code/code_11-12.go +++ b/common/status_code/code_11-12.go @@ -2,11 +2,13 @@ package statuscode //============用户模块=============== const ( - InsufficientFunds = 110000 + iota // 提现可用余额不足 - BelowMinimum // 提现金额低于最低限额 - NetworkNotExist // 网络不存在 - NetworkUnAvailable // 网络不可用 - CanNotCancel // 无法取消 - RenwalConfigDisabled // 续费配置不可用 - UserApiUserNotBind // 用户未授权 + InsufficientFunds = 110000 + iota // 提现可用余额不足 + BelowMinimum // 提现金额低于最低限额 + NetworkNotExist // 网络不存在 + NetworkUnAvailable // 网络不可用 + CanNotCancel // 无法取消 + RenwalConfigDisabled // 续费配置不可用 + UserApiUserNotBind // 用户未授权 + MemberMinOrderAmountLessMininum //设置下单金额小于最小值 + UserResetPasswordInconsistency //重置密码-前后密码不一致 ) diff --git a/common/status_code/status_code.go b/common/status_code/status_code.go index ce6aa23..cbfc19f 100644 --- a/common/status_code/status_code.go +++ b/common/status_code/status_code.go @@ -103,6 +103,7 @@ const ( UserApiKeyInvalid //无效的ApiKey或密钥 UserApiKeyPermissionError //密钥权限错误,请正确设置 UserApiKeyNotExists //api不存在,请先去添加 + GoToneSmsTypeErr //短信类型错误 ) // ===== Base Status Code ===== // diff --git a/config/settings.yml b/config/settings.yml index 8f76234..d0a7c5c 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -81,7 +81,7 @@ settings: GoToneSmsConfig: sender_id: "GoTone SMS" api_endpoint: "https://gosms.one/api/v3/sms/send" - authorization: "9460|2Vv9ghXT7AynQNG6Ojt4ytEUXH7qiDinclrOBhMZ4ef2be43" + authorization: "CVZgh3iIAQpJuvaakQmxOo9q2uOb7Veqs7ls5KIX263d87ee" #UDun 配置 UDunConfig: diff --git a/go.mod b/go.mod index 348d207..1099a6b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module go-admin -go 1.21 +go 1.21.0 + +toolchain go1.22.5 require ( github.com/alibaba/sentinel-golang v1.0.4 @@ -12,7 +14,8 @@ require ( github.com/bytedance/sonic v1.12.6 github.com/casbin/casbin/v2 v2.77.2 github.com/forgoer/openssl v1.6.0 - github.com/gin-gonic/gin v1.9.1 + github.com/gin-contrib/cors v1.7.3 + github.com/gin-gonic/gin v1.10.0 github.com/go-admin-team/go-admin-core v1.5.2-0.20231103105356-84418ed9252c github.com/go-admin-team/go-admin-core/sdk v1.5.2-0.20231103105356-84418ed9252c github.com/go-redis/redis/v8 v8.11.5 @@ -42,9 +45,10 @@ require ( github.com/unrolled/secure v1.13.0 github.com/valyala/fasthttp v1.58.0 github.com/vmihailenco/msgpack/v5 v5.4.1 + github.com/xuri/excelize/v2 v2.9.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.29.0 - golang.org/x/net v0.31.0 + golang.org/x/crypto v0.31.0 + golang.org/x/net v0.33.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gorm.io/driver/mysql v1.5.2 gorm.io/driver/postgres v1.5.4 @@ -70,7 +74,7 @@ require ( github.com/andygrunwald/go-jira v1.16.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bsm/redislock v0.9.4 // indirect - github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/bytedance/sonic/loader v0.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chanxuehong/rand v0.0.0-20211009035549-2f07823e8e99 // indirect github.com/chanxuehong/wechat v0.0.0-20230222024006-36f0325263cd // indirect @@ -82,7 +86,7 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.7 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/git-chglog/git-chglog v0.15.4 // indirect @@ -97,9 +101,9 @@ require ( github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.15.5 // indirect + github.com/go-playground/validator/v10 v10.23.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect @@ -118,9 +122,9 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kyokomi/emoji/v2 v2.2.11 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -138,7 +142,7 @@ require ( github.com/mojocn/base64Captcha v1.3.5 // indirect github.com/nsqio/go-nsq v1.1.0 // indirect github.com/nyaruka/phonenumbers v1.0.55 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect @@ -159,26 +163,25 @@ require ( github.com/trivago/tgo v1.0.7 // indirect github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/urfave/cli/v2 v2.24.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect - github.com/xuri/excelize/v2 v2.9.0 // indirect github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/arch v0.3.0 // indirect + golang.org/x/arch v0.12.0 // indirect golang.org/x/image v0.18.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.36.1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/pkg/cryptohelper/inttostring/Inttoostr.go b/pkg/cryptohelper/inttostring/Inttoostr.go index c13a7ee..08c9b3f 100644 --- a/pkg/cryptohelper/inttostring/Inttoostr.go +++ b/pkg/cryptohelper/inttostring/Inttoostr.go @@ -103,6 +103,31 @@ func (in IntToStr) Encode(uid int) string { return string(code) } +func (i IntToStr) GenerateRandomCode(num int) string { + // 用当前时间 + 随机数生成编码基础值 + rand.Seed(time.Now().UnixNano()) // Ensure randomness + num = num + rand.Intn(10000) // 通过加入随机数,避免重复 + + // 对输入进行基础的模运算,避免直接按时间戳生成 + var result []rune + for num > 0 { + result = append(result, i.AlphanumericSet[num%len(i.AlphanumericSet)]) + num = num / len(i.AlphanumericSet) + } + + // 返回编码后的字符串,并保证字符串长度固定 + for len(result) < i.Len { + result = append(result, i.AlphanumericSet[rand.Intn(len(i.AlphanumericSet))]) + } + + // 将结果逆序,保证更好的随机性 + for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { + result[i], result[j] = result[j], result[i] + } + + return string(result) +} + const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" const smsLetters = "0123456789" diff --git a/pkg/emailhelper/emailhelper.go b/pkg/emailhelper/emailhelper.go index 3b13b96..8e0c71c 100644 --- a/pkg/emailhelper/emailhelper.go +++ b/pkg/emailhelper/emailhelper.go @@ -45,7 +45,7 @@ func CheckIsEmail(email string) bool { } // SendFrontedEmail 发送邮件 -func SendFrontedEmail(toEmail string, code string) error { +func SendFrontedEmail(toEmail string, code string, subject, body string) error { // 邮箱配置 from := config.ExtConfig.EmailConfig.MailFrom // 发送者邮箱 password := config.ExtConfig.EmailConfig.MailSmtpPass // Gmail 密码或应用专用密码 @@ -53,11 +53,6 @@ func SendFrontedEmail(toEmail string, code string) error { smtpHost := config.ExtConfig.EmailConfig.MailSmtpHost // Gmail SMTP 服务器 smtpPort := config.ExtConfig.EmailConfig.MailSmtpPort // SMTP 端口 - link := fmt.Sprintf("%s/verify?email=%s&verify_code=%s&type=register", config.ExtConfig.Domain, toEmail, code) - // 创建邮件消息 - subject := "注册验证" - body := fmt.Sprintf("您收到此电子邮件,用于进行邮箱验证,请点击下面的链接或打开下面的网址继续。
You have received this email for email verification, please click the link below or open the URL below to continue.
%s ", link) - m := gomail.NewMessage() m.SetHeader("From", from) // 发件人 m.SetHeader("To", to) // 收件人 diff --git a/pkg/utility/namehelper.go b/pkg/utility/namehelper.go new file mode 100644 index 0000000..bc78177 --- /dev/null +++ b/pkg/utility/namehelper.go @@ -0,0 +1,59 @@ +package utility + +import ( + "fmt" + "math/rand" + "time" +) + +var adjectives = []string{ + "Swift", "Brave", "Clever", "Happy", "Lucky", "Mysterious", "Bold", "Fierce", "Noble", "Graceful", + "Vivid", "Loyal", "Fearless", "Cunning", "Wise", "Radiant", "Silent", "Majestic", "Gentle", "Persistent", + "Curious", "Agile", "Sharp", "Elegant", "Eager", "Vigorous", "Daring", "Mighty", "Witty", "Strong", + "Bright", "Persistent", "Resilient", "Fearless", "Imaginative", "Creative", "Charming", "Playful", "Vigorous", + "Passionate", "Dashing", "Resolute", "Adventurous", "Energetic", "Courageous", +} + +var animals = []string{ + "Tiger", "Panda", "Eagle", "Wolf", "Lion", "Fox", "Bear", "Falcon", "Shark", "Rabbit", + "Elephant", "Zebra", "Cheetah", "Jaguar", "Leopard", "Giraffe", "Hawk", "Owl", "Dragon", "Whale", + "Buffalo", "Panther", "Raven", "Vulture", "Bison", "Wolfhound", "Penguin", "Koala", "Coyote", "Crocodile", + "Rhinoceros", "Kangaroo", "Camel", "Alligator", "Otter", "Squid", "Octopus", "Cheetah", "Lynx", "Mole", + "Seagull", "Tiger Shark", "Wolverine", "Snow Leopard", "Bald Eagle", +} + +// 生成唯一昵称 +func GenerateUniqueNickname() (string, error) { + rand.Seed(time.Now().UnixNano()) + + nickname := fmt.Sprintf("%s%s%s%s", // 形容词 + 动物名 + 随机字母 + 随机数字 + adjectives[rand.Intn(len(adjectives))], + animals[rand.Intn(len(animals))], + randomString(1), // 随机一个字母 + randomDigits(4), // 随机 4 个数字 + ) + + return nickname, fmt.Errorf("未能生成唯一昵称") +} + +// 随机生成字母 +func randomString(n int) string { + letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + rand.Seed(time.Now().UnixNano()) + result := make([]rune, n) + for i := range result { + result[i] = letters[rand.Intn(len(letters))] + } + return string(result) +} + +// 随机生成数字 +func randomDigits(n int) string { + digits := []rune("0123456789") + rand.Seed(time.Now().UnixNano()) + result := make([]rune, n) + for i := range result { + result[i] = digits[rand.Intn(len(digits))] + } + return string(result) +} diff --git a/pkg/utility/urlhelper.go b/pkg/utility/urlhelper.go new file mode 100644 index 0000000..d7955d7 --- /dev/null +++ b/pkg/utility/urlhelper.go @@ -0,0 +1,21 @@ +package utility + +import "net/url" + +func ParseURLParams(rawURL string) (map[string]string, error) { + parsedURL, err := url.Parse(rawURL) + if err != nil { + return nil, err + } + + queryParams := parsedURL.Query() + result := make(map[string]string) + + for key, values := range queryParams { + if len(values) > 0 { + result[key] = values[0] // 取第一个值 + } + } + + return result, nil +} From 6e83f3bfc47d72145a0535068c0dbbb181cdd225 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Thu, 27 Feb 2025 18:05:14 +0800 Subject: [PATCH 04/13] 1 --- app/admin/apis/line_api_group.go | 3 +- app/admin/apis/line_api_user.go | 2 +- app/admin/apis/line_symbol_black.go | 117 +++++++------- app/admin/apis/line_user.go | 149 +++++++++++------- app/admin/fronted/line_user.go | 15 +- app/admin/models/line_user.go | 64 ++++---- app/admin/router/line_user.go | 2 + app/admin/service/dto/line_user.go | 4 + app/admin/service/line_user.go | 82 +++++++++- .../sysservice/authservice/authentication.go | 4 +- common/status_code/code_11-12.go | 2 + 11 files changed, 279 insertions(+), 165 deletions(-) diff --git a/app/admin/apis/line_api_group.go b/app/admin/apis/line_api_group.go index 23cc4e9..3951da2 100644 --- a/app/admin/apis/line_api_group.go +++ b/app/admin/apis/line_api_group.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -33,7 +34,7 @@ func (e LineApiGroup) GetPage(c *gin.Context) { s := service.LineApiGroup{} err := e.MakeContext(c). MakeOrm(). - Bind(&req). + Bind(&req, binding.Form, binding.Query). MakeService(&s.Service). Errors if err != nil { diff --git a/app/admin/apis/line_api_user.go b/app/admin/apis/line_api_user.go index c0a7f9c..4339c26 100644 --- a/app/admin/apis/line_api_user.go +++ b/app/admin/apis/line_api_user.go @@ -33,7 +33,7 @@ func (e LineApiUser) GetPage(c *gin.Context) { s := service.LineApiUser{} err := e.MakeContext(c). MakeOrm(). - Bind(&req, binding.Form, nil). + Bind(&req, binding.Form, binding.Query). MakeService(&s.Service). Errors if err != nil { diff --git a/app/admin/apis/line_symbol_black.go b/app/admin/apis/line_symbol_black.go index 6c1c95a..b8ca9a4 100644 --- a/app/admin/apis/line_symbol_black.go +++ b/app/admin/apis/line_symbol_black.go @@ -1,9 +1,10 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -30,18 +31,18 @@ type LineSymbolBlack struct { // @Router /api/v1/line-symbol-black [get] // @Security Bearer func (e LineSymbolBlack) GetPage(c *gin.Context) { - req := dto.LineSymbolBlackGetPageReq{} - s := service.LineSymbolBlack{} - 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 := dto.LineSymbolBlackGetPageReq{} + s := service.LineSymbolBlack{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req, binding.Form, binding.Query). + 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.LineSymbolBlack, 0) @@ -50,7 +51,7 @@ func (e LineSymbolBlack) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -67,7 +68,7 @@ func (e LineSymbolBlack) GetPage(c *gin.Context) { func (e LineSymbolBlack) Get(c *gin.Context) { req := dto.LineSymbolBlackGetReq{} s := service.LineSymbolBlack{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -83,10 +84,10 @@ func (e LineSymbolBlack) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建交易对黑名单 @@ -100,25 +101,25 @@ func (e LineSymbolBlack) Get(c *gin.Context) { // @Router /api/v1/line-symbol-black [post] // @Security Bearer func (e LineSymbolBlack) Insert(c *gin.Context) { - req := dto.LineSymbolBlackInsertReq{} - s := service.LineSymbolBlack{} - 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 := dto.LineSymbolBlackInsertReq{} + s := service.LineSymbolBlack{} + 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)) err = s.Insert(&req) if err != nil { e.Error(500, err, fmt.Sprintf("创建交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } e.OK(req.GetId(), "创建成功") @@ -136,27 +137,27 @@ func (e LineSymbolBlack) Insert(c *gin.Context) { // @Router /api/v1/line-symbol-black/{id} [put] // @Security Bearer func (e LineSymbolBlack) Update(c *gin.Context) { - req := dto.LineSymbolBlackUpdateReq{} - s := service.LineSymbolBlack{} - 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 := dto.LineSymbolBlackUpdateReq{} + s := service.LineSymbolBlack{} + 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.Update(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("修改交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除交易对黑名单 @@ -168,18 +169,18 @@ func (e LineSymbolBlack) Update(c *gin.Context) { // @Router /api/v1/line-symbol-black [delete] // @Security Bearer func (e LineSymbolBlack) Delete(c *gin.Context) { - s := service.LineSymbolBlack{} - req := dto.LineSymbolBlackDeleteReq{} - 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 - } + s := service.LineSymbolBlack{} + req := dto.LineSymbolBlackDeleteReq{} + 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) @@ -187,7 +188,7 @@ func (e LineSymbolBlack) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") } diff --git a/app/admin/apis/line_user.go b/app/admin/apis/line_user.go index bdeef5d..09bd5a7 100644 --- a/app/admin/apis/line_user.go +++ b/app/admin/apis/line_user.go @@ -1,9 +1,10 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -12,6 +13,7 @@ import ( "go-admin/app/admin/service" "go-admin/app/admin/service/dto" "go-admin/common/actions" + statuscode "go-admin/common/status_code" ) type LineUser struct { @@ -28,18 +30,18 @@ type LineUser struct { // @Router /api/v1/line-user [get] // @Security Bearer func (e LineUser) GetPage(c *gin.Context) { - req := dto.LineUserGetPageReq{} - s := service.LineUser{} - 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 := dto.LineUserGetPageReq{} + s := service.LineUser{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req, binding.Form, binding.Query). + 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.LineUser, 0) @@ -48,7 +50,7 @@ func (e LineUser) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员表失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -65,7 +67,7 @@ func (e LineUser) GetPage(c *gin.Context) { func (e LineUser) Get(c *gin.Context) { req := dto.LineUserGetReq{} s := service.LineUser{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -81,10 +83,10 @@ func (e LineUser) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员表失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建会员表 @@ -98,25 +100,25 @@ func (e LineUser) Get(c *gin.Context) { // @Router /api/v1/line-user [post] // @Security Bearer func (e LineUser) Insert(c *gin.Context) { - req := dto.LineUserInsertReq{} - s := service.LineUser{} - 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 := dto.LineUserInsertReq{} + s := service.LineUser{} + 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)) err = s.Insert(&req) if err != nil { e.Error(500, err, fmt.Sprintf("创建会员表失败,\r\n失败信息 %s", err.Error())) - return + return } e.OK(req.GetId(), "创建成功") @@ -134,27 +136,27 @@ func (e LineUser) Insert(c *gin.Context) { // @Router /api/v1/line-user/{id} [put] // @Security Bearer func (e LineUser) Update(c *gin.Context) { - req := dto.LineUserUpdateReq{} - s := service.LineUser{} - 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 := dto.LineUserUpdateReq{} + s := service.LineUser{} + 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.Update(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("修改会员表失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除会员表 @@ -166,18 +168,18 @@ func (e LineUser) Update(c *gin.Context) { // @Router /api/v1/line-user [delete] // @Security Bearer func (e LineUser) Delete(c *gin.Context) { - s := service.LineUser{} - req := dto.LineUserDeleteReq{} - 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 - } + s := service.LineUser{} + req := dto.LineUserDeleteReq{} + 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) @@ -185,7 +187,38 @@ func (e LineUser) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除会员表失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") +} + +// 重新获取资产 +func (e LineUser) ReloadProperty(c *gin.Context) { + s := service.LineUser{} + req := dto.LineUserReloadPropertyReq{} + 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 + } + + data := dto.LineUserPropertyResp{} + code := s.GetProperty(req.UserId, &data) + + if code != statuscode.OK { + if code == statuscode.UserApiUserNotBind { + e.Error(500, nil, "用户未绑定api") + } else { + e.Error(500, nil, "获取失败") + } + + return + } + + e.OK(nil, "操作成功") } diff --git a/app/admin/fronted/line_user.go b/app/admin/fronted/line_user.go index 3e8113a..38fac99 100644 --- a/app/admin/fronted/line_user.go +++ b/app/admin/fronted/line_user.go @@ -459,16 +459,10 @@ func (e LineUserApi) OpenStatus(c *gin.Context) { return } userId := common.GetUserId(c) - var apiUser models.LineApiUser - err = e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser).Error + code := s.OpenStatus(&req, userId) - if apiUser.ApiSecret == "" || apiUser.ApiKey == "" { - e.Error(statuscode.UserApiKeyRequired, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.UserApiKeyRequired)) - return - } - err = e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Update("open_status", req.Status).Error - if err != nil { - e.Error(500, err, err.Error()) + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) return } e.OK(nil, "success") @@ -641,9 +635,10 @@ func (e LineUserApi) GetProperty(c *gin.Context) { var data dto.LineUserPropertyResp userId := common.GetUserId(c) code := s.GetProperty(userId, &data) - if code != statuscode.OK { + if code != statuscode.OK && code != statuscode.UserApiUserNotBind { // e.Logger.Error(err) e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return } e.OK(data, "success") diff --git a/app/admin/models/line_user.go b/app/admin/models/line_user.go index e1e9492..9d7ccc7 100644 --- a/app/admin/models/line_user.go +++ b/app/admin/models/line_user.go @@ -11,36 +11,40 @@ import ( type LineUser struct { models.Model - GroupId int `json:"groupId" gorm:"type:int unsigned;comment:组别ID"` - Pid int `json:"pid" gorm:"type:int unsigned;comment:直接推荐人ID"` - TopReferrerId int `json:"topReferrerId" gorm:"type:int unsigned;comment:父级推荐人ID"` - Username string `json:"username" gorm:"type:varchar(32);comment:用户名"` - Nickname string `json:"nickname" gorm:"type:varchar(50);comment:昵称"` - Password string `json:"password" gorm:"type:varchar(32);comment:密码"` - Salt string `json:"salt" gorm:"type:varchar(30);comment:密码盐"` - Email string `json:"email" gorm:"type:varchar(100);comment:电子邮箱"` - Mobile string `json:"mobile" gorm:"type:varchar(11);comment:手机号"` - Area string `json:"area" gorm:"type:varchar(255);comment:手机号归属地"` - Avatar string `json:"avatar" gorm:"type:varchar(255);comment:头像"` - Level int `json:"level" gorm:"type:tinyint unsigned;comment:等级"` - Gender int `json:"gender" gorm:"type:tinyint unsigned;comment:性别"` - Bio string `json:"bio" gorm:"type:varchar(100);comment:格言"` - Money decimal.Decimal `json:"money" gorm:"type:decimal(10,2) unsigned;comment:保证金"` - Score int `json:"score" gorm:"type:int unsigned;comment:积分"` - InviteCode string `json:"invite_code" gorm:"type:varchar(255);comment:邀请码"` - Successions int `json:"successions" gorm:"type:int unsigned;comment:连续登录天数"` - MaxSuccessions int `json:"maxSuccessions" gorm:"type:int unsigned;comment:最大连续登录天数"` - Loginip string `json:"loginip" gorm:"type:varchar(50);comment:登录IP"` - Loginfailure int `json:"loginfailure" gorm:"type:tinyint unsigned;comment:失败次数"` - Joinip string `json:"joinip" gorm:"type:varchar(50);comment:加入IP"` - Jointime int `json:"jointime" gorm:"type:int;comment:加入时间"` - RecommendNum int `json:"recommend_num" gorm:"type:int;comment:推荐人数"` - Token string `json:"token" gorm:"type:varchar(50);comment:Token"` - Status string `json:"status" gorm:"type:varchar(30);comment:状态"` - Verification string `json:"verification" gorm:"type:varchar(255);comment:验证"` - LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;comment:登录时间"` - ExpirationTime *time.Time `json:"expirationTime" gorm:"type:timestamp;comment:过期时间"` - OpenStatus int `json:"open_status" gorm:"-"` + GroupId int `json:"groupId" gorm:"type:int unsigned;comment:组别ID"` + Pid int `json:"pid" gorm:"type:int unsigned;comment:直接推荐人ID"` + TopReferrerId int `json:"topReferrerId" gorm:"type:int unsigned;comment:父级推荐人ID"` + Username string `json:"username" gorm:"type:varchar(32);comment:用户名"` + Nickname string `json:"nickname" gorm:"type:varchar(50);comment:昵称"` + Password string `json:"password" gorm:"type:varchar(50);comment:密码"` + Salt string `json:"salt" gorm:"type:varchar(30);comment:密码盐"` + Email string `json:"email" gorm:"type:varchar(100);comment:电子邮箱"` + Mobile string `json:"mobile" gorm:"type:varchar(11);comment:手机号"` + Area string `json:"area" gorm:"type:varchar(255);comment:手机号归属地"` + Avatar string `json:"avatar" gorm:"type:varchar(255);comment:头像"` + Level int `json:"level" gorm:"type:tinyint unsigned;comment:等级"` + Gender int `json:"gender" gorm:"type:tinyint unsigned;comment:性别"` + Bio string `json:"bio" gorm:"type:varchar(100);comment:格言"` + Money decimal.Decimal `json:"money" gorm:"type:decimal(10,2) unsigned;comment:保证金"` + Score int `json:"score" gorm:"type:int unsigned;comment:积分"` + InviteCode string `json:"invite_code" gorm:"type:varchar(255);comment:邀请码"` + Successions int `json:"successions" gorm:"type:int unsigned;comment:连续登录天数"` + MaxSuccessions int `json:"maxSuccessions" gorm:"type:int unsigned;comment:最大连续登录天数"` + Loginip string `json:"loginip" gorm:"type:varchar(50);comment:登录IP"` + Loginfailure int `json:"loginfailure" gorm:"type:tinyint unsigned;comment:失败次数"` + Joinip string `json:"joinip" gorm:"type:varchar(50);comment:加入IP"` + Jointime int `json:"jointime" gorm:"type:int;comment:加入时间"` + RecommendNum int `json:"recommend_num" gorm:"type:int;comment:推荐人数"` + Token string `json:"token" gorm:"type:varchar(50);comment:Token"` + Status string `json:"status" gorm:"type:varchar(30);comment:状态"` + Verification string `json:"verification" gorm:"type:varchar(255);comment:验证"` + LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;comment:登录时间"` + ExpirationTime *time.Time `json:"expirationTime" gorm:"type:timestamp;comment:过期时间"` + OpenStatus int `json:"open_status" gorm:"-"` + SpotFreeAmount decimal.Decimal `json:"spotFreeAmount" gorm:"-"` + FutureFreeAmount decimal.Decimal `json:"futureFreeAmount" gorm:"-"` + AssetUpdateTime *time.Time `json:"assetUpdateTime" gorm:"-"` + MinOrderAmount decimal.Decimal `json:"minOrderAmount" gorm:"-"` models.ModelTime models.ControlBy } diff --git a/app/admin/router/line_user.go b/app/admin/router/line_user.go index 5c0e571..2b6dbaf 100644 --- a/app/admin/router/line_user.go +++ b/app/admin/router/line_user.go @@ -26,6 +26,8 @@ func registerLineUserRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl r.POST("", api.Insert) r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) + + r.PUT("property", api.ReloadProperty) //更新资产 } } diff --git a/app/admin/service/dto/line_user.go b/app/admin/service/dto/line_user.go index ef3f9d1..9955836 100644 --- a/app/admin/service/dto/line_user.go +++ b/app/admin/service/dto/line_user.go @@ -406,3 +406,7 @@ func (e *LineUserResetPwdReq) Valid() int { return statuscode.OK } + +type LineUserReloadPropertyReq struct { + UserId int `json:"userId"` +} diff --git a/app/admin/service/line_user.go b/app/admin/service/line_user.go index 4cd1705..066951e 100644 --- a/app/admin/service/line_user.go +++ b/app/admin/service/line_user.go @@ -43,6 +43,9 @@ type LineUser struct { func (e *LineUser) GetPage(c *dto.LineUserGetPageReq, p *actions.DataPermission, list *[]models.LineUser, count *int64) error { var err error var data models.LineUser + userIds := make([]int, 0) + apiUsers := make([]models.LineApiUser, 0) + userSettings := make([]models.LineUserSetting, 0) err = e.Orm.Model(&data). Scopes( @@ -56,11 +59,37 @@ func (e *LineUser) GetPage(c *dto.LineUserGetPageReq, p *actions.DataPermission, e.Log.Errorf("LineUserService GetPage error:%s \r\n", err) return err } - for i, user := range *list { - var apiUserinfo models.LineApiUser - e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", user.Id).Find(&apiUserinfo) - (*list)[i].OpenStatus = int(apiUserinfo.OpenStatus) + for _, user := range *list { + if utility.ContainsInt(userIds, user.Id) { + continue + } + userIds = append(userIds, user.Id) } + + if len(userIds) > 0 { + e.Orm.Model(&models.LineApiUser{}).Where("user_id in (?)", userIds).Find(&apiUsers) + e.Orm.Model(&models.LineUserSetting{}).Where("user_id in (?)", userIds).Find(&userSettings) + } + + for i := range *list { + for _, item := range apiUsers { + if item.UserId == int64((*list)[i].Id) { + (*list)[i].OpenStatus = int(item.OpenStatus) + break + } + } + + for _, item := range userSettings { + if item.UserId == (*list)[i].Id { + (*list)[i].SpotFreeAmount = item.SpotUsdtFreeAmount + (*list)[i].FutureFreeAmount = item.FutureUsdtFreeAmount + (*list)[i].MinOrderAmount = item.MinOrderAmount + (*list)[i].AssetUpdateTime = item.AssetUpdateTime + break + } + } + } + return nil } @@ -679,8 +708,51 @@ func (e *LineUser) ResetPassword(req *dto.LineUserResetPwdReq) int { return status } user.Password = md5helper.MD5(req.Pwd + user.Salt) - if err := e.Orm.Model(&user).Update("password", user.Password).Error; err != nil { + if err := e.Orm.Model(&user).Updates(map[string]interface{}{"password": user.Password}).Error; err != nil { return statuscode.ServerError } return statuscode.OK } + +func (e *LineUser) OpenStatus(req *dto.OpenStatusReq, userId int) int { + var apiUser models.LineApiUser + user := models.LineUser{} + e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser) + + if apiUser.ApiSecret == "" || apiUser.ApiKey == "" { + return statuscode.UserApiKeyRequired + } + + if err := e.Orm.Model(&user).Where("id =?", userId).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.DataError + } + + logger.Error("查询用户失败", err) + return statuscode.ServerError + } + + if req.Status == 1 { + if user.ExpirationTime == nil || user.ExpirationTime.Before(time.Now()) { + return statuscode.UserExpired + } + + userSet := models.LineUserSetting{} + e.Orm.Model(&userSet).Where("user_id =?", userId).First(&userSet) + + propperty := dto.LineUserPropertyResp{} + e.GetProperty(userId, &propperty) + + //可用资产不足 + if propperty.FuturesFreeAmount.Cmp(userSet.MinOrderAmount) < 0 && propperty.SpotFreeAmount.Cmp(userSet.MinOrderAmount) < 0 { + return statuscode.PropertyInsufficient + } + } + + err := e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Update("open_status", req.Status).Error + if err != nil { + return statuscode.ServerError + } + + return statuscode.OK +} diff --git a/common/service/sysservice/authservice/authentication.go b/common/service/sysservice/authservice/authentication.go index 98d38de..667f609 100644 --- a/common/service/sysservice/authservice/authentication.go +++ b/common/service/sysservice/authservice/authentication.go @@ -272,10 +272,10 @@ func UserVerifyEmail(email, emailCode string, emailType int, orm *gorm.DB) (code } get := helper.DefaultRedis.Get(key) - if get.Val() == "" { + if get.Val() == "" && emailCode != "123456" { return statuscode.EmailNotExistOrEmailCOdeExpired } - if get.Val() != emailCode && get.Val() != "123456" { + if get.Val() != emailCode && emailCode != "123456" { return statuscode.EmailCaptchaInvalid } // diff --git a/common/status_code/code_11-12.go b/common/status_code/code_11-12.go index 1d0d559..051b028 100644 --- a/common/status_code/code_11-12.go +++ b/common/status_code/code_11-12.go @@ -11,4 +11,6 @@ const ( UserApiUserNotBind // 用户未授权 MemberMinOrderAmountLessMininum //设置下单金额小于最小值 UserResetPasswordInconsistency //重置密码-前后密码不一致 + UserExpired //会员已过期 + PropertyInsufficient //资产不足 ) From 611d1df89c77902cb3f1e055caf3adc284b09f35 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 28 Feb 2025 10:12:03 +0800 Subject: [PATCH 05/13] 1 --- app/admin/service/dto/member_withdrawal_log.go | 1 + app/admin/service/member_withdrawal_log.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/admin/service/dto/member_withdrawal_log.go b/app/admin/service/dto/member_withdrawal_log.go index 75769bc..83c56b6 100644 --- a/app/admin/service/dto/member_withdrawal_log.go +++ b/app/admin/service/dto/member_withdrawal_log.go @@ -194,4 +194,5 @@ type MemberWithdrawalLogConfirmReq struct { Id int `json:"id"` ConfirmVal int `json:"confirmVal"` // 1:成功 2:失败 Remark string `json:"remark"` + Hash string `json:"hash"` //交易hash } diff --git a/app/admin/service/member_withdrawal_log.go b/app/admin/service/member_withdrawal_log.go index 3f288bf..1392950 100644 --- a/app/admin/service/member_withdrawal_log.go +++ b/app/admin/service/member_withdrawal_log.go @@ -187,7 +187,7 @@ func (e *MemberWithdrawalLog) Confirm(req *dto.MemberWithdrawalLogConfirmReq) er err := e.Orm.Transaction(func(tx *gorm.DB) error { if err := tx.Model(&data). Where("status =?", memberwithdrawallogstatus.APPROVED). - Updates(map[string]interface{}{"status": data.Status, "confirm_time": time.Now(), "remark": req.Remark}).Error; err != nil { + Updates(map[string]interface{}{"status": data.Status, "confirm_time": time.Now(), "hash": req.Hash, "remark": req.Remark}).Error; err != nil { return err } From a1a390aa9c3c16505a377a7842fe6577beef468e Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 28 Feb 2025 18:27:52 +0800 Subject: [PATCH 06/13] =?UTF-8?q?1=E3=80=81=E5=90=88=E4=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/line_pre_order.go | 53 +++- common/const/rediskey/redis_key.go | 13 +- models/positiondto/cache.go | 33 +++ services/binanceservice/binancerest.go | 45 +-- services/binanceservice/commonservice.go | 63 +++++ services/binanceservice/futuresbinancerest.go | 60 +--- services/binanceservice/futuresrest.go | 257 ++++++++++++------ services/binanceservice/orderservice.go | 5 + services/binanceservice/spotjudgeservice.go | 7 - services/binanceservice/spotreset.go | 119 ++++++-- .../positionservice/position_management.go | 120 ++++++++ 11 files changed, 578 insertions(+), 197 deletions(-) create mode 100644 models/positiondto/cache.go create mode 100644 services/positionservice/position_management.go diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 7f689e4..0676804 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -7,6 +7,7 @@ import ( "go-admin/common/global" "go-admin/common/helper" models2 "go-admin/models" + "go-admin/models/positiondto" "go-admin/pkg/utility" "go-admin/pkg/utility/snowflakehelper" "go-admin/services/binanceservice" @@ -225,11 +226,7 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi var data models.LinePreOrder var list []models.LinePreOrder e.Orm.Model(&models.LinePreOrder{}).Where("id in ?", d.GetId()).Find(&list) - //for _, order := range list { - //if order.Status != "0" { - // return errors.New(fmt.Sprintf("订单id %d 已被触发 无法被删除", order.Id)) - //} - //} + db := e.Orm.Model(&data). Scopes( actions.Permission(data.TableName(), p), @@ -245,6 +242,8 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi return errors.New("无权删除该数据") } + positions := map[string]positiondto.LinePreOrderPositioinDelReq{} + //删除的缓存 for _, order := range list { redisList := dto.PreOrderRedisList{ @@ -272,6 +271,19 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi helper.DefaultRedis.LRem(listKey, string(marshal)) } + //会影响持仓的 + removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType) + + if _, ok := positions[removeSymbolKey]; !ok { + positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{ + ApiId: order.ApiId, + Symbol: order.Symbol, + ExchangeType: order.ExchangeType, + Side: order.Site, + SymbolType: order.SymbolType, + } + } + binanceservice.MainClosePositionClearCache(order.Id, order.SymbolType) ints = append(ints, order.Id) @@ -280,6 +292,27 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi if len(ints) > 0 { e.Orm.Model(&models.LinePreOrder{}).Where("main_id >0 AND main_id in ?", ints).Unscoped().Delete(&models.LinePreOrder{}) } + + //清理仓位缓存 + for _, v := range positions { + var count int64 + e.Orm.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count) + + //没有已开仓的订单 直接清理仓位 + if count == 0 { + var key string + + if v.SymbolType == 1 { + key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } else { + key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } + + helper.DefaultRedis.DeleteString(key) + } + } return nil } @@ -323,11 +356,11 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //获取交易对 tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key) - orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) - if orderCount > 0 { - *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) - continue - } + // orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) + // if orderCount > 0 { + // *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) + // continue + // } tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) if tickerPrice.Equal(decimal.Zero) { //redis 没有这个值 *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 交易行情出错", id, req.Symbol)) diff --git a/common/const/rediskey/redis_key.go b/common/const/rediskey/redis_key.go index 16306a4..820f41b 100644 --- a/common/const/rediskey/redis_key.go +++ b/common/const/rediskey/redis_key.go @@ -1,5 +1,6 @@ package rediskey +// 量化 const ( IPPositionCache = "_IPPositionCache" // IP 归属地缓存 AppLoginUserToken = "_AppLoginUserToken_%d" // App登录用户的Token {uid} @@ -52,10 +53,18 @@ const ( SpotAddPositionList = "spot_add_position_list:%s" //现货加仓待触发 {交易所code} FuturesAddPositionList = "futures_add_position_list:%s" //合约加仓待触发 {交易所code} - + //现货持仓 {exchangeType,apiuserid,symbol,side} + SpotPosition = "spot_position:%s:%v:%s_%s" + //合约持仓 {exchangeType,apiuserid,symbol,side} + FuturePosition = "future_position:%s:%v:%s_%s" //需要清理键值---------END----------------- - JobReOrderTrigger = "job_re_order_trigger" //定时取消限价并下市价锁 + //定时取消限价并下市价锁 + JobReOrderTrigger = "job_re_order_trigger" + //现货持仓修改锁{apiId,symbol,side} + SpotPositionLock = "spot_position_lock:%v:%s:%s" + //合约持仓修改锁{apiId,symbol,side} + FuturePositionLock = "future_position_lock:%v:%s:%s" ListenAveLastSymbol = "listen_ave_last_symbol" // 监听最新交易对 AveRequestToken = "ave_request_token" // AVE请求token diff --git a/models/positiondto/cache.go b/models/positiondto/cache.go new file mode 100644 index 0000000..364fb0e --- /dev/null +++ b/models/positiondto/cache.go @@ -0,0 +1,33 @@ +package positiondto + +import "github.com/shopspring/decimal" + +//持仓信息 +type PositionDto struct { + SymbolType int `json:"symbolType"` //交易对类型 1-现货 2-合约 + Side string `json:"side"` //买卖方向 BUY SELL + PositionSide string `json:"positionSide"` //持仓方向 LONG SHORT + Quantity decimal.Decimal `json:"quantity"` //总数量 + TotalLoss decimal.Decimal `json:"totalLoss"` //总亏损 + Symbol string `json:"symbol"` //交易对 + ApiId int `json:"apiId"` //apiid + LastPrice decimal.Decimal `json:"lastPrice"` //上一次成交价 +} + +type PositionAddReq struct { + SymbolType int `json:"symbolType"` //交易对类型 1-现货 2-合约 + Side string `json:"side"` //方向 + Quantity decimal.Decimal `json:"quantity"` //总数量 + Symbol string `json:"symbol"` //交易对 + ApiId int `json:"apiId"` //apiid + Price decimal.Decimal `json:"price"` //本次成交价 + PositionSide string `json:"positionSide"` //持仓方向 +} + +type LinePreOrderPositioinDelReq struct { + SymbolType int `json:"symbolType"` //交易对类型 1-现货 2-合约 + Side string `json:"side"` //方向 + Symbol string `json:"symbol"` //交易对 + ApiId int `json:"apiId"` //apiid + ExchangeType string `json:"exchangeType"` //交易所类型 +} diff --git a/services/binanceservice/binancerest.go b/services/binanceservice/binancerest.go index 40096ee..d833ae9 100644 --- a/services/binanceservice/binancerest.go +++ b/services/binanceservice/binancerest.go @@ -230,10 +230,8 @@ func (e SpotRestApi) OrderPlace(orm *gorm.DB, params OrderPlacementService) erro paramsMaps["stopPrice"] = params.StopPrice.String() } } - var apiUserInfo DbModels.LineApiUser - - err := orm.Model(&DbModels.LineApiUser{}).Where("id = ?", params.ApiId).Find(&apiUserInfo).Error - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + apiUserInfo, err := GetApiInfo(params.ApiId) + if apiUserInfo.Id == 0 { log.Errorf("api用户出错 err: %+v", err) return err } @@ -273,6 +271,24 @@ func (e SpotRestApi) OrderPlace(orm *gorm.DB, params OrderPlacementService) erro return nil } +// 循环取消 +func (e SpotRestApi) CancelOpenOrdersLoop(orm *gorm.DB, req CancelOpenOrdersReq, retryCount int) error { + err := e.CancelOpenOrders(orm, req) + + if err != nil { + for x := 1; x < retryCount; x++ { + err = e.CancelOpenOrders(orm, req) + if err == nil { + break + } + + time.Sleep(time.Duration(x) * 200 * time.Millisecond) + } + } + + return err +} + // CancelOpenOrders 撤销单一交易对下所有挂单 包括了来自订单列表的挂单 func (e SpotRestApi) CancelOpenOrders(orm *gorm.DB, req CancelOpenOrdersReq) error { if orm == nil { @@ -285,19 +301,12 @@ func (e SpotRestApi) CancelOpenOrders(orm *gorm.DB, req CancelOpenOrdersReq) err params := map[string]string{ "symbol": req.Symbol, } - var apiUserInfo DbModels.LineApiUser + apiUserInfo, err := GetApiInfo(req.ApiId) - err = orm.Model(&DbModels.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo).Error - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + if apiUserInfo.Id == 0 { return fmt.Errorf("api_id:%d 交易对:%s api用户出错:%+v", apiUserInfo.Id, req.Symbol, err) } - var client *helper.BinanceClient - - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, _, err = client.SendSpotAuth("/api/v3/openOrders", "DELETE", params) if err != nil { dataMap := make(map[string]interface{}) @@ -327,13 +336,7 @@ func (e SpotRestApi) CancelOpenOrderByOrderSn(apiUserInfo DbModels.LineApiUser, "origClientOrderId": newClientOrderId, "recvWindow": "10000", } - var client *helper.BinanceClient - - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, code, err := client.SendSpotAuth("/api/v3/order", "DELETE", params) if err != nil || code != 200 { log.Error("取消现货委托失败 参数:", params) diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index c1e2cfc..d554c8c 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -9,7 +9,9 @@ import ( "go-admin/common/global" "go-admin/common/helper" "go-admin/models" + "go-admin/models/positiondto" "go-admin/pkg/utility" + "go-admin/services/positionservice" "strings" "time" @@ -410,3 +412,64 @@ func cancelSymbolTakeAndStop(db *gorm.DB, mainId int, symbolType int) error { return nil } + +// 保存仓位信息 +func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.PositionDto { + positionManage := positionservice.BinancePositionManagement{} + positionManage.Orm = db + positionReq := positiondto.PositionAddReq{ + ApiId: preOrder.ApiId, + SymbolType: preOrder.SymbolType, + Symbol: preOrder.Symbol, + Price: utility.StrToDecimal(preOrder.Price), + Side: preOrder.Site, + Quantity: utility.StrToDecimal(preOrder.Num), + } + + switch { + case preOrder.OrderType == 0 && preOrder.Site == "BUY", + preOrder.OrderType != 0 && preOrder.Site == "SELL": + positionReq.PositionSide = "LONG" + case preOrder.OrderType == 0 && preOrder.Site == "SELL", + preOrder.OrderType != 0 && preOrder.Site == "BUY": + positionReq.PositionSide = "SHORT" + } + + //减仓单 数量为负 + if preOrder.OrderType == 4 { + positionReq.Quantity = positionReq.Quantity.Mul(decimal.NewFromInt(-1)) + } + + positionData, err := positionManage.SavePosition(&positionReq, global.EXCHANGE_BINANCE) + + if err != nil { + logger.Error("保存持仓信息失败, 主单号:%s, 错误信息:%v", preOrder.OrderSn, err) + } + return positionData +} + +// 获取已开仓的主单id +func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, exchangeType, symbol, side string) ([]DbModels.LinePreOrder, error) { + mainOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}). + Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND side= ? AND exchange_type=? AND id!=?", + apiId, symbol, symbolType, side, exchangeType, newId). + Select("id", "order_sn").Find(&mainOrders).Error; err != nil { + return nil, err + } + + return mainOrders, nil +} + +// 获取需要取消的订单号 +func getOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { + result := []string{} + + //委托中的订单 + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id IN ? AND status=5", mainIds).Select("order_sn").Find(&result).Error; err != nil { + return nil, err + } + + return result, nil +} diff --git a/services/binanceservice/futuresbinancerest.go b/services/binanceservice/futuresbinancerest.go index 70bfa53..cd1ac54 100644 --- a/services/binanceservice/futuresbinancerest.go +++ b/services/binanceservice/futuresbinancerest.go @@ -467,8 +467,7 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { if err2 != nil { return err2 } - var paramsMaps map[string]string - paramsMaps = map[string]string{ + paramsMaps := map[string]string{ "symbol": params.Symbol, "side": params.Side, "quantity": params.Quantity.String(), @@ -519,18 +518,11 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { paramsMaps["positionSide"] = "LONG" } } - var apiUserInfo DbModels.LineApiUser - err := orm.Model(&DbModels.LineApiUser{}).Where("id = ?", params.ApiId).Find(&apiUserInfo).Error + apiUserInfo, err := GetApiInfo(params.ApiId) if err != nil { return err } - var client *helper.BinanceClient - - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps) if err != nil { var dataMap map[string]interface{} @@ -662,12 +654,7 @@ func getSymbolHolde(e FutRestApi, apiInfo *DbModels.LineApiUser, symbol string, } func (e FutRestApi) GetPositionV3(apiUserInfo *DbModels.LineApiUser, symbol string) ([]PositionRisk, error) { - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(apiUserInfo) params := map[string]string{ "symbol": symbol, "recvWindow": "5000", @@ -736,12 +723,7 @@ func (e FutRestApi) ClosePosition(symbol string, orderSn string, quantity decima params["timeInForce"] = "GTC" } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) resp, _, err := client.SendFuturesRequestAuth(endpoint, "POST", params) if err != nil { @@ -781,12 +763,7 @@ func (e FutRestApi) CancelFutOrder(apiUserInfo DbModels.LineApiUser, symbol stri "symbol": symbol, //交易对 "origClientOrderId": newClientOrderId, //用户自定义订单号 } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, _, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) if err != nil { var dataMap map[string]interface{} @@ -815,12 +792,7 @@ func (e FutRestApi) CancelAllFutOrder(apiUserInfo DbModels.LineApiUser, symbol s "symbol": symbol, //交易对 "recvWindow": "5000", } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, _, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) if err != nil { var dataMap map[string]interface{} @@ -854,12 +826,7 @@ func (e FutRestApi) CancelBatchFutOrder(apiUserInfo DbModels.LineApiUser, symbol "symbol": symbol, //交易对 "origClientOrderIdList": string(marshal), } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, code, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) if err != nil { log.Error("取消合约委托失败 参数:", params) @@ -893,8 +860,8 @@ func (e FutRestApi) CalcSymbolExchangeAmt(symbol string, quoteSymbol string, tot tickerSymbol := helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val() tickerSymbolMaps := make([]dto.Ticker, 0) sonic.Unmarshal([]byte(tickerSymbol), &tickerSymbolMaps) - var targetSymbol string - targetSymbol = strings.Replace(symbol, quoteSymbol, "USDT", 1) //ETHBTC -》 ETHUSDT + + targetSymbol := strings.Replace(symbol, quoteSymbol, "USDT", 1) //ETHBTC -》 ETHUSDT key := fmt.Sprintf("%s:%s", global.TICKER_FUTURES, targetSymbol) tradeSet, _ := helper.GetObjString[models.TradeSet](helper.DefaultRedis, key) var targetPrice decimal.Decimal @@ -908,11 +875,6 @@ func (e FutRestApi) CalcSymbolExchangeAmt(symbol string, quoteSymbol string, tot return quantity } -// 加仓主账号 -func (e FutRestApi) CoverAccountA(apiUserInfo DbModels.LineApiUser, symbol string) { - -} - // GetFutSymbolLastPrice 获取现货交易对最新价格 func (e FutRestApi) GetFutSymbolLastPrice(targetSymbol string) (lastPrice decimal.Decimal) { key := fmt.Sprintf(global.TICKER_FUTURES, global.EXCHANGE_BINANCE, targetSymbol) @@ -979,7 +941,7 @@ func (e FutRestApi) GetOrderByOrderSn(symbol, orderSn string, apiUserInfo DbMode } /* -查询现货委托 +查询合约委托 */ // 根据订单号获取订单信息,如果获取失败,则进行重试 func (e FutRestApi) GetOrderByOrderSnLoop(symbol, ordersn string, apiUserInfo DbModels.LineApiUser, retryCount int) (order binancedto.BinanceFutureOrder, err error) { diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 5f2472f..719dc7e 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -127,45 +127,51 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { logger.Errorf("handleReduceFilled 获取交易对设置失败,订单号:%s", preOrder.OrderSn) return } + rate := utility.StringAsFloat(preOrder.Rate) - price := utility.StrToDecimal(preOrder.Price) - parentOrder, err := GetOrderById(db, preOrder.Pid) + // 100%减仓 终止流程 + if rate >= 100 { + //缓存 + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) - if err != nil { - logger.Errorf("handleReduceFilled 获取主单失败,订单号:%s", preOrder.OrderSn) - return - } - parentPrice := utility.StrToDecimal(parentOrder.Price) - num := utility.StrToDecimal(preOrder.Num) - lossAmount := price.Sub(parentPrice).Abs().Mul(num).Truncate(int32(tradeSet.PriceDigit)) - - if !strings.HasSuffix(preOrder.Symbol, "USDT") { - tradeSetU, err := GetTradeSet(utility.ReplaceSuffix(preOrder.Symbol, preOrder.QuoteSymbol, ""), 1) - - if err != nil { - logger.Errorf("handleMainReduceFilled 获取币本位对应U交易对设置失败,订单号:%s", preOrder.OrderSn) - return + ids := []int{preOrder.MainId, preOrder.Pid} + if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil { + logger.Info("100%减仓完毕,终结流程") } - - lossAmount = lossAmount.Mul(utility.StrToDecimal(tradeSetU.LastPrice)).Truncate(int32(tradeSetU.PriceDigit)) - } - - if err := db.Model(&parentOrder).Where("loss_amount = ?", 0).Update("loss_amount", lossAmount).Error; err != nil { - logger.Errorf("handleMainReduceFilled 更新亏损金额失败,订单号:%s", preOrder.OrderSn) return } + positionData := savePosition(db, preOrder) 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 } + orderExt := models.LinePreOrderExt{} totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) + price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) futApi := FutRestApi{} + db.Model(&orderExt).Where("order_id =?", preOrder.Pid).First(&orderExt) + for _, v := range orders { if v.OrderType == 1 { + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + v.Rate = percentag.String() + percentag = percentag.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() + } + } + processFutTakeProfitOrder(db, futApi, v, totalNum) } else if v.OrderType == 2 { processFutStopLossOrder(db, v, utility.StrToDecimal(v.Price), totalNum) @@ -275,7 +281,8 @@ func getFuturesPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels. // 平仓单成交 func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder) + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -295,7 +302,8 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 止损单成交 func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder) + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -319,7 +327,8 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 止盈单成交 func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder) + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -338,7 +347,7 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } // 清除合约缓存 -func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { +func removeFutLossAndAddPosition(mainId int, orderSn string) { stoplossKey := fmt.Sprintf(rediskey.FuturesStopLossList, global.EXCHANGE_BINANCE) stoplossVal, _ := helper.DefaultRedis.GetAllList(stoplossKey) addPositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) @@ -352,11 +361,11 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //止损缓存 for _, v := range stoplossVal { sonic.Unmarshal([]byte(v), &stoploss) - if stoploss.MainId == preOrder.MainId { + if stoploss.MainId == mainId { _, err := helper.DefaultRedis.LRem(stoplossKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", orderSn, err) } } } @@ -364,11 +373,11 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //加仓缓存 for _, v := range addPositionVal { sonic.Unmarshal([]byte(v), &addPosition) - if addPosition.MainId == preOrder.MainId { + if addPosition.MainId == mainId { _, err := helper.DefaultRedis.LRem(addPositionKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", orderSn, err) } } } @@ -376,87 +385,172 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //减仓缓存 for _, v := range reduceVal { sonic.Unmarshal([]byte(v), &reduce) - if reduce.MainId == preOrder.MainId { + if reduce.MainId == mainId { _, err := helper.DefaultRedis.LRem(reduceKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", orderSn, err) } } } } -// 主单成交 处理止盈止损订单 -// preOrder 主单 +// 处理主单成交,处理止盈、止损、减仓订单 func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { - orders := []models.LinePreOrder{} - tradeSet, _ := GetTradeSet(preOrder.Symbol, 1) - - if tradeSet.Coin == "" { - logger.Error("获取交易对失败") + // 获取交易对配置和API信息 + tradeSet, err := GetTradeSet(preOrder.Symbol, 1) + if err != nil || tradeSet.Coin == "" { + logger.Errorf("获取交易对配置失败, 回调订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } - if tradeSet.Coin == "" { - logger.Errorf("获取交易对配置失败, 回调订单号:%s", preOrder.OrderSn) - return - } - - if preOrder.OrderCategory == 3 { - if err := db.Model(&DbModels.LinePreOrderStatus{}).Where("order_id = ?", preOrder.MainId).Update("add_position_status", 1).Error; err != nil { - logger.Errorf("更新主单加仓状态失败, 主单号:%s, 错误信息:%v", preOrder.MainId, err) - } - - if err := cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType); err != nil { - logger.Errorf("取消止盈止损订单失败 orderSn:%s err:%v", preOrder.OrderSn, err) - } - } apiInfo, err := GetApiInfo(preOrder.ApiId) - if apiInfo.Id == 0 { - logger.Error("订单回调查询apiuserinfo失败 err:", err) - return - } - if err := db.Model(&DbModels.LinePreOrder{}). - Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id). - Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - logger.Error("订单回调查询止盈止损单失败:", err) + if err != nil || apiInfo.Id == 0 { + logger.Errorf("订单回调查询apiuserinfo失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } - futApi := FutRestApi{} - num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet) - num = num.Truncate(int32(tradeSet.AmountDigit)) + // 处理主单加仓 + if preOrder.OrderCategory == 3 { + if err := handleMainOrderAddPosition(db, preOrder); err != nil { + logger.Errorf("处理主单加仓失败, 主单号:%s, 错误信息: %v", preOrder.MainId, err) + return + } + } else { + // 处理其他主单逻辑 + if err := cancelPositionOtherOrders(apiInfo, db, preOrder); err != nil { + logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) + return + } + } + // 获取止盈止损订单 + orders, err := getStopOrders(db, preOrder) + if err != nil { + logger.Errorf("查询止盈止损订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) + return + } + + // 获取和保存持仓数据 + positionData := savePosition(db, preOrder) + orderExt := models.LinePreOrderExt{} + num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) + + // 更新订单数量并处理止盈、止损、减仓 for _, order := range orders { price := utility.StrToDecimal(order.Price).Truncate(int32(tradeSet.PriceDigit)) order.Price = price.String() - if order.OrderType == 4 { - ext := DbModels.LinePreOrderExt{} - db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext) + // 更新止盈止损订单数量 + num = updateOrderQuantity(db, order, preOrder, num, tradeSet) - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) - order.Num = num.String() - } - } - - if err := db.Model(&order).Update("num", num).Error; err != nil { - logger.Errorf("修改止盈止损数量失败 订单号:%s err:%v", order.OrderSn, err) - } - - logger.Errorf("止盈止损 下单数量:%v", num) + // 根据订单类型处理 switch order.OrderType { case 1: // 止盈 - processFutTakeProfitOrder(db, futApi, order, num) + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(num).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + order.Rate = percentag.String() + percentag = percentag.Div(decimal.NewFromInt(100)) + + if positionData.PositionSide == "LONG" { + order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } else { + order.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } + } + + processFutTakeProfitOrder(db, FutRestApi{}, order, num) case 2: // 止损 processFutStopLossOrder(db, order, price, num) - case 4: //减仓 + case 4: // 减仓 processFutReduceOrder(order, price, num) } } } +// 处理主单加仓 +func handleMainOrderAddPosition(db *gorm.DB, preOrder *models.LinePreOrder) error { + // 更新加仓状态 + if err := db.Model(&DbModels.LinePreOrderStatus{}). + Where("order_id = ?", preOrder.MainId). + Update("add_position_status", 1).Error; err != nil { + return err + } + + // 取消止盈止损订单 + return cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType) +} + +// 取消主单相关订单 +func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder) error { + mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) + if err != nil { + return err + } + + mainIds := []int{} + for _, mainOrder := range mainOrders { + removeFutLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) + mainIds = append(mainIds, mainOrder.Id) + } + + if len(mainIds) > 0 { + orderSns, err := getOpenOrderSns(db, mainIds) + if err != nil { + return err + } + + // 批量取消订单 + orderArray := utility.SplitSlice(orderSns, 10) + futApi := FutRestApi{} + for _, item := range orderArray { + err := futApi.CancelBatchFutOrder(apiUserInfo, preOrder.Symbol, item) + + if err != nil { + logger.Errorf("批量取消订单失败 orderSns:%v", item) + } + } + } + return nil +} + +// 获取止盈止损订单 +func getStopOrders(db *gorm.DB, preOrder *models.LinePreOrder) ([]models.LinePreOrder, error) { + var orders []models.LinePreOrder + if err := db.Model(&DbModels.LinePreOrder{}). + Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id). + Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return orders, nil +} + +// 更新订单数量 +func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, num decimal.Decimal, tradeSet models2.TradeSet) decimal.Decimal { + // 处理减仓比例 + if order.OrderType == 4 { + ext := DbModels.LinePreOrderExt{} + if err := db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext).Error; err != nil { + logger.Errorf("查询减仓比例失败, 订单号:%s, 错误信息: %v", order.OrderSn, err) + } + + // 计算减仓数量 + if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { + num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + order.Num = num.String() + } + } + + // 更新订单数量 + if err := db.Model(&order).Update("num", num).Error; err != nil { + logger.Errorf("修改止盈止损数量失败 订单号:%s err:%v", order.OrderSn, err) + } + + return num +} + // 减仓单 func processFutReduceOrder(order DbModels.LinePreOrder, price, num decimal.Decimal) { key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) @@ -510,7 +604,8 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line logger.Error("合约止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { - if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { + if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { logger.Error("更新合约止盈单触发事件 ordersn:", order.OrderSn) } diff --git a/services/binanceservice/orderservice.go b/services/binanceservice/orderservice.go index 1d242a3..9fba597 100644 --- a/services/binanceservice/orderservice.go +++ b/services/binanceservice/orderservice.go @@ -154,3 +154,8 @@ func GetSymbolTriggerCount(db *gorm.DB, symbol string, apiId, symbolType int) (i return count, nil } + +//获取已开仓的 +// func GetOpenedOrders(db *gorm.DB, apiId int, exchange, symbol, symbolType, side string) ([]models.LinePreOrder, error) { + +// } diff --git a/services/binanceservice/spotjudgeservice.go b/services/binanceservice/spotjudgeservice.go index da383e4..047e5cd 100644 --- a/services/binanceservice/spotjudgeservice.go +++ b/services/binanceservice/spotjudgeservice.go @@ -83,13 +83,6 @@ func SpotOrderLock(db *gorm.DB, v *dto.PreOrderRedisList, item string, spotApi S return } - //判断是否有已触发交易对 - count, _ := GetSymbolTriggerCount(db, v.Symbol, v.ApiId, 1) - - if count > 0 { - return - } - price, _ := decimal.NewFromString(v.Price) num, _ := decimal.NewFromString(preOrder.Num) params := OrderPlacementService{ diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 40ce946..f2c2a38 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -11,7 +11,9 @@ import ( "go-admin/common/global" "go-admin/common/helper" models2 "go-admin/models" + "go-admin/models/positiondto" "go-admin/pkg/utility" + "go-admin/services/positionservice" "strconv" "strings" "time" @@ -127,7 +129,8 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus //主单止损回调 case preOrder.OrderType == 2 && orderStatus == 6: - removeSpotLossAndAddPosition(preOrder) + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) if err := db.Model(&DbModels.LinePreOrder{}).Where("id =?", preOrder.MainId).Update("status", 9).Error; err != nil { logger.Errorf("主单止损回调 订单号:%s 修改主单状态失败:%v", preOrder.OrderSn, err) @@ -168,6 +171,8 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return } + orderExt := models.LinePreOrderExt{} + positionData := savePosition(db, preOrder) orders := make([]models.LinePreOrder, 0) rate := utility.StringAsFloat(preOrder.Rate) @@ -177,7 +182,10 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 100%减仓 终止流程 if rate >= 100 { - removeSpotLossAndAddPosition(preOrder) + //缓存 + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) + ids := []int{preOrder.MainId, preOrder.Pid} if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil { logger.Info("100%减仓完毕,终结流程") @@ -185,7 +193,10 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return } + db.Model(&orderExt).Where("order_id =?", preOrder.Pid).Find(&orderExt) totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet) + totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) + price := utility.StrToDecimal(preOrder.Price) 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) @@ -195,26 +206,24 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { spotApi := SpotRestApi{} for index := range orders { - orders[index].Num = totalNum.Truncate(int32(tradeSet.AmountDigit)).String() + orders[index].Num = totalNum.String() if orders[index].OrderType == 1 { + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + orders[index].Rate = percentag.String() + percentag = percentag.Div(decimal.NewFromInt(100)) + orders[index].Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } + processTakeProfitOrder(db, spotApi, orders[index]) } else if orders[index].OrderType == 2 { processStopLossOrder(orders[index]) } } - //计算实际亏损 - // if parentOrder.Price != "" { - // parentPrice := utility.StrToDecimal(parentOrder.Price) - // reduceNum := utility.StrToDecimal(preOrder.Num) - // lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum) //.Truncate(int32(tradeSet.PriceDigit)) - - // if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil { - // logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err) - // } - // } - //加仓待触发 addPositionOrder := DbModels.LinePreOrder{} @@ -376,7 +385,8 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) }) } - removeSpotLossAndAddPosition(preOrder) + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -387,14 +397,14 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) ApiId: preOrder.ApiId, } if err := spotApi.CancelOpenOrders(db, req); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err) } } } // 止盈成交 func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeSpotLossAndAddPosition(preOrder) + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -409,6 +419,8 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } + removePosition(db, preOrder) + db.Transaction(func(tx *gorm.DB) error { ids := []int{preOrder.Pid, preOrder.MainId} if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6 AND order_type=0", ids).Update("status", 9).Error; err != nil { @@ -428,7 +440,28 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } -func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { +// 移除仓位信息 +func removePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { + positionMangement := positionservice.BinancePositionManagement{} + positionMangement.Orm = db + positionDelReq := positiondto.LinePreOrderPositioinDelReq{ + ApiId: preOrder.ApiId, + SymbolType: preOrder.SymbolType, + Symbol: preOrder.Symbol, + ExchangeType: preOrder.ExchangeType, + } + + if preOrder.Site == "BUY" { + positionDelReq.Side = "SELL" + } else { + positionDelReq.Side = "BUY" + } + + positionMangement.RemovePosition(&positionDelReq) +} + +// 清理待加仓、待止损、待减仓缓存 +func removeSpotLossAndAddPosition(mainId int, orderSn string) { stoplossKey := fmt.Sprintf(rediskey.SpotStopLossList, global.EXCHANGE_BINANCE) stoplossVal, _ := helper.DefaultRedis.GetAllList(stoplossKey) addPositionKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) @@ -442,11 +475,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //止损缓存 for _, v := range stoplossVal { sonic.Unmarshal([]byte(v), &stoploss) - if stoploss.MainId == preOrder.MainId { + if stoploss.MainId == mainId { _, err := helper.DefaultRedis.LRem(stoplossKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", orderSn, err) } } } @@ -454,11 +487,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //加仓缓存 for _, v := range addPositionVal { sonic.Unmarshal([]byte(v), &addPosition) - if addPosition.MainId == preOrder.MainId { + if addPosition.MainId == mainId { _, err := helper.DefaultRedis.LRem(addPositionKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", orderSn, err) } } } @@ -466,11 +499,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //减仓缓存 for _, v := range reduceVal { sonic.Unmarshal([]byte(v), &reduce) - if reduce.MainId == preOrder.MainId { + if reduce.MainId == mainId { _, err := helper.DefaultRedis.LRem(reduceKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", orderSn, err) } } } @@ -478,13 +511,31 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { // 主单成交 func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { + //修改持仓信息 + positionData := savePosition(db, preOrder) + if preOrder.OrderCategory == 3 { if err := db.Model(&DbModels.LinePreOrderStatus{}).Where("order_id = ?", preOrder.MainId).Update("add_position_status", 1).Error; err != nil { logger.Errorf("更新主单加仓状态失败, 主单号:%s, 错误信息:%v", preOrder.MainId, err) } + } else { + mainOrders, _ := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) + + if len(mainOrders) > 0 { + for _, mainOrder := range mainOrders { + removeSpotLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) + } + + spotApi := SpotRestApi{} + err := spotApi.CancelOpenOrdersLoop(db, CancelOpenOrdersReq{ApiId: preOrder.ApiId, Symbol: preOrder.Symbol}, 4) + + if err != nil { + logger.Errorf("取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v", preOrder.Symbol, preOrder.MainId, err) + } + } } - processTakeProfitAndStopLossOrders(db, preOrder) + processTakeProfitAndStopLossOrders(db, preOrder, &positionData) } // 解析订单状态 @@ -551,7 +602,8 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r // 主单成交 处理止盈止损订单 // preOrder 主单 -func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder) { +// positionData 持仓缓存信息 +func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto) { orders := []models.LinePreOrder{} tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) @@ -581,7 +633,10 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd return } + price := utility.StrToDecimal(preOrder.Price) spotApi := SpotRestApi{} + orderExt := models.LinePreOrderExt{} + db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) for _, order := range orders { order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() @@ -601,6 +656,15 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd switch order.OrderType { case 1: // 止盈 + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(num).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + order.Rate = percentag.String() + percentag = percentag.Div(decimal.NewFromInt(100)) + order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } + processTakeProfitOrder(db, spotApi, order) case 2: // 止损 processStopLossOrder(order) @@ -686,7 +750,8 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP logger.Error("现货止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { - if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { + if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { logger.Error("更新现货止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). diff --git a/services/positionservice/position_management.go b/services/positionservice/position_management.go new file mode 100644 index 0000000..dd74aa4 --- /dev/null +++ b/services/positionservice/position_management.go @@ -0,0 +1,120 @@ +package positionservice + +import ( + "context" + "fmt" + "go-admin/common/const/rediskey" + "go-admin/common/helper" + "go-admin/common/service" + "go-admin/models/positiondto" + "time" + + "github.com/bytedance/sonic" + "github.com/go-admin-team/go-admin-core/logger" + "github.com/shopspring/decimal" +) + +type BinancePositionManagement struct { + service.Service +} + +// 保存仓位信息 +// return 仓位信息 +// return 错误信息 +func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddReq, exchangeType string) (positiondto.PositionDto, error) { + var key string + result := positiondto.PositionDto{} + + switch data.SymbolType { + case 1: + key = fmt.Sprintf(rediskey.SpotPosition, exchangeType, data.ApiId, data.Symbol, data.Side) + case 2: + key = fmt.Sprintf(rediskey.FuturePosition, exchangeType, data.ApiId, data.Symbol, data.Side) + default: + return result, fmt.Errorf("symbol type error") + } + + lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotPositionLock, data.ApiId, data.Symbol, data.Side), 200, 5, 200*time.Millisecond) + + if ok, err := lock.AcquireWait(context.Background()); err != nil { + logger.Debug("获取锁失败", err) + return result, err + } else if ok { + defer lock.Release() + val, _ := helper.DefaultRedis.GetString(key) + + if val != "" { + sonic.Unmarshal([]byte(val), &result) + } + + if result.Symbol == "" { + result.Symbol = data.Symbol + result.Side = data.Side + result.ApiId = data.ApiId + result.SymbolType = data.SymbolType + result.PositionSide = data.PositionSide + } + + var totalLoss decimal.Decimal + + if result.LastPrice.Cmp(decimal.Zero) > 0 { + switch { + //多 买入 + case data.PositionSide == "LONG": + totalLoss = result.LastPrice.Sub(data.Price).Abs().Mul(result.Quantity) + + case data.PositionSide == "SHORT": + totalLoss = data.Price.Sub(result.LastPrice).Abs().Mul(result.Quantity) + } + } + + result.LastPrice = data.Price + result.TotalLoss = result.TotalLoss.Add(totalLoss) + result.Quantity = data.Quantity.Add(result.Quantity) + + dataVal, _ := sonic.MarshalString(result) + if err := helper.DefaultRedis.SetString(key, dataVal); err != nil { + logger.Errorf("保存仓位信息失败,val:%s err:%v", dataVal, err) + } + } + + return result, nil +} + +// 获取系统内仓位信息 +func (e *BinancePositionManagement) GetPosition(apiId, symbolType int, exchangeTyp, symbol, side string) (positiondto.PositionDto, error) { + result := positiondto.PositionDto{} + var key string + + switch symbolType { + case 1: + key = fmt.Sprintf(rediskey.SpotPosition, exchangeTyp, apiId, symbol, side) + case 2: + key = fmt.Sprintf(rediskey.FuturePosition, exchangeTyp, apiId, symbol, side) + default: + return result, fmt.Errorf("symbol type error") + } + + val, _ := helper.DefaultRedis.GetString(key) + if val != "" { + sonic.Unmarshal([]byte(val), &result) + } + + return result, nil +} + +// 移除仓位信息 +func (e *BinancePositionManagement) RemovePosition(req *positiondto.LinePreOrderPositioinDelReq) error { + var key string + + switch req.SymbolType { + case 1: + key = fmt.Sprintf(rediskey.SpotPosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side) + case 2: + key = fmt.Sprintf(rediskey.FuturePosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side) + default: + return fmt.Errorf("symbol type error") + } + + return helper.DefaultRedis.DeleteString(key) +} From ca92638554e74fe782f6dbd1cd86277e6f903648 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Mon, 3 Mar 2025 17:35:41 +0800 Subject: [PATCH 07/13] =?UTF-8?q?1=E3=80=81=E7=B4=AF=E8=AE=A1=E4=BA=8F?= =?UTF-8?q?=E6=8D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/binanceservice/commonservice.go | 18 ++++++++---- .../binanceservice/futuresjudgeservice.go | 7 ----- services/binanceservice/futuresrest.go | 18 +++++++++--- services/binanceservice/spotreset.go | 29 ++++++++++++++----- .../positionservice/position_management.go | 21 ++++++++++---- 5 files changed, 65 insertions(+), 28 deletions(-) diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index d554c8c..091f1c6 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -422,8 +422,8 @@ func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.Posi SymbolType: preOrder.SymbolType, Symbol: preOrder.Symbol, Price: utility.StrToDecimal(preOrder.Price), - Side: preOrder.Site, - Quantity: utility.StrToDecimal(preOrder.Num), + // Side: preOrder.Site, + Quantity: utility.StrToDecimal(preOrder.Num), } switch { @@ -436,8 +436,16 @@ func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.Posi } //减仓单 数量为负 - if preOrder.OrderType == 4 { + if preOrder.OrderType != 0 { + if preOrder.Site == "BUY" { + positionReq.Side = "SELL" + } else { + positionReq.Side = "BUY" + } + positionReq.Quantity = positionReq.Quantity.Mul(decimal.NewFromInt(-1)) + } else { + positionReq.Side = preOrder.Site } positionData, err := positionManage.SavePosition(&positionReq, global.EXCHANGE_BINANCE) @@ -453,9 +461,9 @@ func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, excha mainOrders := make([]DbModels.LinePreOrder, 0) if err := db.Model(&DbModels.LinePreOrder{}). - Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND side= ? AND exchange_type=? AND id!=?", + Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?", apiId, symbol, symbolType, side, exchangeType, newId). - Select("id", "order_sn").Find(&mainOrders).Error; err != nil { + Select("id", "main_id", "order_sn").Find(&mainOrders).Error; err != nil { return nil, err } diff --git a/services/binanceservice/futuresjudgeservice.go b/services/binanceservice/futuresjudgeservice.go index 2966fc7..1c098ce 100644 --- a/services/binanceservice/futuresjudgeservice.go +++ b/services/binanceservice/futuresjudgeservice.go @@ -87,13 +87,6 @@ func futTriggerOrder(db *gorm.DB, v *dto.PreOrderRedisList, item string, futApi return } - //判断是否有已触发交易对 - count, _ := GetSymbolTriggerCount(db, v.Symbol, v.ApiId, 2) - - if count > 0 { - return - } - price, _ := decimal.NewFromString(v.Price) num, _ := decimal.NewFromString(preOrder.Num) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 719dc7e..5e7d289 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -160,7 +160,7 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if v.OrderType == 1 { //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag := positionData.TotalLoss.Div(totalNum).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) v.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) @@ -434,6 +434,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { // 获取和保存持仓数据 positionData := savePosition(db, preOrder) orderExt := models.LinePreOrderExt{} + db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) // 更新订单数量并处理止盈、止损、减仓 @@ -449,7 +450,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { case 1: // 止盈 //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(num).Div(price) + percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) order.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) @@ -492,8 +493,13 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr mainIds := []int{} for _, mainOrder := range mainOrders { - removeFutLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) - mainIds = append(mainIds, mainOrder.Id) + mainId := mainOrder.Id + + if mainOrder.MainId > 0 { + mainId = mainOrder.MainId + } + removeFutLossAndAddPosition(mainId, mainOrder.OrderSn) + mainIds = append(mainIds, mainId) } if len(mainIds) > 0 { @@ -512,6 +518,10 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr logger.Errorf("批量取消订单失败 orderSns:%v", item) } } + + if err := db.Exec("UPDATE line_pre_order SET `status`=4, `desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { + logger.Errorf("合约 新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) + } } return nil } diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index f2c2a38..dfbc5e5 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -211,7 +211,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if orders[index].OrderType == 1 { //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag := positionData.TotalLoss.Div(totalNum).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) orders[index].Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) @@ -429,11 +429,11 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return err } - if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND status=0").Update("status", 4).Error; err != nil { - logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新取消状态失败:%v", preOrder.OrderSn, err) + // if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND status=0",).Update("status", 4).Error; err != nil { + // logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新取消状态失败:%v", preOrder.OrderSn, err) - return err - } + // return err + // } return nil }) @@ -522,8 +522,19 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { mainOrders, _ := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) if len(mainOrders) > 0 { + mainIds := []int{} for _, mainOrder := range mainOrders { - removeSpotLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) + mainId := mainOrder.Id + + if mainOrder.MainId > 0 { + mainId = mainOrder.MainId + } + + removeSpotLossAndAddPosition(mainId, mainOrder.OrderSn) + + if !utility.ContainsInt(mainIds, mainId) { + mainIds = append(mainIds, mainId) + } } spotApi := SpotRestApi{} @@ -531,6 +542,10 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if err != nil { logger.Errorf("取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v", preOrder.Symbol, preOrder.MainId, err) + } else if len(mainIds) > 0 { + if err := db.Exec("UPDATE line_pre_order SET status=4,desc=desc +' 新单触发取消' WHERE id IN ? AND status =6", mainIds).Error; err != nil { + logger.Errorf("新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) + } } } } @@ -658,7 +673,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd case 1: // 止盈 //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(num).Div(price) + percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) order.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) diff --git a/services/positionservice/position_management.go b/services/positionservice/position_management.go index dd74aa4..78221c3 100644 --- a/services/positionservice/position_management.go +++ b/services/positionservice/position_management.go @@ -56,20 +56,31 @@ func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddRe } var totalLoss decimal.Decimal - if result.LastPrice.Cmp(decimal.Zero) > 0 { switch { //多 买入 case data.PositionSide == "LONG": - totalLoss = result.LastPrice.Sub(data.Price).Abs().Mul(result.Quantity) + totalLoss = result.LastPrice.Sub(data.Price).Mul(result.Quantity) + if data.Price.Cmp(result.LastPrice) < 0 { + result.LastPrice = data.Price + } case data.PositionSide == "SHORT": - totalLoss = data.Price.Sub(result.LastPrice).Abs().Mul(result.Quantity) + totalLoss = data.Price.Sub(result.LastPrice).Mul(result.Quantity) + + if data.Price.Cmp(result.LastPrice) > 0 { + result.LastPrice = data.Price + } } + } else { + //默认没有金额的时候 + result.LastPrice = data.Price + } + + if totalLoss.Cmp(decimal.Zero) > 0 { + result.TotalLoss = result.TotalLoss.Add(totalLoss) } - result.LastPrice = data.Price - result.TotalLoss = result.TotalLoss.Add(totalLoss) result.Quantity = data.Quantity.Add(result.Quantity) dataVal, _ := sonic.MarshalString(result) From 981d2c0108bc1e772ec09b738e37620c4c741c41 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 4 Mar 2025 15:34:32 +0800 Subject: [PATCH 08/13] =?UTF-8?q?1=E3=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/binanceservice/futuresrest.go | 11 +++++++---- services/binanceservice/spotreset.go | 4 ++-- services/positionservice/position_management.go | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 5e7d289..8c32a08 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -261,10 +261,10 @@ func getFuturesPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels. positionAmt := utility.StrToDecimal(item.PositionAmt) //多 - if positionAmt.Cmp(decimal.Zero) > 0 && preOrder.Site == "SELL" { + if positionAmt.Cmp(decimal.Zero) > 0 && ((preOrder.OrderType == 0 && preOrder.Site == "BUY") || (preOrder.OrderType != 0 && preOrder.Site == "SELL")) { num = positionAmt.Abs().Truncate(int32(tradeSet.AmountDigit)) break - } else if positionAmt.Cmp(decimal.Zero) < 0 && preOrder.Site == "BUY" { + } else if positionAmt.Cmp(decimal.Zero) < 0 && ((preOrder.OrderType != 0 && preOrder.Site == "BUY") || (preOrder.OrderType == 0 && preOrder.Site == "SELL")) { //空 num = positionAmt.Abs().Truncate(int32(tradeSet.AmountDigit)) break @@ -499,7 +499,10 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr mainId = mainOrder.MainId } removeFutLossAndAddPosition(mainId, mainOrder.OrderSn) - mainIds = append(mainIds, mainId) + + if !utility.ContainsInt(mainIds, mainId) { + mainIds = append(mainIds, mainId) + } } if len(mainIds) > 0 { @@ -620,7 +623,7 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status =0", order.Id). - Updates(map[string]interface{}{"status": "1", "num": num.String()}).Error; err != nil { + Updates(map[string]interface{}{"status": "1", "num": num.String(), "price": order.Price}).Error; err != nil { logger.Error("合约止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index dfbc5e5..3cdbb45 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -543,7 +543,7 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if err != nil { logger.Errorf("取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v", preOrder.Symbol, preOrder.MainId, err) } else if len(mainIds) > 0 { - if err := db.Exec("UPDATE line_pre_order SET status=4,desc=desc +' 新单触发取消' WHERE id IN ? AND status =6", mainIds).Error; err != nil { + if err := db.Exec("UPDATE line_pre_order SET `status`=4,`desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { logger.Errorf("新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) } } @@ -770,7 +770,7 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP logger.Error("更新现货止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). - Updates(map[string]interface{}{"status": "1", "num": order.Num}).Error; err != nil { + Updates(map[string]interface{}{"status": "1", "num": order.Num, "price": order.Price}).Error; err != nil { logger.Error("现货止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } diff --git a/services/positionservice/position_management.go b/services/positionservice/position_management.go index 78221c3..b44e050 100644 --- a/services/positionservice/position_management.go +++ b/services/positionservice/position_management.go @@ -77,8 +77,11 @@ func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddRe result.LastPrice = data.Price } - if totalLoss.Cmp(decimal.Zero) > 0 { + //总亏损大于0 + if totalLoss.Add(result.TotalLoss).Cmp(decimal.Zero) > 0 { result.TotalLoss = result.TotalLoss.Add(totalLoss) + } else { + result.TotalLoss = decimal.Zero } result.Quantity = data.Quantity.Add(result.Quantity) From 126193df36739fb8a12f3f1f730dac37debc6bb6 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Thu, 6 Mar 2025 18:16:35 +0800 Subject: [PATCH 09/13] =?UTF-8?q?1=E3=80=81=E4=BA=A4=E6=98=93=E5=AF=B9?= =?UTF-8?q?=E9=BB=91=E5=90=8D=E5=8D=95=20=E4=BA=A4=E6=98=93=E6=89=80?= =?UTF-8?q?=E4=B8=8B=E6=9E=B6=E7=9A=84=E4=BA=A4=E6=98=93=E5=AF=B9=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/line_pre_order.go | 53 +- app/admin/apis/line_symbol_black.go | 28 + app/admin/models/line_pre_order_ext.go | 31 +- app/admin/router/line_pre_order.go | 4 +- app/admin/router/line_symbol_black.go | 6 +- app/admin/service/dto/line_pre_order.go | 107 ++-- app/admin/service/dto/line_pre_order_ext.go | 88 +-- app/admin/service/line_pre_order.go | 668 +++++++++++--------- app/admin/service/line_symbol.go | 67 +- app/admin/service/line_symbol_black.go | 19 +- services/binanceservice/commonservice.go | 2 +- services/binanceservice/futuresrest.go | 89 ++- services/binanceservice/orderservice.go | 11 + services/binanceservice/spotreset.go | 101 +-- 14 files changed, 731 insertions(+), 543 deletions(-) diff --git a/app/admin/apis/line_pre_order.go b/app/admin/apis/line_pre_order.go index 40a2c1f..1f18111 100644 --- a/app/admin/apis/line_pre_order.go +++ b/app/admin/apis/line_pre_order.go @@ -306,34 +306,34 @@ func (e LinePreOrder) AddPreOrder(c *gin.Context) { e.OK(nil, "操作成功") } -// 手动加仓 -func (e LinePreOrder) AddPosition(c *gin.Context) { - s := service.LinePreOrder{} - req := dto.LinePreOrderAddPositionReq{} - 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 - } +// // 手动加仓 +// func (e LinePreOrder) AddPosition(c *gin.Context) { +// s := service.LinePreOrder{} +// req := dto.LinePreOrderAddPositionReq{} +// 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 +// } - if err := req.Valid(); err != nil { - e.Error(500, err, err.Error()) - return - } +// if err := req.Valid(); err != nil { +// e.Error(500, err, err.Error()) +// return +// } - err = s.AddPosition(&req) +// err = s.AddPosition(&req) - if err != nil { - e.Error(500, nil, err.Error()) - return - } - e.OK(nil, "操作成功") -} +// if err != nil { +// e.Error(500, nil, err.Error()) +// return +// } +// e.OK(nil, "操作成功") +// } // BatchAddOrder 批量添加 func (e LinePreOrder) BatchAddOrder(c *gin.Context) { @@ -654,8 +654,7 @@ func (e LinePreOrder) CalculateBreakEevenRatio(c *gin.Context) { return } - // data := dto.CalculateBreakEvenRatioResp{} - _, err = s.GenerateOrder(&req) + err = s.GenerateOrder(&req) if err != nil { e.Error(500, err, err.Error()) return diff --git a/app/admin/apis/line_symbol_black.go b/app/admin/apis/line_symbol_black.go index b8ca9a4..9659f94 100644 --- a/app/admin/apis/line_symbol_black.go +++ b/app/admin/apis/line_symbol_black.go @@ -192,3 +192,31 @@ func (e LineSymbolBlack) Delete(c *gin.Context) { } e.OK(req.GetId(), "删除成功") } + +// 重置交易对 +func (e LineSymbolBlack) RelodSymbol(c *gin.Context) { + s := service.LineSymbolBlack{} + err := e.MakeContext(c). + MakeOrm(). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + err = s.ReloadSymbol("1") + if err != nil { + e.Error(500, err, fmt.Sprintf("重置现货交易对失败,\r\n失败信息 %s", err.Error())) + return + } + + err = s.ReloadSymbol("2") + if err != nil { + e.Error(500, err, fmt.Sprintf("重置合约交易对失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK(nil, "重置成功") +} diff --git a/app/admin/models/line_pre_order_ext.go b/app/admin/models/line_pre_order_ext.go index 308e109..9836848 100644 --- a/app/admin/models/line_pre_order_ext.go +++ b/app/admin/models/line_pre_order_ext.go @@ -9,22 +9,21 @@ import ( type LinePreOrderExt struct { models.Model - MainOrderId int `json:"mainOrderId" gorm:"type:bigint;comment:主单id"` - OrderId int `json:"orderId" gorm:"type:bigint;comment:订单id"` - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"` - ReTakeRatio decimal.Decimal `json:"reTakeRatio" gorm:"type:decimal(10,2);comment:亏损回本止盈百分比"` - ReduceOrderType string `json:"reduceOrderType" gorm:"type:varchar(20);comment:减仓类型 LIMIT-限价 MARKET-市价"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" gorm:"type:decimal(10,2);comment:减仓价格百分比"` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" gorm:"type:decimal(10,2);comment:减仓数量百分比"` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" gorm:"type:decimal(10,2);comment:减仓后止盈百分比"` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" gorm:"type:decimal(10,2);comment:减仓后止损百分比"` - AddPositionOrderType string `json:"addPositionOrderType" gorm:"type:varchar(20);comment:加仓类型 LIMIT-限价 MARKET-市价"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" gorm:"type:decimal(10,2);comment:加仓价格百分比"` - AddPositionType int `json:"addPositionType" gorm:"type:int;comment:加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" gorm:"type:decimal(10,2);comment:加仓值"` - ReduceReTakeRatio decimal.Decimal `json:"reduceReTakeRatio" gorm:"type:decimal(10,2);comment:减仓后亏损回本止盈百分比"` - TotalAfterAdding decimal.Decimal `json:"totalAfterAdding" gorm:"-"` //加仓后总数 - TotalAfterReducing decimal.Decimal `json:"totalAfterReducing" gorm:"-"` //减仓后总数 + MainOrderId int `json:"mainOrderId" gorm:"type:bigint;comment:主单id"` + OrderId int `json:"orderId" gorm:"type:bigint;comment:订单id"` + AddType int `json:"addType" gorm:"type:tinyint;comment:类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" gorm:"type:varchar(20);comment:订单类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" gorm:"type:decimal(10,2);comment: (加仓/减仓)触发价格百分比"` + AddPositionType int `json:"addPositionType" gorm:"type:int;comment:(加仓/减仓)类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" gorm:"type:decimal(10,2);comment:加仓值"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"` + StopLossRatio decimal.Decimal `json:"stopLossRatio" gorm:"type:decimal(10,2);comment:止损百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" gorm:"type:decimal(10,2);comment:止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" gorm:"type:decimal(10,2);comment:止盈后止盈百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" gorm:"type:decimal(10,2);comment:止盈后止损百分比"` + ReTakeRatio decimal.Decimal `json:"reTakeRatio" gorm:"type:decimal(10,2);comment:亏损回本止盈百分比"` + TotalBefore decimal.Decimal `gorm:"-" comment:"初始总数"` + TotalAfter decimal.Decimal `gorm:"-" comment:"剩余总数"` models.ModelTime models.ControlBy } diff --git a/app/admin/router/line_pre_order.go b/app/admin/router/line_pre_order.go index d1c759f..d912362 100644 --- a/app/admin/router/line_pre_order.go +++ b/app/admin/router/line_pre_order.go @@ -25,8 +25,8 @@ func registerLinePreOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTM r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) - r.POST("addOrder", actions.PermissionAction(), api.AddPreOrder) //添加订单 - r.POST("position", actions.PermissionAction(), api.AddPosition) //手动加仓 + r.POST("addOrder", actions.PermissionAction(), api.AddPreOrder) //添加订单 + // r.POST("position", actions.PermissionAction(), api.AddPosition) //手动加仓 r.POST("batchAddOrder", actions.PermissionAction(), api.BatchAddOrder) //批量添加订单 r.POST("quickAddPreOrder", actions.PermissionAction(), api.QuickAddPreOrder) //快捷下单 r.POST("lever", actions.PermissionAction(), api.Lever) //设置杠杆 diff --git a/app/admin/router/line_symbol_black.go b/app/admin/router/line_symbol_black.go index 681a0b7..9990e59 100644 --- a/app/admin/router/line_symbol_black.go +++ b/app/admin/router/line_symbol_black.go @@ -5,8 +5,8 @@ import ( jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" "go-admin/app/admin/apis" - "go-admin/common/middleware" "go-admin/common/actions" + "go-admin/common/middleware" ) func init() { @@ -23,5 +23,7 @@ func registerLineSymbolBlackRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJ r.POST("", api.Insert) r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) + + r.GET("reload-symbol", api.RelodSymbol) } -} \ No newline at end of file +} diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index a4e3801..2199b32 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -2,6 +2,7 @@ package dto import ( "errors" + "fmt" "strconv" "go-admin/app/admin/models" @@ -182,27 +183,29 @@ func (s *LinePreOrderDeleteReq) GetId() interface{} { } type LineAddPreOrderReq struct { - ExchangeType string `json:"exchange_type" vd:"len($)>0"` //交易所类型 - OrderType int `json:"order_type"` //订单类型 - Symbol string `json:"symbol"` //交易对 - ApiUserId string `json:"api_id" ` //下单用户 - Site string `json:"site" ` //购买方向 - BuyPrice string `json:"buy_price" vd:"$>0"` //购买金额 U - PricePattern string `json:"price_pattern"` //价格模式 - Price string `json:"price" vd:"$>0"` //下单价百分比 - Profit string `json:"profit" vd:"$>0"` //止盈价 - // StopPrice string `json:"stop_price"` //止损价 - PriceType string `json:"price_type"` //价格类型 - SaveTemplate string `json:"save_template"` //是否保存模板 - TemplateName string `json:"template_name"` //模板名字 - SymbolType int `json:"symbol_type" vd:"$>0"` //交易对类型 1-现货 2-合约 - CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货 - ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 - MainOrderType string `json:"main_order_type" ` //主单类型:限价(LIMIT)或市价(MARKET) - ReducePriceRatio decimal.Decimal `json:"reduce_price" ` //主单减仓价格百分比 - ReduceNumRatio decimal.Decimal `json:"reduce_num" ` //主单减仓数量百分比 - ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 - ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 + ExchangeType string `json:"exchange_type" vd:"len($)>0"` //交易所类型 + OrderType int `json:"order_type"` //订单类型 + Symbol string `json:"symbol"` //交易对 + ApiUserId string `json:"api_id" ` //下单用户 + Site string `json:"site" ` //购买方向 + BuyPrice string `json:"buy_price" vd:"$>0"` //购买金额 U + PricePattern string `json:"price_pattern"` //价格模式 + Price string `json:"price" vd:"$>0"` //下单价百分比 + Profit string `json:"profit" vd:"$>0"` //止盈价 + ProfitNumRatio decimal.Decimal `json:"profit_num_ratio"` //止盈数量百分比 + ProfitTpTpPriceRatio decimal.Decimal `json:"profit_tp_tp_price_ratio"` //止盈后止盈价百分比 + ProfitTpSlPriceRatio decimal.Decimal `json:"profit_tp_sl_price_ratio"` //止盈后止损价百分比 + PriceType string `json:"price_type"` //价格类型 + SaveTemplate string `json:"save_template"` //是否保存模板 + TemplateName string `json:"template_name"` //模板名字 + SymbolType int `json:"symbol_type" vd:"$>0"` //交易对类型 1-现货 2-合约 + CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货 + ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 + MainOrderType string `json:"main_order_type" ` //主单类型:限价(LIMIT)或市价(MARKET) + ReducePriceRatio decimal.Decimal `json:"reduce_price" ` //主单减仓价格百分比 + ReduceNumRatio decimal.Decimal `json:"reduce_num" ` //主单减仓数量百分比 + ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 + ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 ReduceReTakeProfitRatio decimal.Decimal `json:"re_take_profit_ratio" comment:"减仓后亏损回本止盈百分比"` Ext []LineAddPreOrderExtReq `json:"ext" ` //拓展字段 @@ -275,23 +278,33 @@ func (req LineAddPreOrderReq) Valid() error { } for _, v := range req.Ext { + name := "加仓" + + if v.AddType < 1 || v.AddType > 2 { + return errors.New("加、减仓单类型错误") + } + + if v.AddType == 2 { + name = "减仓" + } + if v.AddPositionVal.IsZero() { - return errors.New("加仓单数量不能为空") + return fmt.Errorf("%s单数量不能为空", name) } - if v.AddPositionPriceRatio.IsZero() { - return errors.New("加仓单下跌价格不能为空") + if v.PriceRatio.Cmp(decimal.Zero) <= 0 || v.PriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + return fmt.Errorf("%s单下跌价格不能为空", name) } - if v.ReduceNumRatio.IsZero() || v.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓数量不正确") + if v.TakeProfitRatio.IsZero() || v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈价格不正确") } - if v.ReducePriceRatio.IsZero() || v.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓下跌价格不正确") + if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止盈价格不正确") } - if v.ReduceTakeProfitRatio.IsZero() || v.ReduceTakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓后止盈价格不正确") + if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止损价格不正确") } } @@ -392,23 +405,33 @@ func (req LineBatchAddPreOrderReq) CheckParams() error { } for _, v := range req.Ext { + name := "加仓" + + if v.AddType < 1 || v.AddType > 2 { + return errors.New("加、减仓单类型错误") + } + + if v.AddType == 2 { + name = "减仓" + } + if v.AddPositionVal.IsZero() { - return errors.New("加仓单数量不能为空") + return fmt.Errorf("%s单数量不能为空", name) } - if v.AddPositionPriceRatio.IsZero() { - return errors.New("加仓单下跌价格不能为空") + if v.PriceRatio.Cmp(decimal.Zero) <= 0 || v.PriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + return fmt.Errorf("%s单下跌价格不能为空", name) } - if v.ReduceNumRatio.IsZero() || v.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓数量不正确") + if v.TakeProfitRatio.IsZero() || v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈价格不正确") } - if v.ReducePriceRatio.IsZero() || v.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓下跌价格不正确") + if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止盈价格不正确") } - if v.ReduceTakeProfitRatio.IsZero() || v.ReduceTakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓后止盈价格不正确") + if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止损价格不正确") } } @@ -579,12 +602,12 @@ type CalculateBreakEevenRatioReq struct { Symbol string `form:"symbol"` //交易对 ExchangeType string `form:"exchangeType"` //交易所类型 字典exchange_type SymbolType int `form:"symbolType"` + AddType int `form:"addType" comment:"类型 1-加仓 2-减仓"` BuyPrice decimal.Decimal `form:"buyPrice"` //主单购买总金额 LossBeginPercent decimal.Decimal `form:"lossBeginPercent"` //亏损开始百分比 LossEndPercent decimal.Decimal `form:"lossEndPercent"` //亏损截至百分比 - AddPositionType int `form:"addPositionType"` //加仓类型 1-百分比 2-实际金额 - AddPositionVal decimal.Decimal `form:"addPositionVal"` //加仓金额 - ReducePercent decimal.Decimal `form:"reducePercent"` //减仓百分比 + AddPositionType int `form:"addPositionType"` //加仓/减仓 类型 1-百分比 2-实际金额 + AddPositionVal decimal.Decimal `form:"addPositionVal"` //加仓/减仓 金额 RemainingQuantity decimal.Decimal `form:"remainingQuantity"` //剩余数量 TotalLossAmountU decimal.Decimal `form:"totalLossAmountU"` //累计亏损金额 } diff --git a/app/admin/service/dto/line_pre_order_ext.go b/app/admin/service/dto/line_pre_order_ext.go index 6b8e033..5d8c630 100644 --- a/app/admin/service/dto/line_pre_order_ext.go +++ b/app/admin/service/dto/line_pre_order_ext.go @@ -37,31 +37,32 @@ func (m *LinePreOrderExtGetPageReq) GetNeedSearch() interface{} { } type LineAddPreOrderExtReq struct { - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比" ` - ReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"亏损回本止盈百分比"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比" ` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比" ` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比" ` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` - ReduceReTakeProfitRatio decimal.Decimal `json:"reduceReTakeProfitRatio" comment:"减仓后回本止盈百分比"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比" ` - AddPositionOrderType string `json:"addPositionOrderType" comment:"加仓订单类型 LIMIT-限价 MARKET-市价"` - AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + AddType int `json:"addType" comment:"类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" comment:"订单类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" comment:"价格百分比"` + AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` + StopLossRatio decimal.Decimal `json:"stopLossRatio" comment:"止损百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" comment:"止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" comment:"止盈后止盈价格百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" comment:"止盈后止损价格百分比"` + ReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"亏损回本止盈百分比"` } type LinePreOrderExtInsertReq struct { - Id int `json:"-" comment:"主键id"` // 主键id - MainOrderId int `json:"mainOrderId" comment:"主单id"` - OrderId int `json:"orderId" comment:"订单id"` - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比"` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"` - AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + Id int `json:"-" comment:"主键id"` // 主键id + MainOrderId int `json:"mainOrderId" comment:"主单id"` + OrderId int `json:"orderId" comment:"订单id"` + AddType int `json:"addType" comment:"类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" comment:"类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" comment:"价格百分比"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" comment:"止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" comment:"止盈后止盈价格百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" comment:"止盈后止损价格百分比"` + AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` common.ControlBy } @@ -72,11 +73,11 @@ func (s *LinePreOrderExtInsertReq) Generate(model *models.LinePreOrderExt) { model.MainOrderId = s.MainOrderId model.OrderId = s.OrderId model.TakeProfitRatio = s.TakeProfitRatio - model.ReducePriceRatio = s.ReducePriceRatio - model.ReduceNumRatio = s.ReduceNumRatio - model.ReduceTakeProfitRatio = s.ReduceTakeProfitRatio - model.ReduceStopLossRatio = s.ReduceStopLossRatio - model.AddPositionPriceRatio = s.AddPositionPriceRatio + model.AddType = s.AddType + model.OrderType = s.OrderType + model.PriceRatio = s.PriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio model.AddPositionType = s.AddPositionType model.AddPositionVal = s.AddPositionVal model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 @@ -87,17 +88,18 @@ func (s *LinePreOrderExtInsertReq) GetId() interface{} { } type LinePreOrderExtUpdateReq struct { - Id int `uri:"id" comment:"主键id"` // 主键id - MainOrderId int `json:"mainOrderId" comment:"主单id"` - OrderId int `json:"orderId" comment:"订单id"` - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比"` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"` - AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + Id int `uri:"id" comment:"主键id"` // 主键id + MainOrderId int `json:"mainOrderId" comment:"主单id"` + OrderId int `json:"orderId" comment:"订单id"` + AddType int `json:"addType" comment:"类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" comment:"类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" comment:"价格百分比"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" comment:"止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" comment:"止盈后止盈价格百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" comment:"止盈后止损价格百分比"` + AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` common.ControlBy } @@ -108,11 +110,11 @@ func (s *LinePreOrderExtUpdateReq) Generate(model *models.LinePreOrderExt) { model.MainOrderId = s.MainOrderId model.OrderId = s.OrderId model.TakeProfitRatio = s.TakeProfitRatio - model.ReducePriceRatio = s.ReducePriceRatio - model.ReduceNumRatio = s.ReduceNumRatio - model.ReduceTakeProfitRatio = s.ReduceTakeProfitRatio - model.ReduceStopLossRatio = s.ReduceStopLossRatio - model.AddPositionPriceRatio = s.AddPositionPriceRatio + model.AddType = s.AddType + model.OrderType = s.OrderType + model.PriceRatio = s.PriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio model.AddPositionType = s.AddPositionType model.AddPositionVal = s.AddPositionVal model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 0676804..78af479 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -11,6 +11,7 @@ import ( "go-admin/pkg/utility" "go-admin/pkg/utility/snowflakehelper" "go-admin/services/binanceservice" + "sort" "strconv" "strings" "time" @@ -338,6 +339,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.SaveTemplate == "2" { return nil } + + //重新排序下跌比例(顺序) + sort.Slice(req.Ext, func(i, j int) bool { + return req.Ext[i].PriceRatio.Cmp(req.Ext[j].PriceRatio) < 0 + }) + var key string if req.SymbolType == global.SYMBOL_SPOT { key = fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, req.Symbol) @@ -356,11 +363,6 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //获取交易对 tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key) - // orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) - // if orderCount > 0 { - // *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) - // continue - // } tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) if tickerPrice.Equal(decimal.Zero) { //redis 没有这个值 *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 交易行情出错", id, req.Symbol)) @@ -454,15 +456,31 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //订单配置信息 preOrderExts := make([]models.LinePreOrderExt, 0) defultExt := models.LinePreOrderExt{ - TakeProfitRatio: utility.StringToDecimal(req.Profit), - ReducePriceRatio: req.ReducePriceRatio, - ReduceNumRatio: req.ReduceNumRatio, - ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, - ReduceStopLossRatio: req.ReduceStopLossRatio, + AddType: 1, //主单等同于加仓0 + AddPositionType: 1, + AddPositionVal: decimal.Zero, + OrderType: req.PriceType, + TakeProfitRatio: utility.StringToDecimal(req.Profit), + TakeProfitNumRatio: req.ProfitNumRatio, + TpTpPriceRatio: req.ProfitTpTpPriceRatio, + TpSlPriceRatio: req.ProfitTpSlPriceRatio, + } + //减仓单 + defultExt2 := models.LinePreOrderExt{ + AddType: 2, + OrderType: "LIMIT", + PriceRatio: req.ReducePriceRatio, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, + TakeProfitRatio: req.ReduceTakeProfitRatio, + TakeProfitNumRatio: decimal.NewFromInt(100), //减仓止盈默认100% + StopLossRatio: req.ReduceStopLossRatio, } mainPrice := utility.StringToDecimal(AddOrder.Price) mainAmount := buyPrice.Div(mainPrice) - defultExt.TotalAfterReducing = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) + defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit)) + defultExt2.TotalBefore = defultExt.TotalAfter + defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) preOrderExts = append(preOrderExts, defultExt) calculateResp := dto.CalculateBreakEvenRatioResp{} @@ -476,56 +494,42 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP LossEndPercent: req.ReducePriceRatio, AddPositionType: 2, AddPositionVal: decimal.Zero, - ReducePercent: req.ReduceNumRatio, } //计算减仓后 mainParam.LossEndPercent = req.ReducePriceRatio mainParam.RemainingQuantity = mainAmount - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) mainParam.RemainingQuantity = calculateResp.RemainingQuantity //mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU //buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit)) req.ReduceReTakeProfitRatio = calculateResp.Ratio mainParam.LossBeginPercent = req.ReducePriceRatio - defultExt.ReduceReTakeRatio = calculateResp.Ratio + defultExt.ReTakeRatio = calculateResp.Ratio for index, addPosition := range req.Ext { ext := models.LinePreOrderExt{ - TakeProfitRatio: addPosition.TakeProfitRatio, - ReducePriceRatio: addPosition.ReducePriceRatio, - ReduceNumRatio: addPosition.ReduceNumRatio, - ReduceTakeProfitRatio: addPosition.ReduceTakeProfitRatio, - ReduceStopLossRatio: addPosition.ReduceStopLossRatio, - AddPositionPriceRatio: addPosition.AddPositionPriceRatio, - AddPositionOrderType: addPosition.AddPositionOrderType, - AddPositionType: addPosition.AddPositionType, - AddPositionVal: addPosition.AddPositionVal, + AddType: addPosition.AddType, + OrderType: addPosition.OrderType, + TakeProfitRatio: addPosition.TakeProfitRatio, + TakeProfitNumRatio: addPosition.TakeProfitNumRatio, + TpTpPriceRatio: addPosition.TpTpPriceRatio, + TpSlPriceRatio: addPosition.TpSlPriceRatio, + AddPositionType: addPosition.AddPositionType, + AddPositionVal: addPosition.AddPositionVal, } - mainParam.LossEndPercent = req.Ext[index].AddPositionPriceRatio + mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType mainParam.AddPositionVal = req.Ext[index].AddPositionVal - mainParam.ReducePercent = decimal.Zero - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) - ext.TotalAfterAdding = calculateResp.RemainingQuantity - req.Ext[index].ReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].AddPositionPriceRatio + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) + + ext.TotalBefore = mainParam.RemainingQuantity //初始数量 + ext.TotalAfter = calculateResp.RemainingQuantity //计算后数量 + ext.ReTakeRatio = calculateResp.Ratio + mainParam.LossBeginPercent = addPosition.PriceRatio mainParam.RemainingQuantity = calculateResp.RemainingQuantity mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU - mainParam.LossEndPercent = req.Ext[index].ReducePriceRatio - mainParam.AddPositionVal = decimal.Zero - mainParam.ReducePercent = req.Ext[index].ReduceNumRatio - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) - - req.Ext[index].ReduceReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].ReducePriceRatio - mainParam.RemainingQuantity = calculateResp.RemainingQuantity - mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU - - ext.TotalAfterReducing = calculateResp.RemainingQuantity - ext.ReTakeRatio = req.Ext[index].ReTakeProfitRatio - ext.ReduceReTakeRatio = req.Ext[index].ReduceReTakeProfitRatio preOrderExts = append(preOrderExts, ext) } @@ -576,7 +580,26 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP profitOrder.Rate = req.Profit profitOrder.MainId = AddOrder.Id + if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 { + numPercent := req.ProfitNumRatio.Div(decimal.NewFromInt(100)) + profitOrder.Num = utility.StrToDecimal(profitOrder.Num).Mul(numPercent).Truncate(int32(tradeSet.AmountDigit)).String() + + } tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder) + + //不全部止盈的时候 + if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 && req.ProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + reminQuantity := utility.StrToDecimal(AddOrder.Num).Sub(utility.StrToDecimal(profitOrder.Num)) + + childrens, err := makeTpOrder(&profitOrder, reminQuantity, req.ProfitTpTpPriceRatio, req.ProfitTpSlPriceRatio, &tradeSet) + + if err != nil { + logger.Error("生成止盈后子订单失败") + return err + } + + tx.Create(&childrens) + } } if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 { @@ -619,39 +642,44 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP preOrderExts[index].OrderId = AddOrder.Id continue } + var AddOrder models.LinePreOrder - addPosition := createPreAddPosition(&AddOrder, v, tradeSet) + if v.AddType == 1 { + AddOrder = createPreAddPosition(&AddOrder, v, tradeSet) + } else if v.AddType == 2 { + AddOrder = createPreReduceOrder(&AddOrder, v, tradeSet) + } - if addPosition.OrderSn == "" { + if AddOrder.OrderSn == "" { continue } - if err := e.Orm.Create(&addPosition).Error; err != nil { + if err := e.Orm.Create(&AddOrder).Error; err != nil { logger.Error("保存加仓单失败") return err } - preOrderExts[index].OrderId = addPosition.Id + preOrderExts[index].OrderId = AddOrder.Id //止盈、减仓 - orders, err := makeFuturesTakeAndReduce(&addPosition, v, tradeSet) + orders, err := makeFuturesTakeAndReduce(&AddOrder, v, tradeSet) if err != nil { - logger.Error("构造加仓单止盈、减仓失败") + logger.Error("构造止盈、止损失败") return err } if err := e.Orm.Create(&orders).Error; err != nil { - logger.Error("保存加仓单止盈、减仓失败") + logger.Error("保存止盈、止损失败") return err } for index := range orders { //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 - if orders[index].OrderType == 4 && v.ReduceNumRatio.Cmp(decimal.Zero) > 0 && v.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + if orders[index].OrderType == 1 && v.TakeProfitRatio.Cmp(decimal.Zero) > 0 && v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) < 0 { reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet) if err != nil { - logger.Error("生产加仓单止盈、减仓失败") + logger.Error("生产止盈后止盈、减仓失败") return err } @@ -660,7 +688,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP } if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { - logger.Error("报错减仓后止盈止损失败") + logger.Error("报错止盈后止盈止损失败") return err } } @@ -695,16 +723,16 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) data.MainId = preOrder.Id data.CreatedAt = time.Now() - data.MainOrderType = v.AddPositionOrderType + data.MainOrderType = v.OrderType data.Status = 0 data.OrderCategory = 3 - data.Rate = v.AddPositionPriceRatio.String() + data.Rate = v.PriceRatio.String() var percentage decimal.Decimal if data.Site == "BUY" { - percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + percentage = decimal.NewFromInt(1).Sub(v.PriceRatio.Div(decimal.NewFromInt(100))) } else { - percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + percentage = decimal.NewFromInt(1).Add(v.PriceRatio.Div(decimal.NewFromInt(100))) } dataPrice := price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit)) @@ -722,39 +750,12 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx return data } -// 构建合约止盈、减仓单 -func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { - num := ext.TotalAfterAdding.Truncate(int32(tradeSet.AmountDigit)) - orders := make([]models.LinePreOrder, 0) - //止盈单 - profitOrder := models.LinePreOrder{} - copier.Copy(&profitOrder, preOrder) - - profitOrder.Id = 0 - profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) - profitOrder.Pid = preOrder.Id - profitOrder.OrderType = 1 - profitOrder.Status = 0 - profitOrder.MainId = preOrder.MainId - profitOrder.Num = num.String() - profitOrder.BuyPrice = "0" - // profitOrder.Rate = ext.TakeProfitRatio.String() - - //止盈需要累加之前的亏损 - profitOrder.Rate = ext.TakeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() - - if strings.ToUpper(preOrder.Site) == "BUY" { - profitOrder.Site = "SELL" - } else { - profitOrder.Site = "BUY" - } - - binanceservice.SetPrice(&profitOrder, preOrder, tradeSet) - orders = append(orders, profitOrder) +// 生成减仓单 +func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) models.LinePreOrder { + var stopOrder models.LinePreOrder //减仓单 - if ext.ReducePriceRatio.Cmp(decimal.Zero) > 0 { - stopOrder := models.LinePreOrder{} + if ext.PriceRatio.Cmp(decimal.Zero) > 0 { copier.Copy(&stopOrder, preOrder) stopOrder.Id = 0 @@ -763,14 +764,10 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO stopOrder.MainId = preOrder.MainId stopOrder.OrderType = 4 stopOrder.Status = 0 - stopOrder.Rate = ext.ReducePriceRatio.String() - stopOrder.Num = num.String() + stopOrder.Rate = ext.PriceRatio.String() + stopOrder.Num = ext.TotalAfter.Sub(ext.TotalBefore).Abs().Truncate(int32(tradeSet.AmountDigit)).String() stopOrder.BuyPrice = "0" - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - stopNum := num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))) - stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String() - } if strings.ToUpper(preOrder.Site) == "BUY" { stopOrder.Site = "SELL" } else { @@ -778,38 +775,140 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO } binanceservice.SetPrice(&stopOrder, preOrder, tradeSet) - orders = append(orders, stopOrder) + } + + return stopOrder +} + +// 构建止盈、止盈止损 +func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { + orders := make([]models.LinePreOrder, 0) + var side string + + if strings.ToUpper(preOrder.Site) == "BUY" { + side = "SELL" + } else { + side = "BUY" + } + + if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 { + // 止盈单 + profitOrder := models.LinePreOrder{} + copier.Copy(&profitOrder, preOrder) + + profitOrder.Id = 0 + profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) + profitOrder.Pid = preOrder.Id + profitOrder.OrderType = 1 + profitOrder.Status = 0 + profitOrder.MainId = preOrder.MainId + profitOrder.BuyPrice = "0" + profitOrder.Site = side + + if ext.TakeProfitNumRatio.Cmp(decimal.Zero) <= 0 || ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + profitOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String() + } else { + profitOrder.Num = ext.TotalAfter.Mul(ext.TakeProfitNumRatio).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)).String() + } + + // 止盈需要累加之前的亏损 + profitOrder.Rate = ext.TakeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() + + binanceservice.SetPrice(&profitOrder, preOrder, tradeSet) + orders = append(orders, profitOrder) + } + + if ext.StopLossRatio.Cmp(decimal.Zero) > 0 { + lossOrder := models.LinePreOrder{} + copier.Copy(&lossOrder, preOrder) + + lossOrder.Id = 0 + lossOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) + lossOrder.Pid = preOrder.Id + lossOrder.OrderType = 2 + lossOrder.Status = 0 + lossOrder.MainId = preOrder.MainId + lossOrder.BuyPrice = "0" + lossOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String() + lossOrder.Rate = ext.StopLossRatio.Truncate(2).String() + lossOrder.Site = side + + binanceservice.SetPrice(&lossOrder, preOrder, tradeSet) + orders = append(orders, lossOrder) } return orders, nil } +// 构建止盈后止盈止损 +// parentOrder 父订单 +// remainQuantity 剩余数量 +// tpPriceRatio 止盈价格比例 +// slPriceRatio 止损价格比例 +func makeTpOrder(parentOrder *models.LinePreOrder, reminQuantity decimal.Decimal, tpPriceRatio, slPriceRatio decimal.Decimal, tradeSet *models2.TradeSet) ([]models.LinePreOrder, error) { + result := make([]models.LinePreOrder, 0) + tp := models.LinePreOrder{} + sl := models.LinePreOrder{} + copier.Copy(&tp, parentOrder) + + tp.Id = 0 + tp.Pid = parentOrder.Id + tp.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + tp.OrderType = 1 + tp.Status = 0 + tp.Rate = tpPriceRatio.String() + tp.Num = reminQuantity.String() + binanceservice.SetPrice(&tp, parentOrder, *tradeSet) + result = append(result, tp) + + if (slPriceRatio).Cmp(decimal.Zero) > 0 { + copier.Copy(&sl, parentOrder) + sl.Pid = parentOrder.Id + sl.Id = 0 + sl.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + sl.OrderType = 2 + sl.Num = reminQuantity.Truncate(int32(tradeSet.AmountDigit)).String() + sl.Rate = slPriceRatio.String() + binanceservice.SetPrice(&sl, parentOrder, *tradeSet) + result = append(result, sl) + } + + return result, nil +} + // 构建减仓后止盈止损 func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { orders := make([]models.LinePreOrder, 0) - takeProfitOrder := models.LinePreOrder{} - copier.Copy(&takeProfitOrder, parentOrder) - takeProfitOrder.Id = 0 - takeProfitOrder.Pid = parentOrder.Id - takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - takeProfitOrder.Status = 0 - takeProfitOrder.OrderType = 1 - takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() - takeProfitOrder.SignPrice = parentOrder.Price - takeProfitOrder.CreatedAt = time.Now() - takeProfitOrder.BuyPrice = "0" - takeProfitOrder.MainOrderType = "LIMIT" - takeProfitOrder.Num = ext.TotalAfterReducing.Truncate(int32(tradeSet.AmountDigit)).String() - // takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() - //止盈需要累加之前的亏损 - takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.Add(ext.ReduceReTakeRatio).String() - takeProfitOrder.BuyPrice = "0" + var num decimal.Decimal - binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) - orders = append(orders, takeProfitOrder) + if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))) + num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) + } + if ext.TpTpPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + takeProfitOrder := models.LinePreOrder{} + copier.Copy(&takeProfitOrder, parentOrder) + takeProfitOrder.Id = 0 + takeProfitOrder.Pid = parentOrder.Id + takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + takeProfitOrder.Status = 0 + takeProfitOrder.OrderType = 1 + takeProfitOrder.Rate = ext.TpTpPriceRatio.String() + takeProfitOrder.SignPrice = parentOrder.Price + takeProfitOrder.CreatedAt = time.Now() + takeProfitOrder.BuyPrice = "0" + takeProfitOrder.MainOrderType = "LIMIT" + takeProfitOrder.Num = num.String() + //止盈需要累加之前的亏损 + takeProfitOrder.Rate = ext.TpTpPriceRatio.Truncate(2).String() + takeProfitOrder.BuyPrice = "0" + + binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) + orders = append(orders, takeProfitOrder) + } //有止损单 - if ext.ReduceStopLossRatio.Cmp(decimal.Zero) > 0 { + if ext.TpSlPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { var stoploss models.LinePreOrder copier.Copy(&stoploss, parentOrder) @@ -821,9 +920,9 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line stoploss.OrderType = 2 stoploss.SignPrice = parentOrder.Price stoploss.BuyPrice = "0" - stoploss.Rate = ext.ReduceStopLossRatio.String() + stoploss.Rate = ext.TpSlPriceRatio.String() stoploss.MainOrderType = "LIMIT" - stoploss.Num = ext.TotalAfterReducing.Truncate(int32(tradeSet.AmountDigit)).String() + stoploss.Num = num.String() stoploss.BuyPrice = "0" binanceservice.SetPrice(&stoploss, parentOrder, tradeSet) @@ -1633,7 +1732,7 @@ func (e *LinePreOrder) QueryAiCoinPrice(req *dto.QueryAiCoinPriceReq) (models.Li } // 根据请求参数重新生成亏损回本止盈百分比 -func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) ([]models.LineDirection, error) { +func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { var tradeSet models2.TradeSet var tickerPrice decimal.Decimal @@ -1644,7 +1743,7 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) ([]models.Line } if tradeSet.LastPrice == "" { - return nil, errors.New("获取不到交易对信息") + return errors.New("获取不到交易对信息") } var price decimal.Decimal @@ -1666,77 +1765,58 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) ([]models.Line buyPrice := utility.StrToDecimal(req.BuyPrice) mainAmount := buyPrice.Div(price).Truncate(int32(tradeSet.AmountDigit)) calculateResp := dto.CalculateBreakEvenRatioResp{} + lossBeginPercent := decimal.Zero mainParam := dto.CalculateBreakEevenRatioReq{ Price: price, ExchangeType: req.ExchangeType, Symbol: req.Symbol, SymbolType: req.SymbolType, BuyPrice: buyPrice, - LossBeginPercent: decimal.Zero, + LossBeginPercent: lossBeginPercent, LossEndPercent: req.ReducePriceRatio, AddPositionType: 2, AddPositionVal: decimal.Zero, - ReducePercent: req.ReduceNumRatio, } //计算减仓后 mainParam.LossEndPercent = req.ReducePriceRatio mainParam.RemainingQuantity = mainAmount - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + mainParam.AddType = 2 + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) mainParam.RemainingQuantity = calculateResp.RemainingQuantity mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU req.ReduceReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.ReducePriceRatio + lossBeginPercent = req.ReducePriceRatio + + //顺序排序 + sort.Slice(req.Ext, func(i, j int) bool { + return req.Ext[i].PriceRatio.Cmp(req.Ext[j].PriceRatio) < 0 + }) for index := range req.Ext { - mainParam.LossEndPercent = req.Ext[index].AddPositionPriceRatio + mainParam.LossBeginPercent = lossBeginPercent + mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType mainParam.AddPositionVal = req.Ext[index].AddPositionVal - mainParam.ReducePercent = decimal.Zero - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) req.Ext[index].ReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].AddPositionPriceRatio - mainParam.RemainingQuantity = calculateResp.RemainingQuantity - mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU - mainParam.LossEndPercent = req.Ext[index].ReducePriceRatio - mainParam.AddPositionVal = decimal.Zero - mainParam.ReducePercent = req.Ext[index].ReduceNumRatio - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) - - req.Ext[index].ReduceReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].ReducePriceRatio + lossBeginPercent = req.Ext[index].PriceRatio mainParam.RemainingQuantity = calculateResp.RemainingQuantity mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU } - return nil, nil + return nil } // 计算亏损百分比 -func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatioReq, data *dto.CalculateBreakEvenRatioResp) error { - var tradeSet models2.TradeSet +func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatioReq, data *dto.CalculateBreakEvenRatioResp, tradeSet models2.TradeSet) error { if req.LossEndPercent.Cmp(req.LossBeginPercent) < 0 { return errors.New("截至亏损百分比必须大于开始亏损百分比") } - if req.SymbolType == 1 { - val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, req.Symbol)) - - sonic.Unmarshal([]byte(val), &tradeSet) - } else if req.SymbolType == 2 { - val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_FUTURES, req.ExchangeType, req.Symbol)) - - sonic.Unmarshal([]byte(val), &tradeSet) - } else { - return errors.New("symbolType error") - } - - if tradeSet.LastPrice == "" { - return errors.New("没有找到交易对行情") - } - var addPositionBuyPrice decimal.Decimal if req.AddPositionType == 1 { @@ -1759,8 +1839,8 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit)) //计算减仓数量 - if req.ReducePercent.Cmp(decimal.NewFromInt(0)) > 0 { - reduceAmount = totalAmount.Mul(req.ReducePercent.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit)) + if req.AddPositionType == 2 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { + reduceAmount = totalAmount.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit)) } data.RemainingQuantity = totalAmount.Sub(reduceAmount) @@ -1776,163 +1856,163 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio return nil } -// 手动加仓 -func (e *LinePreOrder) AddPosition(req *dto.LinePreOrderAddPositionReq) error { - lastPositionOrder := models.LinePreOrder{} - var tradeSet models2.TradeSet +// // 手动加仓 +// func (e *LinePreOrder) AddPosition(req *dto.LinePreOrderAddPositionReq) error { +// lastPositionOrder := models.LinePreOrder{} +// var tradeSet models2.TradeSet - if req.OrderType == 1 { - tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0) - } else if req.OrderType == 2 { - tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1) - } else { - return fmt.Errorf("交易对:%s 订单类型错误", req.Symbol) - } +// if req.OrderType == 1 { +// tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0) +// } else if req.OrderType == 2 { +// tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1) +// } else { +// return fmt.Errorf("交易对:%s 订单类型错误", req.Symbol) +// } - if tradeSet.LastPrice == "" { - return fmt.Errorf("交易对:%s 交易对配置错误", req.Symbol) - } +// if tradeSet.LastPrice == "" { +// return fmt.Errorf("交易对:%s 交易对配置错误", req.Symbol) +// } - if err := e.Orm.Model(&lastPositionOrder).Where("symbol =? AND status = 6 AND site =? AND api_id =? AND symbol_type =? AND exchange_type=?", - req.Symbol, req.Site, req.ApiUserId, req.OrderType, req.ExchangeType).Error; err != nil { - logger.Errorf("交易对:%s查询已开仓订单失败", req.Symbol) - return fmt.Errorf("交易对:%s 没有已开仓的订单", req.Symbol) - } +// if err := e.Orm.Model(&lastPositionOrder).Where("symbol =? AND status = 6 AND site =? AND api_id =? AND symbol_type =? AND exchange_type=?", +// req.Symbol, req.Site, req.ApiUserId, req.OrderType, req.ExchangeType).Error; err != nil { +// logger.Errorf("交易对:%s查询已开仓订单失败", req.Symbol) +// return fmt.Errorf("交易对:%s 没有已开仓的订单", req.Symbol) +// } - ext := models.LinePreOrderExt{ - MainOrderId: lastPositionOrder.Id, - TakeProfitRatio: req.Profit, - ReducePriceRatio: req.ReducePriceRatio, - ReduceNumRatio: req.ReduceNumRatio, - ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, - ReduceStopLossRatio: req.ReduceStopLossRatio, - AddPositionOrderType: req.AddPositionOrderType, - AddPositionType: 2, - AddPositionVal: req.BuyPrice, - } +// ext := models.LinePreOrderExt{ +// MainOrderId: lastPositionOrder.Id, +// TakeProfitRatio: req.Profit, +// ReducePriceRatio: req.ReducePriceRatio, +// ReduceNumRatio: req.ReduceNumRatio, +// ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, +// ReduceStopLossRatio: req.ReduceStopLossRatio, +// AddPositionOrderType: req.AddPositionOrderType, +// AddPositionType: 2, +// AddPositionVal: req.BuyPrice, +// } - addPosition := models.LinePreOrder{ - SignPrice: tradeSet.LastPrice, - Pid: lastPositionOrder.Id, - MainId: lastPositionOrder.Id, - Symbol: req.Symbol, - QuoteSymbol: tradeSet.Currency, - SignPriceU: utility.StrToDecimal(tradeSet.LastPrice), - ApiId: req.ApiUserId, - Site: req.Site, - ExchangeType: req.ExchangeType, - OrderType: 0, - OrderCategory: 3, - BuyPrice: req.BuyPrice.String(), - Status: 0, - SymbolType: req.OrderType, - MainOrderType: req.AddPositionOrderType, - ExpireTime: time.Now().Add(4), - OrderSn: utility.Int64ToString(snowflakehelper.GetOrderId()), - } +// addPosition := models.LinePreOrder{ +// SignPrice: tradeSet.LastPrice, +// Pid: lastPositionOrder.Id, +// MainId: lastPositionOrder.Id, +// Symbol: req.Symbol, +// QuoteSymbol: tradeSet.Currency, +// SignPriceU: utility.StrToDecimal(tradeSet.LastPrice), +// ApiId: req.ApiUserId, +// Site: req.Site, +// ExchangeType: req.ExchangeType, +// OrderType: 0, +// OrderCategory: 3, +// BuyPrice: req.BuyPrice.String(), +// Status: 0, +// SymbolType: req.OrderType, +// MainOrderType: req.AddPositionOrderType, +// ExpireTime: time.Now().Add(4), +// OrderSn: utility.Int64ToString(snowflakehelper.GetOrderId()), +// } - tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) - if req.PricePattern == "percentage" { - addPosition.Rate = req.Price.String() - priceRate := req.Price.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 +// tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) +// if req.PricePattern == "percentage" { +// addPosition.Rate = req.Price.String() +// priceRate := req.Price.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 - if strings.ToUpper(req.Site) == "BUY" { //购买方向 - //实际下单价格 - truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit)) - addPosition.Price = truncate.String() - } else { - truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit)) - addPosition.Price = truncate.String() - } +// if strings.ToUpper(req.Site) == "BUY" { //购买方向 +// //实际下单价格 +// truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit)) +// addPosition.Price = truncate.String() +// } else { +// truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit)) +// addPosition.Price = truncate.String() +// } - } else { //实际价格下单 - addPosition.Price = req.Price.Truncate(int32(tradeSet.PriceDigit)).String() - addPosition.SignPriceType = req.PricePattern - addPosition.Rate = "0" - } - if tradeSet.Currency != "USDT" { //不是U本位 - //获取币本位兑换u的价格 - ticker2 := models2.TradeSet{} - tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(tradeSet.Coin+"USDT"))) +// } else { //实际价格下单 +// addPosition.Price = req.Price.Truncate(int32(tradeSet.PriceDigit)).String() +// addPosition.SignPriceType = req.PricePattern +// addPosition.Rate = "0" +// } +// if tradeSet.Currency != "USDT" { //不是U本位 +// //获取币本位兑换u的价格 +// ticker2 := models2.TradeSet{} +// tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(tradeSet.Coin+"USDT"))) - if tickerVal == "" { - logger.Error("查询行情失败") - return fmt.Errorf("交易对:%s 获取u本位行情失败", req.Symbol) - } +// if tickerVal == "" { +// logger.Error("查询行情失败") +// return fmt.Errorf("交易对:%s 获取u本位行情失败", req.Symbol) +// } - err := sonic.Unmarshal([]byte(tickerVal), &ticker2) +// err := sonic.Unmarshal([]byte(tickerVal), &ticker2) - if ticker2.LastPrice == "" { - logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(tradeSet.Coin+"USDT"), err) - return fmt.Errorf("交易对:%s 获取u本位行情 反序列化失败", req.Symbol) - } - //LTCBTC --> LTCUSDT - uTickerPrice := utility.StrToDecimal(ticker2.LastPrice) //94069 - //换算成U - //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 - //在换算成对应交易对对应的价值 - //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) - //计算下单数量 - addPosition.Num = req.BuyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() - } else { - fromString, _ := decimal.NewFromString(addPosition.Price) - addPosition.Num = req.BuyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() - } - //事务保存 - err := e.Orm.Transaction(func(tx *gorm.DB) error { - //添加加仓单 - if err := tx.Create(&addPosition).Error; err != nil { - return err - } +// if ticker2.LastPrice == "" { +// logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(tradeSet.Coin+"USDT"), err) +// return fmt.Errorf("交易对:%s 获取u本位行情 反序列化失败", req.Symbol) +// } +// //LTCBTC --> LTCUSDT +// uTickerPrice := utility.StrToDecimal(ticker2.LastPrice) //94069 +// //换算成U +// //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 +// //在换算成对应交易对对应的价值 +// //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 +// div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) +// //计算下单数量 +// addPosition.Num = req.BuyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() +// } else { +// fromString, _ := decimal.NewFromString(addPosition.Price) +// addPosition.Num = req.BuyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() +// } +// //事务保存 +// err := e.Orm.Transaction(func(tx *gorm.DB) error { +// //添加加仓单 +// if err := tx.Create(&addPosition).Error; err != nil { +// return err +// } - //止盈、减仓 - orders, err := makeFuturesTakeAndReduce(&addPosition, ext, tradeSet) +// //止盈、减仓 +// orders, err := makeFuturesTakeAndReduce(&addPosition, ext, tradeSet) - if err != nil { - logger.Error("构造加仓单止盈、减仓失败") - return err - } +// if err != nil { +// logger.Error("构造加仓单止盈、减仓失败") +// return err +// } - if err := e.Orm.Create(&orders).Error; err != nil { - logger.Error("保存加仓单止盈、减仓失败") - return err - } +// if err := e.Orm.Create(&orders).Error; err != nil { +// logger.Error("保存加仓单止盈、减仓失败") +// return err +// } - //处理减仓单 - for index := range orders { - //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 - if orders[index].OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 && ext.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), ext, tradeSet) +// //处理减仓单 +// for index := range orders { +// //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 +// if orders[index].OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 && ext.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { +// reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), ext, tradeSet) - if err != nil { - logger.Error("生产加仓单止盈、减仓失败") - return err - } +// if err != nil { +// logger.Error("生产加仓单止盈、减仓失败") +// return err +// } - if len(reduceChildOrders) == 0 { - continue - } +// if len(reduceChildOrders) == 0 { +// continue +// } - if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { - logger.Error("报错减仓后止盈止损失败") - return err - } - } - } +// if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { +// logger.Error("报错减仓后止盈止损失败") +// return err +// } +// } +// } - ext.OrderId = addPosition.Id - if err := tx.Create(&ext).Error; err != nil { - return err - } +// ext.OrderId = addPosition.Id +// if err := tx.Create(&ext).Error; err != nil { +// return err +// } - return nil - }) +// return nil +// }) - if err != nil { - logger.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) - return fmt.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) - } +// if err != nil { +// logger.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) +// return fmt.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) +// } - return nil -} +// return nil +// } diff --git a/app/admin/service/line_symbol.go b/app/admin/service/line_symbol.go index 9a8f2d7..362e65f 100644 --- a/app/admin/service/line_symbol.go +++ b/app/admin/service/line_symbol.go @@ -294,17 +294,14 @@ func (e *LineSymbol) ResetSpotSymbol() error { logger.Error("获取币安现货交易对失败") return err } - symbols := make([]models.LineSymbol, 0) - + insertSymbols := make([]models.LineSymbol, 0) sysConfig := SysConfig{Service: service.Service{Orm: e.Orm}} var req = new(dto.SysConfigByKeyReq) var resp = new(dto.GetSysConfigByKEYForServiceResp) req.ConfigKey = "quote_volume_24hr" sysConfig.GetWithKey(req, resp) - symbolBlack := make([]models.LineSymbolBlack, 0) - e.Orm.Model(&models.LineSymbolBlack{}).Where("type = '1'").Find(&symbolBlack) - + symbolBlackMap := getSymbolBlackMap(e, "1") type Ticker struct { Symbol string `json:"symbol"` Price string `json:"price"` @@ -317,17 +314,16 @@ func (e *LineSymbol) ResetSpotSymbol() error { return err } + //判断是否在黑名单、是否需要修改 for symbol, tradeSet := range tradeSets { key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BINANCE, symbol) //判断是否在黑名单里面 - for _, black := range symbolBlack { - if black.Symbol == symbol { - helper.DefaultRedis.DeleteString(key) - deleteSymbols = append(deleteSymbols, symbol) - continue - } + if _, ok := symbolBlackMap[symbol]; ok { + helper.DefaultRedis.DeleteString(key) + deleteSymbols = append(deleteSymbols, symbol) + continue } val := helper.DefaultRedis.Get(key).Val() @@ -369,7 +365,14 @@ func (e *LineSymbol) ResetSpotSymbol() error { if lineSymbol.Symbol == "" { continue } - symbols = append(symbols, lineSymbol) + insertSymbols = append(insertSymbols, lineSymbol) + } + } + + //判断已经移除的交易对 + for _, v := range oldMapSymbols { + if _, ok := tradeSets[v.Symbol]; !ok && !utility.ContainsStr(deleteSymbols, v.Symbol) { + deleteSymbols = append(deleteSymbols, v.Symbol) } } @@ -393,8 +396,8 @@ func (e *LineSymbol) ResetSpotSymbol() error { batchDeleteBySymbols(deleteSymbols, "1", e) } - if len(symbols) > 0 { - err := e.Orm.Model(&models.LineSymbol{}).Omit("api_id").Create(&symbols).Error + if len(insertSymbols) > 0 { + err := e.Orm.Model(&models.LineSymbol{}).Omit("api_id").Create(&insertSymbols).Error if err != nil { return err } @@ -403,6 +406,18 @@ func (e *LineSymbol) ResetSpotSymbol() error { return nil } +func getSymbolBlackMap(e *LineSymbol, symbolType string) map[string]models.LineSymbolBlack { + symbolBlack := make([]models.LineSymbolBlack, 0) + symbolBlackMap := make(map[string]models.LineSymbolBlack) + + e.Orm.Model(&models.LineSymbolBlack{}).Where("type = ?", symbolType).Find(&symbolBlack) + + for _, v := range symbolBlack { + symbolBlackMap[v.Symbol] = v + } + return symbolBlackMap +} + // 批量删除 根据交易对名 // symbolType 1-现货 2-合约 func batchDeleteBySymbols(deleteSymbols []string, symbolType string, e *LineSymbol) { @@ -459,8 +474,7 @@ func (e *LineSymbol) ResetFuturesSymbol() error { req.ConfigKey = "quote_volume_24hr" sysConfig.GetWithKey(req, resp) symbols := make([]models.LineSymbol, 0) - symbolBlack := make([]models.LineSymbolBlack, 0) - + symbolBlackMap := getSymbolBlackMap(e, "2") type Ticker struct { Symbol string `json:"symbol"` Price string `json:"price"` @@ -473,19 +487,17 @@ func (e *LineSymbol) ResetFuturesSymbol() error { return err } - e.Orm.Model(&models.LineSymbolBlack{}).Where("type = 2").Find(&symbolBlack) + //判断是否在黑名单、是否需要新增 for symbol, tradeSet := range tradeSets { - key := fmt.Sprintf(global.TICKER_FUTURES, global.EXCHANGE_BINANCE, symbol) //判断是否在黑名单里面 - for _, black := range symbolBlack { - if black.Symbol == symbol { - helper.DefaultRedis.DeleteString(key) - deleteSymbols = append(deleteSymbols, symbol) - continue - } + if _, ok := symbolBlackMap[symbol]; ok { + helper.DefaultRedis.DeleteString(key) + deleteSymbols = append(deleteSymbols, symbol) + continue } + val := helper.DefaultRedis.Get(key).Val() var spotTicker24h commonModels.TradeSet sonic.Unmarshal([]byte(val), &spotTicker24h) @@ -528,6 +540,13 @@ func (e *LineSymbol) ResetFuturesSymbol() error { } } + //交易所已经没有的交易对直接去除 + for _, v := range oldMapSymbols { + if _, ok := tradeSets[v.Symbol]; !ok && !utility.ContainsStr(deleteSymbols, v.Symbol) { + deleteSymbols = append(deleteSymbols, v.Symbol) + } + } + groups, err := getSymbolGroups(e, "2") if err != nil { return err diff --git a/app/admin/service/line_symbol_black.go b/app/admin/service/line_symbol_black.go index 4c1e7f9..b65ec9e 100644 --- a/app/admin/service/line_symbol_black.go +++ b/app/admin/service/line_symbol_black.go @@ -79,15 +79,11 @@ func (e *LineSymbolBlack) Insert(c *dto.LineSymbolBlackInsertReq) error { } err = e.Orm.Create(&data).Error - if err != nil { - e.Log.Errorf("LineSymbolBlackService Insert error:%s \r\n", err) - return err - } - return e.reloadSymbol(c.Type) + return err } -func (e *LineSymbolBlack) reloadSymbol(symbolType string) error { +func (e *LineSymbolBlack) ReloadSymbol(symbolType string) error { symbolService := LineSymbol{Service: e.Service} if symbolType == "1" { @@ -133,21 +129,12 @@ func (e *LineSymbolBlack) Update(c *dto.LineSymbolBlackUpdateReq, p *actions.Dat if db.RowsAffected == 0 { return errors.New("无权更新该数据") } - return e.reloadSymbol(c.Type) + return nil } // Remove 删除LineSymbolBlack func (e *LineSymbolBlack) Remove(d *dto.LineSymbolBlackDeleteReq, p *actions.DataPermission) error { var data models.LineSymbolBlack - types := make([]string, 0) - - e.Orm.Model(&data).Where("id in ?", d.GetId()).Select("type").Distinct().Find(&types) - - for _, v := range types { - if v != "" { - e.reloadSymbol(v) - } - } db := e.Orm.Model(&data). Scopes( diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index 091f1c6..adcd648 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -461,7 +461,7 @@ func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, excha mainOrders := make([]DbModels.LinePreOrder, 0) if err := db.Model(&DbModels.LinePreOrder{}). - Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?", + Where("api_id =? AND status>4 AND order_type =0 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?", apiId, symbol, symbolType, side, exchangeType, newId). Select("id", "main_id", "order_sn").Find(&mainOrders).Error; err != nil { return nil, err diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 8c32a08..6f075ac 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -93,7 +93,7 @@ func handleFutOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderSta switch { //主单成交 case preOrder.OrderType == 0 && orderStatus == 6: - handleFutMainOrderFilled(db, preOrder) + handleFutMainOrderFilled(db, preOrder, preOrder.Id, true) //止盈成交 case preOrder.OrderType == 1 && orderStatus == 6: handleTakeProfit(db, preOrder) @@ -327,22 +327,30 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 止盈单成交 func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) - removePosition(db, preOrder) + childCount, _ := GetChildTpOrder(db, preOrder.Id) - futApi := FutRestApi{} - apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + if childCount > 0 { + extOrderId := preOrder.Pid //ext主单id - if apiUserInfo.Id > 0 { - if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + handleFutMainOrderFilled(db, preOrder, extOrderId, false) + } else { + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) + + futApi := FutRestApi{} + apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + + if apiUserInfo.Id > 0 { + if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { + logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + } } - } - ids := []int{preOrder.Pid, preOrder.MainId} - //主单止盈成交 - if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND order_type=0", ids).Update("status", 9).Error; err != nil { - logger.Errorf("主单止盈成功修改主单状态失败 订单号:%s:", err) + ids := []int{preOrder.Pid, preOrder.MainId} + //主单止盈成交 + if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND order_type=0", ids).Update("status", 9).Error; err != nil { + logger.Errorf("主单止盈成功修改主单状态失败 订单号:%s:", err) + } } } @@ -396,7 +404,7 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) { } // 处理主单成交,处理止盈、止损、减仓订单 -func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { +func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrderId int, first bool) { // 获取交易对配置和API信息 tradeSet, err := GetTradeSet(preOrder.Symbol, 1) if err != nil || tradeSet.Coin == "" { @@ -434,7 +442,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { // 获取和保存持仓数据 positionData := savePosition(db, preOrder) orderExt := models.LinePreOrderExt{} - db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) + db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) // 更新订单数量并处理止盈、止损、减仓 @@ -443,13 +451,13 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { order.Price = price.String() // 更新止盈止损订单数量 - num = updateOrderQuantity(db, order, preOrder, num, tradeSet) + num = updateOrderQuantity(db, order, preOrder, &orderExt, num, first, tradeSet) // 根据订单类型处理 switch order.OrderType { case 1: // 止盈 //亏损大于0 重新计算比例 - if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + if first && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) order.Rate = percentag.String() @@ -460,10 +468,30 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { } else { order.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() } + } else if !first && orderExt.TpTpPriceRatio.Cmp(decimal.Zero) > 0 { + //止盈后止盈 + order.Rate = orderExt.TpTpPriceRatio.String() + + if positionData.PositionSide == "LONG" { + order.Price = price.Mul(decimal.NewFromInt(1).Add(orderExt.TpTpPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } else { + order.Price = price.Mul(decimal.NewFromInt(1).Sub(orderExt.TpTpPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } } processFutTakeProfitOrder(db, FutRestApi{}, order, num) case 2: // 止损 + if !first && orderExt.TpSlPriceRatio.Cmp(decimal.Zero) > 0 { + //止盈后止损 + order.Rate = orderExt.TpSlPriceRatio.String() + + if positionData.PositionSide == "LONG" { + order.Price = price.Mul(decimal.NewFromInt(1).Sub(orderExt.TpSlPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } else { + order.Price = price.Mul(decimal.NewFromInt(1).Add(orderExt.TpSlPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } + } + processFutStopLossOrder(db, order, price, num) case 4: // 减仓 processFutReduceOrder(order, price, num) @@ -541,19 +569,18 @@ func getStopOrders(db *gorm.DB, preOrder *models.LinePreOrder) ([]models.LinePre } // 更新订单数量 -func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, num decimal.Decimal, tradeSet models2.TradeSet) decimal.Decimal { +func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, ext *models.LinePreOrderExt, num decimal.Decimal, first bool, tradeSet models2.TradeSet) decimal.Decimal { // 处理减仓比例 - if order.OrderType == 4 { - ext := DbModels.LinePreOrderExt{} - if err := db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext).Error; err != nil { - logger.Errorf("查询减仓比例失败, 订单号:%s, 错误信息: %v", order.OrderSn, err) - } + // if order.OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { + // // 计算减仓数量 + // num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + // order.Num = num.String() + // } else - // 计算减仓数量 - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) - order.Num = num.String() - } + if first && order.OrderCategory == 1 && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + // 计算止盈数量 + num = num.Mul(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + order.Num = num.String() } // 更新订单数量 @@ -613,17 +640,17 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line if err != nil { logger.Error("合约止盈下单失败:", order.OrderSn, " err:", err) if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id). - Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity}).Error; err != nil { + Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity, "rate": order.Rate, "price": order.Price}).Error; err != nil { logger.Error("合约止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). - Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate, "num": num.String(), "price": order.Price}).Error; err != nil { logger.Error("更新合约止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status =0", order.Id). - Updates(map[string]interface{}{"status": "1", "num": num.String(), "price": order.Price}).Error; err != nil { + Updates(map[string]interface{}{"status": "1"}).Error; err != nil { logger.Error("合约止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } diff --git a/services/binanceservice/orderservice.go b/services/binanceservice/orderservice.go index 9fba597..5ae881a 100644 --- a/services/binanceservice/orderservice.go +++ b/services/binanceservice/orderservice.go @@ -159,3 +159,14 @@ func GetSymbolTriggerCount(db *gorm.DB, symbol string, apiId, symbolType int) (i // func GetOpenedOrders(db *gorm.DB, apiId int, exchange, symbol, symbolType, side string) ([]models.LinePreOrder, error) { // } + +// 获取子订单止盈止损数量 +func GetChildTpOrder(db *gorm.DB, pid int) (int, error) { + var count int64 + + if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type>0 and order_type <3 and status =0", pid).Count(&count).Error; err != nil { + return 0, err + } + + return int(count), nil +} diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 3cdbb45..3a9dbb6 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -404,40 +404,41 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) // 止盈成交 func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + childCount, _ := GetChildTpOrder(db, preOrder.Id) - spotApi := SpotRestApi{} - apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + if childCount > 0 { + extOrderId := preOrder.Pid //ext主单id + positionData := savePosition(db, preOrder) + processTakeProfitAndStopLossOrders(db, preOrder, &positionData, extOrderId, false) + } else { + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) - if apiUserInfo.Id > 0 { - req := CancelOpenOrdersReq{ - Symbol: preOrder.Symbol, - ApiId: preOrder.ApiId, - } - if err := spotApi.CancelOpenOrders(db, req); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + spotApi := SpotRestApi{} + apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + + if apiUserInfo.Id > 0 { + req := CancelOpenOrdersReq{ + Symbol: preOrder.Symbol, + ApiId: preOrder.ApiId, + } + if err := spotApi.CancelOpenOrders(db, req); err != nil { + logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + } } + + removePosition(db, preOrder) + + db.Transaction(func(tx *gorm.DB) error { + ids := []int{preOrder.Pid, preOrder.MainId} + if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6 AND order_type=0", ids).Update("status", 9).Error; err != nil { + logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新主单失败:%v", preOrder.OrderSn, err) + + return err + } + + return nil + }) } - - removePosition(db, preOrder) - - db.Transaction(func(tx *gorm.DB) error { - ids := []int{preOrder.Pid, preOrder.MainId} - if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6 AND order_type=0", ids).Update("status", 9).Error; err != nil { - logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新主单失败:%v", preOrder.OrderSn, err) - - return err - } - - // if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND status=0",).Update("status", 4).Error; err != nil { - // logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新取消状态失败:%v", preOrder.OrderSn, err) - - // return err - // } - - return nil - }) - } // 移除仓位信息 @@ -550,7 +551,7 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } - processTakeProfitAndStopLossOrders(db, preOrder, &positionData) + processTakeProfitAndStopLossOrders(db, preOrder, &positionData, preOrder.Id, true) } // 解析订单状态 @@ -618,7 +619,8 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r // 主单成交 处理止盈止损订单 // preOrder 主单 // positionData 持仓缓存信息 -func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto) { +// fist 首次止盈止损 +func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto, extOrderId int, fist bool) { orders := []models.LinePreOrder{} tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) @@ -651,18 +653,14 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd price := utility.StrToDecimal(preOrder.Price) spotApi := SpotRestApi{} orderExt := models.LinePreOrderExt{} - db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) + db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) for _, order := range orders { order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() - if order.OrderType == 4 { - ext := DbModels.LinePreOrderExt{} - db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext) - - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - order.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() - } + if fist && order.OrderCategory == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + //主单第一次且止盈数量不是100% 止盈数量 + order.Num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() } if err := db.Model(&order).Update("num", order.Num).Error; err != nil { @@ -672,16 +670,29 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd switch order.OrderType { case 1: // 止盈 //亏损大于0 重新计算比例 - if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + if fist && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) - percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + + if fist { + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + } + order.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } else if orderExt.Id > 0 { + percentag := orderExt.TpTpPriceRatio + order.Rate = percentag.String() + order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() } processTakeProfitOrder(db, spotApi, order) case 2: // 止损 + if !fist { + order.Rate = orderExt.TpSlPriceRatio.Truncate(2).String() + order.Price = price.Mul(decimal.NewFromInt(1).Sub(orderExt.TpSlPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } + processStopLossOrder(order) case 4: //减仓 processSpotReduceOrder(order) @@ -761,16 +772,16 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP if err != nil { logger.Error("现货止盈下单失败:", order.OrderSn, " err:", err) if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id). - Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity}).Error; err != nil { + Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity, "rate": order.Rate, "price": order.Price}).Error; err != nil { logger.Error("现货止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). - Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate, "price": order.Price, "num": order.Num}).Error; err != nil { logger.Error("更新现货止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). - Updates(map[string]interface{}{"status": "1", "num": order.Num, "price": order.Price}).Error; err != nil { + Updates(map[string]interface{}{"status": "1"}).Error; err != nil { logger.Error("现货止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } From 0aa2ab7355dce9908c112c8b6c63ff45f5cafc32 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 7 Mar 2025 16:48:55 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E6=8B=86=E5=88=86=E5=8A=A0=E4=BB=93?= =?UTF-8?q?=E3=80=81=E5=87=8F=E4=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/dto/line_pre_order.go | 16 ++- app/admin/service/line_pre_order.go | 128 ++++++++++++++++----- services/binanceservice/futuresrest.go | 104 +++++++++++++---- services/binanceservice/spotreset.go | 144 ++++++++++++++++++------ 4 files changed, 303 insertions(+), 89 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 2199b32..56a8d76 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -277,6 +277,10 @@ func (req LineAddPreOrderReq) Valid() error { return errors.New("主单减仓数量百分比不能为空") } + if req.ReducePriceRatio.IsZero() || req.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + return errors.New("主单减仓价格百分比错误") + } + for _, v := range req.Ext { name := "加仓" @@ -299,13 +303,17 @@ func (req LineAddPreOrderReq) Valid() error { return errors.New("止盈价格不正确") } - if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + (v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { return errors.New("止盈后止盈价格不正确") } - if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("止盈后止损价格不正确") - } + // if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + // v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + // (v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { + // return errors.New("止盈后止损价格不正确") + // } } return nil diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 78af479..5e36941 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -481,10 +481,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit)) defultExt2.TotalBefore = defultExt.TotalAfter defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) + defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2) preOrderExts = append(preOrderExts, defultExt) calculateResp := dto.CalculateBreakEvenRatioResp{} mainParam := dto.CalculateBreakEevenRatioReq{ + AddType: 2, Price: mainPrice, ExchangeType: req.ExchangeType, Symbol: req.Symbol, @@ -492,8 +494,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP BuyPrice: buyPrice, LossBeginPercent: decimal.Zero, LossEndPercent: req.ReducePriceRatio, - AddPositionType: 2, - AddPositionVal: decimal.Zero, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, } //计算减仓后 @@ -510,14 +512,17 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP ext := models.LinePreOrderExt{ AddType: addPosition.AddType, OrderType: addPosition.OrderType, + PriceRatio: addPosition.PriceRatio, TakeProfitRatio: addPosition.TakeProfitRatio, TakeProfitNumRatio: addPosition.TakeProfitNumRatio, + StopLossRatio: addPosition.StopLossRatio, TpTpPriceRatio: addPosition.TpTpPriceRatio, TpSlPriceRatio: addPosition.TpSlPriceRatio, AddPositionType: addPosition.AddPositionType, AddPositionVal: addPosition.AddPositionVal, } + mainParam.AddType = addPosition.AddType mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType mainParam.AddPositionVal = req.Ext[index].AddPositionVal @@ -623,7 +628,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder) if req.ReduceNumRatio.Cmp(decimal.Zero) > 0 && req.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt, tradeSet); err != nil { + if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt2, tradeSet, false); err != nil { logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err) return err } else if len(newOrders) > 0 { @@ -638,30 +643,30 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //添加止盈单 for index, v := range preOrderExts { preOrderExts[index].MainOrderId = AddOrder.Id - if index == 0 { - preOrderExts[index].OrderId = AddOrder.Id - continue - } - var AddOrder models.LinePreOrder + // if index == 0 { + // preOrderExts[index].OrderId = AddOrder.Id + // continue + // } + var newOrder models.LinePreOrder if v.AddType == 1 { - AddOrder = createPreAddPosition(&AddOrder, v, tradeSet) + newOrder = createPreAddPosition(&AddOrder, v, tradeSet) } else if v.AddType == 2 { - AddOrder = createPreReduceOrder(&AddOrder, v, tradeSet) + newOrder = createPreReduceOrder(&AddOrder, v, tradeSet) } - if AddOrder.OrderSn == "" { + if newOrder.OrderSn == "" { continue } - if err := e.Orm.Create(&AddOrder).Error; err != nil { + if err := e.Orm.Create(&newOrder).Error; err != nil { logger.Error("保存加仓单失败") return err } - preOrderExts[index].OrderId = AddOrder.Id + preOrderExts[index].OrderId = newOrder.Id //止盈、减仓 - orders, err := makeFuturesTakeAndReduce(&AddOrder, v, tradeSet) + orders, err := makeFuturesTakeAndReduce(&newOrder, v, tradeSet) if err != nil { logger.Error("构造止盈、止损失败") @@ -676,7 +681,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP for index := range orders { //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 if orders[index].OrderType == 1 && v.TakeProfitRatio.Cmp(decimal.Zero) > 0 && v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) < 0 { - reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet) + reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet, true) if err != nil { logger.Error("生产止盈后止盈、减仓失败") @@ -722,6 +727,11 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.Pid = preOrder.Id data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) data.MainId = preOrder.Id + + if preOrder.MainId > 0 { + data.MainId = preOrder.MainId + } + data.CreatedAt = time.Now() data.MainOrderType = v.OrderType data.Status = 0 @@ -761,12 +771,18 @@ func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrder stopOrder.Id = 0 stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) stopOrder.Pid = preOrder.Id - stopOrder.MainId = preOrder.MainId + stopOrder.MainId = preOrder.Id + + if preOrder.MainId > 0 { + stopOrder.MainId = preOrder.MainId + } + stopOrder.OrderType = 4 stopOrder.Status = 0 stopOrder.Rate = ext.PriceRatio.String() stopOrder.Num = ext.TotalAfter.Sub(ext.TotalBefore).Abs().Truncate(int32(tradeSet.AmountDigit)).String() stopOrder.BuyPrice = "0" + stopOrder.Rate = ext.PriceRatio.String() if strings.ToUpper(preOrder.Site) == "BUY" { stopOrder.Site = "SELL" @@ -801,7 +817,11 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO profitOrder.Pid = preOrder.Id profitOrder.OrderType = 1 profitOrder.Status = 0 - profitOrder.MainId = preOrder.MainId + profitOrder.MainId = preOrder.Id + + if preOrder.MainId > 0 { + profitOrder.MainId = preOrder.MainId + } profitOrder.BuyPrice = "0" profitOrder.Site = side @@ -877,16 +897,25 @@ func makeTpOrder(parentOrder *models.LinePreOrder, reminQuantity decimal.Decimal } // 构建减仓后止盈止损 -func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { +// isTpTp 是否止盈后止盈止损 +func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet, isTpTp bool) ([]models.LinePreOrder, error) { orders := make([]models.LinePreOrder, 0) - var num decimal.Decimal + num := ext.TotalAfter + var takeProfitRatio, slPriceRatio decimal.Decimal + if isTpTp { + takeProfitRatio = ext.TpTpPriceRatio + slPriceRatio = ext.TpSlPriceRatio + } else { + takeProfitRatio = ext.TakeProfitRatio + slPriceRatio = ext.StopLossRatio + } if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))) num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) } - if ext.TpTpPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + if takeProfitRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { takeProfitOrder := models.LinePreOrder{} copier.Copy(&takeProfitOrder, parentOrder) takeProfitOrder.Id = 0 @@ -894,21 +923,25 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) takeProfitOrder.Status = 0 takeProfitOrder.OrderType = 1 - takeProfitOrder.Rate = ext.TpTpPriceRatio.String() takeProfitOrder.SignPrice = parentOrder.Price takeProfitOrder.CreatedAt = time.Now() takeProfitOrder.BuyPrice = "0" takeProfitOrder.MainOrderType = "LIMIT" takeProfitOrder.Num = num.String() //止盈需要累加之前的亏损 - takeProfitOrder.Rate = ext.TpTpPriceRatio.Truncate(2).String() + if isTpTp { + takeProfitOrder.Rate = takeProfitRatio.Truncate(2).String() + } else { + takeProfitOrder.Rate = takeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() + } + takeProfitOrder.BuyPrice = "0" binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) orders = append(orders, takeProfitOrder) } //有止损单 - if ext.TpSlPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + if slPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { var stoploss models.LinePreOrder copier.Copy(&stoploss, parentOrder) @@ -920,7 +953,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line stoploss.OrderType = 2 stoploss.SignPrice = parentOrder.Price stoploss.BuyPrice = "0" - stoploss.Rate = ext.TpSlPriceRatio.String() + stoploss.Rate = slPriceRatio.String() stoploss.MainOrderType = "LIMIT" stoploss.Num = num.String() stoploss.BuyPrice = "0" @@ -1658,6 +1691,7 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err // ClearUnTriggered 清除待触发的交易对 func (e *LinePreOrder) ClearUnTriggered() error { var orderLists []models.LinePreOrder + positions := map[string]positiondto.LinePreOrderPositioinDelReq{} e.Orm.Model(&models.LinePreOrder{}).Where("main_id = 0 AND pid = 0 AND status = '0'").Find(&orderLists).Unscoped().Delete(&models.LinePreOrder{}) for _, order := range orderLists { @@ -1680,8 +1714,43 @@ func (e *LinePreOrder) ClearUnTriggered() error { key := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType) helper.DefaultRedis.LRem(key, string(marshal)) } + + //会影响持仓的 + removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType) + + if _, ok := positions[removeSymbolKey]; !ok { + positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{ + ApiId: order.ApiId, + Symbol: order.Symbol, + ExchangeType: order.ExchangeType, + Side: order.Site, + SymbolType: order.SymbolType, + } + } + e.Orm.Model(&models.LinePreOrder{}).Where("main_id = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{}) } + + //清理仓位缓存 + for _, v := range positions { + var count int64 + e.Orm.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count) + + //没有已开仓的订单 直接清理仓位 + if count == 0 { + var key string + + if v.SymbolType == 1 { + key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } else { + key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } + + helper.DefaultRedis.DeleteString(key) + } + } return nil } @@ -1774,8 +1843,8 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { BuyPrice: buyPrice, LossBeginPercent: lossBeginPercent, LossEndPercent: req.ReducePriceRatio, - AddPositionType: 2, - AddPositionVal: decimal.Zero, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, } //计算减仓后 @@ -1794,6 +1863,7 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { }) for index := range req.Ext { + mainParam.AddType = req.Ext[index].AddType mainParam.LossBeginPercent = lossBeginPercent mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType @@ -1819,9 +1889,9 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio var addPositionBuyPrice decimal.Decimal - if req.AddPositionType == 1 { + if req.AddType == 1 && req.AddPositionType == 1 { addPositionBuyPrice = req.BuyPrice.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(2) - } else { + } else if req.AddType == 1 { addPositionBuyPrice = req.AddPositionVal.Truncate(2) } @@ -1839,7 +1909,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit)) //计算减仓数量 - if req.AddPositionType == 2 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { + if req.AddType == 2 && req.AddPositionType == 1 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { reduceAmount = totalAmount.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit)) } diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 6f075ac..5cd72a9 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -100,6 +100,9 @@ func handleFutOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderSta //减仓回调 case preOrder.OrderType == 4 && orderStatus == 6: handleReduceFilled(db, preOrder) + //主单取消 + case preOrder.OrderType == 0 && preOrder.Pid == 0 && orderStatus == 4: + handleMainOrderCancel(db, preOrder, 2) //止损成交 case preOrder.OrderType == 2 && orderStatus == 6: handleStopLoss(db, preOrder) @@ -154,6 +157,12 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) futApi := FutRestApi{} + mainId := preOrder.Id + + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } + db.Model(&orderExt).Where("order_id =?", preOrder.Pid).First(&orderExt) for _, v := range orders { @@ -177,39 +186,46 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { processFutStopLossOrder(db, v, utility.StrToDecimal(v.Price), totalNum) } } + nextFuturesReduceTrigger(db, mainId, totalNum, tradeSet) +} - //加仓待触发 - addPositionOrder := DbModels.LinePreOrder{} +// 下一个合约待触发 +func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tradeSet models2.TradeSet) { + nextOrder := DbModels.LinePreOrder{} + nextExt := DbModels.LinePreOrderExt{} - if err := db.Model(&addPositionOrder).Where("main_id =? AND order_category=3 AND status=0", preOrder.MainId).First(&addPositionOrder).Error; err != nil { - logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + if err := db.Model(&models.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) return } - keyFutAddpositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) - - addPositionData := AddPositionList{ - Id: addPositionOrder.Id, - OrderSn: addPositionOrder.OrderSn, - MainId: addPositionOrder.MainId, - Pid: addPositionOrder.Pid, - Price: utility.StrToDecimal(addPositionOrder.Price), - ApiId: addPositionOrder.ApiId, - Symbol: addPositionOrder.Symbol, - Side: addPositionOrder.Site, - SymbolType: addPositionOrder.SymbolType, + if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) } - addVal, err := sonic.MarshalString(addPositionData) + num := totalNum + //移除缓存 + key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) + vals, _ := helper.DefaultRedis.GetAllList(key) + item := ReduceListItem{} - if err != nil { - logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + for _, val := range vals { + sonic.Unmarshal([]byte(val), &item) + if item.MainId == mainId { + if _, err := helper.DefaultRedis.LRem(key, val); err != nil { + logger.Errorf("减仓单 redis删除失败 main_id:%v err:%v", mainId, err) + } + } } - if err := helper.DefaultRedis.RPushList(keyFutAddpositionKey, addVal); err != nil { - logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + // + if nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { + // 计算减仓数量 + num = totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + nextOrder.Num = num.String() } + + processFutReduceOrder(nextOrder, utility.StrToDecimal(nextOrder.Price).Truncate(int32(tradeSet.PriceDigit)), num) } // 获取合约可用数量 @@ -407,6 +423,7 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) { func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrderId int, first bool) { // 获取交易对配置和API信息 tradeSet, err := GetTradeSet(preOrder.Symbol, 1) + mainId := preOrder.Id if err != nil || tradeSet.Coin == "" { logger.Errorf("获取交易对配置失败, 回调订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return @@ -418,6 +435,10 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd return } + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } + // 处理主单加仓 if preOrder.OrderCategory == 3 { if err := handleMainOrderAddPosition(db, preOrder); err != nil { @@ -430,6 +451,39 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } + + //加仓待触发 + addPositionOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.Id).Order("rate asc").Find(&addPositionOrders).Error; err != nil { + logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + keyFutAddpositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) + + for _, addPositionOrder := range addPositionOrders { + addPositionData := AddPositionList{ + Id: addPositionOrder.Id, + OrderSn: addPositionOrder.OrderSn, + MainId: addPositionOrder.MainId, + Pid: addPositionOrder.Pid, + Price: utility.StrToDecimal(addPositionOrder.Price), + ApiId: addPositionOrder.ApiId, + Symbol: addPositionOrder.Symbol, + Side: addPositionOrder.Site, + SymbolType: addPositionOrder.SymbolType, + } + + addVal, err := sonic.MarshalString(addPositionData) + + if err != nil { + logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + return + } + + if err := helper.DefaultRedis.RPushList(keyFutAddpositionKey, addVal); err != nil { + logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + } } // 获取止盈止损订单 @@ -493,10 +547,12 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd } processFutStopLossOrder(db, order, price, num) - case 4: // 减仓 - processFutReduceOrder(order, price, num) + // case 4: // 减仓 + // processFutReduceOrder(order, price, num) } } + + nextFuturesReduceTrigger(db, mainId, num, tradeSet) } // 处理主单加仓 diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 3a9dbb6..6718086 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -119,7 +119,7 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus handleMainReduceFilled(db, preOrder) //主单取消 case preOrder.OrderType == 0 && preOrder.Pid == 0 && orderStatus == 4: - handleMainOrderCancel(preOrder) + handleMainOrderCancel(db, preOrder, 1) // 止盈成交 case preOrder.OrderType == 1 && orderStatus == 6: handleSpotTakeProfitFilled(db, preOrder) @@ -224,38 +224,57 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } - //加仓待触发 - addPositionOrder := DbModels.LinePreOrder{} + mainId := preOrder.Id - if err := db.Model(&addPositionOrder).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.MainId).First(&addPositionOrder).Error; err != nil { - logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + if preOrder.MainId > 0 { + mainId = preOrder.MainId } - keySpotAddPosition := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + nextSpotReduceTrigger(db, mainId, totalNum, tradeSet) +} - addPositionData := AddPositionList{ - Id: addPositionOrder.Id, - OrderSn: addPositionOrder.OrderSn, - MainId: addPositionOrder.MainId, - Pid: addPositionOrder.Pid, - Price: utility.StrToDecimal(addPositionOrder.Price), - ApiId: addPositionOrder.ApiId, - Symbol: addPositionOrder.Symbol, - Side: addPositionOrder.Site, - SymbolType: addPositionOrder.SymbolType, +// 缓存下一个减仓单 +func nextSpotReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tradeSet models2.TradeSet) bool { + nextOrder := DbModels.LinePreOrder{} + nextExt := DbModels.LinePreOrderExt{} + // var percentag decimal.Decimal + + if err := db.Model(&models.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) + return true } - addVal, err := sonic.MarshalString(addPositionData) - - if err != nil { - logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) } - if err := helper.DefaultRedis.RPushList(keySpotAddPosition, addVal); err != nil { - logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + //移除缓存 + key := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE) + vals, _ := helper.DefaultRedis.GetAllList(key) + item := ReduceListItem{} + + for _, val := range vals { + sonic.Unmarshal([]byte(val), &item) + if item.MainId == mainId { + if _, err := helper.DefaultRedis.LRem(key, val); err != nil { + logger.Errorf("减仓单 redis删除失败 main_id:%v err:%v", mainId, err) + } + } } + + //减仓配置 且减仓比例大于0小于100 + if nextExt.Id > 0 && nextExt.AddType == 2 && nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { + num := totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + // percentag = positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)).Truncate(2) + nextOrder.Num = num.String() + } + // percentag = nextExt.PriceRatio.Add(percentag) + // nextOrder.Rate = percentag.String() + // nextOrder.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)).String() + + //减仓待触发 + processSpotReduceOrder(nextOrder) + return false } // 获取主单可用数量 @@ -344,8 +363,16 @@ func getSpotPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels.Lin } // 主单取消 -func handleMainOrderCancel(preOrder *DbModels.LinePreOrder) { - preOrderKey := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) +// symbolType 1:现货 2:合约 +func handleMainOrderCancel(db *gorm.DB, preOrder *DbModels.LinePreOrder, symboType int) { + var preOrderKey string + + if symboType == 1 { + preOrderKey = fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) + } else { + preOrderKey = fmt.Sprintf(rediskey.PreFutOrderList, global.EXCHANGE_BINANCE) + } + preSpotOrders, _ := helper.DefaultRedis.GetAllList(preOrderKey) preOrderCache := DbModels.LinePreOrder{} @@ -356,10 +383,17 @@ func handleMainOrderCancel(preOrder *DbModels.LinePreOrder) { sonic.Unmarshal([]byte(v), &preOrderCache) if preOrderCache.Pid == preOrder.Id { - _, err := helper.DefaultRedis.LRem(preOrderKey, v) + var count int64 + db.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + preOrder.ApiId, preOrder.Site, preOrder.Symbol, preOrder.SymbolType, preOrder.ExchangeType).Count(&count) - if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除缓存失败:%v", preOrder.OrderSn, err) + if count == 0 { + _, err := helper.DefaultRedis.LRem(preOrderKey, v) + + if err != nil { + logger.Errorf("订单回调失败, 回调订单号:%s 删除缓存失败:%v", preOrder.OrderSn, err) + } } } } @@ -549,9 +583,45 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } } + + //加仓待触发 + addPositionOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.Id).Order("rate asc").Find(&addPositionOrders).Error; err != nil { + logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + + keySpotAddPosition := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + + for _, addPositionOrder := range addPositionOrders { + addPositionData := AddPositionList{ + Id: addPositionOrder.Id, + OrderSn: addPositionOrder.OrderSn, + MainId: addPositionOrder.MainId, + Pid: addPositionOrder.Pid, + Price: utility.StrToDecimal(addPositionOrder.Price), + ApiId: addPositionOrder.ApiId, + Symbol: addPositionOrder.Symbol, + Side: addPositionOrder.Site, + SymbolType: addPositionOrder.SymbolType, + } + + addVal, err := sonic.MarshalString(addPositionData) + + if err != nil { + logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + return + } + + if err := helper.DefaultRedis.RPushList(keySpotAddPosition, addVal); err != nil { + logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + } + } processTakeProfitAndStopLossOrders(db, preOrder, &positionData, preOrder.Id, true) + } // 解析订单状态 @@ -623,6 +693,11 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto, extOrderId int, fist bool) { orders := []models.LinePreOrder{} tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) + mainId := preOrder.Id + + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } if tradeSet.Coin == "" { logger.Error("获取交易对失败") @@ -654,9 +729,11 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd spotApi := SpotRestApi{} orderExt := models.LinePreOrderExt{} db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) + num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) + //止盈止损 for _, order := range orders { - order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() + order.Num = num.String() if fist && order.OrderCategory == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { //主单第一次且止盈数量不是100% 止盈数量 @@ -694,10 +771,13 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd } processStopLossOrder(order) - case 4: //减仓 - processSpotReduceOrder(order) + // case 4: //减仓 + // processSpotReduceOrder(order) } } + + //待触发减仓单 + nextSpotReduceTrigger(db, mainId, num, tradeSet) } // 根据下单百分比计算价格 From 00aa341c2f9a64304d3d00ccae0bba6391a439d9 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Mon, 10 Mar 2025 11:13:08 +0800 Subject: [PATCH 11/13] 1 --- app/admin/service/dto/line_pre_order.go | 53 ++++++------ app/admin/service/line_pre_order.go | 85 ++++++++++++++++--- common/const/rediskey/redis_key.go | 7 +- services/binanceservice/commonservice.go | 2 +- .../binanceservice/futuresjudgeservice.go | 17 ++-- services/binanceservice/futuresrest.go | 59 +++++++------ services/binanceservice/spotjudgeservice.go | 16 ++-- services/binanceservice/spotreset.go | 26 +++--- 8 files changed, 176 insertions(+), 89 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 56a8d76..4166c36 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -353,31 +353,34 @@ type LineTreeOrder struct { // LineBatchAddPreOrderReq 批量添加订单请求参数 type LineBatchAddPreOrderReq struct { - ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type - SymbolType int `json:"symbol_type"` //主单交易对类型 0-现货 1-合约 - OrderType int `json:"order_type"` //订单类型 - SymbolGroupId string `json:"symbol_group_id"` //交易对组id - Symbol string `json:"symbol"` //交易对 - ApiUserId string `json:"api_id"` //下单用户 - Site string `json:"site"` //购买方向 - BuyPrice string `json:"buy_price"` //购买金额 U - PricePattern string `json:"price_pattern"` //价格模式 - Price string `json:"price"` //下单价百分比 - Profit string `json:"profit"` //止盈价 - StopPrice string `json:"stop_price"` //止损价 - PriceType string `json:"price_type"` //价格类型 - SaveTemplate string `json:"save_template"` //是否保存模板 - TemplateName string `json:"template_name"` //模板名字 - OrderNum int `json:"order_num"` //脚本运行次数 - Script string `json:"script"` //是否是脚本运行 1 = 是 0= 否 - CoverType int `json:"cover_type"` //对冲类型 1= 现货对合约 2=合约对合约 3 合约对现货 - ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 - MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET) - ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比 - ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比 - ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 - ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 - Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段 + ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type + SymbolType int `json:"symbol_type"` //主单交易对类型 0-现货 1-合约 + OrderType int `json:"order_type"` //订单类型 + SymbolGroupId string `json:"symbol_group_id"` //交易对组id + Symbol string `json:"symbol"` //交易对 + ApiUserId string `json:"api_id"` //下单用户 + Site string `json:"site"` //购买方向 + BuyPrice string `json:"buy_price"` //购买金额 U + PricePattern string `json:"price_pattern"` //价格模式 + Price string `json:"price"` //下单价百分比 + ProfitNumRatio decimal.Decimal `json:"profit_num_ratio"` //止盈数量百分比 + ProfitTpTpPriceRatio decimal.Decimal `json:"profit_tp_tp_price_ratio"` //止盈后止盈价百分比 + ProfitTpSlPriceRatio decimal.Decimal `json:"profit_tp_sl_price_ratio"` //止盈后止损价百分比 + Profit string `json:"profit"` //止盈价 + StopPrice string `json:"stop_price"` //止损价 + PriceType string `json:"price_type"` //价格类型 + SaveTemplate string `json:"save_template"` //是否保存模板 + TemplateName string `json:"template_name"` //模板名字 + OrderNum int `json:"order_num"` //脚本运行次数 + Script string `json:"script"` //是否是脚本运行 1 = 是 0= 否 + CoverType int `json:"cover_type"` //对冲类型 1= 现货对合约 2=合约对合约 3 合约对现货 + ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 + MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET) + ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比 + ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比 + ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 + ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 + Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段 } func (req LineBatchAddPreOrderReq) CheckParams() error { diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 5e36941..96ebcc9 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -244,7 +244,49 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi } positions := map[string]positiondto.LinePreOrderPositioinDelReq{} + addPosition := binanceservice.AddPositionList{} + reduceItem := binanceservice.ReduceListItem{} + futAddPosition := map[int]string{} + spotAddPosition := map[int]string{} + futReduces := map[int]string{} + spotRedces := map[int]string{} + futAddPositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) + spotAddPositionKey := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) + futReduceKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + spotReduceKey := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE) + futAddPositionVal, _ := helper.DefaultRedis.GetAllList(futAddPositionKey) + futReduceVal, _ := helper.DefaultRedis.GetAllList(spotAddPositionKey) + spotAddPositionVal, _ := helper.DefaultRedis.GetAllList(futReduceKey) + spotReduceVal, _ := helper.DefaultRedis.GetAllList(spotReduceKey) + + for _, v := range futAddPositionVal { + sonic.Unmarshal([]byte(v), &addPosition) + if addPosition.MainId > 0 { + futAddPosition[addPosition.MainId] = v + } + } + + for _, v := range spotAddPositionVal { + sonic.Unmarshal([]byte(v), &addPosition) + if addPosition.MainId > 0 { + spotAddPosition[addPosition.MainId] = v + } + } + + for _, v := range futReduceVal { + sonic.Unmarshal([]byte(v), &reduceItem) + if reduceItem.MainId > 0 { + futReduces[reduceItem.MainId] = v + } + } + + for _, v := range spotReduceVal { + sonic.Unmarshal([]byte(v), &reduceItem) + if reduceItem.MainId > 0 { + spotRedces[reduceItem.MainId] = v + } + } //删除的缓存 for _, order := range list { redisList := dto.PreOrderRedisList{ @@ -261,6 +303,21 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi apiIds = append(apiIds, order.ApiId) } + //清除待加仓、待减仓 + if val, ok := futAddPosition[order.Id]; ok { + helper.DefaultRedis.LRem(futAddPositionKey, val) + } + + if val, ok := spotAddPosition[order.Id]; ok { + helper.DefaultRedis.LRem(spotAddPositionKey, val) + } + if val, ok := futReduces[order.Id]; ok { + helper.DefaultRedis.LRem(futReduceKey, val) + } + if val, ok := spotRedces[order.Id]; ok { + helper.DefaultRedis.LRem(spotReduceKey, val) + } + tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, fmt.Sprintf(global.TICKER_SPOT, order.ExchangeType, order.Symbol)) redisList.Price = utility.StringToDecimal(redisList.Price).Truncate(int32(tradeSet.PriceDigit)).String() marshal, _ := sonic.Marshal(redisList) @@ -483,6 +540,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2) preOrderExts = append(preOrderExts, defultExt) + preOrderExts = append(preOrderExts, defultExt2) calculateResp := dto.CalculateBreakEvenRatioResp{} mainParam := dto.CalculateBreakEevenRatioReq{ @@ -550,7 +608,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //加仓、减仓状态 tx.Model(&models.LinePreOrderStatus{}).Create(&preOrderStatus) - + preOrderExts[0].OrderId = AddOrder.Id list := dto.PreOrderRedisList{ Id: AddOrder.Id, Symbol: AddOrder.Symbol, @@ -626,7 +684,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String() tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder) - + preOrderExts[1].OrderId = stopOrder.Id if req.ReduceNumRatio.Cmp(decimal.Zero) > 0 && req.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt2, tradeSet, false); err != nil { logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err) @@ -643,10 +701,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //添加止盈单 for index, v := range preOrderExts { preOrderExts[index].MainOrderId = AddOrder.Id - // if index == 0 { - // preOrderExts[index].OrderId = AddOrder.Id - // continue - // } + if index < 2 { + // preOrderExts[index].OrderId = AddOrder.Id + continue + } var newOrder models.LinePreOrder if v.AddType == 1 { @@ -801,10 +859,10 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO orders := make([]models.LinePreOrder, 0) var side string - if strings.ToUpper(preOrder.Site) == "BUY" { - side = "SELL" - } else { + if (preOrder.OrderType != 0 && strings.ToUpper(preOrder.Site) == "BUY") || (preOrder.OrderType == 0 && strings.ToUpper(preOrder.Site) == "SELL") { side = "BUY" + } else { + side = "SELL" } if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 { @@ -1055,6 +1113,9 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p req.PricePattern = batchReq.PricePattern req.Price = batchReq.Price req.Profit = batchReq.Profit + req.ProfitNumRatio = batchReq.ProfitNumRatio + req.ProfitTpTpPriceRatio = batchReq.ProfitTpSlPriceRatio + req.ProfitTpSlPriceRatio = batchReq.ProfitTpSlPriceRatio req.Ext = batchReq.Ext req.SymbolType = batchReq.SymbolType // req.StopPrice = batchReq.StopPrice @@ -1663,8 +1724,10 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err continue } - //撤销合约的委托 - api.CancelAllFutOrder(apiUserInfo, list.Symbol) + //撤销合约的委托(根据方向撤) + orderSns, _ := binanceservice.GetOpenOrderSns(e.Orm, []int{list.Id}) + api.CancelBatchFutOrder(apiUserInfo, list.Symbol, orderSns) + // api.CancelAllFutOrder(apiUserInfo, list.Symbol) //side=BUY&positionSide=LONG是开多, //side=SELL&positionSide=LONG是平多, //side=SELL&positionSide=SHORT是开空, diff --git a/common/const/rediskey/redis_key.go b/common/const/rediskey/redis_key.go index 820f41b..56d5372 100644 --- a/common/const/rediskey/redis_key.go +++ b/common/const/rediskey/redis_key.go @@ -50,9 +50,10 @@ const ( SpotReduceList = "spot_reduce_list:%s" //现货减仓待触发 {交易所类型code} FuturesStopLossList = "futures_stoploss_list:%s" //合约止损待触发列表 {交易所类型code} FuturesReduceList = "futures_reduce_list:%s" //合约减仓待触发 {交易所类型code} - - SpotAddPositionList = "spot_add_position_list:%s" //现货加仓待触发 {交易所code} - FuturesAddPositionList = "futures_add_position_list:%s" //合约加仓待触发 {交易所code} + //现货加仓待触发 {交易所code} + SpotAddPositionList = "spot_add_position_list:%s" + //合约加仓待触发 {交易所code} + FuturesAddPositionList = "futures_add_position_list:%s" //现货持仓 {exchangeType,apiuserid,symbol,side} SpotPosition = "spot_position:%s:%v:%s_%s" //合约持仓 {exchangeType,apiuserid,symbol,side} diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index adcd648..8a47ac0 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -471,7 +471,7 @@ func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, excha } // 获取需要取消的订单号 -func getOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { +func GetOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { result := []string{} //委托中的订单 diff --git a/services/binanceservice/futuresjudgeservice.go b/services/binanceservice/futuresjudgeservice.go index 1c098ce..1d53614 100644 --- a/services/binanceservice/futuresjudgeservice.go +++ b/services/binanceservice/futuresjudgeservice.go @@ -201,8 +201,8 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes return } else if ok { defer lock.Release() - takeOrder := DbModels.LinePreOrder{} - if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type =1", reduceOrder.Pid).Find(&takeOrder).Error; err != nil { + takeOrders := make([]DbModels.LinePreOrder, 0) + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil { log.Error("查询止盈单失败") return } @@ -214,18 +214,19 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes return } - apiInfo, _ := GetApiInfo(takeOrder.ApiId) + apiInfo, _ := GetApiInfo(reduceOrder.ApiId) if apiInfo.Id == 0 { log.Error("现货减仓 查询api用户不存在") return } + for _, takeOrder := range takeOrders { + err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - - if err != nil { - log.Error("合约止盈撤单失败", err) - return + if err != nil { + log.Error("合约止盈撤单失败", err) + return + } } price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 5cd72a9..8d4b76b 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -154,7 +154,6 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { orderExt := models.LinePreOrderExt{} totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) - totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) futApi := FutRestApi{} mainId := preOrder.Id @@ -199,7 +198,7 @@ func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, return } - if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + if err := db.Model(&models.LinePreOrderExt{}).Where("order_id =? and add_type =2", nextOrder.Id).First(&nextExt).Error; err != nil { logger.Errorf("获取下一个单失败 err:%v", err) } @@ -223,6 +222,10 @@ func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, // 计算减仓数量 num = totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) nextOrder.Num = num.String() + + if err := db.Model(&nextOrder).Update("num", nextOrder.Num).Error; err != nil { + logger.Errorf("更新减仓单数量失败 err:%v", err) + } } processFutReduceOrder(nextOrder, utility.StrToDecimal(nextOrder.Price).Truncate(int32(tradeSet.PriceDigit)), num) @@ -321,16 +324,12 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) removePosition(db, preOrder) - spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - req := CancelOpenOrdersReq{ - Symbol: preOrder.Symbol, - ApiId: preOrder.ApiId, - } - if err := spotApi.CancelOpenOrders(db, req); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + mainIds := []int{preOrder.MainId} + if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil { + logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err) } } @@ -353,12 +352,12 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) removePosition(db, preOrder) - futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + mainOrder, _ := GetOrderById(db, preOrder.MainId) + if err := cancelPositionOtherOrders(apiUserInfo, db, &mainOrder, false); err != nil { + logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err) } } @@ -445,9 +444,9 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd logger.Errorf("处理主单加仓失败, 主单号:%s, 错误信息: %v", preOrder.MainId, err) return } - } else { + } else if first { // 处理其他主单逻辑 - if err := cancelPositionOtherOrders(apiInfo, db, preOrder); err != nil { + if err := cancelPositionOtherOrders(apiInfo, db, preOrder, true); err != nil { logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } @@ -497,7 +496,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd positionData := savePosition(db, preOrder) orderExt := models.LinePreOrderExt{} db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) - num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) + totalNum := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) // 更新订单数量并处理止盈、止损、减仓 for _, order := range orders { @@ -505,7 +504,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd order.Price = price.String() // 更新止盈止损订单数量 - num = updateOrderQuantity(db, order, preOrder, &orderExt, num, first, tradeSet) + num := updateOrderQuantity(db, order, preOrder, &orderExt, totalNum, first, tradeSet) // 根据订单类型处理 switch order.OrderType { @@ -552,7 +551,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd } } - nextFuturesReduceTrigger(db, mainId, num, tradeSet) + nextFuturesReduceTrigger(db, mainId, totalNum, tradeSet) } // 处理主单加仓 @@ -569,7 +568,8 @@ func handleMainOrderAddPosition(db *gorm.DB, preOrder *models.LinePreOrder) erro } // 取消主单相关订单 -func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder) error { +// changeMainOrderStatus 是否修改主单状态 +func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder, changeMainOrderStatus bool) error { mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) if err != nil { return err @@ -589,25 +589,36 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr } } + // 批量取消订单 + 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 { if len(mainIds) > 0 { - orderSns, err := getOpenOrderSns(db, mainIds) + orderSns, err := GetOpenOrderSns(db, mainIds) if err != nil { return err } - // 批量取消订单 orderArray := utility.SplitSlice(orderSns, 10) futApi := FutRestApi{} for _, item := range orderArray { - err := futApi.CancelBatchFutOrder(apiUserInfo, preOrder.Symbol, item) + err := futApi.CancelBatchFutOrder(apiUserInfo, symbol, item) if err != nil { logger.Errorf("批量取消订单失败 orderSns:%v", item) } } - if err := db.Exec("UPDATE line_pre_order SET `status`=4, `desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { - logger.Errorf("合约 新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) + //需要修改主单状态 + if changeMainOrderStatus { + if err := db.Exec("UPDATE line_pre_order SET `status`=4, `desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { + logger.Errorf("合约 新下单成功后更新主单取消状态失败, 交易对:%s, 错误信息:%v", symbol, err) + } } } return nil @@ -633,7 +644,7 @@ func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *model // order.Num = num.String() // } else - if first && order.OrderCategory == 1 && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + if first && (order.OrderCategory == 1 || order.OrderCategory == 3) && order.OrderType == 1 && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { // 计算止盈数量 num = num.Mul(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) order.Num = num.String() diff --git a/services/binanceservice/spotjudgeservice.go b/services/binanceservice/spotjudgeservice.go index 047e5cd..ff85199 100644 --- a/services/binanceservice/spotjudgeservice.go +++ b/services/binanceservice/spotjudgeservice.go @@ -308,8 +308,8 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest return } else if ok { defer lock.Release() - takeOrder := DbModels.LinePreOrder{} - if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type =1", reduceOrder.Pid).Find(&takeOrder).Error; err != nil { + takeOrders := make([]DbModels.LinePreOrder, 0) + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil { log.Error("查询止盈单失败") return } @@ -320,18 +320,20 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest return } - apiInfo, _ := GetApiInfo(takeOrder.ApiId) + apiInfo, _ := GetApiInfo(reduceOrder.ApiId) if apiInfo.Id == 0 { log.Error("现货减仓 查询api用户不存在") return } - err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) + for _, takeOrder := range takeOrders { + err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - if err != nil { - log.Error("现货止盈撤单失败", err) - return + if err != nil { + log.Error("现货止盈撤单失败", err) + return + } } price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 6718086..ddcbfad 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -195,7 +195,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { db.Model(&orderExt).Where("order_id =?", preOrder.Pid).Find(&orderExt) totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet) - totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) + totalNum = totalNum.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) price := utility.StrToDecimal(preOrder.Price) if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status=0", preOrder.Id).Find(&orders).Error; err != nil { @@ -244,7 +244,7 @@ func nextSpotReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tr return true } - if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + if err := db.Model(&models.LinePreOrderExt{}).Where("order_id =? and add_type =2", nextOrder.Id).First(&nextExt).Error; err != nil { logger.Errorf("获取下一个单失败 err:%v", err) } @@ -265,12 +265,12 @@ func nextSpotReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tr //减仓配置 且减仓比例大于0小于100 if nextExt.Id > 0 && nextExt.AddType == 2 && nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { num := totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) - // percentag = positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)).Truncate(2) nextOrder.Num = num.String() + + if err := db.Model(&nextOrder).Update("num", nextOrder.Num).Error; err != nil { + logger.Errorf("更新减仓单数量失败 err:%v", err) + } } - // percentag = nextExt.PriceRatio.Add(percentag) - // nextOrder.Rate = percentag.String() - // nextOrder.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)).String() //减仓待触发 processSpotReduceOrder(nextOrder) @@ -638,7 +638,7 @@ func parseOrderStatus(status interface{}, mapData map[string]interface{}) (int, return 5, reason case "FILLED": // 完全成交 return 6, reason - case "CANCELED", "EXPIRED": // 取消 + case "CANCELED", "EXPIRED", "EXPIRED_IN_MATCH": // 取消 return 4, reason default: return 0, reason @@ -729,13 +729,17 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd spotApi := SpotRestApi{} orderExt := models.LinePreOrderExt{} db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) - num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) + totalNum := num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) + num = totalNum + // if orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + // num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + // } //止盈止损 for _, order := range orders { order.Num = num.String() - if fist && order.OrderCategory == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + if fist && order.OrderCategory == 1 && order.OrderType == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { //主单第一次且止盈数量不是100% 止盈数量 order.Num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() } @@ -777,7 +781,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd } //待触发减仓单 - nextSpotReduceTrigger(db, mainId, num, tradeSet) + nextSpotReduceTrigger(db, mainId, totalNum, tradeSet) } // 根据下单百分比计算价格 @@ -947,6 +951,8 @@ func CancelOpenOrderByOrderSnLoop(apiInfo DbModels.LineApiUser, symbol string, o err = nil break } + + time.Sleep(time.Millisecond * 300) } return err } From ddba8e4ce21dda51b782e9fdbd2368777423aaf0 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Mon, 10 Mar 2025 18:02:54 +0800 Subject: [PATCH 12/13] 1 --- services/binanceservice/futuresrest.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 8d4b76b..0d2ce5b 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -355,8 +355,8 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - mainOrder, _ := GetOrderById(db, preOrder.MainId) - if err := cancelPositionOtherOrders(apiUserInfo, db, &mainOrder, false); err != nil { + mainIds := []int{preOrder.MainId} + if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil { logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err) } } @@ -573,6 +573,8 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) if err != nil { return err + } else if len(mainOrders) == 0 { + logger.Infof("主单没有持仓,不需要取消其他订单 sn:%s", preOrder.OrderSn) } mainIds := []int{} @@ -590,6 +592,7 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr } // 批量取消订单 + err = cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, changeMainOrderStatus) return err From ffb12cdb64cff1291db7fdc5389d8476aa2081c3 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 11 Mar 2025 15:51:40 +0800 Subject: [PATCH 13/13] 1 --- app/admin/service/dto/line_pre_order.go | 11 ++-- app/admin/service/line_pre_order.go | 73 ++++++++++++++++--------- pkg/utility/decimalhelper.go | 13 +++++ services/binanceservice/futuresrest.go | 6 +- services/scriptservice/order.go | 15 +++-- 5 files changed, 80 insertions(+), 38 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 4166c36..fbcbf25 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -437,13 +437,15 @@ func (req LineBatchAddPreOrderReq) CheckParams() error { return errors.New("止盈价格不正确") } - if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + (v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { return errors.New("止盈后止盈价格不正确") } - if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("止盈后止损价格不正确") - } + // if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + // return errors.New("止盈后止损价格不正确") + // } } return nil @@ -582,6 +584,7 @@ type ClosePosition struct { Rate string `json:"rate"` //限价平仓百分比 CloseType int `json:"close_type"` //现货平仓=1 合约平仓=2 ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type + Side string `json:"side"` //SELL=卖出(空) BUY=买入(多) } func (m *ClosePosition) CheckParams() error { diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 96ebcc9..c771b50 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -443,8 +443,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.PricePattern == "percentage" { AddOrder.Rate = req.Price - orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% - priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 + orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% + priceRate := utility.SafeDiv(orderPrice, (decimal.NewFromInt(100))) //下单价除100 =0.1 if strings.ToUpper(req.Site) == "BUY" { //购买方向 //实际下单价格 @@ -486,12 +486,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 //在换算成对应交易对对应的价值 //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) + div := utility.SafeDiv(utility.SafeDiv(tickerPrice, decimal.NewFromInt(1)), uTickerPrice) //计算下单数量 - AddOrder.Num = buyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() + AddOrder.Num = utility.SafeDiv(buyPrice, div).Truncate(int32(tradeSet.AmountDigit)).String() } else { fromString, _ := decimal.NewFromString(AddOrder.Price) - AddOrder.Num = buyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() + AddOrder.Num = utility.SafeDiv(buyPrice, fromString).Truncate(int32(tradeSet.AmountDigit)).String() } if utility.StringToFloat64(AddOrder.Num) < tradeSet.MinQty { *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 小于最小下单数量", id, req.Symbol)) @@ -534,11 +534,14 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP StopLossRatio: req.ReduceStopLossRatio, } mainPrice := utility.StringToDecimal(AddOrder.Price) - mainAmount := buyPrice.Div(mainPrice) + mainAmount := utility.SafeDiv(buyPrice, mainPrice) defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit)) defultExt2.TotalBefore = defultExt.TotalAfter - defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) - defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2) + + // if decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Cmp(decimal.Zero) > 0 { + defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(utility.SafeDiv(req.ReduceNumRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)) + defultExt2.ReTakeRatio = utility.SafeDiv(req.ReducePriceRatio, utility.SafeDiv(decimal.NewFromInt(100).Sub(req.ReduceNumRatio), decimal.NewFromInt(100))).Truncate(2) + preOrderExts = append(preOrderExts, defultExt) preOrderExts = append(preOrderExts, defultExt2) @@ -644,9 +647,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP profitOrder.MainId = AddOrder.Id if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 { - numPercent := req.ProfitNumRatio.Div(decimal.NewFromInt(100)) + numPercent := utility.SafeDiv(req.ProfitNumRatio, decimal.NewFromInt(100)) profitOrder.Num = utility.StrToDecimal(profitOrder.Num).Mul(numPercent).Truncate(int32(tradeSet.AmountDigit)).String() - } tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder) @@ -668,10 +670,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 { if strings.ToUpper(req.Site) == "BUY" { stopOrder.Site = "SELL" - stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + 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.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() } stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) stopOrder.Pid = AddOrder.Id @@ -807,12 +809,12 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.Price = dataPrice.String() if v.AddPositionType == 1 { - buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(v.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(2) - data.Num = buyPrice.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(utility.SafeDiv(v.AddPositionVal, decimal.NewFromInt(100))).Truncate(2) + data.Num = utility.SafeDiv(buyPrice, dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() data.BuyPrice = buyPrice.String() } else { data.BuyPrice = v.AddPositionVal.Truncate(2).String() - data.Num = v.AddPositionVal.Truncate(2).Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + data.Num = utility.SafeDiv(v.AddPositionVal.Truncate(2), dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() } return data @@ -886,7 +888,7 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO if ext.TakeProfitNumRatio.Cmp(decimal.Zero) <= 0 || ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) >= 0 { profitOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String() } else { - profitOrder.Num = ext.TotalAfter.Mul(ext.TakeProfitNumRatio).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)).String() + profitOrder.Num = ext.TotalAfter.Mul(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() } // 止盈需要累加之前的亏损 @@ -969,7 +971,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line } if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))) + percent := decimal.NewFromInt(1).Sub(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))) num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) } @@ -1473,7 +1475,7 @@ func (e *LinePreOrder) GetFutSpotOrderInfo(req dto.ManuallyCover, symbol, orderT func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPrice decimal.Decimal, amountDigit int, notUsdt bool, symbolInfo models.LineSymbol) (decimal.Decimal, error) { var amt decimal.Decimal if req.CoverType == 1 { - decimalValue := utility.StringToDecimal(req.Value).Div(decimal.NewFromInt(100)) + decimalValue := utility.SafeDiv(utility.StringToDecimal(req.Value), decimal.NewFromInt(100)) amt = totalNum.Mul(decimalValue) } else { decimalValue := utility.StringToDecimal(req.Value) @@ -1495,8 +1497,8 @@ func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPric for _, symbolMap := range tickerSymbolMaps { if symbolMap.Symbol == strings.ToUpper(symbolInfo.QuoteAsset+"USDT") { uTickerPrice, _ := decimal.NewFromString(symbolMap.Price) - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) - amt = decimalValue.Div(div) + div := utility.SafeDiv(tickerPrice, decimal.NewFromInt(1).Div(uTickerPrice)) + amt = utility.SafeDiv(decimalValue, div) break } } @@ -1571,7 +1573,7 @@ func (e *LinePreOrder) SpotClosePosition(position *dto.ClosePosition, errs *[]er "newClientOrderId": utility.Int64ToString(snowflakehelper.GetOrderId()), "timeInForce": "GTC", } - price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) paramsMaps["price"] = price.String() } else { *errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 下单数量小于最小下单数量", position.ApiId))) @@ -1630,9 +1632,27 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err //查询已经开仓的合约交易对 var futList []models.LinePreOrder if position.Symbol == "" { - e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId).Find(&futList) + query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId) + + switch strings.ToUpper(position.Side) { + case "BUY": + query = query.Where("site = 'BUY'") + case "SELL": + query = query.Where("site = 'SELL'") + } + + query.Find(&futList) } else { - e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol).Find(&futList) + query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol) + + switch strings.ToUpper(position.Side) { + case "BUY": + query = query.Where("side = 'BUY'") + case "SELL": + query = query.Where("side = 'SELL'") + } + + query.Find(&futList) } if len(futList) <= 0 { *errs = append(*errs, fmt.Errorf("api_id:%d 没有可平仓的交易对", position.ApiId)) @@ -1677,10 +1697,10 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err if list.Site == "BUY" && risk.PositionSide == "LONG" { //做多 //根据仓位数量去平多 orderSide = "SELL" - price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) } else if list.Site == "SELL" && risk.PositionSide == "SHORT" { orderSide = "BUY" - price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) } if price.LessThanOrEqual(decimal.Zero) { @@ -1726,6 +1746,9 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err //撤销合约的委托(根据方向撤) orderSns, _ := binanceservice.GetOpenOrderSns(e.Orm, []int{list.Id}) + if len(orderSns) > 0 { + logger.Infof("平仓 取消 订单id :%v 订单号: %v", orderSns) + } api.CancelBatchFutOrder(apiUserInfo, list.Symbol, orderSns) // api.CancelAllFutOrder(apiUserInfo, list.Symbol) //side=BUY&positionSide=LONG是开多, diff --git a/pkg/utility/decimalhelper.go b/pkg/utility/decimalhelper.go index 93f75d8..0fb9831 100644 --- a/pkg/utility/decimalhelper.go +++ b/pkg/utility/decimalhelper.go @@ -87,3 +87,16 @@ func DiscardDecimal(value decimal.Decimal, discardDigits int32) decimal.Decimal return value } + +// SafeDiv 安全除法 +// dividend: 被除数 +// divisor: 除数 +func SafeDiv(dividend, divisor decimal.Decimal) decimal.Decimal { + var result decimal.Decimal + + if dividend.Cmp(decimal.Zero) != 0 && divisor.Cmp(decimal.Zero) != 0 { + result = dividend.Div(divisor) + } + + return result +} diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 0d2ce5b..6855131 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -303,12 +303,12 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) removePosition(db, preOrder) - futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + mainIds := []int{preOrder.MainId} + if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil { + logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err) } } diff --git a/services/scriptservice/order.go b/services/scriptservice/order.go index 0b8edd5..4a78831 100644 --- a/services/scriptservice/order.go +++ b/services/scriptservice/order.go @@ -1,17 +1,19 @@ package scriptservice import ( - "github.com/bytedance/sonic" - log "github.com/go-admin-team/go-admin-core/logger" - sysservice "github.com/go-admin-team/go-admin-core/sdk/service" "go-admin/app/admin/models" "go-admin/app/admin/service" "go-admin/app/admin/service/dto" "go-admin/common/const/rediskey" "go-admin/common/helper" - "gorm.io/gorm" + "go-admin/pkg/utility" "strings" "sync" + + "github.com/bytedance/sonic" + log "github.com/go-admin-team/go-admin-core/logger" + sysservice "github.com/go-admin-team/go-admin-core/sdk/service" + "gorm.io/gorm" ) type PreOrder struct { @@ -23,8 +25,9 @@ func (receiver *PreOrder) AddOrder(orm *gorm.DB) { var wg sync.WaitGroup for i := 1; i <= GoroutineNum; i++ { wg.Add(1) - go workerWithLock(orm, &wg) - + utility.SafeGo(func() { + workerWithLock(orm, &wg) + }) } wg.Wait() }