计算中间币对逻辑
This commit is contained in:
		
							parent
							
								
									8f519aa103
								
							
						
					
					
						commit
						e0700e77a9
					
				| @ -20,7 +20,7 @@ func formatTimestamp(ts string) (string, error) { | |||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	loc, _ := time.LoadLocation("Asia/Shanghai") | 	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数组并提供序列化方法 | // CandleList 封装Candle数组并提供序列化方法 | ||||||
| @ -276,6 +276,113 @@ func (cl *CandleList) ToFluentd() error { | |||||||
| 	return nil | 	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 | // parsePeriod 将周期字符串转换为time.Duration | ||||||
| func parsePeriod(period string) (time.Duration, error) { | func parsePeriod(period string) (time.Duration, error) { | ||||||
| 	switch period { | 	switch period { | ||||||
| @ -374,7 +481,7 @@ func (cl *CandleList) ToElastic() error { | |||||||
| 
 | 
 | ||||||
| 		// 构造记录 | 		// 构造记录 | ||||||
| 		record := map[string]interface{}{ | 		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, | 			"open":      candle.Open, | ||||||
| 			"high":      candle.High, | 			"high":      candle.High, | ||||||
| 			"low":       candle.Low, | 			"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) | //			t.Fatalf("ToEs failed: %v", err) | ||||||
| //		} | //		} | ||||||
| //	} | //	} | ||||||
| func TestCandleList_ToElastic(t *testing.T) { | 
 | ||||||
| 	startTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) | // func TestCandleList_ToElastic(t *testing.T) { | ||||||
| 	endTime := time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC) | // 	startTime := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
| 	cl, err := MakeCandleList("BTC-USDT", "1D", startTime, endTime, 50) | // 	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 { | 	if err != nil { | ||||||
| 		t.Fatalf("ToEs failed: %v", err) | 		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 { | 	if err != nil { | ||||||
| 		t.Fatalf("ToEs failed: %v", err) | 		t.Fatalf("ToEs failed: %v", err) | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zhangkun9038@dingtalk.com
						zhangkun9038@dingtalk.com