package factors import ( "context" "gitee.com/quant1x/engine/cache" "gitee.com/quant1x/engine/datasource/base" "gitee.com/quant1x/engine/datasource/dfcf" "gitee.com/quant1x/engine/market" "gitee.com/quant1x/gotdx/proto" "gitee.com/quant1x/gox/api" "gitee.com/quant1x/gox/logger" ) const ( cacheL5KeyExchange = "exchange" ) // Exchange 上一个交易日的数据快照 type Exchange struct { cache.DataSummary `dataframe:"-"` Date string `name:"日期" dataframe:"日期"` // 数据日期 Code string `name:"证券代码" dataframe:"证券代码"` // 证券代码 Shape uint64 `name:"K线形态" dataframe:"K线形态"` // K线形态 MV3 float64 `name:"前3日分钟均量" dataframe:"前3日分钟均量"` // 前3日分钟均量 MA3 float64 `name:"3日均线" dataframe:"3日均线"` // 3日均价 MV5 float64 `name:"前5日分钟均量" dataframe:"前5日分钟均量"` // 前5日每分钟均量, 量比(QuantityRelativeRatio)需要 MA5 float64 `name:"5日均线" dataframe:"5日均线"` // 5日均价 MA10 float64 `name:"10日均线" dataframe:"10日均线"` // 10日均价 MA20 float64 `name:"20日均线" dataframe:"20日均线"` // 生命线(MA20)/20日线 FundFlow float64 `name:"资金流向" dataframe:"资金流向"` // 资金流向, 暂时无用 VolumeRatio float64 `name:"成交量比" dataframe:"成交量比"` // 成交量放大比例, 相邻的两个交易日进行比对 TurnoverRate float64 `name:"换手率" dataframe:"换手率"` // 换手率 AmplitudeRatio float64 `name:"振幅" dataframe:"振幅"` // 振幅 BidOpen float64 `name:"竞价开盘" dataframe:"竞价开盘"` // 竞价开盘价 BidClose float64 `name:"竞价结束" dataframe:"竞价结束"` // 竞价结束 BidHigh float64 `name:"竞价最高" dataframe:"竞价最高"` // 竞价最高 BidLow float64 `name:"竞价最低" dataframe:"竞价最低"` // 竞价最低 BidMatched float64 `name:"竞价匹配量" dataframe:"竞价匹配量"` // 竞价匹配量 BidUnmatched float64 `name:"竞价未匹配" dataframe:"竞价未匹配"` // 竞价未匹配量 BidDirection int `name:"竞价方向" dataframe:"竞价方向"` // 竞价方向 OpenBiddingDirection int `name:"开盘竞价" dataframe:"开盘竞价"` // 竞价方向, 交易当日集合竞价开盘时更新 OpenVolumeDirection int `name:"开盘竞量" dataframe:"开盘竞量"` // 委托量差, 交易当日集合竞价开盘时更新 CloseBiddingDirection int `name:"收盘竞价" dataframe:"收盘竞价"` // 竞价方向, 交易当日集合竞价收盘时更新 CloseVolumeDirection int `name:"收盘竞量" dataframe:"收盘竞量"` // 委托量差, 交易当日集合竞价收盘时更新 OpenVolume int64 `name:"开盘量" dataframe:"开盘量"` // 开盘量 OpenTurnZ float64 `name:"开盘换手z" dataframe:"开盘换手z"` // 开盘换手z CloseVolume int64 `name:"收盘量" dataframe:"收盘量"` // TODO:快照数据实际上有好几条, 应该用当日成交记录修订 CloseTurnZ float64 `name:"收盘换手z" dataframe:"收盘换手z"` // 收盘换手z LastSentiment float64 `name:"昨日情绪" dataframe:"昨日情绪"` // 昨日情绪 LastConsistent int `name:"昨日情绪一致" dataframe:"昨日情绪一致"` // 昨日情绪一致 OpenSentiment float64 `name:"开盘情绪值" dataframe:"开盘情绪值"` // 开盘情绪值, 个股没有 OpenConsistent int `name:"开盘情绪一致" dataframe:"开盘情绪一致"` // 开盘情绪一致, 个股没有 CloseSentiment float64 `name:"收盘情绪值" dataframe:"收盘情绪值"` // 收盘情绪值 CloseConsistent int `name:"收盘情绪一致" dataframe:"收盘情绪一致"` // 收盘情绪一致 AveragePrice float64 `name:"均价线" dataframe:"均价线"` // 均价线 Volume int64 `name:"成交量" dataframe:"成交量"` // 成交量 InnerVolume int64 `name:"内盘" dataframe:"内盘"` // 内盘 OuterVolume int64 `name:"外盘" dataframe:"外盘"` // 外盘 Change5 float64 `name:"5日涨幅" dataframe:"5日涨幅"` // 5日涨幅 Change10 float64 `name:"10日涨幅" dataframe:"10日涨幅"` // 10日涨幅 InitialPrice float64 `name:"启动价格" dataframe:"启动价格"` // 短线底部(Short-Term Bottom),股价最近一次上穿5日均线 ShortIntensity float64 `name:"短线强度" dataframe:"短线强度"` // 短线强度,Strength ShortIntensityDiff float64 `name:"短线强度增幅" dataframe:"短线强度增幅"` // 短线强度 MediumIntensity float64 `name:"中线强度" dataframe:"中线强度"` // 中线强度 MediumIntensityDiff float64 `name:"中线强度增幅" dataframe:"中线强度增幅"` // 中线强度 Vix float64 `name:"波动率" dataframe:"波动率"` // 波动率 State uint64 `name:"样本状态" dataframe:"样本状态"` } func NewExchange(date, code string) *Exchange { summary := __mapFeatures[FeatureExchange] v := Exchange{ DataSummary: summary, Date: date, Code: code, } return &v } func (this *Exchange) GetDate() string { return this.Date } func (this *Exchange) GetSecurityCode() string { return this.Code } func (this *Exchange) Factory(date string, code string) Feature { v := NewExchange(date, code) return v } func (this *Exchange) Init(ctx context.Context, date string) error { _ = ctx _ = date return nil } func (this *Exchange) FromHistory(history History) Feature { _ = history return this } func (this *Exchange) Update(code, cacheDate, featureDate string, complete bool) { // 1. K线相关 exchangeKLineExtend(this, code, featureDate) // 2. 成交量 exchangeTurnZ(this, code, cacheDate, featureDate) // 3. 情绪 exchangeSentiment(this, code, cacheDate, featureDate) // 4. 资金流向 exchangeFundFlow(this, code, cacheDate, featureDate) } func (this *Exchange) Repair(code, cacheDate, featureDate string, complete bool) { // 1. K线相关 exchangeKLineExtend(this, code, featureDate) // 2. 成交量, 使用cacheDate作为特征的缓存日期 exchangeTurnZ(this, code, cacheDate, cacheDate) // 3. 情绪 exchangeSentiment(this, code, cacheDate, featureDate) // 4. 资金流向 exchangeFundFlow(this, code, cacheDate, featureDate) } func (this *Exchange) Increase(snapshot QuoteSnapshot) Feature { _ = snapshot return this } // ValidateSample 验证样本数据 func (this *Exchange) ValidateSample() error { if this.State > 0 { return nil } return ErrInvalidFeatureSample } // ExchangeKLineExtend 更新Exchange K线相关数据 func exchangeKLineExtend(info *Exchange, securityCode string, featureDate string) { cover := NewExchangeKLine(securityCode, featureDate) if cover == nil { logger.Errorf("code[%s, %s] kline not found", securityCode, featureDate) return } info.Date = cover.Date info.Shape = cover.Shape info.MV3 = cover.MV3 info.MA3 = cover.MA3 info.MV5 = cover.MV5 info.MA5 = cover.MA5 info.MA10 = cover.MA10 info.MA20 = cover.MA20 info.VolumeRatio = cover.VolumeRatio info.TurnoverRate = cover.TurnoverRate info.AmplitudeRatio = cover.AmplitudeRatio info.AveragePrice = cover.AveragePrice info.Change5 = cover.Change5 info.Change10 = cover.Change10 info.InitialPrice = cover.InitialPrice // 强弱指标 info.ShortIntensity = cover.ShortIntensity info.ShortIntensityDiff = cover.ShortIntensityDiff info.MediumIntensity = cover.MediumIntensity info.MediumIntensityDiff = cover.MediumIntensityDiff // 波动率 info.Vix = cover.Vix // 情绪, 指数和板块的情绪从K线上的up和down获取, 这里不处理个股的情绪 if proto.AssertIndexBySecurityCode(info.Code) { info.LastSentiment = cover.Sentiment info.LastConsistent = cover.Consistent } info.State |= cover.Kind() } // 更新 - exchange - 历史成交数据相关, capture,collect func exchangeTurnZ(info *Exchange, securityCode string, cacheDate, featureDate string) { list := base.Transaction(securityCode, featureDate) if len(list) > 0 { summary := CountInflow(list, securityCode, featureDate) // 修正f10的缓存, 应该是缓存日期为准 f10 := GetL5F10(securityCode, cacheDate) if f10 != nil { summary.OpenTurnZ = f10.TurnZ(summary.OpenVolume) summary.CloseTurnZ = f10.TurnZ(summary.CloseVolume) } cover := summary info.OpenVolume = cover.OpenVolume info.OpenTurnZ = cover.OpenTurnZ info.CloseVolume = cover.CloseVolume info.CloseTurnZ = cover.CloseTurnZ info.Volume = cover.InnerVolume + cover.OuterVolume info.InnerVolume = cover.InnerVolume info.OuterVolume = cover.OuterVolume } } // 更新 - exchange - 情绪 func exchangeSentiment(info *Exchange, securityCode string, cacheDate, featureDate string) { if proto.AssertIndexBySecurityCode(securityCode) { // 跳过指数和板块, 只处理个股的情绪值 return } list := base.Transaction(securityCode, featureDate) if len(list) > 0 { cover := CountInflow(list, securityCode, featureDate) info.LastSentiment, info.LastConsistent = market.SecuritySentiment(cover.OuterVolume, cover.InnerVolume) } } // 更新 - exchange - 资金流向 func exchangeFundFlow(info *Exchange, securityCode string, cacheDate, featureDate string) { if !proto.AssertStockBySecurityCode(securityCode) { return } beginDate := proto.MARKET_CH_FIRST_LISTTIME filename := cache.FundFlowFilename(securityCode) cacheList := []dfcf.FundFlow{} err := api.CsvToSlices(filename, &cacheList) cacheLength := len(cacheList) if err == nil && cacheLength > 0 { beginDate = cacheList[cacheLength-1].Date cacheList = cacheList[0 : cacheLength-1] } newList := dfcf.IndividualStocksFundFlow(securityCode, beginDate) if len(newList) == 0 { return } list := append(cacheList, newList...) _ = api.SlicesToCsv(filename, list) last := list[len(list)-1] cover := last.Medium info.FundFlow = cover }