From 5c79aa451e801e97b2a1a8101c7cc310f87048b3 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Sat, 17 Jan 2026 21:39:21 +0800 Subject: [PATCH] feat(market): add Donchian channel calculation Add calculateDonchian function to compute highest high and lowest low over a specified period. This is the foundation for box (range) detection in the multi-period box indicator system for grid trading. --- market/data.go | 32 ++++++++++++++++++++++++++++++++ market/data_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/market/data.go b/market/data.go index 1fa990c0..8cce788d 100644 --- a/market/data.go +++ b/market/data.go @@ -1210,3 +1210,35 @@ func ExportCalculateATR(klines []Kline, period int) float64 { func ExportCalculateBOLL(klines []Kline, period int, multiplier float64) (upper, middle, lower float64) { return calculateBOLL(klines, period, multiplier) } + +// calculateDonchian calculates Donchian channel (highest high, lowest low) for given period +func calculateDonchian(klines []Kline, period int) (upper, lower float64) { + if len(klines) == 0 { + return 0, 0 + } + + // Use all available klines if period > len(klines) + start := len(klines) - period + if start < 0 { + start = 0 + } + + upper = klines[start].High + lower = klines[start].Low + + for i := start + 1; i < len(klines); i++ { + if klines[i].High > upper { + upper = klines[i].High + } + if klines[i].Low < lower { + lower = klines[i].Low + } + } + + return upper, lower +} + +// ExportCalculateDonchian exports calculateDonchian for testing +func ExportCalculateDonchian(klines []Kline, period int) (float64, float64) { + return calculateDonchian(klines, period) +} diff --git a/market/data_test.go b/market/data_test.go index b20a336c..5dcde7c2 100644 --- a/market/data_test.go +++ b/market/data_test.go @@ -500,3 +500,40 @@ func TestIsStaleData_EmptyKlines(t *testing.T) { t.Error("Expected false for empty klines, got true") } } + +func TestCalculateDonchian(t *testing.T) { + // Create test klines with known high/low values + klines := []Kline{ + {High: 100, Low: 90}, + {High: 105, Low: 88}, + {High: 102, Low: 92}, + {High: 108, Low: 85}, + {High: 103, Low: 91}, + } + + upper, lower := ExportCalculateDonchian(klines, 5) + + if upper != 108 { + t.Errorf("Expected upper = 108, got %v", upper) + } + if lower != 85 { + t.Errorf("Expected lower = 85, got %v", lower) + } +} + +func TestCalculateDonchian_PartialPeriod(t *testing.T) { + klines := []Kline{ + {High: 100, Low: 90}, + {High: 105, Low: 88}, + } + + upper, lower := ExportCalculateDonchian(klines, 10) + + // Should use all available klines when period > len(klines) + if upper != 105 { + t.Errorf("Expected upper = 105, got %v", upper) + } + if lower != 88 { + t.Errorf("Expected lower = 88, got %v", lower) + } +}