A fast and simple backtest implementation for algorithmic trading focused on cryptocurrencies written in GoLang.
The price data used to run the backtests can be from any time interval, but it must contain a OHLCV structure (Open High Low Close Volume). It is possible to load data from csv files with the following format:
open | high | low | close | volume |
---|---|---|---|---|
7922.0700 | 7924.9900 | 7920.1600 | 7924.7500 | 9.90606700 |
7923.4300 | 7929.1400 | 7920.8000 | 7922.9000 | 15.83760800 |
7923.1300 | 7934.0900 | 7922.9000 | 7932.2600 | 9.98577900 |
To start using kate backtester you will need to implement the Strategy interface and provide a csv a dataset for execution. The Strategy interface contains 4 functions that describe how/when to trade: PreProcessIndicators, OpenNewPosition, SetStoploss and SetTakeProfit.
Allows the strategy to pre calculate the indicators and values for reuse in the other functions, making the execution faster and less redundant. This pre processing step is the first function called with every new price data avaliable.
This function is responsible for opening new trade positions when there are none open already, the function is called with every new price data to check, a nil return denotes that no positions should be open yet. When opening a position a OpenPositionEvt is returned containing the Direction for the trade (LONG/SHORT) and the desired leverage, a possible return would be return &kate.OpenPositionEvt{Direction: kate.LONG, Leverage: 30}
As the name already implies this function is responsible for setting the stoploss price for the already open position, the function is called when new price data is avaliable and a position is open. This function makes possible changing the stoploss dynamically as the position evolves, the updated PNL is avaliable for checking. A nil return denotes that no changes should be made, a example return would be return &kate.StoplossEvt{Price: openPosition.EntryPrice * 0.995}
This function has the same behavior as SetStoploss but instead it manipulates the take profit price. A example return would be return &kate.TakeProfitEvt{Price: openPosition.EntryPrice * 1.005}
A basic implementation where a strategy opens a long position every time the latest close price is higher than the last close is:
package main
import (
"fmt"
"github.com/victorl2/kate-backtester/kate"
)
type SimpleStrategy struct{
lastPrice *kate.DataPoint
currentPrice *kate.DataPoint
}
func main() {
data, err := kate.PricesFromCSV("../../testdata/ETHUSD5.csv")
if err != nil {
panic("could`t load data." + err.Error())
}
kate.NewBacktester(&SimpleStrategy{}, data)
backtester := kate.NewBacktester(&SimpleStrategy{}, data)
fmt.Println(backtester.Run())
}
//PreProcessIndicators allows the pre processing of indicators
func (stg *SimpleStrategy) PreProcessIndicators(latestPrice kate.DataPoint) {
stg.lastPrice = strategy.currentPrice
stg.currentPrice = &latestPrice
}
//OpenNewPosition process the next data point and checks if a position should be opened
func (stg *SimpleStrategy) OpenNewPosition(latestPrice kate.DataPoint) *kate.OpenPositionEvt {
if stg.lastPrice != nil && stg.currentPrice.Close() > stg.lastPrice.Close() {
return &kate.OpenPositionEvt{Direction: kate.LONG, Leverage: 30}
}
return nil
}
//SetStoploss defines a stoploss for the current open position
func (stg *SimpleStrategy) SetStoploss(openPosition kate.Position) *kate.StoplossEvt {
if openPosition.Direction == kate.LONG && openPosition.Stoploss <= 0 {
return &kate.StoplossEvt{Price: openPosition.EntryPrice * 0.995}
}
return nil
}
//SetTakeProfit defines a takeprofit for the current open position
func (stg *SimpleStrategy) SetTakeProfit(openPosition kate.Position) *kate.TakeProfitEvt {
if openPosition.Direction == kate.LONG && openPosition.TakeProfit <= 0 {
return &kate.TakeProfitEvt{Price: openPosition.EntryPrice * 1.005}
}
return nil
}