Documentation ¶
Overview ¶
Package backtest provides a simultated dealer implementation for running backtests.
Example ¶
nolint
// Verbose error handling omitted for brevity // Create a special simulated dealer for backtesting with initial capital of 1000 dealer := NewDealer() dealer.SetInitialCapital(dec.New(1000)) // Identify the asset to trade asset := market.NewAsset("BTCUSD") // Read a .csv file of historical prices (aka candlestick data) file, _ := os.Open("testdata/BTCUSDT-1h-2021-Q1.csv") defer file.Close() reader := market.NewCSVKlineReader(csv.NewReader(file)) // Iterate prices sending each price interval to the backtest dealer // When connected to a live exchange we would not be required to supply the price to the dealer! var i int for { price, err := reader.Read() if err == io.EOF { break } dealer.ReceivePrice(context.Background(), price) // Place an order for 1 BTC at start of the price series if i == 0 { dealer.PlaceOrder(context.Background(), broker.NewOrder(asset, broker.Buy, dec.New(1))) } i++ } // Close the position and create the trade dealer.PlaceOrder(context.Background(), broker.NewOrder(asset, broker.Sell, dec.New(1))) // Generate a performance report from the dealer execution history trades, _, _ := dealer.ListTrades(context.Background(), nil) equity := dealer.EquityHistory() report := perf.NewPerformanceReport(trades, equity)
Output: Your backtest return is 2974.54%
Index ¶
- Variables
- func MakeDealerFromConfig(config map[string]any) (broker.SimulatedDealer, error)
- type Clock
- type Clocker
- type Coster
- type Dealer
- func (d *Dealer) CancelOrders(ctx context.Context) (*web.Response, error)
- func (d *Dealer) EquityHistory() broker.EquitySeries
- func (d *Dealer) GetBalance(ctx context.Context) (*broker.AccountBalance, *web.Response, error)
- func (d *Dealer) ListPositions(ctx context.Context, opts *web.ListOpts) ([]broker.Position, *web.Response, error)
- func (d *Dealer) ListTrades(ctx context.Context, opts *web.ListOpts) ([]broker.Trade, *web.Response, error)
- func (d *Dealer) PlaceOrder(ctx context.Context, order broker.Order) (*broker.Order, *web.Response, error)
- func (d *Dealer) ReceivePrice(ctx context.Context, price market.Kline) error
- func (d *Dealer) SetInitialCapital(amount decimal.Decimal)
- type PerpCoster
- func (c *PerpCoster) Funding(position broker.Position, price decimal.Decimal, elapsed time.Duration) decimal.Decimal
- func (c *PerpCoster) Slippage(price decimal.Decimal) decimal.Decimal
- func (c *PerpCoster) Spread(price decimal.Decimal) decimal.Decimal
- func (c *PerpCoster) Transaction(order broker.Order) decimal.Decimal
- type Simulator
- func (s *Simulator) AddOrder(order broker.Order) (broker.Order, error)
- func (s *Simulator) Balance() broker.AccountBalance
- func (s *Simulator) CancelOrders() []broker.Order
- func (s *Simulator) EquityHistory() broker.EquitySeries
- func (s *Simulator) Next(price market.Kline) error
- func (s *Simulator) Orders() []broker.Order
- func (s *Simulator) Positions() []broker.Position
- func (s *Simulator) SetInitialCapital(amount decimal.Decimal)
- func (s *Simulator) Trades() []broker.Trade
- type StubClock
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidOrderState = errors.New("order is not valid for processing")
ErrInvalidOrderState is returned when an order is not in a valid state for the simulator to open it.
var ErrRejectedOrder = errors.New("order rejected during processing")
ErrRejectedOrder is returned when an order is rejected during processing due to an exceptional condition.
Functions ¶
func MakeDealerFromConfig ¶
func MakeDealerFromConfig(config map[string]any) (broker.SimulatedDealer, error)
MakeDealerFromConfig mints a new dealer from a config source.
Types ¶
type Clock ¶
type Clock struct {
// contains filtered or unexported fields
}
Clock is the default Clocker implementation for Simulation. When Now is called an incrementally later time is returned. Each increment is a 'tock' which defaults to 1 millisecond. Tock term is used to avoid confusion with 'tick' which has a defined meaning in trading. Clock helps ensure orders are processed in the sequence they are submitted.
func NewClock ¶
func NewClock() *Clock
NewClock sets the start to the zero time and tock interval to 1 millisecond.
func (*Clock) Advance ¶
Advance advances to the next epoch at the given time. When Now is next called it will be epoch + 1 tock interval. Undefined behaviour if the given epoch is earlier than the current.
func (*Clock) Elapsed ¶
Elapsed returns the total elapsed duration since the start. Elapsed time is calculated on each call to Advance. Primarily used for calculating funding charges.
type Clocker ¶
type Clocker interface { // Resets the clock with a new start time and tock interval. Start(time.Time, time.Duration) // Advance advances the simulation clock to a future time. Advance(time.Time) // Now returns an incrementally later time each call. // Increments are defined by the tock interval given to Start. // Returned time should always be >= the start time or latest advance time. Now() time.Time // Peek returns the current time (last value returned by Now()) // but does not advance the time by the tock interval. Peek() time.Time // Elapsed returns the total duration since the start time. Elapsed() time.Duration }
Clocker defines a clock to be used by Simulation to timestamp events.
type Coster ¶
type Coster interface { Slippage(price decimal.Decimal) decimal.Decimal Spread(price decimal.Decimal) decimal.Decimal Transaction(broker.Order) decimal.Decimal Funding(position broker.Position, price decimal.Decimal, elapsed time.Duration) decimal.Decimal }
Coster is a cost model used by a dealer to apply trading charges and fees.
type Dealer ¶
type Dealer struct {
// contains filtered or unexported fields
}
Dealer is a SimulatedDealer implementation for backtesting.
func NewDealer ¶
func NewDealer() *Dealer
NewDealer creates a new Dealer with a default simulator state.
func NewDealerWithCost ¶
NewDealerWithCost creates a new Dealer with the given cost model.
func (*Dealer) CancelOrders ¶
CancelOrders cancels all open (resting) orders on the dealer.
func (*Dealer) EquityHistory ¶
func (d *Dealer) EquityHistory() broker.EquitySeries
EquityHistory returns the equity history (equity curve) of the dealer.
func (*Dealer) GetBalance ¶
GetBalance returns the current balance of the dealer.
func (*Dealer) ListPositions ¶
func (d *Dealer) ListPositions(ctx context.Context, opts *web.ListOpts) ([]broker.Position, *web.Response, error)
ListPositions returns all historical (closed) and open positions.
func (*Dealer) ListTrades ¶
func (d *Dealer) ListTrades(ctx context.Context, opts *web.ListOpts) ([]broker.Trade, *web.Response, error)
ListTrades returns all historical trades.
func (*Dealer) PlaceOrder ¶
func (d *Dealer) PlaceOrder(ctx context.Context, order broker.Order) (*broker.Order, *web.Response, error)
PlaceOrder places an order on the dealer.
func (*Dealer) ReceivePrice ¶
ReceivePrice initiates processing by supplying the next market price to the simulator.
func (*Dealer) SetInitialCapital ¶
SetInitialCapital sets the initial trading balance for the dealer.
type PerpCoster ¶
type PerpCoster struct { SpreadPct decimal.Decimal SlippagePct decimal.Decimal TransactionPct decimal.Decimal FundingHourPct decimal.Decimal // contains filtered or unexported fields }
PerpCoster implements the Coster interface for Perpetual Future style assets.
func (*PerpCoster) Funding ¶
func (c *PerpCoster) Funding(position broker.Position, price decimal.Decimal, elapsed time.Duration) decimal.Decimal
Funding returns the funding fee for a position, calculated on an hourly basis.
func (*PerpCoster) Slippage ¶
func (c *PerpCoster) Slippage(price decimal.Decimal) decimal.Decimal
Slippage returns the cost of slippage incurred by an order. Slippage is a fraction of the order price.
func (*PerpCoster) Spread ¶
func (c *PerpCoster) Spread(price decimal.Decimal) decimal.Decimal
Spread returns the cost of the spread incurred by an order. Spread is a fraction of the order price.
func (*PerpCoster) Transaction ¶
func (c *PerpCoster) Transaction(order broker.Order) decimal.Decimal
Transaction returns the cost of a transaction, calculated as a fraction of the order price and size.
type Simulator ¶
type Simulator struct {
// contains filtered or unexported fields
}
Simulator is a backtest simulator that simulates the execution of orders against a market. Only a single position can be opened at a time, and must be closed in full before another can be opened. Partial fills are not supported. Account balance can go negative and trading will continue. Inspect the equity curve to understand equity change over time and the capital requirements for the algo. To advance the simulation call Next() with the next market price. Market and Limit orders are supported. Market orders execute immediately with the last available close price. To place a stop loss or take profit style order use a limit order with 'ReduceOnly' set to true.
func NewSimulator ¶
func NewSimulator() *Simulator
NewSimulator create a new backtest simulator with zero cost model.
func NewSimulatorWithCost ¶
NewSimulatorWithCost creates a new backtest simulator with the given cost model.
func (*Simulator) AddOrder ¶
AddOrder adds an order to the simulator and returns the processed order or an error.
func (*Simulator) Balance ¶
func (s *Simulator) Balance() broker.AccountBalance
Balance returns the current account balance.
func (*Simulator) CancelOrders ¶
CancelOrders cancels all open orders and returns the cancelled orders.
func (*Simulator) EquityHistory ¶
func (s *Simulator) EquityHistory() broker.EquitySeries
EquityHistory returns a copy of the equity curve.
func (*Simulator) SetInitialCapital ¶
SetInitialCapital sets the initial trade balance.
type StubClock ¶
StubClock provides a canned time to Simulator for testing. For a discussion of various types of 'test double' see: https://www.martinfowler.com/articles/mocksArentStubs.html