From e0700e77a924fb31fc62f8fc3684f2dd565f8c00 Mon Sep 17 00:00:00 2001 From: "zhangkun9038@dingtalk.com" Date: Thu, 27 Mar 2025 22:29:28 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A1=E7=AE=97=E4=B8=AD=E9=97=B4=E5=B8=81?= =?UTF-8?q?=E5=AF=B9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- okx/candleList.go | 111 ++++++++++++++++++++++++++++++++- test/okxAli/aaa.txt | 12 ---- test/okxAli/candleList_test.go | 36 +++++++++-- 3 files changed, 140 insertions(+), 19 deletions(-) delete mode 100644 test/okxAli/aaa.txt diff --git a/okx/candleList.go b/okx/candleList.go index 1bb9fcf..61b75c1 100644 --- a/okx/candleList.go +++ b/okx/candleList.go @@ -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, diff --git a/test/okxAli/aaa.txt b/test/okxAli/aaa.txt deleted file mode 100644 index 48aa4ea..0000000 --- a/test/okxAli/aaa.txt +++ /dev/null @@ -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 diff --git a/test/okxAli/candleList_test.go b/test/okxAli/candleList_test.go index 8452ad6..d626d59 100644 --- a/test/okxAli/candleList_test.go +++ b/test/okxAli/candleList_test.go @@ -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) }