gitee.com/quant1x/engine@v1.8.4/trader/holdingorders.go (about)

     1  package trader
     2  
     3  import (
     4  	"gitee.com/quant1x/exchange"
     5  	"gitee.com/quant1x/gox/api"
     6  	"gitee.com/quant1x/gox/logger"
     7  	"slices"
     8  	"sync"
     9  )
    10  
    11  //// HoldingOrder 持仓订单
    12  //type HoldingOrder struct {
    13  //	AccountType   int     `name:"账户类型" json:"account_type" dataframe:"account_type"`     // 账户类型
    14  //	Date          string  `name:"信号日期" json:"date" dataframe:"date"`                     // 日期
    15  //	AccountId     string  `name:"资金账户" json:"account_id" dataframe:"account_id"`         // 资金账号
    16  //	OrderTime     string  `name:"委托时间" json:"order_time" dataframe:"order_time"`         // 报单时间
    17  //	StockCode     string  `name:"证券代码" json:"stock_code" dataframe:"stock_code"`         // 证券代码, 例如"600000.SH"
    18  //	OrderType     int     `name:"订单类型" json:"order_type" dataframe:"order_type"`         // 委托类型, 23:买, 24:卖
    19  //	Price         float64 `name:"委托价格" json:"price" dataframe:"price"`                   // 报价价格, 如果price_type为指定价, 那price为指定的价格, 否则填0
    20  //	PriceType     int     `name:"报价类型" json:"price_type" dataframe:"price_type"`         // 报价类型, 详见帮助手册
    21  //	OrderVolume   int     `name:"委托量" json:"order_volume" dataframe:"order_volume"`      // 委托数量, 股票以'股'为单位, 债券以'张'为单位
    22  //	OrderId       int     `name:"订单ID" json:"order_id" dataframe:"order_id"`             // 委托编号
    23  //	OrderSysid    string  `name:"合同编号" json:"order_sysid" dataframe:"order_sysid"`       // 柜台编号
    24  //	TradedPrice   float64 `name:"成交均价" json:"traded_price" dataframe:"traded_price"`     // 成交均价
    25  //	TradedVolume  int     `name:"成交数量" json:"traded_volume" dataframe:"traded_volume"`   // 成交数量, 股票以'股'为单位, 债券以'张'为单位
    26  //	OrderStatus   int     `name:"订单状态" json:"order_status" dataframe:"order_status"`     // 委托状态
    27  //	StatusMessage string  `name:"委托状态描述" json:"status_msg" dataframe:"status_message"`   // 委托状态描述, 如废单原因
    28  //	StrategyName  string  `name:"策略名称" json:"strategy_name" dataframe:"strategy_name"`   // 策略名称
    29  //	OrderRemark   string  `name:"委托备注" json:"order_remark" dataframe:"order_remark"`     // 委托备注
    30  //	HoldingPeriod int     `name:"持股周期" json:"holding_period" dataframe:"holding_period"` // 持股周期
    31  //}
    32  //
    33  //// Key 索引字段: 日期/订单类型/策略名称/证券代码
    34  //func (this HoldingOrder) Key() string {
    35  //	return fmt.Sprintf("%s/%d/%s/%s", this.Date, this.OrderType, this.StrategyName, this.StockCode)
    36  //}
    37  
    38  type HoldingPosition struct {
    39  	AccountType     int     `name:"账户类型" dataframe:"account_type"`     // 账户类型
    40  	AccountId       string  `name:"资金账户" dataframe:"account_id"`       // 资金账号
    41  	StockCode       string  `name:"证券代码" dataframe:"stock_code"`       // 证券代码, 例如"600000.SH"
    42  	Volume          int     `name:"持仓数量" dataframe:"volume"`           // 持仓数量,股票以'股'为单位, 债券以'张'为单位
    43  	CanUseVolume    int     `name:"可卖数量" dataframe:"can_use_volume"`   // 可用数量, 股票以'股'为单位, 债券以'张'为单位
    44  	OpenPrice       float64 `name:"开仓价" dataframe:"open_price"`        // 开仓价
    45  	MarketValue     float64 `name:"市值" dataframe:"market_value"`       // 市值
    46  	FrozenVolume    int     `name:"冻结数量" dataframe:"frozen_volume"`    // 冻结数量
    47  	OnRoadVolume    int     `name:"在途股份" dataframe:"on_road_volume"`   // 在途股份
    48  	YesterdayVolume int     `name:"昨夜拥股" dataframe:"yesterday_volume"` // 昨夜拥股
    49  	AvgPrice        float64 `name:"成本价" dataframe:"avg_price"`         // 成本价
    50  	HoldingPeriod   int     `name:"持股周期" dataframe:"holding_period"`   // 持股周期
    51  }
    52  
    53  var (
    54  	//holdingOnce   coroutine.RollingOnce
    55  	holdingOrders []HoldingPosition
    56  	holdingMutex  sync.RWMutex
    57  )
    58  
    59  // 懒加载持仓个股的持股周期
    60  func lazyLoadHoldingOrder() {
    61  	holdingMutex.Lock()
    62  	defer holdingMutex.Unlock()
    63  	methodName := "lazyLoadHoldingOrder"
    64  	// 1. 获取持仓列表
    65  	positions, err := QueryHolding()
    66  	if err != nil {
    67  		logger.Errorf("查询持仓列表失败, error=%+v", err)
    68  		return
    69  	}
    70  	// 过滤掉清仓的个股
    71  	positions = api.Filter(positions, func(detail PositionDetail) bool {
    72  		return detail.Volume > 0
    73  	})
    74  	// 清空缓存
    75  	clear(holdingOrders)
    76  	// 2. 用持仓列表遍历历史订单缓存文件, 补全持仓订单
    77  	dates := GetLocalOrderDates()
    78  	if len(dates) == 0 {
    79  		return
    80  	}
    81  	// 3. 重新评估持仓范围, 有可能存在日期没有成交的可能
    82  	firstDate := dates[0]
    83  	lastTradeDate := exchange.LastTradeDate()
    84  	dates = exchange.TradingDateRange(firstDate, lastTradeDate)
    85  	// 反转日期切片
    86  	slices.Reverse(dates)
    87  	// 4. 用持仓列表遍历历史订单缓存文件, 补全持仓订单
    88  	for _, position := range positions {
    89  		var holding HoldingPosition
    90  		_ = api.Copy(&holding, &position)
    91  		code := position.StockCode
    92  		volume := position.Volume
    93  		// 矫正证券代码
    94  		securityCode := exchange.CorrectSecurityCode(position.StockCode)
    95  		// 历史记录合计买数量
    96  		tmpTradedVolume := 0
    97  		// 最早的持股日期
    98  		earlierDate := lastTradeDate
    99  		// 持股周期
   100  		holdingPeriod := 0
   101  		//if code == "001216.SZ" {
   102  		//	fmt.Println(code)
   103  		//}
   104  		// 从当前日期往前回溯订单
   105  		for _, date := range dates {
   106  			isLastTradeDate := date == lastTradeDate
   107  			// 获取date的订单列表
   108  			orders := GetOrderList(date)
   109  			if len(orders) == 0 && isLastTradeDate {
   110  				// 如果本地缓存订单列表为空, 且是最后一个交易日, 则从券商获取订单列表
   111  				orders, _ = QueryOrders()
   112  			}
   113  			if len(orders) == 0 {
   114  				// 如果订单列表为空, 跳过
   115  				continue
   116  			}
   117  			orders = api.Filter(orders, func(detail OrderDetail) bool {
   118  				return detail.StockCode == code
   119  			})
   120  			if len(orders) == 0 {
   121  				continue
   122  			}
   123  			currentTradedVolume := 0
   124  			for _, order := range orders {
   125  				if order.OrderType != STOCK_BUY && order.OrderType != STOCK_SELL {
   126  					// 如果不是买入和卖出, 忽略
   127  					continue
   128  				}
   129  				plus := 1 // 默认相加
   130  				if order.OrderType == STOCK_SELL {
   131  					// 卖出则减
   132  					plus = -1
   133  				}
   134  				if order.OrderStatus == ORDER_PART_SUCC || order.OrderStatus == ORDER_SUCCEEDED {
   135  					// 部成和已成 , 成交数量累加
   136  					currentTradedVolume += plus * order.TradedVolume
   137  				}
   138  			}
   139  			earlierDate = date
   140  			tmpTradedVolume += currentTradedVolume
   141  			if tmpTradedVolume == volume {
   142  				// 如果订单合计成交量等于持仓量, 则退出
   143  				break
   144  			}
   145  		}
   146  		if tmpTradedVolume != volume {
   147  			// 历史已成买入量和持仓量不一致, 按照持仓逻辑, 会当成持股最后一天来处理
   148  			logger.Errorf("[%s]: 加载(%s)持仓记录异常, 历史委托记录合并持仓量不一致", methodName, securityCode)
   149  		}
   150  		// 计算持股周期
   151  		dateRanges := exchange.TradingDateRange(earlierDate, lastTradeDate)
   152  		holdingPeriod = len(dateRanges) - 1
   153  		holding.HoldingPeriod = holdingPeriod
   154  		holdingOrders = append(holdingOrders, holding)
   155  	}
   156  	// 5. 对于非策略订单的处理
   157  }
   158  
   159  // GetHoldingPeriodList 获取持仓周期列表
   160  func GetHoldingPeriodList() []HoldingPosition {
   161  	lazyLoadHoldingOrder()
   162  	return holdingOrders
   163  }