gitee.com/quant1x/engine@v1.8.4/factors/dataset_wide_old.go (about)

     1  package factors
     2  
     3  import (
     4  	"gitee.com/quant1x/engine/cache"
     5  	"gitee.com/quant1x/engine/datasource/base"
     6  	"gitee.com/quant1x/exchange"
     7  	"gitee.com/quant1x/gotdx"
     8  	"gitee.com/quant1x/gotdx/proto"
     9  	"gitee.com/quant1x/gotdx/quotes"
    10  	"gitee.com/quant1x/gox/api"
    11  	"gitee.com/quant1x/gox/logger"
    12  	"gitee.com/quant1x/num"
    13  	"gitee.com/quant1x/pandas"
    14  	. "gitee.com/quant1x/pandas/formula"
    15  	"reflect"
    16  	"strconv"
    17  )
    18  
    19  var (
    20  	FBarsProtocolFields = []string{"Open", "Close", "High", "Low", "Vol", "Amount", "DateTime", "UpCount", "DownCount"}
    21  	FBarsRawFields      = []string{"open", "close", "high", "low", "volume", "amount", "date", "up", "down"}
    22  	FBarsHalfFields     = []string{"date", "open", "close", "high", "low", "volume", "amount", "up", "down", "open_volume", "open_turnz", "open_unmatched", "close_volume", "close_turnz", "close_unmatched", "inner_volume", "outer_volume", "inner_amount", "outer_amount"}
    23  	FBarsWideFields     = []string{"date", "open", "close", "high", "low", "volume", "amount", "up", "down", "last_close", "change_rate", "open_volume", "open_turnz", "open_unmatched", "close_volume", "close_turnz", "close_unmatched", "inner_volume", "outer_volume", "inner_amount", "outer_amount"}
    24  )
    25  
    26  // SetKLineOffset 设置K线数据调整回补天数
    27  func SetKLineOffset(days int) {
    28  	if days <= 1 {
    29  		return
    30  	}
    31  	base.DataDaysDiff = days
    32  }
    33  
    34  // loadCacheKLine 加载K线
    35  //
    36  //	第2个参数, 是否前复权
    37  func loadCacheKLine(code string, adjust ...bool) pandas.DataFrame {
    38  	// 默认不复权
    39  	qfq := false
    40  	if len(adjust) > 0 {
    41  		qfq = adjust[0]
    42  	}
    43  	filename := cache.WideFilename(code)
    44  	var df pandas.DataFrame
    45  	if !api.FileExist(filename) {
    46  		return df
    47  	} else {
    48  		df = pandas.ReadCSV(filename)
    49  	}
    50  	// 调整字段流程
    51  	{
    52  		// turnover_rate 改为 change_rate
    53  		df.SetName("turnover_rate", "change_rate")
    54  	}
    55  
    56  	fields := FBarsWideFields
    57  	df = df.Select(fields)
    58  	if df.Nrow() == 0 {
    59  		return df
    60  	}
    61  	if qfq {
    62  		xdxrs := base.GetCacheXdxrList(code)
    63  		for i := 0; i < len(xdxrs); i++ {
    64  			xdxr := xdxrs[i]
    65  			if xdxr.Category != 1 {
    66  				continue
    67  			}
    68  			xdxrDate := xdxr.Date
    69  			factor := xdxr.Adjust()
    70  			for j := 0; j < df.Nrow(); j++ {
    71  				m1 := df.IndexOf(j, true)
    72  				dt := m1["date"].(reflect.Value).String()
    73  				if dt > xdxrDate {
    74  					break
    75  				}
    76  				if dt < xdxrDate {
    77  					po := m1["open"].(reflect.Value)
    78  					po.SetFloat(factor(po.Float()))
    79  					pc := m1["close"].(reflect.Value)
    80  					pc.SetFloat(factor(pc.Float()))
    81  					ph := m1["high"].(reflect.Value)
    82  					ph.SetFloat(factor(ph.Float()))
    83  					pl := m1["low"].(reflect.Value)
    84  					pl.SetFloat(factor(pl.Float()))
    85  				}
    86  				plc := m1["last_close"].(reflect.Value)
    87  				plc.SetFloat(factor(plc.Float()))
    88  				if dt == xdxrDate {
    89  					break
    90  				}
    91  			}
    92  		}
    93  	}
    94  	return df
    95  }
    96  
    97  // GetCacheKLine 加载K线
    98  //
    99  //	第2个参数, 是否前复权
   100  func GetCacheKLine(code string, adjust ...bool) pandas.DataFrame {
   101  	df := loadCacheKLine(code, adjust...)
   102  	if df.Nrow() == 0 {
   103  		return df
   104  	}
   105  	// 取出成交量序列
   106  	VOL := df.Col("volume")
   107  	DATES := df.Col("date")
   108  	lastDay := DATES.IndexOf(-1).(string)
   109  	total := df.Nrow()
   110  	// 计算5日均量
   111  	mv5 := MA(VOL, 5)
   112  	mav := REF(mv5, 1)
   113  	lb := VOL.Div(mav)
   114  	lb = lb.Apply2(func(idx int, v any) any {
   115  		if idx+1 < total {
   116  			return v
   117  		} else {
   118  			tmp := num.Any2DType(v)
   119  			ms := num.DType(exchange.Minutes(lastDay)) / float64(exchange.CN_TOTALFZNUM)
   120  			tmp /= ms
   121  			return tmp
   122  		}
   123  	}, true)
   124  
   125  	// 链接量比序列
   126  	oLB := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "lb", lb.DTypes())
   127  	oMV5 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "mv5", mv5.Div(exchange.CN_DEFAULT_TOTALFZNUM).DTypes())
   128  	vr := VOL.Div(REF(VOL, 1))
   129  	oVR := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "vr", vr.DTypes())
   130  	CLOSE := df.Col("close")
   131  	chg5 := CLOSE.Div(REF(CLOSE, 5)).Sub(1.00).Mul(100)
   132  	chg10 := CLOSE.Div(REF(CLOSE, 10)).Sub(1.00).Mul(100)
   133  	oChg5 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "chg5", chg5.DTypes())
   134  	oChg10 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "chg10", chg10.DTypes())
   135  	ma5 := MA(CLOSE, 5)
   136  	ma10 := MA(CLOSE, 10)
   137  	ma20 := MA(CLOSE, 20)
   138  	oMA5 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ma5", ma5.DTypes())
   139  	oMA10 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ma10", ma10.DTypes())
   140  	oMA20 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ma20", ma20.DTypes())
   141  	AMOUNT := df.Col("amount")
   142  	averagePrice := AMOUNT.Div(VOL)
   143  	oAP := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ap", averagePrice.DTypes())
   144  	df = df.Join(oLB, oMV5, oVR, oChg5, oChg10, oMA5, oMA10, oMA20, oAP)
   145  	return df
   146  }
   147  
   148  // GetKLineAll 获取日K线
   149  func GetKLineAll(securityCode string, argv ...int) pandas.DataFrame {
   150  	kType := uint16(proto.KLINE_TYPE_RI_K)
   151  	if len(argv) == 1 {
   152  		kType = uint16(argv[0])
   153  	}
   154  	securityCode = exchange.CorrectSecurityCode(securityCode)
   155  	tdxApi := gotdx.GetTdxApi()
   156  	startDate := exchange.MARKET_CH_FIRST_LISTTIME
   157  	// 默认是缓存中是已经复权的数据
   158  	dfCache := loadCacheKLine(securityCode)
   159  	isIndex := exchange.AssertIndexBySecurityCode(securityCode)
   160  	rawFields := FBarsProtocolFields
   161  	newFields := FBarsRawFields
   162  	// 尝试选择一次字段, 如果出现异常, 则清空dataframe, 重新下载
   163  	dfCache = dfCache.Select(FBarsWideFields)
   164  	if dfCache.Nrow() == 0 {
   165  		dfCache = pandas.DataFrame{}
   166  	}
   167  	var info *quotes.FinanceInfo
   168  	var err error
   169  	var klineDaysOffset = base.DataDaysDiff
   170  	if dfCache.Nrow() > 0 {
   171  		ds := dfCache.Col("date").Strings()
   172  		if klineDaysOffset > len(ds) {
   173  			klineDaysOffset = len(ds)
   174  		}
   175  		startDate = ds[len(ds)-klineDaysOffset]
   176  	} else {
   177  		info, err = tdxApi.GetFinanceInfo(securityCode)
   178  		if err != nil {
   179  			return dfCache
   180  		}
   181  		if info.IPODate > 0 {
   182  			startDate = strconv.FormatInt(int64(info.IPODate), 10)
   183  			startDate = exchange.FixTradeDate(startDate)
   184  		}
   185  	}
   186  	endDate := exchange.Today()
   187  	ts := exchange.TradeRange(startDate, endDate)
   188  	history := []quotes.SecurityBar{}
   189  	step := uint16(quotes.TDX_SECURITY_BARS_MAX)
   190  	total := uint16(len(ts))
   191  	start := uint16(0)
   192  	hs := []quotes.SecurityBarsReply{}
   193  	for {
   194  		count := step
   195  		if total-start >= step {
   196  			count = step
   197  		} else {
   198  			count = total - start
   199  		}
   200  		var data *quotes.SecurityBarsReply
   201  		var err error
   202  		retryTimes := 0
   203  		for retryTimes < quotes.DefaultRetryTimes {
   204  			if isIndex {
   205  				data, err = tdxApi.GetIndexBars(securityCode, kType, start, count)
   206  			} else {
   207  				data, err = tdxApi.GetKLine(securityCode, kType, start, count)
   208  			}
   209  			if err == nil && data != nil {
   210  				break
   211  			}
   212  			retryTimes++
   213  		}
   214  		if err != nil {
   215  			logger.Errorf("code=%s, error=%s", securityCode, err.Error())
   216  			return pandas.DataFrame{}
   217  		}
   218  		hs = append(hs, *data)
   219  		if data.Count < count {
   220  			// 已经是最早的记录
   221  			// 需要排序
   222  			break
   223  		}
   224  		start += count
   225  		if start >= total {
   226  			break
   227  		}
   228  	}
   229  	hs = api.Reverse(hs)
   230  	startDate = exchange.FixTradeDate(startDate)
   231  	for _, v := range hs {
   232  		for _, row := range v.List {
   233  			dateTime := exchange.FixTradeDate(row.DateTime)
   234  			if dateTime < startDate {
   235  				continue
   236  			}
   237  			row.Vol = row.Vol * 100
   238  			history = append(history, row)
   239  		}
   240  	}
   241  	dfNew := pandas.LoadStructs(history)
   242  	dfNew = dfNew.Select(rawFields)
   243  	err = dfNew.SetNames(newFields...)
   244  	if err != nil {
   245  		return pandas.DataFrame{}
   246  	}
   247  	ds1 := dfNew.Col("date", true)
   248  	ds1.Apply2(func(idx int, v any) any {
   249  		date1 := v.(string)
   250  		dt, err := api.ParseTime(date1)
   251  		if err != nil {
   252  			return date1
   253  		}
   254  		return dt.Format(exchange.TradingDayDateFormat)
   255  	}, true)
   256  	// 补充昨日收盘和涨跌幅
   257  	dfNew = attachVolume(dfNew, securityCode)
   258  	dfNew = dfNew.Select(FBarsHalfFields)
   259  	if dfNew.Nrow() > 0 {
   260  		// 除权除息
   261  		xdxrs := base.GetCacheXdxrList(securityCode)
   262  		cacheLastDay := dfNew.Col("date").IndexOf(-1).(string)
   263  		for i := 0; i < len(xdxrs); i++ {
   264  			xdxr := xdxrs[i]
   265  			if xdxr.Category != 1 || xdxr.Date < startDate || xdxr.Date > cacheLastDay {
   266  				continue
   267  			}
   268  			xdxrDate := xdxr.Date
   269  			factor := xdxr.Adjust()
   270  			for j := 0; j < dfNew.Nrow(); j++ {
   271  				m1 := dfNew.IndexOf(j, true)
   272  				barCurrentDate := m1["date"].(reflect.Value).String()
   273  				if barCurrentDate > xdxrDate {
   274  					break
   275  				}
   276  				if barCurrentDate < xdxrDate {
   277  					po := m1["open"].(reflect.Value)
   278  					po.SetFloat(factor(po.Float()))
   279  					pc := m1["close"].(reflect.Value)
   280  					pc.SetFloat(factor(pc.Float()))
   281  					ph := m1["high"].(reflect.Value)
   282  					ph.SetFloat(factor(ph.Float()))
   283  					pl := m1["low"].(reflect.Value)
   284  					pl.SetFloat(factor(pl.Float()))
   285  				}
   286  				if barCurrentDate == xdxrDate {
   287  					break
   288  				}
   289  			}
   290  		}
   291  	}
   292  	dfCache = dfCache.Select(FBarsHalfFields)
   293  	df := dfCache.Subset(0, dfCache.Nrow()-klineDaysOffset)
   294  	if df.Nrow() > 0 {
   295  		df = df.Concat(dfNew)
   296  	} else {
   297  		df = dfNew
   298  	}
   299  	CLOSE := df.Col("close")
   300  	LAST := CLOSE.Shift(1)
   301  	rate := CLOSE.Sub(LAST).Div(LAST).Mul(100.00).DTypes()
   302  
   303  	lc := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "last_close", LAST.DTypes())
   304  	tr := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "change_rate", rate)
   305  	df = df.Join(lc, tr)
   306  	df = df.Select(FBarsWideFields)
   307  	if df.Nrow() > 0 {
   308  		filename := cache.WideFilename(securityCode)
   309  		_ = df.WriteCSV(filename)
   310  	}
   311  	return df
   312  }
   313  
   314  // 附加成交量
   315  func attachVolume(df pandas.DataFrame, securityCode string) pandas.DataFrame {
   316  	securityCode = exchange.CorrectSecurityCode(securityCode)
   317  	dates := df.Col("date").Strings()
   318  	if len(dates) == 0 {
   319  		return df
   320  	}
   321  	buyVolumes := []int64{}
   322  	sellVolumes := []int64{}
   323  	buyAmounts := []num.DType{}
   324  	sellAmounts := []num.DType{}
   325  
   326  	openVolumes := []int64{}
   327  	openTurnZ := []num.DType{}
   328  	openUnmatched := []int64{}
   329  	closeVolumes := []int64{}
   330  	closeTurnZ := []num.DType{}
   331  	closeUnmatched := []int64{}
   332  	for _, tradeDate := range dates {
   333  		tmp := base.CheckoutTransactionData(securityCode, tradeDate, true)
   334  		logger.Warnf("tick: code=%s, date=%s, rows=%d", securityCode, tradeDate, len(tmp))
   335  		//summary := InflowCount(tmp, securityCode)
   336  		summary := CountInflow(tmp, securityCode, tradeDate)
   337  		buyVolumes = append(buyVolumes, summary.OuterVolume)
   338  		sellVolumes = append(sellVolumes, summary.InnerVolume)
   339  		buyAmounts = append(buyAmounts, summary.OuterAmount)
   340  		sellAmounts = append(sellAmounts, summary.InnerAmount)
   341  
   342  		openVolumes = append(openVolumes, summary.OpenVolume)
   343  		openTurnZ = append(openTurnZ, summary.OpenTurnZ)
   344  		openUnmatched = append(openUnmatched, summary.OpenUnmatched)
   345  		closeVolumes = append(closeVolumes, summary.CloseVolume)
   346  		closeTurnZ = append(closeTurnZ, summary.CloseTurnZ)
   347  		closeUnmatched = append(closeUnmatched, summary.CloseUnmatched)
   348  	}
   349  	// 调整字段名
   350  	bv := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "outer_volume", buyVolumes)
   351  	sv := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "inner_volume", sellVolumes)
   352  	ba := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "outer_amount", buyAmounts)
   353  	sa := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "inner_amount", sellAmounts)
   354  
   355  	// 新增字段
   356  	ov := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "open_volume", openVolumes)
   357  	ot := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "open_turnz", openTurnZ)
   358  	ou := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "open_unmatched", openUnmatched)
   359  	cv := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "close_volume", closeVolumes)
   360  	ct := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "close_turnz", closeTurnZ)
   361  	cu := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "close_unmatched", closeUnmatched)
   362  
   363  	df = df.Join(bv, sv, ba, sa, ov, ot, ou, cv, ct, cu)
   364  	return df
   365  }