gitee.com/quant1x/engine@v1.8.4/services/task_sell.go (about)

     1  package services
     2  
     3  import (
     4  	"fmt"
     5  	"gitee.com/quant1x/engine/config"
     6  	"gitee.com/quant1x/engine/factors"
     7  	"gitee.com/quant1x/engine/market"
     8  	"gitee.com/quant1x/engine/models"
     9  	"gitee.com/quant1x/engine/realtime"
    10  	"gitee.com/quant1x/engine/trader"
    11  	"gitee.com/quant1x/exchange"
    12  	"gitee.com/quant1x/gox/logger"
    13  	"gitee.com/quant1x/gox/runtime"
    14  	"gitee.com/quant1x/num"
    15  	"slices"
    16  )
    17  
    18  // 任务 - 卖出117
    19  func jobOneSizeFitsAllSales() {
    20  	updateInRealTime, status := exchange.CanUpdateInRealtime()
    21  	if updateInRealTime && IsTrading(status) {
    22  		cookieCutterSell()
    23  	} else if runtime.Debug() {
    24  		cookieCutterSell()
    25  	}
    26  }
    27  
    28  // 一刀切卖出
    29  func cookieCutterSell() {
    30  	defer runtime.IgnorePanic("")
    31  	sellStrategyCode := models.ModelOneSizeFitsAllSells
    32  	// 1. 获取117号策略(卖出)
    33  	sellRule := config.GetStrategyParameterByCode(sellStrategyCode)
    34  	if sellRule == nil {
    35  		return
    36  	}
    37  	// 2. 判断是否可以指定自动卖出
    38  	if !sellRule.IsCookieCutterForSell() {
    39  		return
    40  	}
    41  	// 3. 判断是否交易日
    42  	if !exchange.DateIsTradingDay() {
    43  		return
    44  	}
    45  	// 3.1 判断是否交易时段
    46  	if !sellRule.Session.IsTrading() {
    47  		return
    48  	}
    49  	// 4. 查询持仓可卖的股票
    50  	positions, err := trader.QueryHolding()
    51  	if err != nil {
    52  		return
    53  	}
    54  	// 5. 确定持股到期的个股列表
    55  	var holdings []string
    56  	for _, position := range positions {
    57  		if position.CanUseVolume < 1 {
    58  			continue
    59  		}
    60  		stockCode := position.StockCode
    61  		securityCode := exchange.CorrectSecurityCode(stockCode)
    62  		holdings = append(holdings, securityCode)
    63  	}
    64  	finalCodeList := CheckoutCanSellStockList(sellStrategyCode, holdings)
    65  	// 6. 遍历持仓
    66  	direction := trader.SELL
    67  	strategyName := sellRule.QmtStrategyName()
    68  	for _, position := range positions {
    69  		orderRemark := sellRule.Flag
    70  		isNeedToSell := false
    71  		// 6.1 如果持仓可卖数据小于1, 继续下一条持仓记录
    72  		if position.CanUseVolume < 1 {
    73  			continue
    74  		}
    75  		// 6.2 对齐证券代码, 检查黑白名单
    76  		stockCode := position.StockCode
    77  		securityCode := exchange.CorrectSecurityCode(stockCode)
    78  		if !trader.CheckForSell(securityCode) {
    79  			// 禁止卖出, 则返回
    80  			logger.Infof("%s[%d]: %s ProhibitForBuying", sellRule.Name, sellRule.Id, securityCode)
    81  			continue
    82  		}
    83  		// 6.3 获取快照
    84  		snapshot := models.GetStrategySnapshot(securityCode)
    85  		if snapshot == nil {
    86  			continue
    87  		}
    88  		// 6.4 现价
    89  		lastPrice := num.Decimal(snapshot.Price)
    90  		// 昨日收盘
    91  		lastClose := num.Decimal(snapshot.LastClose)
    92  		// 6.5 计算涨停价
    93  		limitUp, _ := market.PriceLimit(securityCode, lastClose)
    94  		// 6.6 如果涨停, 则不出
    95  		if lastPrice >= limitUp {
    96  			logger.Infof("%s[%d]: %s LimitUp, skip", sellRule.Name, sellRule.Id, securityCode)
    97  			continue
    98  		}
    99  		// 6.7 持仓成本
   100  		avgPrice := position.OpenPrice
   101  		// 6.8 盈亏比
   102  		floatProfitLossRatio := num.NetChangeRate(avgPrice, lastPrice)
   103  		// 6.9 确定是否规则内最后一天持股
   104  		isFinal := slices.Contains(finalCodeList, securityCode)
   105  		todayLastSession := sellRule.Session.IsTodayLastSession()
   106  		logger.Infof("%s[%d]: %s, profit-loss-ratio=%.02f, last-day=%t, last-session=%t", sellRule.Name, sellRule.Id, securityCode, floatProfitLossRatio, isFinal, todayLastSession)
   107  		// 117. 最后一天持股, 且是最后一个交易时段, 则卖出
   108  		if isFinal && todayLastSession {
   109  			// 卖出
   110  			isNeedToSell = true
   111  			orderRemark = "LASTDAY:P"
   112  			if floatProfitLossRatio > 0 {
   113  				orderRemark = orderRemark + ">0"
   114  			} else if floatProfitLossRatio == 0 {
   115  				orderRemark = orderRemark + "=0"
   116  			} else {
   117  				orderRemark = orderRemark + "<0"
   118  			}
   119  		}
   120  		// 6.10 股价高于前面一天最高价,且大于等于5日线,如果是获利的,卖出
   121  		if !isNeedToSell {
   122  			// 6.10.1 获取历史特征数据
   123  			history := factors.GetL5History(securityCode)
   124  			if history == nil {
   125  				continue
   126  			}
   127  			// 6.10.2 计算5日均线
   128  			ma5 := realtime.IncrementalMovingAverage(history.MA4, 5, lastPrice)
   129  			// 风险收益比(Risk/Reward Ratio)
   130  			orderRemark = "RISK:P"
   131  			// 6.10.3 股价高于前一天最高价, 且站上5日线以及盈利的情况下
   132  			if lastPrice > history.HIGH && lastPrice >= ma5 && floatProfitLossRatio > 0 {
   133  				// 卖出
   134  				isNeedToSell = true
   135  				orderRemark += ">H>MA5>0"
   136  			} else {
   137  				//6.11 如果股价触及止盈比例, 则卖出
   138  				if sellRule.Session.CanTakeProfit() && floatProfitLossRatio > sellRule.TakeProfitRatio {
   139  					isNeedToSell = true
   140  					// 止盈
   141  					orderRemark += ">TPR"
   142  				} else if sellRule.Session.CanStopLoss() && floatProfitLossRatio < sellRule.StopLossRatio {
   143  					isNeedToSell = true
   144  					// 止损
   145  					orderRemark += "<SLR"
   146  				}
   147  			}
   148  		}
   149  		// 7 卖出操作, 最后修订
   150  		// 成本价
   151  		//costPrice := position.OpenPrice
   152  		orderPrice := lastPrice
   153  		orderVolume := position.CanUseVolume
   154  		// 7.1 如果跳空低开, 现价卖出, 如果想开盘就挂卖单, 就需要配置一个早盘集合竞价结束后, 早盘交易之前的交易时段
   155  		// 比如 09:29:00~09:29:59
   156  		if !isNeedToSell && snapshot.ExistDownwardGap() {
   157  			isNeedToSell = true
   158  			orderRemark = "GAP:DOWNWARD"
   159  			orderPrice = lastPrice
   160  		}
   161  		// 7.2 如果卖出策略配置的固定收益率大于0, 则卖出
   162  		if !isNeedToSell && sellRule.FixedYield > 0 {
   163  			fee := trader.EvaluatePriceForSell(securityCode, avgPrice, orderVolume, sellRule.FixedYield)
   164  			if fee != nil && fee.Price > avgPrice {
   165  				isNeedToSell = true
   166  				orderRemark = fmt.Sprintf("FIXEDYIELD:%.2f", sellRule.FixedYield*100)
   167  				orderPrice = fee.Price
   168  			}
   169  		}
   170  		// 18168
   171  		if !isNeedToSell {
   172  			continue
   173  		}
   174  		// 卖出
   175  		order_id, err := trader.DirectOrder(direction, strategyName, orderRemark, securityCode, trader.LATEST_PRICE, orderPrice, orderVolume)
   176  		if err != nil {
   177  			continue
   178  		}
   179  		_ = order_id
   180  	}
   181  }