Skip to content

A fast and simple backtest implementation for algorithmic trading in golang

License

Notifications You must be signed in to change notification settings

grinply/kate-backtester

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go Report Card Maintainability Rating Software License Go Reference

Kate Backtester

A fast and simple backtest implementation for algorithmic trading focused on cryptocurrencies written in GoLang.

Data

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

Usage

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.

PreProcessIndicators

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.

OpenNewPosition

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}

SetStoploss

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}

SetTakeProfit

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
}