You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1495 lines
53 KiB

package data
import (
"context"
"encoding/json"
"github.com/go-xorm/xorm"
"github.com/shopspring/decimal"
"matchmaking-system/internal/biz/structure"
"matchmaking-system/internal/data/socket/publicData"
"matchmaking-system/internal/data/tradedeal/virtual"
"matchmaking-system/internal/pkg/flags"
"matchmaking-system/internal/pkg/logging/applogger"
"matchmaking-system/internal/pkg/logging/common"
"matchmaking-system/internal/pkg/setting"
"matchmaking-system/internal/pkg/utils"
"strconv"
"strings"
orders "matchmaking-system/internal/data/convert"
models "matchmaking-system/internal/pkg/model"
)
// GetBotContractTradeList
//
// @Description: 合约-秒合约订单列表查询
// @receiver uo
// @param ctx
// @param pageSize
// @param pageCount
// @param userId
// @param status
// @return []*models.BotContractTrade
// @return int64
// @return error
func (uo *userOrderRepo) GetBotContractTradeList(ctx context.Context, pageSize, pageCount, userId, status int64) ([]*models.BotContractTrade, int64, error) {
totalCount, err := uo.data.mysqlDB.Table(flags.BotContractTrade).
Where("user_id = ?", userId).
Where("status = ?", status).
Count()
if err != nil {
return nil, 0, flags.ErrMySqlDB
}
if totalCount == 0 {
return nil, 0, nil
}
var contractList []*models.BotContractTrade
if err = uo.data.mysqlDB.Table(flags.BotContractTrade).
Where("user_id = ?", userId).
Where("status = ?", status).
Limit(int(pageSize), int(pageCount)).
Desc(GetOrderByStatusSort(status)).
Find(&contractList); err != nil {
return nil, 0, flags.ErrMySqlDB
}
var contractUpdateList []*models.BotContractTrade
for _, value := range contractList {
value.KeepDecimal = GetKeepDecimal(flags.ContractSystemSetUpKey, value.ContractId)
contractUpdateList = append(contractUpdateList, value)
}
return contractUpdateList, totalCount, nil
}
// CreateBotContractTrade
//
// @Description: 合约下单
// @receiver uo
// @param ctx
// @param userId
// @param order
// @return string
// @return error
func (uo *userOrderRepo) CreateBotContractTrade(ctx context.Context, userId int64, order structure.ContractOrder) (string, error) {
// 1、合约下单订阅
contractId := strings.ToUpper(order.ContractId)
_, ok := contract.ContractMapSymbol.Load(contractId)
if ok {
go func() {
contract.ContractMap <- []byte(contractId)
}()
}
// 2、获取系统设置
system, err := GetContractSystemSetUp(contractId)
if err != nil || system == nil {
return flags.SetNull, flags.ErrContractOne
}
order.System = system
// 3、获取交易币市价
//priceNew, err := GetDigitalCurrencyPrice(ctx, flags.Hy, contractId)
//if len(priceNew) == 0 || err != nil {
// applogger.Error("%v CreateBotContractTrade.ContractSubMarketPrice:%v", common.ErrContract, err)
// return flags.SetNull, flags.ErrPriceUpdate
//}
// 4、合约下单判定
// 杠杆校验
pryNum := decimal.RequireFromString(order.PryNum)
if pryNum.Cmp(order.System.MinPry) < 0 || pryNum.Cmp(order.System.MaxPry) > 0 {
return flags.SetNull, flags.ErrContractTow
}
// 下单判定设置(false无设置|true止盈止损)
checkBool, stopWinPrice, stopLossPrice, err := ContractVoteStopType(order)
if err != nil {
applogger.Error("%v CreateBotContractTrade.ContractVoteStopType:%v", common.ErrContract, err)
return flags.SetNull, err
}
// 下单判定设置(限价|市价)
limitOrMarketPrice, err := uo.ContractVoteDealType(order)
if err != nil {
applogger.Error("%v CreateBotContractTrade.ContractVoteDealType:%v", common.ErrContract, err)
return flags.SetNull, err
}
// 下单判定设置(买涨|买跌)
if err = uo.VoteTradeType(order.TradeType, stopWinPrice, stopLossPrice, limitOrMarketPrice, checkBool); err != nil {
applogger.Error("%v CreateBotContractTrade.VoteTradeType:%v", common.ErrContract, err)
return flags.SetNull, err
}
// 5、合约下单(订单信息|资产信息)
orderId, err := uo.ContractOrdersWriteDB(ctx, userId, order)
if err != nil || len(orderId) == 0 {
applogger.Error("%v CreateBotContractTrade.ContractOrdersWriteDB:%v", common.ErrContract, err)
return flags.SetNull, err
}
// 6、写入缓存列表(挂单缓存|持仓缓存),等待撮合计算
marketStatus, tallyCache, err := virtual.ContractCacheDeal(ctx, userId, orderId, limitOrMarketPrice.String(), order)
if err != nil || tallyCache == nil {
applogger.Error("%v CreateBotContractTrade.ContractCacheDeal:%v", common.ErrContract, err)
return flags.SetNull, err
}
// 7、市价下单开仓处理(订单信息|资产信息|手续费|返佣|资产详情记录)
if marketStatus == setting.MarketContractPosition {
if err = ContractOpenPosition(ctx, uo.data.mysqlDB, userId, orderId, tallyCache.OpenPrice, order); err != nil {
applogger.Error("%v CreateBotContractTrade.ContractOpenPosition:%v", common.ErrContract, err)
return flags.SetNull, err
}
}
// 8、写入(挂单|持仓)缓存列表
if err = virtual.ContractPushAddCache(Reds, marketStatus, tallyCache); err != nil {
applogger.Error("%v CreateBotContractTrade.ContractPushAddCache:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrCacheDB
}
// 9、写入用户订单订阅缓存列表
orderIdKey := virtual.OrderIdListKey(setting.ContractSubscribe, userId)
if err = virtual.ContractHashSetOrderId(Reds, orderIdKey, tallyCache); err != nil {
applogger.Error("%v CreateBotContractTrade.ContractPushAddOrder:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrCacheDB
}
// 10、写入管理员订单订阅缓存列表
if err = virtual.ContractHashSetOrderId(Reds, setting.AdminContractSubscribe, tallyCache); err != nil {
applogger.Error("%v CreateBotContractTrade.ContractHashSetOrderId:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrCacheDB
}
return orderId, nil
}
// ContractOrdersWriteDB
//
// @Description: 合约下单处理
// @receiver uo
// @param ctx
// @param userId
// @param order
// @return string
// @return error
func (uo *userOrderRepo) ContractOrdersWriteDB(ctx context.Context, userId int64, order structure.ContractOrder) (string, error) {
session := uo.data.mysqlDB.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v ContractOrdersWriteDB.NewSession:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
// 查询当前下单用户可用账户金额和冻结金额
var usable, frozen, usableNo, frozenNo decimal.Decimal
usable, frozen, _, err = uo.GetBotUserContract(session, userId, flags.BasicUnit)
if err != nil {
applogger.Error("%v ContractOrdersWriteDB.GetBotUserContract:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
// 检查用户订单下单资产
if usable.IsZero() || usable.IsNegative() || frozen.IsNegative() {
return flags.SetNull, flags.ErrPublicOne
}
// 查询当前下单用户可用非账户和冻结金额
var checkCount int
if order.ContractId != flags.BasicUnit {
usableNo, frozenNo, checkCount, err = uo.GetBotUserContract(session, userId, order.ContractId)
if err != nil {
applogger.Error("%v ContractOrdersWriteDB.GetBotUserContract.Fei:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
}
if !flags.CheckSetting {
applogger.Debug("下单可用金额:%v", usable)
applogger.Debug("下单冻结金额:%v", frozen)
applogger.Debug("非下单可用金额:%v", usableNo)
applogger.Debug("非下单冻结金额:%v", frozenNo)
}
// 写入订单信息
orderId, err := uo.VerifyBotContractTradeOrderId(session)
if err != nil {
applogger.Error("%v ContractOrdersWriteDB.VerifyBotContractTradeOrderId:%v", common.ErrContract, err)
return flags.SetNull, err
}
if !flags.CheckSetting {
applogger.Debug("下单Id:%v", orderId)
applogger.Debug("杠杆:%v", order.PryNum)
}
// 写入订单表
botStockTrade := orders.BotContractTrade(ctx, userId, orderId, order)
if err = uo.CreatBotContractTrade(session, botStockTrade); err != nil {
applogger.Error("%v ContractOrdersWriteDB.CreatBotContractTrade:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
// 订单总金额 = 保证金 + 手续费
earnestMoney := decimal.RequireFromString(order.EarnestMoney) // 保证金
serviceCost := decimal.RequireFromString(order.ServiceCost) // 手续费
totalMoney := earnestMoney.Add(serviceCost)
// 总金额下单判定
residue := usable.Sub(totalMoney).Div(usable)
if usable.Cmp(totalMoney) < 0 || residue.Cmp(utils.DecimalsStrInt()) < 0 {
return flags.SetNull, flags.ErrPublicOne
}
// (买涨|买跌)处理
var usableNew, frozenNew, usableNewNo, frozenNewNo decimal.Decimal
usableNew = usable.Sub(totalMoney) // 可用资产
frozenNew = frozen.Add(totalMoney) // 冻结资产
if order.ContractId != flags.BasicUnit {
usableNewNo = usableNo // 可用非资产
frozenNewNo = frozenNo // 冻结非资产
}
// 检查用户订单下单资产
if usableNew.IsNegative() || usableNew.IsZero() {
return flags.SetNull, flags.ErrPublicTow
}
if !flags.CheckSetting {
applogger.Debug("下单后用户可用资产:%v", usableNew)
applogger.Debug("下单后用户冻结资产:%v", frozenNew)
applogger.Debug("下单后用户非可用资产:%v", usableNewNo)
applogger.Debug("下单后用户非冻结资产:%v", frozenNewNo)
}
// 更新用户资产信息
userContractUSDT := orders.UpdateBotUserContract(ctx, usableNew.String(), frozenNew.String())
if err = uo.UpdateBotUserContract(session, userId, flags.BasicUnit, userContractUSDT); err != nil {
applogger.Error("%v ContractOrdersWriteDB.UpdateBotUserContract:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
// 更新非资产信息
if order.ContractId != flags.BasicUnit {
if checkCount == 0 {
userDigitalSymbol := orders.CreatBotUserContract(ctx, userId, usableNewNo.String(), frozenNewNo.String(), order)
err = uo.CreatBotUserContract(session, userDigitalSymbol)
if err != nil {
applogger.Error("%v ContractOrdersWriteDB.CreatBotUserContract:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
} else {
userContract := orders.UpdateBotUserContract(ctx, usableNewNo.String(), frozenNewNo.String())
if err = uo.UpdateBotUserContract(session, userId, order.ContractId, userContract); err != nil {
applogger.Error("%v ContractOrdersWriteDB.UpdateBotUserContract:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
}
}
if err = session.Commit(); err != nil {
applogger.Error("%v ContractOrdersWriteDB.Commit:%v", common.ErrContract, err)
return flags.SetNull, flags.ErrMySqlDB
}
return orderId, nil
}
// ContractOpenPosition
//
// @Description: 合约开仓处理
// @param ctx
// @param db
// @param userId
// @param orderId
// @param openPrice
// @param order
// @return error
func ContractOpenPosition(ctx context.Context, db *xorm.EngineGroup, userId int64, orderId, openPrice string, order structure.ContractOrder) error {
session := db.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v ContractOpenPosition.NewSession:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 查询当前下单用户可用账户金额和冻结金额
var usable, frozen, usableNo, frozenNo decimal.Decimal
usable, frozen, _, err = Uo.GetBotUserContract(session, userId, flags.BasicUnit)
if err != nil {
applogger.Error("%v ContractOpenPosition.GetBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
if usable.IsNegative() || frozen.IsNegative() {
return flags.ErrPublicThree
}
if !flags.CheckSetting {
applogger.Debug("下单可用金额:%v", usable)
applogger.Debug("下单冻结金额:%v", frozen)
}
if order.ContractId != flags.BasicUnit {
usableNo, frozenNo, _, err = Uo.GetBotUserContract(session, userId, order.ContractId)
if err != nil {
applogger.Error("%v ContractOpenPosition.GetBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
}
// 转换下单信息
earnestMoney := decimal.RequireFromString(order.EarnestMoney) // 下单保证金
orderNumber := decimal.RequireFromString(order.OrderNumber) // 下单仓位
serviceCost := decimal.RequireFromString(order.ServiceCost) // 下单手续费
totalMoney := earnestMoney.Add(serviceCost) // 下单总金额 = (订单手续费 + 保证金)
openOrderPrice := decimal.RequireFromString(openPrice) // 开仓价格
openOrderNumber := orderNumber // 开仓数量
if !flags.CheckSetting {
applogger.Debug("下单保证金:%v", earnestMoney)
applogger.Debug("下单订单数量:%v", orderNumber)
applogger.Debug("下单订单手续费:%v", serviceCost)
applogger.Debug("下单订单总金额:%v", totalMoney)
applogger.Debug("下单开仓量:%v", openOrderNumber)
applogger.Debug("下单开仓价:%v", openOrderPrice)
}
//openFaceValue := order.System.FaceValue // 面值
//openOrderPrice := decimal.RequireFromString(openPrice) // 开仓价格
//pryNum := decimal.RequireFromString(order.PryNum) // 杠杆
//openOrderNumber := orderNumber // 开仓数量
//openOrderNumber := earnestMoney.Mul(pryNum).Div(openOrderPrice).Div(openFaceValue) // 开仓数量 = (保证金 * 杠杆) / 开仓价格 / 面值
// TODO: 手续费记录
//openPriceNew := openOrderPrice.Mul(openOrderNumber).String() // 开仓手续费 = 开仓价格 * 仓位数 * 手续费比例
//cost, err := Uo.CalculateHandlingFees(ctx, session, int(userId), flags.ContractMarketType, flags.OpenBrokType, orderId, openPriceNew, openFaceValue.String())
//if err != nil {
// applogger.Error("%v ContractOpenPosition.CalculateHandlingFees:%v", common.ErrContract, err)
// return err
//}
//openServiceCost := decimal.RequireFromString(cost) // 开仓手续费
openServiceCost := serviceCost //开仓手续费
openTotalMoney := earnestMoney.Add(openServiceCost) // 开仓总金额 = (保证金 + 开仓手续费)
floatPrice := totalMoney.Sub(openTotalMoney) // 计算容差 = 下单总额 - 开仓总额
if !flags.CheckSetting {
applogger.Debug("开仓订单价格:%v", openOrderPrice)
applogger.Debug("开仓仓位:%v", openOrderNumber)
applogger.Debug("开仓手续费:%v", openServiceCost)
applogger.Debug("开仓总金额:%v", openTotalMoney)
applogger.Debug("下单开仓总金额容差值:%v", floatPrice)
}
usableNew := usable.Add(floatPrice) // 可用资产(处理容差值)
frozenNew := frozen.Sub(totalMoney).Add(earnestMoney) // 冻结资产 = 冻结资产 - 下单冻结资产 + 开仓保证金
usableNoNew := usableNo.Add(openOrderNumber) // 非可用资产
frozenNoNew := frozenNo // 非冻结资产
if !flags.CheckSetting {
applogger.Debug("下单后用户可用资产:%v", usableNew)
applogger.Debug("下单后用户冻结资产:%v", frozenNew)
}
applogger.Debug("下单冻结资产:%v", frozen)
applogger.Debug("下单后的冻结总资产:%v", totalMoney)
applogger.Debug("开仓后的保证金:%v", earnestMoney)
applogger.Debug("下单后用户冻结资产:%v", frozenNew)
applogger.Debug("下单后用户可用资产:%v", usableNew)
// 检查用户平仓资产
if usableNoNew.IsNegative() {
return flags.ErrPublicThree
}
// 处理资产信息
userContractUSDT := orders.UpdateBotUserContract(ctx, usableNew.String(), frozenNew.String())
if err = Uo.UpdateBotUserContract(session, userId, flags.BasicUnit, userContractUSDT); err != nil {
applogger.Error("%v ContractOpenPosition.UpdateBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 更新非资产信息
if order.ContractId != flags.BasicUnit {
userContract := orders.UpdateBotUserContract(ctx, usableNoNew.String(), frozenNoNew.String())
if err = Uo.UpdateBotUserContract(session, userId, order.ContractId, userContract); err != nil {
applogger.Error("%v ContractOpenPosition.UpdateBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
}
// 处理订单信息(成交价|仓位|开仓时间|订单总金额|手续费|持仓状态)
trade := orders.UpdateOpenBotContractTrade(ctx, openPrice, openOrderNumber.String(), openTotalMoney.String(), openServiceCost.String())
if err = Uo.UpdateBotContractTradeByOrderId(session, orderId, trade); err != nil {
applogger.Error("%v ContractOpenPosition.UpdateBotContractTradeByOrderId:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 返佣记录|资金信息|资金详情信息
if err = Uo.ContractRebateCalculation(ctx, session, int(userId), flags.ContractMarketType, flags.OpenBrokType, flags.OpenMRebate, openServiceCost.String(), orderId); err != nil {
applogger.Error("%v ContractOpenPosition.ContractRebateCalculation:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 更改交易日志记录方式
var list []models.BotUserContractLog
qData0 := orders.CreatBotUserContractLog(ctx, userId, flags.CostMoney, flags.BasicUnit, NegativeValue(openServiceCost.String()), orderId) // 资金变动明细表(开仓手续费)
qData1 := orders.CreatBotUserContractLog(ctx, userId, flags.Freeze, flags.BasicUnit, NegativeValue(earnestMoney.String()), orderId) // 资金变动明细表(开仓保证金)
qData2 := orders.CreatBotUserContractLog(ctx, userId, flags.ChangeInto, order.ContractId, orderNumber.String(), orderId) // 资金变动明细表(非资产)
list = append(list, qData0, qData1, qData2)
// 批量写入数据信息
if err = Uo.CreatBotUserContractLogList(session, list); err != nil {
applogger.Error("%v ContractOpenPosition.CreatBotUserContractLogList:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v ContractOpenPosition.Commit:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
return nil
}
// ContractClosingPosition
//
// @Description: 合约平仓处理
// @param ctx
// @param db
// @param orderId
// @param price
// @param order
// @return error
func ContractClosingPosition(ctx context.Context, db *xorm.EngineGroup, orderId string, price string, order structure.ContractOrder) error {
session := db.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v ContractClosingPosition.NewSession:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 查询订单信息
tread, err := Uo.GetBotContractTradeByOrderId(session, orderId, flags.PositionStatus)
if err != nil {
applogger.Error("%v ContractClosingPosition.GetBotContractTradeByOrderId:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
var userId int64
var dealPrice, earnestMoney, orderNumber, totalAmount, serviceCost, pryNum decimal.Decimal
if tread != nil {
userId = int64(tread.UserId) // 用户Id
earnestMoney = decimal.RequireFromString(tread.EarnestMoney) // 开仓保证金
orderNumber = decimal.RequireFromString(tread.OrderNumber) // 下单数量
dealPrice = decimal.RequireFromString(tread.DealPrice) // 开仓价格
totalAmount = decimal.RequireFromString(tread.TotalMoney) // 开仓总金额
serviceCost = decimal.RequireFromString(tread.ServiceCost) // 开仓手续费
pryNum = decimal.RequireFromString(strconv.Itoa(tread.PryNum)) // 杠杆
}
if !flags.CheckSetting {
applogger.Debug("下单开仓用户Id:%v", userId)
applogger.Debug("下单开仓开仓价格:%v", dealPrice)
applogger.Debug("下单开仓保证金:%v", earnestMoney)
applogger.Debug("下单开仓订单数量:%v", orderNumber)
applogger.Debug("下单开仓总金额:%v", totalAmount)
applogger.Debug("下单开仓手续费:%v", serviceCost)
applogger.Debug("下单杠杆:%v", pryNum)
}
// 查询当前下单用户可用账户金额和冻结金额
var usable, frozen, usableNo, frozenNo decimal.Decimal
usable, frozen, _, err = Uo.GetBotUserContract(session, userId, flags.BasicUnit)
if err != nil {
applogger.Error("%v ContractClosingPosition.GetBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 检查用户订单平仓资产
if usable.IsNegative() || frozen.IsNegative() {
return flags.ErrContractFive
}
if !flags.CheckSetting {
applogger.Debug("下单可用金额:%v", usable)
applogger.Debug("下单冻结金额:%v", frozen)
}
// 查询当前下单用户可用非账户和冻结金额
if order.ContractId != flags.BasicUnit {
usableNo, frozenNo, _, err = Uo.GetBotUserContract(session, userId, order.ContractId)
if err != nil {
applogger.Error("%vContractClosingPosition.GetBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
}
if !flags.CheckSetting {
applogger.Debug("非下单可用金额:%v", usableNo)
applogger.Debug("非下单冻结金额:%v", frozenNo)
}
closeFaceValue := order.System.FaceValue // 面值
// 手续费处理
closePrice := decimal.RequireFromString(price) // 平仓价格
cloePriceCost := closePrice.Mul(orderNumber).String() // 平仓手续费 = 平仓价格 * 仓位数 * 手续费比例 * 面值
cost, err := Uo.CalculateHandlingFees(ctx, session, int(userId), flags.ContractMarketType, flags.ClosingBrokType, orderId, cloePriceCost, closeFaceValue.String())
if err != nil {
applogger.Error("%v ContractClosingPosition.CalculateHandlingFees:%v", common.ErrContract, err)
return err
}
closeServiceCost := decimal.RequireFromString(cost)
if !flags.CheckSetting {
applogger.Debug("平仓价格:%v", closePrice)
applogger.Debug("平仓手续费手续费:%v", closeServiceCost)
}
//已经实现盈亏计算公式(已平仓结算方式)
//买涨 已经实现盈亏 = (平仓成交价-开仓成交价)*仓位数*面值*杠杆
//买跌 已经实现盈亏 = (开仓成交价-平仓成交价)*仓位数*面值*杠杆
var usableNew, frozenNew, usableNewNo, frozenNewNo, subPrice, resultPrice decimal.Decimal
switch order.TradeType {
case flags.TradeTypeBuy: // 买涨
subPrice = closePrice.Sub(dealPrice)
resultPrice = subPrice.Mul(orderNumber).Mul(closeFaceValue).Mul(pryNum)
case flags.TradeTypeSell: // 买跌
subPrice = dealPrice.Sub(closePrice)
resultPrice = subPrice.Mul(orderNumber).Mul(closeFaceValue).Mul(pryNum)
default:
}
// TODO: 判定亏损不能超过 = 保证金 + 平仓手续费
if resultPrice.IsNegative() {
amountLoss := earnestMoney.Sub(closeServiceCost)
if resultPrice.Abs().Cmp(amountLoss) >= 0 {
resultPrice = amountLoss.Neg()
}
}
// 用户资产账户
usableNew = usable.Add(resultPrice).Add(earnestMoney).Sub(closeServiceCost) // 可用资产 = 原可用资产 + ((正负)盈亏*面值) + 保证金 - 手续费
frozenNew = frozen.Sub(earnestMoney) // 冻结资产
if frozenNew.IsNegative() {
frozenNew = decimal.Zero
}
// 用户非资产账户
if order.ContractId != flags.BasicUnit {
usableNewNo = usableNo.Sub(orderNumber) // 可用非资产 = 原可用非资产 - 下单仓位数
frozenNewNo = frozenNo // 可用非冻结资产
}
// 检查用户订单平仓资产
if usableNewNo.IsNegative() {
return flags.ErrContractFive
}
if !flags.CheckSetting {
applogger.Debug("可用资产:%v", usableNew)
applogger.Debug("冻结资产:%v", frozenNew)
applogger.Debug("可用非资产:%v", usableNewNo)
applogger.Debug("冻结非资产:%v", frozenNewNo)
}
// 平仓(处理资产表(资产))
userContractUSDT := orders.UpdateBotUserContract(ctx, usableNew.String(), frozenNew.String())
if err = Uo.UpdateBotUserContract(session, userId, flags.BasicUnit, userContractUSDT); err != nil {
applogger.Error("%v ContractClosingPosition.UpdateBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 平仓(处理资产表(非资产))
if order.ContractId != flags.BasicUnit {
userContract := orders.UpdateBotUserContract(ctx, usableNewNo.String(), frozenNewNo.String())
if err = Uo.UpdateBotUserContract(session, userId, order.ContractId, userContract); err != nil {
applogger.Error("%v ContractClosingPosition.UpdateBotUserContract:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
}
// 平仓(处理订单信息(平仓价|保证金|平仓|订单总金额|平仓手续费|完成订单))
trade := orders.UpdateCloseBotContractTrade(ctx, price, earnestMoney.String(), totalAmount.String(), cost)
if err = Uo.UpdateBotContractTradeByOrderId(session, orderId, trade); err != nil {
applogger.Error("%v ContractClosingPosition.UpdateBotContractTradeByOrderId:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 平仓(返佣记录表|资产表|资金变动表)
if err = Uo.ContractRebateCalculation(ctx, session, int(userId), flags.ContractMarketType, flags.ClosingBrokType, flags.CloseMRebate, cost, orderId); err != nil {
applogger.Error("%v ContractClosingPosition.ContractRebateCalculation:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 更改交易日志记录方式
var list []models.BotUserContractLog
qData0 := orders.CreatBotUserContractLog(ctx, userId, flags.CostMoney, flags.BasicUnit, NegativeValue(closeServiceCost.String()), orderId) // 平仓(资金变动明细表(-平仓手续费))
qData1 := orders.CreatBotUserContractLog(ctx, userId, flags.Thaw, flags.BasicUnit, earnestMoney.String(), orderId) // 平仓(资金变动明细表(+保证金))
qData2 := orders.CreatBotUserContractLog(ctx, userId, flags.TransferOut, order.ContractId, NegativeValue(orderNumber.String()), orderId) // 平仓(资金变动明细表(-非资产))
list = append(list, qData0, qData1, qData2)
// 平仓(资金变动明细表(正负盈亏))
if resultPrice.IsNegative() {
qData3 := orders.CreatBotUserContractLog(ctx, userId, flags.TransferOut, flags.BasicUnit, resultPrice.String(), orderId)
list = append(list, qData3)
} else {
qData4 := orders.CreatBotUserContractLog(ctx, userId, flags.ChangeInto, flags.BasicUnit, resultPrice.String(), orderId)
list = append(list, qData4)
}
// 平仓(批量写入交易订单日志信息)
if err = Uo.CreatBotUserContractLogList(session, list); err != nil {
applogger.Error("%v ContractClosingPosition.CreatBotUserContractLogList:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v ContractClosingPosition.Commit:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
return nil
}
// UpdateBotContractStopByOrderId
//
// @Description: 合约设置止盈止损
// @receiver uo
// @param ctx
// @param order
// @return bool
// @return error
func (uo *userOrderRepo) UpdateBotContractStopByOrderId(ctx context.Context, order structure.StopOrder) (bool, error) {
session := uo.data.mysqlDB.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.NewSession:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
// 设置止盈止损判定
trade, err := uo.GetBotContractTradeByOrderId(session, order.OrderId, flags.PositionStatus)
if err != nil || trade == nil {
applogger.Error("%v UpdateBotContractStopByOrderId.GetBotContractTradeByOrderId:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
// 下单信息
checkBool, stopWinPrice, stopLossPrice, err := uo.UpdateVoteStopType(order)
if err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.UpdateVoteStopType:%v", common.ErrContract, err)
return false, err
}
if !flags.CheckSetting {
applogger.Debug("获取当前止盈止损数据:%v,%v,%v", checkBool, stopWinPrice, stopLossPrice)
}
// 下单判定设置(限价|市价|开仓价)
limitOrMarketPrice, err := uo.UpdateVoteDealType(trade)
if err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.UpdateVoteDealType:%v", common.ErrContract, err)
return false, err
}
if !flags.CheckSetting {
applogger.Debug("获取当前开仓价格:%v", limitOrMarketPrice)
}
// 下单判定设置(买涨|买跌)
if err = uo.VoteTradeType(int64(trade.TradeType), stopWinPrice, stopLossPrice, limitOrMarketPrice, checkBool); err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.VoteTradeType:%v", common.ErrContract, err)
return false, err
}
// 更新订单表
botStockTrade := orders.BotContractStopByOrderId(ctx, order)
err = uo.UpdateBotContractTradeByOrderId(session, order.OrderId, botStockTrade)
if err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.Update:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
// 修改挂单缓存队列
entrust, err := LoadLRangeList(setting.MarketContractPosition)
if err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.LoadLRangeList:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
for key, value := range entrust {
var entrustJson virtual.ContractTallyCache
if err = json.Unmarshal([]byte(value), &entrustJson); err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.Unmarshal:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
entrustJson.Order.StopType = order.StopType
entrustJson.Order.StopWinPrice = order.StopWinPrice
entrustJson.Order.StopLossPrice = order.StopLossPrice
if !flags.CheckSetting {
applogger.Debug("修改缓存中的止盈止损Type:%v", entrustJson.Order.StopType)
applogger.Debug("修改缓存中的止盈:%v", entrustJson.Order.StopWinPrice)
applogger.Debug("修改缓存中的止损:%v", entrustJson.Order.StopLossPrice)
}
var data []byte
data, err = json.Marshal(&entrustJson)
if err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.Marshal:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
if order.OrderId == entrustJson.OrderId {
if err = Reds.HSet(context.Background(), setting.MarketContractPosition, key, string(data)).Err(); err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.HSet:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
}
}
if err = session.Commit(); err != nil {
applogger.Error("%v UpdateBotContractStopByOrderId.Commit:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
return true, nil
}
// UpdateBotContractCancelByOrderId
//
// @Description: 合约撤单
// @receiver uo
// @param ctx
// @param orderId
// @return bool
// @return error
func (uo *userOrderRepo) UpdateBotContractCancelByOrderId(ctx context.Context, orderId string) (bool, error) {
session := uo.data.mysqlDB.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.NewSession:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
// get BotContractTrade
tread, err := uo.GetBotContractTradeByOrderId(session, orderId, flags.EntrustStatus)
if err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.GetBotContractTradeByOrderId:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
var userId int64
var earnestMoneyT, serviceCostT, symbol, orderNumber string
if tread != nil {
userId = int64(tread.UserId)
earnestMoneyT = tread.EarnestMoney
serviceCostT = tread.ServiceCost
orderNumber = tread.OrderNumber
symbol, err = uo.GetSymbol(tread.ContractId)
if err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.GetSymbol:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
}
// get BotUserStock By USD
var usable, frozen, usableNo, frozenNo decimal.Decimal
usable, frozen, _, err = uo.GetBotUserContract(session, userId, flags.BasicUnit)
if err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.GetBotUserContract:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
// 检查用户订单撤单资产
if frozen.IsNegative() || usable.IsNegative() {
return false, flags.ErrPublicFour
}
if !flags.CheckSetting {
applogger.Debug("用户原始可用资金:%v", usable)
applogger.Debug("用户原始冻结资金:%v", frozen)
}
if symbol != flags.BasicUnit {
usableNo, frozenNo, _, err = uo.GetBotUserContract(session, userId, tread.ContractId)
if err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.GetBotUserContract:%v", common.ErrContract, err)
return false, flags.ErrPublicFour
}
}
if !flags.CheckSetting {
applogger.Debug("用户原始可用非资金:%v", usableNo)
applogger.Debug("用户原始冻结非资金:%v", frozenNo)
}
// 更新订单信息
botStockTrade := orders.BotContractCancelByOrderId(ctx)
if err = uo.UpdateBotContractTradeByOrderId(session, orderId, botStockTrade); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.UpdateBotContractTradeByOrderId:%v", common.ErrContract, err)
return false, flags.ErrPublicFour
}
// Operational Spot Asset Table
earnestMoney := decimal.RequireFromString(earnestMoneyT) // 保证金
serviceCost := decimal.RequireFromString(serviceCostT) // 订单手续费
totalMoney := earnestMoney.Add(serviceCost) // 订单总金额
if !flags.CheckSetting {
applogger.Debug("撤单用户ID:%v", userId)
applogger.Debug("下单交易对:%v", symbol)
applogger.Debug("撤单订单保证金:%v", earnestMoneyT)
applogger.Debug("撤单手续费:%v", serviceCostT)
applogger.Debug("撤单订单数量:%v", orderNumber)
applogger.Debug("撤单总金额数量:%v", totalMoney)
}
// Update total asset data
usableNew := usable.Add(totalMoney) // 用户总资产
frozenNew := frozen.Sub(totalMoney) // 用户冻结资产
usableNoNew := usableNo // 用户非可用资产
frozenNoNew := frozenNo // 用户非冻结资产
// 检查用户订单撤单资产
if frozenNew.IsNegative() {
return false, flags.ErrContractSeven
}
if !flags.CheckSetting {
applogger.Debug("用户撤单可用总资产:%v", usableNew)
applogger.Debug("用户撤单冻结总资产:%v", frozenNew)
applogger.Debug("用户非撤单可用总资产:%v", usableNoNew)
applogger.Debug("用户非撤单冻结总资产:%v", frozenNoNew)
}
userContractUSDT := orders.UpdateBotUserContract(ctx, usableNew.String(), frozenNew.String())
if err = uo.UpdateBotUserContract(session, userId, flags.BasicUnit, userContractUSDT); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.UpdateBotUserContract:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
if symbol != flags.BasicUnit {
userContract := orders.UpdateBotUserContract(ctx, usableNoNew.String(), frozenNoNew.String())
if err = uo.UpdateBotUserContract(session, userId, tread.ContractId, userContract); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.UpdateBotUserContract:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
}
// 清理挂单缓存信息
if err = Reds.HDel(context.Background(), setting.MarketContractEntrust, orderId).Err(); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.HDel:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
// 撤单更新订阅缓存订单状态
userSubKey := virtual.OrderIdListKey(setting.ContractSubscribe, userId)
if err = UpdateContractSubscribeHashStatusByOrderId(orderId, userSubKey, flags.Cancel, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.ContractSubscribe:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
// 撤单更新管理员订阅缓存订单状态
if err = UpdateContractSubscribeHashStatusByOrderId(orderId, setting.AdminContractSubscribe, flags.Cancel, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.AdminContractSubscribe:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.Commit:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
return true, nil
}
// UpdateBotContractClosingByOrderId
//
// @Description: 合约平仓
// @receiver uo
// @param ctx
// @param orderId
// @return bool
// @return error
func (uo *userOrderRepo) UpdateBotContractClosingByOrderId(ctx context.Context, orderId string) (bool, error) {
// 1、订单信息
entrust, err := Reds.HGet(context.Background(), setting.MarketContractPosition, orderId).Result()
if err != nil {
applogger.Error("%v UpdateBotContractClosingByOrderId.MarketContractPosition.HGet:%v", common.ErrContract, err)
return false, flags.ErrMySqlDB
}
var entrustJson virtual.ContractTallyCache
if err = json.Unmarshal([]byte(entrust), &entrustJson); err != nil {
applogger.Error("%v UpdateBotContractClosingByOrderId.Unmarshal:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
if !flags.CheckSetting {
applogger.Debug("手动平仓:%v", entrustJson)
}
// 2、当前最新价格
var openPrice decimal.Decimal
subKey := publicData.SymbolCache(flags.Hy, entrustJson.Symbol, flags.TradeTypePrice)
openPrice, err = uo.GetTheLatestPrice(ctx, subKey, entrustJson.Order.TradeType)
if err != nil {
return false, flags.ErrContractEight
}
if err = Reds.HDel(context.Background(), setting.MarketContractPosition, orderId).Err(); err != nil {
applogger.Error("%v UpdateBotContractClosingByOrderId.MarketContractPosition.HDel:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
// 3、平仓处理
if err = ContractClosingPosition(ctx, uo.data.mysqlDB, orderId, openPrice.String(), entrustJson.Order); err != nil {
applogger.Error("%v UpdateBotContractClosingByOrderId.ContractClosingPosition:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
// 4、平仓更新订阅缓存订单状态
userSubKey := virtual.OrderIdListKey(setting.ContractSubscribe, entrustJson.UserId)
if err = UpdateContractSubscribeHashStatusByOrderId(orderId, userSubKey, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotContractClosingByOrderId.ContractSubscribe:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
// 5、平仓更新管理员订阅缓存订单状态
if err = UpdateContractSubscribeHashStatusByOrderId(orderId, setting.AdminContractSubscribe, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.AdminContractSubscribe:%v", common.ErrContract, err)
return false, flags.ErrCacheDB
}
return true, nil
}
// UpdateBotContractClosingAllByOrderId
//
// @Description: 合约一键平仓
// @receiver uo
// @param ctx
// @param userId
// @return error
func (uo *userOrderRepo) UpdateBotContractClosingAllByOrderId(ctx context.Context, userId int64) error {
// 1、查询当前用户所有订单
orderMap, err := uo.GetBotContractTradeByUserId(userId)
if err != nil {
applogger.Error("%v UpdateBotContractClosingAllByOrderId.GetBotContractTradeByUserId:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 2、查询持仓缓存列表数据-1>清理持仓列表缓存 2>处理平仓数据 3>清理订单ID缓存列表
for _, value := range orderMap {
var entrust string
entrust, err = Reds.HGet(context.Background(), setting.MarketContractPosition, value).Result()
if err != nil {
applogger.Error("%v UpdateBotContractClosingAllByOrderId.MarketContractPosition.LRange:%v", common.ErrContract, err)
return flags.ErrCacheDB
}
var entrustJson virtual.ContractTallyCache
if err = json.Unmarshal([]byte(entrust), &entrustJson); err != nil {
applogger.Error("%v UpdateBotContractClosingAllByOrderId.Unmarshal:%v", common.ErrContract, err)
return flags.ErrCacheDB
}
// 当前市价
var openPrice decimal.Decimal
subKey := publicData.SymbolCache(flags.Hy, entrustJson.Symbol, flags.TradeTypePrice)
openPrice, err = uo.GetTheLatestPrice(ctx, subKey, entrustJson.Order.TradeType)
if err != nil {
applogger.Error("%v UpdateBotContractClosingAllByOrderId.GetTheLatestPrice:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
if !flags.CheckSetting {
applogger.Debug("订阅Key:%v,最新价格:%v,订单ID:%v", subKey, openPrice, entrustJson.OrderId)
}
// 清理持仓缓存列表
if err = Reds.HDel(context.Background(), setting.MarketContractPosition, value).Err(); err != nil {
applogger.Error("%v UpdateBotContractClosingAllByOrderId.MarketContractPosition.HDel:%v", common.ErrContract, err)
return flags.ErrCacheDB
}
// 平仓
if err = ContractClosingPosition(ctx, uo.data.mysqlDB, entrustJson.OrderId, openPrice.String(), entrustJson.Order); err != nil {
applogger.Error("%v UpdateBotContractClosingAllByOrderId.ContractClosingPosition:%v", common.ErrContract, err)
return err
}
// 平仓更新用户订阅订单状态
userSubKey := virtual.OrderIdListKey(setting.ContractSubscribe, entrustJson.UserId)
if err = UpdateContractSubscribeHashStatusByOrderId(entrustJson.OrderId, userSubKey, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotContractClosingAllByOrderId.ContractSubscribe:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
// 平仓更新管理员订阅订单状态
if err = UpdateContractSubscribeHashStatusByOrderId(entrustJson.OrderId, setting.AdminContractSubscribe, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotContractCancelByOrderId.AdminContractSubscribe:%v", common.ErrContract, err)
return flags.ErrMySqlDB
}
}
return nil
}
// GetBotContractTradeByOrderId
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param orderId
// @param status
// @return *models.BotContractTrade
// @return error
func (uo *userOrderRepo) GetBotContractTradeByOrderId(session *xorm.Session, orderId string, status int) (*models.BotContractTrade, error) {
var botContractTrade []models.BotContractTrade
if err := session.Table(flags.BotContractTrade).
Where("order_id = ?", orderId).
Where("`status` = ?", status).
Find(&botContractTrade); err != nil {
return nil, err
}
for _, value := range botContractTrade {
return &value, nil
}
return nil, nil
}
// GetBotContractTradeByUserId
//
// @Description:
// @receiver uo
// @param ctx
// @param userId
// @return map[string]string
// @return error
func (uo *userOrderRepo) GetBotContractTradeByUserId(userId int64) (map[string]string, error) {
var botContractTrade []models.BotContractTrade
if err := uo.data.mysqlDB.Table(flags.BotContractTrade).
Where("user_id = ? ", userId).
Where("status = 1").
Find(&botContractTrade); err != nil {
return nil, err
}
botMap := make(map[string]string)
for _, value := range botContractTrade {
botMap[value.OrderId] = value.OrderId
}
return botMap, nil
}
// GetBotUserContract
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param userId
// @param symbol
// @return decimal.Decimal
// @return decimal.Decimal
// @return int
// @return error
func (uo *userOrderRepo) GetBotUserContract(session *xorm.Session, userId int64, symbol string) (decimal.Decimal, decimal.Decimal, int, error) {
var contractUSDT []models.BotUserContract
if err := session.Table(flags.BotUserContract).
Where("user_id = ?", userId).
Where("contract_id = ?", strings.ToUpper(symbol)).
Find(&contractUSDT); err != nil {
return decimal.Zero, decimal.Zero, 0, err
}
var usableNum, frozenNum decimal.Decimal
for _, value := range contractUSDT {
usableNum = decimal.RequireFromString(value.UsableNum) // 资产可用余额
frozenNum = decimal.RequireFromString(value.FrozenNum) // 资产冻结数量
}
return usableNum, frozenNum, len(contractUSDT), nil
}
// ContractVoteDealType
//
// @Description:
// @receiver uo
// @param ctx
// @param order
// @return decimal.Decimal
// @return error
func (uo *userOrderRepo) ContractVoteDealType(order structure.ContractOrder) (decimal.Decimal, error) {
var limitOrMarketPrice decimal.Decimal
switch order.DealType {
case flags.DealTypeLimited: // 限价
if len(order.LimitPrice) != 0 {
limitOrMarketPrice = decimal.RequireFromString(order.LimitPrice)
}
case flags.DealTypeMarket: // 市价
if len(order.MarketPrice) != 0 {
limitOrMarketPrice = decimal.RequireFromString(order.MarketPrice)
}
default:
return decimal.Decimal{}, flags.ErrPublicFive
}
return limitOrMarketPrice, nil
}
// UpdateVoteDealType
//
// @Description:
// @receiver uo
// @param ctx
// @param order
// @return decimal.Decimal
// @return error
func (uo *userOrderRepo) UpdateVoteDealType(order *models.BotContractTrade) (decimal.Decimal, error) {
var limitOrMarketPrice decimal.Decimal
switch order.DealType {
case flags.DealTypeLimited: // 限价
if len(order.LimitPrice) != 0 {
limitOrMarketPrice = decimal.RequireFromString(order.LimitPrice)
}
case flags.DealTypeMarket: // 市价
if len(order.MarketPrice) != 0 {
limitOrMarketPrice = decimal.RequireFromString(order.MarketPrice)
}
default:
return decimal.Decimal{}, flags.ErrPublicFive
}
if len(order.DealPrice) != 0 {
dealPrice := decimal.RequireFromString(order.DealPrice)
if !dealPrice.IsZero() {
limitOrMarketPrice = dealPrice
}
}
return limitOrMarketPrice, nil
}
// UpdateVoteStopType
//
// @Description:
// @receiver uo
// @param ctx
// @param order
// @return bool
// @return decimal.Decimal
// @return decimal.Decimal
// @return error
func (uo *userOrderRepo) UpdateVoteStopType(order structure.StopOrder) (bool, decimal.Decimal, decimal.Decimal, error) {
var checkBool bool
var stopWinPrice, stopLossPrice decimal.Decimal
switch order.StopType {
case flags.StopTypeNone: // 暂无设置止盈止损
checkBool = false
case flags.StopTypeSet: // 设置止盈止损
if len(order.StopWinPrice) != 0 {
stopWinPrice = decimal.RequireFromString(order.StopWinPrice)
}
if len(order.StopLossPrice) != 0 {
stopLossPrice = decimal.RequireFromString(order.StopLossPrice)
}
// 判定设置是否都为零
if stopWinPrice.IsZero() && stopLossPrice.IsZero() {
return checkBool, decimal.Decimal{}, decimal.Decimal{}, flags.ErrContractTen
} else {
checkBool = true
}
default:
return checkBool, stopWinPrice, stopLossPrice, flags.ErrContractEleven
}
return checkBool, stopWinPrice, stopLossPrice, nil
}
// VoteTradeType
//
// @Description: 股票判定止盈止损配置
// @receiver uo
// @param ctx
// @param tradeType
// @param stopWinPrice
// @param stopLossPrice
// @param price
// @param checkBool
// @return error
func (uo *userOrderRepo) VoteTradeType(tradeType int64, stopWinPrice, stopLossPrice, price decimal.Decimal, checkBool bool) error {
if checkBool {
switch tradeType {
case flags.TradeTypeBuy: // 买涨
if !stopWinPrice.IsZero() {
if stopWinPrice.Cmp(price) <= 0 {
// 限价买涨下单,止盈价格必须大于限价
return flags.ErrBuyStopWinPrice
}
}
if !stopLossPrice.IsZero() {
if stopLossPrice.Cmp(price) >= 0 {
// 限价买涨下单,止损价格必须小于限价
return flags.ErrBuyStopLossPrice
}
}
case flags.TradeTypeSell: // 买跌
if !stopWinPrice.IsZero() {
if stopWinPrice.Cmp(price) >= 0 {
// 限价买跌下单,止盈价格必须小于限价
return flags.ErrSellStopWinPrice
}
}
if !stopLossPrice.IsZero() {
if stopLossPrice.Cmp(price) <= 0 {
// 限价买跌下单,止损价格必须大于限价
return flags.ErrSellStopLossPrice
}
}
default:
return flags.ErrContractTwelve
}
}
return nil
}
// VoteTradeTypeOption
//
// @Description: 期权判定止盈止损配置
// @receiver uo
// @param dealType 市价|限价
// @param tradeType 看涨(calls-CE)|看跌(puts-PE)
// @param tradingType 买入(buy)|卖出(sell)
// @param stopWinPrice 止盈价格
// @param stopLossPrice 止损价格
// @param limitOrMarketPrice 设置价格(市价|限价)
// @param bid 买一价
// @param ask 卖一价
// @param checkBool
// @return error
func (uo *userOrderRepo) VoteTradeTypeOption(dealType, tradeType, tradingType int64, stopWinPrice, stopLossPrice, limitOrMarketPrice, bid, ask decimal.Decimal, checkBool bool) error {
if checkBool {
switch dealType {
case flags.DealTypeMarket: // 市价
switch tradeType {
case flags.OptionCalls: // call-(buy|sell)
return StopOrWinCmp(tradingType, ask, bid, stopWinPrice, stopLossPrice, limitOrMarketPrice, false)
case flags.OptionPuts: // put-(buy|sell)
return StopOrWinCmp(tradingType, ask, bid, stopWinPrice, stopLossPrice, limitOrMarketPrice, false)
default:
return flags.ErrContractTwelve
}
case flags.DealTypeLimited: // 限价
switch tradeType {
case flags.TradeTypeBuy: // call-(buy|sell)
return StopOrWinCmp(tradingType, ask, bid, stopWinPrice, stopLossPrice, limitOrMarketPrice, true)
case flags.TradeTypeSell: // put-(buy|sell)
return StopOrWinCmp(tradingType, ask, bid, stopWinPrice, stopLossPrice, limitOrMarketPrice, true)
default:
return flags.ErrContractTwelve
}
default:
return flags.ErrContractTwelve
}
}
return nil
}
// StopOrWinCmp
//
// @Description:
// @param tradingType
// @param ask
// @param bid
// @param stopWinPrice
// @param stopLossPrice
// @return error
func StopOrWinCmp(tradingType int64, ask, bid, stopWinPrice, stopLossPrice, limitOrMarketPrice decimal.Decimal, check bool) error {
/** 期权下单止盈止损判定
一、call-buy 和 put-buy
限价开仓价格 < 卖一价
止盈价 > 买一价
止损价 < 卖一价
二、call-sell 和 put-sell
限价单开仓价格 > 买一价
止盈价 < 卖一价
止损价 > 买一价
*/
switch tradingType {
case flags.OptionBuy: // buy
if !stopWinPrice.IsZero() {
if stopWinPrice.Cmp(bid) > 0 {
return flags.ErrBuyStopWinPriceOption // 看涨下单,止盈价大于买一价
}
}
if !stopLossPrice.IsZero() {
if stopLossPrice.Cmp(ask) < 0 {
return flags.ErrBuyStopLossPriceOption // 看涨下单,止损价小于卖一价
}
}
if check { // limit order start
if limitOrMarketPrice.Cmp(ask) < 0 {
return flags.ErrBuyLimitPriceOption // 看涨下单,开仓价格小于卖一价
}
}
case flags.OptionSell: // sell
if !stopWinPrice.IsZero() {
if stopWinPrice.Cmp(ask) < 0 {
return flags.ErrSellStopWinPriceOption // 看跌下单,止盈价小于卖一价
}
}
if !stopLossPrice.IsZero() {
if stopLossPrice.Cmp(bid) > 0 {
return flags.ErrSellStopLossPriceOption // 看跌下单,止损价大于买一价
}
}
if check { // limit order start
if limitOrMarketPrice.Cmp(bid) > 0 {
return flags.ErrSellLimitPriceOption // 看跌下单,开仓价格大于买一价
}
}
default:
return flags.ErrContractTwelve
}
return nil
}
// CreatBotUserContract
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param digital
// @return error
func (uo *userOrderRepo) CreatBotUserContract(session *xorm.Session, digital models.BotUserContract) error {
_, err := session.Table(flags.BotUserContract).Insert(&digital)
if err != nil {
return err
}
return nil
}
// CreatBotContractTrade
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param trade
// @return error
func (uo *userOrderRepo) CreatBotContractTrade(session *xorm.Session, trade models.BotContractTrade) error {
_, err := session.Table(flags.BotContractTrade).Insert(&trade)
if err != nil {
return err
}
return nil
}
// UpdateBotContractTradeByOrderId
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param orderId
// @param trade
// @return error
func (uo *userOrderRepo) UpdateBotContractTradeByOrderId(session *xorm.Session, orderId string, trade models.BotContractTrade) error {
_, err := session.Table(flags.BotContractTrade).
Where("order_id = ?", orderId).
Update(&trade)
if err != nil {
return err
}
return nil
}
// UpdateBotUserContract
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param userId
// @param symbol
// @param userContract
// @return error
func (uo *userOrderRepo) UpdateBotUserContract(session *xorm.Session, userId int64, symbol string, userContract models.BotUserContract) error {
_, err := session.Table(flags.BotUserContract).
Where("user_id = ?", userId).
Where("contract_id = ?", symbol).
Update(&userContract)
if err != nil {
return err
}
return nil
}
// GetSymbol
//
// @Description:
// @receiver uo
// @param ctx
// @param contractId
// @return string
// @return error
func (uo *userOrderRepo) GetSymbol(contractId string) (string, error) {
var symbol string
symbolList := strings.Split(contractId, "-")
if len(symbolList) == 2 {
symbol = symbolList[0]
} else {
return "", flags.ErrContractThree
}
return symbol, nil
}
// VerifyBotContractTradeOrderId
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @return string
// @return error
func (uo *userOrderRepo) VerifyBotContractTradeOrderId(session *xorm.Session) (string, error) {
var orderId string
for {
orderId = orders.CreateRandCodeOrder(10)
var orderList []models.BotContractTrade
err := session.Table(flags.BotContractTrade).Where("order_id = ?", orderId).Find(&orderList)
if err != nil || len(orderList) > 0 {
applogger.Error("%v VerifyBotContractTradeOrderId.Find:%v", common.ErrContract, err)
continue
}
break
}
return orderId, nil
}
// GetTheLatestPrice
//
// @Description:
// @receiver uo
// @param ctx
// @param subKey
// @param tradeType
// @return decimal.Decimal
// @return error
func (uo *userOrderRepo) GetTheLatestPrice(ctx context.Context, subKey string, tradeType int64) (decimal.Decimal, error) {
priceNew, err := virtual.ContractSubMarketPrice(ctx, subKey)
if err != nil {
applogger.Error("%v GetTheLatestPrice.ContractSubMarketPrice:%v", common.ErrContract, err)
return decimal.Decimal{}, err
}
priceS := decimal.RequireFromString(priceNew)
var openPrice decimal.Decimal
difference := priceS.Mul(utils.Difference()) // 设置价差
switch tradeType {
case flags.TradeTypeBuy: // 买涨
openPrice = priceS.Sub(difference) // 开仓价格
case flags.TradeTypeSell: // 买跌
openPrice = priceS.Add(difference) // 开仓价格
default:
}
return openPrice, nil
}