Skip to content

Latest commit

 

History

History
851 lines (597 loc) · 37.3 KB

File metadata and controls

851 lines (597 loc) · 37.3 KB

strategy

import "github.com/cinar/indicator/v2/strategy"

Package strategy contains the strategy functions.

This package belongs to the Indicator project. Indicator is a Golang module that supplies a variety of technical indicators, strategies, and a backtesting framework for analysis.

License

Copyright (c) 2021-2026 The Indicator Authors.
The source code is provided under GNU AGPLv3 License.
https://github.com/cinar/indicator

Disclaimer

The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security.

Index

Constants

const (
    // DefaultSharpeRatioPeriodsPerYear is the default number of return periods in a year, matching
    // the approximate number of trading days used to annualize a Sharpe Ratio computed from daily
    // outcomes.
    DefaultSharpeRatioPeriodsPerYear = 252
)

const (
    // DefaultSortinoRatioPeriodsPerYear is the default number of return periods in a year, matching
    // the approximate number of trading days used to annualize a Sortino Ratio computed from daily
    // outcomes.
    DefaultSortinoRatioPeriodsPerYear = 252
)

func ActionSources(strategies []Strategy, snapshots <-chan *asset.Snapshot) []<-chan Action

ActionSources creates a slice of action channels, one for each strategy, where each channel emits actions computed by its corresponding strategy based on snapshots from the provided snapshot channel.

Deprecated: Use ActionSourcesWithContext instead.

func ActionSourcesWithContext(ctx context.Context, strategies []Strategy, snapshots <-chan *asset.Snapshot) []<-chan Action

ActionSourcesWithContext creates a slice of action channels, one for each strategy, where each channel emits actions computed by its corresponding strategy based on snapshots from the provided snapshot channel, supporting context cancellation.

func ActionsToAnnotations(ac <-chan Action) <-chan string

ActionsToAnnotations takes a channel of action recommendations and returns a new channel containing corresponding annotations for those actions.

Deprecated: Use ActionsToAnnotationsWithContext instead.

func ActionsToAnnotationsWithContext(ctx context.Context, ac <-chan Action) <-chan string

ActionsToAnnotationsWithContext takes a channel of action recommendations and returns a new channel containing corresponding annotations for those actions, supporting context cancellation.

func ComputeWithContext(ctx context.Context, s Strategy, c <-chan *asset.Snapshot) <-chan Action

ComputeWithContext processes snapshots with a strategy using context.

func ComputeWithOutcome(s Strategy, c <-chan *asset.Snapshot) (<-chan Action, <-chan float64)

ComputeWithOutcome uses the given strategy to processes the provided asset snapshots and generates a stream of actionable recommendations and outcomes.

Deprecated: Use ComputeWithOutcomeWithContext instead.

func ComputeWithOutcomeAndTiming(s Strategy, c <-chan *asset.Snapshot, timing ExecutionTiming) (<-chan Action, <-chan float64)

ComputeWithOutcomeAndTiming uses the given strategy to process the provided asset snapshots and generates a stream of actionable recommendations and outcomes, using the given ExecutionTiming to decide which price a simulated trade executes at.

See ComputeWithOutcomeAndTimingWithContext for details, including the note on the outcomes channel being shorter than the actions channel when timing is NextOpen or NextClose.

func ComputeWithOutcomeAndTimingWithContext(ctx context.Context, s Strategy, c <-chan *asset.Snapshot, timing ExecutionTiming) (<-chan Action, <-chan float64)

ComputeWithOutcomeAndTimingWithContext uses the given strategy to process the provided asset snapshots and generates a stream of actionable recommendations and outcomes, using the given ExecutionTiming to decide which price a simulated trade executes at, supporting context cancellation.

With AtClose, this behaves identically to ComputeWithOutcomeWithContext: each action is paired with the closing price of the same bar it was computed from.

With NextOpen or NextClose, each action is instead paired with the opening or closing price of the following bar. As a result, the returned outcomes channel yields one fewer value than the returned actions channel, since there is no following bar for the last action. Callers that need the actions and outcomes channels aligned position-for-position (for example, to build a report column) must account for this offset themselves, the same way many strategies already skip-align channels of differing lengths. Callers only interested in the final/aggregate outcome (for example, via helper.Last(outcomes, 1)) are unaffected either way.

func ComputeWithOutcomeWithContext(ctx context.Context, s Strategy, c <-chan *asset.Snapshot) (<-chan Action, <-chan float64)

ComputeWithOutcomeWithContext uses the given strategy to processes the provided asset snapshots and generates a stream of actionable recommendations and outcomes, supporting context cancellation.

func CountActions(acs []<-chan Action) (int, int, int, bool)

CountActions taken a slice of Action channels, and counts them by their type.

func CountTransactions(ac <-chan Action) <-chan int

CountTransactions counts the number of recommended Buy and Sell actions.

func DenormalizeActions(ac <-chan Action) <-chan Action

DenormalizeActions simplifies the representation of the action sequence.

Deprecated: Use DenormalizeActionsWithContext instead.

func DenormalizeActionsWithContext(ctx context.Context, ac <-chan Action) <-chan Action

DenormalizeActionsWithContext simplifies the representation of the action sequence and facilitates subsequent processing by transforming the given channel of actions, supporting context cancellation.

func NormalizeActions(ac <-chan Action) <-chan Action

NormalizeActions transforms the given channel of actions to ensure a consistent and predictable sequence.

Deprecated: Use NormalizeActionsWithContext instead.

func NormalizeActionsWithContext(ctx context.Context, ac <-chan Action) <-chan Action

NormalizeActionsWithContext transforms the given channel of actions to ensure a consistent and predictable sequence, supporting context cancellation.

func Outcome

func Outcome[T helper.Number](values <-chan T, actions <-chan Action) <-chan float64

Outcome simulates the potential result of executing the given actions based on the provided values.

See OutcomeWithContext for details on the same-bar/"at close" execution assumption.

Deprecated: Use OutcomeWithContext instead.

func OutcomeWithContext[T helper.Number](ctx context.Context, values <-chan T, actions <-chan Action) <-chan float64

OutcomeWithContext simulates the potential result of executing the given actions based on the provided values, supporting context cancellation.

The values and actions channels are paired positionally: the value at position i is assumed to be the execution price for the action at position i. Callers that pass same-bar closing prices are therefore simulating same-bar/"at close" execution, i.e. the trade is assumed to fill at the very same closing price that produced the signal. This is unrealistic (a signal cannot be acted upon before the bar that generated it has been observed) and tends to overstate backtest performance. Callers who want the more realistic assumption of executing on the next bar's open or close should use ComputeWithOutcomeAndTimingWithContext (or ComputeWithOutcomeAndTiming) with ExecutionTiming NextOpen or NextClose instead of constructing the values channel directly.

func SharpeRatio(outcomes <-chan float64, periodsPerYear int) float64

SharpeRatio wraps SharpeRatioWithContext for backwards compatibility.

Deprecated: Use SharpeRatioWithContext instead.

func SharpeRatioWithContext(ctx context.Context, outcomes <-chan float64, periodsPerYear int) float64

SharpeRatioWithContext computes the annualized Sharpe Ratio for the given stream of cumulative outcome values, as produced by OutcomeWithContext, supporting context cancellation.

Sharpe = Mean(periodReturns) / StdDev(periodReturns) * Sqrt(periodsPerYear)

The risk-free rate is assumed to be zero. The outcomes channel is assumed to hold one cumulative return value per trading period (for example, one per daily snapshot), which is exactly what OutcomeWithContext produces. Per-period returns are derived from the change in the underlying equity curve (1 + outcome) between consecutive outcomes.

Fewer than two outcome values, or a return series with zero (or floating-point-noise-level) variance, such as a strategy that never trades, yields a Sharpe Ratio of zero rather than dividing by a near-zero standard deviation.

func SortinoRatio(outcomes <-chan float64, periodsPerYear int) float64

SortinoRatio wraps SortinoRatioWithContext for backwards compatibility.

Deprecated: Use SortinoRatioWithContext instead.

func SortinoRatioWithContext(ctx context.Context, outcomes <-chan float64, periodsPerYear int) float64

SortinoRatioWithContext computes the annualized Sortino Ratio for the given stream of cumulative outcome values, as produced by OutcomeWithContext, supporting context cancellation.

Sortino = Mean(periodReturns) / DownsideDeviation(periodReturns) * Sqrt(periodsPerYear)

Unlike the Sharpe Ratio, which divides by the standard deviation of all returns, the Sortino Ratio divides by the downside deviation: the root-mean-square of only the shortfall below a minimum acceptable return (assumed to be zero here), with periods at or above that return contributing zero. Two return series with identical upside volatility but different downside volatility therefore yield different Sortino Ratios, even when their Sharpe Ratios are equal.

The outcomes channel is assumed to hold one cumulative return value per trading period (for example, one per daily snapshot), which is exactly what OutcomeWithContext produces. Per-period returns are derived from the change in the underlying equity curve (1 + outcome) between consecutive outcomes.

Fewer than two outcome values, or a return series with zero (or floating-point-noise-level) downside deviation, such as a strategy whose returns never fall below the minimum acceptable return, yields a Sortino Ratio of zero rather than dividing by a near-zero downside deviation.

type Action

Action represents the different action categories that a strategy can recommend.

type Action int

const (
    // Hold suggests maintaining the current position and not
    // taking any actions on the asset.
    Hold Action = 0

    // Sell suggests disposing of the asset and exiting the current position.
    // This recommendation typically indicates that the strategy believes the
    // asset's price has reached its peak or is likely to decline.
    Sell Action = -1

    // Buy suggests acquiring the asset and entering a new position. This
    // recommendation usually implies that the strategy believes the
    // asset's price is undervalued.
    Buy Action = 1
)

func (Action) Annotation

func (a Action) Annotation() string

Annotation returns a single character string representing the recommended action. It returns "S" for Sell, "B" for Buy, and an empty string for Hold.

AndStrategy combines multiple strategies and emits actionable recommendations when **all** strategies in the group **reach the same actionable conclusion**. This can be a conservative approach, potentially delaying recommendations until full consensus is reached.

type AndStrategy struct {
    // Strategies are the group of strategies that will be consulted to make an actionable recommendation.
    Strategies []Strategy
    // contains filtered or unexported fields
}

func NewAndStrategy(name string, strategies ...Strategy) *AndStrategy

NewAndStrategy function initializes an empty and strategies group with the given name.

func (*AndStrategy) Compute

func (a *AndStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action

Compute wraps ComputeWithContext for backwards compatibility.

Deprecated: Use ComputeWithContext instead.

func (*AndStrategy) ComputeWithContext

func (a *AndStrategy) ComputeWithContext(ctx context.Context, snapshots <-chan *asset.Snapshot) <-chan Action

ComputeWithContext processes the provided asset snapshots and generates an illustrative stream of actions.

func (*AndStrategy) Name

func (a *AndStrategy) Name() string

Name returns the name of the example strategy.

func (*AndStrategy) Report

func (a *AndStrategy) Report(c <-chan *asset.Snapshot) *helper.Report

Report processes the provided asset snapshots and generates an illustrative report annotated with example actions.

func (*AndStrategy) String

func (a *AndStrategy) String() string

String is the string representation of the AndStrategy.

BuyAndHoldStrategy demonstrates a baseline buy-and-hold strategy for illustrative and benchmarking purposes.

type BuyAndHoldStrategy struct {
}

func NewBuyAndHoldStrategy() *BuyAndHoldStrategy

NewBuyAndHoldStrategy initializes an example BuyAndHoldStrategy instance with default parameters.

func (*BuyAndHoldStrategy) Compute

func (b *BuyAndHoldStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action

Compute wraps ComputeWithContext for backwards compatibility.

Deprecated: Use ComputeWithContext instead.

func (*BuyAndHoldStrategy) ComputeWithContext

func (b *BuyAndHoldStrategy) ComputeWithContext(ctx context.Context, snapshots <-chan *asset.Snapshot) <-chan Action

ComputeWithContext processes the provided asset snapshots and generates an illustrative stream of actions.

func (*BuyAndHoldStrategy) Name

func (*BuyAndHoldStrategy) Name() string

Name returns the name of the example strategy.

func (*BuyAndHoldStrategy) Report

func (b *BuyAndHoldStrategy) Report(c <-chan *asset.Snapshot) *helper.Report

Report processes the provided asset snapshots and generates an illustrative report annotated with example actions.

func (*BuyAndHoldStrategy) String

func (b *BuyAndHoldStrategy) String() string

String is the string representation of the BuyAndHoldStrategy.

ExecutionTiming represents when a simulated trade executes relative to the bar its action was computed from.

type ExecutionTiming int

const (
    // AtClose executes at the same bar's closing price. This is the existing default behavior used by
    // OutcomeWithContext and ComputeWithOutcomeWithContext, and assumes the trade fills at the very
    // close that produced the signal.
    AtClose ExecutionTiming = iota

    // NextOpen executes at the opening price of the bar immediately following the signal. This is a
    // more realistic assumption for strategies that can only act after a bar has fully closed.
    NextOpen

    // NextClose executes at the closing price of the bar immediately following the signal.
    NextClose
)

func (ExecutionTiming) String

func (e ExecutionTiming) String() string

String returns the string representation of the ExecutionTiming.

MajorityStrategy emits actionable recommendations aligned with what the strategies in the group recommends.

type MajorityStrategy struct {
    // Strategies are the group of strategies that will be consulted to make an actionable recommendation.
    Strategies []Strategy
    // contains filtered or unexported fields
}

func NewMajorityStrategy(name string) *MajorityStrategy

NewMajorityStrategy function initializes an empty majority strategies group with the given name.

func NewMajorityStrategyWith(name string, strategies []Strategy) *MajorityStrategy

NewMajorityStrategyWith function initializes a majority strategies group with the given name and strategies.

func (*MajorityStrategy) Compute

func (a *MajorityStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action

Compute wraps ComputeWithContext for backwards compatibility.

Deprecated: Use ComputeWithContext instead.

func (*MajorityStrategy) ComputeWithContext

func (a *MajorityStrategy) ComputeWithContext(ctx context.Context, snapshots <-chan *asset.Snapshot) <-chan Action

ComputeWithContext processes the provided asset snapshots and generates an illustrative stream of actions.

func (*MajorityStrategy) Name

func (a *MajorityStrategy) Name() string

Name returns the name of the example strategy.

func (*MajorityStrategy) Report

func (a *MajorityStrategy) Report(c <-chan *asset.Snapshot) *helper.Report

Report processes the provided asset snapshots and generates an illustrative report annotated with example actions.

func (*MajorityStrategy) String

func (a *MajorityStrategy) String() string

String is the string representation of the MajorityStrategy.

OrStrategy emits actionable recommendations when **at least one** strategy in the group recommends an action **without any conflicting recommendations** from other strategies.

type OrStrategy struct {
    // Strategies are the group of strategies that will be consulted to make an actionable recommendation.
    Strategies []Strategy
    // contains filtered or unexported fields
}

func NewOrStrategy(name string, strategies ...Strategy) *OrStrategy

NewOrStrategy function initializes an empty or strategies group with the given name.

func (*OrStrategy) Compute

func (a *OrStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action

Compute wraps ComputeWithContext for backwards compatibility.

Deprecated: Use ComputeWithContext instead.

func (*OrStrategy) ComputeWithContext

func (a *OrStrategy) ComputeWithContext(ctx context.Context, snapshots <-chan *asset.Snapshot) <-chan Action

ComputeWithContext processes the provided asset snapshots and generates an illustrative stream of actions.

func (*OrStrategy) Name

func (a *OrStrategy) Name() string

Name returns the name of the example strategy.

func (*OrStrategy) Report

func (a *OrStrategy) Report(c <-chan *asset.Snapshot) *helper.Report

Report processes the provided asset snapshots and generates an illustrative report annotated with example actions.

func (*OrStrategy) String

func (a *OrStrategy) String() string

String is the string representation of the OrStrategy.

type Result

Result is only used inside the test cases to facilitate the comparison between the actual and expected strategy results.

type Result struct {
    Action Action
}

SplitStrategy leverages two separate strategies. It utilizes the first strategy to identify potential Buy opportunities, and the second strategy to identify potential Sell opportunities. When there is a conflicting recommendation, returns Hold.

type SplitStrategy struct {
    // BuyStrategy is used to identify potential Buy opportunities.
    BuyStrategy Strategy

    // SellStrategy is used to identify potential Sell opportunities.
    SellStrategy Strategy
}

func NewSplitStrategy(buyStrategy, sellStrategy Strategy) *SplitStrategy

NewSplitStrategy initializes an example SplitStrategy instance with default parameters.

func (*SplitStrategy) Compute

func (s *SplitStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action

Compute wraps ComputeWithContext for backwards compatibility.

Deprecated: Use ComputeWithContext instead.

func (*SplitStrategy) ComputeWithContext

func (s *SplitStrategy) ComputeWithContext(ctx context.Context, snapshots <-chan *asset.Snapshot) <-chan Action

ComputeWithContext processes the provided asset snapshots and generates an illustrative stream of actions.

func (*SplitStrategy) Name

func (s *SplitStrategy) Name() string

Name returns the name of the example strategy.

func (*SplitStrategy) Report

func (s *SplitStrategy) Report(c <-chan *asset.Snapshot) *helper.Report

Report processes the provided asset snapshots and generates an illustrative report annotated with example actions.

func (*SplitStrategy) String

func (s *SplitStrategy) String() string

String is the string representation of the SplitStrategy.

Strategy defines a shared interface for trading strategies.

type Strategy interface {
    // Name returns the name of the example strategy.
    Name() string

    // Compute processes the provided asset snapshots and generates a
    // stream of actionable recommendations.
    Compute(snapshots <-chan *asset.Snapshot) <-chan Action

    // Report processes the provided asset snapshots and generates a
    // report annotated with the recommended actions.
    Report(snapshots <-chan *asset.Snapshot) *helper.Report
}

func AllAndStrategies(strategies []Strategy) []Strategy

AllAndStrategies performs a cartesian product operation on the given strategies, resulting in a collection containing all and strategies formed by combining two strategies together.

func AllSplitStrategies(strategies []Strategy) []Strategy

AllSplitStrategies performs a cartesian product operation on the given strategies, resulting in a collection containing all split strategies formed by combining individual buy and sell strategies.

func AllStrategies() []Strategy

AllStrategies returns a slice containing references to all available base strategies.

WithContext defines a shared interface for trading strategies supporting context-aware computations.

type WithContext interface {
    Strategy
    ComputeWithContext(ctx context.Context, snapshots <-chan *asset.Snapshot) <-chan Action
}

Generated by gomarkdoc