mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
Fix account value tests with price solver
Signed-off-by: c9s <yoanlin93@gmail.com>
This commit is contained in:
parent
c3bf0ed7e7
commit
14fa561f6e
|
@ -9,6 +9,7 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/pricesolver"
|
||||||
"github.com/c9s/bbgo/pkg/risk"
|
"github.com/c9s/bbgo/pkg/risk"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,8 @@ var maxIsolatedMarginLeverage = fixedpoint.NewFromInt(10)
|
||||||
var maxCrossMarginLeverage = fixedpoint.NewFromInt(3)
|
var maxCrossMarginLeverage = fixedpoint.NewFromInt(3)
|
||||||
|
|
||||||
type AccountValueCalculator struct {
|
type AccountValueCalculator struct {
|
||||||
|
priceSolver *pricesolver.SimplePriceSolver
|
||||||
|
|
||||||
session *ExchangeSession
|
session *ExchangeSession
|
||||||
quoteCurrency string
|
quoteCurrency string
|
||||||
prices map[string]fixedpoint.Value
|
prices map[string]fixedpoint.Value
|
||||||
|
@ -27,8 +30,13 @@ type AccountValueCalculator struct {
|
||||||
updateTime time.Time
|
updateTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccountValueCalculator(session *ExchangeSession, quoteCurrency string) *AccountValueCalculator {
|
func NewAccountValueCalculator(
|
||||||
|
session *ExchangeSession,
|
||||||
|
priceSolver *pricesolver.SimplePriceSolver,
|
||||||
|
quoteCurrency string,
|
||||||
|
) *AccountValueCalculator {
|
||||||
return &AccountValueCalculator{
|
return &AccountValueCalculator{
|
||||||
|
priceSolver: priceSolver,
|
||||||
session: session,
|
session: session,
|
||||||
quoteCurrency: quoteCurrency,
|
quoteCurrency: quoteCurrency,
|
||||||
prices: make(map[string]fixedpoint.Value),
|
prices: make(map[string]fixedpoint.Value),
|
||||||
|
@ -39,6 +47,8 @@ func NewAccountValueCalculator(session *ExchangeSession, quoteCurrency string) *
|
||||||
func (c *AccountValueCalculator) UpdatePrices(ctx context.Context) error {
|
func (c *AccountValueCalculator) UpdatePrices(ctx context.Context) error {
|
||||||
balances := c.session.Account.Balances()
|
balances := c.session.Account.Balances()
|
||||||
currencies := balances.Currencies()
|
currencies := balances.Currencies()
|
||||||
|
|
||||||
|
// TODO: improve this part
|
||||||
var symbols []string
|
var symbols []string
|
||||||
for _, currency := range currencies {
|
for _, currency := range currencies {
|
||||||
if currency == c.quoteCurrency {
|
if currency == c.quoteCurrency {
|
||||||
|
@ -49,19 +59,7 @@ func (c *AccountValueCalculator) UpdatePrices(ctx context.Context) error {
|
||||||
symbols = append(symbols, symbol)
|
symbols = append(symbols, symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
tickers, err := c.session.Exchange.QueryTickers(ctx, symbols...)
|
return c.priceSolver.UpdateFromTickers(ctx, c.session.Exchange, symbols...)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.tickers = tickers
|
|
||||||
for symbol, ticker := range tickers {
|
|
||||||
c.prices[symbol] = ticker.Last
|
|
||||||
if ticker.Time.After(c.updateTime) {
|
|
||||||
c.updateTime = ticker.Time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AccountValueCalculator) DebtValue(ctx context.Context) (fixedpoint.Value, error) {
|
func (c *AccountValueCalculator) DebtValue(ctx context.Context) (fixedpoint.Value, error) {
|
||||||
|
@ -103,13 +101,16 @@ func (c *AccountValueCalculator) MarketValue(ctx context.Context) (fixedpoint.Va
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
symbol := b.Currency + c.quoteCurrency
|
if c.priceSolver != nil {
|
||||||
price, ok := c.prices[symbol]
|
if price, ok := c.priceSolver.ResolvePrice(b.Currency, c.quoteCurrency); ok {
|
||||||
if !ok {
|
marketValue = marketValue.Add(b.Total().Mul(price))
|
||||||
continue
|
}
|
||||||
|
} else {
|
||||||
|
symbol := b.Currency + c.quoteCurrency
|
||||||
|
if price, ok := c.prices[symbol]; ok {
|
||||||
|
marketValue = marketValue.Add(b.Total().Mul(price))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
marketValue = marketValue.Add(b.Total().Mul(price))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return marketValue, nil
|
return marketValue, nil
|
||||||
|
@ -123,25 +124,22 @@ func (c *AccountValueCalculator) NetValue(ctx context.Context) (fixedpoint.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
balances := c.session.Account.Balances()
|
balances := c.session.Account.Balances()
|
||||||
accountValue := calculateNetValueInQuote(balances, c.prices, c.quoteCurrency)
|
accountValue := calculateNetValueInQuote(balances, c.priceSolver, c.quoteCurrency)
|
||||||
return accountValue, nil
|
return accountValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateNetValueInQuote(balances types.BalanceMap, prices types.PriceMap, quoteCurrency string) (accountValue fixedpoint.Value) {
|
func calculateNetValueInQuote(
|
||||||
|
balances types.BalanceMap, priceSolver *pricesolver.SimplePriceSolver, quoteCurrency string,
|
||||||
|
) (accountValue fixedpoint.Value) {
|
||||||
accountValue = fixedpoint.Zero
|
accountValue = fixedpoint.Zero
|
||||||
|
|
||||||
for _, b := range balances {
|
for _, b := range balances {
|
||||||
if b.Currency == quoteCurrency {
|
if b.Currency == quoteCurrency {
|
||||||
accountValue = accountValue.Add(b.Net())
|
accountValue = accountValue.Add(b.Net())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
symbol := b.Currency + quoteCurrency // for BTC/USDT, ETH/USDT pairs
|
if price, ok := priceSolver.ResolvePrice(b.Currency, quoteCurrency); ok {
|
||||||
symbolReverse := quoteCurrency + b.Currency // for USDT/USDC or USDT/TWD pairs
|
|
||||||
if price, ok := prices[symbol]; ok {
|
|
||||||
accountValue = accountValue.Add(b.Net().Mul(price))
|
accountValue = accountValue.Add(b.Net().Mul(price))
|
||||||
} else if priceReverse, ok2 := prices[symbolReverse]; ok2 {
|
|
||||||
accountValue = accountValue.Add(b.Net().Div(priceReverse))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +218,9 @@ func usdFiatBalances(balances types.BalanceMap) (fiats types.BalanceMap, rest ty
|
||||||
return fiats, rest
|
return fiats, rest
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateBaseQuantity(session *ExchangeSession, market types.Market, price, quantity, leverage fixedpoint.Value) (fixedpoint.Value, error) {
|
func CalculateBaseQuantity(
|
||||||
|
session *ExchangeSession, market types.Market, price, quantity, leverage fixedpoint.Value,
|
||||||
|
) (fixedpoint.Value, error) {
|
||||||
// default leverage guard
|
// default leverage guard
|
||||||
if leverage.IsZero() {
|
if leverage.IsZero() {
|
||||||
leverage = defaultLeverage
|
leverage = defaultLeverage
|
||||||
|
@ -254,7 +254,7 @@ func CalculateBaseQuantity(session *ExchangeSession, market types.Market, price,
|
||||||
if len(restBalances) == 1 && types.IsUSDFiatCurrency(market.QuoteCurrency) {
|
if len(restBalances) == 1 && types.IsUSDFiatCurrency(market.QuoteCurrency) {
|
||||||
totalUsdValue = aggregateUsdNetValue(balances)
|
totalUsdValue = aggregateUsdNetValue(balances)
|
||||||
} else if len(restBalances) > 1 {
|
} else if len(restBalances) > 1 {
|
||||||
accountValue := NewAccountValueCalculator(session, "USDT")
|
accountValue := NewAccountValueCalculator(session, nil, "USDT")
|
||||||
netValue, err := accountValue.NetValue(context.Background())
|
netValue, err := accountValue.NetValue(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return quantity, err
|
return quantity, err
|
||||||
|
@ -329,7 +329,9 @@ func CalculateBaseQuantity(session *ExchangeSession, market types.Market, price,
|
||||||
errors.New("quantity is zero, can not submit sell order, please check your settings"))
|
errors.New("quantity is zero, can not submit sell order, please check your settings"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateQuoteQuantity(ctx context.Context, session *ExchangeSession, quoteCurrency string, leverage fixedpoint.Value) (fixedpoint.Value, error) {
|
func CalculateQuoteQuantity(
|
||||||
|
ctx context.Context, session *ExchangeSession, quoteCurrency string, leverage fixedpoint.Value,
|
||||||
|
) (fixedpoint.Value, error) {
|
||||||
// default leverage guard
|
// default leverage guard
|
||||||
if leverage.IsZero() {
|
if leverage.IsZero() {
|
||||||
leverage = defaultLeverage
|
leverage = defaultLeverage
|
||||||
|
@ -359,7 +361,7 @@ func CalculateQuoteQuantity(ctx context.Context, session *ExchangeSession, quote
|
||||||
}
|
}
|
||||||
|
|
||||||
// using leverage -- starts from here
|
// using leverage -- starts from here
|
||||||
accountValue := NewAccountValueCalculator(session, quoteCurrency)
|
accountValue := NewAccountValueCalculator(session, nil, quoteCurrency)
|
||||||
availableQuote, err := accountValue.AvailableQuote(ctx)
|
availableQuote, err := accountValue.AvailableQuote(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Errorf("can not update available quote")
|
log.WithError(err).Errorf("can not update available quote")
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/pricesolver"
|
||||||
|
. "github.com/c9s/bbgo/pkg/testing/testhelper"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
"github.com/c9s/bbgo/pkg/types/mocks"
|
"github.com/c9s/bbgo/pkg/types/mocks"
|
||||||
)
|
)
|
||||||
|
@ -27,6 +29,8 @@ func newTestTicker() types.Ticker {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountValueCalculator_NetValue(t *testing.T) {
|
func TestAccountValueCalculator_NetValue(t *testing.T) {
|
||||||
|
symbol := "BTCUSDT"
|
||||||
|
markets := AllMarkets()
|
||||||
|
|
||||||
t.Run("borrow and available", func(t *testing.T) {
|
t.Run("borrow and available", func(t *testing.T) {
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(t)
|
||||||
|
@ -35,8 +39,8 @@ func TestAccountValueCalculator_NetValue(t *testing.T) {
|
||||||
mockEx := mocks.NewMockExchange(mockCtrl)
|
mockEx := mocks.NewMockExchange(mockCtrl)
|
||||||
// for market data stream and user data stream
|
// for market data stream and user data stream
|
||||||
mockEx.EXPECT().NewStream().Return(&types.StandardStream{}).Times(2)
|
mockEx.EXPECT().NewStream().Return(&types.StandardStream{}).Times(2)
|
||||||
mockEx.EXPECT().QueryTickers(gomock.Any(), []string{"BTCUSDT"}).Return(map[string]types.Ticker{
|
mockEx.EXPECT().QueryTickers(gomock.Any(), []string{symbol}).Return(map[string]types.Ticker{
|
||||||
"BTCUSDT": newTestTicker(),
|
"BTCUSDT": Ticker(symbol),
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
session := NewExchangeSession("test", mockEx)
|
session := NewExchangeSession("test", mockEx)
|
||||||
|
@ -60,10 +64,12 @@ func TestAccountValueCalculator_NetValue(t *testing.T) {
|
||||||
})
|
})
|
||||||
assert.NotNil(t, session)
|
assert.NotNil(t, session)
|
||||||
|
|
||||||
cal := NewAccountValueCalculator(session, "USDT")
|
ctx := context.Background()
|
||||||
|
priceSolver := pricesolver.NewSimplePriceResolver(markets)
|
||||||
|
|
||||||
|
cal := NewAccountValueCalculator(session, priceSolver, "USDT")
|
||||||
assert.NotNil(t, cal)
|
assert.NotNil(t, cal)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
netValue, err := cal.NetValue(ctx)
|
netValue, err := cal.NetValue(ctx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "20000", netValue.String())
|
assert.Equal(t, "20000", netValue.String())
|
||||||
|
@ -76,8 +82,8 @@ func TestAccountValueCalculator_NetValue(t *testing.T) {
|
||||||
mockEx := mocks.NewMockExchange(mockCtrl)
|
mockEx := mocks.NewMockExchange(mockCtrl)
|
||||||
// for market data stream and user data stream
|
// for market data stream and user data stream
|
||||||
mockEx.EXPECT().NewStream().Return(&types.StandardStream{}).Times(2)
|
mockEx.EXPECT().NewStream().Return(&types.StandardStream{}).Times(2)
|
||||||
mockEx.EXPECT().QueryTickers(gomock.Any(), []string{"BTCUSDT"}).Return(map[string]types.Ticker{
|
mockEx.EXPECT().QueryTickers(gomock.Any(), []string{symbol}).Return(map[string]types.Ticker{
|
||||||
"BTCUSDT": newTestTicker(),
|
symbol: Ticker(symbol),
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
session := NewExchangeSession("test", mockEx)
|
session := NewExchangeSession("test", mockEx)
|
||||||
|
@ -101,10 +107,12 @@ func TestAccountValueCalculator_NetValue(t *testing.T) {
|
||||||
})
|
})
|
||||||
assert.NotNil(t, session)
|
assert.NotNil(t, session)
|
||||||
|
|
||||||
cal := NewAccountValueCalculator(session, "USDT")
|
ctx := context.Background()
|
||||||
|
priceSolver := pricesolver.NewSimplePriceResolver(markets)
|
||||||
|
|
||||||
|
cal := NewAccountValueCalculator(session, priceSolver, "USDT")
|
||||||
assert.NotNil(t, cal)
|
assert.NotNil(t, cal)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
netValue, err := cal.NetValue(ctx)
|
netValue, err := cal.NetValue(ctx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "2000", netValue.String()) // 21000-19000
|
assert.Equal(t, "2000", netValue.String()) // 21000-19000
|
||||||
|
@ -115,11 +123,13 @@ func TestNewAccountValueCalculator_MarginLevel(t *testing.T) {
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(t)
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
|
|
||||||
|
ticker := newTestTicker()
|
||||||
|
|
||||||
mockEx := mocks.NewMockExchange(mockCtrl)
|
mockEx := mocks.NewMockExchange(mockCtrl)
|
||||||
// for market data stream and user data stream
|
// for market data stream and user data stream
|
||||||
mockEx.EXPECT().NewStream().Return(&types.StandardStream{}).Times(2)
|
mockEx.EXPECT().NewStream().Return(&types.StandardStream{}).Times(2)
|
||||||
mockEx.EXPECT().QueryTickers(gomock.Any(), []string{"BTCUSDT"}).Return(map[string]types.Ticker{
|
mockEx.EXPECT().QueryTickers(gomock.Any(), []string{"BTCUSDT"}).Return(map[string]types.Ticker{
|
||||||
"BTCUSDT": newTestTicker(),
|
"BTCUSDT": ticker,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
session := NewExchangeSession("test", mockEx)
|
session := NewExchangeSession("test", mockEx)
|
||||||
|
@ -143,10 +153,15 @@ func TestNewAccountValueCalculator_MarginLevel(t *testing.T) {
|
||||||
})
|
})
|
||||||
assert.NotNil(t, session)
|
assert.NotNil(t, session)
|
||||||
|
|
||||||
cal := NewAccountValueCalculator(session, "USDT")
|
ctx := context.Background()
|
||||||
|
markets := AllMarkets()
|
||||||
|
priceSolver := pricesolver.NewSimplePriceResolver(markets)
|
||||||
|
err := priceSolver.UpdateFromTickers(ctx, mockEx, "BTCUSDT")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
cal := NewAccountValueCalculator(session, priceSolver, "USDT")
|
||||||
assert.NotNil(t, cal)
|
assert.NotNil(t, cal)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
marginLevel, err := cal.MarginLevel(ctx)
|
marginLevel, err := cal.MarginLevel(ctx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -173,10 +188,9 @@ func Test_aggregateUsdValue(t *testing.T) {
|
||||||
name: "mixed",
|
name: "mixed",
|
||||||
args: args{
|
args: args{
|
||||||
balances: types.BalanceMap{
|
balances: types.BalanceMap{
|
||||||
"USDC": types.Balance{Currency: "USDC", Available: number(70.0)},
|
"USDC": types.Balance{Currency: "USDC", Available: Number(70.0 + 80.0)},
|
||||||
"USDT": types.Balance{Currency: "USDT", Available: number(100.0)},
|
"USDT": types.Balance{Currency: "USDT", Available: Number(100.0)},
|
||||||
"BUSD": types.Balance{Currency: "BUSD", Available: number(80.0)},
|
"BTC": types.Balance{Currency: "BTC", Available: Number(0.01)},
|
||||||
"BTC": types.Balance{Currency: "BTC", Available: number(0.01)},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: number(250.0),
|
want: number(250.0),
|
||||||
|
@ -202,19 +216,17 @@ func Test_usdFiatBalances(t *testing.T) {
|
||||||
{
|
{
|
||||||
args: args{
|
args: args{
|
||||||
balances: types.BalanceMap{
|
balances: types.BalanceMap{
|
||||||
"USDC": types.Balance{Currency: "USDC", Available: number(70.0)},
|
"USDC": types.Balance{Currency: "USDC", Available: Number(70.0 + 80.0)},
|
||||||
"USDT": types.Balance{Currency: "USDT", Available: number(100.0)},
|
"USDT": types.Balance{Currency: "USDT", Available: Number(100.0)},
|
||||||
"BUSD": types.Balance{Currency: "BUSD", Available: number(80.0)},
|
"BTC": types.Balance{Currency: "BTC", Available: Number(0.01)},
|
||||||
"BTC": types.Balance{Currency: "BTC", Available: number(0.01)},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantFiats: types.BalanceMap{
|
wantFiats: types.BalanceMap{
|
||||||
"USDC": types.Balance{Currency: "USDC", Available: number(70.0)},
|
"USDC": types.Balance{Currency: "USDC", Available: Number(70.0 + 80.0)},
|
||||||
"USDT": types.Balance{Currency: "USDT", Available: number(100.0)},
|
"USDT": types.Balance{Currency: "USDT", Available: Number(100.0)},
|
||||||
"BUSD": types.Balance{Currency: "BUSD", Available: number(80.0)},
|
|
||||||
},
|
},
|
||||||
wantRest: types.BalanceMap{
|
wantRest: types.BalanceMap{
|
||||||
"BTC": types.Balance{Currency: "BTC", Available: number(0.01)},
|
"BTC": types.Balance{Currency: "BTC", Available: Number(0.01)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -242,52 +254,46 @@ func Test_calculateNetValueInQuote(t *testing.T) {
|
||||||
name: "positive asset",
|
name: "positive asset",
|
||||||
args: args{
|
args: args{
|
||||||
balances: types.BalanceMap{
|
balances: types.BalanceMap{
|
||||||
"USDC": types.Balance{Currency: "USDC", Available: number(70.0)},
|
"USDC": types.Balance{Currency: "USDC", Available: number(70.0 + 80.0)},
|
||||||
"USDT": types.Balance{Currency: "USDT", Available: number(100.0)},
|
"USDT": types.Balance{Currency: "USDT", Available: number(100.0)},
|
||||||
"BUSD": types.Balance{Currency: "BUSD", Available: number(80.0)},
|
|
||||||
"BTC": types.Balance{Currency: "BTC", Available: number(0.01)},
|
"BTC": types.Balance{Currency: "BTC", Available: number(0.01)},
|
||||||
},
|
},
|
||||||
prices: types.PriceMap{
|
prices: types.PriceMap{
|
||||||
"USDCUSDT": number(1.0),
|
"USDCUSDT": Number(1.0),
|
||||||
"BUSDUSDT": number(1.0),
|
"BTCUSDT": Number(19000.0),
|
||||||
"BTCUSDT": number(19000.0),
|
|
||||||
},
|
},
|
||||||
quoteCurrency: "USDT",
|
quoteCurrency: "USDT",
|
||||||
},
|
},
|
||||||
wantAccountValue: number(19000.0*0.01 + 100.0 + 80.0 + 70.0),
|
wantAccountValue: Number(19000.0*0.01 + 100.0 + 80.0 + 70.0),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reversed usdt price",
|
name: "reversed usdt price",
|
||||||
args: args{
|
args: args{
|
||||||
balances: types.BalanceMap{
|
balances: types.BalanceMap{
|
||||||
"USDC": types.Balance{Currency: "USDC", Available: number(70.0)},
|
"USDC": types.Balance{Currency: "USDC", Available: Number(70.0 + 80.0)},
|
||||||
"TWD": types.Balance{Currency: "TWD", Available: number(3000.0)},
|
"TWD": types.Balance{Currency: "TWD", Available: Number(3000.0)},
|
||||||
"USDT": types.Balance{Currency: "USDT", Available: number(100.0)},
|
"USDT": types.Balance{Currency: "USDT", Available: Number(100.0)},
|
||||||
"BUSD": types.Balance{Currency: "BUSD", Available: number(80.0)},
|
"BTC": types.Balance{Currency: "BTC", Available: Number(0.01)},
|
||||||
"BTC": types.Balance{Currency: "BTC", Available: number(0.01)},
|
|
||||||
},
|
},
|
||||||
prices: types.PriceMap{
|
prices: types.PriceMap{
|
||||||
"USDTTWD": number(30.0),
|
"USDTTWD": Number(30.0),
|
||||||
"USDCUSDT": number(1.0),
|
"USDCUSDT": Number(1.0),
|
||||||
"BUSDUSDT": number(1.0),
|
"BTCUSDT": Number(19000.0),
|
||||||
"BTCUSDT": number(19000.0),
|
|
||||||
},
|
},
|
||||||
quoteCurrency: "USDT",
|
quoteCurrency: "USDT",
|
||||||
},
|
},
|
||||||
wantAccountValue: number(19000.0*0.01 + 100.0 + 80.0 + 70.0 + (3000.0 / 30.0)),
|
wantAccountValue: Number(19000.0*0.01 + 100.0 + 80.0 + 70.0 + (3000.0 / 30.0)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "borrow base asset",
|
name: "borrow base asset",
|
||||||
args: args{
|
args: args{
|
||||||
balances: types.BalanceMap{
|
balances: types.BalanceMap{
|
||||||
"USDT": types.Balance{Currency: "USDT", Available: number(20000.0 * 2)},
|
"USDT": types.Balance{Currency: "USDT", Available: Number(20000.0*2 + 80.0)},
|
||||||
"USDC": types.Balance{Currency: "USDC", Available: number(70.0)},
|
"USDC": types.Balance{Currency: "USDC", Available: Number(70.0)},
|
||||||
"BUSD": types.Balance{Currency: "BUSD", Available: number(80.0)},
|
"BTC": types.Balance{Currency: "BTC", Available: Number(0), Borrowed: Number(2.0)},
|
||||||
"BTC": types.Balance{Currency: "BTC", Available: number(0), Borrowed: number(2.0)},
|
|
||||||
},
|
},
|
||||||
prices: types.PriceMap{
|
prices: types.PriceMap{
|
||||||
"USDCUSDT": number(1.0),
|
"USDCUSDT": number(1.0),
|
||||||
"BUSDUSDT": number(1.0),
|
|
||||||
"BTCUSDT": number(19000.0),
|
"BTCUSDT": number(19000.0),
|
||||||
},
|
},
|
||||||
quoteCurrency: "USDT",
|
quoteCurrency: "USDT",
|
||||||
|
@ -298,26 +304,35 @@ func Test_calculateNetValueInQuote(t *testing.T) {
|
||||||
name: "multi base asset",
|
name: "multi base asset",
|
||||||
args: args{
|
args: args{
|
||||||
balances: types.BalanceMap{
|
balances: types.BalanceMap{
|
||||||
"USDT": types.Balance{Currency: "USDT", Available: number(20000.0 * 2)},
|
"USDT": types.Balance{Currency: "USDT", Available: Number(20000.0*2 + 80.0)},
|
||||||
"USDC": types.Balance{Currency: "USDC", Available: number(70.0)},
|
"USDC": types.Balance{Currency: "USDC", Available: Number(70.0)},
|
||||||
"BUSD": types.Balance{Currency: "BUSD", Available: number(80.0)},
|
"ETH": types.Balance{Currency: "ETH", Available: Number(10.0)},
|
||||||
"ETH": types.Balance{Currency: "ETH", Available: number(10.0)},
|
"BTC": types.Balance{Currency: "BTC", Available: Number(0), Borrowed: Number(2.0)},
|
||||||
"BTC": types.Balance{Currency: "BTC", Available: number(0), Borrowed: number(2.0)},
|
|
||||||
},
|
},
|
||||||
prices: types.PriceMap{
|
prices: types.PriceMap{
|
||||||
"USDCUSDT": number(1.0),
|
"USDCUSDT": Number(1.0),
|
||||||
"BUSDUSDT": number(1.0),
|
"BTCUSDT": Number(19000.0),
|
||||||
"ETHUSDT": number(1700.0),
|
"ETHUSDT": Number(1700.0),
|
||||||
"BTCUSDT": number(19000.0),
|
|
||||||
},
|
},
|
||||||
quoteCurrency: "USDT",
|
quoteCurrency: "USDT",
|
||||||
},
|
},
|
||||||
wantAccountValue: number(19000.0*-2.0 + 1700.0*10.0 + 20000.0*2 + 80.0 + 70.0),
|
wantAccountValue: Number(19000.0*-2.0 + 1700.0*10.0 + 20000.0*2 + 80.0 + 70.0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Equalf(t, tt.wantAccountValue, calculateNetValueInQuote(tt.args.balances, tt.args.prices, tt.args.quoteCurrency), "calculateNetValueInQuote(%v, %v, %v)", tt.args.balances, tt.args.prices, tt.args.quoteCurrency)
|
markets := AllMarkets()
|
||||||
|
priceSolver := pricesolver.NewSimplePriceResolver(markets)
|
||||||
|
|
||||||
|
for symbol, price := range tt.args.prices {
|
||||||
|
priceSolver.Update(symbol, price)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.InDeltaf(t,
|
||||||
|
tt.wantAccountValue.Float64(),
|
||||||
|
calculateNetValueInQuote(tt.args.balances, priceSolver, tt.args.quoteCurrency).Float64(),
|
||||||
|
0.01,
|
||||||
|
"calculateNetValueInQuote(%v, %v, %v)", tt.args.balances, tt.args.prices, tt.args.quoteCurrency)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,36 +7,64 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var markets = map[string]types.Market{
|
var _markets = types.MarketMap{
|
||||||
"BTCUSDT": {
|
"BTCUSDT": {
|
||||||
Symbol: "BTCUSDT",
|
Symbol: "BTCUSDT",
|
||||||
PricePrecision: 2,
|
PricePrecision: 2,
|
||||||
VolumePrecision: 8,
|
VolumePrecision: 8,
|
||||||
QuoteCurrency: "USDT",
|
QuoteCurrency: "USDT",
|
||||||
BaseCurrency: "BTC",
|
BaseCurrency: "BTC",
|
||||||
MinNotional: fixedpoint.MustNewFromString("0.001"),
|
MinNotional: fixedpoint.MustNewFromString("10.0"),
|
||||||
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
||||||
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
||||||
TickSize: fixedpoint.MustNewFromString("0.01"),
|
TickSize: fixedpoint.MustNewFromString("0.01"),
|
||||||
},
|
},
|
||||||
|
|
||||||
"ETHUSDT": {
|
"ETHUSDT": {
|
||||||
Symbol: "ETH",
|
Symbol: "ETHUSDT",
|
||||||
PricePrecision: 2,
|
PricePrecision: 2,
|
||||||
VolumePrecision: 8,
|
VolumePrecision: 8,
|
||||||
QuoteCurrency: "USDT",
|
QuoteCurrency: "USDT",
|
||||||
BaseCurrency: "ETH",
|
BaseCurrency: "ETH",
|
||||||
MinNotional: fixedpoint.MustNewFromString("0.005"),
|
MinNotional: fixedpoint.MustNewFromString("10.0"),
|
||||||
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
||||||
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
||||||
TickSize: fixedpoint.MustNewFromString("0.01"),
|
TickSize: fixedpoint.MustNewFromString("0.01"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"USDCUSDT": {
|
||||||
|
Symbol: "USDCUSDT",
|
||||||
|
PricePrecision: 5,
|
||||||
|
VolumePrecision: 2,
|
||||||
|
QuoteCurrency: "USDT",
|
||||||
|
BaseCurrency: "USDC",
|
||||||
|
MinNotional: fixedpoint.MustNewFromString("10.0"),
|
||||||
|
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
||||||
|
MinQuantity: fixedpoint.MustNewFromString("10.0"),
|
||||||
|
TickSize: fixedpoint.MustNewFromString("0.0001"),
|
||||||
|
},
|
||||||
|
|
||||||
|
"USDTTWD": {
|
||||||
|
Symbol: "USDTTWD",
|
||||||
|
PricePrecision: 2,
|
||||||
|
VolumePrecision: 1,
|
||||||
|
QuoteCurrency: "TWD",
|
||||||
|
BaseCurrency: "USDT",
|
||||||
|
MinNotional: fixedpoint.MustNewFromString("10.0"),
|
||||||
|
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
||||||
|
MinQuantity: fixedpoint.MustNewFromString("10.0"),
|
||||||
|
TickSize: fixedpoint.MustNewFromString("0.01"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func AllMarkets() types.MarketMap {
|
||||||
|
return _markets
|
||||||
}
|
}
|
||||||
|
|
||||||
func Market(symbol string) types.Market {
|
func Market(symbol string) types.Market {
|
||||||
market, ok := markets[symbol]
|
market, ok := _markets[symbol]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Errorf("%s market not found, valid markets: %+v", symbol, markets))
|
panic(fmt.Errorf("%s test market not found, valid markets: %+v", symbol, _markets))
|
||||||
}
|
}
|
||||||
|
|
||||||
return market
|
return market
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tickers = map[string]types.Ticker{
|
var _tickers = map[string]types.Ticker{
|
||||||
"BTCUSDT": {
|
"BTCUSDT": {
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Volume: fixedpoint.Zero,
|
Volume: fixedpoint.Zero,
|
||||||
|
@ -33,7 +33,7 @@ var tickers = map[string]types.Ticker{
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ticker(symbol string) types.Ticker {
|
func Ticker(symbol string) types.Ticker {
|
||||||
ticker, ok := tickers[symbol]
|
ticker, ok := _tickers[symbol]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Errorf("%s test ticker not found, valid tickers: %+v", symbol, []string{"BTCUSDT", "ETHUSDT"}))
|
panic(fmt.Errorf("%s test ticker not found, valid tickers: %+v", symbol, []string{"BTCUSDT", "ETHUSDT"}))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user