mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
feat: add BOLL (Bollinger Bands) indicator to Strategy Studio
- Add BOLL to frontend indicator grid with period selection - Add BOLL calculation (upper/middle/lower bands) in market data - Add BOLL fields to TimeframeSeriesData struct - Integrate BOLL into AI decision engine prompts - Support multi-timeframe BOLL analysis
This commit is contained in:
@@ -868,6 +868,14 @@ func (e *StrategyEngine) writeAvailableIndicators(sb *strings.Builder) {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
if indicators.EnableBOLL {
|
||||
sb.WriteString("- Bollinger Bands (BOLL) - Upper/Middle/Lower bands")
|
||||
if len(indicators.BOLLPeriods) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" (periods: %v)", indicators.BOLLPeriods))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
if indicators.EnableVolume {
|
||||
sb.WriteString("- Volume data\n")
|
||||
}
|
||||
@@ -1193,6 +1201,12 @@ func (e *StrategyEngine) formatTimeframeSeriesData(sb *strings.Builder, data *ma
|
||||
sb.WriteString(fmt.Sprintf("ATR14: %.4f\n", data.ATR14))
|
||||
}
|
||||
|
||||
if indicators.EnableBOLL && len(data.BOLLUpper) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("BOLL Upper: %s\n", formatFloatSlice(data.BOLLUpper)))
|
||||
sb.WriteString(fmt.Sprintf("BOLL Middle: %s\n", formatFloatSlice(data.BOLLMiddle)))
|
||||
sb.WriteString(fmt.Sprintf("BOLL Lower: %s\n", formatFloatSlice(data.BOLLLower)))
|
||||
}
|
||||
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +227,9 @@ func calculateTimeframeSeries(klines []Kline, timeframe string, count int) *Time
|
||||
RSI7Values: make([]float64, 0, count),
|
||||
RSI14Values: make([]float64, 0, count),
|
||||
Volume: make([]float64, 0, count),
|
||||
BOLLUpper: make([]float64, 0, count),
|
||||
BOLLMiddle: make([]float64, 0, count),
|
||||
BOLLLower: make([]float64, 0, count),
|
||||
}
|
||||
|
||||
// Get latest N data points based on count from config
|
||||
@@ -277,6 +280,14 @@ func calculateTimeframeSeries(klines []Kline, timeframe string, count int) *Time
|
||||
rsi14 := calculateRSI(klines[:i+1], 14)
|
||||
data.RSI14Values = append(data.RSI14Values, rsi14)
|
||||
}
|
||||
|
||||
// Calculate Bollinger Bands (period 20, std dev multiplier 2)
|
||||
if i >= 19 {
|
||||
upper, middle, lower := calculateBOLL(klines[:i+1], 20, 2.0)
|
||||
data.BOLLUpper = append(data.BOLLUpper, upper)
|
||||
data.BOLLMiddle = append(data.BOLLMiddle, middle)
|
||||
data.BOLLLower = append(data.BOLLLower, lower)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate ATR14
|
||||
@@ -466,6 +477,36 @@ func calculateATR(klines []Kline, period int) float64 {
|
||||
return atr
|
||||
}
|
||||
|
||||
// calculateBOLL calculates Bollinger Bands (upper, middle, lower)
|
||||
// period: typically 20, multiplier: typically 2
|
||||
func calculateBOLL(klines []Kline, period int, multiplier float64) (upper, middle, lower float64) {
|
||||
if len(klines) < period {
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
// Calculate SMA (middle band)
|
||||
sum := 0.0
|
||||
for i := len(klines) - period; i < len(klines); i++ {
|
||||
sum += klines[i].Close
|
||||
}
|
||||
sma := sum / float64(period)
|
||||
|
||||
// Calculate standard deviation
|
||||
variance := 0.0
|
||||
for i := len(klines) - period; i < len(klines); i++ {
|
||||
diff := klines[i].Close - sma
|
||||
variance += diff * diff
|
||||
}
|
||||
stdDev := math.Sqrt(variance / float64(period))
|
||||
|
||||
// Calculate bands
|
||||
middle = sma
|
||||
upper = sma + multiplier*stdDev
|
||||
lower = sma - multiplier*stdDev
|
||||
|
||||
return upper, middle, lower
|
||||
}
|
||||
|
||||
// calculateIntradaySeries calculates intraday series data
|
||||
func calculateIntradaySeries(klines []Kline) *IntradayData {
|
||||
data := &IntradayData{
|
||||
|
||||
@@ -41,6 +41,10 @@ type TimeframeSeriesData struct {
|
||||
RSI14Values []float64 `json:"rsi14_values"` // RSI14 series
|
||||
Volume []float64 `json:"volume"` // Volume series (deprecated, use Klines)
|
||||
ATR14 float64 `json:"atr14"` // ATR14
|
||||
// Bollinger Bands (period 20, std dev multiplier 2)
|
||||
BOLLUpper []float64 `json:"boll_upper"` // Upper band
|
||||
BOLLMiddle []float64 `json:"boll_middle"` // Middle band (SMA)
|
||||
BOLLLower []float64 `json:"boll_lower"` // Lower band
|
||||
}
|
||||
|
||||
// OIData Open Interest data
|
||||
|
||||
@@ -82,6 +82,7 @@ type IndicatorConfig struct {
|
||||
EnableMACD bool `json:"enable_macd"`
|
||||
EnableRSI bool `json:"enable_rsi"`
|
||||
EnableATR bool `json:"enable_atr"`
|
||||
EnableBOLL bool `json:"enable_boll"` // Bollinger Bands
|
||||
EnableVolume bool `json:"enable_volume"`
|
||||
EnableOI bool `json:"enable_oi"` // open interest
|
||||
EnableFundingRate bool `json:"enable_funding_rate"` // funding rate
|
||||
@@ -91,6 +92,8 @@ type IndicatorConfig struct {
|
||||
RSIPeriods []int `json:"rsi_periods,omitempty"` // default [7, 14]
|
||||
// ATR period configuration
|
||||
ATRPeriods []int `json:"atr_periods,omitempty"` // default [14]
|
||||
// BOLL period configuration (period, standard deviation multiplier is fixed at 2)
|
||||
BOLLPeriods []int `json:"boll_periods,omitempty"` // default [20] - can select multiple timeframes
|
||||
// external data sources
|
||||
ExternalDataSources []ExternalDataSource `json:"external_data_sources,omitempty"`
|
||||
// quantitative data sources (capital flow, position changes, price changes)
|
||||
@@ -241,12 +244,14 @@ func GetDefaultStrategyConfig(lang string) StrategyConfig {
|
||||
EnableMACD: false,
|
||||
EnableRSI: false,
|
||||
EnableATR: false,
|
||||
EnableBOLL: false,
|
||||
EnableVolume: true,
|
||||
EnableOI: true,
|
||||
EnableFundingRate: true,
|
||||
EMAPeriods: []int{20, 50},
|
||||
RSIPeriods: []int{7, 14},
|
||||
ATRPeriods: []int{14},
|
||||
BOLLPeriods: []int{20},
|
||||
EnableQuantData: true,
|
||||
QuantDataAPIURL: "http://nofxaios.com:30006/api/coin/{symbol}?include=netflow,oi,price&auth=cm_568c67eae410d912c54c",
|
||||
EnableQuantOI: true,
|
||||
|
||||
@@ -72,6 +72,8 @@ export function IndicatorEditor({
|
||||
rsiDesc: { zh: '相对强弱指标', en: 'Relative Strength Index' },
|
||||
atr: { zh: 'ATR', en: 'ATR' },
|
||||
atrDesc: { zh: '真实波幅均值', en: 'Average True Range' },
|
||||
boll: { zh: 'BOLL 布林带', en: 'Bollinger Bands' },
|
||||
bollDesc: { zh: '布林带指标(上中下轨)', en: 'Upper/Middle/Lower Bands' },
|
||||
volume: { zh: '成交量', en: 'Volume' },
|
||||
volumeDesc: { zh: '交易量分析', en: 'Trading volume analysis' },
|
||||
oi: { zh: '持仓量', en: 'Open Interest' },
|
||||
@@ -295,6 +297,7 @@ export function IndicatorEditor({
|
||||
{ key: 'enable_macd', label: 'macd', desc: 'macdDesc', color: '#a855f7' },
|
||||
{ key: 'enable_rsi', label: 'rsi', desc: 'rsiDesc', color: '#F6465D', periodKey: 'rsi_periods', defaultPeriods: '7,14' },
|
||||
{ key: 'enable_atr', label: 'atr', desc: 'atrDesc', color: '#60a5fa', periodKey: 'atr_periods', defaultPeriods: '14' },
|
||||
{ key: 'enable_boll', label: 'boll', desc: 'bollDesc', color: '#ec4899', periodKey: 'boll_periods', defaultPeriods: '20' },
|
||||
].map(({ key, label, desc, color, periodKey, defaultPeriods }) => (
|
||||
<div
|
||||
key={key}
|
||||
|
||||
@@ -467,12 +467,14 @@ export interface IndicatorConfig {
|
||||
enable_macd: boolean;
|
||||
enable_rsi: boolean;
|
||||
enable_atr: boolean;
|
||||
enable_boll: boolean;
|
||||
enable_volume: boolean;
|
||||
enable_oi: boolean;
|
||||
enable_funding_rate: boolean;
|
||||
ema_periods?: number[];
|
||||
rsi_periods?: number[];
|
||||
atr_periods?: number[];
|
||||
boll_periods?: number[];
|
||||
external_data_sources?: ExternalDataSource[];
|
||||
// 量化数据源(资金流向、持仓变化、价格变化)
|
||||
enable_quant_data?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user