|
|
@@ -1,15 +1,376 @@
|
|
|
package ermcp
|
|
|
|
|
|
-import "github.com/gin-gonic/gin"
|
|
|
+import (
|
|
|
+ "mtp2_if/global/app"
|
|
|
+ "mtp2_if/global/e"
|
|
|
+ "mtp2_if/logger"
|
|
|
+ "mtp2_if/models"
|
|
|
+ "mtp2_if/utils"
|
|
|
+ "net/http"
|
|
|
|
|
|
+ "github.com/gin-gonic/gin"
|
|
|
+)
|
|
|
+
|
|
|
+// QueryErmcpTradePositionReq 获取企业风管期货持仓头寸信息请求参数
|
|
|
type QueryErmcpTradePositionReq struct {
|
|
|
AccountID int `form:"accountID" binding:"required"`
|
|
|
+ MarketID int `form:"marketID"`
|
|
|
}
|
|
|
|
|
|
+// QueryErmcpTradePositionRsp 获取企业风管期货持仓头寸信息返回模型
|
|
|
type QueryErmcpTradePositionRsp struct {
|
|
|
- Goodsname string `json:"goodsname"` // 商品名称
|
|
|
+ Goodsname string `json:"goodsname"` // 商品名称
|
|
|
+ BuyOrSell int64 `json:"buyorsell"` // 方向 - 0:买 1:卖
|
|
|
+ EnableQTY int64 `json:"enableqty"` // 可用量(总仓可用)
|
|
|
+ CurPositionQTY int64 `json:"curpositionqty"` // 当前持仓总数量(总仓数量)
|
|
|
+ Last float64 `json:"last"` // 现价
|
|
|
+ CurHolderAmount float64 `json:"curholderamount" ` // 当前持仓总金额[商品币种](成本???)
|
|
|
+ PositionPL float64 `json:"positionpl"` // 持仓盈亏
|
|
|
+ PositionPLRate float64 `json:"positionplrate"` // 持仓盈亏率
|
|
|
+ CurTDPosition int64 `json:"curtdposition"` // 期末今日头寸(今仓数量)
|
|
|
+ CurTDPositionEnabled int64 `json:"curtdpositionenabled"` // 今仓可用
|
|
|
+ AveragePrice float64 `json:"averageprice"` // 持仓均价
|
|
|
+ UsedMargin float64 `json:"usedmargin"` // 占用保证金[商品币种]
|
|
|
+ ExExehangeName string `json:"exexchangename"` // 外部交易所名称(简称)
|
|
|
+
|
|
|
+ Goodsid int64 `json:"goodsid"` // 商品ID(自增ID SEQ_GOODS)
|
|
|
+ Goodscode string `json:"goodscode"` // 商品代码(内部)
|
|
|
+ Outgoodscode string `json:"outgoodscode"` // 商品代码(外部)
|
|
|
+ Agreeunit float64 `json:"agreeunit"` // 合约单位
|
|
|
+ Decimalplace int64 `json:"decimalplace"` // 报价小数位
|
|
|
}
|
|
|
|
|
|
+// QueryErmcpTradePosition 获取企业风管期货持仓头寸信息
|
|
|
+// @Summary 获取企业风管期货持仓头寸信息
|
|
|
+// @Produce json
|
|
|
+// @Security ApiKeyAuth
|
|
|
+// @Param accountID query int true "资金账户ID"
|
|
|
+// @Param marketID query int false "所属市场ID"
|
|
|
+// @Success 200 {object} QueryErmcpTradePositionRsp
|
|
|
+// @Failure 500 {object} app.Response
|
|
|
+// @Router /Ermcp/QueryErmcpTradePosition [get]
|
|
|
+// @Tags 企业风险管理(app)
|
|
|
func QueryErmcpTradePosition(c *gin.Context) {
|
|
|
+ appG := app.Gin{C: c}
|
|
|
+
|
|
|
+ // 获取请求参数
|
|
|
+ var req QueryErmcpTradePositionReq
|
|
|
+ if err := appG.C.ShouldBindQuery(&req); err != nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ rsp := make([]QueryErmcpTradePositionRsp, 0)
|
|
|
+
|
|
|
+ // 获取资金账户信息
|
|
|
+ taAccount, err := models.GetTaAccountByID(req.AccountID)
|
|
|
+ if err != nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if taAccount == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 母账户要从HEDGE_OUTTRADEPOSITION表查询持仓头寸(未生成内部头寸);子账户从TRADEPOSITION表获持仓头寸
|
|
|
+ if taAccount.Ismain == 1 {
|
|
|
+ // 母账户
|
|
|
+ hedgeOutTradePositions, err := models.GetHedgeOutTradePositions(req.AccountID, req.MarketID)
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 分解买卖方向的头寸
|
|
|
+ for _, v := range hedgeOutTradePositions {
|
|
|
+ // 分解买卖方向的头寸
|
|
|
+ if v.Curbuyposition > 0 {
|
|
|
+ // 买方向
|
|
|
+ item := QueryErmcpTradePositionRsp{
|
|
|
+ BuyOrSell: 0,
|
|
|
+ EnableQTY: v.Curbuyposition - v.Fretdbuyposition - v.Freydbuyposition, // 买可用 = 期末买头寸 - 冻结今日买头寸 - 冻结上日买头寸
|
|
|
+ CurPositionQTY: v.Curbuyposition,
|
|
|
+ CurHolderAmount: 0, // FIXME: - 缺少数据?
|
|
|
+ CurTDPosition: v.Curtdbuyposition,
|
|
|
+ CurTDPositionEnabled: v.Curtdbuyposition - v.Fretdbuyposition, // 今仓买可用 = 期末今日买头寸 - 冻结今日买头寸
|
|
|
+ UsedMargin: 0, // FIXME: - 缺少数据?
|
|
|
+
|
|
|
+ Goodsid: v.Hedgegoodsid,
|
|
|
+ }
|
|
|
+ // 获取对应商品信息
|
|
|
+ goods, err := models.GetGoods(int(v.Hedgegoodsid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if goods == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.Goodscode = goods.Goodscode
|
|
|
+ item.Outgoodscode = goods.Outgoodscode
|
|
|
+ item.Goodsname = goods.Goodsname
|
|
|
+ item.Agreeunit = goods.Agreeunit
|
|
|
+ // 获取对应外部交易所名称
|
|
|
+ exchange, err := models.GetExternalexchangeByGoodsGroupID(int(goods.Goodsgroupid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if exchange == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.ExExehangeName = exchange.Exchangefullname
|
|
|
+ // 获取对应商品盘面信息
|
|
|
+ quoteDays, err := models.GetQuoteDays("'" + goods.Outgoodscode + "'")
|
|
|
+ if err != nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if len(quoteDays) > 0 {
|
|
|
+ if quoteDays[0].Last != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Last), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ // 没有现价则尝试用昨结
|
|
|
+ if item.Last == 0 && quoteDays[0].Presettle != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Presettle), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // FIXME: - 目前外部持仓头寸表里没有相关金额的字段,等相关服务处理
|
|
|
+
|
|
|
+ rsp = append(rsp, item)
|
|
|
+ }
|
|
|
+ if v.Cursellposition > 0 {
|
|
|
+ // 卖方向
|
|
|
+ item := QueryErmcpTradePositionRsp{
|
|
|
+ BuyOrSell: 1,
|
|
|
+ EnableQTY: v.Cursellposition - v.Fretdsellposition - v.Freydsellposition,
|
|
|
+ CurPositionQTY: v.Cursellposition,
|
|
|
+ CurHolderAmount: 0, // FIXME: - 缺少数据?
|
|
|
+ CurTDPosition: v.Curtdsellposition,
|
|
|
+ CurTDPositionEnabled: v.Curtdsellposition - v.Fretdsellposition,
|
|
|
+ UsedMargin: 0, // FIXME: - 缺少数据?
|
|
|
+
|
|
|
+ Goodsid: v.Hedgegoodsid,
|
|
|
+ }
|
|
|
+ // 获取对应商品信息
|
|
|
+ goods, err := models.GetGoods(int(v.Hedgegoodsid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if goods == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.Goodscode = goods.Goodscode
|
|
|
+ item.Outgoodscode = goods.Outgoodscode
|
|
|
+ item.Goodsname = goods.Goodsname
|
|
|
+ item.Agreeunit = goods.Agreeunit
|
|
|
+ // 获取对应外部交易所名称
|
|
|
+ exchange, err := models.GetExternalexchangeByGoodsGroupID(int(goods.Goodsgroupid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if exchange == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.ExExehangeName = exchange.Exchangefullname
|
|
|
+ // 获取对应商品盘面信息
|
|
|
+ quoteDays, err := models.GetQuoteDays("'" + goods.Outgoodscode + "'")
|
|
|
+ if err != nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if len(quoteDays) > 0 {
|
|
|
+ if quoteDays[0].Last != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Last), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ // 没有现价则尝试用昨结
|
|
|
+ if item.Last == 0 && quoteDays[0].Presettle != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Presettle), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // FIXME: - 目前外部持仓头寸表里没有相关金额的字段,等相关服务处理
|
|
|
+
|
|
|
+ rsp = append(rsp, item)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 子账户
|
|
|
+ tradePositions, err := models.GetTradePositions(req.AccountID, req.MarketID)
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 分解买卖方向的头寸
|
|
|
+ for _, v := range tradePositions {
|
|
|
+ if v.Buycurpositionqty > 0 {
|
|
|
+ // 买方向
|
|
|
+ item := QueryErmcpTradePositionRsp{
|
|
|
+ BuyOrSell: 0,
|
|
|
+ EnableQTY: v.Buycurpositionqty - v.Buyfrozenqty, // 买可用 = 买当前持仓总数量 - 买持仓冻结数量
|
|
|
+ CurPositionQTY: v.Buycurpositionqty,
|
|
|
+ CurHolderAmount: v.Buycurholderamount,
|
|
|
+ CurTDPosition: v.Buycurtdposition,
|
|
|
+ CurTDPositionEnabled: v.Buycurtdposition - v.Buyfretdposition, // 今仓买可用 = 买期末今日头寸 - 买冻结今日头寸
|
|
|
+ UsedMargin: v.Usedmargin,
|
|
|
+
|
|
|
+ Goodsid: int64(v.Goodsid),
|
|
|
+ }
|
|
|
+ // 获取对应商品信息
|
|
|
+ goods, err := models.GetGoods(int(v.Goodsid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if goods == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.Goodscode = goods.Goodscode
|
|
|
+ item.Outgoodscode = goods.Outgoodscode
|
|
|
+ item.Goodsname = goods.Goodsname
|
|
|
+ item.Agreeunit = goods.Agreeunit
|
|
|
+ // 获取对应外部交易所名称
|
|
|
+ exchange, err := models.GetExternalexchangeByGoodsGroupID(int(goods.Goodsgroupid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if exchange == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.ExExehangeName = exchange.Exchangefullname
|
|
|
+ // 获取对应商品盘面信息
|
|
|
+ quoteDays, err := models.GetQuoteDays("'" + goods.Outgoodscode + "'")
|
|
|
+ if err != nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if len(quoteDays) > 0 {
|
|
|
+ if quoteDays[0].Last != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Last), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ // 没有现价则尝试用昨结
|
|
|
+ if item.Last == 0 && quoteDays[0].Presettle != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Presettle), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 计算持仓均价 = 买当前持仓总金额 / 买当前持仓总数量 / 合约单位
|
|
|
+ item.AveragePrice = v.Buycurholderamount / float64(v.Buycurpositionqty) / goods.Agreeunit
|
|
|
+ // 计算持仓盈亏:买方向持仓盈亏 = (最新价 - 持仓价) * 数量 * 合约单位
|
|
|
+ // 知识点:交易服务在结算后,会按结算价更新持仓单金额,所以持仓均价就是持仓价(结算价)
|
|
|
+ // 知识点:持仓盈亏是今日浮动盈亏,针对结算价进行计算;平仓盈亏是针对建仓价进行计算
|
|
|
+ // if item.Last == 0 {
|
|
|
+ // item.PositionPL = 0
|
|
|
+ // } else {
|
|
|
+ // item.PositionPL = (item.Last - item.AveragePrice) * float64(v.Buycurpositionqty) * goods.Agreeunit
|
|
|
+ // }
|
|
|
+ // FIXME: - 与老邓及王旭讨论过后,确认后期头寸相关价格会由交易服务落地,目前暂不进行计算
|
|
|
+ }
|
|
|
+ if v.Sellcurpositionqty > 0 {
|
|
|
+ // 卖方向
|
|
|
+ item := QueryErmcpTradePositionRsp{
|
|
|
+ BuyOrSell: 0,
|
|
|
+ EnableQTY: v.Sellcurpositionqty - v.Sellfrozenqty,
|
|
|
+ CurPositionQTY: v.Sellcurpositionqty,
|
|
|
+ CurHolderAmount: v.Sellcurholderamount,
|
|
|
+ CurTDPosition: v.Sellcurtdposition,
|
|
|
+ CurTDPositionEnabled: v.Sellcurtdposition - v.Sellfretdposition,
|
|
|
+ UsedMargin: v.Usedmargin,
|
|
|
+
|
|
|
+ Goodsid: int64(v.Goodsid),
|
|
|
+ }
|
|
|
+ // 获取对应商品信息
|
|
|
+ goods, err := models.GetGoods(int(v.Goodsid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if goods == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.Goodscode = goods.Goodscode
|
|
|
+ item.Outgoodscode = goods.Outgoodscode
|
|
|
+ item.Goodsname = goods.Goodsname
|
|
|
+ item.Agreeunit = goods.Agreeunit
|
|
|
+ // 获取对应外部交易所名称
|
|
|
+ exchange, err := models.GetExternalexchangeByGoodsGroupID(int(goods.Goodsgroupid))
|
|
|
+ if err != nil {
|
|
|
+ // 查询失败
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if exchange == nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed")
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ item.ExExehangeName = exchange.Exchangefullname
|
|
|
+ // 获取对应商品盘面信息
|
|
|
+ quoteDays, err := models.GetQuoteDays("'" + goods.Outgoodscode + "'")
|
|
|
+ if err != nil {
|
|
|
+ logger.GetLogger().Errorf("QueryErmcpTradePosition failed: %s", err.Error())
|
|
|
+ appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if len(quoteDays) > 0 {
|
|
|
+ if quoteDays[0].Last != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Last), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ // 没有现价则尝试用昨结
|
|
|
+ if item.Last == 0 && quoteDays[0].Presettle != 0 {
|
|
|
+ item.Last = utils.IntToFloat64(int(quoteDays[0].Presettle), int(goods.Decimalplace))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 计算持仓均价 = 买当前持仓总金额 / 买当前持仓总数量 / 合约单位
|
|
|
+ item.AveragePrice = v.Buycurholderamount / float64(v.Buycurpositionqty) / goods.Agreeunit
|
|
|
+ // 计算持仓盈亏:卖方向持仓盈亏 = (持仓价 - 最新价) * 数量 * 合约单位
|
|
|
+ // 知识点:交易服务在结算后,会按结算价更新持仓单金额,所以持仓均价就是持仓价(结算价)
|
|
|
+ // 知识点:持仓盈亏是今日浮动盈亏,针对结算价进行计算;平仓盈亏是针对建仓价进行计算
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ // 查询成功返回
|
|
|
+ logger.GetLogger().Debugln("QueryErmcpTradePosition successed: %v", rsp)
|
|
|
+ appG.Response(http.StatusOK, e.SUCCESS, rsp)
|
|
|
}
|