计算中间币对逻辑
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