stock rsi push to elasticsearch
This commit is contained in:
		
							parent
							
								
									957816e407
								
							
						
					
					
						commit
						f9a5a8cae4
					
				
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -6,7 +6,7 @@ go 1.21 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/go-redis/redis v6.15.9+incompatible | ||||
| 	github.com/phyer/core v0.1.62 | ||||
| 	github.com/phyer/core v0.1.64 | ||||
| 	github.com/sirupsen/logrus v1.9.3 | ||||
| ) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -49,8 +49,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y | ||||
| github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= | ||||
| github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= | ||||
| github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= | ||||
| github.com/phyer/core v0.1.62 h1:WIrdjV+0SG9pnTll1LwphA9nd3EzrwbSyuIJYKorlpw= | ||||
| github.com/phyer/core v0.1.62/go.mod h1:XZdniJiiZPzOU8+QHPFRQWdvJa6m5Ilj5VClWWI0OQg= | ||||
| github.com/phyer/core v0.1.64 h1:/FJC51rjD4ATnnr6efrlb+RoZP9l5SUq329+T7QUvWM= | ||||
| github.com/phyer/core v0.1.64/go.mod h1:XZdniJiiZPzOU8+QHPFRQWdvJa6m5Ilj5VClWWI0OQg= | ||||
| github.com/phyer/texus v0.0.0-20241207132635-0e7fb63f8196 h1:P1sxgCsS0VIL38ufZzgUuZLLyY/B+po6kSY7ziNZT7E= | ||||
| github.com/phyer/texus v0.0.0-20241207132635-0e7fb63f8196/go.mod h1:iZexs5agdApNlp8HW/FqKgma4Ij1x8/o+ZLcMvY3f80= | ||||
| github.com/phyer/v5sdkgo v0.1.4 h1:mAxxjPJVTYGuGDarqOcFGkzj5AgqbbzJGsnYmmsbapU= | ||||
|  | ||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							| @ -17,7 +17,7 @@ func main() { | ||||
| 	cr.TickerInforocessChan = make(chan *core.TickerInfo) | ||||
| 	cr.CandlesProcessChan = make(chan *core.Candle) | ||||
| 	cr.MaXProcessChan = make(chan *core.MaX) | ||||
| 	cr.RsiProcessChan = make(chan *core.Rsi) | ||||
| 	cr.StockRsiProcessChan = make(chan *core.StockRsi) | ||||
| 	cr.MakeMaXsChan = make(chan *core.Candle) | ||||
| 	cli, _ := cr.GetRedisLocalCli() | ||||
| 	cr.RedisRemoteCli = cli | ||||
|  | ||||
| @ -170,16 +170,10 @@ func LoopMakeMaX(cr *core.Core) { | ||||
| 		}(cd) | ||||
| 		go func(cad *core.Candle) { | ||||
| 			time.Sleep(time.Duration(300) * time.Millisecond) | ||||
| 			err, ct := MakeRsi(cr, cad, 16) | ||||
| 			err, ct := MakeRsi(cr, cad, 14) | ||||
| 			logrus.Warn(GetFuncName(), " rsi16 err:", err, " ct:", ct, " cd.InstID:", cd.InstID, " cd.Period:", cd.Period) | ||||
| 			// cd.InvokeRestQFromRemote(cr, ct) | ||||
| 		}(cd) | ||||
| 		go func(cad *core.Candle) { | ||||
| 			time.Sleep(time.Duration(300) * time.Millisecond) | ||||
| 			err, ct := MakeRsi(cr, cad, 12) | ||||
| 			logrus.Warn(GetFuncName(), " rsi12 err:", err, " ct:", ct, " cd.InstID:", cd.InstID, " cd.Period:", cd.Period) | ||||
| 			// cd.InvokeRestQFromRemote(cr, ct) | ||||
| 		}(cd) | ||||
| 		// TODO TODO 这地方不能加延时,否则makeMax处理不过来,多的就丢弃了,造成maX的sortedSet比candle的短很多。后面所有依赖的逻辑都受影响. | ||||
| 		// time.Sleep(300 * time.Millisecond) | ||||
| 	} | ||||
| @ -281,7 +275,8 @@ func MakeRsi(cr *core.Core, cl *core.Candle, count int) (error, int) { | ||||
| 	// keyName := "candle" + cl.Period + "|" + cl.InstID + "|ts:" + tss | ||||
| 	lastTime := time.UnixMilli(tsi) | ||||
| 	setName := "candle" + cl.Period + "|" + cl.InstID + "|sortedSet" | ||||
| 	cdl, err := GetRangeCandleSortedSet(cr, setName, count, lastTime) | ||||
| 	dcount := count * 2 | ||||
| 	cdl, err := GetRangeCandleSortedSet(cr, setName, dcount, lastTime) | ||||
| 	if err != nil { | ||||
| 		return err, 0 | ||||
| 	} | ||||
| @ -294,18 +289,29 @@ func MakeRsi(cr *core.Core, cl *core.Candle, count int) (error, int) { | ||||
| 	for _, v := range cdl.List { | ||||
| 		closeList = append(closeList, ToFloat64(v.Data[4])) | ||||
| 	} | ||||
| 	rv, err := CalculateRSI(closeList, count) | ||||
| 	rsiList, err := CalculateRSI(closeList, count) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("Error calculating RSI:", err) | ||||
| 		return err, 0 | ||||
| 	} | ||||
| 	// rv, err := CalculateRSI(closeList, dcount) | ||||
| 	percentK, percentD, err := CalculateStochRSI(rsiList, count, 3, 3) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("Error calculating StochRSI:", err) | ||||
| 		return err, 0 | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		logrus.Error("CalculateRSI err: ", err) | ||||
| 	} | ||||
| 	rsi := core.Rsi{ | ||||
| 	rsi := core.StockRsi{ | ||||
| 		InstID:     cl.InstID, | ||||
| 		Period:     cl.Period, | ||||
| 		Timestamp:  cl.Timestamp, | ||||
| 		Ts:         tsi, | ||||
| 		Count:      count, | ||||
| 		LastUpdate: time.Now(), | ||||
| 		RsiVol:     rv, | ||||
| 		KVol:       percentK[0], | ||||
| 		DVol:       percentD[0], | ||||
| 		Confirm:    false, | ||||
| 	} | ||||
| 	periodMins, err := cr.PeriodToMinutes(cl.Period) | ||||
| @ -315,7 +321,7 @@ func MakeRsi(cr *core.Core, cl *core.Candle, count int) (error, int) { | ||||
| 	if duration > time.Duration(periodMins-1)*time.Minute { | ||||
| 		rsi.Confirm = true | ||||
| 	} | ||||
| 	cr.RsiProcessChan <- &rsi | ||||
| 	cr.StockRsiProcessChan <- &rsi | ||||
| 	return nil, 0 | ||||
| } | ||||
| func MakeMaX(cr *core.Core, cl *core.Candle, count int) (error, int) { | ||||
| @ -501,14 +507,14 @@ func MaXsProcess(cr *core.Core) { | ||||
| } | ||||
| func RsisProcess(cr *core.Core) { | ||||
| 	for { | ||||
| 		rsi := <-cr.RsiProcessChan | ||||
| 		srsi := <-cr.StockRsiProcessChan | ||||
| 		// logrus.Debug("mx: ", mx) | ||||
| 		go func(rsi *core.Rsi) { | ||||
| 			mrs := MyRsi{ | ||||
| 				Rsi: *rsi, | ||||
| 		go func(srsi *core.StockRsi) { | ||||
| 			mrs := MyStockRsi{ | ||||
| 				StockRsi: *srsi, | ||||
| 			} | ||||
| 			mrs.Process(cr) | ||||
| 		}(rsi) | ||||
| 		}(srsi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -529,36 +535,106 @@ func TickerInfoProcess(cr *core.Core) { | ||||
| 
 | ||||
| // CalculateRSI calculates the RSI value for a given period and price data. | ||||
| // prices: input price data, must be equal to the period length. | ||||
| // period: the period for calculating RSI (e.g., 14). | ||||
| // Returns the RSI value (float64) or an error if input validation fails. | ||||
| func CalculateRSI(prices []float64, count int) (float64, error) { | ||||
| 	if len(prices) != count { | ||||
| 		return 0, errors.New("input prices length must be equal to the period length") | ||||
| 
 | ||||
| // CalculateRSI calculates the Relative Strength Index (RSI) for a given period. | ||||
| func CalculateRSI(prices []float64, period int) ([]float64, error) { | ||||
| 	if len(prices) < period { | ||||
| 		return nil, errors.New("not enough data to calculate RSI") | ||||
| 	} | ||||
| 
 | ||||
| 	// Calculate gains and losses | ||||
| 	var gainSum, lossSum float64 | ||||
| 	for i := 1; i < len(prices); i++ { | ||||
| 	rsi := make([]float64, len(prices)-period+1) | ||||
| 	var avgGain, avgLoss float64 | ||||
| 
 | ||||
| 	// Initial average gain and loss | ||||
| 	for i := 1; i <= period; i++ { | ||||
| 		change := prices[i] - prices[i-1] | ||||
| 		if change > 0 { | ||||
| 			gainSum += change | ||||
| 			avgGain += change | ||||
| 		} else { | ||||
| 			lossSum -= change | ||||
| 			avgLoss -= change | ||||
| 		} | ||||
| 	} | ||||
| 	avgGain /= float64(period) | ||||
| 	avgLoss /= float64(period) | ||||
| 
 | ||||
| 	if avgLoss == 0 { | ||||
| 		rsi[0] = 100 | ||||
| 	} else { | ||||
| 		rs := avgGain / avgLoss | ||||
| 		rsi[0] = 100 - (100 / (1 + rs)) | ||||
| 	} | ||||
| 
 | ||||
| 	// Calculate RSI for the rest of the data | ||||
| 	for i := period; i < len(prices); i++ { | ||||
| 		change := prices[i] - prices[i-1] | ||||
| 		if change > 0 { | ||||
| 			avgGain = (avgGain*(float64(period)-1) + change) / float64(period) | ||||
| 			avgLoss = (avgLoss * (float64(period) - 1)) / float64(period) | ||||
| 		} else { | ||||
| 			avgGain = (avgGain * (float64(period) - 1)) / float64(period) | ||||
| 			avgLoss = (avgLoss*(float64(period)-1) - change) / float64(period) | ||||
| 		} | ||||
| 
 | ||||
| 		if avgLoss == 0 { | ||||
| 			rsi[i-period+1] = 100 | ||||
| 		} else { | ||||
| 			rs := avgGain / avgLoss | ||||
| 			rsi[i-period+1] = 100 - (100 / (1 + rs)) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Average gains and losses | ||||
| 	avgGain := gainSum / float64(count) | ||||
| 	avgLoss := lossSum / float64(count) | ||||
| 
 | ||||
| 	// Prevent division by zero | ||||
| 	if avgLoss == 0 { | ||||
| 		return 100, nil // RSI is 100 if there's no loss | ||||
| 	} | ||||
| 
 | ||||
| 	// Calculate RS and RSI | ||||
| 	rs := avgGain / avgLoss | ||||
| 	rsi := 100 - (100 / (1 + rs)) | ||||
| 
 | ||||
| 	return rsi, nil | ||||
| } | ||||
| 
 | ||||
| // CalculateStochRSI calculates the Stochastic RSI. | ||||
| func CalculateStochRSI(rsi []float64, period int, kSmoothing int, dSmoothing int) ([]float64, []float64, error) { | ||||
| 	if len(rsi) < period { | ||||
| 		return nil, nil, errors.New("not enough data to calculate StochRSI") | ||||
| 	} | ||||
| 
 | ||||
| 	stochRsi := make([]float64, len(rsi)-period+1) | ||||
| 	for i := period; i <= len(rsi); i++ { | ||||
| 		lowest := rsi[i-period] | ||||
| 		highest := rsi[i-period] | ||||
| 		for j := i - period + 1; j < i; j++ { | ||||
| 			if rsi[j] < lowest { | ||||
| 				lowest = rsi[j] | ||||
| 			} | ||||
| 			if rsi[j] > highest { | ||||
| 				highest = rsi[j] | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if highest == lowest { | ||||
| 			stochRsi[i-period] = 0 | ||||
| 		} else { | ||||
| 			stochRsi[i-period] = (rsi[i-1] - lowest) / (highest - lowest) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Smooth %K | ||||
| 	percentK := smooth(stochRsi, kSmoothing) | ||||
| 
 | ||||
| 	// Smooth %D (signal line) | ||||
| 	percentD := smooth(percentK, dSmoothing) | ||||
| 
 | ||||
| 	return percentK, percentD, nil | ||||
| } | ||||
| 
 | ||||
| // Smooth applies a simple moving average to smooth the data. | ||||
| func smooth(data []float64, period int) []float64 { | ||||
| 	if period <= 1 || len(data) < period { | ||||
| 		return data | ||||
| 	} | ||||
| 
 | ||||
| 	smoothed := make([]float64, len(data)-period+1) | ||||
| 	for i := period - 1; i < len(data); i++ { | ||||
| 		sum := 0.0 | ||||
| 		for j := i - period + 1; j <= i; j++ { | ||||
| 			sum += data[j] | ||||
| 		} | ||||
| 		smoothed[i-period+1] = sum / float64(period) | ||||
| 	} | ||||
| 
 | ||||
| 	return smoothed | ||||
| } | ||||
|  | ||||
| @ -17,12 +17,12 @@ import ( | ||||
| 	// logrus "github.com/sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| type MyRsi struct { | ||||
| 	core.Rsi | ||||
| type MyStockRsi struct { | ||||
| 	core.StockRsi | ||||
| } | ||||
| 
 | ||||
| func (mrsi *MyRsi) Process(cr *core.Core) { | ||||
| 	rsi := mrsi.Rsi | ||||
| func (mrsi *MyStockRsi) Process(cr *core.Core) { | ||||
| 	rsi := mrsi.StockRsi | ||||
| 	go func() { | ||||
| 		rsi.PushToWriteLogChan(cr) | ||||
| 	}() | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zhangkun9038@dingtalk.com
						zhangkun9038@dingtalk.com