mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 08:15:15 +00:00
refactor balance, asset and remove price cache check
This commit is contained in:
parent
f33e8a3de2
commit
36c764efa9
|
@ -649,9 +649,10 @@ func (session *ExchangeSession) FormatOrder(order types.SubmitOrder) (types.Subm
|
|||
}
|
||||
|
||||
func (session *ExchangeSession) UpdatePrices(ctx context.Context, currencies []string, fiat string) (err error) {
|
||||
if session.lastPriceUpdatedAt.After(time.Now().Add(-time.Hour)) {
|
||||
return nil
|
||||
}
|
||||
// TODO: move this cache check to the http routes
|
||||
// if session.lastPriceUpdatedAt.After(time.Now().Add(-time.Hour)) {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
var symbols []string
|
||||
for _, c := range currencies {
|
||||
|
|
|
@ -78,7 +78,6 @@ var Ten = fixedpoint.NewFromInt(10)
|
|||
func (s *Strategy) CrossSubscribe(sessions map[string]*bbgo.ExchangeSession) {}
|
||||
|
||||
func (s *Strategy) recordNetAssetValue(ctx context.Context, sessions map[string]*bbgo.ExchangeSession) {
|
||||
totalAssets := types.AssetMap{}
|
||||
totalBalances := types.BalanceMap{}
|
||||
allPrices := map[string]fixedpoint.Value{}
|
||||
sessionBalances := map[string]types.BalanceMap{}
|
||||
|
@ -113,19 +112,20 @@ func (s *Strategy) recordNetAssetValue(ctx context.Context, sessions map[string]
|
|||
s.Environment.RecordAsset(priceTime, session, assets)
|
||||
}
|
||||
|
||||
allAssets := totalBalances.Assets(allPrices, priceTime)
|
||||
for currency, asset := range allAssets {
|
||||
displayAssets := types.AssetMap{}
|
||||
totalAssets := totalBalances.Assets(allPrices, priceTime)
|
||||
s.Environment.RecordAsset(priceTime, &bbgo.ExchangeSession{Name: "ALL"}, totalAssets)
|
||||
|
||||
for currency, asset := range totalAssets {
|
||||
// calculated if it's dust only when InUSD (usd value) is defined.
|
||||
if s.IgnoreDusts && !asset.InUSD.IsZero() && asset.InUSD.Compare(Ten) < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
totalAssets[currency] = asset
|
||||
displayAssets[currency] = asset
|
||||
}
|
||||
|
||||
s.Environment.RecordAsset(priceTime, &bbgo.ExchangeSession{Name: "ALL"}, totalAssets)
|
||||
|
||||
s.Notifiability.Notify(totalAssets)
|
||||
s.Notifiability.Notify(displayAssets)
|
||||
|
||||
if s.state != nil {
|
||||
if s.state.IsOver24Hours() {
|
||||
|
@ -196,7 +196,7 @@ func (s *Strategy) CrossRun(ctx context.Context, _ bbgo.OrderExecutionRouter, se
|
|||
}
|
||||
|
||||
// TODO: if interval is supported, we can use kline as the ticker
|
||||
if _, ok := types.SupportedIntervals[s.Interval] ; ok {
|
||||
if _, ok := types.SupportedIntervals[s.Interval]; ok {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/slack-go/slack"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -21,253 +16,12 @@ func init() {
|
|||
debugBalance = viper.GetBool("debug-balance")
|
||||
}
|
||||
|
||||
type Balance struct {
|
||||
Currency string `json:"currency"`
|
||||
Available fixedpoint.Value `json:"available"`
|
||||
Locked fixedpoint.Value `json:"locked,omitempty"`
|
||||
|
||||
// margin related fields
|
||||
Borrowed fixedpoint.Value `json:"borrowed,omitempty"`
|
||||
Interest fixedpoint.Value `json:"interest,omitempty"`
|
||||
|
||||
// NetAsset = (Available + Locked) - Borrowed - Interest
|
||||
NetAsset fixedpoint.Value `json:"net,omitempty"`
|
||||
}
|
||||
|
||||
func (b Balance) Total() fixedpoint.Value {
|
||||
return b.Available.Add(b.Locked)
|
||||
}
|
||||
|
||||
func (b Balance) String() (o string) {
|
||||
|
||||
o = fmt.Sprintf("%s: %s", b.Currency, b.Available.String())
|
||||
|
||||
if b.Locked.Sign() > 0 {
|
||||
o += fmt.Sprintf(" (locked %v)", b.Locked)
|
||||
}
|
||||
|
||||
if b.Borrowed.Sign() > 0 {
|
||||
o += fmt.Sprintf(" (borrowed: %v)", b.Borrowed)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
type Asset struct {
|
||||
Currency string `json:"currency" db:"currency"`
|
||||
Total fixedpoint.Value `json:"total" db:"total"`
|
||||
InUSD fixedpoint.Value `json:"inUSD" db:"in_usd"`
|
||||
InBTC fixedpoint.Value `json:"inBTC" db:"in_btc"`
|
||||
Time time.Time `json:"time" db:"time"`
|
||||
Locked fixedpoint.Value `json:"lock" db:"lock" `
|
||||
Available fixedpoint.Value `json:"available" db:"available"`
|
||||
Borrowed fixedpoint.Value `json:"borrowed" db:"borrowed"`
|
||||
NetAsset fixedpoint.Value `json:"netAsset" db:"net_asset"`
|
||||
PriceInUSD fixedpoint.Value `json:"priceInUSD" db:"price_in_usd"`
|
||||
}
|
||||
|
||||
type AssetMap map[string]Asset
|
||||
|
||||
func (m AssetMap) PlainText() (o string) {
|
||||
var assets = m.Slice()
|
||||
|
||||
// sort assets
|
||||
sort.Slice(assets, func(i, j int) bool {
|
||||
return assets[i].InUSD.Compare(assets[j].InUSD) > 0
|
||||
})
|
||||
|
||||
sumUsd := fixedpoint.Zero
|
||||
sumBTC := fixedpoint.Zero
|
||||
for _, a := range assets {
|
||||
usd := a.InUSD
|
||||
btc := a.InBTC
|
||||
if !a.InUSD.IsZero() {
|
||||
o += fmt.Sprintf(" %s: %s (≈ %s) (≈ %s)",
|
||||
a.Currency,
|
||||
a.Total.String(),
|
||||
USD.FormatMoney(usd),
|
||||
BTC.FormatMoney(btc),
|
||||
) + "\n"
|
||||
sumUsd = sumUsd.Add(usd)
|
||||
sumBTC = sumBTC.Add(btc)
|
||||
} else {
|
||||
o += fmt.Sprintf(" %s: %s",
|
||||
a.Currency,
|
||||
a.Total.String(),
|
||||
) + "\n"
|
||||
}
|
||||
}
|
||||
o += fmt.Sprintf(" Summary: (≈ %s) (≈ %s)",
|
||||
USD.FormatMoney(sumUsd),
|
||||
BTC.FormatMoney(sumBTC),
|
||||
) + "\n"
|
||||
return o
|
||||
}
|
||||
|
||||
func (m AssetMap) Slice() (assets []Asset) {
|
||||
for _, a := range m {
|
||||
assets = append(assets, a)
|
||||
}
|
||||
return assets
|
||||
}
|
||||
|
||||
func (m AssetMap) SlackAttachment() slack.Attachment {
|
||||
var fields []slack.AttachmentField
|
||||
var totalBTC, totalUSD fixedpoint.Value
|
||||
|
||||
var assets = m.Slice()
|
||||
|
||||
// sort assets
|
||||
sort.Slice(assets, func(i, j int) bool {
|
||||
return assets[i].InUSD.Compare(assets[j].InUSD) > 0
|
||||
})
|
||||
|
||||
for _, a := range assets {
|
||||
totalUSD = totalUSD.Add(a.InUSD)
|
||||
totalBTC = totalBTC.Add(a.InBTC)
|
||||
}
|
||||
|
||||
for _, a := range assets {
|
||||
if !a.InUSD.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: a.Currency,
|
||||
Value: fmt.Sprintf("%s (≈ %s) (≈ %s) (%s)",
|
||||
a.Total.String(),
|
||||
USD.FormatMoney(a.InUSD),
|
||||
BTC.FormatMoney(a.InBTC),
|
||||
a.InUSD.Div(totalUSD).FormatPercentage(2),
|
||||
),
|
||||
Short: false,
|
||||
})
|
||||
} else {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: a.Currency,
|
||||
Value: fmt.Sprintf("%s", a.Total.String()),
|
||||
Short: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return slack.Attachment{
|
||||
Title: fmt.Sprintf("Net Asset Value %s (≈ %s)",
|
||||
USD.FormatMoney(totalUSD),
|
||||
BTC.FormatMoney(totalBTC),
|
||||
),
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
type BalanceMap map[string]Balance
|
||||
type PositionMap map[string]Position
|
||||
type IsolatedMarginAssetMap map[string]IsolatedMarginAsset
|
||||
type MarginAssetMap map[string]MarginUserAsset
|
||||
type FuturesAssetMap map[string]FuturesUserAsset
|
||||
type FuturesPositionMap map[string]FuturesPosition
|
||||
|
||||
func (m BalanceMap) Currencies() (currencies []string) {
|
||||
for _, b := range m {
|
||||
currencies = append(currencies, b.Currency)
|
||||
}
|
||||
return currencies
|
||||
}
|
||||
|
||||
func (m BalanceMap) Add(bm BalanceMap) BalanceMap {
|
||||
var total = BalanceMap{}
|
||||
for _, b := range bm {
|
||||
tb := total[b.Currency]
|
||||
tb.Available = tb.Available.Add(b.Available)
|
||||
tb.Locked = tb.Locked.Add(b.Locked)
|
||||
tb.Borrowed = tb.Borrowed.Add(b.Borrowed)
|
||||
tb.NetAsset = tb.NetAsset.Add(b.NetAsset)
|
||||
tb.Interest = tb.Interest.Add(b.Interest)
|
||||
total[b.Currency] = tb
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (m BalanceMap) String() string {
|
||||
var ss []string
|
||||
for _, b := range m {
|
||||
ss = append(ss, b.String())
|
||||
}
|
||||
|
||||
return "BalanceMap[" + strings.Join(ss, ", ") + "]"
|
||||
}
|
||||
|
||||
func (m BalanceMap) Copy() (d BalanceMap) {
|
||||
d = make(BalanceMap)
|
||||
for c, b := range m {
|
||||
d[c] = b
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// Assets converts balances into assets with the given prices
|
||||
func (m BalanceMap) Assets(prices map[string]fixedpoint.Value, priceTime time.Time) AssetMap {
|
||||
assets := make(AssetMap)
|
||||
btcusdt, hasBtcPrice := prices["BTCUSDT"]
|
||||
for currency, b := range m {
|
||||
if b.Locked.IsZero() && b.Available.IsZero() && b.Borrowed.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
total := b.Available.Add(b.Locked)
|
||||
netAsset := b.NetAsset
|
||||
if netAsset.IsZero() {
|
||||
netAsset = total.Sub(b.Borrowed)
|
||||
}
|
||||
|
||||
asset := Asset{
|
||||
Currency: currency,
|
||||
Total: total,
|
||||
Time: priceTime,
|
||||
Locked: b.Locked,
|
||||
Available: b.Available,
|
||||
Borrowed: b.Borrowed,
|
||||
NetAsset: netAsset,
|
||||
}
|
||||
|
||||
usdMarkets := []string{currency + "USDT", currency + "USDC", currency + "USD", "USDT" + currency}
|
||||
for _, market := range usdMarkets {
|
||||
if usdPrice, ok := prices[market] ; ok {
|
||||
// this includes USDT, USD, USDC and so on
|
||||
if strings.HasPrefix(market, "USD") {
|
||||
if !asset.Total.IsZero() {
|
||||
asset.InUSD = asset.Total.Div(usdPrice)
|
||||
}
|
||||
asset.PriceInUSD = usdPrice
|
||||
} else {
|
||||
if !asset.Total.IsZero() {
|
||||
asset.InUSD = asset.Total.Mul(usdPrice)
|
||||
}
|
||||
asset.PriceInUSD = fixedpoint.One.Div(usdPrice)
|
||||
}
|
||||
|
||||
if hasBtcPrice && !asset.InUSD.IsZero() {
|
||||
asset.InBTC = asset.InUSD.Div(btcusdt)
|
||||
}
|
||||
}
|
||||
assets[currency] = asset
|
||||
}
|
||||
}
|
||||
|
||||
return assets
|
||||
}
|
||||
|
||||
func (m BalanceMap) Print() {
|
||||
for _, balance := range m {
|
||||
if balance.Available.IsZero() && balance.Locked.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
if balance.Locked.Sign() > 0 {
|
||||
logrus.Infof(" %s: %v (locked %v)", balance.Currency, balance.Available, balance.Locked)
|
||||
} else {
|
||||
logrus.Infof(" %s: %v", balance.Currency, balance.Available)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type AccountType string
|
||||
|
||||
const (
|
||||
|
|
115
pkg/types/asset.go
Normal file
115
pkg/types/asset.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/slack-go/slack"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
type Asset struct {
|
||||
Currency string `json:"currency" db:"currency"`
|
||||
Total fixedpoint.Value `json:"total" db:"total"`
|
||||
InUSD fixedpoint.Value `json:"inUSD" db:"in_usd"`
|
||||
InBTC fixedpoint.Value `json:"inBTC" db:"in_btc"`
|
||||
Time time.Time `json:"time" db:"time"`
|
||||
Locked fixedpoint.Value `json:"lock" db:"lock" `
|
||||
Available fixedpoint.Value `json:"available" db:"available"`
|
||||
Borrowed fixedpoint.Value `json:"borrowed" db:"borrowed"`
|
||||
NetAsset fixedpoint.Value `json:"netAsset" db:"net_asset"`
|
||||
PriceInUSD fixedpoint.Value `json:"priceInUSD" db:"price_in_usd"`
|
||||
}
|
||||
|
||||
type AssetMap map[string]Asset
|
||||
|
||||
func (m AssetMap) PlainText() (o string) {
|
||||
var assets = m.Slice()
|
||||
|
||||
// sort assets
|
||||
sort.Slice(assets, func(i, j int) bool {
|
||||
return assets[i].InUSD.Compare(assets[j].InUSD) > 0
|
||||
})
|
||||
|
||||
sumUsd := fixedpoint.Zero
|
||||
sumBTC := fixedpoint.Zero
|
||||
for _, a := range assets {
|
||||
usd := a.InUSD
|
||||
btc := a.InBTC
|
||||
if !a.InUSD.IsZero() {
|
||||
o += fmt.Sprintf(" %s: %s (≈ %s) (≈ %s)",
|
||||
a.Currency,
|
||||
a.Total.String(),
|
||||
USD.FormatMoney(usd),
|
||||
BTC.FormatMoney(btc),
|
||||
) + "\n"
|
||||
sumUsd = sumUsd.Add(usd)
|
||||
sumBTC = sumBTC.Add(btc)
|
||||
} else {
|
||||
o += fmt.Sprintf(" %s: %s",
|
||||
a.Currency,
|
||||
a.Total.String(),
|
||||
) + "\n"
|
||||
}
|
||||
}
|
||||
o += fmt.Sprintf(" Summary: (≈ %s) (≈ %s)",
|
||||
USD.FormatMoney(sumUsd),
|
||||
BTC.FormatMoney(sumBTC),
|
||||
) + "\n"
|
||||
return o
|
||||
}
|
||||
|
||||
func (m AssetMap) Slice() (assets []Asset) {
|
||||
for _, a := range m {
|
||||
assets = append(assets, a)
|
||||
}
|
||||
return assets
|
||||
}
|
||||
|
||||
func (m AssetMap) SlackAttachment() slack.Attachment {
|
||||
var fields []slack.AttachmentField
|
||||
var totalBTC, totalUSD fixedpoint.Value
|
||||
|
||||
var assets = m.Slice()
|
||||
|
||||
// sort assets
|
||||
sort.Slice(assets, func(i, j int) bool {
|
||||
return assets[i].InUSD.Compare(assets[j].InUSD) > 0
|
||||
})
|
||||
|
||||
for _, a := range assets {
|
||||
totalUSD = totalUSD.Add(a.InUSD)
|
||||
totalBTC = totalBTC.Add(a.InBTC)
|
||||
}
|
||||
|
||||
for _, a := range assets {
|
||||
if !a.InUSD.IsZero() {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: a.Currency,
|
||||
Value: fmt.Sprintf("%s (≈ %s) (≈ %s) (%s)",
|
||||
a.Total.String(),
|
||||
USD.FormatMoney(a.InUSD),
|
||||
BTC.FormatMoney(a.InBTC),
|
||||
a.InUSD.Div(totalUSD).FormatPercentage(2),
|
||||
),
|
||||
Short: false,
|
||||
})
|
||||
} else {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: a.Currency,
|
||||
Value: fmt.Sprintf("%s", a.Total.String()),
|
||||
Short: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return slack.Attachment{
|
||||
Title: fmt.Sprintf("Net Asset Value %s (≈ %s)",
|
||||
USD.FormatMoney(totalUSD),
|
||||
BTC.FormatMoney(totalBTC),
|
||||
),
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
150
pkg/types/balance.go
Normal file
150
pkg/types/balance.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
type Balance struct {
|
||||
Currency string `json:"currency"`
|
||||
Available fixedpoint.Value `json:"available"`
|
||||
Locked fixedpoint.Value `json:"locked,omitempty"`
|
||||
|
||||
// margin related fields
|
||||
Borrowed fixedpoint.Value `json:"borrowed,omitempty"`
|
||||
Interest fixedpoint.Value `json:"interest,omitempty"`
|
||||
|
||||
// NetAsset = (Available + Locked) - Borrowed - Interest
|
||||
NetAsset fixedpoint.Value `json:"net,omitempty"`
|
||||
}
|
||||
|
||||
func (b Balance) Total() fixedpoint.Value {
|
||||
return b.Available.Add(b.Locked)
|
||||
}
|
||||
|
||||
func (b Balance) String() (o string) {
|
||||
o = fmt.Sprintf("%s: %s", b.Currency, b.Available.String())
|
||||
|
||||
if b.Locked.Sign() > 0 {
|
||||
o += fmt.Sprintf(" (locked %v)", b.Locked)
|
||||
}
|
||||
|
||||
if b.Borrowed.Sign() > 0 {
|
||||
o += fmt.Sprintf(" (borrowed: %v)", b.Borrowed)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
|
||||
type BalanceMap map[string]Balance
|
||||
|
||||
func (m BalanceMap) Currencies() (currencies []string) {
|
||||
for _, b := range m {
|
||||
currencies = append(currencies, b.Currency)
|
||||
}
|
||||
return currencies
|
||||
}
|
||||
|
||||
func (m BalanceMap) Add(bm BalanceMap) BalanceMap {
|
||||
var total = BalanceMap{}
|
||||
for _, b := range bm {
|
||||
tb := total[b.Currency]
|
||||
tb.Available = tb.Available.Add(b.Available)
|
||||
tb.Locked = tb.Locked.Add(b.Locked)
|
||||
tb.Borrowed = tb.Borrowed.Add(b.Borrowed)
|
||||
tb.NetAsset = tb.NetAsset.Add(b.NetAsset)
|
||||
tb.Interest = tb.Interest.Add(b.Interest)
|
||||
total[b.Currency] = tb
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (m BalanceMap) String() string {
|
||||
var ss []string
|
||||
for _, b := range m {
|
||||
ss = append(ss, b.String())
|
||||
}
|
||||
|
||||
return "BalanceMap[" + strings.Join(ss, ", ") + "]"
|
||||
}
|
||||
|
||||
func (m BalanceMap) Copy() (d BalanceMap) {
|
||||
d = make(BalanceMap)
|
||||
for c, b := range m {
|
||||
d[c] = b
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// Assets converts balances into assets with the given prices
|
||||
func (m BalanceMap) Assets(prices map[string]fixedpoint.Value, priceTime time.Time) AssetMap {
|
||||
assets := make(AssetMap)
|
||||
btcusdt, hasBtcPrice := prices["BTCUSDT"]
|
||||
for currency, b := range m {
|
||||
if b.Locked.IsZero() && b.Available.IsZero() && b.Borrowed.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
total := b.Available.Add(b.Locked)
|
||||
netAsset := b.NetAsset
|
||||
if netAsset.IsZero() {
|
||||
netAsset = total.Sub(b.Borrowed)
|
||||
}
|
||||
|
||||
asset := Asset{
|
||||
Currency: currency,
|
||||
Total: total,
|
||||
Time: priceTime,
|
||||
Locked: b.Locked,
|
||||
Available: b.Available,
|
||||
Borrowed: b.Borrowed,
|
||||
NetAsset: netAsset,
|
||||
}
|
||||
|
||||
usdMarkets := []string{currency + "USDT", currency + "USDC", currency + "USD", "USDT" + currency}
|
||||
for _, market := range usdMarkets {
|
||||
if usdPrice, ok := prices[market]; ok {
|
||||
// this includes USDT, USD, USDC and so on
|
||||
if strings.HasPrefix(market, "USD") {
|
||||
if !asset.Total.IsZero() {
|
||||
asset.InUSD = asset.Total.Div(usdPrice)
|
||||
}
|
||||
asset.PriceInUSD = usdPrice
|
||||
} else {
|
||||
if !asset.Total.IsZero() {
|
||||
asset.InUSD = asset.Total.Mul(usdPrice)
|
||||
}
|
||||
asset.PriceInUSD = fixedpoint.One.Div(usdPrice)
|
||||
}
|
||||
|
||||
if hasBtcPrice && !asset.InUSD.IsZero() {
|
||||
asset.InBTC = asset.InUSD.Div(btcusdt)
|
||||
}
|
||||
}
|
||||
assets[currency] = asset
|
||||
}
|
||||
}
|
||||
|
||||
return assets
|
||||
}
|
||||
|
||||
func (m BalanceMap) Print() {
|
||||
for _, balance := range m {
|
||||
if balance.Available.IsZero() && balance.Locked.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
if balance.Locked.Sign() > 0 {
|
||||
logrus.Infof(" %s: %v (locked %v)", balance.Currency, balance.Available, balance.Locked)
|
||||
} else {
|
||||
logrus.Infof(" %s: %v", balance.Currency, balance.Available)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user