mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
replace sliceorderbook with orderbook interface
This commit is contained in:
parent
cca37d309a
commit
8acada76a9
|
@ -308,7 +308,6 @@ import (
|
|||
_ "github.com/c9s/bbgo/pkg/strategy/buyandhold"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/flashcrash"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/grid"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/mirrormaker"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/support"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/swing"
|
||||
|
|
|
@ -32,7 +32,7 @@ func (store *MarketDataStore) SetKLineWindows(windows map[types.Interval]types.K
|
|||
store.KLineWindows = windows
|
||||
}
|
||||
|
||||
func (store *MarketDataStore) OrderBook() types.SliceOrderBook {
|
||||
func (store *MarketDataStore) OrderBook() types.OrderBook {
|
||||
return store.orderBook.Copy()
|
||||
}
|
||||
|
||||
|
|
|
@ -65,18 +65,7 @@ func (e *TwapExecution) connectUserData(ctx context.Context) {
|
|||
|
||||
func (e *TwapExecution) getSideBook() (pvs types.PriceVolumeSlice, err error) {
|
||||
book := e.orderBook.Copy()
|
||||
|
||||
switch e.Side {
|
||||
case types.SideTypeSell:
|
||||
pvs = book.Asks
|
||||
|
||||
case types.SideTypeBuy:
|
||||
pvs = book.Bids
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("invalid side type: %+v", e.Side)
|
||||
}
|
||||
|
||||
pvs = book.SideBook(e.Side)
|
||||
return pvs, err
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
_ "github.com/c9s/bbgo/pkg/strategy/flashcrash"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/gap"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/grid"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/mirrormaker"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/schedule"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/support"
|
||||
|
|
|
@ -1,310 +0,0 @@
|
|||
package mirrormaker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
const ID = "mirrormaker"
|
||||
|
||||
var defaultMargin = fixedpoint.NewFromFloat(0.01)
|
||||
|
||||
var defaultQuantity = fixedpoint.NewFromFloat(0.001)
|
||||
|
||||
var log = logrus.WithField("strategy", ID)
|
||||
|
||||
func init() {
|
||||
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||
}
|
||||
|
||||
type Strategy struct {
|
||||
*bbgo.Graceful
|
||||
*bbgo.Persistence
|
||||
|
||||
Symbol string `json:"symbol"`
|
||||
SourceExchange string `json:"sourceExchange"`
|
||||
MakerExchange string `json:"makerExchange"`
|
||||
|
||||
UpdateInterval time.Duration `json:"updateInterval"`
|
||||
Margin fixedpoint.Value `json:"margin"`
|
||||
BidMargin fixedpoint.Value `json:"bidMargin"`
|
||||
AskMargin fixedpoint.Value `json:"askMargin"`
|
||||
Quantity fixedpoint.Value `json:"quantity"`
|
||||
QuantityMultiplier fixedpoint.Value `json:"quantityMultiplier"`
|
||||
|
||||
NumLayers int `json:"numLayers"`
|
||||
Pips int `json:"pips"`
|
||||
|
||||
makerSession *bbgo.ExchangeSession
|
||||
sourceSession *bbgo.ExchangeSession
|
||||
|
||||
sourceMarket types.Market
|
||||
makerMarket types.Market
|
||||
|
||||
book *types.StreamOrderBook
|
||||
activeMakerOrders *bbgo.LocalActiveOrderBook
|
||||
|
||||
orderStore *bbgo.OrderStore
|
||||
|
||||
Position fixedpoint.Value
|
||||
lastPrice float64
|
||||
|
||||
stopC chan struct{}
|
||||
}
|
||||
|
||||
func (s *Strategy) ID() string {
|
||||
return ID
|
||||
}
|
||||
|
||||
func (s *Strategy) CrossSubscribe(sessions map[string]*bbgo.ExchangeSession) {
|
||||
sourceSession, ok := sessions[s.SourceExchange]
|
||||
if !ok {
|
||||
panic(fmt.Errorf("source exchange %s is not defined", s.SourceExchange))
|
||||
}
|
||||
|
||||
log.Infof("subscribing %s from %s", s.Symbol, s.SourceExchange)
|
||||
sourceSession.Subscribe(types.BookChannel, s.Symbol, types.SubscribeOptions{})
|
||||
}
|
||||
|
||||
func (s *Strategy) updateQuote(ctx context.Context) {
|
||||
if err := s.makerSession.Exchange.CancelOrders(ctx, s.activeMakerOrders.Orders()...); err != nil {
|
||||
log.WithError(err).Errorf("can not cancel orders")
|
||||
return
|
||||
}
|
||||
|
||||
// avoid unlock issue
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
sourceBook := s.book.Copy()
|
||||
if len(sourceBook.Bids) == 0 || len(sourceBook.Asks) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
bestBidPrice := sourceBook.Bids[0].Price
|
||||
bestAskPrice := sourceBook.Asks[0].Price
|
||||
log.Infof("best bid price %f, best ask price: %f", bestBidPrice.Float64(), bestAskPrice.Float64())
|
||||
|
||||
bidQuantity := s.Quantity
|
||||
bidPrice := bestBidPrice.MulFloat64(1.0 - s.BidMargin.Float64())
|
||||
|
||||
askQuantity := s.Quantity
|
||||
askPrice := bestAskPrice.MulFloat64(1.0 + s.AskMargin.Float64())
|
||||
|
||||
log.Infof("quote bid price: %f ask price: %f", bidPrice.Float64(), askPrice.Float64())
|
||||
|
||||
var submitOrders []types.SubmitOrder
|
||||
|
||||
balances := s.makerSession.Account.Balances()
|
||||
makerQuota := &bbgo.QuotaTransaction{}
|
||||
if b, ok := balances[s.makerMarket.BaseCurrency]; ok {
|
||||
makerQuota.BaseAsset.Add(b.Available)
|
||||
}
|
||||
if b, ok := balances[s.makerMarket.QuoteCurrency]; ok {
|
||||
makerQuota.QuoteAsset.Add(b.Available)
|
||||
}
|
||||
|
||||
hedgeBalances := s.sourceSession.Account.Balances()
|
||||
hedgeQuota := &bbgo.QuotaTransaction{}
|
||||
if b, ok := hedgeBalances[s.sourceMarket.BaseCurrency]; ok {
|
||||
hedgeQuota.BaseAsset.Add(b.Available)
|
||||
}
|
||||
if b, ok := hedgeBalances[s.sourceMarket.QuoteCurrency]; ok {
|
||||
hedgeQuota.QuoteAsset.Add(b.Available)
|
||||
}
|
||||
|
||||
log.Infof("maker quota: %+v", makerQuota)
|
||||
log.Infof("hedge quota: %+v", hedgeQuota)
|
||||
|
||||
for i := 0; i < s.NumLayers; i++ {
|
||||
// bid orders
|
||||
if makerQuota.QuoteAsset.Lock(bidQuantity.Mul(bidPrice)) && hedgeQuota.BaseAsset.Lock(bidQuantity) {
|
||||
// if we bought, then we need to sell the base from the hedge session
|
||||
submitOrders = append(submitOrders, types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Type: types.OrderTypeLimit,
|
||||
Side: types.SideTypeBuy,
|
||||
Price: bidPrice.Float64(),
|
||||
Quantity: bidQuantity.Float64(),
|
||||
TimeInForce: "GTC",
|
||||
})
|
||||
|
||||
makerQuota.Commit()
|
||||
hedgeQuota.Commit()
|
||||
} else {
|
||||
makerQuota.Rollback()
|
||||
hedgeQuota.Rollback()
|
||||
}
|
||||
|
||||
// ask orders
|
||||
if makerQuota.BaseAsset.Lock(askQuantity) && hedgeQuota.QuoteAsset.Lock(askQuantity.Mul(askPrice)) {
|
||||
// if we bought, then we need to sell the base from the hedge session
|
||||
submitOrders = append(submitOrders, types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Type: types.OrderTypeLimit,
|
||||
Side: types.SideTypeSell,
|
||||
Price: askPrice.Float64(),
|
||||
Quantity: askQuantity.Float64(),
|
||||
TimeInForce: "GTC",
|
||||
})
|
||||
makerQuota.Commit()
|
||||
hedgeQuota.Commit()
|
||||
} else {
|
||||
makerQuota.Rollback()
|
||||
hedgeQuota.Rollback()
|
||||
}
|
||||
|
||||
bidPrice -= fixedpoint.NewFromFloat(s.makerMarket.TickSize * float64(s.Pips))
|
||||
askPrice += fixedpoint.NewFromFloat(s.makerMarket.TickSize * float64(s.Pips))
|
||||
|
||||
askQuantity = askQuantity.Mul(s.QuantityMultiplier)
|
||||
bidQuantity = bidQuantity.Mul(s.QuantityMultiplier)
|
||||
}
|
||||
|
||||
if len(submitOrders) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
makerOrderExecutor := &bbgo.ExchangeOrderExecutor{Session: s.makerSession}
|
||||
makerOrders, err := makerOrderExecutor.SubmitOrders(ctx, submitOrders...)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("order submit error")
|
||||
return
|
||||
}
|
||||
|
||||
s.activeMakerOrders.Add(makerOrders...)
|
||||
s.orderStore.Add(makerOrders...)
|
||||
}
|
||||
|
||||
func (s *Strategy) handleTradeUpdate(trade types.Trade) {
|
||||
log.Infof("received trade %+v", trade)
|
||||
if s.orderStore.Exists(trade.OrderID) {
|
||||
log.Infof("identified trade %d with an existing order: %d", trade.ID, trade.OrderID)
|
||||
|
||||
q := fixedpoint.NewFromFloat(trade.Quantity)
|
||||
if trade.Side == types.SideTypeSell {
|
||||
q = -q
|
||||
}
|
||||
|
||||
s.Position.AtomicAdd(q)
|
||||
|
||||
pos := s.Position.AtomicLoad()
|
||||
log.Warnf("position changed: %f", pos.Float64())
|
||||
|
||||
s.lastPrice = trade.Price
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Strategy) CrossRun(ctx context.Context, _ bbgo.OrderExecutionRouter, sessions map[string]*bbgo.ExchangeSession) error {
|
||||
if s.UpdateInterval == 0 {
|
||||
s.UpdateInterval = time.Second
|
||||
}
|
||||
|
||||
if s.NumLayers == 0 {
|
||||
s.NumLayers = 1
|
||||
}
|
||||
|
||||
if s.BidMargin == 0 {
|
||||
if s.Margin != 0 {
|
||||
s.BidMargin = s.Margin
|
||||
} else {
|
||||
s.BidMargin = defaultMargin
|
||||
}
|
||||
}
|
||||
|
||||
if s.AskMargin == 0 {
|
||||
if s.Margin != 0 {
|
||||
s.AskMargin = s.Margin
|
||||
} else {
|
||||
s.AskMargin = defaultMargin
|
||||
}
|
||||
}
|
||||
|
||||
if s.Quantity == 0 {
|
||||
s.Quantity = defaultQuantity
|
||||
}
|
||||
|
||||
sourceSession, ok := sessions[s.SourceExchange]
|
||||
if !ok {
|
||||
return fmt.Errorf("source exchange session %s is not defined", s.SourceExchange)
|
||||
}
|
||||
|
||||
s.sourceSession = sourceSession
|
||||
|
||||
makerSession, ok := sessions[s.MakerExchange]
|
||||
if !ok {
|
||||
return fmt.Errorf("maker exchange session %s is not defined", s.MakerExchange)
|
||||
}
|
||||
|
||||
s.makerSession = makerSession
|
||||
|
||||
s.sourceMarket, ok = s.sourceSession.Market(s.Symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("source session market %s is not defined", s.Symbol)
|
||||
}
|
||||
|
||||
s.makerMarket, ok = s.makerSession.Market(s.Symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("maker session market %s is not defined", s.Symbol)
|
||||
}
|
||||
|
||||
s.book = types.NewStreamBook(s.Symbol)
|
||||
s.book.BindStream(s.sourceSession.Stream)
|
||||
|
||||
s.makerSession.Stream.OnTradeUpdate(s.handleTradeUpdate)
|
||||
|
||||
s.activeMakerOrders = bbgo.NewLocalActiveOrderBook()
|
||||
s.activeMakerOrders.BindStream(s.makerSession.Stream)
|
||||
|
||||
s.orderStore = bbgo.NewOrderStore(s.Symbol)
|
||||
s.orderStore.BindStream(s.makerSession.Stream)
|
||||
|
||||
s.stopC = make(chan struct{})
|
||||
|
||||
if err := s.Persistence.Load(&s.Position, "position"); err != nil {
|
||||
log.WithError(err).Warnf("can not load position")
|
||||
} else {
|
||||
log.Infof("position is loaded successfully, position=%f", s.Position.Float64())
|
||||
}
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(s.UpdateInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
|
||||
case <-s.stopC:
|
||||
return
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
case <-ticker.C:
|
||||
s.updateQuote(ctx)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
s.Graceful.OnShutdown(func(ctx context.Context, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
close(s.stopC)
|
||||
|
||||
if err := s.Persistence.Save(&s.Position, "position"); err != nil {
|
||||
log.WithError(err).Error("persistence save error")
|
||||
}
|
||||
|
||||
if err := s.makerSession.Exchange.CancelOrders(ctx, s.activeMakerOrders.Orders()...); err != nil {
|
||||
log.WithError(err).Errorf("can not cancel orders")
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
|
@ -185,10 +185,6 @@ func (s *Strategy) updateQuote(ctx context.Context, orderExecutionRouter bbgo.Or
|
|||
}
|
||||
|
||||
sourceBook := s.book.Copy()
|
||||
if len(sourceBook.Bids) == 0 || len(sourceBook.Asks) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if valid, err := sourceBook.IsValid(); !valid {
|
||||
log.WithError(err).Errorf("%s invalid order book, skip quoting: %v", s.Symbol, err)
|
||||
return
|
||||
|
@ -265,8 +261,11 @@ func (s *Strategy) updateQuote(ctx context.Context, orderExecutionRouter bbgo.Or
|
|||
return
|
||||
}
|
||||
|
||||
bestBidPrice := sourceBook.Bids[0].Price
|
||||
bestAskPrice := sourceBook.Asks[0].Price
|
||||
bestBid, _ := sourceBook.BestBid()
|
||||
bestBidPrice := bestBid.Price
|
||||
|
||||
bestAsk, _ := sourceBook.BestAsk()
|
||||
bestAskPrice := bestAsk.Price
|
||||
log.Infof("%s book ticker: best ask / best bid = %f / %f", s.Symbol, bestAskPrice.Float64(), bestBidPrice.Float64())
|
||||
|
||||
var submitOrders []types.SubmitOrder
|
||||
|
@ -335,7 +334,7 @@ func (s *Strategy) updateQuote(ctx context.Context, orderExecutionRouter bbgo.Or
|
|||
}
|
||||
|
||||
accumulativeBidQuantity += bidQuantity
|
||||
bidPrice := aggregatePrice(sourceBook.Bids, accumulativeBidQuantity)
|
||||
bidPrice := aggregatePrice(sourceBook.SideBook(types.SideTypeBuy), accumulativeBidQuantity)
|
||||
bidPrice = bidPrice.MulFloat64(1.0 - bidMargin.Float64())
|
||||
|
||||
if i > 0 && pips > 0 {
|
||||
|
@ -382,7 +381,7 @@ func (s *Strategy) updateQuote(ctx context.Context, orderExecutionRouter bbgo.Or
|
|||
}
|
||||
accumulativeAskQuantity += askQuantity
|
||||
|
||||
askPrice := aggregatePrice(sourceBook.Asks, accumulativeAskQuantity)
|
||||
askPrice := aggregatePrice(sourceBook.SideBook(types.SideTypeSell), accumulativeAskQuantity)
|
||||
askPrice = askPrice.MulFloat64(1.0 + askMargin.Float64())
|
||||
if i > 0 && pips > 0 {
|
||||
askPrice -= pips.MulFloat64(s.makerMarket.TickSize)
|
||||
|
@ -447,19 +446,14 @@ func (s *Strategy) Hedge(ctx context.Context, pos fixedpoint.Value) {
|
|||
switch side {
|
||||
|
||||
case types.SideTypeBuy:
|
||||
if len(sourceBook.Asks) > 0 {
|
||||
if pv, ok := sourceBook.Asks.First(); ok {
|
||||
lastPrice = pv.Price.Float64()
|
||||
}
|
||||
if bestAsk, ok := sourceBook.BestAsk(); ok {
|
||||
lastPrice = bestAsk.Price.Float64()
|
||||
}
|
||||
|
||||
case types.SideTypeSell:
|
||||
if len(sourceBook.Bids) > 0 {
|
||||
if pv, ok := sourceBook.Bids.First(); ok {
|
||||
lastPrice = pv.Price.Float64()
|
||||
}
|
||||
if bestBid, ok := sourceBook.BestBid(); ok {
|
||||
lastPrice = bestBid.Price.Float64()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
notional := quantity.MulFloat64(lastPrice)
|
||||
|
|
|
@ -110,7 +110,7 @@ func (s *Strategy) update(orderExecutor bbgo.OrderExecutor, session *bbgo.Exchan
|
|||
|
||||
func (s *Strategy) updateOrders(orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession, side types.SideType) {
|
||||
var book = s.book.Copy()
|
||||
var pvs = book.PriceVolumesBySide(side)
|
||||
var pvs = book.SideBook(side)
|
||||
if pvs == nil || len(pvs) == 0 {
|
||||
log.Warnf("empty side: %s", side)
|
||||
return
|
||||
|
|
|
@ -2,6 +2,8 @@ package types
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
|
@ -24,47 +26,59 @@ type OrderBook interface {
|
|||
Reset()
|
||||
Load(book SliceOrderBook)
|
||||
Update(book SliceOrderBook)
|
||||
Copy() OrderBook
|
||||
CopyDepth(depth int) OrderBook
|
||||
SideBook(sideType SideType) PriceVolumeSlice
|
||||
IsValid() (bool, error)
|
||||
}
|
||||
|
||||
type MutexOrderBook struct {
|
||||
sync.Mutex
|
||||
|
||||
*SliceOrderBook
|
||||
Symbol string
|
||||
OrderBook OrderBook
|
||||
}
|
||||
|
||||
func NewMutexOrderBook(symbol string) *MutexOrderBook {
|
||||
var book OrderBook = NewSliceOrderBook(symbol)
|
||||
|
||||
if v, _ := strconv.ParseBool(os.Getenv("ENABLE_RBT_ORDERBOOK")); v {
|
||||
book = NewRBOrderBook(symbol)
|
||||
}
|
||||
|
||||
return &MutexOrderBook{
|
||||
SliceOrderBook: &SliceOrderBook{Symbol: symbol},
|
||||
Symbol: symbol,
|
||||
OrderBook: book,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *MutexOrderBook) Load(book SliceOrderBook) {
|
||||
b.Lock()
|
||||
b.SliceOrderBook.Load(book)
|
||||
b.OrderBook.Load(book)
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *MutexOrderBook) Reset() {
|
||||
b.Lock()
|
||||
b.SliceOrderBook.Reset()
|
||||
b.OrderBook.Reset()
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *MutexOrderBook) CopyDepth(depth int) SliceOrderBook {
|
||||
func (b *MutexOrderBook) CopyDepth(depth int) OrderBook {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
return b.SliceOrderBook.CopyDepth(depth)
|
||||
return b.OrderBook.CopyDepth(depth)
|
||||
}
|
||||
|
||||
func (b *MutexOrderBook) Copy() SliceOrderBook {
|
||||
func (b *MutexOrderBook) Copy() OrderBook {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
return b.SliceOrderBook.Copy()
|
||||
return b.OrderBook.Copy()
|
||||
}
|
||||
|
||||
func (b *MutexOrderBook) Update(update SliceOrderBook) {
|
||||
b.Lock()
|
||||
b.SliceOrderBook.Update(update)
|
||||
b.OrderBook.Update(update)
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
|
@ -85,7 +99,7 @@ func NewStreamBook(symbol string) *StreamOrderBook {
|
|||
|
||||
func (sb *StreamOrderBook) BindStream(stream Stream) {
|
||||
stream.OnBookSnapshot(func(book SliceOrderBook) {
|
||||
if sb.Symbol != book.Symbol {
|
||||
if sb.MutexOrderBook.Symbol != book.Symbol {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -94,7 +108,7 @@ func (sb *StreamOrderBook) BindStream(stream Stream) {
|
|||
})
|
||||
|
||||
stream.OnBookUpdate(func(book SliceOrderBook) {
|
||||
if sb.Symbol != book.Symbol {
|
||||
if sb.MutexOrderBook.Symbol != book.Symbol {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ func BenchmarkOrderBook_Load(b *testing.B) {
|
|||
}
|
||||
})
|
||||
|
||||
b.Run("SliceOrderBook", func(b *testing.B) {
|
||||
b.Run("OrderBook", func(b *testing.B) {
|
||||
book := &SliceOrderBook{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, ask := range asks {
|
||||
|
@ -84,7 +84,7 @@ func BenchmarkOrderBook_UpdateAndInsert(b *testing.B) {
|
|||
sliceBook.Bids = sliceBook.Bids.Upsert(bid, true)
|
||||
}
|
||||
}
|
||||
b.Run("SliceOrderBook", func(b *testing.B) {
|
||||
b.Run("OrderBook", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var price = fixedpoint.NewFromFloat(rand.Float64() * 2000.0)
|
||||
if price >= fixedpoint.NewFromFloat(1000) {
|
||||
|
|
|
@ -121,14 +121,64 @@ func (b *RBTOrderBook) load(book SliceOrderBook) {
|
|||
b.updateAsks(book.Asks)
|
||||
}
|
||||
|
||||
func (b *RBTOrderBook) Print() {
|
||||
b.Bids.PostorderOf(b.Bids.Root, func(n *RBNode) bool {
|
||||
fmt.Printf("bid: %f x %f", n.Key.Float64(), n.Value.Float64())
|
||||
func (b *RBTOrderBook) Copy() OrderBook {
|
||||
var book = NewRBOrderBook(b.Symbol)
|
||||
book.Asks = b.Asks.Copy()
|
||||
book.Bids = b.Bids.Copy()
|
||||
return book
|
||||
}
|
||||
|
||||
func (b *RBTOrderBook) CopyDepth(depth int) OrderBook {
|
||||
var book = NewRBOrderBook(b.Symbol)
|
||||
book.Asks = b.Asks.CopyInorder(depth)
|
||||
book.Bids = b.Bids.CopyInorder(depth)
|
||||
return book
|
||||
}
|
||||
|
||||
func (b *RBTOrderBook) convertTreeToPriceVolumeSlice(tree *RBTree, descending bool) (pvs PriceVolumeSlice) {
|
||||
if descending {
|
||||
tree.InorderReverse(func(n *RBNode) bool {
|
||||
pvs = append(pvs, PriceVolume{
|
||||
Price: n.Key,
|
||||
Volume: n.Value,
|
||||
})
|
||||
return true
|
||||
})
|
||||
return pvs
|
||||
}
|
||||
|
||||
tree.Inorder(func(n *RBNode) bool {
|
||||
pvs = append(pvs, PriceVolume{
|
||||
Price: n.Key,
|
||||
Volume: n.Value,
|
||||
})
|
||||
return true
|
||||
})
|
||||
return pvs
|
||||
}
|
||||
|
||||
b.Asks.PostorderOf(b.Asks.Root, func(n *RBNode) bool {
|
||||
func (b *RBTOrderBook) SideBook(sideType SideType) PriceVolumeSlice {
|
||||
switch sideType {
|
||||
|
||||
case SideTypeBuy:
|
||||
return b.convertTreeToPriceVolumeSlice(b.Bids, false)
|
||||
|
||||
case SideTypeSell:
|
||||
return b.convertTreeToPriceVolumeSlice(b.Asks, true)
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *RBTOrderBook) Print() {
|
||||
b.Asks.Inorder(func(n *RBNode) bool {
|
||||
fmt.Printf("ask: %f x %f", n.Key.Float64(), n.Value.Float64())
|
||||
return true
|
||||
})
|
||||
|
||||
b.Bids.InorderReverse(func(n *RBNode) bool {
|
||||
fmt.Printf("bid: %f x %f", n.Key.Float64(), n.Value.Float64())
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
|
|
@ -20,6 +20,12 @@ type SliceOrderBook struct {
|
|||
updateCallbacks []func(book *SliceOrderBook)
|
||||
}
|
||||
|
||||
func NewSliceOrderBook(symbol string) *SliceOrderBook {
|
||||
return &SliceOrderBook{
|
||||
Symbol: symbol,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) Spread() (fixedpoint.Value, bool) {
|
||||
bestBid, ok := b.BestBid()
|
||||
if !ok {
|
||||
|
@ -50,6 +56,20 @@ func (b *SliceOrderBook) BestAsk() (PriceVolume, bool) {
|
|||
return b.Asks[0], true
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) SideBook(sideType SideType) PriceVolumeSlice {
|
||||
switch sideType {
|
||||
|
||||
case SideTypeBuy:
|
||||
return b.Bids
|
||||
|
||||
case SideTypeSell:
|
||||
return b.Asks
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) IsValid() (bool, error) {
|
||||
bid, hasBid := b.BestBid()
|
||||
ask, hasAsk := b.BestAsk()
|
||||
|
@ -82,20 +102,6 @@ func (b *SliceOrderBook) PriceVolumesBySide(side SideType) PriceVolumeSlice {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) CopyDepth(depth int) (book SliceOrderBook) {
|
||||
book = *b
|
||||
book.Bids = book.Bids.CopyDepth(depth)
|
||||
book.Asks = book.Asks.CopyDepth(depth)
|
||||
return book
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) Copy() (book SliceOrderBook) {
|
||||
book = *b
|
||||
book.Bids = book.Bids.Copy()
|
||||
book.Asks = book.Asks.Copy()
|
||||
return book
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) updateAsks(pvs PriceVolumeSlice) {
|
||||
for _, pv := range pvs {
|
||||
if pv.Volume == 0 {
|
||||
|
@ -172,3 +178,19 @@ func (b *SliceOrderBook) String() string {
|
|||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) CopyDepth(depth int) OrderBook {
|
||||
var book SliceOrderBook
|
||||
book = *b
|
||||
book.Bids = book.Bids.CopyDepth(depth)
|
||||
book.Asks = book.Asks.CopyDepth(depth)
|
||||
return &book
|
||||
}
|
||||
|
||||
func (b *SliceOrderBook) Copy() OrderBook {
|
||||
var book SliceOrderBook
|
||||
book = *b
|
||||
book.Bids = book.Bids.Copy()
|
||||
book.Asks = book.Asks.Copy()
|
||||
return &book
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user