specification_pattern

package module
v0.0.0-...-295d2c8 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2019 License: LGPL-3.0 Imports: 8 Imported by: 0

README

specification_pattern_go

The Specification Pattern is a pattern aimed for composing abstract conditions.

Check this amazing read by Eric Evans and Martin Fowler https://www.martinfowler.com/apsupp/spec.pdf which explains in details the advantages of the specification pattern over DDD and other architectural methods.

You can also learn more on the wikipedia page (don't forget to donate) https://en.wikipedia.org/wiki/Specification_pattern

This small demo shows how to implement the specification pattern in Go with a technical analysis example. As long as a struct implement the following interface, it can be reused and composed with other structs.


type IRule interface {
	IsCalculated(date int64) bool
	IsSatisfied(date int64) (bool, error)
	And(rule IRule) IRule
	Or(rule IRule) IRule
	Xor(rule IRule) IRule
	Negation() IRule
	// simplified
}

This is not a package aimed at production - merely an example of the specification pattern implementation.

This is hugely inspired by the ta4j java library https://github.com/ta4j/ta4j - which I took some of the resource testing data from.

The original code is proprietary (owned by me), and I stripped down the sensitive parts. - which is the reason why you still get remnants of caching implementation for high performance processing.

Technically the original code can be used this way:


// this is a dummy example

history := NewPerformanceAskBidTradeHistory()
cache := NewIndicatorCache(history)
ruleCache := NewRuleCache(history)

constant5 := NewIndicatorConstant(5, cache)
constant6 := NewIndicatorConstant(4, cache)

sma := NewIndicatorSMA(60, live, cache)

smaOver5 := NewOverIndicatorRule(sma, constant5, 0, rulecache)

smaOver6 := NewOverIndicatorRule(sma, constant6, 0, rulecache)
smaUnder6 := NewNotRule(smaOver64, ruleCache)

smaOver5AndSmaUnder6 := NewAndRule(smaOver5, smaUnder6, ruleCache)
smaOver5AndSmaUnder6.IsSatisfied(time.Now().Unix())

This code is certainly faster in some parts than the Java original:

For example, the SMA implementation of ta4j makes the SUM divided by the length every time to calculate it. https://github.com/ta4j/ta4j/blob/master/ta4j-core/src/main/java/org/ta4j/core/indicators/SMAIndicator.java

protected Num calculate(int index) {
    Num sum = getTimeSeries().numOf(0);
    for (int i = Math.max(0, index - barCount + 1); i <= index; i++) {
        sum = sum.plus(indicator.getValue(i));
    }

    final int realBarCount = Math.min(barCount, index + 1);
    return sum.dividedBy(getTimeSeries().numOf(realBarCount));
}

Instead my implementation uses the previous calculated value and do one addition and one subtraction instead. It is uglier - but faster.

I don't know how much faster it is, because I don't bother benchmarking Java code - but it certainly is.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FloatToStringPrecision

func FloatToStringPrecision(input_num float64, precision int) string

func PointToPeriod

func PointToPeriod(nbPoints int) string

func StrToFloat

func StrToFloat(input string) float64

Types

type AbstractRule

type AbstractRule struct{}

type AndRule

type AndRule struct {
	*CommonRule
	// contains filtered or unexported fields
}

func (*AndRule) IsSatisfied

func (r *AndRule) IsSatisfied(date int64) (bool, error)

type AskBidTrade

type AskBidTrade struct {
	InputDate time.Time
	Volume    float64
	Trade     Record
	Ask       Record
	Bid       Record
}

func NewAskBidTradeFromAskBid

func NewAskBidTradeFromAskBid(askPrice float64, bidPrice float64, inputDate time.Time) *AskBidTrade

func NewAskBidTradeFromTrade

func NewAskBidTradeFromTrade(transactionsPrice float64, volume float64, inputDate time.Time) *AskBidTrade

func (*AskBidTrade) GetBuyValue

func (f *AskBidTrade) GetBuyValue() float64

func (*AskBidTrade) GetFromSource

func (f *AskBidTrade) GetFromSource(source RecordDataSource) float64

func (*AskBidTrade) GetSellValue

func (f *AskBidTrade) GetSellValue() float64

func (*AskBidTrade) HasZero

func (f *AskBidTrade) HasZero() bool

func (*AskBidTrade) PingAsk

func (f *AskBidTrade) PingAsk(askPrice float64, inputDate time.Time)

func (*AskBidTrade) PingBid

func (f *AskBidTrade) PingBid(bidPrice float64, inputDate time.Time)

func (*AskBidTrade) PingTrade

func (f *AskBidTrade) PingTrade(transactionPrice float64, volume float64, inputDate time.Time)

func (*AskBidTrade) Similar

func (f *AskBidTrade) Similar(abd *AskBidTrade, percentage float64) bool

type BooleanRule

type BooleanRule struct {
	*CommonRule
	// contains filtered or unexported fields
}

func (*BooleanRule) IsSatisfied

func (r *BooleanRule) IsSatisfied(date int64) (bool, error)

type CommonRule

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

func (*CommonRule) And

func (cr *CommonRule) And(rule IRule) IRule

func (*CommonRule) Index

func (cr *CommonRule) Index() int

func (*CommonRule) IsCalculated

func (cr *CommonRule) IsCalculated(date int64) bool

func (*CommonRule) Name

func (cr *CommonRule) Name() string

func (*CommonRule) Negation

func (cr *CommonRule) Negation() IRule

func (*CommonRule) Or

func (cr *CommonRule) Or(rule IRule) IRule

func (*CommonRule) SetIndex

func (cr *CommonRule) SetIndex(index int)

func (*CommonRule) TriggerId

func (cr *CommonRule) TriggerId() int

func (*CommonRule) Xor

func (cr *CommonRule) Xor(rule IRule) IRule

type ConstantIndicator

type ConstantIndicator struct {
	*IndicatorCommon
	// contains filtered or unexported fields
}

func (*ConstantIndicator) Calculate

func (ind *ConstantIndicator) Calculate(date int64, add bool) error

type CrossDownIndicatorRule

type CrossDownIndicatorRule struct {
	*CommonRule
	// contains filtered or unexported fields
}

func (*CrossDownIndicatorRule) IsSatisfied

func (r *CrossDownIndicatorRule) IsSatisfied(date int64) (bool, error)

type CrossPosition

type CrossPosition string
const (
	CrossPositionOver  CrossPosition = "Over"
	CrossPositionUnder CrossPosition = "Under"
	CrossPositionZero  CrossPosition = "Zero"
)

type CrossUpIndicatorRule

type CrossUpIndicatorRule struct {
	*CommonRule
	// contains filtered or unexported fields
}

func (*CrossUpIndicatorRule) IsSatisfied

func (r *CrossUpIndicatorRule) IsSatisfied(date int64) (bool, error)

type Crosser

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

func NewCrosser

func NewCrosser(threshold float64, position CrossPosition) *Crosser

func (*Crosser) Calculate

func (c *Crosser) Calculate(val1, val2 float64) (bool, error)

type DiffData

type DiffData struct {
	Ratio float64
}

func NewDiffData

func NewDiffData(val1, val2 float64) DiffData

func (*DiffData) IsZero

func (d *DiffData) IsZero(zeroThreshold float64) bool

func (*DiffData) Sign

func (d *DiffData) Sign(zeroThreshold float64) CrossPosition

type IIndicator

type IIndicator interface {
	Calculate(date int64, add bool) error

	GetLastValues(nb int) []float64
	GetValue(index int) float64
	GetValues(fromIndex, toIndex int) []float64

	IsCalculated(date int64) bool
	Len() int
	Name() string
	SetCache(cache *IndicatorCache)
	SetHistory(history *PerformanceAskBidTradeHistory)
	Type() IndicatorType
	Values() []float64
	Val() float64
}

func NewIndicatorAbstractEma

func NewIndicatorAbstractEma(indicatorType IndicatorType, source IIndicator, barCount float64, multiplier float64, cache *IndicatorCache) IIndicator

func NewIndicatorConstant

func NewIndicatorConstant(constant float64, cache *IndicatorCache) IIndicator

func NewIndicatorEMA

func NewIndicatorEMA(nbPoints float64, source IIndicator, cache *IndicatorCache) IIndicator

func NewIndicatorLive

func NewIndicatorLive(source RecordDataSource, cache *IndicatorCache, history *PerformanceAskBidTradeHistory) IIndicator

func NewIndicatorMMA

func NewIndicatorMMA(source IIndicator, barCount float64, cache *IndicatorCache) IIndicator

func NewIndicatorSMA

func NewIndicatorSMA(nbPoints float64, source IIndicator, cache *IndicatorCache) IIndicator

type IRule

type IRule interface {
	IsCalculated(date int64) bool
	IsSatisfied(date int64) (bool, error)
	And(rule IRule) IRule
	Or(rule IRule) IRule
	Xor(rule IRule) IRule
	Negation() IRule

	Index() int
	SetIndex(index int)
	Name() string
}

func NewAndRule

func NewAndRule(rule1, rule2 IRule, cache *RuleCache) IRule

func NewBooleanRule

func NewBooleanRule(val bool, cache *RuleCache) IRule

func NewCrossDownIndicatorRule

func NewCrossDownIndicatorRule(indicator1, indicator2 IIndicator, threshold float64, cache *RuleCache) IRule

func NewCrossUpIndicatorRule

func NewCrossUpIndicatorRule(indicator1, indicator2 IIndicator, threshold float64, cache *RuleCache) IRule

func NewNotRule

func NewNotRule(rule IRule, cache *RuleCache) IRule

func NewOrRule

func NewOrRule(rule1, rule2 IRule, cache *RuleCache) IRule

func NewOverIndicatorRule

func NewOverIndicatorRule(indicator1, indicator2 IIndicator, thresholdInPercent float64, cache *RuleCache) IRule

* threshold is a percentage, for example threshold = 0.1, means 0.10%

if zero, it takes strict value ind1>ind2 otherwise - it takes percentage of ind1 > (1 + thresholdInPercent/100) * ind2

ind1 - ind2 -

func NewXorRule

func NewXorRule(rule1, rule2 IRule, cache *RuleCache) IRule

type IndicatorAbstractEma

type IndicatorAbstractEma struct {
	*IndicatorCommon
	// contains filtered or unexported fields
}

func (*IndicatorAbstractEma) Calculate

func (ind *IndicatorAbstractEma) Calculate(date int64, add bool) error

type IndicatorCache

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

func NewIndicatorCache

func NewIndicatorCache(history *PerformanceAskBidTradeHistory) *IndicatorCache

func (*IndicatorCache) Calculate

func (ic *IndicatorCache) Calculate(record *AskBidTrade, add bool) error

func (*IndicatorCache) Get

func (ic *IndicatorCache) Get(name string) IIndicator

func (*IndicatorCache) GetFirstLive

func (ic *IndicatorCache) GetFirstLive() IIndicator

func (*IndicatorCache) Has

func (ic *IndicatorCache) Has(name string) bool

func (*IndicatorCache) History

func (*IndicatorCache) List

func (ic *IndicatorCache) List() []string

func (*IndicatorCache) Set

func (ic *IndicatorCache) Set(indicator IIndicator)

type IndicatorCommon

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

func NewIndicatorCommon

func NewIndicatorCommon(indicatorType IndicatorType, name string, sourceName string) *IndicatorCommon

func (*IndicatorCommon) GetLastValues

func (ic *IndicatorCommon) GetLastValues(nb int) []float64

func (*IndicatorCommon) GetValue

func (ic *IndicatorCommon) GetValue(index int) float64

func (*IndicatorCommon) GetValues

func (ic *IndicatorCommon) GetValues(fromIndex, toIndex int) []float64

func (*IndicatorCommon) IsCalculated

func (ic *IndicatorCommon) IsCalculated(date int64) bool

func (*IndicatorCommon) Len

func (ic *IndicatorCommon) Len() int

func (*IndicatorCommon) Length

func (ic *IndicatorCommon) Length() int

func (*IndicatorCommon) Name

func (ic *IndicatorCommon) Name() string

func (*IndicatorCommon) SetCache

func (ic *IndicatorCommon) SetCache(cache *IndicatorCache)

func (*IndicatorCommon) SetHistory

func (ic *IndicatorCommon) SetHistory(history *PerformanceAskBidTradeHistory)

func (*IndicatorCommon) Type

func (ic *IndicatorCommon) Type() IndicatorType

func (*IndicatorCommon) Val

func (ic *IndicatorCommon) Val() float64

func (*IndicatorCommon) Values

func (ic *IndicatorCommon) Values() []float64

type IndicatorLive

type IndicatorLive struct {
	*IndicatorCommon
	// contains filtered or unexported fields
}

func (*IndicatorLive) Calculate

func (sma *IndicatorLive) Calculate(date int64, add bool) error

func (*IndicatorLive) GetRecordSource

func (sma *IndicatorLive) GetRecordSource() RecordSource

type IndicatorSMA

type IndicatorSMA struct {
	*IndicatorCommon
	// contains filtered or unexported fields
}

func (*IndicatorSMA) Calculate

func (sma *IndicatorSMA) Calculate(date int64, add bool) error

type IndicatorType

type IndicatorType string
const (
	CONSTANT IndicatorType = "CONSTANT"
	BOOL     IndicatorType = "BOOL"
	SMA      IndicatorType = "SMA"
	EMA      IndicatorType = "EMA"
	ROC      IndicatorType = "ROC"
	LOSS     IndicatorType = "LOSS Average"
	OBV      IndicatorType = "OBV"
	RSI      IndicatorType = "RSI"
	LIVE     IndicatorType = "LIVE"
	EMPTY    IndicatorType = ""
	ADX      IndicatorType = "ADX"
	MMA      IndicatorType = "MMA"
)

type NotRule

type NotRule struct {
	*CommonRule
}

func (*NotRule) IsSatisfied

func (r *NotRule) IsSatisfied(date int64) (bool, error)

type OrRule

type OrRule struct {
	*CommonRule
	// contains filtered or unexported fields
}

func (*OrRule) IsSatisfied

func (r *OrRule) IsSatisfied(date int64) (bool, error)

type OverIndicatorRule

type OverIndicatorRule struct {
	*CommonRule
	// contains filtered or unexported fields
}

func (*OverIndicatorRule) IsSatisfied

func (r *OverIndicatorRule) IsSatisfied(date int64) (bool, error)

type PerformanceAskBidTradeHistory

type PerformanceAskBidTradeHistory struct {
	Volumes      []float64 // 0
	TradeLow     []float64 // 1
	TradeHigh    []float64 // 2
	TradeOpen    []float64 // 3
	TradeClose   []float64 // 4
	AskLow       []float64 // 5
	AskHigh      []float64 // 6
	AskOpen      []float64 // 7
	AskClose     []float64 // 8
	BidLow       []float64 // 9
	BidHigh      []float64 // 10
	BidOpen      []float64 // 11
	BidClose     []float64 // 12
	InputDate    []time.Time
	InputDateStr []string
	// contains filtered or unexported fields
}

func NewPerformanceAskBidTradeHistory

func NewPerformanceAskBidTradeHistory() *PerformanceAskBidTradeHistory

func (*PerformanceAskBidTradeHistory) Append

func (p *PerformanceAskBidTradeHistory) Append(record *AskBidTrade)

func (*PerformanceAskBidTradeHistory) Current

func (*PerformanceAskBidTradeHistory) GetPoint

func (p *PerformanceAskBidTradeHistory) GetPoint(index int) (*AskBidTrade, error)

func (*PerformanceAskBidTradeHistory) IncludeCurrent

func (*PerformanceAskBidTradeHistory) Len

func (*PerformanceAskBidTradeHistory) SetCurrent

func (p *PerformanceAskBidTradeHistory) SetCurrent(record *AskBidTrade)

type Record

type Record struct {
	Low   float64
	High  float64
	Open  float64
	Close float64
}

func NewEmptyRecord

func NewEmptyRecord() Record

func NewRecord

func NewRecord(previousRecord Record) Record

func (Record) IsZero

func (r Record) IsZero() bool

func (Record) Similar

func (r Record) Similar(record Record, percentage float64) bool

type RecordDataSource

type RecordDataSource int
const (
	AskLow   RecordDataSource = 1
	AskHigh  RecordDataSource = 2
	AskClose RecordDataSource = 3
	BidLow   RecordDataSource = 4
	BidHigh  RecordDataSource = 5
	BidClose RecordDataSource = 6
	Volume   RecordDataSource = 7
	Invalid  RecordDataSource = 8
)

func (RecordDataSource) RecordSource

func (r RecordDataSource) RecordSource() RecordSource

func (RecordDataSource) String

func (r RecordDataSource) String() string

type RecordSource

type RecordSource int
const (
	Ask RecordSource = 1
	Bid RecordSource = 2
)

func (RecordSource) String

func (r RecordSource) String() string

type RuleCache

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

Satisfaction cache

func NewRuleCache

func NewRuleCache(history *PerformanceAskBidTradeHistory) *RuleCache

func (*RuleCache) AddBuyProcessor

func (rc *RuleCache) AddBuyProcessor(ruleIndex int, processorIndex int)

func (*RuleCache) AddSellProcessor

func (rc *RuleCache) AddSellProcessor(ruleIndex int, processorIndex int)

func (*RuleCache) Calculate

func (rc *RuleCache) Calculate(date time.Time) error

func (*RuleCache) GetById

func (rc *RuleCache) GetById(index int) IRule

func (*RuleCache) GetByName

func (rc *RuleCache) GetByName(name string) IRule

func (*RuleCache) GetSatisfiedProcessors

func (rc *RuleCache) GetSatisfiedProcessors() ([]int, []int)

func (*RuleCache) GetSatisfiedRules

func (rc *RuleCache) GetSatisfiedRules() []string

func (*RuleCache) IsSatisfied

func (rc *RuleCache) IsSatisfied(date int64, index int) (bool, error)

func (*RuleCache) Len

func (rc *RuleCache) Len() int

func (*RuleCache) Set

func (rc *RuleCache) Set(rule IRule) IRule

type SatisfactionStatus

type SatisfactionStatus int

type XorRule

type XorRule struct {
	*CommonRule
	// contains filtered or unexported fields
}

func (*XorRule) IsSatisfied

func (r *XorRule) IsSatisfied(date int64) (bool, error)

Jump to

Keyboard shortcuts

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