2020-11-02 14:14:01 +00:00
|
|
|
package bbgo
|
|
|
|
|
|
|
|
import (
|
2021-03-17 17:15:06 +00:00
|
|
|
"encoding/json"
|
|
|
|
|
2020-11-10 06:19:33 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2020-11-02 14:14:01 +00:00
|
|
|
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// LocalActiveOrderBook manages the local active order books.
|
2020-11-11 15:18:53 +00:00
|
|
|
//go:generate callbackgen -type LocalActiveOrderBook
|
2020-11-02 14:14:01 +00:00
|
|
|
type LocalActiveOrderBook struct {
|
2021-12-29 15:53:46 +00:00
|
|
|
Asks, Bids *types.SyncOrderMap
|
2020-11-11 15:18:53 +00:00
|
|
|
|
|
|
|
filledCallbacks []func(o types.Order)
|
2020-11-02 14:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewLocalActiveOrderBook() *LocalActiveOrderBook {
|
|
|
|
return &LocalActiveOrderBook{
|
|
|
|
Bids: types.NewSyncOrderMap(),
|
|
|
|
Asks: types.NewSyncOrderMap(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 17:15:06 +00:00
|
|
|
func (b *LocalActiveOrderBook) MarshalJSON() ([]byte, error) {
|
|
|
|
orders := b.Backup()
|
|
|
|
return json.Marshal(orders)
|
|
|
|
}
|
|
|
|
|
2021-03-15 10:25:36 +00:00
|
|
|
func (b *LocalActiveOrderBook) Backup() []types.SubmitOrder {
|
|
|
|
return append(b.Bids.Backup(), b.Asks.Backup()...)
|
|
|
|
}
|
|
|
|
|
2020-11-11 15:18:53 +00:00
|
|
|
func (b *LocalActiveOrderBook) BindStream(stream types.Stream) {
|
|
|
|
stream.OnOrderUpdate(b.orderUpdateHandler)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *LocalActiveOrderBook) orderUpdateHandler(order types.Order) {
|
2020-12-15 06:14:44 +00:00
|
|
|
log.Debugf("[LocalActiveOrderBook] received order update: %+v", order)
|
2020-11-11 15:18:53 +00:00
|
|
|
|
|
|
|
switch order.Status {
|
|
|
|
case types.OrderStatusFilled:
|
|
|
|
// make sure we have the order and we remove it
|
|
|
|
if b.Remove(order) {
|
|
|
|
b.EmitFilled(order)
|
|
|
|
}
|
|
|
|
|
2021-05-11 06:57:09 +00:00
|
|
|
case types.OrderStatusPartiallyFilled:
|
2020-11-11 15:18:53 +00:00
|
|
|
b.Update(order)
|
|
|
|
|
2021-05-11 06:57:09 +00:00
|
|
|
case types.OrderStatusNew:
|
|
|
|
if order.Quantity == 0 {
|
2021-12-30 14:02:50 +00:00
|
|
|
log.Debugf("[LocalActiveOrderBook] order status %s, removing order %s", order.Status, order)
|
2021-05-11 06:57:09 +00:00
|
|
|
b.Remove(order)
|
|
|
|
} else {
|
|
|
|
b.Update(order)
|
|
|
|
}
|
|
|
|
|
2020-11-11 15:18:53 +00:00
|
|
|
case types.OrderStatusCanceled, types.OrderStatusRejected:
|
2021-12-30 14:02:50 +00:00
|
|
|
log.Debugf("[LocalActiveOrderBook] order status %s, removing order %s", order.Status, order)
|
2020-11-11 15:18:53 +00:00
|
|
|
b.Remove(order)
|
2021-05-09 11:44:43 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
log.Warnf("unhandled order status: %s", order.Status)
|
2020-11-11 15:18:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 14:14:01 +00:00
|
|
|
func (b *LocalActiveOrderBook) Print() {
|
|
|
|
for _, o := range b.Bids.Orders() {
|
2021-05-09 12:03:16 +00:00
|
|
|
log.Infof("%s bid order: %d @ %f -> %s", o.Symbol, o.OrderID, o.Price, o.Status)
|
2020-11-02 14:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, o := range b.Asks.Orders() {
|
2021-05-09 12:03:16 +00:00
|
|
|
log.Infof("%s ask order: %d @ %f -> %s", o.Symbol, o.OrderID, o.Price, o.Status)
|
2020-11-02 14:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-07 07:07:06 +00:00
|
|
|
func (b *LocalActiveOrderBook) Update(orders ...types.Order) {
|
|
|
|
for _, order := range orders {
|
|
|
|
switch order.Side {
|
|
|
|
case types.SideTypeBuy:
|
|
|
|
b.Bids.Update(order)
|
|
|
|
|
|
|
|
case types.SideTypeSell:
|
|
|
|
b.Asks.Update(order)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 14:14:01 +00:00
|
|
|
func (b *LocalActiveOrderBook) Add(orders ...types.Order) {
|
|
|
|
for _, order := range orders {
|
|
|
|
switch order.Side {
|
|
|
|
case types.SideTypeBuy:
|
|
|
|
b.Bids.Add(order)
|
|
|
|
|
|
|
|
case types.SideTypeSell:
|
|
|
|
b.Asks.Add(order)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *LocalActiveOrderBook) NumOfBids() int {
|
|
|
|
return b.Bids.Len()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *LocalActiveOrderBook) NumOfAsks() int {
|
|
|
|
return b.Asks.Len()
|
|
|
|
}
|
|
|
|
|
2021-05-22 09:44:07 +00:00
|
|
|
func (b *LocalActiveOrderBook) Exists(order types.Order) bool {
|
|
|
|
|
|
|
|
switch order.Side {
|
|
|
|
|
|
|
|
case types.SideTypeBuy:
|
|
|
|
return b.Bids.Exists(order.OrderID)
|
|
|
|
|
|
|
|
case types.SideTypeSell:
|
|
|
|
return b.Asks.Exists(order.OrderID)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-11-11 15:18:53 +00:00
|
|
|
func (b *LocalActiveOrderBook) Remove(order types.Order) bool {
|
2020-11-02 14:14:01 +00:00
|
|
|
switch order.Side {
|
|
|
|
case types.SideTypeBuy:
|
2020-11-11 15:18:53 +00:00
|
|
|
return b.Bids.Remove(order.OrderID)
|
2020-11-02 14:14:01 +00:00
|
|
|
|
|
|
|
case types.SideTypeSell:
|
2020-11-11 15:18:53 +00:00
|
|
|
return b.Asks.Remove(order.OrderID)
|
2020-11-02 14:14:01 +00:00
|
|
|
|
|
|
|
}
|
2020-11-11 15:18:53 +00:00
|
|
|
|
|
|
|
return false
|
2020-11-02 14:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WriteOff writes off the filled order on the opposite side.
|
|
|
|
// This method does not write off order by order amount or order quantity.
|
|
|
|
func (b *LocalActiveOrderBook) WriteOff(order types.Order) bool {
|
|
|
|
if order.Status != types.OrderStatusFilled {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
switch order.Side {
|
|
|
|
case types.SideTypeSell:
|
|
|
|
// find the filled bid to remove
|
|
|
|
if filledOrder, ok := b.Bids.AnyFilled(); ok {
|
2020-11-11 15:18:53 +00:00
|
|
|
b.Bids.Remove(filledOrder.OrderID)
|
|
|
|
b.Asks.Remove(order.OrderID)
|
2020-11-02 14:14:01 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
case types.SideTypeBuy:
|
|
|
|
// find the filled ask order to remove
|
|
|
|
if filledOrder, ok := b.Asks.AnyFilled(); ok {
|
2020-11-11 15:18:53 +00:00
|
|
|
b.Asks.Remove(filledOrder.OrderID)
|
|
|
|
b.Bids.Remove(order.OrderID)
|
2020-11-02 14:14:01 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-05-13 11:41:05 +00:00
|
|
|
func (b *LocalActiveOrderBook) NumOfOrders() int {
|
|
|
|
return b.Asks.Len() + b.Bids.Len()
|
|
|
|
}
|
|
|
|
|
2020-11-02 14:14:01 +00:00
|
|
|
func (b *LocalActiveOrderBook) Orders() types.OrderSlice {
|
|
|
|
return append(b.Asks.Orders(), b.Bids.Orders()...)
|
|
|
|
}
|