58 lines
2 KiB
Go
58 lines
2 KiB
Go
package backtest
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"git.toki-labs.com/toki/alt/packages/domain/backtest"
|
|
"git.toki-labs.com/toki/alt/packages/domain/market"
|
|
)
|
|
|
|
// BuiltInStrategyID is the identifier of the worker's default bundled strategy.
|
|
// Command workflows and YAML scenarios reference it so a backtest can run
|
|
// end-to-end on imported bars without an operator authoring a custom strategy.
|
|
const BuiltInStrategyID backtest.StrategyID = "strategy-v1"
|
|
|
|
// BuiltInStrategyPort resolves the worker's bundled strategies. It is the
|
|
// production StrategyPort wired in cmd/alt-worker. Unknown ids return an error
|
|
// so a misconfigured run fails fast instead of silently producing an empty
|
|
// result.
|
|
type BuiltInStrategyPort struct{}
|
|
|
|
func NewBuiltInStrategyPort() *BuiltInStrategyPort {
|
|
return &BuiltInStrategyPort{}
|
|
}
|
|
|
|
func (p *BuiltInStrategyPort) GetStrategy(ctx context.Context, id backtest.StrategyID) (backtest.Strategy, error) {
|
|
switch id {
|
|
case BuiltInStrategyID:
|
|
return buyAndHoldStrategy{id: id}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unknown strategy %q", id)
|
|
}
|
|
}
|
|
|
|
// buyAndHoldStrategy buys a single unit of each instrument the first time it is
|
|
// seen and then holds. It is intentionally minimal and deterministic so command
|
|
// validation can assert a stable terminal result from imported daily bars.
|
|
type buyAndHoldStrategy struct {
|
|
id backtest.StrategyID
|
|
}
|
|
|
|
func (s buyAndHoldStrategy) ID() backtest.StrategyID { return s.id }
|
|
|
|
func (s buyAndHoldStrategy) Decide(input backtest.StrategyInput) ([]backtest.OrderIntent, error) {
|
|
// Hold once a position exists. The engine passes the pre-trade portfolio for
|
|
// the current bar, so an existing position means this instrument was already
|
|
// bought on an earlier bar.
|
|
if _, held := input.Portfolio.Position(input.Bar.InstrumentID); held {
|
|
return nil, nil
|
|
}
|
|
return []backtest.OrderIntent{
|
|
{
|
|
InstrumentID: input.Bar.InstrumentID,
|
|
Side: backtest.OrderSideBuy,
|
|
Quantity: market.Quantity{Amount: market.Decimal{Value: "1"}},
|
|
},
|
|
}, nil
|
|
}
|