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  }