Anatomy of a Strategy
An Algolang strategy is a Go package that implements a specific interface. This chapter describes each component.
Mandatory components
Every strategy must contain:
- A package declaration
- An import declaration (at minimum, the engine package)
- A Strategy struct declaration
- A Strategy function
Optional components
Strategies may also include:
- A Begin function (called before the first bar)
- An End function (called after the last bar)
- A Name function (returns the strategy’s display name)
- A Description function (returns a description)
The package declaration
The package declaration is the first line of the file:
package mystrategyThe package name serves as the default strategy name if no Name() function is provided.
The import declaration
At minimum, the engine package must be imported:
import (
"github.com/kmglennan/algolang/pkg/engine"
)Additional packages from the Go standard library or any external source can be imported as needed.
The Strategy struct
The Strategy struct defines the strategy’s inputs and any state that must persist between bars:
type Strategy struct {
LenFast int `algolang:"default=10,values=[5,10,15,20,25,30,40,50]"`
LenSlow int `algolang:"default=30,values=[20,25,30,40,50]"`
}The algolang: struct tag
Struct fields with the algolang: tag are treated as strategy inputs. The tag supports:
| Tag | Description | Example |
|---|---|---|
default=N | Default value | default=10 |
values=[...] | Discrete values for optimisation | values=[5,10,15,20] |
min=N | Minimum value for optimisation | min=5 |
max=N | Maximum value for optimisation | max=100 |
by=N | Step size for optimisation | by=5 |
Examples:
// Discrete values
Length int `algolang:"default=20,values=[10,20,50,100,200]"`
// Range with step
Mult float64 `algolang:"default=1.5,min=0.5,max=3.0,by=0.5"`
// Boolean input
IsDebug bool `algolang:"default=false"`
// String input
SwingMode string `algolang:"default=SSC,values=[SSC,NSC]"`Fields without the algolang: tag are private to the strategy and can be used to store any state between bars.
The Strategy function
The Strategy function is called once for every bar in the primary data series. It receives a *engine.RunState parameter that provides access to all price data, position state, and order placement:
func (s *Strategy) Strategy(rs *engine.RunState) error {
// Trading logic here
return nil
}Returning a non-nil error halts the strategy immediately.
The Begin function
Called once before the first bar is processed. Use it for initialisation, such as loading additional data series:
func (s *Strategy) Begin(rs *engine.RunState) error {
// Load weekly data for the primary symbol
_, err := rs.LoadSeries(engine.SeriesOptions{
BarInterval: "weekly",
})
return err
}The End function
Called once after the last bar is processed. Use it for any cleanup or final calculations:
func (s *Strategy) End(rs *engine.RunState) error {
return nil
}The Name and Description functions
These return the display name and description shown in the algo strategies -l listing:
func (s *Strategy) Name() string {
return "Moving Average Crossover"
}
func (s *Strategy) Description() string {
return "Simple moving-average crossover strategy."
}If not provided, the package name is used for both.
Complete example
Here is a complete moving average crossover strategy:
package macrossover
import (
"github.com/kmglennan/algolang/pkg/engine"
)
type Strategy struct {
LenFast int `algolang:"default=10,values=[5,10,15,20]"`
LenSlow int `algolang:"default=30,values=[20,30,40,50,75,100]"`
}
func (s *Strategy) Strategy(rs *engine.RunState) error {
if rs.MarketPosition() <= 0 {
if rs.Close(0).Average(s.LenFast).CrossesAbove(rs.Close(0).Average(s.LenSlow)) {
rs.Buy()
}
}
if rs.MarketPosition() >= 0 {
if rs.Close(0).Average(s.LenFast).CrossesBelow(rs.Close(0).Average(s.LenSlow)) {
rs.SellShort()
}
}
return nil
}
func (s *Strategy) Name() string {
return "Moving Average Crossover"
}
func (s *Strategy) Description() string {
return "Simple moving-average crossover strategy."
}Registering a strategy
To make a strategy available as a built-in:
- Create a directory under
strategies/(e.g.,strategies/mystrategy/) - Place the strategy
.gofile inside it - Run
make generate && make algoto rebuild
The code generator scans strategies/ and registers all packages that contain a Strategy struct.