bbgo_origin/pkg/strategy/grid2/grid.go

193 lines
4.4 KiB
Go
Raw Normal View History

2022-11-03 05:41:47 +00:00
package grid2
import (
2022-12-03 06:58:53 +00:00
"fmt"
2022-11-03 16:26:28 +00:00
"math"
2022-11-06 02:12:50 +00:00
"sort"
2022-11-03 16:26:28 +00:00
2022-11-03 05:41:47 +00:00
"github.com/c9s/bbgo/pkg/fixedpoint"
)
2022-11-09 11:43:14 +00:00
type PinCalculator func() []Pin
2022-11-03 05:41:47 +00:00
type Grid struct {
UpperPrice fixedpoint.Value `json:"upperPrice"`
LowerPrice fixedpoint.Value `json:"lowerPrice"`
// Size is the number of total grids
Size fixedpoint.Value `json:"size"`
2022-11-07 05:51:44 +00:00
// TickSize is the price tick size, this is used for truncating price
TickSize fixedpoint.Value `json:"tickSize"`
// Spread is a immutable number
Spread fixedpoint.Value `json:"spread"`
2022-11-03 05:41:47 +00:00
// Pins are the pinned grid prices, from low to high
2022-11-03 16:26:28 +00:00
Pins []Pin `json:"pins"`
2022-11-03 05:41:47 +00:00
2022-11-03 16:26:28 +00:00
pinsCache map[Pin]struct{} `json:"-"`
2022-11-09 11:43:14 +00:00
calculator PinCalculator
2022-11-03 05:41:47 +00:00
}
2022-11-03 16:26:28 +00:00
type Pin fixedpoint.Value
2022-11-07 05:51:44 +00:00
func calculateArithmeticPins(lower, upper, spread, tickSize fixedpoint.Value) []Pin {
2022-11-03 16:26:28 +00:00
var pins []Pin
2022-11-25 10:07:02 +00:00
var ts = tickSize.Float64()
2022-11-03 16:26:28 +00:00
for p := lower; p.Compare(upper) <= 0; p = p.Add(spread) {
// tickSize here = 0.01
2022-11-25 10:07:02 +00:00
pp := p.Float64() / ts
pp = math.Trunc(pp) * ts
pin := Pin(fixedpoint.NewFromFloat(pp))
pins = append(pins, pin)
2022-11-03 16:26:28 +00:00
}
return pins
}
2022-11-03 05:41:47 +00:00
2022-11-03 16:26:28 +00:00
func buildPinCache(pins []Pin) map[Pin]struct{} {
cache := make(map[Pin]struct{}, len(pins))
for _, pin := range pins {
cache[pin] = struct{}{}
2022-11-03 05:41:47 +00:00
}
2022-11-03 16:26:28 +00:00
return cache
}
func NewGrid(lower, upper, size, tickSize fixedpoint.Value) *Grid {
height := upper.Sub(lower)
spread := height.Div(size)
2022-11-03 05:41:47 +00:00
grid := &Grid{
UpperPrice: upper,
LowerPrice: lower,
2022-11-03 16:26:28 +00:00
Size: size,
2022-11-07 05:51:44 +00:00
TickSize: tickSize,
Spread: spread,
2022-11-03 05:41:47 +00:00
}
2022-11-03 16:26:28 +00:00
2022-11-03 05:41:47 +00:00
return grid
}
2022-11-09 11:43:14 +00:00
func (g *Grid) CalculateGeometricPins() {
g.calculator = func() []Pin {
// TODO: implement geometric calculator
2022-11-09 11:43:14 +00:00
// return calculateArithmeticPins(g.LowerPrice, g.UpperPrice, g.Spread, g.TickSize)
return nil
}
g.addPins(g.calculator())
}
func (g *Grid) CalculateArithmeticPins() {
g.calculator = func() []Pin {
return calculateArithmeticPins(g.LowerPrice, g.UpperPrice, g.Spread, g.TickSize)
}
g.addPins(g.calculator())
2022-11-09 08:26:04 +00:00
}
2022-11-06 02:06:57 +00:00
func (g *Grid) Height() fixedpoint.Value {
return g.UpperPrice.Sub(g.LowerPrice)
}
2022-11-03 05:41:47 +00:00
func (g *Grid) Above(price fixedpoint.Value) bool {
2022-11-03 16:26:28 +00:00
return price.Compare(g.UpperPrice) > 0
2022-11-03 05:41:47 +00:00
}
func (g *Grid) Below(price fixedpoint.Value) bool {
2022-11-03 16:26:28 +00:00
return price.Compare(g.LowerPrice) < 0
2022-11-03 05:41:47 +00:00
}
func (g *Grid) OutOfRange(price fixedpoint.Value) bool {
2022-11-03 16:26:28 +00:00
return price.Compare(g.LowerPrice) < 0 || price.Compare(g.UpperPrice) > 0
2022-11-03 05:41:47 +00:00
}
2022-11-03 16:26:28 +00:00
func (g *Grid) HasPin(pin Pin) (ok bool) {
2022-11-03 05:41:47 +00:00
_, ok = g.pinsCache[pin]
return ok
}
// NextHigherPin finds the next higher pin
func (g *Grid) NextHigherPin(price fixedpoint.Value) (Pin, bool) {
i := g.SearchPin(price)
if i < len(g.Pins) && fixedpoint.Value(g.Pins[i]).Compare(price) == 0 && i+1 < len(g.Pins) {
return g.Pins[i+1], true
}
return Pin(fixedpoint.Zero), false
}
// NextLowerPin finds the next lower pin
func (g *Grid) NextLowerPin(price fixedpoint.Value) (Pin, bool) {
i := g.SearchPin(price)
if i < len(g.Pins) && fixedpoint.Value(g.Pins[i]).Compare(price) == 0 && i-1 >= 0 {
return g.Pins[i-1], true
}
return Pin(fixedpoint.Zero), false
}
func (g *Grid) SearchPin(price fixedpoint.Value) int {
i := sort.Search(len(g.Pins), func(i int) bool {
a := fixedpoint.Value(g.Pins[i])
return a.Compare(price) >= 0
})
return i
}
2022-11-03 16:26:28 +00:00
func (g *Grid) ExtendUpperPrice(upper fixedpoint.Value) (newPins []Pin) {
if upper.Compare(g.UpperPrice) <= 0 {
return nil
}
newPins = calculateArithmeticPins(g.UpperPrice.Add(g.Spread), upper, g.Spread, g.TickSize)
2022-11-06 02:14:07 +00:00
g.UpperPrice = upper
g.addPins(newPins)
2022-11-03 05:41:47 +00:00
return newPins
}
2022-11-03 16:26:28 +00:00
func (g *Grid) ExtendLowerPrice(lower fixedpoint.Value) (newPins []Pin) {
if lower.Compare(g.LowerPrice) >= 0 {
return nil
2022-11-06 02:12:50 +00:00
}
2022-11-06 02:06:57 +00:00
n := g.LowerPrice.Sub(lower).Div(g.Spread).Floor()
lower = g.LowerPrice.Sub(g.Spread.Mul(n))
newPins = calculateArithmeticPins(lower, g.LowerPrice.Sub(g.Spread), g.Spread, g.TickSize)
2022-11-06 02:13:13 +00:00
g.LowerPrice = lower
2022-11-06 02:12:50 +00:00
g.addPins(newPins)
return newPins
}
2022-11-03 05:41:47 +00:00
func (g *Grid) TopPin() Pin {
return g.Pins[len(g.Pins)-1]
}
func (g *Grid) BottomPin() Pin {
return g.Pins[0]
}
2022-11-06 02:12:50 +00:00
func (g *Grid) addPins(pins []Pin) {
g.Pins = append(g.Pins, pins...)
2022-11-03 05:41:47 +00:00
2022-11-06 02:12:50 +00:00
sort.Slice(g.Pins, func(i, j int) bool {
a := fixedpoint.Value(g.Pins[i])
b := fixedpoint.Value(g.Pins[j])
return a.Compare(b) < 0
})
2022-11-03 05:41:47 +00:00
g.updatePinsCache()
}
2022-11-07 05:51:44 +00:00
func (g *Grid) updatePinsCache() {
g.pinsCache = buildPinCache(g.Pins)
}
2022-12-03 06:58:53 +00:00
func (g *Grid) String() string {
return fmt.Sprintf("GRID: priceRange: %f <=> %f size: %f spread: %f", g.LowerPrice.Float64(), g.UpperPrice.Float64(), g.Size.Float64(), g.Spread.Float64())
2022-12-03 06:58:53 +00:00
}