techan

package module
v0.14.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 9 Imported by: 38

README

Techan

Tests

codecov

TechAn is a technical analysis library for Go! It provides a suite of tools and frameworks to analyze financial data and make trading decisions.

Features

  • Basic and advanced technical analysis indicators
  • Profit and trade analysis
  • Strategy building

Requires Go 1.21 or newer.

Installation

$ go get github.com/sdcoffey/techan

Quickstart

series := techan.NewTimeSeries()

// fetch this from your preferred exchange
dataset := [][]string{
	// Timestamp, Open, Close, High, Low, volume
	{"1609459200", "99", "100", "101", "98", "12"},
	{"1609545600", "100", "102", "103", "99", "15"},
	{"1609632000", "102", "101", "104", "100", "11"},
	{"1609718400", "101", "104", "105", "100", "18"},
}

for _, datum := range dataset {
	start, err := strconv.ParseInt(datum[0], 10, 64)
	if err != nil {
		panic(err)
	}
	period := techan.NewTimePeriod(time.Unix(start, 0), time.Hour*24)

	candle := techan.NewCandle(period)
	candle.OpenPrice = big.NewFromString(datum[1])
	candle.ClosePrice = big.NewFromString(datum[2])
	candle.MaxPrice = big.NewFromString(datum[3])
	candle.MinPrice = big.NewFromString(datum[4])
	candle.Volume = big.NewFromString(datum[5])

	if !series.AddCandle(candle) {
		panic("candles must be in chronological order")
	}
}

closePrices := techan.NewClosePriceIndicator(series)
movingAverage := techan.NewEMAIndicator(closePrices, 3) // Three observations per window

if series.LastIndex() >= techan.FirstValidIndex(movingAverage) {
	fmt.Println(movingAverage.Calculate(series.LastIndex()).FormattedString(2))
}

Windowed indicators such as SMA and EMA return 0 until enough data exists to fill their first window. For example, a 10-period EMA starts producing calculated values at index 9.

Use techan.FirstValidIndex(indicator) when composing indicators. For example, MACD(12,26) first becomes usable at index 25, and its 9-period signal at index 33. Custom indicators can implement WarmupIndicator to declare their first usable index; otherwise they are assumed ready at zero. RSI uses Wilder's seed of N actual price changes, so RSI(14) starts at index 14; a flat window returns zero. Bollinger bands and windowed standard deviation return zero until a complete window is available.

EMA and MMA fill caches iteratively and round stored values to 256 binary bits (about 77 significant decimal digits). This keeps memory proportional to history length without narrowing prices to float64. Caches still retain historical results. After editing candles, call techan.ResetCacheFrom(rootIndicator, index) on each indicator graph you use; it reaches built-in nested caches. For a reindexed or truncated series, reset from zero or rebuild the indicators. A full reset recomputes from the retained history; it does not preserve EMA history that has been removed. Mutation, calculation, and resetting must be serialized by the caller when indicators are shared between goroutines.

Creating trading strategies

indicator := techan.NewClosePriceIndicator(series)

// record trades on this object
record := techan.NewTradingRecord()

entryConstant := techan.NewConstantIndicator(30)
exitConstant := techan.NewConstantIndicator(10)

// Is satisfied when the close price moves above 30 and the current position is new
entryRule := techan.And(
	techan.NewCrossUpIndicatorRule(entryConstant, indicator),
	techan.PositionNewRule{})
	
// Is satisfied when the close price moves below 10 and the current position is open
exitRule := techan.And(
	techan.NewCrossDownIndicatorRule(indicator, exitConstant),
	techan.PositionOpenRule{})

strategy := techan.RuleStrategy{
	UnstablePeriod: 10, // Period before which ShouldEnter and ShouldExit will always return false
	EntryRule:      entryRule,
	ExitRule:       exitRule,
}

strategy.ShouldEnter(0, record) // returns false

Crossover rules fire only on a crossing bar. Equality alone does not trigger a crossing; an equality plateau is resolved using the preceding unequal values. UnstablePeriod is the first index where the strategy may enter or exit.

Analysis conventions

Profit analyses account for long and short order direction. PercentGainAnalysis returns total realized profit divided by the first closed position's entry cost, which is treated as initial capital. It does not model deposits, withdrawals, or fees. PeriodProfitAnalysis includes fractional periods. The buy-and-hold comparison uses only completed candles through the record's last closed trade. Maximum drawdown measures declines after positive peaks within the requested window; a window without a positive peak returns zero.

Contributing

  • make test runs all tests without rewriting source files.
  • make format applies goimports; make lint runs vet and Staticcheck.
  • make bench reports timing and allocations, including EMA/MMA history scaling.
  • go test -run '^$' -fuzz FuzzParseTimePeriod -fuzztime 10s exercises parsing.

Formatting and lint tools are version-pinned in the Makefile and run separately from the library module. They may automatically select a newer Go toolchain; the library's minimum remains Go 1.21. See the executable examples in the test files for indicator composition, cache resets, and a complete trading record.

Enjoying this project?

Are you using techan in production? You can sponsor its development by buying me a coffee! ☕

ETH: 0x2D9d3A1c16F118A3a59d0e446d574e1F01F62949

Credits

Techan is heavily influenced by the great ta4j. Many of the ideas and frameworks in this library owe their genesis to the great work done over there.

License

Techan is released under the MIT license. See LICENSE for details.

Documentation

Index

Examples

Constants

View Source
const (
	SimpleDateTimeFormat = "01/02/2006T15:04:05"
	SimpleDateFormat     = "01/02/2006"

	SimpleTimeFormat   = "15:04:05"
	SimpleDateFormatV2 = "2006-01-02"
)

Constants representing basic, human-readable and writable date formats

Variables

View Source
var (
	SimpleTimeFomatRegex    = regexp.MustCompile(`T\d{2}:\d{2}:\d{2}`)
	SimpleDateFormatV2Regex = regexp.MustCompile(`\d{4}-\d{2}-\d{2}`)
)

Constants representing regexes for parsing datetimes

Functions

func Abs

func Abs(b int) int

Abs returns the absolute value of the passed-in integer

func FirstValidIndex added in v0.14.0

func FirstValidIndex(indicator Indicator) int

FirstValidIndex returns an indicator's first usable index. Custom indicators without warm-up metadata are assumed to be usable from index zero.

func Max

func Max(i, j int) int

Max returns the larger of the two integers passed in

func Min

func Min(i, j int) int

Min returns the smaller integer of the two integers passed in

func Pow

func Pow(i, j int) int

Pow returns the first integer to the power of the second integer

func ResetCacheFrom added in v0.13.0

func ResetCacheFrom(indicator Indicator, index int) bool

ResetCacheFrom invalidates cached values from index onward when the indicator supports cache resets or uses the built-in result cache, including inputs of built-in composites. It returns true if any cache was reset. Reset each root used by the caller; parents outside that root's input graph are not visited.

Example
package main

import (
	"fmt"
	"time"

	"github.com/sdcoffey/big"
	"github.com/sdcoffey/techan"
)

func main() {
	series := techan.NewTimeSeries()
	for i := 0; i < 4; i++ {
		candle := techan.NewCandle(techan.NewTimePeriod(time.Unix(int64(i), 0), time.Second))
		candle.ClosePrice = big.NewFromInt(10)
		series.AddCandle(candle)
	}
	macd := techan.NewMACDIndicator(techan.NewClosePriceIndicator(series), 2, 3)
	fmt.Println(macd.Calculate(3).FormattedString(4))
	series.Candles[3].ClosePrice = big.NewFromInt(20)
	techan.ResetCacheFrom(macd, 3)
	fmt.Println(macd.Calculate(3).FormattedString(4))
}
Output:
0.0000
1.6667

Types

type Analysis

type Analysis interface {
	Analyze(*TradingRecord) float64
}

Analysis measures the performance of a trading record.

type AverageProfitAnalysis

type AverageProfitAnalysis struct{}

AverageProfitAnalysis measures average realized profit per closed position.

func (AverageProfitAnalysis) Analyze

func (AverageProfitAnalysis) Analyze(record *TradingRecord) float64

Analyze returns average realized profit, or zero if no positions are closed.

type BuyAndHoldAnalysis

type BuyAndHoldAnalysis struct {
	TimeSeries    *TimeSeries
	StartingMoney float64
}

BuyAndHoldAnalysis compares buying at the first available candle close with holding until the last closed trade's exit time. Only candles ending at or before that time are eligible, so future or unfinished bars cannot affect it.

func (BuyAndHoldAnalysis) Analyze

func (baha BuyAndHoldAnalysis) Analyze(record *TradingRecord) float64

Analyze returns the hypothetical profit, or zero if no comparison is possible.

type CacheResetter added in v0.13.0

type CacheResetter interface {
	ResetCacheFrom(index int)
}

CacheResetter describes indicators whose cached values can be invalidated after their underlying data changes.

type Candle

type Candle struct {
	Period     TimePeriod
	OpenPrice  big.Decimal
	ClosePrice big.Decimal
	MaxPrice   big.Decimal
	MinPrice   big.Decimal
	Volume     big.Decimal
	TradeCount uint
}

Candle represents basic market information for a security over a given time period

func NewCandle

func NewCandle(period TimePeriod) (c *Candle)

NewCandle returns a new *Candle for a given time period

func (*Candle) AddTrade

func (c *Candle) AddTrade(tradeAmount, tradePrice big.Decimal)

AddTrade adds a trade to this candle. It will determine if the current price is higher or lower than the min or max price and increment the tradecount.

func (*Candle) String

func (c *Candle) String() string

type DecreaseRule

type DecreaseRule struct {
	Indicator
}

DecreaseRule is satisfied when the given Indicator at the given index is less than the value at the previous index.

func (DecreaseRule) IsSatisfied

func (dr DecreaseRule) IsSatisfied(index int, record *TradingRecord) bool

IsSatisfied returns true when the given Indicator at the given index is less than the value at the previous index.

type DerivativeIndicator

type DerivativeIndicator struct {
	Indicator Indicator
}

DerivativeIndicator returns an indicator that calculates the derivative of the underlying Indicator. The derivative is defined as the difference between the value at the previous index and the value at the current index. Eg series [1, 1, 2, 3, 5, 8] -> [0, 0, 1, 1, 2, 3]

func (DerivativeIndicator) Calculate

func (di DerivativeIndicator) Calculate(index int) big.Decimal

Calculate returns the derivative of the underlying indicator. At index 0, it will always return 0.

func (DerivativeIndicator) FirstValidIndex added in v0.14.0

func (di DerivativeIndicator) FirstValidIndex() int

type IncreaseRule

type IncreaseRule struct {
	Indicator
}

IncreaseRule is satisfied when the given Indicator at the given index is greater than the value at the previous index.

func (IncreaseRule) IsSatisfied

func (ir IncreaseRule) IsSatisfied(index int, record *TradingRecord) bool

IsSatisfied returns true when the given Indicator at the given index is greater than the value at the previous index.

type Indicator

type Indicator interface {
	Calculate(int) big.Decimal
}

Indicator is an interface that describes a methodology by which to analyze a trading record for a specific property or trend. For example. MovingAverageIndicator implements the Indicator interface and, for a given index in the timeSeries, returns the current moving average of the prices in that series.

func NewAroonDownIndicator added in v0.9.0

func NewAroonDownIndicator(indicator Indicator, window int) Indicator

NewAroonDownIndicator returns a derivative indicator that will return a value based on the number of ticks since the lowest price in the window https://www.investopedia.com/terms/a/aroon.asp

Note: this indicator should be constructed with a either a LowPriceIndicator or a derivative thereof

func NewAroonUpIndicator added in v0.9.0

func NewAroonUpIndicator(indicator Indicator, window int) Indicator

NewAroonUpIndicator returns a derivative indicator that will return a value based on the number of ticks since the highest price in the window https://www.investopedia.com/terms/a/aroon.asp

Note: this indicator should be constructed with a either a HighPriceIndicator or a derivative thereof

func NewAverageGainsIndicator

func NewAverageGainsIndicator(indicator Indicator, window int) Indicator

NewAverageGainsIndicator Returns a new average gains indicator, which returns the average gains in the given window based on the given indicator. The divisor is the number of usable source observations so far, capped at window.

func NewAverageLossesIndicator

func NewAverageLossesIndicator(indicator Indicator, window int) Indicator

NewAverageLossesIndicator Returns a new average losses indicator, which returns the average losses in the given window based on the given indicator. The divisor is the number of usable source observations so far, capped at window.

func NewAverageTrueRangeIndicator added in v0.12.1

func NewAverageTrueRangeIndicator(series *TimeSeries, window int) Indicator

NewAverageTrueRangeIndicator returns a base indicator that calculates the average true range of the underlying over a window https://www.investopedia.com/terms/a/atr.asp

func NewBollingerLowerBandIndicator added in v0.11.0

func NewBollingerLowerBandIndicator(indicator Indicator, window int, sigma float64) Indicator

NewBollingerLowerBandIndicator returns a a derivative indicator which returns the lower bound of a bollinger band on the underlying indicator

func NewBollingerUpperBandIndicator added in v0.11.0

func NewBollingerUpperBandIndicator(indicator Indicator, window int, sigma float64) Indicator

NewBollingerUpperBandIndicator a a derivative indicator which returns the upper bound of a bollinger band on the underlying indicator

func NewCCIIndicator

func NewCCIIndicator(ts *TimeSeries, window int) Indicator

NewCCIIndicator Returns a new Commodity Channel Index Indicator http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:commodity_channel_index_cci

func NewClosePriceIndicator

func NewClosePriceIndicator(series *TimeSeries) Indicator

NewClosePriceIndicator returns an Indicator which returns the close price of a candle for a given index

func NewConstantIndicator

func NewConstantIndicator(constant float64) Indicator

NewConstantIndicator returns an indicator which always returns the same value for any index. It's useful when combined with other, fluxuating indicators to determine when an indicator has crossed a threshold.

func NewCumulativeGainsIndicator

func NewCumulativeGainsIndicator(indicator Indicator, window int) Indicator

NewCumulativeGainsIndicator returns a derivative indicator which returns all gains made in a base indicator for a given window.

func NewCumulativeLossesIndicator

func NewCumulativeLossesIndicator(indicator Indicator, window int) Indicator

NewCumulativeLossesIndicator returns a derivative indicator which returns all losses in a base indicator for a given window.

func NewDifferenceIndicator

func NewDifferenceIndicator(minuend, subtrahend Indicator) Indicator

NewDifferenceIndicator returns an indicator which returns the difference between one indicator (minuend) and a second indicator (subtrahend).

func NewEMAIndicator

func NewEMAIndicator(indicator Indicator, window int) Indicator

NewEMAIndicator returns a derivative indicator which returns the average of the current and preceding values in the given windowSize, with values closer to current index given more weight. It returns zero for indices before the first complete window. A more in-depth explanation can be found here: http://www.investopedia.com/terms/e/ema.asp Cached values are rounded to 256 binary bits (approximately 77 significant decimal digits) to bound memory usage over arbitrarily long histories.

func NewFastStochasticIndicator added in v0.12.1

func NewFastStochasticIndicator(series *TimeSeries, timeframe int) Indicator

NewFastStochasticIndicator returns a derivative Indicator which returns the fast stochastic indicator (%K) for the given window. https://www.investopedia.com/terms/s/stochasticoscillator.asp

func NewFixedIndicator

func NewFixedIndicator(vals ...float64) Indicator

NewFixedIndicator returns an indicator with a fixed set of values that are returned when an index is passed in

func NewGainIndicator added in v0.8.0

func NewGainIndicator(indicator Indicator) Indicator

NewGainIndicator returns a derivative indicator that returns the gains in the underlying indicator in the last bar, if any. If the delta is negative, zero is returned

func NewHighPriceIndicator

func NewHighPriceIndicator(series *TimeSeries) Indicator

NewHighPriceIndicator returns an Indicator which returns the high price of a candle for a given index

func NewKeltnerChannelLowerIndicator added in v0.12.1

func NewKeltnerChannelLowerIndicator(series *TimeSeries, window int) Indicator

func NewKeltnerChannelUpperIndicator added in v0.12.1

func NewKeltnerChannelUpperIndicator(series *TimeSeries, window int) Indicator

func NewLossIndicator added in v0.8.0

func NewLossIndicator(indicator Indicator) Indicator

NewLossIndicator returns a derivative indicator that returns the losses in the underlying indicator in the last bar, if any. If the delta is positive, zero is returned

func NewLowPriceIndicator

func NewLowPriceIndicator(series *TimeSeries) Indicator

NewLowPriceIndicator returns an Indicator which returns the low price of a candle for a given index

func NewMACDHistogramIndicator

func NewMACDHistogramIndicator(macdIdicator Indicator, signalLinewindow int) Indicator

NewMACDHistogramIndicator returns a derivative Indicator based on the MACDIndicator, the result of which is the macd indicator minus it's signalLinewindow EMA. A more in-depth explanation can be found here: http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:macd-histogram

Example
package main

import (
	"fmt"

	"github.com/sdcoffey/techan"
)

func main() {
	prices := techan.NewFixedIndicator(100, 100, 100, 100, 100, 100)
	macd := techan.NewMACDIndicator(prices, 2, 4)
	histogram := techan.NewMACDHistogramIndicator(macd, 2)
	first := techan.FirstValidIndex(histogram)
	fmt.Println(first, histogram.Calculate(first))
}
Output:
4 0

func NewMACDIndicator

func NewMACDIndicator(baseIndicator Indicator, shortwindow, longwindow int) Indicator

NewMACDIndicator returns a derivative Indicator which returns the difference between two EMAIndicators with long and short windows. It's useful for gauging the strength of price movements. A more in-depth explanation can be found here: http://www.investopedia.com/terms/m/macd.asp

func NewMMAIndicator added in v0.8.0

func NewMMAIndicator(indicator Indicator, window int) Indicator

NewMMAIndicator returns Wilder's modified moving average. It seeds with the first full window of usable input values, then uses a weight of 1/window. Cached values use the same 256-bit precision policy as EMA.

func NewMaximumDrawdownIndicator added in v0.12.0

func NewMaximumDrawdownIndicator(ind Indicator, window int) Indicator

NewMaximumDrawdownIndicator returns a derivative Indicator which returns the maximum drawdown of the underlying indicator over a window. Maximum drawdown is defined as the maximum observed loss from peak of an underlying indicator in a given timeframe. Maximum drawdown is given as a percentage of the peak. Use a window value of -1 to include all values present in the underlying indicator. Only declines after positive peaks are measured; if none exist, it returns zero. See: https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp

func NewMaximumValueIndicator added in v0.12.0

func NewMaximumValueIndicator(ind Indicator, window int) Indicator

NewMaximumValueIndicator returns a derivative Indicator which returns the maximum value present in a given window. Use a window value of -1 to include all values in the underlying indicator.

func NewMeanDeviationIndicator

func NewMeanDeviationIndicator(indicator Indicator, window int) Indicator

NewMeanDeviationIndicator returns a derivative Indicator which returns the mean deviation of a base indicator in a given window. Mean deviation is an average of all values on the base indicator from the mean of that indicator.

func NewMinimumValueIndicator added in v0.12.0

func NewMinimumValueIndicator(ind Indicator, window int) Indicator

NewMinimumValueIndicator returns a derivative Indicator which returns the minimum value present in a given window. Use a window value of -1 to include all values in the underlying indicator.

func NewOpenPriceIndicator

func NewOpenPriceIndicator(series *TimeSeries) Indicator

NewOpenPriceIndicator returns an Indicator which returns the open price of a candle for a given index

func NewPercentChangeIndicator

func NewPercentChangeIndicator(indicator Indicator) Indicator

NewPercentChangeIndicator returns a derivative indicator which returns the percent change (positive or negative) made in a base indicator up until the given indicator

func NewRelativeStrengthIndexIndicator

func NewRelativeStrengthIndexIndicator(indicator Indicator, timeframe int) Indicator

NewRelativeStrengthIndexIndicator returns a derivative Indicator which returns the relative strength index of the base indicator in a given time frame. Wilder's initialization uses timeframe actual changes (timeframe+1 prices), with zero returned during warm-up and for flat histories. A more in-depth explanation of relative strength index can be found here: https://www.investopedia.com/terms/r/rsi.asp

func NewRelativeStrengthIndicator

func NewRelativeStrengthIndicator(indicator Indicator, timeframe int) Indicator

NewRelativeStrengthIndicator returns a derivative Indicator which returns the relative strength of the base indicator in a given time frame. Relative strength is the average again of up periods during the time frame divided by the average loss of down period during the same time frame

func NewRelativeVigorIndexIndicator

func NewRelativeVigorIndexIndicator(series *TimeSeries) Indicator

NewRelativeVigorIndexIndicator returns an Indicator which returns the index of the relative vigor of the prices of a sercurity. Relative Vigor Index is simply the difference of the previous four days' close and open prices divided by the difference between the previous four days high and low prices. A more in-depth explanation of relative vigor index can be found here: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/relative-vigor-index

func NewRelativeVigorSignalLine

func NewRelativeVigorSignalLine(series *TimeSeries) Indicator

NewRelativeVigorSignalLine returns an Indicator intended to be used in conjunction with Relative vigor index, which returns the average value of the last 4 indices of the RVI indicator.

func NewSimpleMovingAverage

func NewSimpleMovingAverage(indicator Indicator, window int) Indicator

NewSimpleMovingAverage returns a derivative Indicator which returns the average of the current value and preceding values in the given windowSize. It returns zero for indices before the first complete window.

func NewSlowStochasticIndicator added in v0.12.1

func NewSlowStochasticIndicator(k Indicator, window int) Indicator

NewSlowStochasticIndicator returns a derivative Indicator which returns the slow stochastic indicator (%D) for the given window. https://www.investopedia.com/terms/s/stochasticoscillator.asp

func NewStandardDeviationIndicator

func NewStandardDeviationIndicator(ind Indicator) Indicator

NewStandardDeviationIndicator calculates the standard deviation of a base indicator. See https://www.investopedia.com/terms/s/standarddeviation.asp

func NewTrendlineIndicator

func NewTrendlineIndicator(indicator Indicator, window int) Indicator

NewTrendlineIndicator returns an indicator whose output is the slope of the trend line given by the values in the window.

func NewTrueRangeIndicator added in v0.12.1

func NewTrueRangeIndicator(series *TimeSeries) Indicator

NewTrueRangeIndicator returns a base indicator which calculates the true range at the current point in time for a series https://www.investopedia.com/terms/a/atr.asp

func NewTypicalPriceIndicator

func NewTypicalPriceIndicator(series *TimeSeries) Indicator

NewTypicalPriceIndicator returns an Indicator which returns the typical price of a candle for a given index. The typical price is an average of the high, low, and close prices for a given candle.

func NewVarianceIndicator

func NewVarianceIndicator(ind Indicator) Indicator

NewVarianceIndicator provides a way to find the variance in a base indicator, where variances is the sum of squared deviations from the mean at any given index in the time series.

func NewVolumeIndicator

func NewVolumeIndicator(series *TimeSeries) Indicator

NewVolumeIndicator returns an indicator which returns the volume of a candle for a given index

func NewWindowedStandardDeviationIndicator added in v0.11.0

func NewWindowedStandardDeviationIndicator(ind Indicator, window int) Indicator

NewWindowedStandardDeviationIndicator returns a indicator which calculates the standard deviation of the underlying indicator over a window. It returns zero before a complete window of usable input observations exists.

type LogTradesAnalysis

type LogTradesAnalysis struct {
	io.Writer
}

LogTradesAnalysis writes closed positions and their realized profits.

func (LogTradesAnalysis) Analyze

func (lta LogTradesAnalysis) Analyze(record *TradingRecord) float64

Analyze writes each closed position using its actual order sides.

type NumTradesAnalysis

type NumTradesAnalysis string

NumTradesAnalysis counts the trades in a record.

func (NumTradesAnalysis) Analyze

func (NumTradesAnalysis) Analyze(record *TradingRecord) float64

Analyze returns the number of recorded trades.

type Order

type Order struct {
	Side          OrderSide
	Security      string
	Price         big.Decimal
	Amount        big.Decimal
	ExecutionTime time.Time
}

Order represents a trade execution (buy or sell) with associated metadata.

type OrderSide

type OrderSide int

OrderSide is a simple enumeration representing the side of an Order (buy or sell)

const (
	BUY OrderSide = iota
	SELL
)

BUY and SELL enumerations

type OverIndicatorRule

type OverIndicatorRule struct {
	First  Indicator
	Second Indicator
}

OverIndicatorRule is a rule where the First Indicator must be greater than the Second Indicator to be Satisfied

func (OverIndicatorRule) IsSatisfied

func (oir OverIndicatorRule) IsSatisfied(index int, record *TradingRecord) bool

IsSatisfied returns true when the First Indicator is greater than the Second Indicator

type PercentGainAnalysis

type PercentGainAnalysis struct{}

PercentGainAnalysis normalizes total realized profit by the first closed position's entry cost. Returns are fractions (0.1 means 10%). It assumes that this cost represents starting capital; deposits, withdrawals, and fees are not modeled. Both long and short positions contribute their directional profit.

func (PercentGainAnalysis) Analyze

func (PercentGainAnalysis) Analyze(record *TradingRecord) float64

Analyze returns profit relative to starting capital, or zero for no closed positions or a zero initial cost basis.

type PeriodProfitAnalysis

type PeriodProfitAnalysis struct {
	Period time.Duration
}

PeriodProfitAnalysis normalizes realized profit to a duration, including fractional periods. For example, profit of 15 over 90 minutes is 10 per hour.

func (PeriodProfitAnalysis) Analyze

func (ppa PeriodProfitAnalysis) Analyze(record *TradingRecord) float64

Analyze returns zero if there are no closed trades or the durations are invalid.

type Position

type Position struct {
	// contains filtered or unexported fields
}

Position is a pair of two Order objects

func NewPosition

func NewPosition(openOrder Order) (t *Position)

NewPosition returns a new Position with the passed-in order as the open order

func (*Position) CostBasis

func (p *Position) CostBasis() big.Decimal

CostBasis returns the price to enter this order

func (*Position) Enter

func (p *Position) Enter(order Order)

Enter sets the open order to the order passed in

func (*Position) EntranceOrder

func (p *Position) EntranceOrder() *Order

EntranceOrder returns the entrance order of this position

func (*Position) Exit

func (p *Position) Exit(order Order)

Exit sets the exit order to the order passed in

func (*Position) ExitOrder

func (p *Position) ExitOrder() *Order

ExitOrder returns the exit order of this position

func (*Position) ExitValue

func (p *Position) ExitValue() big.Decimal

ExitValue returns the value accrued by closing the position

func (*Position) IsClosed

func (p *Position) IsClosed() bool

IsClosed returns true of there are both entrance and exit orders

func (*Position) IsLong

func (p *Position) IsLong() bool

IsLong returns true if the entrance order is a buy order

func (*Position) IsNew

func (p *Position) IsNew() bool

IsNew returns true if there is neither an entrance or exit order

func (*Position) IsOpen

func (p *Position) IsOpen() bool

IsOpen returns true if there is an entrance order but no exit order

func (*Position) IsShort

func (p *Position) IsShort() bool

IsShort returns true if the entrance order is a sell order

type PositionNewRule

type PositionNewRule struct{}

PositionNewRule is satisfied when the current position in the trading record is new (no open positions).

func (PositionNewRule) IsSatisfied

func (pnr PositionNewRule) IsSatisfied(index int, record *TradingRecord) bool

IsSatisfied returns true if the current position in the record is new

type PositionOpenRule

type PositionOpenRule struct{}

PositionOpenRule is satisfied when the current position in the trading record is open (position has been entered but not exited).

func (PositionOpenRule) IsSatisfied

func (pnr PositionOpenRule) IsSatisfied(index int, record *TradingRecord) bool

IsSatisfied returns true if the current position in the record is Open

type ProfitableTradesAnalysis

type ProfitableTradesAnalysis struct{}

ProfitableTradesAnalysis counts profitable closed positions.

func (ProfitableTradesAnalysis) Analyze

Analyze counts profitable long and short positions.

type Rule

type Rule interface {
	IsSatisfied(index int, record *TradingRecord) bool
}

Rule is an interface describing an algorithm by which a set of criteria may be satisfied

func And

func And(r1, r2 Rule) Rule

And returns a new rule whereby BOTH of the passed-in rules must be satisfied for the rule to be satisfied

func NewCrossDownIndicatorRule

func NewCrossDownIndicatorRule(upper, lower Indicator) Rule

NewCrossDownIndicatorRule returns a new rule that is satisfied when the upper indicator has crossed below the lower indicator.

func NewCrossUpIndicatorRule

func NewCrossUpIndicatorRule(upper, lower Indicator) Rule

NewCrossUpIndicatorRule returns a new rule that is satisfied when the lower indicator has crossed above the upper indicator.

func NewPercentChangeRule

func NewPercentChangeRule(indicator Indicator, percent float64) Rule

NewPercentChangeRule returns a rule whereby the given Indicator must have changed by a given percentage to be satisfied. You should specify percent as a float value between -1 and 1

func NewStopLossRule

func NewStopLossRule(series *TimeSeries, lossTolerance float64) Rule

NewStopLossRule returns a new rule that is satisfied when the given loss tolerance (a percentage) is met or exceeded. Loss tolerance should be a value between -1 and 1.

func Or

func Or(r1, r2 Rule) Rule

Or returns a new rule whereby ONE OF the passed-in rules must be satisfied for the rule to be satisfied

type RuleStrategy

type RuleStrategy struct {
	EntryRule      Rule
	ExitRule       Rule
	UnstablePeriod int
}

RuleStrategy is a strategy based on rules and an unstable period. The two rules determine whether a position should be created or closed, and the unstable period is an index before no positions should be created or exited

Example
package main

import (
	"fmt"
	"time"

	"github.com/sdcoffey/big"
	"github.com/sdcoffey/techan"
)

func main() {
	prices := techan.NewFixedIndicator(99, 101, 102, 98)
	threshold := techan.NewConstantIndicator(100)
	strategy := techan.RuleStrategy{
		EntryRule: techan.NewCrossUpIndicatorRule(threshold, prices),
		ExitRule:  techan.NewCrossDownIndicatorRule(prices, threshold),
	}
	record := techan.NewTradingRecord()
	for i := 0; i < 4; i++ {
		if strategy.ShouldEnter(i, record) {
			record.Operate(techan.Order{Side: techan.BUY, Price: prices.Calculate(i), Amount: big.ONE, ExecutionTime: time.Unix(int64(i), 0)})
			fmt.Println("enter", i)
		} else if strategy.ShouldExit(i, record) {
			record.Operate(techan.Order{Side: techan.SELL, Price: prices.Calculate(i), Amount: big.ONE, ExecutionTime: time.Unix(int64(i), 0)})
			fmt.Println("exit", i)
		}
	}
	fmt.Println("profit", techan.TotalProfitAnalysis{}.Analyze(record))
}
Output:
enter 1
exit 3
profit -3

func (RuleStrategy) ShouldEnter

func (rs RuleStrategy) ShouldEnter(index int, record *TradingRecord) bool

ShouldEnter returns true at or after UnstablePeriod when the position is new and the entry rule is satisfied.

func (RuleStrategy) ShouldExit

func (rs RuleStrategy) ShouldExit(index int, record *TradingRecord) bool

ShouldExit returns true at or after UnstablePeriod when the position is open and the exit rule is satisfied.

type Strategy

type Strategy interface {
	ShouldEnter(index int, record *TradingRecord) bool
	ShouldExit(index int, record *TradingRecord) bool
}

Strategy is an interface that describes desired entry and exit trading behavior

type TimePeriod

type TimePeriod struct {
	Start time.Time
	End   time.Time
}

TimePeriod is a simple struct that describes a period of time with a Start and End time

func NewTimePeriod

func NewTimePeriod(start time.Time, period time.Duration) TimePeriod

NewTimePeriod returns a TimePeriod starting at the given time and ending at the given time plus the given duration

func Parse deprecated

func Parse(timerange string) (tr TimePeriod, err error)

Parse takes a string in one of the following formats and returns a new TimePeriod, and optionally, an error

Deprecated: Please use ParseTimePeriod instead

func ParseTimePeriod added in v0.9.0

func ParseTimePeriod(period string) (TimePeriod, error)

ParseTimePeriod parses one or two UTC endpoints in YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS format, separated by whitespace or punctuation. A single endpoint uses the current time as its end. Only whitespace may surround the endpoints; dangling separators, malformed or reversed ranges return errors.

Unlike the deprecated Parse function, dates use year-month-day ordering.

Example
// Any separator between two times is valid
parseable := "2009-01-20T12:00:00 -- 2017-01-20T12:00:00"
timePeriod, err := ParseTimePeriod(parseable)
if err != nil {
	return
}

fmt.Println(timePeriod.Start.Year(), timePeriod.End.Year())
Output:
2009 2017

func (TimePeriod) Advance

func (tp TimePeriod) Advance(iterations int) TimePeriod

Advance will return a new TimePeriod with the start and end periods moved forwards or backwards in time in accordance with the number of iterations given.

Example: A timePeriod that is one hour long, starting at unix time 0 and ending at unix time 3600, and advanced by one, will return a time period starting at unix time 3600 and ending at unix time 7200

func (TimePeriod) Format

func (tp TimePeriod) Format(layout string) string

Format returns the string representation of this timePeriod in the given format

func (TimePeriod) In added in v0.10.0

func (tp TimePeriod) In(location *time.Location) TimePeriod

In returns a copy of TimePeriod tp with both start and end times' location set to the specified location

func (TimePeriod) Length

func (tp TimePeriod) Length() time.Duration

Length returns the length of the period as a time.Duration value

func (TimePeriod) Since

func (tp TimePeriod) Since(other TimePeriod) time.Duration

Since returns the amount of time elapsed since the end of another TimePeriod as a time.Duration value

func (TimePeriod) String

func (tp TimePeriod) String() string

func (TimePeriod) UTC added in v0.10.0

func (tp TimePeriod) UTC() TimePeriod

UTC returns a copy of TimePeriod tp with both start and end times' location set to UTC

type TimeSeries

type TimeSeries struct {
	Candles []*Candle
}

TimeSeries represents an array of candles

func NewTimeSeries

func NewTimeSeries() (t *TimeSeries)

NewTimeSeries returns a new, empty, TimeSeries

func (*TimeSeries) AddCandle

func (ts *TimeSeries) AddCandle(candle *Candle) bool

AddCandle adds the given candle to this TimeSeries if it is not nil and after the last candle in this timeseries. If the candle is added, AddCandle will return true, otherwise it will return false.

func (*TimeSeries) LastCandle

func (ts *TimeSeries) LastCandle() *Candle

LastCandle will return the lastCandle in this series, or nil if this series is empty

func (*TimeSeries) LastIndex

func (ts *TimeSeries) LastIndex() int

LastIndex will return the index of the last candle in this series

type TotalProfitAnalysis

type TotalProfitAnalysis struct{}

TotalProfitAnalysis measures realized profit, accounting for order direction.

func (TotalProfitAnalysis) Analyze

func (TotalProfitAnalysis) Analyze(record *TradingRecord) float64

Analyze returns total realized profit across closed positions.

type TradingRecord

type TradingRecord struct {
	Trades []*Position
	// contains filtered or unexported fields
}

TradingRecord is an object describing a series of trades made and a current position

func NewTradingRecord

func NewTradingRecord() (t *TradingRecord)

NewTradingRecord returns a new TradingRecord

func (*TradingRecord) CurrentPosition

func (tr *TradingRecord) CurrentPosition() *Position

CurrentPosition returns the current position in this record

func (*TradingRecord) LastTrade

func (tr *TradingRecord) LastTrade() *Position

LastTrade returns the last trade executed in this record

func (*TradingRecord) Operate

func (tr *TradingRecord) Operate(order Order)

Operate takes an order and adds it to the current TradingRecord. It will only add the order if: - The current position is open and the passed order was executed after the entrance order - The current position is new and the passed order was executed after the last exit order

type UnderIndicatorRule

type UnderIndicatorRule struct {
	First  Indicator
	Second Indicator
}

UnderIndicatorRule is a rule where the First Indicator must be less than the Second Indicator to be Satisfied

func (UnderIndicatorRule) IsSatisfied

func (uir UnderIndicatorRule) IsSatisfied(index int, record *TradingRecord) bool

IsSatisfied returns true when the First Indicator is less than the Second Indicator

type WarmupIndicator added in v0.14.0

type WarmupIndicator interface {
	FirstValidIndex() int
}

WarmupIndicator reports the first index with enough observations to calculate a value. Earlier values are placeholders and must not seed another indicator.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL