Skip to content

Commit

Permalink
tri: fix tri bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
c9s committed Jan 26, 2024
1 parent ecc0c9c commit 67b500f
Showing 1 changed file with 122 additions and 45 deletions.
167 changes: 122 additions & 45 deletions pkg/strategy/tri/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/core"
"github.com/c9s/bbgo/pkg/exchange/retry"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/sigchan"
"github.com/c9s/bbgo/pkg/style"
Expand All @@ -31,7 +32,7 @@ var log = logrus.WithField("strategy", ID)

var one = fixedpoint.One
var marketOrderProtectiveRatio = fixedpoint.NewFromFloat(0.008)
var balanceBufferRatio = fixedpoint.NewFromFloat(0.005)
var balanceBufferRatio = fixedpoint.NewFromFloat(0.002)

func init() {
bbgo.RegisterStrategy(ID, &Strategy{})
Expand Down Expand Up @@ -104,6 +105,8 @@ type State struct {
}

type Strategy struct {
*bbgo.Environment

Symbols []string `json:"symbols"`
Paths [][]string `json:"paths"`
MinSpreadRatio fixedpoint.Value `json:"minSpreadRatio"`
Expand Down Expand Up @@ -168,12 +171,24 @@ func (s *Strategy) executeOrder(ctx context.Context, order types.SubmitOrder) *t
return nil
}

func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {

func (s *Strategy) Defaults() error {
if s.TradeState == nil {
s.TradeState = types.NewTradeStats("")
}

if len(s.Symbols) == 0 {
s.Symbols = collectSymbols(s.Paths)
}

return nil
}

func (s *Strategy) Initialize() error {
return nil
}

func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {

s.Symbols = compileSymbols(s.Symbols)

if s.MarketOrderProtectiveRatio.IsZero() {
Expand Down Expand Up @@ -278,46 +293,51 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.paths = append(s.paths, p)
}

go func() {
fs := []ratioFunction{calculateForwardRatio, calculateBackwardRate}
log.Infof("waiting for market prices ready...")
wait := true
for wait {
wait = false
for _, p := range s.paths {
if !p.Ready() {
wait = true
break
session.UserDataStream.OnAuth(func() {
go func() {
fs := []ratioFunction{calculateForwardRatio, calculateBackwardRate}
log.Infof("waiting for market prices ready...")
wait := true
for wait {
wait = false
for _, p := range s.paths {
if !p.Ready() {
wait = true
break
}
}
}
}

log.Infof("all markets ready")
log.Infof("all markets ready")

for {
select {
case <-ctx.Done():
return
case <-s.sigC:
minRatio := s.MinSpreadRatio.Float64()
for side, f := range fs {
ranks := s.calculateRanks(minRatio, f)
if len(ranks) == 0 {
break
}

forward := side == 0
bestRank := ranks[0]
if forward {
log.Infof("%d paths elected, found best forward path %s profit %.5f%%", len(ranks), bestRank.Path, (bestRank.Ratio-1.0)*100.0)
} else {
log.Infof("%d paths elected, found best backward path %s profit %.5f%%", len(ranks), bestRank.Path, (bestRank.Ratio-1.0)*100.0)
for {
select {
case <-ctx.Done():
return
case <-s.sigC:
minRatio := s.MinSpreadRatio.Float64()
for side, f := range fs {
ranks := s.calculateRanks(minRatio, f)
if len(ranks) == 0 {
break
}

forward := side == 0
bestRank := ranks[0]
if forward {
debug("%d paths elected, found best forward path %s profit %.5f%%", len(ranks), bestRank.Path, (bestRank.Ratio-1.0)*100.0)
} else {
debug("%d paths elected, found best backward path %s profit %.5f%%", len(ranks), bestRank.Path, (bestRank.Ratio-1.0)*100.0)
}

logMarketPath(bestRank.Path)

s.executePath(ctx, session, bestRank.Path, bestRank.Ratio, forward)
}
s.executePath(ctx, session, bestRank.Path, bestRank.Ratio, forward)
}
}
}
}()
}()
})

return nil
}
Expand All @@ -326,13 +346,16 @@ type ratioFunction func(p *Path) float64

func (s *Strategy) checkMinimalOrderQuantity(orders [3]types.SubmitOrder) error {
for _, order := range orders {
market := s.arbMarkets[order.Symbol]
if order.Quantity.Compare(market.market.MinQuantity) < 0 {
return fmt.Errorf("order quantity is too small: %f < %f", order.Quantity.Float64(), market.market.MinQuantity.Float64())
if order.Quantity.Compare(order.Market.MinQuantity) <= 0 {
return fmt.Errorf("%s order quantity is too small: %f < %f",
order.Symbol,
order.Quantity.Float64(), order.Market.MinQuantity.Float64())
}

if order.Quantity.Mul(order.Price).Compare(market.market.MinNotional) < 0 {
return fmt.Errorf("order min notional is too small: %f < %f", order.Quantity.Mul(order.Price).Float64(), market.market.MinNotional.Float64())
if order.Quantity.Mul(order.Price).Compare(order.Market.MinNotional) <= 0 {
return fmt.Errorf("%s order min notional is too small: %f < %f",
order.Symbol,
order.Quantity.Mul(order.Price).Float64(), order.Market.MinNotional.Float64())
}
}

Expand Down Expand Up @@ -365,7 +388,7 @@ func (s *Strategy) optimizeMarketQuantityPrecision() {
}

func (s *Strategy) applyBalanceMaxQuantity(balances types.BalanceMap) types.BalanceMap {
if s.Limits == nil {
if s.Limits == nil || len(s.Limits) == 0 {
return balances
}

Expand Down Expand Up @@ -529,19 +552,23 @@ func (s *Strategy) iocOrderExecution(ctx context.Context, session *bbgo.Exchange
return
} else if o != nil {
select {
case <-ctx.Done():
return
case iocOrderC <- *o:
default:
}
}
}()

go func() {
o, err := waitForOrderFilled(ctx, service, *iocOrder, 3*time.Second)
o, err := retry.QueryOrderUntilFilled(ctx, service, iocOrder.Symbol, iocOrder.OrderID)
if err != nil {
log.WithError(err).Errorf("ioc order restful wait error")
return
} else if o != nil {
select {
case <-ctx.Done():
return
case iocOrderC <- *o:
default:
}
Expand All @@ -567,6 +594,9 @@ func (s *Strategy) iocOrderExecution(ctx context.Context, session *bbgo.Exchange
orders[1].Quantity = orders[1].Quantity.Mul(filledRatio)
orders[2].Quantity = orders[2].Quantity.Mul(filledRatio)

orders[1].Quantity = orders[1].Market.TruncateQuantity(orders[1].Quantity)
orders[2].Quantity = orders[2].Market.TruncateQuantity(orders[2].Quantity)

if orders[1].Quantity.Compare(orders[1].Market.MinQuantity) <= 0 {
log.Warnf("order #2 quantity %f is less than min quantity %f, skip", orders[1].Quantity.Float64(), orders[1].Market.MinQuantity.Float64())
return nil, nil
Expand Down Expand Up @@ -760,7 +790,7 @@ func (s *Strategy) buildArbMarkets(session *bbgo.ExchangeSession, symbols []stri

book := types.NewStreamBook(symbol)
priceUpdater := func(_ types.SliceOrderBook) {
bestAsk, bestBid, _ := book.BestBidAndAsk()
bestBid, bestAsk, _ := book.BestBidAndAsk()
if bestAsk.Equals(m.bestAsk) && bestBid.Equals(m.bestBid) {
return
}
Expand Down Expand Up @@ -828,7 +858,9 @@ func (s *Strategy) calculateRanks(minRatio float64, method func(p *Path) float64
return ranks
}

func waitForOrderFilled(ctx context.Context, ex types.ExchangeOrderQueryService, order types.Order, timeout time.Duration) (*types.Order, error) {
func waitForOrderFilled(
ctx context.Context, ex types.ExchangeOrderQueryService, order types.Order, timeout time.Duration,
) (*types.Order, error) {
prof := util.StartTimeProfile("waitForOrderFilled")
defer prof.StopAndLog(log.Infof)

Expand Down Expand Up @@ -878,3 +910,48 @@ func tradeAveragePrice(trades []types.Trade, orderID uint64) fixedpoint.Value {

return totalAmount.Div(totalQuantity)
}

func displayBook(id string, market *ArbMarket) {
if !debugMode {
return
}

var s strings.Builder

s.WriteString(id + ") " + market.market.Symbol + "\n")
s.WriteString(fmt.Sprintf("bestAsk: %-12s x %s\n",
market.bestAsk.Price.String(),
market.bestAsk.Volume.String(),
))
s.WriteString(fmt.Sprintf("bestBid: %-12s x %s\n",
market.bestBid.Price.String(),
market.bestBid.Volume.String(),
))

debug(s.String())
}

func logMarketPath(path *Path) {
if !debugMode {
return
}

displayBook("A", path.marketA)
displayBook("B", path.marketB)
displayBook("C", path.marketC)
}

func collectSymbols(paths [][]string) (symbols []string) {
symbolMap := make(map[string]struct{})
for _, path := range paths {
for _, s := range path {
symbolMap[s] = struct{}{}
}
}

for s := range symbolMap {
symbols = append(symbols, s)
}

return symbols
}

0 comments on commit 67b500f

Please sign in to comment.