Documentation
¶
Overview ¶
Package backtest is an idiomatic-Go reimplementation of Python's backtesting.py with faithful numerical semantics.
Note: the import path is github.com/florinel-chis/gobacktest but the package name is backtest, so callers use backtest.New, backtest.Order, etc.
Index ¶
- Variables
- type Backtest
- type Bar
- type Commission
- type CommissionFunc
- type Data
- func (d *Data) AddColumn(name string, vals []float64) error
- func (d *Data) Bars() []Bar
- func (d *Data) Close() Series
- func (d *Data) Column(name string) (Series, bool)
- func (d *Data) FullLen() int
- func (d *Data) High() Series
- func (d *Data) Len() int
- func (d *Data) Low() Series
- func (d *Data) Open() Series
- func (d *Data) Time() []time.Time
- func (d *Data) TimeAt(i int) time.Time
- func (d *Data) Volume() Series
- type EquityPoint
- type HeatmapEntry
- type Indicator
- type IndicatorOption
- type IndicatorSeries
- type MultiBacktest
- type OpenTrade
- type OptimizeOptions
- type OptimizeResult
- type Options
- type Order
- type OrderHandle
- type Params
- type Position
- type Result
- type Series
- type State
- func (s *State) Buy(o Order) OrderHandle
- func (s *State) ClosedTrades() []Trade
- func (s *State) Data() *Data
- func (s *State) Equity() float64
- func (s *State) I(name string, compute func() []float64, opts ...IndicatorOption) *Indicator
- func (s *State) OpenTrades() []*OpenTrade
- func (s *State) Orders() []OrderHandle
- func (s *State) Position() *Position
- func (s *State) Sell(o Order) OrderHandle
- func (s *State) Trades() []Trade
- type Stats
- type Strategy
- type Trade
Constants ¶
This section is empty.
Variables ¶
var ErrNoBars = errors.New("backtest: data has no bars")
ErrNoBars is returned by Backtest.Run when the data contains zero bars (e.g. a Data built from empty slices), so there is nothing to run on.
var ErrOutOfMoney = errors.New("backtest: out of money")
ErrOutOfMoney is returned by Backtest.Run when available equity goes to zero (the account is liquidated). On this path the equity curve ends at 0 and Stats.EquityFinal is 0.
Functions ¶
This section is empty.
Types ¶
type Backtest ¶
type Backtest struct {
// contains filtered or unexported fields
}
Backtest binds data + strategy + options.
type Commission ¶
type Commission interface {
// contains filtered or unexported methods
}
Commission computes the cash cost of trading `size` units at `price`.
func FixedPlusPct ¶
func FixedPlusPct(fixed, rate float64) Commission
FixedPlusPct charges a fixed amount plus a relative commission.
func Pct ¶
func Pct(rate float64) Commission
Pct charges a relative commission: |size| * price * rate.
type CommissionFunc ¶
CommissionFunc adapts a function to the Commission interface.
type Data ¶
type Data struct {
// contains filtered or unexported fields
}
Data holds OHLCV as typed columns (struct-of-arrays). The current-bar view length n is advanced by the engine via setLen; accessors return zero-copy subslices up to n. NOTE: update clone() when adding fields.
func FromCSV ¶
FromCSV loads OHLCV from a CSV with a header row. Required columns (case-insensitive): Date, Open, High, Low, Close, Volume. Dates are parsed as "2006-01-02" or RFC3339.
func FromOHLCV ¶
FromOHLCV builds Data from parallel columns. All slices must be equal length. The view starts at full length. FromOHLCV retains (does not copy) the provided slices; callers must not mutate them after construction.
func (*Data) AddColumn ¶
AddColumn attaches a named float64 column (e.g. an indicator) aligned to bars.
func (*Data) Bars ¶
Bars returns all OHLCV bars as a slice of Bar (full dataset, not view-limited). Use this outside a run to access the complete data for report generation.
func (*Data) FullLen ¶
FullLen returns the total number of bars in the dataset (unaffected by the current bar-view length used during a run).
type EquityPoint ¶
EquityPoint is one bar of the equity curve.
type HeatmapEntry ¶
HeatmapEntry holds one combo's parameters, metric value, and full stats. Params is read-only. Stats is the zero Stats when Value == -math.MaxFloat64 (combo ran out of money).
type Indicator ¶
type Indicator struct {
// contains filtered or unexported fields
}
Indicator is a live handle to a precomputed column that always reflects the current bar via the data view length. Strategies store these in Init and read them in Next.
type IndicatorOption ¶
type IndicatorOption func(*Indicator)
IndicatorOption configures a registered indicator's plot metadata.
func Color ¶
func Color(hex string) IndicatorOption
Color sets the indicator's line color (hex, e.g. "#2962FF").
func Overlay ¶
func Overlay() IndicatorOption
Overlay draws the indicator on the price pane (e.g. a moving average).
type IndicatorSeries ¶
IndicatorSeries is a registered indicator's values + plot metadata (for reports).
type MultiBacktest ¶
type MultiBacktest struct {
// contains filtered or unexported fields
}
MultiBacktest runs a single strategy across many datasets in parallel — e.g. a portfolio of symbols or walk-forward folds. Mirrors backtesting.py's lib.MultiBacktest. build must return a FRESH Strategy on each call (each dataset gets its own strategy instance, like Optimize) since a Strategy holds per-run indicator state.
func NewMultiBacktest ¶
func NewMultiBacktest(datasets []*Data, build func() Strategy, opts Options) *MultiBacktest
NewMultiBacktest constructs a MultiBacktest over the given datasets. datasets and build are used as-is; build is called once per dataset (concurrently) to obtain a fresh Strategy instance.
func (*MultiBacktest) MeanStat ¶
MeanStat runs every dataset (via Stats) and returns the mean of one metric across all datasets, skipping NaN values. sel extracts the metric from a Stats value, e.g. func(s Stats) float64 { return s.ReturnPct }. Returns NaN if there are no datasets or every selected value is NaN.
func (*MultiBacktest) Run ¶
func (m *MultiBacktest) Run() ([]*Result, error)
Run backtests every dataset concurrently and returns one *Result per dataset in the SAME ORDER as the input datasets slice. If any dataset errors, Run returns the first error (by ascending dataset index, deterministic regardless of goroutine completion order) and a nil slice.
An empty datasets slice returns (nil slice, nil error) — there is nothing to run and nothing to report as an error.
Race safety: each worker calls build() to obtain its own Strategy and runs a per-run clone of datasets[i] — like Optimize — because a run mutates the dataset's bar-view length and indicator columns. Cloning makes it safe to pass the same *Data in several slots (or keep using it after Run); the underlying OHLCV slices are shared read-only, so clones are cheap. Results are written into a pre-allocated slice at the worker's assigned index.
func (*MultiBacktest) Stats ¶
func (m *MultiBacktest) Stats(riskFreeRate float64) ([]Stats, error)
Stats runs every dataset and computes per-dataset Stats (same order as the input datasets slice).
func (*MultiBacktest) Workers ¶
func (m *MultiBacktest) Workers(n int) *MultiBacktest
Workers sets the goroutine pool size (0 → runtime.GOMAXPROCS(0)). Returns the receiver for chaining.
type OpenTrade ¶
type OpenTrade struct {
// contains filtered or unexported fields
}
OpenTrade is a live, mutable handle to a currently-open trade (for trailing stops and other dynamic SL/TP logic). Obtain via State.OpenTrades() and use it within the same bar — once the underlying trade closes, the handle becomes inert (the broker no longer reads its SL/TP). Re-fetch OpenTrades() each Next().
func (*OpenTrade) EntryPrice ¶
type OptimizeOptions ¶
type OptimizeOptions struct {
// Maximize is the stat name to maximise (e.g. "SQN", "Return [%]").
// Ignored when MaximizeFunc is set. Defaults to "SQN" when both are zero.
Maximize string
// MaximizeFunc is an optional custom metric extractor. When non-nil it takes
// precedence over Maximize.
MaximizeFunc func(Stats) float64
// Constraint filters combos before evaluation; nil means accept all.
// Workers MUST NOT mutate the Params they receive — treat them as read-only.
Constraint func(Params) bool
// MaxTries limits random sampling: 0 or >= len(combos) → full grid.
MaxTries int
// RandomSeed seeds the sampler (deterministic for a given seed).
RandomSeed int64
// Workers is the goroutine pool size; 0 → runtime.GOMAXPROCS(0).
Workers int
// ReturnHeatmap populates OptimizeResult.Heatmap for every evaluated combo.
ReturnHeatmap bool
// RiskFreeRate is the annualised risk-free rate forwarded to Compute.
RiskFreeRate float64
}
OptimizeOptions configures how Optimize explores the parameter space.
type OptimizeResult ¶
type OptimizeResult struct {
// Best is the parameter combination that achieved BestValue (first in
// enumeration order when multiple combos tie). Nil only when no combo ran.
// Best is read-only; it aliases a heatmap entry's Params.
Best Params
BestStats Stats
BestValue float64
// Heatmap is populated (in enumeration order) when OptimizeOptions.ReturnHeatmap
// is true.
Heatmap []HeatmapEntry
}
OptimizeResult is the output of Optimize.
func Optimize ¶
func Optimize( data *Data, opts Options, build func(Params) Strategy, space map[string][]any, oo OptimizeOptions, ) (*OptimizeResult, error)
Optimize runs a backtest for every parameter combination in space, using a bounded worker pool for parallelism, and returns the best result together with an optional heatmap.
The build factory is called concurrently (one fresh Strategy per combo) and must not capture or mutate shared state. The Params passed to build are read-only.
Race safety: each worker operates on its own data.clone() and writes results into a pre-allocated slice at its assigned index, protected by a mutex. Deterministic best: results are scanned in enumeration order after all workers finish, so the outcome is reproducible regardless of goroutine scheduling.
Returns an error if space is empty or all combos are filtered out; propagates any non-ErrOutOfMoney run error. ErrOutOfMoney is treated as the worst possible metric (-math.MaxFloat64) rather than a fatal error.
type Options ¶
type Options struct {
Cash float64
Spread float64
Commission Commission
Margin float64
TradeOnClose bool
Hedging bool
ExclusiveOrders bool
FinalizeTrades bool
}
Options configures a Backtest run.
type Order ¶
Order is the public specification passed to State.Buy / State.Sell. Size is a fraction in (0,1] of available equity, or absolute units >= 1. Zero-valued Limit/Stop/SL/TP mean "unset".
type OrderHandle ¶
type OrderHandle struct {
// contains filtered or unexported fields
}
OrderHandle is a cancelable reference to a queued order returned by Buy/Sell.
func (OrderHandle) Cancel ¶
func (h OrderHandle) Cancel()
Cancel cancels the queued order; the broker skips it on the next pass.
func (OrderHandle) Size ¶
func (h OrderHandle) Size() float64
Size is the signed size of the queued order (positive long, negative short).
type Position ¶
type Position struct {
// contains filtered or unexported fields
}
Position is a live view over the broker's open trades.
func (*Position) Close ¶
func (p *Position) Close()
Close queue-closes all open trades. Matching backtesting.py, each close is a market order filled at the NEXT bar's open (not synchronously), so it never introduces look-ahead. The position remains open during the current bar.
type Result ¶
type Result struct {
EquityCurve []EquityPoint
Trades []Trade
FinalEquity float64
StartBar int
// InPositionBars[k] is whether a position was open at equity-curve point k,
// recorded after strategy.Next (a same-bar Buy is not yet filled). Note: the
// ExitBar of a deferred close reads false here; exposure is derived from trades.
InPositionBars []bool
// Indicators holds each registered indicator's values and plot metadata.
Indicators []IndicatorSeries
}
Result is the raw timeline produced by a run. Statistics (Plan 3) are computed from this.
type Series ¶
type Series []float64
Series is a float64 column aligned to bars. The "current bar" is the last element; a per-bar view is a zero-copy subslice (s[:n]).
func (Series) At ¶
At returns the value i bars back from the end. At(0) == Last(). It panics if i is out of range (i<0 or i>=Len()).
type State ¶
type State struct {
// contains filtered or unexported fields
}
State is the per-run context passed to Init/Next.
func (*State) Buy ¶
func (s *State) Buy(o Order) OrderHandle
Buy enqueues a long order. Size must be > 0.
func (*State) ClosedTrades ¶
func (*State) I ¶
func (s *State) I(name string, compute func() []float64, opts ...IndicatorOption) *Indicator
I registers an indicator: compute runs once (full data view), the result must match the data's full length, and the returned handle auto-slices per bar. Optional IndicatorOption values (Overlay, Color) attach plot metadata for reports.
func (*State) OpenTrades ¶
OpenTrades returns mutable handles to the currently-open trades.
func (*State) Orders ¶
func (s *State) Orders() []OrderHandle
func (*State) Sell ¶
func (s *State) Sell(o Order) OrderHandle
Sell enqueues a short order. Size must be > 0 (enqueued as negative).
type Stats ¶
type Stats struct {
Start time.Time
End time.Time
Duration time.Duration
ExposureTimePct float64
EquityFinal float64
EquityPeak float64
ReturnPct float64
BuyHoldReturnPct float64
NumTrades int
// --- added by later tasks (declared now for a stable struct) ---
ReturnAnnPct float64
VolatilityAnnPct float64
CAGRPct float64
SharpeRatio float64
SortinoRatio float64
CalmarRatio float64
AlphaPct float64
Beta float64
MaxDrawdownPct float64
AvgDrawdownPct float64
MaxDrawdownDur time.Duration
AvgDrawdownDur time.Duration
WinRatePct float64
BestTradePct float64
WorstTradePct float64
AvgTradePct float64
MaxTradeDur time.Duration
AvgTradeDur time.Duration
ProfitFactor float64
ExpectancyPct float64
SQN float64
KellyCriterion float64
CommissionsTotal float64
}
Stats holds the computed performance metrics for a backtest run. Fields are added across Plan 3 tasks; unimplemented fields stay zero until their task.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
example
command
Command example is the README quickstart as a runnable program.
|
Command example is the README quickstart as a runnable program. |
|
genheatmap
command
Command genheatmap optimizes an SMA-crossover over an n1×n2 grid on the bundled AAPL data and writes a self-contained HTML heatmap — a demo of report.Heatmap.
|
Command genheatmap optimizes an SMA-crossover over an n1×n2 grid on the bundled AAPL data and writes a self-contained HTML heatmap — a demo of report.Heatmap. |
|
genreport
command
Command genreport runs an SMA-crossover backtest on the bundled AAPL data and writes a self-contained HTML report — a demo of the report package.
|
Command genreport runs an SMA-crossover backtest on the bundled AAPL data and writes a self-contained HTML report — a demo of the report package. |
|
Package costs computes trading costs the engine does not model natively (per-trade holding/financing cost).
|
Package costs computes trading costs the engine does not model natively (per-trade holding/financing cost). |
|
Package indicators wraps github.com/markcheno/go-talib with NaN-warmup and composition support (so indicators chain, e.g.
|
Package indicators wraps github.com/markcheno/go-talib with NaN-warmup and composition support (so indicators chain, e.g. |
|
Package lib provides strategy helpers (crossovers) over indicator series.
|
Package lib provides strategy helpers (crossovers) over indicator series. |
|
Package report generates self-contained HTML backtest reports embedding TradingView Lightweight Charts v5 (vendored) via go:embed.
|
Package report generates self-contained HTML backtest reports embedding TradingView Lightweight Charts v5 (vendored) via go:embed. |
|
Package source defines a provider-agnostic contract for fetching OHLCV market data.
|
Package source defines a provider-agnostic contract for fetching OHLCV market data. |
|
Package strategies provides reusable, parameterized Strategy implementations shared by the demo and research commands.
|
Package strategies provides reusable, parameterized Strategy implementations shared by the demo and research commands. |