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 }