gitee.com/quant1x/engine@v1.8.4/factors/feature_misc.go (about) 1 package factors 2 3 import ( 4 "context" 5 "gitee.com/quant1x/engine/cache" 6 "gitee.com/quant1x/engine/config" 7 "gitee.com/quant1x/engine/datasource/base" 8 "gitee.com/quant1x/engine/datasource/dfcf" 9 "gitee.com/quant1x/engine/market" 10 "gitee.com/quant1x/engine/utils" 11 "gitee.com/quant1x/exchange" 12 "gitee.com/quant1x/gox/api" 13 "gitee.com/quant1x/gox/logger" 14 "gitee.com/quant1x/num" 15 "gitee.com/quant1x/pandas" 16 . "gitee.com/quant1x/pandas/formula" 17 ) 18 19 const ( 20 cacheL5KeyMisc = "misc" 21 ) 22 23 // Misc 上一个交易日的数据快照 24 type Misc struct { 25 cache.DataSummary `dataframe:"-"` 26 Date string `name:"日期" dataframe:"日期"` // 数据日期 27 Code string `name:"证券代码" dataframe:"证券代码"` // 证券代码 28 Shape uint64 `name:"K线形态" dataframe:"K线形态"` // K线形态 29 MV3 float64 `name:"前3日分钟均量" dataframe:"前3日分钟均量"` // 前3日分钟均量 30 MA3 float64 `name:"3日均线" dataframe:"3日均线"` // 3日均价 31 MV5 float64 `name:"前5日分钟均量" dataframe:"前5日分钟均量"` // 前5日每分钟均量, 量比(QuantityRelativeRatio)需要 32 MA5 float64 `name:"5日均线" dataframe:"5日均线"` // 5日均价 33 MA10 float64 `name:"10日均线" dataframe:"10日均线"` // 10日均价 34 MA20 float64 `name:"20日均线" dataframe:"20日均线"` // 生命线(MA20)/20日线 35 FundFlow float64 `name:"资金流向" dataframe:"资金流向"` // 资金流向, 暂时无用 36 RZYEZB float64 `name:"融资余额占流通市值比(%)" dataframe:"RZYEZB"` // 融资余额占流通市值比 37 VolumeRatio float64 `name:"成交量比" dataframe:"成交量比"` // 成交量放大比例, 相邻的两个交易日进行比对 38 TurnoverRate float64 `name:"换手率" dataframe:"换手率"` // 换手率 39 AmplitudeRatio float64 `name:"振幅" dataframe:"振幅"` // 振幅 40 BidOpen float64 `name:"竞价开盘" dataframe:"竞价开盘"` // 竞价开盘价 41 BidClose float64 `name:"竞价结束" dataframe:"竞价结束"` // 竞价结束 42 BidHigh float64 `name:"竞价最高" dataframe:"竞价最高"` // 竞价最高 43 BidLow float64 `name:"竞价最低" dataframe:"竞价最低"` // 竞价最低 44 BidMatched float64 `name:"竞价匹配量" dataframe:"竞价匹配量"` // 竞价匹配量 45 BidUnmatched float64 `name:"竞价未匹配" dataframe:"竞价未匹配"` // 竞价未匹配量 46 BidDirection int `name:"竞价方向" dataframe:"竞价方向"` // 竞价方向 47 OpenBiddingDirection int `name:"开盘竞价" dataframe:"开盘竞价"` // 竞价方向, 交易当日集合竞价开盘时更新 48 OpenVolumeDirection int `name:"开盘竞量" dataframe:"开盘竞量"` // 委托量差, 交易当日集合竞价开盘时更新 49 CloseBiddingDirection int `name:"收盘竞价" dataframe:"收盘竞价"` // 竞价方向, 交易当日集合竞价收盘时更新 50 CloseVolumeDirection int `name:"收盘竞量" dataframe:"收盘竞量"` // 委托量差, 交易当日集合竞价收盘时更新 51 OpenVolume int64 `name:"开盘量" dataframe:"开盘量"` // 开盘量 52 OpenTurnZ float64 `name:"开盘换手z" dataframe:"开盘换手z"` // 开盘换手z 53 CloseVolume int64 `name:"收盘量" dataframe:"收盘量"` // TODO:快照数据实际上有好几条, 应该用当日成交记录修订 54 CloseTurnZ float64 `name:"收盘换手z" dataframe:"收盘换手z"` // 收盘换手z 55 LastSentiment float64 `name:"昨日情绪" dataframe:"昨日情绪"` // 昨日情绪 56 LastConsistent int `name:"昨日情绪一致" dataframe:"昨日情绪一致"` // 昨日情绪一致 57 OpenSentiment float64 `name:"开盘情绪值" dataframe:"开盘情绪值"` // 开盘情绪值, 个股没有 58 OpenConsistent int `name:"开盘情绪一致" dataframe:"开盘情绪一致"` // 开盘情绪一致, 个股没有 59 CloseSentiment float64 `name:"收盘情绪值" dataframe:"收盘情绪值"` // 收盘情绪值 60 CloseConsistent int `name:"收盘情绪一致" dataframe:"收盘情绪一致"` // 收盘情绪一致 61 AveragePrice float64 `name:"均价线" dataframe:"均价线"` // 均价线 62 Volume int64 `name:"成交量" dataframe:"成交量"` // 成交量 63 InnerVolume int64 `name:"内盘" dataframe:"内盘"` // 内盘 64 OuterVolume int64 `name:"外盘" dataframe:"外盘"` // 外盘 65 InnerAmount float64 `name:"卖出金额" dataframe:"inner_amount"` // 卖出金额合计 66 OuterAmount float64 `name:"买入金额" dataframe:"outer_amount"` // 买入金额合计 67 Change5 float64 `name:"5日涨幅" dataframe:"5日涨幅"` // 5日涨幅 68 Change10 float64 `name:"10日涨幅" dataframe:"10日涨幅"` // 10日涨幅 69 InitialPrice float64 `name:"启动价格" dataframe:"启动价格"` // 短线底部(Short-Term Bottom),股价最近一次上穿5日均线 70 ShortIntensity float64 `name:"短线强度" dataframe:"短线强度"` // 短线强度,Strength 71 ShortIntensityDiff float64 `name:"短线强度增幅" dataframe:"短线强度增幅"` // 短线强度 72 MediumIntensity float64 `name:"中线强度" dataframe:"中线强度"` // 中线强度 73 MediumIntensityDiff float64 `name:"中线强度增幅" dataframe:"中线强度增幅"` // 中线强度 74 Vix float64 `name:"波动率" dataframe:"波动率"` // 波动率 75 BullPower float64 `name:"多方强度" dataframe:"多方强度"` // 多方强度 76 BearPower float64 `name:"空方强度" dataframe:"空方强度"` // 空方强度 77 PowerTrendReversal int `name:"多空反转" dataframe:"多空反转"` // 多空反转 78 PowerTrendPeriod int `name:"多空趋势周期" dataframe:"多空趋势周期"` // 多空趋势周期 79 State uint64 `name:"样本状态" dataframe:"样本状态"` // 样本状态 80 } 81 82 func NewMisc(date, code string) *Misc { 83 summary := __mapFeatures[FeatureMisc] 84 v := Misc{ 85 DataSummary: summary, 86 Date: date, 87 Code: code, 88 } 89 return &v 90 } 91 92 func (this *Misc) GetDate() string { 93 return this.Date 94 } 95 96 func (this *Misc) GetSecurityCode() string { 97 return this.Code 98 } 99 100 func (this *Misc) Factory(date string, code string) Feature { 101 v := NewMisc(date, code) 102 return v 103 } 104 105 func (this *Misc) Init(ctx context.Context, date string) error { 106 _ = ctx 107 _ = date 108 return nil 109 } 110 111 func (this *Misc) FromHistory(history History) Feature { 112 _ = history 113 return this 114 } 115 116 func (this *Misc) Update(code, cacheDate, featureDate string, complete bool) { 117 // 1. K线相关 118 miscKLineExtend(this, code, featureDate) 119 // 2. 成交量 120 miscTurnZ(this, code, cacheDate, featureDate) 121 // 3. 情绪 122 miscSentiment(this, code, cacheDate, featureDate) 123 // 4. 资金流向 124 miscFundFlow(this, code, cacheDate, featureDate) 125 miscPower(this, code, cacheDate, featureDate) 126 127 _ = complete 128 } 129 130 func (this *Misc) Repair(code, cacheDate, featureDate string, complete bool) { 131 // 1. K线相关 132 miscKLineExtend(this, code, featureDate) 133 // 2. 成交量, 使用cacheDate作为特征的缓存日期 134 marketSessionEnd := exchange.TradeSessionHasEnd(cacheDate) 135 if marketSessionEnd { 136 // 收盘后, 用当日数据 137 miscTurnZ(this, code, cacheDate, cacheDate) 138 } else { 139 // 盘中修复, 用上一个交易日的数据 140 miscTurnZ(this, code, cacheDate, featureDate) 141 } 142 // 3. 情绪 143 miscSentiment(this, code, cacheDate, featureDate) 144 // 4. 资金流向 145 miscFundFlow(this, code, cacheDate, featureDate) 146 miscPower(this, code, cacheDate, featureDate) 147 148 // 5. 融资融券 149 securityCode := exchange.CorrectSecurityCode(code) 150 rzrq, ok := GetMarginTradingTarget(securityCode) 151 if ok { 152 this.RZYEZB = rzrq.RZYEZB 153 } 154 155 _ = complete 156 } 157 158 func (this *Misc) Increase(snapshot QuoteSnapshot) Feature { 159 _ = snapshot 160 return this 161 } 162 163 // ValidateSample 验证样本数据 164 func (this *Misc) ValidateSample() error { 165 if this.State > 0 { 166 return nil 167 } 168 return ErrInvalidFeatureSample 169 } 170 171 // ExchangeKLineExtend 更新Exchange K线相关数据 172 func miscKLineExtend(info *Misc, securityCode string, featureDate string) { 173 cover := NewMiscKLine(securityCode, featureDate) 174 if cover == nil { 175 logger.Errorf("code[%s, %s] kline not found", securityCode, featureDate) 176 return 177 } 178 info.Date = cover.Date 179 info.Shape = cover.Shape 180 info.MV3 = cover.MV3 181 info.MA3 = cover.MA3 182 info.MV5 = cover.MV5 183 info.MA5 = cover.MA5 184 info.MA10 = cover.MA10 185 info.MA20 = cover.MA20 186 info.VolumeRatio = cover.VolumeRatio 187 info.TurnoverRate = cover.TurnoverRate 188 info.AmplitudeRatio = cover.AmplitudeRatio 189 info.AveragePrice = cover.AveragePrice 190 info.Change5 = cover.Change5 191 info.Change10 = cover.Change10 192 info.InitialPrice = cover.InitialPrice 193 194 // 强弱指标 195 info.ShortIntensity = cover.ShortIntensity 196 info.ShortIntensityDiff = cover.ShortIntensityDiff 197 info.MediumIntensity = cover.MediumIntensity 198 info.MediumIntensityDiff = cover.MediumIntensityDiff 199 200 // 波动率 201 info.Vix = cover.Vix 202 203 // 情绪, 指数和板块的情绪从K线上的up和down获取, 这里不处理个股的情绪 204 if exchange.AssertIndexBySecurityCode(info.Code) { 205 info.LastSentiment = cover.Sentiment 206 info.LastConsistent = cover.Consistent 207 } 208 info.State |= cover.Kind() 209 } 210 211 // 更新 - misc - 历史成交数据相关, capture,collect 212 func miscTurnZ(info *Misc, securityCode string, cacheDate, featureDate string) { 213 //list := base.GetHistoricalTradingData(securityCode, featureDate) 214 list := base.CheckoutTransactionData(securityCode, featureDate, true) 215 if len(list) > 0 { 216 summary := CountInflow(list, securityCode, featureDate) 217 // 修正f10的缓存, 应该是缓存日期为准 218 f10 := GetL5F10(securityCode, cacheDate) 219 if f10 != nil { 220 summary.OpenTurnZ = f10.TurnZ(summary.OpenVolume) 221 summary.CloseTurnZ = f10.TurnZ(summary.CloseVolume) 222 } 223 cover := summary 224 info.OpenVolume = cover.OpenVolume 225 info.OpenTurnZ = cover.OpenTurnZ 226 info.CloseVolume = cover.CloseVolume 227 info.CloseTurnZ = cover.CloseTurnZ 228 info.Volume = cover.InnerVolume + cover.OuterVolume 229 info.InnerVolume = cover.InnerVolume 230 info.OuterVolume = cover.OuterVolume 231 info.InnerAmount = cover.InnerAmount 232 info.OuterAmount = cover.OuterAmount 233 } 234 } 235 236 // 更新 - misc - 情绪 237 func miscSentiment(info *Misc, securityCode string, cacheDate, featureDate string) { 238 if exchange.AssertIndexBySecurityCode(securityCode) { 239 // 跳过指数和板块, 只处理个股的情绪值 240 return 241 } 242 list := base.CheckoutTransactionData(securityCode, featureDate, true) 243 if len(list) > 0 { 244 cover := CountInflow(list, securityCode, featureDate) 245 info.LastSentiment, info.LastConsistent = market.SecuritySentiment(cover.OuterVolume, cover.InnerVolume) 246 } 247 } 248 249 // 更新 - misc - 资金流向 250 func miscFundFlow(info *Misc, securityCode string, cacheDate, featureDate string) { 251 if !exchange.AssertStockBySecurityCode(securityCode) { 252 return 253 } 254 beginDate := exchange.MARKET_CH_FIRST_LISTTIME 255 filename := cache.FundFlowFilename(securityCode) 256 cacheList := []dfcf.FundFlow{} 257 err := api.CsvToSlices(filename, &cacheList) 258 cacheLength := len(cacheList) 259 if err == nil && cacheLength > 0 { 260 beginDate = cacheList[cacheLength-1].Date 261 cacheList = cacheList[0 : cacheLength-1] 262 } 263 newList := dfcf.IndividualStocksFundFlow(securityCode, beginDate) 264 if len(newList) == 0 { 265 return 266 } 267 list := append(cacheList, newList...) 268 _ = api.SlicesToCsv(filename, list) 269 last := list[len(list)-1] 270 cover := last.Medium 271 info.FundFlow = cover 272 _ = cacheDate 273 _ = featureDate 274 } 275 276 type PowerTrend = int 277 278 const ( 279 // SignalNegative indicates that a particular value is a negative peak. 280 SignalNegative PowerTrend = -1 281 // SignalNeutral indicates that a particular value is not a peak. 282 SignalNeutral PowerTrend = 0 283 // SignalPositive indicates that a particular value is a positive peak. 284 SignalPositive PowerTrend = 1 285 ) 286 287 // 更新 - misc - 资金多空强度 288 func miscPower(info *Misc, securityCode string, cacheDate, featureDate string) { 289 lines := CheckoutWideTableByDate(securityCode, featureDate) 290 if len(lines) == 0 { 291 return 292 } 293 df := pandas.LoadStructs(lines) 294 if df.Nrow() == 0 { 295 return 296 } 297 ia := df.ColAsNDArray("inner_amount") 298 oa := df.ColAsNDArray("outer_amount") 299 diffIA := ia.Sub(REF(ia, 1)) 300 diffOA := oa.Sub(REF(oa, 1)) 301 info.BullPower = utils.Float64IndexOf(diffOA, -1) 302 info.BearPower = utils.Float64IndexOf(diffIA, -1) 303 bull := CROSS(diffOA, diffIA) 304 bear := CROSS(diffIA, diffOA) 305 nBull := BARSLAST(bull) 306 nBear := BARSLAST(bear) 307 trend := IFF(nBull.Lt(nBear), SignalPositive, SignalNegative) 308 period := IFF(trend.Eq(SignalPositive), nBull, nBear) 309 info.PowerTrendReversal = utils.IntegerIndexOf(trend, -1) 310 info.PowerTrendPeriod = utils.IntegerIndexOf(period, -1) 311 } 312 313 // AuctionWeaknessToStrength 弱转强, 开盘价下方买入 314 func (this *Misc) AuctionWeaknessToStrength() bool { 315 if this.ValidateSample() != nil { 316 return false 317 } 318 // 1. 竞价走低, 但是有承接力量 319 // 竞价走低 320 c1 := this.BidClose < this.BidOpen 321 // 竞价结束不低于竞价最低价 322 c2 := this.BidClose >= this.BidLow 323 // 未匹配量远低于匹配量 324 undertakePower := float64(this.BidUnmatched) / float64(this.BidMatched) 325 diffRatio := 1 - undertakePower 326 c3 := diffRatio >= config.TraderConfig().UndertakeRatio 327 // 竞价落差比 328 bidChangeRate := num.NetChangeRate(this.BidOpen, this.BidClose) 329 c4 := bidChangeRate >= -3.82 330 return c1 && c2 && c3 && c4 331 } 332 333 // AuctionStrengthToWeakness 竞价强转弱, 开盘就跑 334 func (this *Misc) AuctionStrengthToWeakness() bool { 335 if this.ValidateSample() != nil { 336 return false 337 } 338 // 1. 竞价不走低, 但是跟风力量不强, 开盘就跑 339 // 竞价不走低 340 c1 := this.BidClose >= this.BidOpen 341 // 竞价结束不低于竞价最低价 342 c2 := this.BidClose >= this.BidLow 343 // 未匹配量远低于匹配量 344 undertakePower := float64(this.BidUnmatched) / float64(this.BidMatched) 345 diffRatio := undertakePower 346 c3 := diffRatio <= config.TraderConfig().UndertakeRatio 347 return c1 && c2 && c3 348 }