计算中间币对逻辑

This commit is contained in:
zhangkun9038@dingtalk.com 2025-03-27 22:29:28 +08:00
parent 8f519aa103
commit e0700e77a9
3 changed files with 140 additions and 19 deletions

View File

@ -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,

View File

@ -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

View File

@ -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)
} }