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.

1365 lines
55 KiB

package data
import (
"context"
"encoding/json"
"github.com/go-xorm/xorm"
"github.com/shopspring/decimal"
"matchmaking-system/internal/biz/structure"
orders "matchmaking-system/internal/data/convert"
"matchmaking-system/internal/data/tradedeal/option"
"matchmaking-system/internal/data/tradedeal/virtual"
"matchmaking-system/internal/pkg/flags"
"matchmaking-system/internal/pkg/logging/applogger"
"matchmaking-system/internal/pkg/logging/common"
models "matchmaking-system/internal/pkg/model"
"matchmaking-system/internal/pkg/setting"
"matchmaking-system/internal/pkg/utils"
)
// GetBotStockOptionInrTradeList
//
// @Description: 期权-印度股订单列表查询
// @receiver uo
// @param ctx
// @param pageSize
// @param pageCount
// @param userId
// @param status
// @return []*models.BotStockOptionInrTrade
// @return int64
// @return error
func (uo *userOrderRepo) GetBotStockOptionInrTradeList(ctx context.Context, pageSize, pageCount, userId, status int64) ([]*models.BotStockOptionInrTrade, int64, error) {
totalCount, err := uo.data.mysqlDB.Table(flags.BotStockOptionInrTrade).
Where("user_id = ?", userId).
Where("status = ?", status).
Count()
if err != nil {
return nil, 0, flags.ErrMySqlDB
}
if totalCount == 0 {
return nil, 0, nil
}
var stockList []*models.BotStockOptionInrTrade
if err = uo.data.mysqlDB.Table(flags.BotStockOptionInrTrade).
Where("user_id = ?", userId).
Where("status = ?", status).
Limit(int(pageSize), int(pageCount)).
Desc(GetOrderByStatusSort(status)).
Find(&stockList); err != nil {
return nil, 0, flags.ErrMySqlDB
}
var stockUpdateList []*models.BotStockOptionInrTrade
for _, value := range stockList {
value.KeepDecimal = GetKeepDecimal(flags.OptionOpiSystemSetUpKey, value.StockId)
value.StockName = GetStockName(flags.OptionOpiSystemSetUpKey, value.StockId)
stockUpdateList = append(stockUpdateList, value)
}
return stockUpdateList, totalCount, nil
}
// PlacingStockOptionInrOrders
//
// @Description: 期权-印度股下单
// @receiver uo
// @param ctx
// @param userId
// @param order
// @return string
// @return error
func (uo *userOrderRepo) PlacingStockOptionInrOrders(ctx context.Context, userId int64, order structure.ShareOrder) (string, error) {
// 1、通知下单订阅
symbol := order.StockCode
_, ok := optionInr.OpiMapSymbol.Load(symbol)
if !ok {
go func() {
optionInr.OpiMap <- []byte(symbol)
}()
}
// 判定是否存在欠款订单
userIdMap, _ := GetBotUserArrears(ctx)
_, isOk := userIdMap[userId]
if isOk {
return flags.ErrPublicServe.Error(), nil
}
// 杠杆初始化
order = PryNumInit(order)
// 当前时间和到期时间相同-不容许下单
//if !utils.TimeYYYMMDD(order.StopTime) {
// return flags.SetNull, flags.ErrIsParameter
//}
// 期权-印度股市场设置判定
if CheckGlobalTread(flags.OpiMarket) {
return flags.SetNull, flags.ErrStopTread
}
// 保证金比例(后台配置)
order.Ratio = GetOptionInrForcedClosure(flags.OptionOpiSystemSetUpKey, symbol).String()
// 期权-强平阈值
flatRatio := GetCacheForcedClosure(flags.OptionOpiSystemSetUpKey, symbol)
system := &structure.ShareSystem{
StrongFlatRatio: flatRatio.String(),
}
order.System = system
// 2、股票下单判定
// 下单判定设置(false无设置|true止盈止损)
checkBool, stopWinPrice, stopLossPrice, err := OptionInrVoteStopType(order)
if err != nil {
applogger.Error("%v PlacingStockOptionInrOrders.OptionInrVoteStopType:%v", common.ErrOptionInr, err)
return flags.SetNull, err
}
// 下单判定设置(限价|市价)
limitOrMarketPrice, err := uo.OptionInrVoteDealType(order)
if err != nil {
applogger.Error("%v PlacingStockOptionInrOrders.StockVoteDealType:%v", common.ErrOptionInr, err)
return flags.SetNull, err
}
// 下单判定设置(看涨|看跌)
ask := decimal.RequireFromString(order.Ask)
bid := decimal.RequireFromString(order.Bid)
if err = uo.VoteTradeTypeOption(order.DealType, order.TradeType, order.TradingType, stopWinPrice, stopLossPrice, limitOrMarketPrice, bid, ask, checkBool); err != nil {
applogger.Error("%v PlacingStockOptionInrOrders.StockVoteTradeType:%v", common.ErrOptionInr, err)
return flags.SetNull, err
}
// 3、订单持久化录入(处理资产信息)
marginRatio := GetOptionInrCostPrice(limitOrMarketPrice, order) // 保证金计算
orderId, err := uo.OptionInrOrderWriteDB(ctx, userId, order, marginRatio)
if err != nil || len(orderId) == 0 {
applogger.Error("%v PlacingStockOptionInrOrders.OptionInrOrderWriteDB:%v", common.ErrOptionInr, err)
return flags.SetNull, err
}
// 4、处理订单信息
marketStatus, shareOrder, err := option.OptionInrCacheDeal(ctx, userId, orderId, order)
if err != nil || shareOrder == nil {
applogger.Error("%v PlacingStockOptionInrOrders.OptionInrCacheDeal:%v", common.ErrOptionInr, err)
return flags.SetNull, err
}
if !flags.CheckSetting {
applogger.Debug("订单ID:%v", shareOrder.OrderId)
applogger.Debug("订单交易对:%v", shareOrder.Symbol)
applogger.Debug("订单状态:%v", shareOrder.Status)
applogger.Debug("订单用户ID:%v", shareOrder.UserId)
applogger.Debug("开盘价:%v", shareOrder.OpenPrice)
applogger.Debug("收盘价:%v", shareOrder.ClosingPrice)
applogger.Debug("下单信息:%v", shareOrder.Order)
}
// 5、订单(挂单|持仓)缓存列表,等待队列计算
if err = option.OptionInrHashUserOrder(Reds, marketStatus, shareOrder); err != nil {
applogger.Error("%v PlacingStockOptionInrOrders.OptionInrHashUserOrder:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrCacheDB
}
// 6、用户订阅股票订单信息
orderIdKey := virtual.OrderIdListKey(setting.OptionInrSubscribe, userId)
if err = option.OptionInrHashUserOrder(Reds, orderIdKey, shareOrder); err != nil {
applogger.Error("%v PlacingStockOptionInrOrders.OptionInrSubscribe:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrCacheDB
}
// 7、管理员订阅订单信息
if err = option.OptionInrHashUserOrder(Reds, setting.AdminOptionInrSubscribe, shareOrder); err != nil {
applogger.Error("%v PlacingStockOptionInrOrders.AdminOptionInrSubscribe:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrCacheDB
}
return orderId, nil
}
// OptionInrOrderWriteDB
//
// @Description: 期权-印度股下单处理
// @receiver uo
// @param ctx
// @param userId
// @param order
// @param limitOrMarketPrice
// @return string
// @return error
func (uo *userOrderRepo) OptionInrOrderWriteDB(ctx context.Context, userId int64, order structure.ShareOrder, marginRatio decimal.Decimal) (string, error) {
session := uo.data.mysqlDB.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v OptionInrOrderWriteDB.Begin:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
// 用户期权-印度股资产信息
var usableOld, frozenOld decimal.Decimal
opInr, err := uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit)
if err != nil || opInr == nil {
applogger.Error("%v OptionInrOrderWriteDB.INR:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
usableOld = decimal.RequireFromString(opInr.UsableNum)
frozenOld = decimal.RequireFromString(opInr.FrozenNum)
// 检查用户期权印度股资产信息
if usableOld.IsZero() || usableOld.IsNegative() || frozenOld.IsNegative() {
return flags.SetNull, flags.ErrShareOne
}
if !flags.CheckSetting {
applogger.Debug("下单可用资产:%v", usableOld)
applogger.Debug("下单冻结资产:%v", frozenOld)
}
// 用户期权印度股非资产信息
var usableFOld, frozenFOld decimal.Decimal
stockFIdr, err := uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, order.StockId)
if err != nil {
applogger.Error("%v OptionInrOrderWriteDB.FeiIDR:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
if stockFIdr != nil {
usableFOld = decimal.RequireFromString(stockFIdr.UsableNum)
frozenFOld = decimal.RequireFromString(stockFIdr.FrozenNum)
}
if !flags.CheckSetting {
applogger.Debug("下单查询非资产账户:%v", stockFIdr)
applogger.Debug("下单可用非资产:%v", usableFOld)
applogger.Debug("下单冻结非资产:%v", frozenFOld)
}
// 检查期权-印度股订单ID是否存在,新增订单信息
var orderId string
orderId, err = uo.VerifyBotStockOptionInrTradeOrderId(session)
if err != nil {
applogger.Error("%v OptionInrOrderWriteDB.VerifyBotStockOptionInrTradeOrderId:%v", common.ErrOptionInr, err)
return flags.SetNull, err
}
trade := orders.BotStockOptionInrTrade(ctx, userId, orderId, marginRatio.String(), order)
if err = uo.CreateBotStockOptionInrTrade(session, trade); err != nil {
applogger.Error("%v OptionInrOrderWriteDB.CreateBotStockOptionInrTrade:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
// 检查用户下单资产信息
orderMoney := marginRatio.Add(decimal.RequireFromString(order.ServiceCost)) // 订单总金额 = 保证金 + 手续费
residue := usableOld.Sub(orderMoney).Div(usableOld)
if usableOld.Cmp(orderMoney) <= 0 || residue.Cmp(utils.DecimalsStrInt()) <= 0 {
return flags.SetNull, flags.ErrPublicTow
}
// 处理用户下单资产信息
orderNumber := decimal.RequireFromString(order.OrderNumber) // 订单数量
var usableNew, frozenNew, usableFNew, frozenFNew decimal.Decimal
usableNew = usableOld.Sub(orderMoney) // 可用资产
frozenNew = frozenOld.Add(orderMoney) // 冻结资产
if order.StockId != flags.OptionInrBasicUnit {
usableFNew = usableFOld.Add(orderNumber) // 可用非资产
frozenFNew = frozenFOld // 冻结非资产
}
// 检查用户下单资产信息
if usableNew.IsNegative() || frozenNew.IsNegative() {
return flags.SetNull, flags.ErrPublicTow
}
// 下单更新资产信息
stockIdrModel := orders.UpdateBotUserStockOptionInr(ctx, usableNew.String(), frozenNew.String())
err = uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit, stockIdrModel)
if err != nil {
applogger.Error("%v OptionInrOrderWriteDB.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
// 下单(新增|更新)非资产信息
if stockFIdr == nil {
stockFIDRModel := orders.CreateBotUserStockOptionInr(ctx, userId, order.StockId, usableFNew.String(), frozenFNew.String())
err = uo.CreateBotUserStockOptionInrFIRD(session, stockFIDRModel)
if err != nil {
applogger.Error("%v OptionInrOrderWriteDB.CreateBotUserStockOptionInrFIRD:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
} else {
stockFIDRModel := orders.UpdateBotUserStockOptionInr(ctx, usableFNew.String(), frozenFNew.String())
err = uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, order.StockId, stockFIDRModel)
if err != nil {
applogger.Error("%v OptionInrOrderWriteDB.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
}
if err = session.Commit(); err != nil {
applogger.Error("%v OptionInrOrderWriteDB.Commit:%v", common.ErrOptionInr, err)
return flags.SetNull, flags.ErrMySqlDB
}
return orderId, nil
}
// OptionInrOpenOrder
//
// @Description: 期权-印度股系统开仓
// @param ctx
// @param db
// @param userId
// @param orderId
// @param openPrice
// @param order
// @return error
func OptionInrOpenOrder(ctx context.Context, db *xorm.EngineGroup, userId int64, orderId, openPrice string, order structure.ShareOrder) error {
session := db.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v OptionInrOpenOrder.NewSession:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
// 查询当前下单用户可用账户金额和冻结金额
var usable, frozen, usableNo, frozenNo decimal.Decimal
stockIdr, err := Uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit)
if err != nil || stockIdr == nil {
applogger.Error("%v OptionInrOpenOrder.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
usable = decimal.RequireFromString(stockIdr.UsableNum) // 用户可用资产
frozen = decimal.RequireFromString(stockIdr.FrozenNum) // 用户冻结资产
if !flags.CheckSetting {
applogger.Debug("下单可用金额:%v", usable)
applogger.Debug("下单冻结金额:%v", frozen)
}
// 检查用户开仓资产信息
if usable.IsNegative() || frozen.IsNegative() {
return flags.ErrPublicThree
}
// 检查用户非资产信息
if order.StockId != flags.OptionInrBasicUnit {
stockFIdr, err := Uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, order.StockId)
if err != nil {
applogger.Error("%v OptionInrOpenOrder.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
usableNo = decimal.RequireFromString(stockFIdr.UsableNum) // 用户非可用资产
frozenNo = decimal.RequireFromString(stockFIdr.FrozenNum) // 用户非冻结资产
}
if !flags.CheckSetting {
applogger.Debug("下单非可用金额:%v", usableNo)
applogger.Debug("下单非冻结金额:%v", frozenNo)
}
// 转换股票下单信息
openOrderPrice := decimal.RequireFromString(openPrice) // 开盘价格
orderNumber := decimal.RequireFromString(order.OrderNumber) // 下单股数
marketMoney := decimal.RequireFromString(order.MarketMoney) // 下单金额
serviceCost := decimal.RequireFromString(order.ServiceCost) // 下单手续费
orderMoney := marketMoney.Add(serviceCost) // 下单总金额 = (下单手续费 + 下单金额)
if !flags.CheckSetting {
applogger.Debug("下单订单数量:%v", orderNumber)
applogger.Debug("下单订单金额:%v", marketMoney)
applogger.Debug("下单订单手续费:%v", serviceCost)
applogger.Debug("下单订单总金额:%v", orderMoney)
}
costOpenPrice, err := Uo.CalculateHandlingFeesOption(ctx, session, int(userId), flags.OptionInrMarketType, flags.OpenBrokType, orderId, openPrice, order.OrderNumber)
if err != nil {
applogger.Error("%v OptionInrOpenOrder.CalculateHandlingFeesOption:%v", common.ErrShareUs, err)
return err
}
openServiceCost := decimal.RequireFromString(costOpenPrice) // 期权-开仓手续费
marginRatio := GetOptionInrCostPrice(openOrderPrice, order) // 期权-(开仓订单金额-开仓保证金)
openOrderMoney := marginRatio.Add(openServiceCost) // 期权-(订单总额 = 保证金 + 手续费)
floatPrice := orderMoney.Sub(openOrderMoney) // 期权-(计算容差 = 下单总额 - 开仓总额)
if !flags.CheckSetting {
applogger.Debug("开仓订单价格:%v", openOrderPrice)
applogger.Debug("开仓手续费:%v", openServiceCost)
applogger.Debug("开仓保证金:%v", marginRatio)
applogger.Debug("开仓总金额:%v", openOrderMoney)
applogger.Debug("下单->开仓总金额容差值:%v", floatPrice)
}
var usableNew, frozenNew, usableNoNew, frozenNoNew decimal.Decimal
usableNew = usable.Add(floatPrice) // 可用资产(处理容差值) = 可用余额 + 下单保证金 - 开仓保证金
frozenNew = frozen.Sub(orderMoney).Add(marginRatio) // 冻结资产 = 冻结资产 - 下单总额 + 开仓保证金
if order.StockId != flags.ShareInrBasicUnit {
usableNoNew = usableNo // 非可用资产
frozenNoNew = frozenNo // 非冻结资产
}
if !flags.CheckSetting {
applogger.Debug("下单后用户可用资产:%v", usableNew)
applogger.Debug("下单后用户冻结资产:%v", frozenNew)
}
// 检查用户开仓资产
if frozenNew.IsNegative() || usableNew.IsNegative() {
return flags.ErrPublicThree
}
// 处理资产信息
userStockIdr := orders.UpdateBotUserStockOptionInr(ctx, usableNew.String(), frozenNew.String())
if err = Uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit, userStockIdr); err != nil {
applogger.Error("%v OptionInrOpenOrder.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
// 更新非资产信息
if order.StockId != flags.OptionInrBasicUnit {
userStockFIdr := orders.UpdateBotUserStockOptionInr(ctx, usableNoNew.String(), frozenNoNew.String())
if err = Uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, order.StockId, userStockFIdr); err != nil {
applogger.Error("%v OptionInrOpenOrder.OPI.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
}
// 处理订单信息(成交价|仓位|开仓时间|订单总金额|手续费|持仓状态)
trade := orders.UpdateOpenBotStockOptionInrTrade(ctx, openPrice, order.OrderNumber, openOrderMoney.String(), order.ServiceCost, marginRatio.String())
if err = Uo.UpdateBotStockOptionInrTradeByOrderId(session, orderId, trade); err != nil {
applogger.Error("%v OptionInrOpenOrder.UpdateBotStockOptionInrTradeByOrderId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
// 更改交易日志记录方式
var list []models.BotUserStockOptionInrLog
qData1 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.Freeze, flags.OptionInrBasicUnit, NegativeValue(marginRatio.String()), orderId) // 资金变动明细表(开仓保证金)
qData2 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.ChangeInto, order.StockId, order.OrderNumber, orderId) // 资金变动明细表(非资产)
qData3 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.CostMoney, flags.OptionInrBasicUnit, NegativeValue(openServiceCost.String()), orderId) // 资金变动明细表(开仓手续费)
list = append(list, qData1, qData2, qData3)
// 批量写入数据信息
if err = Uo.CreatBotUserStockOptionInrLogList(session, list); err != nil {
applogger.Error("%v OptionInrOpenOrder.CreatBotUserStockOptionInrLogList:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v OptionInrOpenOrder.Commit:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
return nil
}
// OptionInrClosingOrder
//
// @Description: 期权-印度股系统平仓
// @param ctx
// @param db
// @param orderId
// @param price
// @param order
// @return error
func OptionInrClosingOrder(ctx context.Context, db *xorm.EngineGroup, orderId string, price string, order structure.ShareOrder) error {
session := db.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v OptionInrClosingOrder.NewSession:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
// 查询订单信息
tread, err := Uo.GetBotStockOptionInrTradeByOrderIdOrStatus(session, orderId, flags.PositionStatus)
if err != nil {
applogger.Error("%v OptionInrClosingOrder.GetBotStockOptionInrTradeByOrderIdOrStatus:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
var userId int64
var openPrice, orderNumber, orderMoney, serviceCost, marketMoney, strikePrice decimal.Decimal
if tread != nil {
userId = int64(tread.UserId) // 用户Id
orderNumber = decimal.RequireFromString(tread.OrderNumber) // 订单数量
openPrice = decimal.RequireFromString(tread.DealPrice) // 开仓价格
orderMoney = decimal.RequireFromString(tread.OrderMoney) // 订单总金额
serviceCost = decimal.RequireFromString(tread.ServiceCost) // 开仓手续费
marketMoney = decimal.RequireFromString(tread.MarketMoney) // 保证金
strikePrice = decimal.RequireFromString(tread.StrikePrice) // 行权价
}
if !flags.CheckSetting {
applogger.Debug("下单开仓用户Id:%v", userId)
applogger.Debug("下单开仓开仓价格:%v", openPrice)
applogger.Debug("下单开仓订单数量:%v", orderNumber)
applogger.Debug("下单开仓总金额:%v", orderMoney)
applogger.Debug("下单开仓手续费:%v", serviceCost)
applogger.Debug("下单开仓订单金额:%v", marketMoney)
applogger.Debug("下单权利金:%v", strikePrice)
}
// 查询当前下单用户可用账户金额和冻结金额
var usable, frozen, usableNo, frozenNo decimal.Decimal
userStockIdr, err := Uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit)
if err != nil {
applogger.Error("%v OptionInrClosingOrder.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
usable = decimal.RequireFromString(userStockIdr.UsableNum) // 用户可用资金
frozen = decimal.RequireFromString(userStockIdr.FrozenNum) // 用户非冻结资金
// 检查用户平仓资产
if usable.IsNegative() || frozen.IsNegative() {
return flags.ErrShareTow
}
// 查询当前下单用户可用非账户和冻结金额
if order.StockId != flags.OptionInrBasicUnit {
userStockFIdr, err := Uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, order.StockId)
if err != nil {
applogger.Error("%v OptionInrClosingOrder.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
usableNo = decimal.RequireFromString(userStockFIdr.UsableNum) // 用户非可用资金
frozenNo = decimal.RequireFromString(userStockFIdr.FrozenNum) // 用户非冻结资金
}
if !flags.CheckSetting {
applogger.Debug("下单可用金额:%v", usable)
applogger.Debug("下单冻结金额:%v", frozen)
applogger.Debug("下单非可用金额:%v", usableNo)
applogger.Debug("下单非冻结金额:%v", frozenNo)
applogger.Debug("平仓价格:%v", price)
}
costClosePrice, err := Uo.CalculateHandlingFeesOption(ctx, session, int(userId), flags.OptionInrMarketType, flags.ClosingBrokType, orderId, price, order.OrderNumber)
if err != nil {
applogger.Error("%v OptionInrClosingOrder.CalculateHandlingFeesOption:%v", common.ErrShareUs, err)
return flags.ErrMySqlDB
}
closePrice := decimal.RequireFromString(price) // 期权-平仓价格
closeServiceCost := decimal.RequireFromString(costClosePrice) // 期权-平仓手续费
resultPrice := GetOptionInrFloatingPL(order, openPrice, closePrice, orderNumber) // 期权-浮动盈亏
if !flags.CheckSetting {
applogger.Debug("平仓手续费手续费:%v", closeServiceCost)
applogger.Debug("平仓浮动盈亏:%v", resultPrice)
}
// TODO: 判定亏损不能超过 = 保证金 + 平仓手续费
if resultPrice.IsNegative() {
amountLoss := marketMoney.Sub(closeServiceCost)
if resultPrice.Abs().Cmp(amountLoss) >= 0 {
resultPrice = amountLoss.Neg()
}
}
var usableNew, frozenNew, usableNewNo, frozenNewNo decimal.Decimal
usableNew = usable.Add(resultPrice).Add(marketMoney).Sub(closeServiceCost) // 可用资产 = 原可用资产 + ((正负)盈亏) + 开仓订单金额 - 平仓手续费
if frozen.Cmp(marketMoney) == -1 {
frozenNew = frozen.Sub(frozen) // 冻结资产
} else {
frozenNew = frozen.Sub(marketMoney) // 冻结资产
}
if order.StockId != flags.OptionInrBasicUnit {
usableNewNo = usableNo.Sub(orderNumber) // 可用非资产 = 原可用非资产 - 下单股数
frozenNewNo = frozenNo // 可用非冻结资产
}
// 检查用户订单平仓资产
if frozenNew.IsNegative() || usableNewNo.IsNegative() {
return flags.ErrShareTow
}
if !flags.CheckSetting {
applogger.Debug("可用资产:%v", usableNew)
applogger.Debug("冻结资产:%v", frozenNew)
applogger.Debug("可用非资产:%v", usableNewNo)
applogger.Debug("冻结非资产:%v", frozenNewNo)
}
// 平仓(处理资产表(资产))
userStockIDR := orders.UpdateBotUserStockOptionInr(ctx, usableNew.String(), frozenNew.String())
if err = Uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit, userStockIDR); err != nil {
applogger.Error("%v OptionInrClosingOrder.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
// 平仓(处理资产表(非资产))
if order.StockId != flags.OptionInrBasicUnit {
userStockFIDR := orders.UpdateBotUserStockOptionInr(ctx, usableNewNo.String(), frozenNewNo.String())
if err = Uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, order.StockId, userStockFIDR); err != nil {
applogger.Error("%v OptionInrClosingOrder.Opi.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
}
// 平仓(处理订单信息(平仓价|平仓手续费|完成订单))
trade := orders.UpdateCloseBotStockOptionInrTrade(ctx, price, closeServiceCost.String())
if err = Uo.UpdateBotStockOptionInrTradeByOrderId(session, orderId, trade); err != nil {
applogger.Error("%v OptionInrClosingOrder.UpdateBotStockOptionInrTradeByOrderId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
// 更改交易日志记录方式
var list []models.BotUserStockOptionInrLog
qData1 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.Thaw, flags.OptionInrBasicUnit, marketMoney.String(), orderId) // 平仓(资金变动明细表(开仓保证金))
qData2 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.CostMoney, flags.OptionInrBasicUnit, NegativeValue(closeServiceCost.String()), orderId) // 平仓(资金变动明细表(平仓手续费))
qData3 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.TransferOut, order.StockId, NegativeValue(orderNumber.String()), orderId) // 平仓(资金变动明细表(-非资产))
list = append(list, qData1, qData2, qData3)
// 平仓(资金变动明细表(正负盈亏))
if resultPrice.IsNegative() {
if resultPrice.Abs().Cmp(marketMoney) >= 0 {
resultPrice = marketMoney.Neg()
}
qData4 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.TransferOut, flags.OptionInrBasicUnit, resultPrice.String(), orderId)
list = append(list, qData4)
} else {
qData5 := orders.CreatBotUserStockOptionInrLog(ctx, userId, flags.ChangeInto, flags.OptionInrBasicUnit, resultPrice.String(), orderId)
list = append(list, qData5)
}
// 平仓(批量写入交易订单日志信息)
if err = Uo.CreatBotUserStockOptionInrLogList(session, list); err != nil {
applogger.Error("%v OptionInrClosingOrder.CreatBotUserStockOptionInrLogList:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v OptionInrClosingOrder.Commit:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
return nil
}
// UpdateBotStockOptionInrCancelByOrderId
//
// @Description: 期权-印度股撤单
// @param ctx
// @param db
// @param orderId
// @return bool
// @return error
func UpdateBotStockOptionInrCancelByOrderId(ctx context.Context, db *xorm.EngineGroup, orderId string) (bool, error) {
session := db.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.Begin:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
// 订单信息
stockTrade, err := Uo.GetBotStockOptionInrTradeByOrderId(session, orderId)
if err != nil || stockTrade == nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.GetBotStockOptionInrTradeByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
userId := int64(stockTrade.UserId) // 用户Id
stockId := stockTrade.StockId // 订单仓位量
marketMoneyOld := decimal.RequireFromString(stockTrade.MarketMoney) // 订单保证金
serviceCostOld := decimal.RequireFromString(stockTrade.ServiceCost) // 订单手续费
orderNumber := decimal.RequireFromString(stockTrade.OrderNumber) // 订单仓位量
// 用户资产信息
var usable, frozen, usableNo, frozenNo decimal.Decimal
stockIdr, err := Uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit)
if err != nil || stockIdr == nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
usable = decimal.RequireFromString(stockIdr.UsableNum) // 用户账户可用资产
frozen = decimal.RequireFromString(stockIdr.FrozenNum) // 用户账户冻结资产
// 检查用户订单撤单资产
if usable.IsNegative() || frozen.IsNegative() {
return false, flags.ErrShareThree
}
if stockId != flags.OptionInrBasicUnit {
stockFUsd, err := Uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, stockId)
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return false, flags.ErrPublicFour
}
usableNo = decimal.RequireFromString(stockFUsd.UsableNum) // 用户非账户可用资产
frozenNo = decimal.RequireFromString(stockFUsd.FrozenNum) // 用户非账户冻结资产
}
if !flags.CheckSetting {
applogger.Debug("用户原始可用资金:%v", usable)
applogger.Debug("用户原始冻结资金:%v", frozen)
applogger.Debug("用户原始可用非资金:%v", usableNo)
applogger.Debug("用户原始冻结非资金:%v", frozenNo)
}
// 更新订单信息
botStockTrade := orders.BotStockOptionInrCancelByOrderId(ctx)
if err = Uo.UpdateBotStockOptionInrTradeByOrderId(session, orderId, botStockTrade); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.UpdateBotStockOptionInrTradeByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
totalMoney := marketMoneyOld.Add(serviceCostOld) // 用户订单总金额
usableNew := usable.Add(totalMoney) // 用户总资产
frozenNew := frozen.Sub(totalMoney) // 用户冻结资产
usableNoNew := usableNo.Sub(orderNumber) // 用户非可用资产
frozenNoNew := frozenNo // 用户非冻结资产
// 检查用户订单撤单资产
if frozenNew.IsNegative() || usableNoNew.IsNegative() {
return false, flags.ErrShareThree
}
if !flags.CheckSetting {
applogger.Debug("用户撤单可用总资产:%v", usableNew)
applogger.Debug("用户撤单冻结总资产:%v", frozenNew)
applogger.Debug("用户非撤单可用总资产:%v", usableNoNew)
applogger.Debug("用户非撤单冻结总资产:%v", frozenNoNew)
}
// 更新资产信息和非资产信息
userOptionInr := orders.UpdateBotUserStockOptionInr(ctx, usableNew.String(), frozenNew.String())
if err = Uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit, userOptionInr); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.OptionInrBasicUnit:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
if stockId != flags.OptionInrBasicUnit {
userStockFIdn := orders.UpdateBotUserStockOptionInr(ctx, usableNoNew.String(), frozenNoNew.String())
if err = Uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, stockId, userStockFIdn); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.Opi.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
}
// 删除订单缓存列表
if err = Reds.HDel(context.Background(), setting.MarketOptionInrEntrust, orderId).Err(); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.HDel:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
// 更新用户订阅订单缓存列表
userSubKey := virtual.OrderIdListKey(setting.OptionInrSubscribe, userId)
if err = option.UpdateOptionInrHashByOrderId(Reds, orderId, userSubKey, flags.Cancel); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.UpdateOptionInrHashByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
// 更新管理员订阅订单缓存列表
if err = UpdateOptionInrSubscribeHashStatusByOrderId(orderId, setting.AdminOptionInrSubscribe, flags.Cancel, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.AdminOptionInrSubscribe:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.Commit:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
return true, nil
}
// UpdateBotStockOptionInrCancelByOrderId
//
// @Description: 期权-印度股撤单
// @receiver uo
// @param ctx
// @param orderId
// @return bool
// @return error
func (uo *userOrderRepo) UpdateBotStockOptionInrCancelByOrderId(ctx context.Context, orderId string) (bool, error) {
session := uo.data.mysqlDB.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.Begin:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
// 订单信息
stockTrade, err := uo.GetBotStockOptionInrTradeByOrderId(session, orderId)
if err != nil || stockTrade == nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.GetBotStockOptionInrTradeByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
userId := int64(stockTrade.UserId) // 用户Id
stockId := stockTrade.StockId // 订单交易对
marketMoneyOld := decimal.RequireFromString(stockTrade.MarketMoney) // 订单保证金
serviceCostOld := decimal.RequireFromString(stockTrade.ServiceCost) // 订单手续费
orderNumber := decimal.RequireFromString(stockTrade.OrderNumber) // 订单仓位量
// 用户资产信息
var usable, frozen, usableNo, frozenNo decimal.Decimal
stockIdr, err := uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit)
if err != nil || stockIdr == nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
usable = decimal.RequireFromString(stockIdr.UsableNum) // 用户账户可用资产
frozen = decimal.RequireFromString(stockIdr.FrozenNum) // 用户账户冻结资产
// 检查用户订单撤单资产
if usable.IsNegative() || frozen.IsNegative() {
return false, flags.ErrShareThree
}
if stockId != flags.OptionInrBasicUnit {
stockFUsd, err := uo.GetBotUserStockOptionInrByUserIdAndStockId(session, userId, stockId)
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.GetBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return false, flags.ErrPublicFour
}
usableNo = decimal.RequireFromString(stockFUsd.UsableNum) // 用户非账户可用资产
frozenNo = decimal.RequireFromString(stockFUsd.FrozenNum) // 用户非账户冻结资产
}
if !flags.CheckSetting {
applogger.Debug("用户原始可用资金:%v", usable)
applogger.Debug("用户原始冻结资金:%v", frozen)
applogger.Debug("用户原始可用非资金:%v", usableNo)
applogger.Debug("用户原始冻结非资金:%v", frozenNo)
}
// 更新订单信息
botStockTrade := orders.BotStockOptionInrCancelByOrderId(ctx)
if err = uo.UpdateBotStockOptionInrTradeByOrderId(session, orderId, botStockTrade); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.UpdateBotStockOptionInrTradeByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
// Operational Spot Asset Table
totalMoney := marketMoneyOld.Add(serviceCostOld) // 订单总金额
// update total asset data
usableNew := usable.Add(totalMoney) // 用户总资产
frozenNew := frozen.Sub(totalMoney) // 用户冻结资产
usableNoNew := usableNo.Sub(orderNumber) // 用户非可用资产
frozenNoNew := frozenNo // 用户非冻结资产
// 检查用户订单撤单资产
if frozenNew.IsNegative() || usableNoNew.IsNegative() {
return false, flags.ErrShareThree
}
if !flags.CheckSetting {
applogger.Debug("用户撤单可用总资产:%v", usableNew)
applogger.Debug("用户撤单冻结总资产:%v", frozenNew)
applogger.Debug("用户非撤单可用总资产:%v", usableNoNew)
applogger.Debug("用户非撤单冻结总资产:%v", frozenNoNew)
}
// 更新资产信息和非资产信息
userOptionInr := orders.UpdateBotUserStockOptionInr(ctx, usableNew.String(), frozenNew.String())
if err = uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, flags.OptionInrBasicUnit, userOptionInr); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.OptionInrBasicUnit:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
if stockId != flags.OptionInrBasicUnit {
userStockFIdn := orders.UpdateBotUserStockOptionInr(ctx, usableNoNew.String(), frozenNoNew.String())
if err = uo.UpdateBotUserStockOptionInrByUserIdAndStockId(session, userId, stockId, userStockFIdn); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.Opi.UpdateBotUserStockOptionInrByUserIdAndStockId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
}
// 删除订单缓存列表
if err = Reds.HDel(context.Background(), setting.MarketOptionInrEntrust, orderId).Err(); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.HDel:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
// 更新用户订阅订单缓存列表
userSubKey := virtual.OrderIdListKey(setting.OptionInrSubscribe, userId)
if err = option.UpdateOptionInrHashByOrderId(Reds, orderId, userSubKey, flags.Cancel); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.UpdateOptionInrHashByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
// 更新管理员订阅订单缓存列表
if err = UpdateOptionInrSubscribeHashStatusByOrderId(orderId, setting.AdminOptionInrSubscribe, flags.Cancel, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.AdminOptionInrSubscribe:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v UpdateBotStockOptionInrCancelByOrderId.Commit:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
return true, nil
}
// UpdateBotStockOptionInrStopByOrderId
//
// @Description: 期权-印度股设置止盈止损
// @receiver uo
// @param ctx
// @param order
// @return bool
// @return error
func (uo *userOrderRepo) UpdateBotStockOptionInrStopByOrderId(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 UpdateBotStockOptionInrStopByOrderId.NewSession:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
// 设置止盈止损
var userId int64
if order.StopType == 1 {
trade, err := uo.GetBotStockOptionInrTradeByOrderIdOrStatus(session, order.OrderId, flags.PositionStatus)
if err != nil || trade == nil {
applogger.Error("%v UpdateBotStockOptionInrStopByOrderId.GetBotStockOptionInrTradeByOrderIdOrStatus:%v", common.ErrOptionInr, err)
return false, flags.ErrShareFour
}
// 下单信息
checkBool, stopWinPrice, stopLossPrice, err := uo.UpdateVoteStopType(order)
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrStopByOrderId.UpdateVoteStopType:%v", common.ErrOptionInr, err)
return false, err
}
if !flags.CheckSetting {
applogger.Debug("当前止盈止损数据:%v,%v,%v", checkBool, stopWinPrice, stopLossPrice)
}
// 下单判定设置(限价|市价|开仓价)
limitOrMarketPrice, err := uo.UpdateOptionInrVoteDealType(trade)
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrStopByOrderId.UpdateOptionInrVoteDealType:%v", common.ErrOptionInr, err)
return false, err
}
if !flags.CheckSetting {
applogger.Debug("当前开仓价格:%v", limitOrMarketPrice)
}
// 下单判定设置(买涨|买跌)
bid := decimal.RequireFromString(trade.Bid)
ask := decimal.RequireFromString(trade.Ask)
userId = int64(trade.UserId)
if err = uo.VoteTradeTypeOption(int64(trade.DealType), int64(trade.TradeType), int64(trade.TradingType), stopWinPrice, stopLossPrice, limitOrMarketPrice, bid, ask, checkBool); err != nil {
applogger.Error("%v UpdateBotStockOptionInrStopByOrderId.VoteTradeType:%v", common.ErrOptionInr, err)
return false, err
}
} else {
order.StopLossPrice = decimal.Zero.String()
order.StopWinPrice = decimal.Zero.String()
}
// TODO: 获取存在IPO未支付订单的用户
userIdMap, _ := GetBotUserArrears(ctx)
_, ok := userIdMap[userId]
if ok {
return false, flags.ErrPublicServe
}
// 更新订单表
botStockIdnTrade := orders.BotStockOptionInrStopByOrderId(ctx, order)
if err = uo.UpdateBotStockOptionInrTradeByOrderId(session, order.OrderId, botStockIdnTrade); err != nil {
applogger.Error("%v UpdateBotStockOptionInrStopByOrderId.UpdateBotStockOptionInrTradeByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
// 修改挂单缓存队列
if err = option.UpdateOptionInrStopByOrderId(Reds, order, setting.MarketOptionInrPosition, flags.Position); err != nil {
applogger.Error("%v UpdateBotStockOptionInrStopByOrderId.UpdateOptionInrStopByOrderId:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
if err = session.Commit(); err != nil {
applogger.Error("%v UpdateBotStockOptionInrStopByOrderId.Commit:%v", common.ErrOptionInr, err)
return false, flags.ErrMySqlDB
}
return true, nil
}
// UpdateBotStockOptionInrClosingByOrderId
//
// @Description: 期权-印度股用户平仓
// @receiver uo
// @param ctx
// @param orderId
// @return bool
// @return error
func (uo *userOrderRepo) UpdateBotStockOptionInrClosingByOrderId(ctx context.Context, orderId string) (bool, error) {
// 下单判定-冷静期停止所有交易
if CheckGlobalTread(flags.OpiMarket) {
return false, flags.ErrStopTread
}
// 1、查询持仓缓存列表数据并清理对应缓存
var entrustCache *option.OptionInrTallyCache
entrust, err := Reds.HGet(context.Background(), setting.MarketOptionInrPosition, orderId).Result()
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrClosingByOrderId.HGet:%v", common.ErrOptionInr, err)
return false, flags.ErrShareFive
}
var entrustJson option.OptionInrTallyCache
if err = json.Unmarshal([]byte(entrust), &entrustJson); err != nil {
applogger.Error("%v UpdateBotStockOptionInrClosingByOrderId.Unmarshal:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
if orderId == entrustJson.OrderId {
entrustCache = &entrustJson
}
if entrustCache == nil {
return false, flags.ErrShareFive
}
// TODO: 获取存在IPO未支付订单的用户
userIdMap, _ := GetBotUserArrears(ctx)
_, isOk := userIdMap[entrustJson.UserId]
if isOk {
return false, flags.ErrPublicServe
}
// 2、获取当前最新价格
var closingPrice decimal.Decimal
closingPrice, err = uo.GetOptionInrTheLatestPrice(entrustJson)
if err != nil || closingPrice.IsZero() {
applogger.Error("%v UpdateBotStockOptionInrClosingByOrderId.GetOptionInrTheLatestPrice:%v", common.ErrOptionInr, err)
return false, flags.ErrStopTread
}
// 3、平仓处理
if err = OptionInrClosingOrder(ctx, Msql, orderId, closingPrice.String(), entrustCache.Order); err != nil {
applogger.Error("%v UpdateBotStockOptionInrClosingByOrderId.OptionInrClosingOrder:%v", common.ErrOptionInr, err)
return false, err
}
// 4、清理持仓订单信息
var removedCount int64
removedCount, err = Reds.HDel(context.Background(), setting.MarketOptionInrPosition, orderId).Result()
if err != nil || removedCount == 0 {
applogger.Error("%v UpdateBotStockOptionInrClosingByOrderId.HDel:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
// 5、平仓更新订阅缓存订单状态
userSubKey := virtual.OrderIdListKey(setting.OptionInrSubscribe, entrustCache.UserId)
if err = UpdateOptionInrSubscribeHashStatusByOrderId(orderId, userSubKey, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotStockOptionInrClosingByOrderId.OptionInrSubscribe:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
// 6、平仓更新管理订阅缓存订单状态
if err = UpdateOptionInrSubscribeHashStatusByOrderId(orderId, setting.AdminOptionInrSubscribe, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotStockOptionInrClosingByOrderId.AdminOptionInrSubscribe:%v", common.ErrOptionInr, err)
return false, flags.ErrCacheDB
}
return true, nil
}
// UpdateBotStockOptionInrAllClosingByOrderId
//
// @Description: 期权-印度股用户一键平仓
// @receiver uo
// @param ctx
// @param userId
// @return error
func (uo *userOrderRepo) UpdateBotStockOptionInrAllClosingByOrderId(ctx context.Context, userId int64) error {
// 下单判定-冷静期停止所有交易
if CheckGlobalTread(flags.OpiMarket) {
return flags.ErrStopTread
}
// TODO: 获取存在IPO未支付订单的用户
userIdMap, _ := GetBotUserArrears(ctx)
_, isOk := userIdMap[userId]
if isOk {
return flags.ErrPublicServe
}
// 1、查询当前用户所有订单
orderMap, err := uo.GetBotStockOptionInrTradeByUserId(userId)
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.GetBotStockOptionInrTradeByUserId:%v", common.ErrOptionInr, err)
return flags.ErrMySqlDB
}
// 2、查询持仓缓存列表数据-1、清理持仓列表缓存 2、处理平仓数据 3、清理订单ID缓存列表
entrust, err := Reds.HGetAll(context.Background(), setting.MarketOptionInrPosition).Result()
if err != nil {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.HGetAll:%v", common.ErrOptionInr, err)
return flags.ErrShareFive
}
// 3、循环检查股票交易对最新价格(有一个查询不到既不能平仓)
var entrustList []option.OptionInrTallyCache
closePriceMap := make(map[string]decimal.Decimal)
for key, value := range entrust {
var entrustJson option.OptionInrTallyCache
if err = json.Unmarshal([]byte(value), &entrustJson); err != nil {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.Unmarshal:%v", common.ErrOptionInr, err)
return flags.ErrStopTread
}
_, ok := orderMap[entrustJson.OrderId]
if ok {
var closingPrice decimal.Decimal
closingPrice, err = uo.GetOptionInrTheLatestPrice(entrustJson)
if err != nil || closingPrice.IsZero() {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.GetOptionInrTheLatestPrice:%v", common.ErrOptionInr, err)
return flags.ErrStopTread
}
if !flags.CheckSetting {
applogger.Debug("订阅Key:%v,最新价格:%v,订单ID:%v", entrustJson.Symbol, closingPrice, entrustJson.OrderId)
}
closePriceMap[key] = closingPrice
entrustList = append(entrustList, entrustJson)
}
}
// 4、股票一键平仓
for _, value := range entrustList {
closePrice, ok := closePriceMap[value.OrderId]
if ok {
// 平仓
if err = OptionInrClosingOrder(ctx, uo.data.mysqlDB, value.OrderId, closePrice.String(), value.Order); err != nil {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.OptionInrClosingOrder:%v", common.ErrOptionInr, err)
return err
}
// 清理持仓缓存列表
var removedCount int64
removedCount, err = Reds.HDel(context.Background(), setting.MarketOptionInrPosition, value.OrderId).Result()
if err != nil || removedCount == 0 {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.HDel:%v", common.ErrOptionInr, err)
return flags.ErrCacheDB
}
// 平仓更新用户订阅订单状态
userSubKey := virtual.OrderIdListKey(setting.OptionInrSubscribe, value.UserId)
if err = UpdateOptionInrSubscribeHashStatusByOrderId(value.OrderId, userSubKey, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.OptionInrSubscribe:%v", common.ErrOptionInr, err)
return flags.ErrCacheDB
}
// 平仓更新管理员订阅订单状态
if err = UpdateOptionInrSubscribeHashStatusByOrderId(value.OrderId, setting.AdminOptionInrSubscribe, flags.Close, flags.SetNull); err != nil {
applogger.Error("%v UpdateBotStockOptionInrAllClosingByOrderId.AdminShareInrSubscribe:%v", common.ErrOptionInr, err)
return flags.ErrCacheDB
}
}
}
return nil
}
// GetBotStockOptionInrTradeByUserId
//
// @Description:
// @receiver uo
// @param ctx
// @param userId
// @return map[string]string
// @return error
func (uo *userOrderRepo) GetBotStockOptionInrTradeByUserId(userId int64) (map[string]string, error) {
var botStockOptionInrTrade []models.BotStockOptionInrTrade
if err := uo.data.mysqlDB.Table(flags.BotStockOptionInrTrade).
Where("user_id = ?", userId).
Where("status = 1").
Find(&botStockOptionInrTrade); err != nil {
return nil, err
}
botMap := make(map[string]string)
for _, value := range botStockOptionInrTrade {
botMap[value.OrderId] = value.OrderId
}
return botMap, nil
}
// GetOptionInrTheLatestPrice
//
// @Description:
// @receiver uo
// @param subKey
// @param tradeType
// @return decimal.Decimal
// @return error
func (uo *userOrderRepo) GetOptionInrTheLatestPrice(order option.OptionInrTallyCache) (decimal.Decimal, error) {
closePrice := decimal.Zero
_, bid, ask, err := GetOptionInrPrice(order.Order.StockCode)
if err != nil {
return closePrice, err
}
// 平仓
// buy-call 和 buy-put 平仓价格以买一价(bid)成交
// sell-call 和 sell-put 平仓价以卖一价(ask)成交
switch order.Order.TradeType {
case flags.OptionCalls: // call
switch order.Order.TradingType {
case flags.OptionBuy: // call - buy
if !bid.IsZero() {
closePrice = bid
}
case flags.OptionSell: // call - sell
if !ask.IsZero() {
closePrice = ask
}
default:
}
case flags.OptionPuts: // put
switch order.Order.TradingType {
case flags.OptionBuy: // put - buy
if !bid.IsZero() {
closePrice = bid
}
case flags.OptionSell: // put - sell
if !ask.IsZero() {
closePrice = ask
}
default:
}
}
return closePrice, nil
}
// GetBotStockOptionInrTradeByOrderId
//
// @Description:
// @receiver uo
// @param session
// @param orderId
// @return *models.BotStockOptionInrTrade
// @return error
func (uo *userOrderRepo) GetBotStockOptionInrTradeByOrderId(session *xorm.Session, orderId string) (*models.BotStockOptionInrTrade, error) {
var botStockOptionInrTrade []models.BotStockOptionInrTrade
if err := session.Table(flags.BotStockOptionInrTrade).
Where("order_id = ?", orderId).
Find(&botStockOptionInrTrade); err != nil {
return nil, err
}
for _, value := range botStockOptionInrTrade {
return &value, nil
}
return nil, nil
}
// GetBotUserStockOptionInrByUserIdAndStockId
//
// @Description:
// @receiver uo
// @param session
// @param userId
// @param stockId
// @return *models.BotUserStockOptionInr
// @return error
func (uo *userOrderRepo) GetBotUserStockOptionInrByUserIdAndStockId(session *xorm.Session, userId int64, stockId string) (*models.BotUserStockOptionInr, error) {
var stockOptionInr []models.BotUserStockOptionInr
if err := session.Table(flags.BotUserStockOptionInr).
Where("user_id = ?", userId).
Where("stock_id = ?", stockId).
Find(&stockOptionInr); err != nil || len(stockOptionInr) <= 0 {
return nil, err
}
for _, value := range stockOptionInr {
return &value, nil
}
return nil, nil
}
// UpdateBotStockOptionInrTradeByOrderId
//
// @Description:
// @receiver uo
// @param session
// @param orderId
// @param stock
// @return error
func (uo *userOrderRepo) UpdateBotStockOptionInrTradeByOrderId(session *xorm.Session, orderId string, stock models.BotStockOptionInrTrade) error {
checkInt, err := session.Table(flags.BotStockOptionInrTrade).
Where("order_id = ?", orderId).
Update(&stock)
if err != nil || checkInt < 0 {
return err
}
return nil
}
// CreateBotStockOptionInrTrade
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param trade
// @return error
func (uo *userOrderRepo) CreateBotStockOptionInrTrade(session *xorm.Session, trade models.BotStockOptionInrTrade) error {
_, err := session.Table(flags.BotStockOptionInrTrade).Insert(&trade)
if err != nil {
return err
}
return nil
}
// CreateBotUserStockOptionInrFIRD
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @param stock
// @return error
func (uo *userOrderRepo) CreateBotUserStockOptionInrFIRD(session *xorm.Session, stock models.BotUserStockOptionInr) error {
_, err := session.Table(flags.BotUserStockOptionInr).Insert(&stock)
if err != nil {
return err
}
return nil
}
// UpdateBotUserStockOptionInrByUserIdAndStockId
//
// @Description:
// @receiver uo
// @param session
// @param userId
// @param stockId
// @param stock
// @return error
func (uo *userOrderRepo) UpdateBotUserStockOptionInrByUserIdAndStockId(session *xorm.Session, userId int64, stockId string, stock models.BotUserStockOptionInr) error {
checkNum, err := session.Table(flags.BotUserStockOptionInr).
Where("user_id = ?", userId).
Where("stock_id = ?", stockId).
Update(&stock)
if err != nil || checkNum < 0 {
return err
}
return nil
}
// OptionInrVoteDealType
//
// @Description:
// @receiver uo
// @param ctx
// @param order
// @return decimal.Decimal
// @return error
func (uo *userOrderRepo) OptionInrVoteDealType(order structure.ShareOrder) (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
}
// UpdateOptionInrVoteDealType
//
// @Description:
// @receiver uo
// @param ctx
// @param order
// @return decimal.Decimal
// @return error
func (uo *userOrderRepo) UpdateOptionInrVoteDealType(order *models.BotStockOptionInrTrade) (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
}
// VerifyBotStockOptionInrTradeOrderId
//
// @Description:
// @receiver uo
// @param ctx
// @param session
// @return string
// @return error
func (uo *userOrderRepo) VerifyBotStockOptionInrTradeOrderId(session *xorm.Session) (string, error) {
var orderId string
for {
// 生成订单ID
orderId = orders.CreateRandCodeOrder(10)
var orderList []models.BotStockOptionInrTrade
err := session.Table(flags.BotStockOptionInrTrade).
Where("order_id = ?", orderId).
Find(&orderList)
if err != nil || len(orderList) > 0 {
applogger.Warn("%v VerifyBotStockOptionInrTradeOrderId.Find:%v", common.ErrOptionInr, err)
continue
}
break
}
return orderId, nil
}
// GetBotStockOptionInrTradeByOrderIdOrStatus
//
// @Description:
// @receiver uo
// @param session
// @param orderId
// @param status
// @return *models.BotStockOptionInrTrade
// @return error
func (uo *userOrderRepo) GetBotStockOptionInrTradeByOrderIdOrStatus(session *xorm.Session, orderId string, status int) (*models.BotStockOptionInrTrade, error) {
var botStockOptionInrTrade []models.BotStockOptionInrTrade
if err := session.Table(flags.BotStockOptionInrTrade).
Where("order_id = ?", orderId).
Where("`status` = ?", status).
Find(&botStockOptionInrTrade); err != nil {
return nil, err
}
for _, value := range botStockOptionInrTrade {
return &value, nil
}
return nil, nil
}