mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-13 02:23:51 +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) {
|
func (inc *AD) Update(kLine types.KLine) {
|
||||||
close := kLine.Close.Float64()
|
cloze := kLine.Close.Float64()
|
||||||
high := kLine.High.Float64()
|
high := kLine.High.Float64()
|
||||||
low := kLine.Low.Float64()
|
low := kLine.Low.Float64()
|
||||||
volume := kLine.Volume.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
|
ad := inc.Last() + moneyFlowVolume
|
||||||
inc.Values.Push(ad)
|
inc.Values.Push(ad)
|
||||||
|
@ -43,10 +48,10 @@ func (inc *AD) Last() float64 {
|
||||||
|
|
||||||
func (inc *AD) Index(i int) float64 {
|
func (inc *AD) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
length := len(inc.Values)
|
||||||
if length == 0 || length - i - 1 < 0 {
|
if length == 0 || length-i-1 < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values[length - i - 1]
|
return inc.Values[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *AD) Length() int {
|
func (inc *AD) Length() int {
|
||||||
|
|
|
@ -6,6 +6,11 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"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 {
|
type Line struct {
|
||||||
types.IntervalWindow
|
types.IntervalWindow
|
||||||
start float64
|
start float64
|
||||||
|
@ -39,4 +44,25 @@ func (l *Line) Length() int {
|
||||||
return int(l.startTime.Sub(l.currentTime).Minutes()) / l.Interval.Minutes()
|
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{}
|
var _ types.Series = &Line{}
|
||||||
|
|
|
@ -65,10 +65,10 @@ func (inc *RSI) Last() float64 {
|
||||||
|
|
||||||
func (inc *RSI) Index(i int) float64 {
|
func (inc *RSI) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
length := len(inc.Values)
|
||||||
if length <= 0 || length - i - 1 < 0 {
|
if length <= 0 || length-i-1 < 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
return inc.Values[length - i - 1]
|
return inc.Values[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RSI) Length() int {
|
func (inc *RSI) Length() int {
|
||||||
|
|
|
@ -32,11 +32,11 @@ func (inc *SMA) Last() float64 {
|
||||||
|
|
||||||
func (inc *SMA) Index(i int) float64 {
|
func (inc *SMA) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
length := len(inc.Values)
|
||||||
if length == 0 || length - i - 1 < 0 {
|
if length == 0 || length-i-1 < 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
return inc.Values[length - i - 1]
|
return inc.Values[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *SMA) Length() int {
|
func (inc *SMA) Length() int {
|
||||||
|
|
|
@ -105,10 +105,10 @@ func (inc *DSeries) Length() int {
|
||||||
|
|
||||||
func (inc *DSeries) Index(i int) float64 {
|
func (inc *DSeries) Index(i int) float64 {
|
||||||
length := len(inc.D)
|
length := len(inc.D)
|
||||||
if length == 0 || length - i - 1 < 0 {
|
if length == 0 || length-i-1 < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.D[length - i - 1]
|
return inc.D[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.Series = &DSeries{}
|
var _ types.Series = &DSeries{}
|
||||||
|
@ -123,10 +123,10 @@ func (inc *KSeries) Last() float64 {
|
||||||
|
|
||||||
func (inc *KSeries) Index(i int) float64 {
|
func (inc *KSeries) Index(i int) float64 {
|
||||||
length := len(inc.K)
|
length := len(inc.K)
|
||||||
if length == 0 || length - i - 1 < 0 {
|
if length == 0 || length-i-1 < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.K[length - i - 1]
|
return inc.K[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *KSeries) Length() int {
|
func (inc *KSeries) Length() int {
|
||||||
|
|
|
@ -37,11 +37,11 @@ func (inc *VWAP) Last() float64 {
|
||||||
|
|
||||||
func (inc *VWAP) Index(i int) float64 {
|
func (inc *VWAP) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
length := len(inc.Values)
|
||||||
if length == 0 || length - i - 1 < 0 {
|
if length == 0 || length-i-1 < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return inc.Values[length - i - 1]
|
return inc.Values[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWAP) Length() int {
|
func (inc *VWAP) Length() int {
|
||||||
|
|
|
@ -36,10 +36,10 @@ func (inc *VWMA) Last() float64 {
|
||||||
|
|
||||||
func (inc *VWMA) Index(i int) float64 {
|
func (inc *VWMA) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
length := len(inc.Values)
|
||||||
if length == 0 || length - i - 1 < 0 {
|
if length == 0 || length-i-1 < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values[length - i - 1]
|
return inc.Values[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWMA) Length() int {
|
func (inc *VWMA) Length() int {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user