mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
fix: ad NaN error. feature: Line indicator init functions. feature: indicator manual
This commit is contained in:
parent
567e7bd214
commit
e171101d90
100
doc/development/indicator.md
Normal file
100
doc/development/indicator.md
Normal file
|
@ -0,0 +1,100 @@
|
|||
How To Use Builtin Indicators and Create New Indicators
|
||||
-------------------------------------------------------
|
||||
|
||||
### Built-in Indicators
|
||||
In bbgo session, we already have several indicators defined inside.
|
||||
We could refer to the live-data without the worriedness of handling market data subscription.
|
||||
To use the builtin ones, we could refer the `StandardIndicatorSet` type:
|
||||
|
||||
```go
|
||||
// defined in pkg/bbgo/session.go
|
||||
(*StandardIndicatorSet) BOLL(iw types.IntervalWindow, bandwidth float64) *indicator.BOLL
|
||||
(*StandardIndicatorSet) SMA(iw types.IntervalWindow) *indicator.SMA
|
||||
(*StandardIndicatorSet) EWMA(iw types.IntervalWindow) *indicator.EWMA
|
||||
(*StandardIndicatorSet) STOCH(iw types.IntervalWindow) *indicator.STOCH
|
||||
(*StandardIndicatorSet) VOLATILITY(iw types.IntervalWindow) *indicator.VOLATILITY
|
||||
```
|
||||
|
||||
and to get the `*StandardIndicatorSet` from `ExchangeSession`, just need to call:
|
||||
```go
|
||||
indicatorSet, ok := session.StandardIndicatorSet("BTCUSDT") // param: symbol
|
||||
```
|
||||
in your strategy's `Run` function.
|
||||
|
||||
|
||||
And in `Subscribe` function in strategy, just subscribe the `KLineChannel` on the interval window of the indicator you want to query, you should be able to acquire the latest number on the indicators.
|
||||
|
||||
However, what if you want to use the indicators not defined in `StandardIndicatorSet`? For example, the `AD` indicator defined in `pkg/indicators/ad.go`?
|
||||
|
||||
Here's a simple example in what you should write in your strategy code:
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
)
|
||||
|
||||
type Strategy struct {}
|
||||
|
||||
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||
session.Subscribe(types.KLineChannel, s.Symbol. types.SubscribeOptions{Interval: "1m"})
|
||||
}
|
||||
|
||||
func (s *Strategy) Run(ctx context.Context, oe bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||
// first we need to get market data store(cached market data) from the exchange session
|
||||
st, ok := session.MarketDataStore(s.Symbol)
|
||||
if !ok {
|
||||
...
|
||||
return err
|
||||
}
|
||||
// setup the time frame size
|
||||
window := types.IntervalWindow{Window: 10, Interval: types.Interval1m}
|
||||
// construct AD indicator
|
||||
AD := &indicator.AD{IntervalWindow: window}
|
||||
// bind indicator to the data store, so that our callback could be triggered
|
||||
AD.Bind(st)
|
||||
AD.OnUpdate(func (ad float64) {
|
||||
fmt.Printf("now we've got ad: %f, total length: %d\n", ad, AD.Length())
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### To Contribute
|
||||
|
||||
try to create new indicators in `pkg/indicator/` folder, and add compilation hint of go generator:
|
||||
```go
|
||||
// go:generate callbackgen -type StructName
|
||||
type StructName struct {
|
||||
...
|
||||
UpdateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
```
|
||||
And implement required interface methods:
|
||||
```go
|
||||
// custom function
|
||||
func (inc *StructName) calculateAndUpdate(kLines []types.KLine) {
|
||||
// calculation...
|
||||
// assign the result to calculatedValue
|
||||
inc.EmitUpdate(calculatedValue) // produce data, broadcast to the subscribers
|
||||
}
|
||||
|
||||
// custom function
|
||||
func (inc *StructName) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
// filter on interval
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
// required
|
||||
func (inc *StructName) Bind(updator KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
```
|
||||
|
||||
The `KLineWindowUpdater` interface is currently defined in `pkg/indicator/ewma.go` and may be moved out in the future.
|
||||
|
||||
Once the implementation is done, run `go generate` to generate the callback functions of the indicator.
|
||||
You should be able to implement your strategy and use the new indicator in the same way as `AD`.
|
|
@ -23,12 +23,17 @@ type AD struct {
|
|||
}
|
||||
|
||||
func (inc *AD) Update(kLine types.KLine) {
|
||||
close := kLine.Close.Float64()
|
||||
cloze := kLine.Close.Float64()
|
||||
high := kLine.High.Float64()
|
||||
low := kLine.Low.Float64()
|
||||
volume := kLine.Volume.Float64()
|
||||
|
||||
moneyFlowVolume := ((2*close - high - low) / (high - low)) * volume
|
||||
var moneyFlowVolume float64
|
||||
if high == low {
|
||||
moneyFlowVolume = 0
|
||||
} else {
|
||||
moneyFlowVolume = ((2*cloze - high - low) / (high - low)) * volume
|
||||
}
|
||||
|
||||
ad := inc.Last() + moneyFlowVolume
|
||||
inc.Values.Push(ad)
|
||||
|
@ -43,10 +48,10 @@ func (inc *AD) Last() float64 {
|
|||
|
||||
func (inc *AD) Index(i int) float64 {
|
||||
length := len(inc.Values)
|
||||
if length == 0 || length - i - 1 < 0 {
|
||||
if length == 0 || length-i-1 < 0 {
|
||||
return 0
|
||||
}
|
||||
return inc.Values[length - i - 1]
|
||||
return inc.Values[length-i-1]
|
||||
}
|
||||
|
||||
func (inc *AD) Length() int {
|
||||
|
|
|
@ -6,6 +6,11 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
// Line indicator is a utility that helps to simulate either the
|
||||
// 1. trend
|
||||
// 2. support
|
||||
// 3. resistance
|
||||
// of the market data, defined with series interface
|
||||
type Line struct {
|
||||
types.IntervalWindow
|
||||
start float64
|
||||
|
@ -39,4 +44,25 @@ func (l *Line) Length() int {
|
|||
return int(l.startTime.Sub(l.currentTime).Minutes()) / l.Interval.Minutes()
|
||||
}
|
||||
|
||||
func (l *Line) SetX1(value float64, startTime time.Time) {
|
||||
l.startTime = startTime
|
||||
l.start = value
|
||||
}
|
||||
|
||||
func (l *Line) SetX2(value float64, endTime time.Time) {
|
||||
l.endTime = endTime
|
||||
l.end = value
|
||||
}
|
||||
|
||||
func NewLine(startValue float64, startTime time.Time, endValue float64, endTime time.Time, interval types.Interval) *Line {
|
||||
return &Line{
|
||||
start: startValue,
|
||||
end: endValue,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
currentTime: endTime,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
var _ types.Series = &Line{}
|
||||
|
|
|
@ -65,10 +65,10 @@ func (inc *RSI) Last() float64 {
|
|||
|
||||
func (inc *RSI) Index(i int) float64 {
|
||||
length := len(inc.Values)
|
||||
if length <= 0 || length - i - 1 < 0 {
|
||||
if length <= 0 || length-i-1 < 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[length - i - 1]
|
||||
return inc.Values[length-i-1]
|
||||
}
|
||||
|
||||
func (inc *RSI) Length() int {
|
||||
|
|
|
@ -32,11 +32,11 @@ func (inc *SMA) Last() float64 {
|
|||
|
||||
func (inc *SMA) Index(i int) float64 {
|
||||
length := len(inc.Values)
|
||||
if length == 0 || length - i - 1 < 0 {
|
||||
if length == 0 || length-i-1 < 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return inc.Values[length - i - 1]
|
||||
return inc.Values[length-i-1]
|
||||
}
|
||||
|
||||
func (inc *SMA) Length() int {
|
||||
|
|
|
@ -105,10 +105,10 @@ func (inc *DSeries) Length() int {
|
|||
|
||||
func (inc *DSeries) Index(i int) float64 {
|
||||
length := len(inc.D)
|
||||
if length == 0 || length - i - 1 < 0 {
|
||||
if length == 0 || length-i-1 < 0 {
|
||||
return 0
|
||||
}
|
||||
return inc.D[length - i - 1]
|
||||
return inc.D[length-i-1]
|
||||
}
|
||||
|
||||
var _ types.Series = &DSeries{}
|
||||
|
@ -123,10 +123,10 @@ func (inc *KSeries) Last() float64 {
|
|||
|
||||
func (inc *KSeries) Index(i int) float64 {
|
||||
length := len(inc.K)
|
||||
if length == 0 || length - i - 1 < 0 {
|
||||
if length == 0 || length-i-1 < 0 {
|
||||
return 0
|
||||
}
|
||||
return inc.K[length - i - 1]
|
||||
return inc.K[length-i-1]
|
||||
}
|
||||
|
||||
func (inc *KSeries) Length() int {
|
||||
|
|
|
@ -37,11 +37,11 @@ func (inc *VWAP) Last() float64 {
|
|||
|
||||
func (inc *VWAP) Index(i int) float64 {
|
||||
length := len(inc.Values)
|
||||
if length == 0 || length - i - 1 < 0 {
|
||||
if length == 0 || length-i-1 < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return inc.Values[length - i - 1]
|
||||
return inc.Values[length-i-1]
|
||||
}
|
||||
|
||||
func (inc *VWAP) Length() int {
|
||||
|
|
|
@ -36,10 +36,10 @@ func (inc *VWMA) Last() float64 {
|
|||
|
||||
func (inc *VWMA) Index(i int) float64 {
|
||||
length := len(inc.Values)
|
||||
if length == 0 || length - i - 1 < 0 {
|
||||
if length == 0 || length-i-1 < 0 {
|
||||
return 0
|
||||
}
|
||||
return inc.Values[length - i - 1]
|
||||
return inc.Values[length-i-1]
|
||||
}
|
||||
|
||||
func (inc *VWMA) Length() int {
|
||||
|
|
Loading…
Reference in New Issue
Block a user