mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-24 15:55:14 +00:00
fix all the fixedpoint use other than strategy
This commit is contained in:
parent
b8bf2af14d
commit
d9450e823e
|
@ -16,7 +16,7 @@ import (
|
|||
// BINANCE uses 0.1% for both maker and taker
|
||||
// for BNB holders, it's 0.075% for both maker and taker
|
||||
// MAX uses 0.050% for maker and 0.15% for taker
|
||||
const DefaultFeeRate = 0.075 * 0.01
|
||||
var DefaultFeeRate = fixedpoint.NewFromFloat(0.075 * 0.01)
|
||||
|
||||
var orderID uint64 = 1
|
||||
var tradeID uint64 = 1
|
||||
|
@ -92,12 +92,12 @@ func (m *SimplePriceMatching) CancelOrder(o types.Order) (types.Order, error) {
|
|||
|
||||
switch o.Side {
|
||||
case types.SideTypeBuy:
|
||||
if err := m.Account.UnlockBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(o.Price*o.Quantity)); err != nil {
|
||||
if err := m.Account.UnlockBalance(m.Market.QuoteCurrency, o.Price.Mul(o.Quantity)); err != nil {
|
||||
return o, err
|
||||
}
|
||||
|
||||
case types.SideTypeSell:
|
||||
if err := m.Account.UnlockBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(o.Quantity)); err != nil {
|
||||
if err := m.Account.UnlockBalance(m.Market.BaseCurrency, o.Quantity); err != nil {
|
||||
return o, err
|
||||
}
|
||||
}
|
||||
|
@ -114,32 +114,32 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (closedOrders *typ
|
|||
|
||||
switch o.Type {
|
||||
case types.OrderTypeMarket:
|
||||
if m.LastPrice == 0 {
|
||||
if m.LastPrice.IsZero() {
|
||||
panic("unexpected: last price can not be zero")
|
||||
}
|
||||
|
||||
price = m.LastPrice.Float64()
|
||||
price = m.LastPrice
|
||||
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
||||
price = o.Price
|
||||
}
|
||||
|
||||
if o.Quantity < m.Market.MinQuantity {
|
||||
return nil, nil, fmt.Errorf("order quantity %f is less than minQuantity %f, order: %+v", o.Quantity, m.Market.MinQuantity, o)
|
||||
if o.Quantity.Compare(m.Market.MinQuantity) < 0 {
|
||||
return nil, nil, fmt.Errorf("order quantity %s is less than minQuantity %s, order: %+v", o.Quantity.String(), m.Market.MinQuantity.String(), o)
|
||||
}
|
||||
|
||||
quoteQuantity := o.Quantity * price
|
||||
if quoteQuantity < m.Market.MinNotional {
|
||||
return nil, nil, fmt.Errorf("order amount %f is less than minNotional %f, order: %+v", quoteQuantity, m.Market.MinNotional, o)
|
||||
quoteQuantity := o.Quantity.Mul(price)
|
||||
if quoteQuantity.Compare(m.Market.MinNotional) < 0 {
|
||||
return nil, nil, fmt.Errorf("order amount %s is less than minNotional %s, order: %+v", quoteQuantity.String(), m.Market.MinNotional.String(), o)
|
||||
}
|
||||
|
||||
switch o.Side {
|
||||
case types.SideTypeBuy:
|
||||
if err := m.Account.LockBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(quoteQuantity)); err != nil {
|
||||
if err := m.Account.LockBalance(m.Market.QuoteCurrency, quoteQuantity); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
case types.SideTypeSell:
|
||||
if err := m.Account.LockBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(o.Quantity)); err != nil {
|
||||
if err := m.Account.LockBalance(m.Market.BaseCurrency, o.Quantity); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
@ -190,13 +190,13 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
|
|||
var err error
|
||||
// execute trade, update account balances
|
||||
if trade.IsBuyer {
|
||||
err = m.Account.UseLockedBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(trade.Price*trade.Quantity))
|
||||
err = m.Account.UseLockedBalance(m.Market.QuoteCurrency, trade.Price.Mul(trade.Quantity))
|
||||
|
||||
m.Account.AddBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(trade.Quantity))
|
||||
m.Account.AddBalance(m.Market.BaseCurrency, trade.Quantity)
|
||||
} else {
|
||||
err = m.Account.UseLockedBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(trade.Quantity))
|
||||
err = m.Account.UseLockedBalance(m.Market.BaseCurrency, trade.Quantity)
|
||||
|
||||
m.Account.AddBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(trade.Quantity*trade.Price))
|
||||
m.Account.AddBalance(m.Market.QuoteCurrency, trade.Quantity.Mul(trade.Price))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -213,37 +213,37 @@ func (m *SimplePriceMatching) newTradeFromOrder(order types.Order, isMaker bool)
|
|||
// MAX uses 0.050% for maker and 0.15% for taker
|
||||
var feeRate = DefaultFeeRate
|
||||
if isMaker {
|
||||
if m.MakerFeeRate > 0 {
|
||||
feeRate = m.MakerFeeRate.Float64()
|
||||
if m.MakerFeeRate.Sign() > 0 {
|
||||
feeRate = m.MakerFeeRate
|
||||
}
|
||||
} else {
|
||||
if m.TakerFeeRate > 0 {
|
||||
feeRate = m.TakerFeeRate.Float64()
|
||||
if m.TakerFeeRate.Sign() > 0 {
|
||||
feeRate = m.TakerFeeRate
|
||||
}
|
||||
}
|
||||
|
||||
price := order.Price
|
||||
switch order.Type {
|
||||
case types.OrderTypeMarket, types.OrderTypeStopMarket:
|
||||
if m.LastPrice == 0 {
|
||||
if m.LastPrice.IsZero() {
|
||||
panic("unexpected: last price can not be zero")
|
||||
}
|
||||
|
||||
price = m.LastPrice.Float64()
|
||||
price = m.LastPrice
|
||||
|
||||
}
|
||||
|
||||
var fee float64
|
||||
var fee fixedpoint.Value
|
||||
var feeCurrency string
|
||||
|
||||
switch order.Side {
|
||||
|
||||
case types.SideTypeBuy:
|
||||
fee = order.Quantity * feeRate
|
||||
fee = order.Quantity.Mul(feeRate)
|
||||
feeCurrency = m.Market.BaseCurrency
|
||||
|
||||
case types.SideTypeSell:
|
||||
fee = order.Quantity * price * feeRate
|
||||
fee = order.Quantity.Mul(price).Mul(feeRate)
|
||||
feeCurrency = m.Market.QuoteCurrency
|
||||
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ func (m *SimplePriceMatching) newTradeFromOrder(order types.Order, isMaker bool)
|
|||
Exchange: "backtest",
|
||||
Price: price,
|
||||
Quantity: order.Quantity,
|
||||
QuoteQuantity: order.Quantity * price,
|
||||
QuoteQuantity: order.Quantity.Mul(price),
|
||||
Symbol: order.Symbol,
|
||||
Side: order.Side,
|
||||
IsBuyer: order.Side == types.SideTypeBuy,
|
||||
|
@ -267,7 +267,6 @@ func (m *SimplePriceMatching) newTradeFromOrder(order types.Order, isMaker bool)
|
|||
}
|
||||
|
||||
func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders []types.Order, trades []types.Trade) {
|
||||
var priceF = price.Float64()
|
||||
var askOrders []types.Order
|
||||
|
||||
for _, o := range m.askOrders {
|
||||
|
@ -275,7 +274,7 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
|
||||
case types.OrderTypeStopMarket:
|
||||
// should we trigger the order
|
||||
if priceF <= o.StopPrice {
|
||||
if price.Compare(o.StopPrice) <= 0 {
|
||||
// not triggering it, put it back
|
||||
askOrders = append(askOrders, o)
|
||||
break
|
||||
|
@ -283,7 +282,7 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
|
||||
o.Type = types.OrderTypeMarket
|
||||
o.ExecutedQuantity = o.Quantity
|
||||
o.Price = priceF
|
||||
o.Price = price
|
||||
o.Status = types.OrderStatusFilled
|
||||
closedOrders = append(closedOrders, o)
|
||||
|
||||
|
@ -296,7 +295,7 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
|
||||
case types.OrderTypeStopLimit:
|
||||
// should we trigger the order?
|
||||
if priceF <= o.StopPrice {
|
||||
if price.Compare(o.StopPrice) <= 0 {
|
||||
askOrders = append(askOrders, o)
|
||||
break
|
||||
}
|
||||
|
@ -304,7 +303,7 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
o.Type = types.OrderTypeLimit
|
||||
|
||||
// is it a taker order?
|
||||
if priceF >= o.Price {
|
||||
if price.Compare(o.Price) >= 0 {
|
||||
o.ExecutedQuantity = o.Quantity
|
||||
o.Status = types.OrderStatusFilled
|
||||
closedOrders = append(closedOrders, o)
|
||||
|
@ -321,7 +320,7 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
}
|
||||
|
||||
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
||||
if priceF >= o.Price {
|
||||
if price.Compare(o.Price) >= 0 {
|
||||
o.ExecutedQuantity = o.Quantity
|
||||
o.Status = types.OrderStatusFilled
|
||||
closedOrders = append(closedOrders, o)
|
||||
|
@ -349,14 +348,14 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
}
|
||||
|
||||
func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders []types.Order, trades []types.Trade) {
|
||||
var sellPrice = price.Float64()
|
||||
var sellPrice = price
|
||||
var bidOrders []types.Order
|
||||
for _, o := range m.bidOrders {
|
||||
switch o.Type {
|
||||
|
||||
case types.OrderTypeStopMarket:
|
||||
// should we trigger the order
|
||||
if sellPrice <= o.StopPrice {
|
||||
if sellPrice.Compare(o.StopPrice) <= 0 {
|
||||
o.ExecutedQuantity = o.Quantity
|
||||
o.Price = sellPrice
|
||||
o.Status = types.OrderStatusFilled
|
||||
|
@ -374,10 +373,10 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
|
|||
|
||||
case types.OrderTypeStopLimit:
|
||||
// should we trigger the order
|
||||
if sellPrice <= o.StopPrice {
|
||||
if sellPrice.Compare(o.StopPrice) <= 0 {
|
||||
o.Type = types.OrderTypeLimit
|
||||
|
||||
if sellPrice <= o.Price {
|
||||
if sellPrice.Compare(o.Price) <= 0 {
|
||||
o.ExecutedQuantity = o.Quantity
|
||||
o.Status = types.OrderStatusFilled
|
||||
closedOrders = append(closedOrders, o)
|
||||
|
@ -396,7 +395,7 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
|
|||
}
|
||||
|
||||
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
||||
if sellPrice <= o.Price {
|
||||
if sellPrice.Compare(o.Price) <= 0 {
|
||||
o.ExecutedQuantity = o.Quantity
|
||||
o.Status = types.OrderStatusFilled
|
||||
closedOrders = append(closedOrders, o)
|
||||
|
@ -428,31 +427,31 @@ func (m *SimplePriceMatching) processKLine(kline types.KLine) {
|
|||
|
||||
switch kline.Direction() {
|
||||
case types.DirectionDown:
|
||||
if kline.High >= kline.Open {
|
||||
m.BuyToPrice(fixedpoint.NewFromFloat(kline.High))
|
||||
if kline.High.Compare(kline.Open) >= 0 {
|
||||
m.BuyToPrice(kline.High)
|
||||
}
|
||||
|
||||
if kline.Low > kline.Close {
|
||||
m.SellToPrice(fixedpoint.NewFromFloat(kline.Low))
|
||||
m.BuyToPrice(fixedpoint.NewFromFloat(kline.Close))
|
||||
if kline.Low.Compare(kline.Close) > 0 {
|
||||
m.SellToPrice(kline.Low)
|
||||
m.BuyToPrice(kline.Close)
|
||||
} else {
|
||||
m.SellToPrice(fixedpoint.NewFromFloat(kline.Close))
|
||||
m.SellToPrice(kline.Close)
|
||||
}
|
||||
|
||||
case types.DirectionUp:
|
||||
if kline.Low <= kline.Open {
|
||||
m.SellToPrice(fixedpoint.NewFromFloat(kline.Low))
|
||||
if kline.Low.Compare(kline.Open) <= 0 {
|
||||
m.SellToPrice(kline.Low)
|
||||
}
|
||||
|
||||
if kline.High > kline.Close {
|
||||
m.BuyToPrice(fixedpoint.NewFromFloat(kline.High))
|
||||
m.SellToPrice(fixedpoint.NewFromFloat(kline.Close))
|
||||
if kline.High.Compare(kline.Close) > 0 {
|
||||
m.BuyToPrice(kline.High)
|
||||
m.SellToPrice(kline.Close)
|
||||
} else {
|
||||
m.BuyToPrice(fixedpoint.NewFromFloat(kline.Close))
|
||||
m.BuyToPrice(kline.Close)
|
||||
}
|
||||
default: // no trade up or down
|
||||
if m.LastPrice == 0 {
|
||||
m.BuyToPrice(fixedpoint.NewFromFloat(kline.Close))
|
||||
if m.LastPrice.IsZero() {
|
||||
m.BuyToPrice(kline.Close)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -464,7 +463,7 @@ func (m *SimplePriceMatching) newOrder(o types.SubmitOrder, orderID uint64) type
|
|||
SubmitOrder: o,
|
||||
Exchange: types.ExchangeBacktest,
|
||||
Status: types.OrderStatusNew,
|
||||
ExecutedQuantity: 0,
|
||||
ExecutedQuantity: fixedpoint.Zero,
|
||||
IsWorking: true,
|
||||
CreationTime: types.Time(m.CurrentTime),
|
||||
UpdateTime: types.Time(m.CurrentTime),
|
||||
|
|
|
@ -15,7 +15,7 @@ type PriceOrder struct {
|
|||
type PriceOrderSlice []PriceOrder
|
||||
|
||||
func (slice PriceOrderSlice) Len() int { return len(slice) }
|
||||
func (slice PriceOrderSlice) Less(i, j int) bool { return slice[i].Price < slice[j].Price }
|
||||
func (slice PriceOrderSlice) Less(i, j int) bool { return slice[i].Price.Compare(slice[j].Price) < 0 }
|
||||
func (slice PriceOrderSlice) Swap(i, j int) { slice[i], slice[j] = slice[j], slice[i] }
|
||||
|
||||
func (slice PriceOrderSlice) InsertAt(idx int, po PriceOrder) PriceOrderSlice {
|
||||
|
@ -47,9 +47,9 @@ func (slice PriceOrderSlice) First() (PriceOrder, bool) {
|
|||
func (slice PriceOrderSlice) Find(price fixedpoint.Value, descending bool) (pv PriceOrder, idx int) {
|
||||
idx = sort.Search(len(slice), func(i int) bool {
|
||||
if descending {
|
||||
return slice[i].Price <= price
|
||||
return slice[i].Price.Compare(price) <= 0
|
||||
}
|
||||
return slice[i].Price >= price
|
||||
return slice[i].Price.Compare(price) >= 0
|
||||
})
|
||||
|
||||
if idx >= len(slice) || slice[idx].Price != price {
|
||||
|
|
|
@ -463,7 +463,7 @@ func (environ *Environment) BindSync(userConfig *Config) {
|
|||
orderWriter := func(order types.Order) {
|
||||
switch order.Status {
|
||||
case types.OrderStatusFilled, types.OrderStatusCanceled:
|
||||
if order.ExecutedQuantity > 0.0 {
|
||||
if order.ExecutedQuantity.Sign() > 0 {
|
||||
if err := environ.OrderService.Insert(order); err != nil {
|
||||
log.WithError(err).Errorf("order insert error: %+v", order)
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/interact"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
type PositionCloser interface {
|
||||
ClosePosition(ctx context.Context, percentage float64) error
|
||||
ClosePosition(ctx context.Context, percentage fixedpoint.Value) error
|
||||
}
|
||||
|
||||
type PositionReader interface {
|
||||
|
@ -23,7 +24,7 @@ type PositionReader interface {
|
|||
type closePositionContext struct {
|
||||
signature string
|
||||
closer PositionCloser
|
||||
percentage float64
|
||||
percentage fixedpoint.Value
|
||||
}
|
||||
|
||||
type CoreInteraction struct {
|
||||
|
@ -75,7 +76,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
message := "Your balances\n"
|
||||
balances := session.Account.Balances()
|
||||
for _, balance := range balances {
|
||||
if balance.Total() == 0 {
|
||||
if balance.Total().IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -121,7 +122,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
reply.Send("Your current position:")
|
||||
reply.Send(position.PlainText())
|
||||
|
||||
if position.Base == 0 {
|
||||
if position.Base.IsZero() {
|
||||
reply.Message(fmt.Sprintf("Strategy %q has no opened position", signature))
|
||||
return fmt.Errorf("strategy %T has no opened position", strategy)
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
reply.Send("Your current position:")
|
||||
reply.Send(position.PlainText())
|
||||
|
||||
if position.Base == 0 {
|
||||
if position.Base.IsZero() {
|
||||
reply.Message("No opened position")
|
||||
if kc, ok := reply.(interact.KeyboardController) ; ok {
|
||||
kc.RemoveKeyboard()
|
||||
|
@ -190,7 +191,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
|
||||
return nil
|
||||
}).Next(func(percentageStr string, reply interact.Reply) error {
|
||||
percentage, err := parseFloatPercent(percentageStr, 64)
|
||||
percentage, err := fixedpoint.NewFromString(percentageStr)
|
||||
if err != nil {
|
||||
reply.Message(fmt.Sprintf("%q is not a valid percentage string", percentageStr))
|
||||
return err
|
||||
|
|
|
@ -3,7 +3,6 @@ package bbgo
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -122,8 +121,10 @@ func (c *BasicRiskController) ProcessOrders(session *ExchangeSession, orders ...
|
|||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
accumulativeQuoteAmount := 0.0
|
||||
accumulativeBaseSellQuantity := 0.0
|
||||
accumulativeQuoteAmount := fixedpoint.Zero
|
||||
accumulativeBaseSellQuantity := fixedpoint.Zero
|
||||
increaseFactor := fixedpoint.NewFromFloat(1.01)
|
||||
|
||||
for _, order := range orders {
|
||||
lastPrice, ok := session.LastPrice(order.Symbol)
|
||||
if !ok {
|
||||
|
@ -146,6 +147,7 @@ func (c *BasicRiskController) ProcessOrders(session *ExchangeSession, orders ...
|
|||
|
||||
switch order.Side {
|
||||
case types.SideTypeBuy:
|
||||
minAmount := market.MinAmount.Mul(increaseFactor)
|
||||
// Critical conditions for placing buy orders
|
||||
quoteBalance, ok := balances[market.QuoteCurrency]
|
||||
if !ok {
|
||||
|
@ -153,67 +155,70 @@ func (c *BasicRiskController) ProcessOrders(session *ExchangeSession, orders ...
|
|||
continue
|
||||
}
|
||||
|
||||
if quoteBalance.Available < c.MinQuoteBalance {
|
||||
if quoteBalance.Available.Compare(c.MinQuoteBalance) < 0 {
|
||||
addError(errors.Wrapf(ErrQuoteBalanceLevelTooLow, "can not place buy order, quote balance level is too low: %s < %s, order: %s",
|
||||
types.USD.FormatMoneyFloat64(quoteBalance.Available.Float64()),
|
||||
types.USD.FormatMoneyFloat64(c.MinQuoteBalance.Float64()), order.String()))
|
||||
types.USD.FormatMoney(quoteBalance.Available),
|
||||
types.USD.FormatMoney(c.MinQuoteBalance), order.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
// Increase the quantity if the amount is not enough,
|
||||
// this is the only increase op, later we will decrease the quantity if it meets the criteria
|
||||
quantity = AdjustFloatQuantityByMinAmount(quantity, price, market.MinAmount*1.01)
|
||||
quantity = AdjustFloatQuantityByMinAmount(quantity, price, minAmount)
|
||||
|
||||
if c.MaxOrderAmount > 0 {
|
||||
quantity = AdjustFloatQuantityByMaxAmount(quantity, price, c.MaxOrderAmount.Float64())
|
||||
if c.MaxOrderAmount.Sign() > 0 {
|
||||
quantity = AdjustFloatQuantityByMaxAmount(quantity, price, c.MaxOrderAmount)
|
||||
}
|
||||
|
||||
quoteAssetQuota := math.Max(0.0, quoteBalance.Available.Float64()-c.MinQuoteBalance.Float64())
|
||||
if quoteAssetQuota < market.MinAmount {
|
||||
quoteAssetQuota := fixedpoint.Max(
|
||||
fixedpoint.Zero, quoteBalance.Available.Sub(c.MinQuoteBalance))
|
||||
if quoteAssetQuota.Compare(market.MinAmount) < 0 {
|
||||
addError(
|
||||
errors.Wrapf(
|
||||
ErrInsufficientQuoteBalance,
|
||||
"can not place buy order, insufficient quote balance: quota %f < min amount %f, order: %s",
|
||||
quoteAssetQuota, market.MinAmount, order.String()))
|
||||
"can not place buy order, insufficient quote balance: quota %s < min amount %s, order: %s",
|
||||
quoteAssetQuota.String(), market.MinAmount.String(), order.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
quantity = AdjustFloatQuantityByMaxAmount(quantity, price, quoteAssetQuota)
|
||||
|
||||
// if MaxBaseAssetBalance is enabled, we should check the current base asset balance
|
||||
if baseBalance, hasBaseAsset := balances[market.BaseCurrency]; hasBaseAsset && c.MaxBaseAssetBalance > 0 {
|
||||
if baseBalance.Available > c.MaxBaseAssetBalance {
|
||||
if baseBalance, hasBaseAsset := balances[market.BaseCurrency]; hasBaseAsset && c.MaxBaseAssetBalance.Sign() > 0 {
|
||||
if baseBalance.Available.Compare(c.MaxBaseAssetBalance) > 0 {
|
||||
addError(
|
||||
errors.Wrapf(
|
||||
ErrAssetBalanceLevelTooHigh,
|
||||
"should not place buy order, asset balance level is too high: %f > %f, order: %s",
|
||||
baseBalance.Available.Float64(),
|
||||
c.MaxBaseAssetBalance.Float64(),
|
||||
"should not place buy order, asset balance level is too high: %s > %s, order: %s",
|
||||
baseBalance.Available.String(),
|
||||
c.MaxBaseAssetBalance.String(),
|
||||
order.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
baseAssetQuota := math.Max(0.0, c.MaxBaseAssetBalance.Float64()-baseBalance.Available.Float64())
|
||||
if quantity > baseAssetQuota {
|
||||
baseAssetQuota := fixedpoint.Max(fixedpoint.Zero, c.MaxBaseAssetBalance.Sub(baseBalance.Available))
|
||||
if quantity.Compare(baseAssetQuota) > 0 {
|
||||
quantity = baseAssetQuota
|
||||
}
|
||||
}
|
||||
|
||||
// if the amount is still too small, we should skip it.
|
||||
notional := quantity * lastPrice
|
||||
if notional < market.MinAmount {
|
||||
notional := quantity.Mul(lastPrice)
|
||||
if notional.Compare(market.MinAmount) < 0 {
|
||||
addError(
|
||||
fmt.Errorf(
|
||||
"can not place buy order, quote amount too small: notional %f < min amount %f, order: %s",
|
||||
notional,
|
||||
market.MinAmount,
|
||||
"can not place buy order, quote amount too small: notional %s < min amount %s, order: %s",
|
||||
notional.String(),
|
||||
market.MinAmount.String(),
|
||||
order.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
accumulativeQuoteAmount += notional
|
||||
accumulativeQuoteAmount = accumulativeQuoteAmount.Add(notional)
|
||||
|
||||
case types.SideTypeSell:
|
||||
minNotion := market.MinNotional.Mul(increaseFactor)
|
||||
|
||||
// Critical conditions for placing SELL orders
|
||||
baseAssetBalance, ok := balances[market.BaseCurrency]
|
||||
if !ok {
|
||||
|
@ -226,58 +231,58 @@ func (c *BasicRiskController) ProcessOrders(session *ExchangeSession, orders ...
|
|||
}
|
||||
|
||||
// if the amount is too small, we should increase it.
|
||||
quantity = AdjustFloatQuantityByMinAmount(quantity, price, market.MinNotional*1.01)
|
||||
quantity = AdjustFloatQuantityByMinAmount(quantity, price, minNotion)
|
||||
|
||||
// we should not SELL too much
|
||||
quantity = math.Min(quantity, baseAssetBalance.Available.Float64())
|
||||
quantity = fixedpoint.Min(quantity, baseAssetBalance.Available)
|
||||
|
||||
if c.MinBaseAssetBalance > 0 {
|
||||
if baseAssetBalance.Available < c.MinBaseAssetBalance {
|
||||
if c.MinBaseAssetBalance.Sign() > 0 {
|
||||
if baseAssetBalance.Available.Compare(c.MinBaseAssetBalance) < 0 {
|
||||
addError(
|
||||
errors.Wrapf(
|
||||
ErrAssetBalanceLevelTooLow,
|
||||
"asset balance level is too low: %f > %f", baseAssetBalance.Available.Float64(), c.MinBaseAssetBalance.Float64()))
|
||||
"asset balance level is too low: %s > %s", baseAssetBalance.Available.String(), c.MinBaseAssetBalance.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
quantity = math.Min(quantity, baseAssetBalance.Available.Float64()-c.MinBaseAssetBalance.Float64())
|
||||
if quantity < market.MinQuantity {
|
||||
quantity = fixedpoint.Min(quantity, baseAssetBalance.Available.Sub(c.MinBaseAssetBalance))
|
||||
if quantity.Compare(market.MinQuantity) < 0 {
|
||||
addError(
|
||||
errors.Wrapf(
|
||||
ErrInsufficientAssetBalance,
|
||||
"insufficient asset balance: %f > minimal quantity %f",
|
||||
baseAssetBalance.Available.Float64(),
|
||||
market.MinQuantity))
|
||||
"insufficient asset balance: %s > minimal quantity %s",
|
||||
baseAssetBalance.Available.String(),
|
||||
market.MinQuantity.String()))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if c.MaxOrderAmount > 0 {
|
||||
quantity = AdjustFloatQuantityByMaxAmount(quantity, price, c.MaxOrderAmount.Float64())
|
||||
if c.MaxOrderAmount.Sign() > 0 {
|
||||
quantity = AdjustFloatQuantityByMaxAmount(quantity, price, c.MaxOrderAmount)
|
||||
}
|
||||
|
||||
notional := quantity * lastPrice
|
||||
if notional < market.MinNotional {
|
||||
notional := quantity.Mul(lastPrice)
|
||||
if notional.Compare(market.MinNotional) < 0 {
|
||||
addError(
|
||||
fmt.Errorf(
|
||||
"can not place sell order, notional %f < min notional: %f, order: %s",
|
||||
notional,
|
||||
market.MinNotional,
|
||||
"can not place sell order, notional %s < min notional: %s, order: %s",
|
||||
notional.String(),
|
||||
market.MinNotional.String(),
|
||||
order.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
if quantity < market.MinQuantity {
|
||||
if quantity.Compare(market.MinQuantity) < 0 {
|
||||
addError(
|
||||
fmt.Errorf(
|
||||
"can not place sell order, quantity %f is less than the minimal lot %f, order: %s",
|
||||
quantity,
|
||||
market.MinQuantity,
|
||||
"can not place sell order, quantity %s is less than the minimal lot %s, order: %s",
|
||||
quantity.String(),
|
||||
market.MinQuantity.String(),
|
||||
order.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
accumulativeBaseSellQuantity += quantity
|
||||
accumulativeBaseSellQuantity = accumulativeBaseSellQuantity.Add(quantity)
|
||||
}
|
||||
|
||||
// update quantity and format the order
|
||||
|
|
|
@ -18,7 +18,7 @@ var (
|
|||
func AdjustQuantityByMaxAmount(quantity, currentPrice, maxAmount fixedpoint.Value) fixedpoint.Value {
|
||||
// modify quantity for the min amount
|
||||
amount := currentPrice.Mul(quantity)
|
||||
if amount < maxAmount {
|
||||
if amount.Compare(maxAmount) < 0 {
|
||||
return quantity
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ func AdjustQuantityByMaxAmount(quantity, currentPrice, maxAmount fixedpoint.Valu
|
|||
func AdjustQuantityByMinAmount(quantity, currentPrice, minAmount fixedpoint.Value) fixedpoint.Value {
|
||||
// modify quantity for the min amount
|
||||
amount := currentPrice.Mul(quantity)
|
||||
if amount < minAmount {
|
||||
if amount.Compare(minAmount) < 0 {
|
||||
ratio := minAmount.Div(amount)
|
||||
quantity = quantity.Mul(ratio)
|
||||
}
|
||||
|
@ -39,22 +39,22 @@ func AdjustQuantityByMinAmount(quantity, currentPrice, minAmount fixedpoint.Valu
|
|||
}
|
||||
|
||||
// AdjustFloatQuantityByMinAmount adjusts the quantity to make the amount greater than the given minAmount
|
||||
func AdjustFloatQuantityByMinAmount(quantity, currentPrice, minAmount float64) float64 {
|
||||
func AdjustFloatQuantityByMinAmount(quantity, currentPrice, minAmount fixedpoint.Value) fixedpoint.Value {
|
||||
// modify quantity for the min amount
|
||||
amount := currentPrice * quantity
|
||||
if amount < minAmount {
|
||||
ratio := minAmount / amount
|
||||
quantity *= ratio
|
||||
amount := currentPrice.Mul(quantity)
|
||||
if amount.Compare(minAmount) < 0 {
|
||||
ratio := minAmount.Div(amount)
|
||||
return quantity.Mul(ratio)
|
||||
}
|
||||
|
||||
return quantity
|
||||
}
|
||||
|
||||
func AdjustFloatQuantityByMaxAmount(quantity float64, price float64, maxAmount float64) float64 {
|
||||
amount := price * quantity
|
||||
if amount > maxAmount {
|
||||
ratio := maxAmount / amount
|
||||
quantity *= ratio
|
||||
func AdjustFloatQuantityByMaxAmount(quantity fixedpoint.Value, price fixedpoint.Value, maxAmount fixedpoint.Value) fixedpoint.Value {
|
||||
amount := price.Mul(quantity)
|
||||
if amount.Compare(maxAmount) > 0 {
|
||||
ratio := maxAmount.Div(amount)
|
||||
return quantity.Mul(ratio)
|
||||
}
|
||||
|
||||
return quantity
|
||||
|
|
|
@ -140,7 +140,7 @@ func (s *OrderStore) handleOrderUpdate(order types.Order) {
|
|||
case types.OrderStatusCanceled:
|
||||
if s.RemoveCancelled {
|
||||
s.Remove(order)
|
||||
} else if order.ExecutedQuantity == 0.0 {
|
||||
} else if order.ExecutedQuantity.IsZero() {
|
||||
s.Remove(order)
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ func (p *Profit) SlackAttachment() slack.Attachment {
|
|||
|
||||
var fields []slack.AttachmentField
|
||||
|
||||
if p.NetProfit != 0 {
|
||||
if !p.NetProfit.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Net Profit",
|
||||
Value: pnlSignString(p.NetProfit) + " " + p.QuoteCurrency,
|
||||
|
@ -56,7 +56,7 @@ func (p *Profit) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if p.ProfitMargin != 0 {
|
||||
if !p.ProfitMargin.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Profit Margin",
|
||||
Value: p.ProfitMargin.Percentage(),
|
||||
|
@ -64,7 +64,7 @@ func (p *Profit) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if p.NetProfitMargin != 0 {
|
||||
if !p.NetProfitMargin.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Net Profit Margin",
|
||||
Value: p.NetProfitMargin.Percentage(),
|
||||
|
@ -72,7 +72,7 @@ func (p *Profit) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if p.TradeAmount != 0.0 {
|
||||
if !p.TradeAmount.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Trade Amount",
|
||||
Value: p.TradeAmount.String() + " " + p.QuoteCurrency,
|
||||
|
@ -80,7 +80,7 @@ func (p *Profit) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if p.FeeInUSD != 0 {
|
||||
if !p.FeeInUSD.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Fee In USD",
|
||||
Value: p.FeeInUSD.String() + " USD",
|
||||
|
@ -106,19 +106,19 @@ func (p *Profit) SlackAttachment() slack.Attachment {
|
|||
|
||||
func (p *Profit) PlainText() string {
|
||||
var emoji string
|
||||
if p.ProfitMargin != 0 {
|
||||
if !p.ProfitMargin.IsZero() {
|
||||
emoji = pnlEmojiMargin(p.Profit, p.ProfitMargin, defaultPnlLevelResolution)
|
||||
} else {
|
||||
emoji = pnlEmojiSimple(p.Profit)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s trade profit %s %f %s (%.2f%%), net profit =~ %f %s (%.2f%%)",
|
||||
return fmt.Sprintf("%s trade profit %s %s %s (%s), net profit =~ %s %s (%s)",
|
||||
p.Symbol,
|
||||
emoji,
|
||||
p.Profit.Float64(), p.QuoteCurrency,
|
||||
p.ProfitMargin.Float64()*100.0,
|
||||
p.NetProfit.Float64(), p.QuoteCurrency,
|
||||
p.NetProfitMargin.Float64()*100.0,
|
||||
p.Profit.String(), p.QuoteCurrency,
|
||||
p.ProfitMargin.Percentage(),
|
||||
p.NetProfit.String(), p.QuoteCurrency,
|
||||
p.NetProfitMargin.Percentage(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -127,25 +127,25 @@ var profitEmoji = "💰"
|
|||
var defaultPnlLevelResolution = fixedpoint.NewFromFloat(0.001)
|
||||
|
||||
func pnlColor(pnl fixedpoint.Value) string {
|
||||
if pnl > 0 {
|
||||
if pnl.Sign() > 0 {
|
||||
return types.GreenColor
|
||||
}
|
||||
return types.RedColor
|
||||
}
|
||||
|
||||
func pnlSignString(pnl fixedpoint.Value) string {
|
||||
if pnl > 0 {
|
||||
if pnl.Sign() > 0 {
|
||||
return "+" + pnl.String()
|
||||
}
|
||||
return pnl.String()
|
||||
}
|
||||
|
||||
func pnlEmojiSimple(pnl fixedpoint.Value) string {
|
||||
if pnl < 0 {
|
||||
if pnl.Sign() < 0 {
|
||||
return lossEmoji
|
||||
}
|
||||
|
||||
if pnl == 0 {
|
||||
if pnl.IsZero() {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -153,26 +153,26 @@ func pnlEmojiSimple(pnl fixedpoint.Value) string {
|
|||
}
|
||||
|
||||
func pnlEmojiMargin(pnl, margin, resolution fixedpoint.Value) (out string) {
|
||||
if margin == 0 {
|
||||
if margin.IsZero() {
|
||||
return pnlEmojiSimple(pnl)
|
||||
}
|
||||
|
||||
if pnl < 0 {
|
||||
if pnl.Sign() < 0 {
|
||||
out = lossEmoji
|
||||
level := (-margin).Div(resolution).Floor()
|
||||
for i := 1; i < level.Int(); i++ {
|
||||
level := (margin.Neg()).Div(resolution).Int()
|
||||
for i := 1; i < level; i++ {
|
||||
out += lossEmoji
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
if pnl == 0 {
|
||||
if pnl.IsZero() {
|
||||
return out
|
||||
}
|
||||
|
||||
out = profitEmoji
|
||||
level := margin.Div(resolution).Floor()
|
||||
for i := 1; i < level.Int(); i++ {
|
||||
level := margin.Div(resolution).Int()
|
||||
for i := 1; i < level; i++ {
|
||||
out += profitEmoji
|
||||
}
|
||||
return out
|
||||
|
@ -207,17 +207,17 @@ func (s *ProfitStats) Init(market types.Market) {
|
|||
}
|
||||
|
||||
func (s *ProfitStats) AddProfit(profit Profit) {
|
||||
s.AccumulatedPnL += profit.Profit
|
||||
s.AccumulatedNetProfit += profit.NetProfit
|
||||
s.TodayPnL += profit.Profit
|
||||
s.TodayNetProfit += profit.NetProfit
|
||||
s.AccumulatedPnL = s.AccumulatedPnL.Add(profit.Profit)
|
||||
s.AccumulatedNetProfit = s.AccumulatedNetProfit.Add(profit.NetProfit)
|
||||
s.TodayPnL = s.TodayPnL.Add(profit.Profit)
|
||||
s.TodayNetProfit = s.TodayNetProfit.Add(profit.NetProfit)
|
||||
|
||||
if profit.Profit < 0 {
|
||||
s.AccumulatedLoss += profit.Profit
|
||||
s.TodayLoss += profit.Profit
|
||||
} else if profit.Profit > 0 {
|
||||
s.AccumulatedProfit += profit.Profit
|
||||
s.TodayProfit += profit.Profit
|
||||
if profit.Profit.Sign() < 0 {
|
||||
s.AccumulatedLoss = s.AccumulatedLoss.Add(profit.Profit)
|
||||
s.TodayLoss = s.TodayLoss.Add(profit.Profit)
|
||||
} else if profit.Profit.Sign() > 0 {
|
||||
s.AccumulatedProfit = s.AccumulatedLoss.Add(profit.Profit)
|
||||
s.TodayProfit = s.TodayProfit.Add(profit.Profit)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ func (s *ProfitStats) AddTrade(trade types.Trade) {
|
|||
s.ResetToday()
|
||||
}
|
||||
|
||||
s.AccumulatedVolume += fixedpoint.NewFromFloat(trade.Quantity)
|
||||
s.AccumulatedVolume = s.AccumulatedVolume.Add(trade.Quantity)
|
||||
}
|
||||
|
||||
func (s *ProfitStats) IsOver24Hours() bool {
|
||||
|
@ -234,10 +234,10 @@ func (s *ProfitStats) IsOver24Hours() bool {
|
|||
}
|
||||
|
||||
func (s *ProfitStats) ResetToday() {
|
||||
s.TodayPnL = 0
|
||||
s.TodayNetProfit = 0
|
||||
s.TodayProfit = 0
|
||||
s.TodayLoss = 0
|
||||
s.TodayPnL = fixedpoint.Zero
|
||||
s.TodayNetProfit = fixedpoint.Zero
|
||||
s.TodayProfit = fixedpoint.Zero
|
||||
s.TodayLoss = fixedpoint.Zero
|
||||
|
||||
var beginningOfTheDay = util.BeginningOfTheDay(time.Now().Local())
|
||||
s.TodaySince = beginningOfTheDay.Unix()
|
||||
|
@ -246,21 +246,21 @@ func (s *ProfitStats) ResetToday() {
|
|||
func (s *ProfitStats) PlainText() string {
|
||||
since := time.Unix(s.AccumulatedSince, 0).Local()
|
||||
return fmt.Sprintf("%s Profit Today\n"+
|
||||
"Profit %f %s\n"+
|
||||
"Net profit %f %s\n"+
|
||||
"Trade Loss %f %s\n"+
|
||||
"Profit %s %s\n"+
|
||||
"Net profit %s %s\n"+
|
||||
"Trade Loss %s %s\n"+
|
||||
"Summary:\n"+
|
||||
"Accumulated Profit %f %s\n"+
|
||||
"Accumulated Net Profit %f %s\n"+
|
||||
"Accumulated Trade Loss %f %s\n"+
|
||||
"Accumulated Profit %s %s\n"+
|
||||
"Accumulated Net Profit %s %s\n"+
|
||||
"Accumulated Trade Loss %s %s\n"+
|
||||
"Since %s",
|
||||
s.Symbol,
|
||||
s.TodayPnL.Float64(), s.QuoteCurrency,
|
||||
s.TodayNetProfit.Float64(), s.QuoteCurrency,
|
||||
s.TodayLoss.Float64(), s.QuoteCurrency,
|
||||
s.AccumulatedPnL.Float64(), s.QuoteCurrency,
|
||||
s.AccumulatedNetProfit.Float64(), s.QuoteCurrency,
|
||||
s.AccumulatedLoss.Float64(), s.QuoteCurrency,
|
||||
s.TodayPnL.String(), s.QuoteCurrency,
|
||||
s.TodayNetProfit.String(), s.QuoteCurrency,
|
||||
s.TodayLoss.String(), s.QuoteCurrency,
|
||||
s.AccumulatedPnL.String(), s.QuoteCurrency,
|
||||
s.AccumulatedNetProfit.String(), s.QuoteCurrency,
|
||||
s.AccumulatedLoss.String(), s.QuoteCurrency,
|
||||
since.Format(time.RFC822),
|
||||
)
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ func (s *ProfitStats) SlackAttachment() slack.Attachment {
|
|||
|
||||
var fields []slack.AttachmentField
|
||||
|
||||
if s.TodayPnL != 0 {
|
||||
if !s.TodayPnL.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "P&L Today",
|
||||
Value: pnlSignString(s.TodayPnL) + " " + s.QuoteCurrency,
|
||||
|
@ -282,7 +282,7 @@ func (s *ProfitStats) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if s.TodayProfit != 0 {
|
||||
if !s.TodayProfit.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Profit Today",
|
||||
Value: pnlSignString(s.TodayProfit) + " " + s.QuoteCurrency,
|
||||
|
@ -290,7 +290,7 @@ func (s *ProfitStats) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if s.TodayNetProfit != 0 {
|
||||
if !s.TodayNetProfit.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Net Profit Today",
|
||||
Value: pnlSignString(s.TodayNetProfit) + " " + s.QuoteCurrency,
|
||||
|
@ -298,7 +298,7 @@ func (s *ProfitStats) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if s.TodayLoss != 0 {
|
||||
if !s.TodayLoss.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Loss Today",
|
||||
Value: pnlSignString(s.TodayLoss) + " " + s.QuoteCurrency,
|
||||
|
@ -306,28 +306,28 @@ func (s *ProfitStats) SlackAttachment() slack.Attachment {
|
|||
})
|
||||
}
|
||||
|
||||
if s.AccumulatedPnL != 0 {
|
||||
if !s.AccumulatedPnL.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Accumulated P&L",
|
||||
Value: pnlSignString(s.AccumulatedPnL) + " " + s.QuoteCurrency,
|
||||
})
|
||||
}
|
||||
|
||||
if s.AccumulatedProfit != 0 {
|
||||
if !s.AccumulatedProfit.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Accumulated Profit",
|
||||
Value: pnlSignString(s.AccumulatedProfit) + " " + s.QuoteCurrency,
|
||||
})
|
||||
}
|
||||
|
||||
if s.AccumulatedNetProfit != 0 {
|
||||
if !s.AccumulatedNetProfit.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Accumulated Net Profit",
|
||||
Value: pnlSignString(s.AccumulatedNetProfit) + " " + s.QuoteCurrency,
|
||||
})
|
||||
}
|
||||
|
||||
if s.AccumulatedLoss != 0 {
|
||||
if !s.AccumulatedLoss.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Accumulated Loss",
|
||||
Value: pnlSignString(s.AccumulatedLoss) + " " + s.QuoteCurrency,
|
||||
|
|
|
@ -18,11 +18,11 @@ type QuantityOrAmount struct {
|
|||
}
|
||||
|
||||
func (qa *QuantityOrAmount) IsSet() bool {
|
||||
return qa.Quantity > 0 || qa.Amount > 0
|
||||
return qa.Quantity.Sign() > 0 || qa.Amount.Sign() > 0
|
||||
}
|
||||
|
||||
func (qa *QuantityOrAmount) Validate() error {
|
||||
if qa.Quantity == 0 && qa.Amount == 0 {
|
||||
if qa.Quantity.IsZero() && qa.Amount.IsZero() {
|
||||
return errors.New("either quantity or amount can not be empty")
|
||||
}
|
||||
return nil
|
||||
|
@ -31,7 +31,7 @@ func (qa *QuantityOrAmount) Validate() error {
|
|||
// CalculateQuantity calculates the equivalent quantity of the given price when amount is set
|
||||
// it returns the quantity if the quantity is set
|
||||
func (qa *QuantityOrAmount) CalculateQuantity(currentPrice fixedpoint.Value) fixedpoint.Value {
|
||||
if qa.Amount > 0 {
|
||||
if qa.Amount.Sign() > 0 {
|
||||
quantity := qa.Amount.Div(currentPrice)
|
||||
return quantity
|
||||
}
|
||||
|
|
|
@ -14,18 +14,18 @@ type Quota struct {
|
|||
|
||||
func (q *Quota) Add(fund fixedpoint.Value) {
|
||||
q.mu.Lock()
|
||||
q.Available += fund
|
||||
q.Available = q.Available.Add(fund)
|
||||
q.mu.Unlock()
|
||||
}
|
||||
|
||||
func (q *Quota) Lock(fund fixedpoint.Value) bool {
|
||||
if fund > q.Available {
|
||||
if fund.Compare(q.Available) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
q.mu.Lock()
|
||||
q.Available -= fund
|
||||
q.Locked += fund
|
||||
q.Available = q.Available.Sub(fund)
|
||||
q.Locked = q.Locked.Add(fund)
|
||||
q.mu.Unlock()
|
||||
|
||||
return true
|
||||
|
@ -33,14 +33,14 @@ func (q *Quota) Lock(fund fixedpoint.Value) bool {
|
|||
|
||||
func (q *Quota) Commit() {
|
||||
q.mu.Lock()
|
||||
q.Locked = 0
|
||||
q.Locked = fixedpoint.Zero
|
||||
q.mu.Unlock()
|
||||
}
|
||||
|
||||
func (q *Quota) Rollback() {
|
||||
q.mu.Lock()
|
||||
q.Available += q.Locked
|
||||
q.Locked = 0
|
||||
q.Available = q.Available.Add(q.Locked)
|
||||
q.Locked = fixedpoint.Zero
|
||||
q.mu.Unlock()
|
||||
}
|
||||
|
||||
|
|
|
@ -93,10 +93,10 @@ func NewStandardIndicatorSet(symbol string, store *MarketDataStore) *StandardInd
|
|||
|
||||
// BOLL returns the bollinger band indicator of the given interval and the window,
|
||||
// Please note that the K for std dev is fixed and defaults to 2.0
|
||||
func (set *StandardIndicatorSet) BOLL(iw types.IntervalWindow, bandWidth float64) *indicator.BOLL {
|
||||
func (set *StandardIndicatorSet) BOLL(iw types.IntervalWindow, bandWidth fixedpoint.Value) *indicator.BOLL {
|
||||
inc, ok := set.boll[iw]
|
||||
if !ok {
|
||||
inc = &indicator.BOLL{IntervalWindow: iw, K: bandWidth}
|
||||
inc = &indicator.BOLL{IntervalWindow: iw, K: bandWidth.Float64()}
|
||||
inc.Bind(set.store)
|
||||
set.boll[iw] = inc
|
||||
}
|
||||
|
@ -545,17 +545,17 @@ func (session *ExchangeSession) OrderBook(symbol string) (s *types.StreamOrderBo
|
|||
return s, ok
|
||||
}
|
||||
|
||||
func (session *ExchangeSession) StartPrice(symbol string) (price float64, ok bool) {
|
||||
func (session *ExchangeSession) StartPrice(symbol string) (price fixedpoint.Value, ok bool) {
|
||||
price, ok = session.startPrices[symbol]
|
||||
return price, ok
|
||||
}
|
||||
|
||||
func (session *ExchangeSession) LastPrice(symbol string) (price float64, ok bool) {
|
||||
func (session *ExchangeSession) LastPrice(symbol string) (price fixedpoint.Value, ok bool) {
|
||||
price, ok = session.lastPrices[symbol]
|
||||
return price, ok
|
||||
}
|
||||
|
||||
func (session *ExchangeSession) LastPrices() map[string]float64 {
|
||||
func (session *ExchangeSession) LastPrices() map[string]fixedpoint.Value {
|
||||
return session.lastPrices
|
||||
}
|
||||
|
||||
|
@ -644,7 +644,7 @@ func (session *ExchangeSession) FindPossibleSymbols() (symbols []string, err err
|
|||
var fiatAssets []string
|
||||
|
||||
for _, currency := range types.FiatCurrencies {
|
||||
if balance, ok := balances[currency]; ok && balance.Total() > 0 {
|
||||
if balance, ok := balances[currency]; ok && balance.Total().Sign() > 0 {
|
||||
fiatAssets = append(fiatAssets, currency)
|
||||
}
|
||||
}
|
||||
|
@ -659,7 +659,7 @@ func (session *ExchangeSession) FindPossibleSymbols() (symbols []string, err err
|
|||
|
||||
// ignore the asset that we don't have in the balance sheet
|
||||
balance, hasAsset := balances[market.BaseCurrency]
|
||||
if !hasAsset || balance.Total() == 0 {
|
||||
if !hasAsset || balance.Total().IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -740,8 +740,8 @@ func (session *ExchangeSession) InitExchange(name string, exchange types.Exchang
|
|||
|
||||
session.orderBooks = make(map[string]*types.StreamOrderBook)
|
||||
session.markets = make(map[string]types.Market)
|
||||
session.lastPrices = make(map[string]float64)
|
||||
session.startPrices = make(map[string]float64)
|
||||
session.lastPrices = make(map[string]fixedpoint.Value)
|
||||
session.startPrices = make(map[string]fixedpoint.Value)
|
||||
session.marketDataStores = make(map[string]*MarketDataStore)
|
||||
session.positions = make(map[string]*types.Position)
|
||||
session.standardIndicatorSets = make(map[string]*StandardIndicatorSet)
|
||||
|
@ -812,7 +812,7 @@ func (session *ExchangeSession) metricsTradeUpdater(trade types.Trade) {
|
|||
"symbol": trade.Symbol,
|
||||
"liquidity": trade.Liquidity(),
|
||||
}
|
||||
metricsTradingVolume.With(labels).Add(trade.Quantity * trade.Price)
|
||||
metricsTradingVolume.With(labels).Add(trade.Quantity.Mul(trade.Price).Float64())
|
||||
metricsTradesTotal.With(labels).Inc()
|
||||
metricsLastUpdateTimeBalance.With(prometheus.Labels{
|
||||
"exchange": session.ExchangeName.String(),
|
||||
|
|
|
@ -3,7 +3,6 @@ package bbgo
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
|
@ -37,7 +36,7 @@ type TrailingStopController struct {
|
|||
Symbol string
|
||||
|
||||
position *types.Position
|
||||
latestHigh float64
|
||||
latestHigh fixedpoint.Value
|
||||
averageCost fixedpoint.Value
|
||||
|
||||
// activated: when the price reaches the min profit price, we set the activated to true to enable trailing stop
|
||||
|
@ -74,25 +73,25 @@ func (c *TrailingStopController) Run(ctx context.Context, session *ExchangeSessi
|
|||
}
|
||||
|
||||
// if average cost is zero, we don't need trailing stop
|
||||
if c.averageCost == 0 || c.position == nil {
|
||||
if c.averageCost.IsZero() || c.position == nil {
|
||||
return
|
||||
}
|
||||
|
||||
closePrice := kline.Close
|
||||
|
||||
// if we don't hold position, we just skip dust position
|
||||
if c.position.Base.Abs().Float64() < c.position.Market.MinQuantity || c.position.Base.Abs().Float64()*closePrice < c.position.Market.MinNotional {
|
||||
if c.position.Base.Abs().Compare(c.position.Market.MinQuantity) < 0 || c.position.Base.Abs().Mul(closePrice).Compare(c.position.Market.MinNotional) < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if c.MinProfit <= 0 {
|
||||
if c.MinProfit.Sign() <= 0 {
|
||||
// when minProfit is not set, we should always activate the trailing stop order
|
||||
c.activated = true
|
||||
} else if closePrice > c.averageCost.Float64() ||
|
||||
changeRate(closePrice, c.averageCost.Float64()) > c.MinProfit.Float64() {
|
||||
} else if closePrice.Compare(c.averageCost) > 0 ||
|
||||
changeRate(closePrice, c.averageCost).Compare(c.MinProfit) > 0 {
|
||||
|
||||
if !c.activated {
|
||||
log.Infof("%s trailing stop activated at price %f", c.Symbol, closePrice)
|
||||
log.Infof("%s trailing stop activated at price %s", c.Symbol, closePrice.String())
|
||||
c.activated = true
|
||||
}
|
||||
} else {
|
||||
|
@ -105,37 +104,37 @@ func (c *TrailingStopController) Run(ctx context.Context, session *ExchangeSessi
|
|||
|
||||
// if the trailing stop order is activated, we should update the latest high
|
||||
// update the latest high
|
||||
c.latestHigh = math.Max(closePrice, c.latestHigh)
|
||||
c.latestHigh = fixedpoint.Max(closePrice, c.latestHigh)
|
||||
|
||||
// if it's in the callback rate, we don't want to trigger stop
|
||||
if closePrice < c.latestHigh && changeRate(closePrice, c.latestHigh) < c.CallbackRate.Float64() {
|
||||
if closePrice.Compare(c.latestHigh) < 0 && changeRate(closePrice, c.latestHigh).Compare(c.CallbackRate) < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Virtual {
|
||||
// if the profit rate is defined, and it is less than our minimum profit rate, we skip stop
|
||||
if c.MinProfit > 0 &&
|
||||
(closePrice < c.averageCost.Float64() ||
|
||||
changeRate(closePrice, c.averageCost.Float64()) < c.MinProfit.Float64()) {
|
||||
if c.MinProfit.Sign() > 0 &&
|
||||
closePrice.Compare(c.averageCost) < 0 ||
|
||||
changeRate(closePrice, c.averageCost).Compare(c.MinProfit) < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("%s trailing stop emitted, latest high: %f, closed price: %f, average cost: %f, profit spread: %f",
|
||||
log.Infof("%s trailing stop emitted, latest high: %s, closed price: %s, average cost: %s, profit spread: %s",
|
||||
c.Symbol,
|
||||
c.latestHigh,
|
||||
closePrice,
|
||||
c.averageCost.Float64(),
|
||||
closePrice-c.averageCost.Float64())
|
||||
c.latestHigh.String(),
|
||||
closePrice.String(),
|
||||
c.averageCost.String(),
|
||||
closePrice.Sub(c.averageCost).String())
|
||||
|
||||
log.Infof("current %s position: %s", c.Symbol, c.position.String())
|
||||
|
||||
marketOrder := c.position.NewClosePositionOrder(c.ClosePosition.Float64())
|
||||
marketOrder := c.position.NewClosePositionOrder(c.ClosePosition)
|
||||
if marketOrder != nil {
|
||||
log.Infof("submitting %s market order to stop: %+v", c.Symbol, marketOrder)
|
||||
|
||||
// skip dust order
|
||||
if marketOrder.Quantity*closePrice < c.position.Market.MinNotional {
|
||||
log.Warnf("%s market order quote quantity %f < min notional %f, skip placing order", c.Symbol, marketOrder.Quantity*closePrice, c.position.Market.MinNotional)
|
||||
if marketOrder.Quantity.Mul(closePrice).Compare(c.position.Market.MinNotional) < 0 {
|
||||
log.Warnf("%s market order quote quantity %s < min notional %s, skip placing order", c.Symbol, marketOrder.Quantity.Mul(closePrice).String(), c.position.Market.MinNotional.String())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -148,16 +147,16 @@ func (c *TrailingStopController) Run(ctx context.Context, session *ExchangeSessi
|
|||
tradeCollector.Process()
|
||||
|
||||
// reset the state
|
||||
c.latestHigh = 0.0
|
||||
c.latestHigh = fixedpoint.Zero
|
||||
c.activated = false
|
||||
}
|
||||
} else {
|
||||
// place stop order only when the closed price is greater than the current average cost
|
||||
if c.MinProfit > 0 && closePrice > c.averageCost.Float64() &&
|
||||
changeRate(closePrice, c.averageCost.Float64()) >= c.MinProfit.Float64() {
|
||||
if c.MinProfit.Sign() > 0 && closePrice.Compare(c.averageCost) > 0 &&
|
||||
changeRate(closePrice, c.averageCost).Compare(c.MinProfit) >= 0 {
|
||||
|
||||
stopPrice := c.averageCost.MulFloat64(1.0 + c.MinProfit.Float64())
|
||||
orderForm := c.GenerateStopOrder(stopPrice.Float64(), c.averageCost.Float64())
|
||||
stopPrice := c.averageCost.Mul(fixedpoint.One.Add(c.MinProfit))
|
||||
orderForm := c.GenerateStopOrder(stopPrice, c.averageCost)
|
||||
if orderForm != nil {
|
||||
log.Infof("updating %s stop limit order to simulate trailing stop order...", c.Symbol)
|
||||
|
||||
|
@ -175,26 +174,27 @@ func (c *TrailingStopController) Run(ctx context.Context, session *ExchangeSessi
|
|||
})
|
||||
}
|
||||
|
||||
func (c *TrailingStopController) GenerateStopOrder(stopPrice, price float64) *types.SubmitOrder {
|
||||
func (c *TrailingStopController) GenerateStopOrder(stopPrice, price fixedpoint.Value) *types.SubmitOrder {
|
||||
base := c.position.GetBase()
|
||||
if base == 0 {
|
||||
if base.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
quantity := math.Abs(base.Float64())
|
||||
quoteQuantity := price * quantity
|
||||
quantity := base.Abs()
|
||||
quoteQuantity := price.Mul(quantity)
|
||||
|
||||
if c.ClosePosition > 0 {
|
||||
quantity = quantity * c.ClosePosition.Float64()
|
||||
if c.ClosePosition.Sign() > 0 {
|
||||
quantity = quantity.Mul(c.ClosePosition)
|
||||
}
|
||||
|
||||
// skip dust orders
|
||||
if quantity < c.position.Market.MinQuantity || quoteQuantity < c.position.Market.MinNotional {
|
||||
if quantity.Compare(c.position.Market.MinQuantity) < 0 ||
|
||||
quoteQuantity.Compare(c.position.Market.MinNotional) < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
side := types.SideTypeSell
|
||||
if base < 0 {
|
||||
if base.Sign() < 0 {
|
||||
side = types.SideTypeBuy
|
||||
}
|
||||
|
||||
|
@ -282,6 +282,6 @@ func (s *SmartStops) RunStopControllers(ctx context.Context, session *ExchangeSe
|
|||
}
|
||||
}
|
||||
|
||||
func changeRate(a, b float64) float64 {
|
||||
return math.Abs(a-b) / b
|
||||
func changeRate(a, b fixedpoint.Value) fixedpoint.Value {
|
||||
return a.Sub(b).Div(b).Abs()
|
||||
}
|
||||
|
|
|
@ -93,56 +93,56 @@ func (e *TwapExecution) newBestPriceOrder() (orderForm types.SubmitOrder, err er
|
|||
// if number of ticks = 2, than the tickSpread is 0.02
|
||||
// tickSpread = min(0.02 - 0.01, 0.02)
|
||||
// price = first bid price 28.00 + tickSpread (0.01) = 28.01
|
||||
tickSize := fixedpoint.NewFromFloat(e.market.TickSize)
|
||||
tickSpread := tickSize.MulInt(e.NumOfTicks)
|
||||
if spread > tickSize {
|
||||
tickSize := e.market.TickSize
|
||||
tickSpread := tickSize.Mul(fixedpoint.NewFromInt(int64(e.NumOfTicks)))
|
||||
if spread.Compare(tickSize) > 0 {
|
||||
// there is a gap in the spread
|
||||
tickSpread = fixedpoint.Min(tickSpread, spread-tickSize)
|
||||
tickSpread = fixedpoint.Min(tickSpread, spread.Sub(tickSize))
|
||||
switch e.Side {
|
||||
case types.SideTypeSell:
|
||||
newPrice -= tickSpread
|
||||
newPrice = newPrice.Sub(tickSpread)
|
||||
case types.SideTypeBuy:
|
||||
newPrice += tickSpread
|
||||
newPrice = newPrice.Add(tickSpread)
|
||||
}
|
||||
}
|
||||
|
||||
if e.StopPrice > 0 {
|
||||
if e.StopPrice.Sign() > 0 {
|
||||
switch e.Side {
|
||||
case types.SideTypeSell:
|
||||
if newPrice < e.StopPrice {
|
||||
log.Infof("%s order price %f is lower than the stop sell price %f, setting order price to the stop sell price %f",
|
||||
if newPrice.Compare(e.StopPrice) < 0 {
|
||||
log.Infof("%s order price %s is lower than the stop sell price %s, setting order price to the stop sell price %s",
|
||||
e.Symbol,
|
||||
newPrice.Float64(),
|
||||
e.StopPrice.Float64(),
|
||||
e.StopPrice.Float64())
|
||||
newPrice.String(),
|
||||
e.StopPrice.String(),
|
||||
e.StopPrice.String())
|
||||
newPrice = e.StopPrice
|
||||
}
|
||||
|
||||
case types.SideTypeBuy:
|
||||
if newPrice > e.StopPrice {
|
||||
log.Infof("%s order price %f is higher than the stop buy price %f, setting order price to the stop buy price %f",
|
||||
if newPrice.Compare(e.StopPrice) > 0 {
|
||||
log.Infof("%s order price %s is higher than the stop buy price %s, setting order price to the stop buy price %s",
|
||||
e.Symbol,
|
||||
newPrice.Float64(),
|
||||
e.StopPrice.Float64(),
|
||||
e.StopPrice.Float64())
|
||||
newPrice.String(),
|
||||
e.StopPrice.String(),
|
||||
e.StopPrice.String())
|
||||
newPrice = e.StopPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
minQuantity := fixedpoint.NewFromFloat(e.market.MinQuantity)
|
||||
minQuantity := e.market.MinQuantity
|
||||
base := e.position.GetBase()
|
||||
|
||||
restQuantity := e.TargetQuantity - fixedpoint.Abs(base)
|
||||
restQuantity := e.TargetQuantity.Sub(base.Abs())
|
||||
|
||||
if restQuantity <= 0 {
|
||||
if restQuantity.Sign() <= 0 {
|
||||
if e.cancelContextIfTargetQuantityFilled() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if restQuantity < minQuantity {
|
||||
return orderForm, fmt.Errorf("can not continue placing orders, rest quantity %f is less than the min quantity %f", restQuantity.Float64(), minQuantity.Float64())
|
||||
if restQuantity.Compare(minQuantity) < 0 {
|
||||
return orderForm, fmt.Errorf("can not continue placing orders, rest quantity %s is less than the min quantity %s", restQuantity.String(), minQuantity.String())
|
||||
}
|
||||
|
||||
// when slice = 1000, if we only have 998, we should adjust our quantity to 998
|
||||
|
@ -150,12 +150,12 @@ func (e *TwapExecution) newBestPriceOrder() (orderForm types.SubmitOrder, err er
|
|||
|
||||
// if the rest quantity in the next round is not enough, we should merge the rest quantity into this round
|
||||
// if there are rest slices
|
||||
nextRestQuantity := restQuantity - e.SliceQuantity
|
||||
if nextRestQuantity > 0 && nextRestQuantity < minQuantity {
|
||||
nextRestQuantity := restQuantity.Sub(e.SliceQuantity)
|
||||
if nextRestQuantity.Sign() > 0 && nextRestQuantity.Compare(minQuantity) < 0 {
|
||||
orderQuantity = restQuantity
|
||||
}
|
||||
|
||||
minNotional := fixedpoint.NewFromFloat(e.market.MinNotional)
|
||||
minNotional := e.market.MinNotional
|
||||
orderQuantity = AdjustQuantityByMinAmount(orderQuantity, newPrice, minNotional)
|
||||
|
||||
switch e.Side {
|
||||
|
@ -179,7 +179,7 @@ func (e *TwapExecution) newBestPriceOrder() (orderForm types.SubmitOrder, err er
|
|||
Symbol: e.Symbol,
|
||||
Side: e.Side,
|
||||
Type: types.OrderTypeMarket,
|
||||
Quantity: restQuantity.Float64(),
|
||||
Quantity: restQuantity,
|
||||
Market: e.market,
|
||||
}
|
||||
return orderForm, nil
|
||||
|
@ -191,8 +191,8 @@ func (e *TwapExecution) newBestPriceOrder() (orderForm types.SubmitOrder, err er
|
|||
Symbol: e.Symbol,
|
||||
Side: e.Side,
|
||||
Type: types.OrderTypeLimitMaker,
|
||||
Quantity: orderQuantity.Float64(),
|
||||
Price: newPrice.Float64(),
|
||||
Quantity: orderQuantity,
|
||||
Price: newPrice,
|
||||
Market: e.market,
|
||||
TimeInForce: "GTC",
|
||||
}
|
||||
|
@ -214,8 +214,9 @@ func (e *TwapExecution) updateOrder(ctx context.Context) error {
|
|||
return fmt.Errorf("no secoond price on the %s order book %s, can not update", e.Symbol, e.Side)
|
||||
}
|
||||
|
||||
tickSize := fixedpoint.NewFromFloat(e.market.TickSize)
|
||||
tickSpread := tickSize.MulInt(e.NumOfTicks)
|
||||
tickSize := e.market.TickSize
|
||||
numOfTicks := fixedpoint.NewFromInt(int64(e.NumOfTicks))
|
||||
tickSpread := tickSize.Mul(numOfTicks)
|
||||
|
||||
// check and see if we need to cancel the existing active orders
|
||||
for e.activeMakerOrders.NumOfOrders() > 0 {
|
||||
|
@ -227,12 +228,12 @@ func (e *TwapExecution) updateOrder(ctx context.Context) error {
|
|||
|
||||
// get the first order
|
||||
order := orders[0]
|
||||
orderPrice := fixedpoint.NewFromFloat(order.Price)
|
||||
orderPrice := order.Price
|
||||
// quantity := fixedpoint.NewFromFloat(order.Quantity)
|
||||
|
||||
remainingQuantity := order.Quantity - order.ExecutedQuantity
|
||||
if remainingQuantity <= e.market.MinQuantity {
|
||||
log.Infof("order remaining quantity %f is less than the market minimal quantity %f, skip updating order", remainingQuantity, e.market.MinQuantity)
|
||||
remainingQuantity := order.Quantity.Sub(order.ExecutedQuantity)
|
||||
if remainingQuantity.Compare(e.market.MinQuantity) <= 0 {
|
||||
log.Infof("order remaining quantity %s is less than the market minimal quantity %s, skip updating order", remainingQuantity.String(), e.market.MinQuantity.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -241,24 +242,24 @@ func (e *TwapExecution) updateOrder(ctx context.Context) error {
|
|||
// DO NOT UPDATE IF:
|
||||
// tickSpread > 0 AND current order price == second price + tickSpread
|
||||
// current order price == first price
|
||||
log.Infof("orderPrice = %f first.Price = %f second.Price = %f tickSpread = %f", orderPrice.Float64(), first.Price.Float64(), second.Price.Float64(), tickSpread.Float64())
|
||||
log.Infof("orderPrice = %s first.Price = %s second.Price = %s tickSpread = %s", orderPrice.String(), first.Price.String(), second.Price.String(), tickSpread.String())
|
||||
|
||||
switch e.Side {
|
||||
case types.SideTypeBuy:
|
||||
if tickSpread > 0 && orderPrice == second.Price+tickSpread {
|
||||
log.Infof("the current order is already on the best ask price %f", orderPrice.Float64())
|
||||
if tickSpread.Sign() > 0 && orderPrice == second.Price.Add(tickSpread) {
|
||||
log.Infof("the current order is already on the best ask price %s", orderPrice.String())
|
||||
return nil
|
||||
} else if orderPrice == first.Price {
|
||||
log.Infof("the current order is already on the best bid price %f", orderPrice.Float64())
|
||||
log.Infof("the current order is already on the best bid price %s", orderPrice.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
case types.SideTypeSell:
|
||||
if tickSpread > 0 && orderPrice == second.Price-tickSpread {
|
||||
log.Infof("the current order is already on the best ask price %f", orderPrice.Float64())
|
||||
if tickSpread.Sign() > 0 && orderPrice == second.Price.Sub(tickSpread) {
|
||||
log.Infof("the current order is already on the best ask price %s", orderPrice.String())
|
||||
return nil
|
||||
} else if orderPrice == first.Price {
|
||||
log.Infof("the current order is already on the best ask price %f", orderPrice.Float64())
|
||||
log.Infof("the current order is already on the best ask price %s", orderPrice.String())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +341,7 @@ func (e *TwapExecution) orderUpdater(ctx context.Context) {
|
|||
func (e *TwapExecution) cancelContextIfTargetQuantityFilled() bool {
|
||||
base := e.position.GetBase()
|
||||
|
||||
if fixedpoint.Abs(base) >= e.TargetQuantity {
|
||||
if base.Abs().Compare(e.TargetQuantity) >= 0 {
|
||||
log.Infof("filled target quantity, canceling the order execution context")
|
||||
e.cancelExecution()
|
||||
return true
|
||||
|
|
|
@ -418,17 +418,17 @@ func genFakeAssets() types.AssetMap {
|
|||
"MAX": types.Balance{Currency: "MAX", Available: fixedpoint.NewFromFloat(200000.0 * rand.Float64())},
|
||||
"COMP": types.Balance{Currency: "COMP", Available: fixedpoint.NewFromFloat(100.0 * rand.Float64())},
|
||||
}
|
||||
assets := balances.Assets(map[string]float64{
|
||||
"BTCUSDT": 38000.0,
|
||||
"BCHUSDT": 478.0,
|
||||
"LTCUSDT": 150.0,
|
||||
"COMPUSDT": 450.0,
|
||||
"ETHUSDT": 1700.0,
|
||||
"BNBUSDT": 70.0,
|
||||
"GRTUSDT": 0.89,
|
||||
"DOTUSDT": 20.0,
|
||||
"SANDUSDT": 0.13,
|
||||
"MAXUSDT": 0.122,
|
||||
assets := balances.Assets(map[string]fixedpoint.Value{
|
||||
"BTCUSDT": fixedpoint.NewFromFloat(38000.0),
|
||||
"BCHUSDT": fixedpoint.NewFromFloat(478.0),
|
||||
"LTCUSDT": fixedpoint.NewFromFloat(150.0),
|
||||
"COMPUSDT": fixedpoint.NewFromFloat(450.0),
|
||||
"ETHUSDT": fixedpoint.NewFromFloat(1700.0),
|
||||
"BNBUSDT": fixedpoint.NewFromFloat(70.0),
|
||||
"GRTUSDT": fixedpoint.NewFromFloat(0.89),
|
||||
"DOTUSDT": fixedpoint.NewFromFloat(20.0),
|
||||
"SANDUSDT": fixedpoint.NewFromFloat(0.13),
|
||||
"MAXUSDT": fixedpoint.NewFromFloat(0.122),
|
||||
})
|
||||
for currency, asset := range assets {
|
||||
totalAssets[currency] = asset
|
||||
|
|
Loading…
Reference in New Issue
Block a user