Deterministic, no-lookahead support/resistance detection for Go trading systems.
go-sr is a focused Go module for support/resistance detection that is designed for backtests and live systems where reproducibility and no-lookahead behavior matter.
- Deterministic results for the same candle prefix and options
- Closed-candle inputs and confirmation-based zone pivots
- Legacy line-based and ATR-aware zone modes
- Nearest support/resistance metadata for strategy logic
- Multi-timeframe candle aggregation and sizing helpers
- No third-party runtime dependencies
- CI with race detection, static analysis, a high statement-coverage floor, and fuzz smoke tests
Zone-mode output generated from the repository's BTC 5m fixture. The preview shows detected support/resistance structure, qualified zones, and nearest levels.
go get github.com/laclance/go-sr@latestimport sr "github.com/laclance/go-sr"levels, err := sr.Compute(candles, sr.Options{
Timeframe: "5m",
Lookback: 120,
Mode: sr.ModeZones,
MinStrength: 2,
})
if err != nil {
return err
}
fmt.Printf("support=%.2f resistance=%.2f\n",
levels.NearestSupport,
levels.NearestResistance,
)candles is a slice of closed OHLCV candles:
[]sr.Candle{
{
OpenTime: openTime,
CloseTime: closeTime,
Open: 100.0,
High: 103.0,
Low: 99.0,
Close: 102.0,
Volume: 1250,
},
}The result includes the detected levels plus strategy-friendly nearest-level metadata:
levels.Levels
levels.NearestSupport
levels.NearestResistance
levels.NearestSupportDistance
levels.NearestResistanceDistance
levels.NearestSupportStrength
levels.NearestResistanceStrength
levels.NearestSupportScore
levels.NearestResistanceScore
levels.NearSupport
levels.NearResistanceexamples/basic is a self-contained runnable program. It generates deterministic closed 5m candles, runs zone mode, and prints the detected level count plus nearest support and resistance.
From a clean directory, copy and run:
git clone --depth=1 https://github.com/laclance/go-sr.git
cd go-sr
go run ./examples/basicThe example imports only the Go standard library plus github.com/laclance/go-sr; it does not depend on repository test helpers. You can also copy examples/basic/main.go into another Go module unchanged, then replace demoCandles with candles from your exchange, broker, backtest fixture, or market-data pipeline.
- BBGO closed-kline adapter shows how to map BBGO
types.KLineevents into a bounded slice of closedsr.Candlevalues without adding BBGO to this module.
Many trading implementations accidentally make support/resistance look better in backtests by allowing future candles to influence historical pivots. go-sr is built around prefix-stable, confirmation-based behavior so the same candle history produces the same result whether it is processed in a backtest or a live strategy.
That makes it a good fit when you need S/R as a dependable input rather than a chart-only visual indicator.
levels, err := sr.Compute(candles, sr.Options{
Timeframe: "5m",
Lookback: 120,
Mode: sr.ModeZones,
MinStrength: 2,
})Zone mode clusters confirmed swing pivots into support/resistance zones. MinStrength filters qualified zones; values <= 0 use the default of 2.
levels, err := sr.Compute(candles, sr.Options{
Timeframe: "5m",
Lookback: 120,
Mode: sr.ModeLegacy,
Tolerance: 0.002,
})Legacy mode provides line-based S/R behavior. Tolerance applies only to legacy mode; values <= 0 use the default 0.002.
Aggregate lower-timeframe candles before computing higher-timeframe S/R:
candles15m := sr.AggregateCandlesToTimeframe(candles5m, "5m", "15m")
levels15m, err := sr.Compute(candles15m, sr.Options{
Timeframe: "15m",
Lookback: 50,
Mode: sr.ModeZones,
})Helpers are also available for calculating warmup and exchange-fetch requirements for a finite lookback:
warmup := sr.WarmupCandles(50, sr.ModeZones)
limit := sr.RequiredKlineLimit("5m", "1h", 50, sr.ModeZones)WarmupCandles and RequiredKlineLimit return 0 when lookback <= 0 because an all-supplied-history calculation has no finite warmup/fetch size. For bounded lookbacks, RequiredKlineLimit includes enough slack for fixed-duration UTC target buckets anchored at 1970-01-01T00:00:00Z, plus one potentially live final candle. Exclude that still-open candle before calling AggregateCandlesToTimeframe or Compute; both APIs expect closed candles.
type Mode string
const (
ModeLegacy Mode = "legacy"
ModeZones Mode = "zone"
)
type Options struct {
Timeframe string
Lookback int
Mode Mode
Tolerance float64
MinStrength int
}
func Compute(candles []Candle, opts Options) (Levels, error)
func EmptyLevels(timeframe string) Levels
func AggregateCandlesToTimeframe(candles []Candle, fromInterval, toInterval string) []Candle
func WarmupCandles(lookback int, mode Mode) int
func RequiredKlineLimit(baseInterval, targetInterval string, lookback int, mode Mode) intSee the standalone program in examples/basic, runnable package examples in examples_test.go, and the generated API documentation on pkg.go.dev.
Computeis deterministic for the same candle prefix and options.- The zero value of
Options.ModeselectsModeLegacy. Options.Lookback <= 0uses all supplied candle history in both modes.- Unknown modes cause
Computeto returnEmptyLevels(opts.Timeframe)plus an error. WarmupCandlesandRequiredKlineLimitrequire a positive, bounded lookback and return0when a finite size cannot be provided, including for non-positive lookbacks and existing invalid-input cases.- Zone-mode pivots are confirmation-based; no future candles are read beyond the current prefix.
AggregateCandlesToTimeframeuses fixed-duration UTC buckets anchored at1970-01-01T00:00:00Z. Only complete contiguous target buckets are emitted; partial or gapped buckets are dropped.- For positive lookbacks,
RequiredKlineLimitincludes enough raw candles to preserve the higher-timeframe warmup after alignment to that bucket grid, plus one potentially live candle for exchange REST responses. - Callers must exclude still-open candles before passing data to
AggregateCandlesToTimeframeorCompute. - Supported interval strings use
<n><unit>withm,h, ord; the target interval must be larger than and evenly divisible by the base interval. NearSupport/NearResistancedescribe whether the nearest level on each side is within the mode-specific near threshold.- In zone mode, the near threshold is
2 ×the zone half-width; zero-width zones fall back to0.1%of the absolute current price. - In legacy mode, the near threshold is
Tolerance × |close|.
This module owns:
- Closed-candle S/R detection
- Legacy line-based and zone-based S/R modes
- Deterministic nearest support/resistance metadata
- S/R-specific multi-timeframe aggregation, warmup sizing, and fetch sizing
This module intentionally does not own:
- Exchange or Binance parsing
- Strategy scoring or trade evaluation
- App-specific timeframe policy
- Order execution
Keeping exchange and strategy concerns outside the package makes go-sr usable across backtest engines, bots, and brokers.
The repository includes a BTC fixture and an HTML chart generator for visually inspecting detected zones:
GO_SR_CHART=/tmp/go-sr-btc-5m.html \
go test -run TestGenerateManualSRChart -count=1 -v
xdg-open /tmp/go-sr-btc-5m.htmlOptional overrides:
GO_SR_CHART_TIMEFRAME=15m
GO_SR_CHART_MODE=legacy
GO_SR_CHART_LOOKBACK=80
GO_SR_CHART_WINDOW=300
GO_SR_CHART_MIN_STRENGTH=1CI runs on pull requests and pushes to main.
Required status checks on main are:
Minimum Go Compatibility— runs the test suite on the minimum Go version declared bygo.modCurrent Go Quality Gate— runsgofmt, tests, race detection,go vet, Staticcheck, golangci-lint, the statement-coverage floor, and fuzz smoke testsAPI Compatibility— rejects backward-incompatible exported Go API changes against the latest stable release
Additional non-required CI checks are:
Root Vulnerability Scan— runsgovulncheckon the root module with current stable GoBBGO Example Compatibility— verifies the root module remains BBGO-free and compiles the isolated BBGO adapter module
Issues and pull requests are welcome. See CONTRIBUTING.md before making a change, and use SECURITY.md for security reports.
If you are using go-sr in a project, opening a discussion or issue with your use case is also useful feedback for the API.
Apache-2.0. See LICENSE.
