2023-04-27 13:06:29 +00:00
|
|
|
package grid2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// For grid trading, there are twin orders between a grid
|
|
|
|
// e.g. 100, 200, 300, 400, 500
|
|
|
|
// BUY 100 and SELL 200 are a twin.
|
|
|
|
// BUY 200 and SELL 300 are a twin.
|
|
|
|
// Because they can't be placed on orderbook at the same time
|
|
|
|
|
|
|
|
type TwinOrder struct {
|
|
|
|
BuyOrder types.Order
|
|
|
|
SellOrder types.Order
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TwinOrder) IsValid() bool {
|
|
|
|
// XOR
|
|
|
|
return (t.BuyOrder.OrderID == 0) != (t.SellOrder.OrderID == 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TwinOrder) Exist() bool {
|
|
|
|
return t.BuyOrder.OrderID != 0 || t.SellOrder.OrderID != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TwinOrder) GetOrder() types.Order {
|
|
|
|
if t.BuyOrder.OrderID != 0 {
|
|
|
|
return t.BuyOrder
|
|
|
|
}
|
|
|
|
|
|
|
|
return t.SellOrder
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TwinOrder) SetOrder(order types.Order) {
|
|
|
|
if order.Side == types.SideTypeBuy {
|
|
|
|
t.BuyOrder = order
|
|
|
|
t.SellOrder = types.Order{}
|
|
|
|
} else {
|
|
|
|
t.SellOrder = order
|
|
|
|
t.BuyOrder = types.Order{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type TwinOrderMap map[fixedpoint.Value]TwinOrder
|
|
|
|
|
|
|
|
func findTwinOrderMapKey(grid *Grid, order types.Order) (fixedpoint.Value, error) {
|
|
|
|
if order.Side == types.SideTypeSell {
|
|
|
|
return order.Price, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if order.Side == types.SideTypeBuy {
|
|
|
|
pin, ok := grid.NextHigherPin(order.Price)
|
|
|
|
if !ok {
|
|
|
|
return fixedpoint.Zero, fmt.Errorf("there is no next higher price for this order (%d, price: %s)", order.OrderID, order.Price)
|
|
|
|
}
|
|
|
|
|
|
|
|
return fixedpoint.Value(pin), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return fixedpoint.Zero, fmt.Errorf("unsupported side: %s of this order (%d)", order.Side, order.OrderID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m TwinOrderMap) AscendingOrders() []types.Order {
|
|
|
|
var orders []types.Order
|
2023-04-28 08:02:22 +00:00
|
|
|
for _, twinOrder := range m {
|
2023-04-27 13:06:29 +00:00
|
|
|
// skip empty order
|
2023-04-28 08:02:22 +00:00
|
|
|
if !twinOrder.Exist() {
|
2023-04-27 13:06:29 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-04-28 08:02:22 +00:00
|
|
|
orders = append(orders, twinOrder.GetOrder())
|
2023-04-27 13:06:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
types.SortOrdersUpdateTimeAscending(orders)
|
|
|
|
|
|
|
|
return orders
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m TwinOrderMap) SyncOrderMap() *types.SyncOrderMap {
|
|
|
|
orderMap := types.NewSyncOrderMap()
|
|
|
|
for _, twin := range m {
|
|
|
|
orderMap.Add(twin.GetOrder())
|
|
|
|
}
|
|
|
|
|
|
|
|
return orderMap
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m TwinOrderMap) String() string {
|
|
|
|
var sb strings.Builder
|
|
|
|
var pins []fixedpoint.Value
|
|
|
|
for pin, _ := range m {
|
|
|
|
pins = append(pins, pin)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(pins, func(i, j int) bool {
|
2023-05-24 06:58:19 +00:00
|
|
|
return pins[j].Compare(pins[i]) < 0
|
2023-04-27 13:06:29 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
sb.WriteString("================== TWIN ORDER MAP ==================\n")
|
|
|
|
for _, pin := range pins {
|
|
|
|
twin := m[pin]
|
|
|
|
twinOrder := twin.GetOrder()
|
|
|
|
sb.WriteString(fmt.Sprintf("-> %8s) %s\n", pin, twinOrder.String()))
|
|
|
|
}
|
|
|
|
sb.WriteString("================== END OF PIN ORDER MAP ==================\n")
|
|
|
|
return sb.String()
|
|
|
|
}
|
2023-10-20 08:23:18 +00:00
|
|
|
|
|
|
|
type TwinOrderBook struct {
|
|
|
|
// sort in asc order
|
|
|
|
pins []fixedpoint.Value
|
|
|
|
|
|
|
|
// pin index, use to find the next or last pin in desc order
|
|
|
|
pinIdx map[fixedpoint.Value]int
|
|
|
|
|
|
|
|
// orderbook
|
|
|
|
m map[fixedpoint.Value]*TwinOrder
|
|
|
|
|
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func newTwinOrderBook(pins []Pin) *TwinOrderBook {
|
2023-10-20 08:23:18 +00:00
|
|
|
var v []fixedpoint.Value
|
|
|
|
for _, pin := range pins {
|
|
|
|
v = append(v, fixedpoint.Value(pin))
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort it in asc order
|
|
|
|
sort.Slice(v, func(i, j int) bool {
|
|
|
|
return v[j].Compare(v[i]) > 0
|
|
|
|
})
|
|
|
|
|
|
|
|
pinIdx := make(map[fixedpoint.Value]int)
|
|
|
|
m := make(map[fixedpoint.Value]*TwinOrder)
|
|
|
|
for i, pin := range v {
|
|
|
|
m[pin] = &TwinOrder{}
|
|
|
|
pinIdx[pin] = i
|
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
return &TwinOrderBook{
|
2023-10-20 08:23:18 +00:00
|
|
|
pins: v,
|
|
|
|
pinIdx: pinIdx,
|
|
|
|
m: m,
|
|
|
|
size: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) String() string {
|
2023-10-20 08:23:18 +00:00
|
|
|
var sb strings.Builder
|
|
|
|
|
|
|
|
sb.WriteString("================== TWIN ORDERBOOK ==================\n")
|
2023-10-23 05:00:17 +00:00
|
|
|
for _, pin := range b.pins {
|
|
|
|
twin := b.m[fixedpoint.Value(pin)]
|
2023-10-20 08:23:18 +00:00
|
|
|
twinOrder := twin.GetOrder()
|
|
|
|
sb.WriteString(fmt.Sprintf("-> %8s) %s\n", pin, twinOrder.String()))
|
|
|
|
}
|
|
|
|
sb.WriteString("================== END OF TWINORDERBOOK ==================\n")
|
|
|
|
return sb.String()
|
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) GetTwinOrderPin(order types.Order) (fixedpoint.Value, error) {
|
|
|
|
idx, exist := b.pinIdx[order.Price]
|
2023-10-20 08:23:18 +00:00
|
|
|
if !exist {
|
|
|
|
return fixedpoint.Zero, fmt.Errorf("the order's (%d) price (%s) is not in pins", order.OrderID, order.Price)
|
|
|
|
}
|
|
|
|
|
|
|
|
if order.Side == types.SideTypeBuy {
|
|
|
|
idx++
|
2023-10-23 05:00:17 +00:00
|
|
|
if idx >= len(b.pins) {
|
2023-10-20 08:23:18 +00:00
|
|
|
return fixedpoint.Zero, fmt.Errorf("this order's twin order price is not in pins, %+v", order)
|
|
|
|
}
|
|
|
|
} else if order.Side == types.SideTypeSell {
|
|
|
|
if idx == 0 {
|
|
|
|
return fixedpoint.Zero, fmt.Errorf("this order's twin order price is at zero index, %+v", order)
|
|
|
|
}
|
|
|
|
// do nothing
|
|
|
|
} else {
|
|
|
|
// should not happen
|
|
|
|
return fixedpoint.Zero, fmt.Errorf("the order's (%d) side (%s) is not supported", order.OrderID, order.Side)
|
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
return b.pins[idx], nil
|
2023-10-20 08:23:18 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) AddOrder(order types.Order) error {
|
|
|
|
pin, err := b.GetTwinOrderPin(order)
|
2023-10-20 08:23:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
twinOrder, exist := b.m[pin]
|
2023-10-20 08:23:18 +00:00
|
|
|
if !exist {
|
|
|
|
// should not happen
|
|
|
|
return fmt.Errorf("no any empty twin order at pins, should not happen, check it")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !twinOrder.Exist() {
|
2023-10-23 05:00:17 +00:00
|
|
|
b.size++
|
2023-10-20 08:23:18 +00:00
|
|
|
}
|
|
|
|
twinOrder.SetOrder(order)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) GetTwinOrder(pin fixedpoint.Value) *TwinOrder {
|
|
|
|
return b.m[pin]
|
2023-10-20 08:23:18 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) AddTwinOrder(pin fixedpoint.Value, order *TwinOrder) {
|
|
|
|
b.m[pin] = order
|
2023-10-20 08:23:18 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) Size() int {
|
|
|
|
return b.size
|
2023-10-20 08:23:18 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) EmptyTwinOrderSize() int {
|
|
|
|
return len(b.pins) - 1 - b.size
|
2023-10-20 08:23:18 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 05:00:17 +00:00
|
|
|
func (b *TwinOrderBook) SyncOrderMap() *types.SyncOrderMap {
|
2023-10-20 08:23:18 +00:00
|
|
|
orderMap := types.NewSyncOrderMap()
|
2023-10-23 05:00:17 +00:00
|
|
|
for _, twin := range b.m {
|
2023-10-20 08:23:18 +00:00
|
|
|
if twin.Exist() {
|
|
|
|
orderMap.Add(twin.GetOrder())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return orderMap
|
|
|
|
}
|