backtest

package
v0.0.0-...-6deb405 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 12, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Agent replay mode for backtesting with trading agents

Package backtest provides a backtesting framework for trading strategies

Performance metrics calculation for backtesting

Parameter optimization for backtesting strategies

HTML report generation for backtest results

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExportResults

func ExportResults(engine *Engine, filePath string) error

ExportResults exports backtest results to JSON file

func GenerateReport

func GenerateReport(metrics *Metrics) string

GenerateReport generates a human-readable performance report

func GetRecommendation

func GetRecommendation(kellyPercent float64) string

GetRecommendation provides interpretation of Kelly percentage

Types

type Agent

type Agent interface {
	// GetName returns the agent's unique identifier
	GetName() string

	// Analyze receives market data and returns a trading signal
	Analyze(ctx context.Context, data *MarketData) (*Signal, error)

	// Reset resets the agent's internal state (for multi-run backtests)
	Reset() error
}

Agent represents a trading agent that can generate signals

type AgentPerformance

type AgentPerformance struct {
	AgentName        string  `json:"agent_name"`
	SignalsGenerated int     `json:"signals_generated"`
	BuySignals       int     `json:"buy_signals"`
	SellSignals      int     `json:"sell_signals"`
	HoldSignals      int     `json:"hold_signals"`
	SignalsExecuted  int     `json:"signals_executed"` // Signals that resulted in trades
	AvgConfidence    float64 `json:"average_confidence"`
	CorrectSignals   int     `json:"correct_signals"`   // Signals that led to profitable trades
	IncorrectSignals int     `json:"incorrect_signals"` // Signals that led to losing trades
	Accuracy         float64 `json:"accuracy"`          // Percentage of correct signals
}

AgentPerformance tracks individual agent performance during backtest

type AgentReplayAdapter

type AgentReplayAdapter struct {
	// contains filtered or unexported fields
}

AgentReplayAdapter adapts trading agents to work with the backtest engine

func NewAgentReplayAdapter

func NewAgentReplayAdapter(consensus ConsensusStrategy) *AgentReplayAdapter

NewAgentReplayAdapter creates a new agent replay adapter

func (*AgentReplayAdapter) AddAgent

func (a *AgentReplayAdapter) AddAgent(agent Agent) error

AddAgent registers an agent for replay

func (*AgentReplayAdapter) Finalize

func (a *AgentReplayAdapter) Finalize(engine *Engine) error

Finalize implements the Strategy interface for the backtest engine

func (*AgentReplayAdapter) GenerateSignals

func (a *AgentReplayAdapter) GenerateSignals(engine *Engine) ([]*Signal, error)

GenerateSignals implements the Strategy interface for the backtest engine

func (*AgentReplayAdapter) GetAgentMetrics

func (a *AgentReplayAdapter) GetAgentMetrics() map[string]*AgentPerformance

GetAgentMetrics returns performance metrics for all agents

func (*AgentReplayAdapter) Initialize

func (a *AgentReplayAdapter) Initialize(engine *Engine) error

Initialize implements the Strategy interface for the backtest engine

func (*AgentReplayAdapter) PrintAgentReport

func (a *AgentReplayAdapter) PrintAgentReport() string

PrintAgentReport prints a summary of agent performance

func (*AgentReplayAdapter) SetContext

func (a *AgentReplayAdapter) SetContext(key string, value interface{})

SetContext sets shared context data for all agents

type BacktestConfig

type BacktestConfig struct {
	InitialCapital float64
	CommissionRate float64
	PositionSizing string // "fixed", "percent", "kelly"
	PositionSize   float64
	MaxPositions   int
	StartDate      time.Time
	EndDate        time.Time
	Symbols        []string
}

BacktestConfig holds configuration for a backtest

type Candlestick

type Candlestick struct {
	Symbol    string    `json:"symbol"`
	Timestamp time.Time `json:"timestamp"`
	Open      float64   `json:"open"`
	High      float64   `json:"high"`
	Low       float64   `json:"low"`
	Close     float64   `json:"close"`
	Volume    float64   `json:"volume"`
}

Candlestick represents OHLCV data for a time period

func LoadFromCSV

func LoadFromCSV(filePath string) ([]*Candlestick, error)

LoadFromCSV loads historical data from a CSV file CSV format: timestamp,symbol,open,high,low,close,volume timestamp can be Unix timestamp (integer) or RFC3339 string

func LoadFromJSON

func LoadFromJSON(filePath string) ([]*Candlestick, error)

LoadFromJSON loads historical data from a JSON file JSON format: array of candlestick objects or object with "candles" array

type ClosedPosition

type ClosedPosition struct {
	Symbol      string        `json:"symbol"`
	Side        string        `json:"side"`
	EntryTime   time.Time     `json:"entry_time"`
	ExitTime    time.Time     `json:"exit_time"`
	EntryPrice  float64       `json:"entry_price"`
	ExitPrice   float64       `json:"exit_price"`
	Quantity    float64       `json:"quantity"`
	RealizedPL  float64       `json:"realized_pl"`
	ReturnPct   float64       `json:"return_pct"`
	HoldingTime time.Duration `json:"holding_time"`
	Commission  float64       `json:"commission"`
}

ClosedPosition represents a closed position with P&L

type ConsensusStrategy

type ConsensusStrategy string

ConsensusStrategy defines how signals from multiple agents are combined

const (
	ConsensusMajority  ConsensusStrategy = "majority"  // Follow majority vote
	ConsensusUnanimous ConsensusStrategy = "unanimous" // All agents must agree
	ConsensusWeighted  ConsensusStrategy = "weighted"  // Weight by agent confidence
	ConsensusFirst     ConsensusStrategy = "first"     // Use first agent only
	ConsensusAll       ConsensusStrategy = "all"       // Execute all agent signals independently
)

type Engine

type Engine struct {
	// Configuration
	InitialCapital float64 `json:"initial_capital"`
	CommissionRate float64 `json:"commission_rate"` // e.g., 0.001 for 0.1%
	PositionSizing string  `json:"position_sizing"` // "fixed", "percent", "kelly"
	PositionSize   float64 `json:"position_size"`   // Amount per trade
	MaxPositions   int     `json:"max_positions"`   // Maximum concurrent positions

	// State
	Cash            float64              `json:"cash"`
	Positions       map[string]*Position `json:"positions"` // symbol -> position
	Trades          []*Trade             `json:"trades"`
	ClosedPositions []*ClosedPosition    `json:"closed_positions"`
	EquityCurve     []*EquityPoint       `json:"equity_curve"`

	// Historical data
	Data         map[string][]*Candlestick `json:"-"` // symbol -> candlesticks
	CurrentIndex map[string]int            `json:"-"` // symbol -> current index

	// Statistics (calculated during backtest)
	TotalTrades    int     `json:"total_trades"`
	WinningTrades  int     `json:"winning_trades"`
	LosingTrades   int     `json:"losing_trades"`
	TotalProfit    float64 `json:"total_profit"`
	TotalLoss      float64 `json:"total_loss"`
	MaxDrawdown    float64 `json:"max_drawdown"`
	MaxDrawdownPct float64 `json:"max_drawdown_pct"`
	PeakEquity     float64 `json:"peak_equity"`
}

Engine is the main backtesting engine

func NewEngine

func NewEngine(config BacktestConfig) *Engine

NewEngine creates a new backtesting engine

func (*Engine) ExecuteSignal

func (e *Engine) ExecuteSignal(signal *Signal) error

ExecuteSignal executes a trading signal

func (*Engine) GetCurrentCandle

func (e *Engine) GetCurrentCandle(symbol string) (*Candlestick, error)

GetCurrentCandle returns the current candlestick for a symbol

func (*Engine) GetCurrentEquity

func (e *Engine) GetCurrentEquity() float64

GetCurrentEquity returns current portfolio equity (cash + unrealized P&L)

func (*Engine) GetHistoricalCandles

func (e *Engine) GetHistoricalCandles(symbol string, lookback int) ([]*Candlestick, error)

GetHistoricalCandles returns N candlesticks before current index

func (*Engine) LoadHistoricalData

func (e *Engine) LoadHistoricalData(symbol string, candlesticks []*Candlestick) error

LoadHistoricalData loads candlestick data for backtesting

func (*Engine) Run

func (e *Engine) Run(ctx context.Context, strategy Strategy) error

Run executes the complete backtest

func (*Engine) Step

func (e *Engine) Step(ctx context.Context) (bool, error)

Step advances the backtest by one time step

type EquityPoint

type EquityPoint struct {
	Timestamp time.Time `json:"timestamp"`
	Equity    float64   `json:"equity"`
	Cash      float64   `json:"cash"`
	Holdings  float64   `json:"holdings"`
}

EquityPoint represents portfolio equity at a point in time

type GeneticOptimizer

type GeneticOptimizer struct {
	// contains filtered or unexported fields
}

GeneticOptimizer performs genetic algorithm optimization

func NewGeneticOptimizer

func NewGeneticOptimizer(factory StrategyFactory, params []*Parameter, objective ObjectiveFunction, config BacktestConfig) *GeneticOptimizer

NewGeneticOptimizer creates a new genetic algorithm optimizer Random seed is initialized with current time for non-deterministic behavior Use SetSeed() to set a specific seed for reproducible results

func (*GeneticOptimizer) Optimize

func (opt *GeneticOptimizer) Optimize(ctx context.Context, data map[string][]*Candlestick) (*OptimizationSummary, error)

Optimize performs genetic algorithm optimization

func (*GeneticOptimizer) SetParameters

func (opt *GeneticOptimizer) SetParameters(popSize, gens int, mutRate, eliteRatio float64)

SetParameters configures genetic algorithm parameters

func (*GeneticOptimizer) SetSeed

func (opt *GeneticOptimizer) SetSeed(seed int64)

SetSeed sets a specific random seed for reproducible results This is useful for testing and debugging. If not called, a time-based seed is used.

type GridSearchOptimizer

type GridSearchOptimizer struct {
	// contains filtered or unexported fields
}

GridSearchOptimizer performs exhaustive grid search over parameter space

func NewGridSearchOptimizer

func NewGridSearchOptimizer(factory StrategyFactory, params []*Parameter, objective ObjectiveFunction, config BacktestConfig) *GridSearchOptimizer

NewGridSearchOptimizer creates a new grid search optimizer

func (*GridSearchOptimizer) Optimize

func (opt *GridSearchOptimizer) Optimize(ctx context.Context, data map[string][]*Candlestick) (*OptimizationSummary, error)

Optimize performs grid search optimization

func (*GridSearchOptimizer) SetParallelism

func (opt *GridSearchOptimizer) SetParallelism(n int)

SetParallelism sets the number of parallel workers

type HistoricalDataLoader

type HistoricalDataLoader struct {
	// contains filtered or unexported fields
}

HistoricalDataLoader loads historical candlestick data from database

func NewHistoricalDataLoader

func NewHistoricalDataLoader(database *db.DB) *HistoricalDataLoader

NewHistoricalDataLoader creates a new historical data loader

func (*HistoricalDataLoader) LoadFromDatabase

func (h *HistoricalDataLoader) LoadFromDatabase(symbol, exchange, interval string, startDate, endDate time.Time) ([]*Candlestick, error)

LoadFromDatabase loads historical data for backtesting from TimescaleDB

type KellyCalculator

type KellyCalculator struct {
	// contains filtered or unexported fields
}

KellyCalculator calculates optimal position sizes using Kelly Criterion

func NewKellyCalculator

func NewKellyCalculator(database *db.DB) *KellyCalculator

NewKellyCalculator creates a new Kelly Criterion calculator

func (*KellyCalculator) CalculatePositionSize

func (kc *KellyCalculator) CalculatePositionSize(
	stats *TradingStats,
	capital float64,
	kellyFraction float64,
) float64

CalculatePositionSize calculates optimal position size using Kelly Criterion

Kelly Criterion Formula: f* = (p * b - q) / b

Where: - f* = fraction of capital to bet (Kelly percentage) - p = probability of winning (win rate) - q = probability of losing (1 - p) - b = ratio of average win to average loss (win/loss ratio)

The formula can also be written as: f* = (W - L) / W Where W = average win, L = average loss

Kelly fraction is applied to reduce risk (typically 0.25 to 0.5 for quarter or half Kelly)

func (*KellyCalculator) CalculateStats

func (kc *KellyCalculator) CalculateStats(ctx context.Context, sessionID uuid.UUID) (*TradingStats, error)

CalculateStats computes trading statistics from historical data This can be used for database-backed statistics

type MarketData

type MarketData struct {
	Timestamp    time.Time              `json:"timestamp"`
	Symbol       string                 `json:"symbol"`
	CurrentPrice float64                `json:"current_price"`
	OHLCV        *Candlestick           `json:"current_candle"`
	History      []*Candlestick         `json:"historical_candles"`
	Indicators   map[string]float64     `json:"indicators,omitempty"`
	Context      map[string]interface{} `json:"context,omitempty"`
}

MarketData represents market data available to an agent at a point in time

type Metrics

type Metrics struct {
	// Returns
	TotalReturn      float64 `json:"total_return"`      // Total profit/loss
	TotalReturnPct   float64 `json:"total_return_pct"`  // Total return percentage
	AnnualizedReturn float64 `json:"annualized_return"` // Annualized return percentage
	CAGR             float64 `json:"cagr"`              // Compound Annual Growth Rate

	// Risk metrics
	MaxDrawdown    float64 `json:"max_drawdown"`     // Maximum drawdown in dollars
	MaxDrawdownPct float64 `json:"max_drawdown_pct"` // Maximum drawdown percentage
	Volatility     float64 `json:"volatility"`       // Standard deviation of returns
	SharpeRatio    float64 `json:"sharpe_ratio"`     // Risk-adjusted return
	SortinoRatio   float64 `json:"sortino_ratio"`    // Downside risk-adjusted return
	CalmarRatio    float64 `json:"calmar_ratio"`     // CAGR / Max Drawdown

	// Trade statistics
	TotalTrades   int     `json:"total_trades"`
	WinningTrades int     `json:"winning_trades"`
	LosingTrades  int     `json:"losing_trades"`
	WinRate       float64 `json:"win_rate"`     // Percentage of winning trades
	AverageWin    float64 `json:"average_win"`  // Average profit per winning trade
	AverageLoss   float64 `json:"average_loss"` // Average loss per losing trade
	LargestWin    float64 `json:"largest_win"`
	LargestLoss   float64 `json:"largest_loss"`
	ProfitFactor  float64 `json:"profit_factor"` // Total profit / Total loss
	Expectancy    float64 `json:"expectancy"`    // Expected value per trade

	// Time statistics
	AverageHoldingTime time.Duration `json:"average_holding_time"`
	MedianHoldingTime  time.Duration `json:"median_holding_time"`
	MaxHoldingTime     time.Duration `json:"max_holding_time"`
	MinHoldingTime     time.Duration `json:"min_holding_time"`

	// Portfolio statistics
	InitialCapital float64       `json:"initial_capital"`
	FinalEquity    float64       `json:"final_equity"`
	PeakEquity     float64       `json:"peak_equity"`
	EquityLow      float64       `json:"equity_low"`
	StartDate      time.Time     `json:"start_date"`
	EndDate        time.Time     `json:"end_date"`
	Duration       time.Duration `json:"duration"`
}

Metrics holds all performance metrics for a backtest

func CalculateMetrics

func CalculateMetrics(engine *Engine) (*Metrics, error)

CalculateMetrics calculates all performance metrics from a backtest

type ObjectiveFunction

type ObjectiveFunction func(*Metrics) float64

ObjectiveFunction calculates a fitness score from backtest metrics

var (
	// MaximizeSharpeRatio optimizes for risk-adjusted returns
	MaximizeSharpeRatio ObjectiveFunction = func(m *Metrics) float64 {
		return m.SharpeRatio
	}

	// MaximizeSortinoRatio optimizes for downside risk-adjusted returns
	MaximizeSortinoRatio ObjectiveFunction = func(m *Metrics) float64 {
		return m.SortinoRatio
	}

	// MaximizeCalmarRatio optimizes for return/max drawdown
	MaximizeCalmarRatio ObjectiveFunction = func(m *Metrics) float64 {
		return m.CalmarRatio
	}

	// MaximizeTotalReturn optimizes for absolute returns
	MaximizeTotalReturn ObjectiveFunction = func(m *Metrics) float64 {
		return m.TotalReturnPct
	}

	// MaximizeProfitFactor optimizes for profit/loss ratio
	MaximizeProfitFactor ObjectiveFunction = func(m *Metrics) float64 {
		return m.ProfitFactor
	}

	// MinimizeDrawdown optimizes for low drawdown
	MinimizeDrawdown ObjectiveFunction = func(m *Metrics) float64 {
		return -m.MaxDrawdownPct
	}

	// BalancedObjective combines multiple metrics
	BalancedObjective ObjectiveFunction = func(m *Metrics) float64 {

		sharpe := math.Max(0, m.SharpeRatio)
		winRate := m.WinRate / 100.0
		calmar := math.Max(0, m.CalmarRatio)
		return 0.4*sharpe + 0.3*winRate + 0.3*calmar
	}
)

Predefined objective functions

type OptimizationResult

type OptimizationResult struct {
	Parameters    ParameterSet `json:"parameters"`
	Metrics       *Metrics     `json:"metrics"`
	Score         float64      `json:"score"`         // Fitness score
	Rank          int          `json:"rank"`          // Rank among all results
	IsOutOfSample bool         `json:"is_out_sample"` // Walk-forward out-of-sample flag
}

OptimizationResult represents the result of a parameter optimization

type OptimizationSummary

type OptimizationSummary struct {
	Method          string                `json:"method"` // grid_search, walk_forward, genetic
	TotalRuns       int                   `json:"total_runs"`
	Duration        time.Duration         `json:"duration"`
	BestResult      *OptimizationResult   `json:"best_result"`
	TopResults      []*OptimizationResult `json:"top_results"` // Top 10 results
	ParameterRanges []*Parameter          `json:"parameter_ranges"`
	ObjectiveMetric string                `json:"objective_metric"` // What we're optimizing
	StartDate       time.Time             `json:"start_date"`
	EndDate         time.Time             `json:"end_date"`
}

OptimizationSummary summarizes an optimization run

type ParamType

type ParamType string

ParamType defines the type of parameter

const (
	ParamTypeInt    ParamType = "int"
	ParamTypeFloat  ParamType = "float"
	ParamTypeBool   ParamType = "bool"
	ParamTypeString ParamType = "string"
)

type Parameter

type Parameter struct {
	Name   string    `json:"name"`
	Type   ParamType `json:"type"`   // int, float, bool, string
	Min    float64   `json:"min"`    // For numeric types
	Max    float64   `json:"max"`    // For numeric types
	Step   float64   `json:"step"`   // Step size for grid search
	Values []string  `json:"values"` // For string/categorical types
}

Parameter represents a tunable parameter for strategy optimization

type ParameterSet

type ParameterSet map[string]interface{}

ParameterSet represents a set of parameter values

func (ParameterSet) Clone

func (ps ParameterSet) Clone() ParameterSet

Clone creates a deep copy of the parameter set

type Position

type Position struct {
	Symbol       string    `json:"symbol"`
	Side         string    `json:"side"` // "LONG", "SHORT"
	EntryTime    time.Time `json:"entry_time"`
	EntryPrice   float64   `json:"entry_price"`
	Quantity     float64   `json:"quantity"`
	CurrentPrice float64   `json:"current_price"`
	UnrealizedPL float64   `json:"unrealized_pl"`
	Commission   float64   `json:"commission"`
}

Position represents an open trading position

type ReportGenerator

type ReportGenerator struct {
	// contains filtered or unexported fields
}

ReportGenerator generates HTML reports for backtest results

func NewOptimizationReportGenerator

func NewOptimizationReportGenerator(engine *Engine, summary *OptimizationSummary) (*ReportGenerator, error)

NewOptimizationReportGenerator creates a report generator for optimization results

func NewReportGenerator

func NewReportGenerator(engine *Engine) (*ReportGenerator, error)

NewReportGenerator creates a new report generator

func (*ReportGenerator) GenerateHTML

func (r *ReportGenerator) GenerateHTML() (string, error)

GenerateHTML generates a complete HTML report

func (*ReportGenerator) SaveToFile

func (r *ReportGenerator) SaveToFile(filepath string) error

SaveToFile saves the HTML report to a file

type Signal

type Signal struct {
	Timestamp  time.Time              `json:"timestamp"`
	Symbol     string                 `json:"symbol"`
	Side       string                 `json:"side"`       // "BUY", "SELL", "HOLD"
	Confidence float64                `json:"confidence"` // 0.0 to 1.0
	Reasoning  string                 `json:"reasoning"`
	Agent      string                 `json:"agent"` // Which agent generated this signal
	Metadata   map[string]interface{} `json:"metadata,omitempty"`
}

Signal represents a trading signal from an agent

type Strategy

type Strategy interface {
	// Initialize is called before the backtest starts
	Initialize(engine *Engine) error

	// GenerateSignals generates trading signals at each time step
	GenerateSignals(engine *Engine) ([]*Signal, error)

	// Finalize is called after the backtest ends
	Finalize(engine *Engine) error
}

Strategy is the interface that trading strategies must implement

type StrategyFactory

type StrategyFactory func(params ParameterSet) (Strategy, error)

StrategyFactory creates a strategy with given parameters

type Trade

type Trade struct {
	ID         int       `json:"id"`
	Timestamp  time.Time `json:"timestamp"`
	Symbol     string    `json:"symbol"`
	Side       string    `json:"side"` // "BUY", "SELL"
	Quantity   float64   `json:"quantity"`
	Price      float64   `json:"price"`
	Commission float64   `json:"commission"`
	Value      float64   `json:"value"` // price * quantity
	Signal     *Signal   `json:"signal,omitempty"`
}

Trade represents an executed trade

type TradingStats

type TradingStats struct {
	TotalTrades   int     `json:"total_trades"`
	WinningTrades int     `json:"winning_trades"`
	LosingTrades  int     `json:"losing_trades"`
	AvgWin        float64 `json:"avg_win"`        // Average profit per winning trade
	AvgLoss       float64 `json:"avg_loss"`       // Average loss per losing trade (positive value)
	WinRate       float64 `json:"win_rate"`       // Percentage of winning trades (0.0 to 1.0)
	AvgReturn     float64 `json:"avg_return"`     // Average return per trade
	TotalProfit   float64 `json:"total_profit"`   // Total profit from all winning trades
	TotalLoss     float64 `json:"total_loss"`     // Total loss from all losing trades (positive value)
	LargestWin    float64 `json:"largest_win"`    // Largest single win
	LargestLoss   float64 `json:"largest_loss"`   // Largest single loss (positive value)
	WinLossRatio  float64 `json:"win_loss_ratio"` // AvgWin / AvgLoss
}

TradingStats holds statistical data for Kelly Criterion calculation

func CalculateStatsFromTrades

func CalculateStatsFromTrades(closedPositions []*ClosedPosition) *TradingStats

CalculateStatsFromTrades computes trading statistics from in-memory trades This is useful for backtesting where we don't have database access

type WalkForwardOptimizer

type WalkForwardOptimizer struct {
	// contains filtered or unexported fields
}

WalkForwardOptimizer performs walk-forward analysis

func NewWalkForwardOptimizer

func NewWalkForwardOptimizer(factory StrategyFactory, params []*Parameter, objective ObjectiveFunction, config BacktestConfig) *WalkForwardOptimizer

NewWalkForwardOptimizer creates a new walk-forward optimizer

func (*WalkForwardOptimizer) Optimize

func (opt *WalkForwardOptimizer) Optimize(ctx context.Context, data map[string][]*Candlestick) (*OptimizationSummary, error)

Optimize performs walk-forward optimization

func (*WalkForwardOptimizer) SetPeriods

func (opt *WalkForwardOptimizer) SetPeriods(inSample, outSample time.Duration)

SetPeriods sets the in-sample and out-of-sample periods

type WalkForwardWindow

type WalkForwardWindow struct {
	InSampleStart  time.Time
	InSampleEnd    time.Time
	OutSampleStart time.Time
	OutSampleEnd   time.Time
}

WalkForwardWindow represents a training/testing window

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL