计算中间币对逻辑
This commit is contained in:
		
							parent
							
								
									8f519aa103
								
							
						
					
					
						commit
						e0700e77a9
					
				| @ -20,7 +20,7 @@ func formatTimestamp(ts string) (string, error) { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	loc, _ := time.LoadLocation("Asia/Shanghai") | ||||
| 	return time.UnixMilli(millis).In(loc).Format("2006-01-02 15:04:05"), nil | ||||
| 	return time.UnixMilli(millis).In(loc).Format("2006-01-02T15:04:05+08:00"), nil | ||||
| } | ||||
| 
 | ||||
| // CandleList 封装Candle数组并提供序列化方法 | ||||
| @ -276,6 +276,113 @@ func (cl *CandleList) ToFluentd() error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // CalculateCrossPair 计算两个币对的中间币对 | ||||
| func (cl *CandleList) CalculateCrossPair(other *CandleList) (*CandleList, error) { | ||||
| 	// 验证周期是否一致 | ||||
| 	if cl.Period != other.Period { | ||||
| 		return nil, fmt.Errorf("period mismatch: %s vs %s", cl.Period, other.Period) | ||||
| 	} | ||||
| 
 | ||||
| 	// 解析币对名称 | ||||
| 	base1, quote1 := parsePair(cl.CoinPair) | ||||
| 	base2, quote2 := parsePair(other.CoinPair) | ||||
| 
 | ||||
| 	// 验证是否构成交叉币对 | ||||
| 	if quote1 != quote2 { | ||||
| 		return nil, fmt.Errorf("not a valid cross pair: %s and %s", cl.CoinPair, other.CoinPair) | ||||
| 	} | ||||
| 
 | ||||
| 	// 创建新的CandleList | ||||
| 	crossPair := fmt.Sprintf("%s-%s", base1, base2) | ||||
| 	result := &CandleList{ | ||||
| 		CoinPair: crossPair, | ||||
| 		Period:   cl.Period, | ||||
| 	} | ||||
| 
 | ||||
| 	fmt.Println("result.CoinPair, result.Period: ", result.CoinPair, result.Period) | ||||
| 	// 创建时间戳映射 | ||||
| 	clMap := make(map[string]*Candle) | ||||
| 	for _, candle := range cl.Candles { | ||||
| 		clMap[candle.Timestamp] = candle | ||||
| 	} | ||||
| 
 | ||||
| 	// 遍历另一个CandleList,寻找匹配的时间戳 | ||||
| 	for _, otherCandle := range other.Candles { | ||||
| 		if clCandle, exists := clMap[otherCandle.Timestamp]; exists { | ||||
| 			// 计算中间币对的价格 | ||||
| 			open, err := calculateCrossPrice(clCandle.Open, otherCandle.Open) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			high, err := calculateCrossPrice(clCandle.High, otherCandle.High) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			low, err := calculateCrossPrice(clCandle.Low, otherCandle.Low) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			close, err := calculateCrossPrice(clCandle.Close, otherCandle.Close) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			// 创建新的Candle | ||||
| 			crossCandle := &Candle{ | ||||
| 				Timestamp: otherCandle.Timestamp, | ||||
| 				Open:      open, | ||||
| 				High:      high, | ||||
| 				Low:       low, | ||||
| 				Close:     close, | ||||
| 				Volume:    "0", // 成交量不直接转换 | ||||
| 				VolumeCcy: "0", // 成交量不直接转换 | ||||
| 			} | ||||
| 
 | ||||
| 			result.Candles = append(result.Candles, crossCandle) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(result.Candles) == 0 { | ||||
| 		return nil, fmt.Errorf("no overlapping timestamps found") | ||||
| 	} | ||||
| 
 | ||||
| 	return result, nil | ||||
| } | ||||
| 
 | ||||
| // parsePair 解析币对名称 | ||||
| func parsePair(pair string) (string, string) { | ||||
| 	parts := strings.Split(pair, "-") | ||||
| 	if len(parts) != 2 { | ||||
| 		return "", "" | ||||
| 	} | ||||
| 	return parts[0], parts[1] | ||||
| } | ||||
| 
 | ||||
| // calculateCrossPrice 计算交叉币对价格 | ||||
| func calculateCrossPrice(price1, price2 string) (string, error) { | ||||
| 	p1, err := strconv.ParseFloat(price1, 64) | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("invalid price1: %v", err) | ||||
| 	} | ||||
| 	p2, err := strconv.ParseFloat(price2, 64) | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("invalid price2: %v", err) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("invalid price2: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if p2 == 0 { | ||||
| 		return "", fmt.Errorf("division by zero") | ||||
| 	} | ||||
| 
 | ||||
| 	result := p1 / p2 | ||||
| 	return fmt.Sprintf("%.8f", result), nil | ||||
| } | ||||
| 
 | ||||
| // parsePeriod 将周期字符串转换为time.Duration | ||||
| func parsePeriod(period string) (time.Duration, error) { | ||||
| 	switch period { | ||||
| @ -374,7 +481,7 @@ func (cl *CandleList) ToElastic() error { | ||||
| 
 | ||||
| 		// 构造记录 | ||||
| 		record := map[string]interface{}{ | ||||
| 			"dataTime":  cstTime.Format("2006-01-02 15:04:05"), | ||||
| 			"dataTime":  cstTime.Format("2006-01-02T15:04:05") + "+08:00", | ||||
| 			"open":      candle.Open, | ||||
| 			"high":      candle.High, | ||||
| 			"low":       candle.Low, | ||||
|  | ||||
| @ -1,12 +0,0 @@ | ||||
| === RUN   TestCandleList_ToJson | ||||
| lens of allCandles:  400 | ||||
| Number of candles: 400 | ||||
|     candleList_test.go:29: JSON output written to /tmp/candle_test.json | ||||
| --- PASS: TestCandleList_ToJson (1.74s) | ||||
| === RUN   TestCandleList_ToCsv | ||||
| lens of allCandles:  400 | ||||
| Number of candles: 400 | ||||
|     candleList_test.go:51: CSV output written to /tmp/candle_test.csv | ||||
| --- PASS: TestCandleList_ToCsv (1.02s) | ||||
| PASS | ||||
| ok  	command-line-arguments	3.293s | ||||
| @ -63,14 +63,40 @@ import ( | ||||
| //			t.Fatalf("ToEs failed: %v", err) | ||||
| //		} | ||||
| //	} | ||||
| func TestCandleList_ToElastic(t *testing.T) { | ||||
| 	startTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	endTime := time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC) | ||||
| 	cl, err := MakeCandleList("BTC-USDT", "1D", startTime, endTime, 50) | ||||
| 
 | ||||
| // func TestCandleList_ToElastic(t *testing.T) { | ||||
| // 	startTime := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| // 	endTime := time.Date(2025, 3, 1, 0, 0, 0, 0, time.UTC) | ||||
| // 	cl, err := MakeCandleList("BTC-USDT", "1D", startTime, endTime, 50) | ||||
| // 	if err != nil { | ||||
| // 		t.Fatalf("ToEs failed: %v", err) | ||||
| // 	} | ||||
| // 	err = cl.ToElastic() | ||||
| // 	if err != nil { | ||||
| // 		t.Fatalf("ToEs failed: %v", err) | ||||
| // 	} | ||||
| // } | ||||
| 
 | ||||
| func TestCandleListI_CalculateCrossPair(t *testing.T) { | ||||
| 	startTime := time.Date(2025, 3, 15, 0, 0, 0, 0, time.UTC) | ||||
| 	endTime := time.Date(2025, 3, 19, 23, 0, 0, 0, time.UTC) | ||||
| 	okbUsdt, err := MakeCandleList("OKB-USDT", "4H", startTime, endTime, 50) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("ToEs failed: %v", err) | ||||
| 	} | ||||
| 	err = cl.ToElastic() | ||||
| 	ethUsdt, err := MakeCandleList("ETH-USDT", "4H", startTime, endTime, 50) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("ToEs failed: %v", err) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("ToEs failed: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	okbEth, err := okbUsdt.CalculateCrossPair(ethUsdt) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("ToEs failed: %v", err) | ||||
| 	} | ||||
| 	err = okbEth.ToElastic() | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("ToEs failed: %v", err) | ||||
| 	} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zhangkun9038@dingtalk.com
						zhangkun9038@dingtalk.com