mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-21 22:43:52 +00:00
strategy: add fmaker
fmaker: cleanup
This commit is contained in:
parent
61a53947ee
commit
c904f9f0f7
26
config/fmaker.yaml
Normal file
26
config/fmaker.yaml
Normal file
|
@ -0,0 +1,26 @@
|
|||
sessions:
|
||||
binance:
|
||||
exchange: binance
|
||||
envVarPrefix: binance
|
||||
|
||||
|
||||
exchangeStrategies:
|
||||
- on: binance
|
||||
fmaker:
|
||||
symbol: BTCUSDT
|
||||
interval: 1m
|
||||
spread: 0.15%
|
||||
amount: 300 # 11
|
||||
|
||||
backtest:
|
||||
sessions:
|
||||
- binance
|
||||
startTime: "2022-01-01"
|
||||
endTime: "2022-05-31"
|
||||
symbols:
|
||||
- BTCUSDT
|
||||
account:
|
||||
binance:
|
||||
balances:
|
||||
BTC: 1 # 1
|
||||
USDT: 45_000 # 30_000
|
|
@ -10,6 +10,7 @@ import (
|
|||
_ "github.com/c9s/bbgo/pkg/strategy/ewoDgtrd"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/factorzoo"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/flashcrash"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/fmaker"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/funding"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/grid"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/kline"
|
||||
|
|
90
pkg/strategy/fmaker/A18.go
Normal file
90
pkg/strategy/fmaker/A18.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type A18
|
||||
type A18 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *A18) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A18) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateA18(recentT, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *A18) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A18) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
// CLOSE/DELAY(CLOSE,5)
|
||||
func calculateA18(klines []types.KLine, valClose KLineValueMapper) (float64, error) {
|
||||
window := 5
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
closes.Push(valClose(k))
|
||||
}
|
||||
|
||||
delay5 := closes.Index(4)
|
||||
curr := closes.Index(0)
|
||||
alpha := curr / delay5
|
||||
|
||||
return alpha, nil
|
||||
}
|
102
pkg/strategy/fmaker/A2.go
Normal file
102
pkg/strategy/fmaker/A2.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type A2
|
||||
type A2 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *A2) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A2) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateA2(recentT, KLineLowPriceMapper, KLineHighPriceMapper, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *A2) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A2) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
// (-1 * DELTA((((CLOSE - LOW) - (HIGH - CLOSE)) / (HIGH - LOW)), 1))
|
||||
func calculateA2(klines []types.KLine, valLow KLineValueMapper, valHigh KLineValueMapper, valClose KLineValueMapper) (float64, error) {
|
||||
window := 2
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var lows types.Float64Slice
|
||||
var highs types.Float64Slice
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
lows.Push(valLow(k))
|
||||
highs.Push(valHigh(k))
|
||||
closes.Push(valClose(k))
|
||||
}
|
||||
|
||||
prev := ((closes.Index(1) - lows.Index(1)) - (highs.Index(1) - closes.Index(1))) / (highs.Index(1) - lows.Index(1))
|
||||
curr := ((closes.Index(0) - lows.Index(0)) - (highs.Index(0) - closes.Index(0))) / (highs.Index(0) - lows.Index(0))
|
||||
alpha := (curr - prev) * -1 // delta(1 interval)
|
||||
|
||||
return alpha, nil
|
||||
}
|
||||
|
||||
func KLineLowPriceMapper(k types.KLine) float64 {
|
||||
return k.Low.Float64()
|
||||
}
|
||||
|
||||
func KLineHighPriceMapper(k types.KLine) float64 {
|
||||
return k.High.Float64()
|
||||
}
|
108
pkg/strategy/fmaker/A3.go
Normal file
108
pkg/strategy/fmaker/A3.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type A3
|
||||
type A3 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *A3) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A3) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateA3(recentT, KLineLowPriceMapper, KLineHighPriceMapper, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate pivots")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *A3) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A3) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
// SUM((CLOSE = DELAY(CLOSE, 1)?0:CLOSE-(CLOSE>DELAY(CLOSE, 1)?MIN(LOW, DELAY(CLOSE, 1)):MAX(HIGH, DELAY(CLOSE, 1)))), 6)
|
||||
func calculateA3(klines []types.KLine, valLow KLineValueMapper, valHigh KLineValueMapper, valClose KLineValueMapper) (float64, error) {
|
||||
window := 6 + 2
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var lows types.Float64Slice
|
||||
var highs types.Float64Slice
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
lows.Push(valLow(k))
|
||||
highs.Push(valHigh(k))
|
||||
closes.Push(valClose(k))
|
||||
}
|
||||
|
||||
a := 0.
|
||||
sumA := 0.
|
||||
for i := 1; i <= 6; i++ {
|
||||
if closes.Index(len(closes)-i) == closes.Index(len(closes)-i-1) {
|
||||
a = 0.
|
||||
} else {
|
||||
if closes.Index(len(closes)-i) > closes.Index(1) {
|
||||
a = closes.Index(len(closes)-i) - math.Min(lows.Index(len(lows)-i), closes.Index(len(closes)-i-1))
|
||||
} else {
|
||||
a = closes.Index(len(closes)-i) - math.Max(highs.Index(len(highs)-i), closes.Index(len(closes)-i-1))
|
||||
}
|
||||
}
|
||||
sumA += a
|
||||
}
|
||||
|
||||
alpha := sumA // sum(a, 6 interval)
|
||||
|
||||
return alpha, nil
|
||||
}
|
96
pkg/strategy/fmaker/A34.go
Normal file
96
pkg/strategy/fmaker/A34.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type A34
|
||||
type A34 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *A34) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A34) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateA34(recentT, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate pivots")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *A34) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A34) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateA34(klines []types.KLine, valClose KLineValueMapper) (float64, error) {
|
||||
window := 12
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
closes.Push(valClose(k))
|
||||
}
|
||||
|
||||
c := closes.Last()
|
||||
|
||||
sumC := 0.
|
||||
for i := 1; i <= 12; i++ {
|
||||
sumC += closes.Index(len(closes) - i)
|
||||
}
|
||||
|
||||
meanC := sumC / 12
|
||||
|
||||
alpha := meanC / c
|
||||
|
||||
return alpha, nil
|
||||
}
|
93
pkg/strategy/fmaker/R.go
Normal file
93
pkg/strategy/fmaker/R.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
var zeroTime time.Time
|
||||
|
||||
type KLineValueMapper func(k types.KLine) float64
|
||||
|
||||
//go:generate callbackgen -type R
|
||||
type R struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *R) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *R) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateR(recentT, indicator.KLineOpenPriceMapper, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate pivots")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *R) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *R) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateR(klines []types.KLine, valOpen KLineValueMapper, valClose KLineValueMapper) (float64, error) {
|
||||
window := 1
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var opens types.Float64Slice
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
opens.Push(valOpen(k))
|
||||
closes.Push(valClose(k))
|
||||
}
|
||||
|
||||
ret := opens.Index(0)/closes.Index(0) - 1 // delta(1 interval)
|
||||
|
||||
return ret, nil
|
||||
}
|
88
pkg/strategy/fmaker/S0.go
Normal file
88
pkg/strategy/fmaker/S0.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S0
|
||||
type S0 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *S0) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S0) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateS0(recentT, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *S0) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S0) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS0(klines []types.KLine, valClose KLineValueMapper) (float64, error) {
|
||||
window := 20
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
closes.Push(valClose(k))
|
||||
}
|
||||
|
||||
sma := types.Float64Slice.Sum(closes[len(closes)-window:len(closes)-1]) / float64(window)
|
||||
alpha := sma / closes.Last()
|
||||
|
||||
return alpha, nil
|
||||
}
|
99
pkg/strategy/fmaker/S1.go
Normal file
99
pkg/strategy/fmaker/S1.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S1
|
||||
type S1 struct {
|
||||
types.IntervalWindow
|
||||
Values types.Float64Slice
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *S1) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S1) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
correlation, err := calculateS1(recentT, inc.Window, KLineAmplitudeMapper, indicator.KLineVolumeMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate correlation")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(correlation)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(correlation)
|
||||
}
|
||||
|
||||
func (inc *S1) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S1) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS1(klines []types.KLine, window int, valA KLineValueMapper, valB KLineValueMapper) (float64, error) {
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window)
|
||||
}
|
||||
|
||||
sumA, sumB, sumAB, squareSumA, squareSumB := 0., 0., 0., 0., 0.
|
||||
for _, k := range klines {
|
||||
// sum of elements of array A
|
||||
sumA += valA(k)
|
||||
// sum of elements of array B
|
||||
sumB += valB(k)
|
||||
|
||||
// sum of A[i] * B[i].
|
||||
sumAB = sumAB + valA(k)*valB(k)
|
||||
|
||||
// sum of square of array elements.
|
||||
squareSumA = squareSumA + valA(k)*valA(k)
|
||||
squareSumB = squareSumB + valB(k)*valB(k)
|
||||
}
|
||||
// use formula for calculating correlation coefficient.
|
||||
corr := (float64(window)*sumAB - sumA*sumB) /
|
||||
math.Sqrt((float64(window)*squareSumA-sumA*sumA)*(float64(window)*squareSumB-sumB*sumB))
|
||||
|
||||
return -corr, nil
|
||||
}
|
||||
|
||||
func KLineAmplitudeMapper(k types.KLine) float64 {
|
||||
return k.High.Div(k.Low).Float64()
|
||||
}
|
95
pkg/strategy/fmaker/S2.go
Normal file
95
pkg/strategy/fmaker/S2.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S2
|
||||
type S2 struct {
|
||||
types.IntervalWindow
|
||||
Values types.Float64Slice
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *S2) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S2) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
correlation, err := calculateS2(recentT, inc.Window, indicator.KLineOpenPriceMapper, indicator.KLineVolumeMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate correlation")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(correlation)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(correlation)
|
||||
}
|
||||
|
||||
func (inc *S2) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S2) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS2(klines []types.KLine, window int, valA KLineValueMapper, valB KLineValueMapper) (float64, error) {
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window)
|
||||
}
|
||||
|
||||
sumA, sumB, sumAB, squareSumA, squareSumB := 0., 0., 0., 0., 0.
|
||||
for _, k := range klines {
|
||||
// sum of elements of array A
|
||||
sumA += valA(k)
|
||||
// sum of elements of array B
|
||||
sumB += valB(k)
|
||||
|
||||
// sum of A[i] * B[i].
|
||||
sumAB = sumAB + valA(k)*valB(k)
|
||||
|
||||
// sum of square of array elements.
|
||||
squareSumA = squareSumA + valA(k)*valA(k)
|
||||
squareSumB = squareSumB + valB(k)*valB(k)
|
||||
}
|
||||
// use formula for calculating correlation coefficient.
|
||||
corr := (float64(window)*sumAB - sumA*sumB) /
|
||||
math.Sqrt((float64(window)*squareSumA-sumA*sumA)*(float64(window)*squareSumB-sumB*sumB))
|
||||
|
||||
return -corr, nil
|
||||
}
|
91
pkg/strategy/fmaker/S3.go
Normal file
91
pkg/strategy/fmaker/S3.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S3
|
||||
type S3 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *S3) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S3) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateS3(recentT, indicator.KLineClosePriceMapper, indicator.KLineOpenPriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *S3) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S3) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS3(klines []types.KLine, valClose KLineValueMapper, valOpen KLineValueMapper) (float64, error) {
|
||||
window := 2
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var closes types.Float64Slice
|
||||
var opens types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
closes.Push(valClose(k))
|
||||
opens.Push(valOpen(k))
|
||||
}
|
||||
|
||||
prevC := closes.Index(1)
|
||||
currO := opens.Index(0)
|
||||
alpha := currO / prevC
|
||||
|
||||
return alpha, nil
|
||||
}
|
88
pkg/strategy/fmaker/S4.go
Normal file
88
pkg/strategy/fmaker/S4.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S4
|
||||
type S4 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *S4) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S4) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateS4(recentT, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *S4) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S4) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS4(klines []types.KLine, valClose KLineValueMapper) (float64, error) {
|
||||
window := 2
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
closes.Push(valClose(k))
|
||||
}
|
||||
|
||||
currC := closes.Index(0)
|
||||
alpha := 1 / currC
|
||||
|
||||
return alpha, nil
|
||||
}
|
96
pkg/strategy/fmaker/S5.go
Normal file
96
pkg/strategy/fmaker/S5.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S5
|
||||
type S5 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *S5) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S5) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateS5(recentT, indicator.KLineVolumeMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate pivots")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *S5) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S5) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS5(klines []types.KLine, valVolume KLineValueMapper) (float64, error) {
|
||||
window := 10
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var volumes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
volumes.Push(valVolume(k))
|
||||
}
|
||||
|
||||
v := volumes.Last()
|
||||
|
||||
sumV := 0.
|
||||
for i := 1; i <= 10; i++ {
|
||||
sumV += volumes.Index(len(volumes) - i)
|
||||
}
|
||||
|
||||
meanV := sumV / 10
|
||||
|
||||
alpha := -v / meanV
|
||||
|
||||
return alpha, nil
|
||||
}
|
98
pkg/strategy/fmaker/S6.go
Normal file
98
pkg/strategy/fmaker/S6.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S6
|
||||
type S6 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *S6) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S6) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateS6(recentT, indicator.KLineHighPriceMapper, indicator.KLineLowPriceMapper, indicator.KLineClosePriceMapper, indicator.KLineVolumeMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *S6) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S6) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS6(klines []types.KLine, valHigh KLineValueMapper, valLow KLineValueMapper, valClose KLineValueMapper, valVolume KLineValueMapper) (float64, error) {
|
||||
window := 2
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var highs types.Float64Slice
|
||||
var lows types.Float64Slice
|
||||
var closes types.Float64Slice
|
||||
var volumes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
highs.Push(valHigh(k))
|
||||
lows.Push(valLow(k))
|
||||
closes.Push(valClose(k))
|
||||
volumes.Push(valVolume(k))
|
||||
|
||||
}
|
||||
|
||||
H := highs.Last()
|
||||
L := lows.Last()
|
||||
C := closes.Last()
|
||||
V := volumes.Last()
|
||||
alpha := (H + L + C) / 3 * V
|
||||
|
||||
return alpha, nil
|
||||
}
|
92
pkg/strategy/fmaker/S7.go
Normal file
92
pkg/strategy/fmaker/S7.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate callbackgen -type S7
|
||||
type S7 struct {
|
||||
types.IntervalWindow
|
||||
|
||||
// Values
|
||||
Values types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(val float64)
|
||||
}
|
||||
|
||||
func (inc *S7) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S7) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
|
||||
val, err := calculateS7(recentT, indicator.KLineOpenPriceMapper, indicator.KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not calculate")
|
||||
return
|
||||
}
|
||||
inc.Values.Push(val)
|
||||
|
||||
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(val)
|
||||
|
||||
}
|
||||
|
||||
func (inc *S7) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S7) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func calculateS7(klines []types.KLine, valOpen KLineValueMapper, valClose KLineValueMapper) (float64, error) {
|
||||
window := 2
|
||||
length := len(klines)
|
||||
if length == 0 || length < window {
|
||||
return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
|
||||
}
|
||||
var opens types.Float64Slice
|
||||
var closes types.Float64Slice
|
||||
|
||||
for _, k := range klines {
|
||||
opens.Push(valOpen(k))
|
||||
closes.Push(valClose(k))
|
||||
|
||||
}
|
||||
|
||||
O := opens.Last()
|
||||
C := closes.Last()
|
||||
alpha := -(1 - O/C)
|
||||
|
||||
return alpha, nil
|
||||
}
|
15
pkg/strategy/fmaker/a18_callbacks.go
Normal file
15
pkg/strategy/fmaker/a18_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type A18"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *A18) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *A18) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/a2_callbacks.go
Normal file
15
pkg/strategy/fmaker/a2_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type A2"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *A2) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *A2) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/a34_callbacks.go
Normal file
15
pkg/strategy/fmaker/a34_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type A34"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *A34) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *A34) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/a3_callbacks.go
Normal file
15
pkg/strategy/fmaker/a3_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type A3"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *A3) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *A3) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/r_callbacks.go
Normal file
15
pkg/strategy/fmaker/r_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type R"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *R) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *R) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s0_callbacks.go
Normal file
15
pkg/strategy/fmaker/s0_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S0"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S0) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S0) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s1_callbacks.go
Normal file
15
pkg/strategy/fmaker/s1_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S1"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S1) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S1) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s2_callbacks.go
Normal file
15
pkg/strategy/fmaker/s2_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S2"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S2) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S2) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s3_callbacks.go
Normal file
15
pkg/strategy/fmaker/s3_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S3"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S3) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S3) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s4_callbacks.go
Normal file
15
pkg/strategy/fmaker/s4_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S4"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S4) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S4) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s5_callbacks.go
Normal file
15
pkg/strategy/fmaker/s5_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S5"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S5) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S5) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s6_callbacks.go
Normal file
15
pkg/strategy/fmaker/s6_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S6"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S6) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S6) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
15
pkg/strategy/fmaker/s7_callbacks.go
Normal file
15
pkg/strategy/fmaker/s7_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Code generated by "callbackgen -type S7"; DO NOT EDIT.
|
||||
|
||||
package fmaker
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *S7) OnUpdate(cb func(val float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *S7) EmitUpdate(val float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(val)
|
||||
}
|
||||
}
|
532
pkg/strategy/fmaker/strategy.go
Normal file
532
pkg/strategy/fmaker/strategy.go
Normal file
|
@ -0,0 +1,532 @@
|
|||
package fmaker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/sajari/regression"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"math"
|
||||
)
|
||||
|
||||
const ID = "fmaker"
|
||||
|
||||
var fifteen = fixedpoint.NewFromInt(15)
|
||||
var three = fixedpoint.NewFromInt(3)
|
||||
var two = fixedpoint.NewFromInt(2)
|
||||
|
||||
var log = logrus.WithField("strategy", ID)
|
||||
|
||||
func init() {
|
||||
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||
}
|
||||
|
||||
type IntervalWindowSetting struct {
|
||||
types.IntervalWindow
|
||||
}
|
||||
|
||||
type Strategy struct {
|
||||
*bbgo.Graceful
|
||||
*bbgo.Notifiability
|
||||
*bbgo.Persistence
|
||||
|
||||
Environment *bbgo.Environment
|
||||
Symbol string `json:"symbol"`
|
||||
Market types.Market
|
||||
Interval types.Interval `json:"interval"`
|
||||
Quantity fixedpoint.Value `json:"quantity"`
|
||||
|
||||
// persistence fields
|
||||
Position *types.Position `json:"position,omitempty" persistence:"position"`
|
||||
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
|
||||
|
||||
Spread fixedpoint.Value `json:"spread" persistence:"spread"`
|
||||
|
||||
activeMakerOrders *bbgo.LocalActiveOrderBook
|
||||
//closePositionOrders *bbgo.LocalActiveOrderBook
|
||||
orderStore *bbgo.OrderStore
|
||||
tradeCollector *bbgo.TradeCollector
|
||||
|
||||
session *bbgo.ExchangeSession
|
||||
|
||||
bbgo.QuantityOrAmount
|
||||
|
||||
S0 *S0
|
||||
S1 *S1
|
||||
S2 *S2
|
||||
S3 *S3
|
||||
S4 *S4
|
||||
S5 *S5
|
||||
S6 *S6
|
||||
S7 *S7
|
||||
|
||||
A2 *A2
|
||||
A3 *A3
|
||||
A18 *A18
|
||||
A34 *A34
|
||||
|
||||
R *R
|
||||
|
||||
// StrategyController
|
||||
bbgo.StrategyController
|
||||
}
|
||||
|
||||
func (s *Strategy) ID() string {
|
||||
return ID
|
||||
}
|
||||
|
||||
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||
log.Infof("subscribe %s", s.Symbol)
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval})
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: types.Interval15m})
|
||||
|
||||
}
|
||||
|
||||
func (s *Strategy) placeOrder(ctx context.Context, price fixedpoint.Value, qty fixedpoint.Value, orderExecutor bbgo.OrderExecutor) {
|
||||
submitOrder := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeLimit,
|
||||
Price: price,
|
||||
Quantity: qty,
|
||||
}
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, submitOrder)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not place orders")
|
||||
}
|
||||
s.orderStore.Add(createdOrders...)
|
||||
s.activeMakerOrders.Add(createdOrders...)
|
||||
//s.tradeCollector.Process()
|
||||
}
|
||||
|
||||
func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Value) error {
|
||||
base := s.Position.GetBase()
|
||||
if base.IsZero() {
|
||||
return fmt.Errorf("no opened %s position", s.Position.Symbol)
|
||||
}
|
||||
|
||||
// make it negative
|
||||
quantity := base.Mul(percentage).Abs()
|
||||
side := types.SideTypeBuy
|
||||
if base.Sign() > 0 {
|
||||
side = types.SideTypeSell
|
||||
}
|
||||
|
||||
if quantity.Compare(s.Market.MinQuantity) < 0 {
|
||||
return fmt.Errorf("order quantity %v is too small, less than %v", quantity, s.Market.MinQuantity)
|
||||
}
|
||||
|
||||
submitOrder := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: side,
|
||||
Type: types.OrderTypeMarket,
|
||||
Quantity: quantity,
|
||||
//Price: closePrice,
|
||||
Market: s.Market,
|
||||
}
|
||||
|
||||
//s.Notify("Submitting %s %s order to close position by %v", s.Symbol, side.String(), percentage, submitOrder)
|
||||
|
||||
createdOrders, err := s.session.Exchange.SubmitOrders(ctx, submitOrder)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not place position close order")
|
||||
}
|
||||
|
||||
s.orderStore.Add(createdOrders...)
|
||||
s.activeMakerOrders.Add(createdOrders...)
|
||||
return err
|
||||
}
|
||||
func (s *Strategy) InstanceID() string {
|
||||
return fmt.Sprintf("%s:%s", ID, s.Symbol)
|
||||
}
|
||||
|
||||
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||
// initial required information
|
||||
s.session = session
|
||||
//s.prevClose = fixedpoint.Zero
|
||||
|
||||
// first we need to get market data store(cached market data) from the exchange session
|
||||
//st, _ := session.MarketDataStore(s.Symbol)
|
||||
|
||||
s.activeMakerOrders = bbgo.NewLocalActiveOrderBook(s.Symbol)
|
||||
s.activeMakerOrders.BindStream(session.UserDataStream)
|
||||
|
||||
//s.closePositionOrders = bbgo.NewLocalActiveOrderBook(s.Symbol)
|
||||
//s.closePositionOrders.BindStream(session.UserDataStream)
|
||||
|
||||
s.orderStore = bbgo.NewOrderStore(s.Symbol)
|
||||
s.orderStore.BindStream(session.UserDataStream)
|
||||
|
||||
if s.Position == nil {
|
||||
s.Position = types.NewPositionFromMarket(s.Market)
|
||||
}
|
||||
|
||||
// calculate group id for orders
|
||||
instanceID := s.InstanceID()
|
||||
//s.groupID = util.FNV32(instanceID)
|
||||
|
||||
// Always update the position fields
|
||||
s.Position.Strategy = ID
|
||||
s.Position.StrategyInstanceID = instanceID
|
||||
|
||||
s.tradeCollector = bbgo.NewTradeCollector(s.Symbol, s.Position, s.orderStore)
|
||||
s.tradeCollector.OnTrade(func(trade types.Trade, profit, netProfit fixedpoint.Value) {
|
||||
// StrategyController
|
||||
if s.Status != types.StrategyStatusRunning {
|
||||
return
|
||||
}
|
||||
|
||||
s.Notifiability.Notify(trade)
|
||||
s.ProfitStats.AddTrade(trade)
|
||||
|
||||
if profit.Compare(fixedpoint.Zero) == 0 {
|
||||
s.Environment.RecordPosition(s.Position, trade, nil)
|
||||
} else {
|
||||
log.Infof("%s generated profit: %v", s.Symbol, profit)
|
||||
p := s.Position.NewProfit(trade, profit, netProfit)
|
||||
p.Strategy = ID
|
||||
p.StrategyInstanceID = instanceID
|
||||
s.Notify(&p)
|
||||
|
||||
s.ProfitStats.AddProfit(p)
|
||||
s.Notify(&s.ProfitStats)
|
||||
|
||||
s.Environment.RecordPosition(s.Position, trade, &p)
|
||||
}
|
||||
})
|
||||
|
||||
s.tradeCollector.OnPositionUpdate(func(position *types.Position) {
|
||||
log.Infof("position changed: %s", s.Position)
|
||||
s.Notify(s.Position)
|
||||
})
|
||||
s.tradeCollector.BindStream(session.UserDataStream)
|
||||
st, _ := session.MarketDataStore(s.Symbol)
|
||||
|
||||
riw := types.IntervalWindow{Window: 1, Interval: s.Interval}
|
||||
s.R = &R{IntervalWindow: riw}
|
||||
s.R.Bind(st)
|
||||
|
||||
s0iw := types.IntervalWindow{Window: 20, Interval: s.Interval}
|
||||
s.S0 = &S0{IntervalWindow: s0iw}
|
||||
s.S0.Bind(st)
|
||||
|
||||
s1iw := types.IntervalWindow{Window: 20, Interval: s.Interval}
|
||||
s.S1 = &S1{IntervalWindow: s1iw}
|
||||
s.S1.Bind(st)
|
||||
|
||||
s2iw := types.IntervalWindow{Window: 20, Interval: s.Interval}
|
||||
s.S2 = &S2{IntervalWindow: s2iw}
|
||||
s.S2.Bind(st)
|
||||
|
||||
s3iw := types.IntervalWindow{Window: 2, Interval: s.Interval}
|
||||
s.S3 = &S3{IntervalWindow: s3iw}
|
||||
s.S3.Bind(st)
|
||||
|
||||
s4iw := types.IntervalWindow{Window: 2, Interval: s.Interval}
|
||||
s.S4 = &S4{IntervalWindow: s4iw}
|
||||
s.S4.Bind(st)
|
||||
|
||||
s5iw := types.IntervalWindow{Window: 10, Interval: s.Interval}
|
||||
s.S5 = &S5{IntervalWindow: s5iw}
|
||||
s.S5.Bind(st)
|
||||
|
||||
s6iw := types.IntervalWindow{Window: 2, Interval: s.Interval}
|
||||
s.S6 = &S6{IntervalWindow: s6iw}
|
||||
s.S6.Bind(st)
|
||||
|
||||
s7iw := types.IntervalWindow{Window: 2, Interval: s.Interval}
|
||||
s.S7 = &S7{IntervalWindow: s7iw}
|
||||
s.S7.Bind(st)
|
||||
|
||||
a2iw := types.IntervalWindow{Window: 2, Interval: s.Interval}
|
||||
s.A2 = &A2{IntervalWindow: a2iw}
|
||||
s.A2.Bind(st)
|
||||
|
||||
a3iw := types.IntervalWindow{Window: 8, Interval: s.Interval}
|
||||
s.A3 = &A3{IntervalWindow: a3iw}
|
||||
s.A3.Bind(st)
|
||||
|
||||
a18iw := types.IntervalWindow{Window: 5, Interval: s.Interval}
|
||||
s.A18 = &A18{IntervalWindow: a18iw}
|
||||
s.A18.Bind(st)
|
||||
|
||||
a34iw := types.IntervalWindow{Window: 12, Interval: s.Interval}
|
||||
s.A34 = &A34{IntervalWindow: a34iw}
|
||||
s.A34.Bind(st)
|
||||
|
||||
session.UserDataStream.OnStart(func() {
|
||||
log.Infof("connected")
|
||||
})
|
||||
|
||||
outlook := 1
|
||||
|
||||
//futuresMode := s.session.Futures || s.session.IsolatedFutures
|
||||
cnt := 0
|
||||
|
||||
//var prevEr float64
|
||||
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
|
||||
|
||||
//if kline.Interval == types.Interval15m && kline.Symbol == s.Symbol && !s.Market.IsDustQuantity(s.Position.GetBase(), kline.Close) {
|
||||
// if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
||||
// log.WithError(err).Errorf("graceful cancel order error")
|
||||
// }
|
||||
// s.ClosePosition(ctx, fixedpoint.One)
|
||||
// s.tradeCollector.Process()
|
||||
//}
|
||||
if kline.Symbol != s.Symbol || kline.Interval != s.Interval {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
||||
log.WithError(err).Errorf("graceful cancel order error")
|
||||
}
|
||||
|
||||
cnt += 1
|
||||
if cnt < 15+1+outlook {
|
||||
return
|
||||
}
|
||||
|
||||
r := new(regression.Regression)
|
||||
r.SetObserved("Return Rate Per Interval")
|
||||
r.SetVar(0, "S0")
|
||||
r.SetVar(1, "S1")
|
||||
r.SetVar(2, "S2")
|
||||
//r.SetVar(2, "S3")
|
||||
r.SetVar(3, "S4")
|
||||
r.SetVar(4, "S5")
|
||||
r.SetVar(5, "S6")
|
||||
r.SetVar(6, "S7")
|
||||
r.SetVar(7, "A2")
|
||||
r.SetVar(8, "A3")
|
||||
r.SetVar(9, "A18")
|
||||
r.SetVar(10, "A34")
|
||||
|
||||
var rdps regression.DataPoints
|
||||
|
||||
for i := 1; i <= 15; i++ {
|
||||
s0 := s.S0.Values[len(s.S0.Values)-i-outlook]
|
||||
s1 := s.S1.Values[len(s.S1.Values)-i-outlook]
|
||||
s2 := s.S2.Values[len(s.S2.Values)-i-outlook]
|
||||
//s3 := s.S3.Values[len(s.S3.Values)-i-1]
|
||||
s4 := s.S4.Values[len(s.S4.Values)-i-outlook]
|
||||
s5 := s.S5.Values[len(s.S5.Values)-i-outlook]
|
||||
s6 := s.S6.Values[len(s.S6.Values)-i-outlook]
|
||||
s7 := s.S7.Values[len(s.S7.Values)-i-outlook]
|
||||
a2 := s.A2.Values[len(s.A2.Values)-i-outlook]
|
||||
a3 := s.A3.Values[len(s.A3.Values)-i-outlook]
|
||||
a18 := s.A18.Values[len(s.A18.Values)-i-outlook]
|
||||
a34 := s.A34.Values[len(s.A34.Values)-i-outlook]
|
||||
|
||||
ret := s.R.Values[len(s.R.Values)-i]
|
||||
rdps = append(rdps, regression.DataPoint(ret, types.Float64Slice{s0, s1, s2, s4, s5, s6, s7, a2, a3, a18, a34}))
|
||||
}
|
||||
//for i := 40; i > 20; i-- {
|
||||
// s0 := preprocessing(s.S0.Values[len(s.S0.Values)-i : len(s.S0.Values)-i+20-outlook])
|
||||
// s1 := preprocessing(s.S1.Values[len(s.S1.Values)-i : len(s.S1.Values)-i+20-outlook])
|
||||
// s2 := preprocessing(s.S2.Values[len(s.S2.Values)-i : len(s.S2.Values)-i+20-outlook])
|
||||
// //s3 := s.S3.Values[len(s.S3.Values)-i-1]
|
||||
// s4 := preprocessing(s.S4.Values[len(s.S4.Values)-i : len(s.S4.Values)-i+20-outlook])
|
||||
// s5 := preprocessing(s.S5.Values[len(s.S5.Values)-i : len(s.S5.Values)-i+20-outlook])
|
||||
// a2 := preprocessing(s.A2.Values[len(s.A2.Values)-i : len(s.A2.Values)-i+20-outlook])
|
||||
// a3 := preprocessing(s.A3.Values[len(s.A3.Values)-i : len(s.A3.Values)-i+20-outlook])
|
||||
// a18 := preprocessing(s.A18.Values[len(s.A18.Values)-i : len(s.A18.Values)-i+20-outlook])
|
||||
// a34 := preprocessing(s.A18.Values[len(s.A18.Values)-i : len(s.A18.Values)-i+20-outlook])
|
||||
//
|
||||
// ret := s.R.Values[len(s.R.Values)-i]
|
||||
// rdps = append(rdps, regression.DataPoint(ret, types.Float64Slice{s0, s1, s2, s4, s5, a2, a3, a18, a34}))
|
||||
//}
|
||||
r.Train(rdps...)
|
||||
r.Run()
|
||||
er, _ := r.Predict(types.Float64Slice{s.S0.Last(), s.S1.Last(), s.S2.Last(), s.S4.Last(), s.S5.Last(), s.S6.Last(), s.S7.Last(), s.A2.Last(), s.A3.Last(), s.A18.Last(), s.A34.Last()})
|
||||
log.Infof("Expected Return Rate: %f", er)
|
||||
|
||||
q := new(regression.Regression)
|
||||
q.SetObserved("Order Quantity Per Interval")
|
||||
q.SetVar(0, "S0")
|
||||
q.SetVar(1, "S1")
|
||||
q.SetVar(2, "S2")
|
||||
//q.SetVar(2, "S3")
|
||||
q.SetVar(3, "S4")
|
||||
q.SetVar(4, "S5")
|
||||
q.SetVar(5, "S6")
|
||||
q.SetVar(6, "S7")
|
||||
q.SetVar(7, "A2")
|
||||
q.SetVar(8, "A3")
|
||||
q.SetVar(9, "A18")
|
||||
q.SetVar(10, "A34")
|
||||
|
||||
var qdps regression.DataPoints
|
||||
|
||||
for i := 1; i <= 15; i++ {
|
||||
s0 := math.Pow(s.S0.Values[len(s.S0.Values)-i-outlook], 1)
|
||||
s1 := math.Pow(s.S1.Values[len(s.S1.Values)-i-outlook], 1)
|
||||
s2 := math.Pow(s.S2.Values[len(s.S2.Values)-i-outlook], 1)
|
||||
//s3 := s.S3.Values[len(s.S3.Values)-i-1]
|
||||
s4 := math.Pow(s.S4.Values[len(s.S4.Values)-i-outlook], 1)
|
||||
s5 := math.Pow(s.S5.Values[len(s.S5.Values)-i-outlook], 1)
|
||||
s6 := s.S6.Values[len(s.S6.Values)-i-outlook]
|
||||
s7 := s.S7.Values[len(s.S7.Values)-i-outlook]
|
||||
a2 := math.Pow(s.A2.Values[len(s.A2.Values)-i-outlook], 1)
|
||||
a3 := math.Pow(s.A3.Values[len(s.A3.Values)-i-outlook], 1)
|
||||
a18 := math.Pow(s.A18.Values[len(s.A18.Values)-i-outlook], 1)
|
||||
a34 := math.Pow(s.A34.Values[len(s.A34.Values)-i-outlook], 1)
|
||||
|
||||
ret := s.R.Values[len(s.R.Values)-i]
|
||||
qty := math.Abs(ret)
|
||||
qdps = append(qdps, regression.DataPoint(qty, types.Float64Slice{s0, s1, s2, s4, s5, s6, s7, a2, a3, a18, a34}))
|
||||
}
|
||||
//for i := 40; i > 20; i-- {
|
||||
// s0 := preprocessing(s.S0.Values[len(s.S0.Values)-i : len(s.S0.Values)-i+20-outlook])
|
||||
// s1 := preprocessing(s.S1.Values[len(s.S1.Values)-i : len(s.S1.Values)-i+20-outlook])
|
||||
// s2 := preprocessing(s.S2.Values[len(s.S2.Values)-i : len(s.S2.Values)-i+20-outlook])
|
||||
// //s3 := s.S3.Values[len(s.S3.Values)-i-1]
|
||||
// s4 := preprocessing(s.S4.Values[len(s.S4.Values)-i : len(s.S4.Values)-i+20-outlook])
|
||||
// s5 := preprocessing(s.S5.Values[len(s.S5.Values)-i : len(s.S5.Values)-i+20-outlook])
|
||||
// a2 := preprocessing(s.A2.Values[len(s.A2.Values)-i : len(s.A2.Values)-i+20-outlook])
|
||||
// a3 := preprocessing(s.A3.Values[len(s.A3.Values)-i : len(s.A3.Values)-i+20-outlook])
|
||||
// a18 := preprocessing(s.A18.Values[len(s.A18.Values)-i : len(s.A18.Values)-i+20-outlook])
|
||||
// a34 := preprocessing(s.A18.Values[len(s.A18.Values)-i : len(s.A18.Values)-i+20-outlook])
|
||||
//
|
||||
// ret := s.R.Values[len(s.R.Values)-i]
|
||||
// qty := math.Abs(ret)
|
||||
// qdps = append(qdps, regression.DataPoint(qty, types.Float64Slice{s0, s1, s2, s4, s5, a2, a3, a18, a34}))
|
||||
//}
|
||||
q.Train(qdps...)
|
||||
|
||||
q.Run()
|
||||
|
||||
log.Info(s.S0.Last(), s.S1.Last(), s.S2.Last(), s.S3.Last(), s.S4.Last(), s.S5.Last(), s.S6.Last(), s.S7.Last(), s.A2.Last(), s.A3.Last(), s.A18.Last(), s.A34.Last())
|
||||
|
||||
log.Infof("Return Rate Regression formula:\n%v", r.Formula)
|
||||
log.Infof("Order Quantity Regression formula:\n%v", q.Formula)
|
||||
|
||||
//s0 := preprocessing(s.S0.Values[len(s.S0.Values)-20 : len(s.S0.Values)-1])
|
||||
//s1 := preprocessing(s.S1.Values[len(s.S1.Values)-20 : len(s.S1.Values)-1-outlook])
|
||||
//s2 := preprocessing(s.S2.Values[len(s.S2.Values)-20 : len(s.S2.Values)-1-outlook])
|
||||
////s3 := s.S3.Values[len(s.S3.Values)-i-1]
|
||||
//s4 := preprocessing(s.S4.Values[len(s.S4.Values)-20 : len(s.S4.Values)-1-outlook])
|
||||
//s5 := preprocessing(s.S5.Values[len(s.S5.Values)-20 : len(s.S5.Values)-1-outlook])
|
||||
//a2 := preprocessing(s.A2.Values[len(s.A2.Values)-20 : len(s.A2.Values)-1-outlook])
|
||||
//a3 := preprocessing(s.A3.Values[len(s.A3.Values)-20 : len(s.A3.Values)-1-outlook])
|
||||
//a18 := preprocessing(s.A18.Values[len(s.A18.Values)-20 : len(s.A18.Values)-1-outlook])
|
||||
//a34 := preprocessing(s.A18.Values[len(s.A18.Values)-20 : len(s.A18.Values)-1-outlook])
|
||||
//er, _ := r.Predict(types.Float64Slice{s0, s1, s2, s4, s5, a2, a3, a18, a34})
|
||||
//eq, _ := q.Predict(types.Float64Slice{s0, s1, s2, s4, s5, a2, a3, a18, a34})
|
||||
eq, _ := q.Predict(types.Float64Slice{s.S0.Last(), s.S1.Last(), s.S2.Last(), s.S4.Last(), s.S5.Last(), s.S6.Last(), s.S7.Last(), s.A2.Last(), s.A3.Last(), s.A18.Last(), s.A34.Last(), er})
|
||||
log.Infof("Expected Order Quantity: %f", eq)
|
||||
//if float64(s.Position.GetBase().Sign())*er < 0 {
|
||||
// s.ClosePosition(ctx, fixedpoint.One, kline.Close)
|
||||
// s.tradeCollector.Process()
|
||||
//}
|
||||
//prevEr = er
|
||||
|
||||
//spd := s.Spread.Float64()
|
||||
|
||||
// inventory = m * alpha + spread
|
||||
AskAlphaBoundary := (s.Position.GetBase().Mul(kline.Close).Float64() - 100) / 10000
|
||||
BidAlphaBoundary := (s.Position.GetBase().Mul(kline.Close).Float64() + 100) / 10000
|
||||
|
||||
log.Info(s.Position.GetBase().Mul(kline.Close).Float64(), AskAlphaBoundary, er, BidAlphaBoundary)
|
||||
|
||||
BidPrice := kline.Close.Mul(fixedpoint.One.Sub(s.Spread))
|
||||
BidQty := s.QuantityOrAmount.CalculateQuantity(BidPrice)
|
||||
BidQty = BidQty //.Mul(fixedpoint.One.Add(fixedpoint.NewFromFloat(eq)))
|
||||
|
||||
AskPrice := kline.Close.Mul(fixedpoint.One.Add(s.Spread))
|
||||
AskQty := s.QuantityOrAmount.CalculateQuantity(AskPrice)
|
||||
AskQty = AskQty //.Mul(fixedpoint.One.Add(fixedpoint.NewFromFloat(eq)))
|
||||
|
||||
if er > 0 || (er < 0 && er > AskAlphaBoundary/kline.Close.Float64()) {
|
||||
submitOrder := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
Type: types.OrderTypeLimitMaker,
|
||||
Price: BidPrice,
|
||||
Quantity: BidQty, //0.0005
|
||||
}
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, submitOrder)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not place orders")
|
||||
}
|
||||
s.orderStore.Add(createdOrders...)
|
||||
s.activeMakerOrders.Add(createdOrders...)
|
||||
s.tradeCollector.Process()
|
||||
|
||||
//submitOrder = types.SubmitOrder{
|
||||
// Symbol: s.Symbol,
|
||||
// Side: types.SideTypeSell,
|
||||
// Type: types.OrderTypeLimitMaker,
|
||||
// Price: kline.Close.Mul(fixedpoint.One.Add(s.Spread)),
|
||||
// Quantity: fixedpoint.NewFromFloat(math.Max(math.Min(eq, 0.003), 0.0005)), //0.0005
|
||||
//}
|
||||
//createdOrders, err = orderExecutor.SubmitOrders(ctx, submitOrder)
|
||||
//if err != nil {
|
||||
// log.WithError(err).Errorf("can not place orders")
|
||||
//}
|
||||
//s.orderStore.Add(createdOrders...)
|
||||
//s.activeMakerOrders.Add(createdOrders...)
|
||||
//s.tradeCollector.Process()
|
||||
}
|
||||
if er < 0 || (er > 0 && er < BidAlphaBoundary/kline.Close.Float64()) {
|
||||
submitOrder := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeLimitMaker,
|
||||
Price: AskPrice,
|
||||
Quantity: AskQty, //0.0005
|
||||
}
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, submitOrder)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not place orders")
|
||||
}
|
||||
s.orderStore.Add(createdOrders...)
|
||||
s.activeMakerOrders.Add(createdOrders...)
|
||||
s.tradeCollector.Process()
|
||||
|
||||
//submitOrder = types.SubmitOrder{
|
||||
// Symbol: s.Symbol,
|
||||
// Side: types.SideTypeBuy,
|
||||
// Type: types.OrderTypeLimitMaker,
|
||||
// Price: kline.Close.Mul(fixedpoint.One.Sub(s.Spread)),
|
||||
// Quantity: fixedpoint.NewFromFloat(math.Max(math.Min(eq, 0.003), 0.0005)), //0.0005
|
||||
//}
|
||||
//createdOrders, err = orderExecutor.SubmitOrders(ctx, submitOrder)
|
||||
//if err != nil {
|
||||
// log.WithError(err).Errorf("can not place orders")
|
||||
//}
|
||||
//s.orderStore.Add(createdOrders...)
|
||||
//s.activeMakerOrders.Add(createdOrders...)
|
||||
//s.tradeCollector.Process()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tanh(x float64) float64 {
|
||||
y := (math.Exp(x) - math.Exp(-x)) / (math.Exp(x) + math.Exp(-x))
|
||||
return y
|
||||
}
|
||||
|
||||
func mean(xs []float64) float64 {
|
||||
return floats.Sum(xs) / float64(len(xs))
|
||||
}
|
||||
|
||||
func stddev(xs []float64) float64 {
|
||||
mu := mean(xs)
|
||||
squaresum := 0.
|
||||
for _, x := range xs {
|
||||
squaresum += (x - mu) * (x - mu)
|
||||
}
|
||||
return math.Sqrt(squaresum / float64(len(xs)-1))
|
||||
}
|
||||
|
||||
func preprocessing(xs []float64) float64 {
|
||||
//return 0.5 * tanh(0.01*((xs[len(xs)-1]-mean(xs))/stddev(xs))) // tanh estimator
|
||||
return tanh((xs[len(xs)-1] - mean(xs)) / stddev(xs)) // tanh z-score
|
||||
return (xs[len(xs)-1] - mean(xs)) / stddev(xs) // z-score
|
||||
}
|
Loading…
Reference in New Issue
Block a user