backtest

package
v0.0.17 Latest Latest
Warning

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

Go to latest
Published: May 29, 2022 License: MIT Imports: 11 Imported by: 0

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

Examples

Constants

This section is empty.

Variables

View Source
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.

View Source
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

func (c *Clock) Advance(epoch time.Time)

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

func (c *Clock) Elapsed() time.Duration

Elapsed returns the total elapsed duration since the start. Elapsed time is calculated on each call to Advance. Primarily used for calculating funding charges.

func (*Clock) Now

func (c *Clock) Now() time.Time

Now returns the time incremented by a tock, which by default is 1 * time.millisecond later than the last call.

func (*Clock) Peek

func (c *Clock) Peek() time.Time

Peek returns the time without mutation.

func (*Clock) Start

func (c *Clock) Start(start time.Time, tock time.Duration)

Start initializes the clock and resets all state.

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

func NewDealerWithCost(cost Coster) *Dealer

NewDealerWithCost creates a new Dealer with the given cost model.

func (*Dealer) CancelOrders

func (d *Dealer) CancelOrders(ctx context.Context) (*web.Response, error)

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

func (d *Dealer) GetBalance(ctx context.Context) (*broker.AccountBalance, *web.Response, error)

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

func (d *Dealer) ReceivePrice(ctx context.Context, price market.Kline) error

ReceivePrice initiates processing by supplying the next market price to the simulator.

func (*Dealer) SetInitialCapital

func (d *Dealer) SetInitialCapital(amount decimal.Decimal)

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 NewPerpCoster

func NewPerpCoster() *PerpCoster

NewPerpCoster creates a new PerpCoster.

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

func NewSimulatorWithCost(cost Coster) *Simulator

NewSimulatorWithCost creates a new backtest simulator with the given cost model.

func (*Simulator) AddOrder

func (s *Simulator) AddOrder(order broker.Order) (broker.Order, error)

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

func (s *Simulator) CancelOrders() []broker.Order

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) Next

func (s *Simulator) Next(price market.Kline) error

Next advances the simulation by one kline.

func (*Simulator) Orders

func (s *Simulator) Orders() []broker.Order

Orders returns a copy of all historical and open orders.

func (*Simulator) Positions

func (s *Simulator) Positions() []broker.Position

Positions returns a copy of all historical and open positions.

func (*Simulator) SetInitialCapital

func (s *Simulator) SetInitialCapital(amount decimal.Decimal)

SetInitialCapital sets the initial trade balance.

func (*Simulator) Trades

func (s *Simulator) Trades() []broker.Trade

Trades returns a copy of all historical trades.

type StubClock

type StubClock struct {

	// Fixed is the time to return
	Fixed time.Time
}

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

func (*StubClock) Advance

func (c *StubClock) Advance(epoch time.Time)

Advance not implemented.

func (*StubClock) Elapsed

func (c *StubClock) Elapsed() time.Duration

Elapsed returns 0.

func (*StubClock) Now

func (c *StubClock) Now() time.Time

Now returns Fixed.

func (*StubClock) Peek

func (c *StubClock) Peek() time.Time

Peek returns Fixed.

func (*StubClock) Start

func (c *StubClock) Start(start time.Time, tock time.Duration)

Start not implemented.

Jump to

Keyboard shortcuts

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