Bollinger Bands are a volatility indicator consisting of a middle band (simple moving average) and two outer bands set at standard deviations above and below the middle band.
| Parameter | Type | Default | Description |
|---|---|---|---|
period |
int | 20 | The number of periods for the SMA calculation |
stdDev |
float64 | 2.0 | Number of standard deviations for the bands |
lookback |
duration | “24h” | Lookback period for data retrieval |
[period, stdDev, lookback]
Example:
[20, 2.0, "24h"]
The signal’s RawValue field contains a JSON object with the following keys:
| Key | Type | Description |
|---|---|---|
upper |
float64 | Upper band (middle + stdDev * standard_deviation) |
middle |
float64 | Middle band (Simple Moving Average) |
lower |
float64 | Lower band (middle - stdDev * standard_deviation) |
Example:
{"upper": 155.50, "middle": 150.00, "lower": 144.50}
| Condition | Signal Type | Description |
|---|---|---|
| Price < Lower Band | SIGNAL_TYPE_BUY_LONG |
Price below lower band - oversold |
| Price > Upper Band | SIGNAL_TYPE_SELL_LONG |
Price above upper band - overbought |
| Otherwise | SIGNAL_TYPE_NO_ACTION |
Price within bands |
If insufficient data (fewer than period points), returns SIGNAL_TYPE_NO_ACTION.
func (s *MyStrategy) Initialize(_ context.Context, req *strategy.InitializeRequest) (*emptypb.Empty, error) {
api := strategy.NewStrategyApi()
// Configure Bollinger Bands
_, err := api.ConfigureIndicator(context.Background(), &strategy.ConfigureRequest{
IndicatorType: strategy.IndicatorType_INDICATOR_BOLLINGER_BANDS,
Config: `[20, 2.0, "24h"]`,
})
if err != nil {
return nil, fmt.Errorf("failed to configure Bollinger Bands: %w", err)
}
return &emptypb.Empty{}, nil
}
func (s *MyStrategy) ProcessData(ctx context.Context, req *strategy.ProcessDataRequest) (*emptypb.Empty, error) {
data := req.Data
api := strategy.NewStrategyApi()
// Get Bollinger Bands signal
signal, err := api.GetSignal(ctx, &strategy.GetSignalRequest{
IndicatorType: strategy.IndicatorType_INDICATOR_BOLLINGER_BANDS,
MarketData: data,
})
if err != nil {
return nil, fmt.Errorf("failed to get Bollinger Bands signal: %w", err)
}
// Parse raw value
var bbValue struct {
Upper float64 `json:"upper"`
Middle float64 `json:"middle"`
Lower float64 `json:"lower"`
}
if err := json.Unmarshal([]byte(signal.RawValue), &bbValue); err != nil {
return nil, fmt.Errorf("failed to parse Bollinger Bands value: %w", err)
}
// Calculate bandwidth and %B
bandwidth := (bbValue.Upper - bbValue.Lower) / bbValue.Middle * 100
percentB := (data.Close - bbValue.Lower) / (bbValue.Upper - bbValue.Lower)
fmt.Printf("BB Upper: %.2f, Middle: %.2f, Lower: %.2f\n",
bbValue.Upper, bbValue.Middle, bbValue.Lower)
fmt.Printf("Bandwidth: %.2f%%, %%B: %.2f\n", bandwidth, percentB)
// Use signal type
switch signal.Type {
case strategy.SignalType_SIGNAL_TYPE_BUY_LONG:
// Price below lower band - potential buy
case strategy.SignalType_SIGNAL_TYPE_SELL_LONG:
// Price above upper band - potential sell
}
return &emptypb.Empty{}, nil
}
| Condition | Interpretation |
|---|---|
| Price at upper band | Potentially overbought / strong uptrend |
| Price at lower band | Potentially oversold / strong downtrend |
| Bands widening | Increasing volatility |
| Bands narrowing (squeeze) | Decreasing volatility, potential breakout coming |
| Price bouncing off middle band | Middle band acting as support/resistance |
You can calculate additional metrics from the raw values:
| Metric | Formula | Description |
|---|---|---|
| Bandwidth | (Upper - Lower) / Middle * 100 | Measures volatility as percentage |
| %B | (Price - Lower) / (Upper - Lower) | Where price is within the bands (0-1 range) |
| Style | Period | Std Dev | Use Case |
|---|---|---|---|
| Standard | 20 | 2.0 | General purpose |
| Tight | 20 | 1.5 | More frequent signals |
| Wide | 20 | 2.5 | Fewer, stronger signals |
| Short-term | 10 | 1.5 | Day trading |
| Long-term | 50 | 2.0 | Position trading |