diff --git a/frbnc/account.go b/frbnc/account.go index 6b3323e..3e7f3e9 100644 --- a/frbnc/account.go +++ b/frbnc/account.go @@ -79,6 +79,7 @@ func (a *Account) MarginRatio() (ratio, margin, totalPos float64) { acct := a.Futures margin = acct.TotalMarginBalance + // ETHBTC需要单独考虑 quote asset是USDC的无伤大雅 for _, pos := range acct.Positions { totalPos += pos.PositionInitialMargin * pos.Leverage } @@ -92,6 +93,10 @@ func (a *Account) MarginRatio() (ratio, margin, totalPos float64) { return } +func (a *Account) FuturesMMR() (mmr float64) { + return a.Futures.TotalMaintMargin / a.Futures.TotalMarginBalance +} + func QueryAccount(user *bnc.User) (resp *resty.Response, acct *Account, err cex.RequestError) { resp, spot, err := user.SpotAccount() if err.IsNotNil() { diff --git a/frbnc/analysis.go b/frbnc/analysis.go index c12db3a..62c2495 100644 --- a/frbnc/analysis.go +++ b/frbnc/analysis.go @@ -3,7 +3,7 @@ package frbnc import ( "errors" "math" - "sort" + "slices" "github.com/dwdwow/cex/bnc" "github.com/dwdwow/mathy" @@ -93,6 +93,7 @@ func AnalyzeFuturesUsdt(acct *Account) (analysis FuturesUsdtAnalysis) { analysis.Err = errors.New("can not get usdt futures wallet balance") return } + // -5000 needs modification, it is a trigger value if usdt.WalletBalance > -5000 { return } @@ -129,8 +130,8 @@ func AnalyzeMarginableSpotBals(acct *Account) (bals []MarginableSpotBal) { Err: err, }) } - sort.Slice(bals, func(i, j int) bool { - return bals[i].MarginAvailable > bals[j].MarginAvailable + slices.SortFunc(bals, func(i, j MarginableSpotBal) int { + return int(math.Copysign(1, j.MarginAvailable-i.MarginAvailable)) }) return } diff --git a/frbnc/main.go b/frbnc/main.go index b5a8978..31e0118 100644 --- a/frbnc/main.go +++ b/frbnc/main.go @@ -7,7 +7,6 @@ import ( "math" "os" "slices" - "sort" "sync" "time" @@ -46,8 +45,7 @@ func NewMain(user *bnc.User, logger *slog.Logger) (*Main, error) { func (m *Main) wait() { suber := m.acctWatcher.Sub() - for { - acct := <-suber + for acct := range suber { if acct.Err != nil { m.logger.Error("Receive Account Error", "err", acct.Err) continue @@ -64,8 +62,7 @@ func (m *Main) handle(acct *Account) { m.handleRedundant(acct) - analysis := AnalyzeAccount(acct) - m.handleAnalysis(acct, analysis) + m.handleAnalysis(acct, AnalyzeAccount(acct)) } func (m *Main) handleRedundant(acct *Account) { @@ -97,7 +94,7 @@ func (m *Main) handleLowLtvOrds(acct *Account) { } func (m *Main) adjustLowRiskFutureAccount(acct *Account) { - marginRatio, marginValue, totalPos := acct.MarginRatio() + _, marginValue, totalPos := acct.MarginRatio() var marginGap float64 @@ -105,8 +102,7 @@ func (m *Main) adjustLowRiskFutureAccount(acct *Account) { m.logger.Info("Future Total Position Is 0") return } else { - marginRatio = marginValue / totalPos - marginGap = totalPos * math.Abs(marginRatio-middleFuturesAccountMarginRatio) + marginGap = totalPos * math.Abs(marginValue/totalPos-middleFuturesAccountMarginRatio) } remainMarginGap := marginGap @@ -222,12 +218,12 @@ func (m *Main) ClassifyLoanOrds(acct *Account) (lowLtvOrds, highLtvOrds []bnc.Cr } } - sort.Slice(lowLtvOrds, func(i, j int) bool { - return lowLtvOrds[i].CurrentLTV < lowLtvOrds[j].CurrentLTV + slices.SortFunc(lowLtvOrds, func(i, j bnc.CryptoLoanFlexibleOngoingOrder) int { + return int(math.Copysign(1, i.CurrentLTV-j.CurrentLTV)) }) - sort.Slice(highLtvOrds, func(i, j int) bool { - return highLtvOrds[i].CurrentLTV > highLtvOrds[j].CurrentLTV + slices.SortFunc(highLtvOrds, func(i, j bnc.CryptoLoanFlexibleOngoingOrder) int { + return int(math.Copysign(1, j.CurrentLTV-i.CurrentLTV)) }) //var lowCollaterals, highCollaterals []string @@ -794,12 +790,13 @@ func (m *Main) NewPos(spPair, fuPair cex.Pair, spSide cex.OrderSide, spQty, fuEx } var spTrader, fuTrader cex.MarketTraderFunc - if spSide == cex.OrderSideBuy { + switch spSide { + case cex.OrderSideBuy: spTrader = m.user.NewSpotMarketBuyOrder - fuTrader = m.user.NewFuturesMarketSellOrder - } else if spSide == cex.OrderSideSell { - spTrader = m.user.NewSpotMarketSellOrder fuTrader = m.user.NewFuturesMarketBuyOrder + case cex.OrderSideSell: + spTrader = m.user.NewSpotMarketSellOrder + fuTrader = m.user.NewFuturesMarketSellOrder } logger.Info("Placing Spot Market Order") diff --git a/frbnc/util.go b/frbnc/util.go index 660c1b8..93ca12c 100644 --- a/frbnc/util.go +++ b/frbnc/util.go @@ -15,6 +15,14 @@ func slice2map[T any, S []T](s S, key func(T) string) map[string]T { return m } +func slice2mapkv[T1 any, T2 any, S []T1](s S, key func(T1) string, value func(T1) T2) map[string]T2 { + m := make(map[string]T2, len(s)) + for _, t := range s { + m[key(t)] = value(t) + } + return m +} + func fuPrice(pos bnc.FuturesAccountPosition) (price float64, err error) { posAmt := pos.AbsPositionAmt() if posAmt > 0 { @@ -70,10 +78,21 @@ func QueryFuPairs() (map[string]cex.Pair, error) { return queryPairs(bnc.QueryFuturesPairs) } -func mapGetter[U any](m map[string]U, key string) (U, bool) { +func mapGetter[U any](m map[string]U, key string) (v U, ok bool) { if m == nil { - return *new(U), false + return + } + v, ok = m[key] + return +} + +func delSpotAcctAssetZeroValue(acct *VIPPortmarAccount) []bnc.SpotBalance { + spotBals := make([]bnc.SpotBalance, 0, len(acct.spBals)) + for _, bal := range acct.spBals { + if bal.Free == 0 && bal.Locked == 0 { + continue + } + spotBals = append(spotBals, bal) } - v, ok := m[key] - return v, ok + return spotBals } diff --git a/frbnc/vip_portmar_account.go b/frbnc/vip_portmar_account.go index 5fc0350..2b9a091 100644 --- a/frbnc/vip_portmar_account.go +++ b/frbnc/vip_portmar_account.go @@ -14,9 +14,12 @@ const ( ) type VIPPortmarAccountConfig struct { - MinUniMMR float64 - BalancedUniMMR float64 - MaxUniMMR float64 + MinUniMMR float64 + BalancedUniMMR float64 + MaxUniMMR float64 + MinVIPLoanLTV float64 + BalancedVIPLoanLTV float64 + MaxVIPLoanLTV float64 } type VIPPortmarAccount struct { @@ -26,16 +29,32 @@ type VIPPortmarAccount struct { PortmarAccountDetail bnc.PortfolioMarginAccountDetail `json:"portmarAccountDetail"` PortmarAccountInformation bnc.PortfolioMarginAccountInformation `json:"portmarAccountInformation"` - - LoanOrders []bnc.VIPLoanOngoingOrder `json:"loanOrders"` - LoanStatusInfo []bnc.VIPLoanApplicationStatusInfo `json:"loanStatusInfo"` - - CollateralRates []bnc.PortfolioMarginCollateralRate `json:"collateralRates"` - - spBals map[string]bnc.SpotBalance - pmAssets map[string]bnc.PortfolioMarginAccountAsset - pmPoss map[string]bnc.PortfolioMarginAccountPosition - collRates map[string]bnc.PortfolioMarginCollateralRate + PortMarAccountBalances []bnc.PortfolioMarginBalance `json:"portMarAccountBalances"` + PortMarAccountUMPositions []bnc.PortfolioMarginUMPositionRisk `json:"portMarAccountUMPositions"` + PortMarAccountCMPositions []bnc.PortfolioMarginCMPositionRisk `json:"portMarAccountCMPositions"` + PortMarCollateralRates []bnc.PortfolioMarginCollateralRate `json:"portMarCollateralRates"` + + LoanOrders []bnc.VIPLoanOngoingOrder `json:"loanOrders"` + LoanStatusInfo []bnc.VIPLoanApplicationStatusInfo `json:"loanStatusInfo"` + LoanCollateralAssets []bnc.VIPLoanCollateralAsset `json:"loanCollateralAssets"` + + // spot account + spBals map[string]bnc.SpotBalance + // cross margin account + pmBals map[string]bnc.PortfolioMarginBalance + // um futures account + umPoss map[string]bnc.PortfolioMarginUMPositionRisk + // cm futures account + cmPoss map[string]bnc.PortfolioMarginCMPositionRisk + + pmAssets map[string]bnc.PortfolioMarginAccountAsset + pmPoss map[string]bnc.PortfolioMarginAccountPosition + pmCollRateMap map[string]float64 + loanCollAssets map[string]bnc.VIPLoanCollateralAsset + + spPriceMap map[string]float64 + umPriceMap map[string]float64 + cmPriceMap map[string]float64 } func (a VIPPortmarAccount) SpotBalance(asset string) (bnc.SpotBalance, bool) { @@ -50,8 +69,8 @@ func (a VIPPortmarAccount) PortmarPosition(symbol string) (bnc.PortfolioMarginAc return mapGetter(a.pmPoss, symbol) } -func (a VIPPortmarAccount) PortmarCollateralRate(asset string) (bnc.PortfolioMarginCollateralRate, bool) { - return mapGetter(a.collRates, asset) +func (a VIPPortmarAccount) PortmarCollateralRate(asset string) (float64, bool) { + return mapGetter(a.pmCollRateMap, asset) } func QueryVIPPortmarAccount(user *bnc.User) (resp *resty.Response, acct *VIPPortmarAccount, reqErr cex.RequestError) { @@ -59,43 +78,91 @@ func QueryVIPPortmarAccount(user *bnc.User) (resp *resty.Response, acct *VIPPort if reqErr.IsNotNil() { return } + resp, pmDetail, reqErr := user.PortfolioMarginAccountDetail() if reqErr.IsNotNil() { return } + resp, pmInfo, reqErr := user.PortfolioMarginAccountInformation() if reqErr.IsNotNil() { return } + + _, pmBals, reqErr := user.PortfolioMarginBalances() + if reqErr.IsNotNil() { + return + } + + _, pmPoss, reqErr := user.PortfolioMarginPositions("") + if reqErr.IsNotNil() { + return + } + resp, loanOrders, reqErr := user.VIPLoanOngoingOrders("", "", "", "") if reqErr.IsNotNil() { return } + resp, loanStatusInfo, reqErr := user.VIPLoanApplicationStatus() if reqErr.IsNotNil() { return } - collRates, err := bnc.QueryPortfolioMarginCollateralRates() + + pmCollRates, err := bnc.QueryPortfolioMarginCollateralRates() + if err != nil { + reqErr = cex.RequestError{Err: err} + return + } + + spPrices, err := bnc.QuerySpotPrices() + if err != nil { + reqErr = cex.RequestError{Err: err} + return + } + + fuPrices, err := bnc.QueryFuturesPrices() if err != nil { reqErr = cex.RequestError{Err: err} return } acct = &VIPPortmarAccount{ - ApiKey: user.Api().ApiKey, - Time: time.Now().UnixMilli(), - Spot: spot, + ApiKey: user.Api().ApiKey, + Time: time.Now().UnixMilli(), + Spot: spot, + PortmarAccountDetail: pmDetail, PortmarAccountInformation: pmInfo, - LoanOrders: loanOrders.Rows, - LoanStatusInfo: loanStatusInfo.Rows, - CollateralRates: collRates, - spBals: slice2map(spot.Balances, func(balance bnc.SpotBalance) string { return balance.Asset }), - pmAssets: slice2map(pmDetail.Assets, func(asset bnc.PortfolioMarginAccountAsset) string { return asset.Asset }), - pmPoss: slice2map(pmDetail.Positions, func(position bnc.PortfolioMarginAccountPosition) string { return position.Symbol }), - collRates: slice2map(collRates, func(rate bnc.PortfolioMarginCollateralRate) string { + PortMarAccountBalances: pmBals, + PortMarAccountUMPositions: pmPoss, + PortMarAccountCMPositions: []bnc.PortfolioMarginCMPositionRisk{}, + PortMarCollateralRates: pmCollRates, + + LoanOrders: loanOrders.Rows, + LoanStatusInfo: loanStatusInfo.Rows, + LoanCollateralAssets: []bnc.VIPLoanCollateralAsset{}, + + spBals: slice2map(spot.Balances, func(balance bnc.SpotBalance) string { return balance.Asset }), + + pmBals: slice2map(pmBals, func(balance bnc.PortfolioMarginBalance) string { return balance.Asset }), + + umPoss: slice2map(pmPoss, func(position bnc.PortfolioMarginUMPositionRisk) string { return position.Symbol }), + + cmPoss: map[string]bnc.PortfolioMarginCMPositionRisk{}, + + pmAssets: slice2map(pmDetail.Assets, func(asset bnc.PortfolioMarginAccountAsset) string { return asset.Asset }), + pmPoss: slice2map(pmDetail.Positions, func(position bnc.PortfolioMarginAccountPosition) string { return position.Symbol }), + pmCollRateMap: slice2mapkv(pmCollRates, func(rate bnc.PortfolioMarginCollateralRate) string { return rate.Asset + }, func(rate bnc.PortfolioMarginCollateralRate) float64 { + return rate.CollateralRate }), + loanCollAssets: map[string]bnc.VIPLoanCollateralAsset{}, + + spPriceMap: slice2mapkv(spPrices, func(price bnc.SpotPriceTicker) string { return price.Symbol }, func(price bnc.SpotPriceTicker) float64 { return price.Price }), + umPriceMap: slice2mapkv(fuPrices, func(price bnc.FuturesPriceTicker) string { return price.Symbol }, func(price bnc.FuturesPriceTicker) float64 { return price.Price }), + cmPriceMap: map[string]float64{}, } return diff --git a/frbnc/vip_portmar_acct_analy.go b/frbnc/vip_portmar_acct_analy.go index 62ddbe0..141606a 100644 --- a/frbnc/vip_portmar_acct_analy.go +++ b/frbnc/vip_portmar_acct_analy.go @@ -1,9 +1,7 @@ package frbnc -func AnalyseVIPLoan(acct *VIPPortmarAccount) { - //acct.LoanOrders +func AnalyseVIPLoan(acct *VIPPortmarAccount, cfg VIPPortmarAccountConfig) { } func AnalysePortmar(acct *VIPPortmarAccount, cfg VIPPortmarAccountConfig) { - } diff --git a/frbnc/vip_portmar_acct_simple.go b/frbnc/vip_portmar_acct_simple.go index 6eb3f11..bcf7ffd 100644 --- a/frbnc/vip_portmar_acct_simple.go +++ b/frbnc/vip_portmar_acct_simple.go @@ -1,6 +1,15 @@ package frbnc -import "github.com/dwdwow/cex/bnc" +import ( + "fmt" + "log/slog" + "math" + "slices" + "sync" + "time" + + "github.com/dwdwow/cex/bnc" +) type VIPPortmarAcctSimple struct { user *bnc.User @@ -9,21 +18,204 @@ type VIPPortmarAcctSimple struct { } func (v *VIPPortmarAcctSimple) start() { - for { - msg := <-v.chAcct + for msg := range v.chAcct { if msg.Err != nil { + slog.Error(msg.Err.Error()) + continue + } + if time.Now().UnixMilli()-msg.Acct.Time > 2000 { + slog.Error("Account data too old") + continue } + + v.handle(msg.Acct) } } func (v *VIPPortmarAcctSimple) handle(acct *VIPPortmarAccount) { + wg := &sync.WaitGroup{} + wg.Add(2) + go v.handleLowMMR(wg, acct) + go v.handleHighLtv(wg, acct) + wg.Wait() +} + +func (v *VIPPortmarAcctSimple) handleLowMMR(wg *sync.WaitGroup, acct *VIPPortmarAccount) { + defer wg.Done() + + pmAcctInfo := acct.PortmarAccountInformation + + // check uniMMR + if pmAcctInfo.UniMMR > v.cfg.MinUniMMR { + return + } + + needUSDTAmount := pmAcctInfo.AccountMaintMargin*v.cfg.BalancedUniMMR - pmAcctInfo.AccountEquity + + // add pm collateral + if err := addPortmarCollateral(v.user, acct, needUSDTAmount); err != nil { + return + } +} + +func addPortmarCollateral(user *bnc.User, acct *VIPPortmarAccount, needUSDTAmount float64) error { + if needUSDTAmount <= 1e2 { + return fmt.Errorf("needUSDTAmount too small: %f", needUSDTAmount) + } + + var availUSDTAmount float64 + switch len(acct.LoanOrders) { + case 0: + // no loan order, trasfer from spot to portmar + // availUSDTAmount = +Inf is appropriate + // 1e4 here is a random cap, need to be modified later + availUSDTAmount = 1e4 + case 1: + // has only one loan order, simplify the logic + vipLoanOrder := acct.LoanOrders[0] + availUSDTAmount = math.Max((vipLoanOrder.TotalCollateralValueAfterHaircut-vipLoanOrder.LockedCollateralValue)*0.99, 0) + default: + // need implementation + // if has multiple USDT loan orders, same as one order + // if has non-USDT loan orders, return + for _, ord := range acct.LoanOrders { + if ord.LoanCoin != "USDT" { + return fmt.Errorf("unexpected loan coin: %s", ord.LoanCoin) + } + } + vipLoanOrder := acct.LoanOrders[0] + availUSDTAmount = math.Max((vipLoanOrder.TotalCollateralValueAfterHaircut-vipLoanOrder.LockedCollateralValue)*0.99, 0) + } + + // sort spot assets by collateral ratio in descending order + spotBals := delSpotAcctAssetZeroValue(acct) + pmCollateralRateMap := acct.pmCollRateMap + + slices.SortFunc(spotBals, func(i, j bnc.SpotBalance) int { + switch { + case pmCollateralRateMap[i.Asset] > pmCollateralRateMap[j.Asset]: + return -1 + case pmCollateralRateMap[i.Asset] < pmCollateralRateMap[j.Asset]: + return 1 + default: + return 0 + } + }) + + for _, sBal := range spotBals { + collateralRate, ok := pmCollateralRateMap[sBal.Asset] + if !ok || collateralRate <= 0.2 { + continue + } + price := acct.spPriceMap[sBal.Asset+"USDT"] + + // transferCoinAmount needs precision control + transferCoinAmount := math.Min(math.Min(sBal.Free, availUSDTAmount/price), needUSDTAmount/collateralRate/price) + if transferCoinAmount*price < 10 { + continue + } + + time.Sleep(time.Second) + if _, _, err := user.Transfer( + bnc.TransferTypeMainMargin, + sBal.Asset, + transferCoinAmount, + ); err.IsNotNil() { + slog.Error(err.Error()) + continue + } + + availUSDTAmount -= transferCoinAmount * price + needUSDTAmount -= transferCoinAmount * collateralRate * price + + if availUSDTAmount < 10 && needUSDTAmount > 1e3 { + return fmt.Errorf("needUSDTAmount not fully covered: %f", needUSDTAmount) + } + + if needUSDTAmount < 10 { + return nil + } + } + + return nil } -func (v *VIPPortmarAcctSimple) handleLowMMR(acct *VIPPortmarAccount) { - if acct.PortmarAccountInformation.UniMMR > v.cfg.MinUniMMR { +func (v *VIPPortmarAcctSimple) handleHighLtv(wg *sync.WaitGroup, acct *VIPPortmarAccount) { + defer wg.Done() + + //acct.LoanOrders + var vipLoanOrder *bnc.VIPLoanOngoingOrder + switch len(acct.LoanOrders) { + case 0: + // no loan order, do nothing + return + case 1: + // has only one loan order, simplify the logic + vipLoanOrder = &acct.LoanOrders[0] + if vipLoanOrder.LoanCoin != "USDT" { + return + } + default: + for _, ord := range acct.LoanOrders { + if ord.LoanCoin != "USDT" { + return + } + } + vipLoanOrder = &acct.LoanOrders[0] + } + + if vipLoanOrder.CurrentLTV < v.cfg.MaxVIPLoanLTV { return } + + needUSDTAmount := vipLoanOrder.TotalCollateralValueAfterHaircut * (vipLoanOrder.CurrentLTV/v.cfg.BalancedVIPLoanLTV - 1) + + // add sp collateral + if err := addSPCollateral(v.user, acct, needUSDTAmount); err != nil { + slog.Error(err.Error()) + } } -func (v *VIPPortmarAcctSimple) handleHighLtv(acct *VIPPortmarAccount) {} +func addSPCollateral(user *bnc.User, acct *VIPPortmarAccount, needUSDTAmount float64) error { + if needUSDTAmount < 1e2 { + return fmt.Errorf("needUSDTAmount too small: %f", needUSDTAmount) + } + + if acct.PortmarAccountInformation.UniMMR < 10 { + return fmt.Errorf("UniMMR too low: %f", acct.PortmarAccountInformation.UniMMR) + } + + availUSDTAmount := acct.PortmarAccountInformation.VirtualMaxWithdrawAmount + + for _, pBal := range acct.PortMarAccountBalances { + if _, ok := acct.loanCollAssets[pBal.Asset]; !ok { + continue + } + + price := acct.spPriceMap[pBal.Asset+"USDT"] + transferCoinAmount := math.Min(pBal.CrossMarginFree, math.Min(needUSDTAmount, availUSDTAmount)/price) + if transferCoinAmount*price < 10 { + continue + } + + time.Sleep(time.Second) + if _, _, err := user.Transfer(bnc.TransferTypeMarginMain, pBal.Asset, transferCoinAmount); err.IsNotNil() { + slog.Error(err.Error()) + continue + } + + availUSDTAmount -= transferCoinAmount * price + needUSDTAmount -= transferCoinAmount * price + + if availUSDTAmount < 10 && needUSDTAmount > 1e3 { + return fmt.Errorf("needUSDTAmount not fully covered: %f", needUSDTAmount) + } + + if needUSDTAmount < 10 { + return nil + } + } + + return nil +} diff --git a/frbnc/vip_portmar_acct_watcher.go b/frbnc/vip_portmar_acct_watcher.go index 3601223..6fe7f48 100644 --- a/frbnc/vip_portmar_acct_watcher.go +++ b/frbnc/vip_portmar_acct_watcher.go @@ -112,8 +112,7 @@ func (aw *VIPPortmarAcctWatcher) broadcast(acct *VIPPortmarAccount, err error) { aw.muxSubbers.Lock() defer aw.muxSubbers.Unlock() for _, suber := range aw.subbers { - suber := suber - go func() { + go func(suber chan VIPPortmarAcctWatcherMsg) { timer := time.NewTimer(time.Second) defer timer.Stop() select { @@ -121,7 +120,7 @@ func (aw *VIPPortmarAcctWatcher) broadcast(acct *VIPPortmarAccount, err error) { aw.logger.Error("No Reader Of Account Channel Within 1 Second") case suber <- VIPPortmarAcctWatcherMsg{acct, err}: } - }() + }(suber) } } diff --git a/frbnc/vip_portmar_acct_watcher_test.go b/frbnc/vip_portmar_acct_watcher_test.go index b81be13..4bdf0fc 100644 --- a/frbnc/vip_portmar_acct_watcher_test.go +++ b/frbnc/vip_portmar_acct_watcher_test.go @@ -16,10 +16,9 @@ func TestNewVIPPortmarAcctWatcher(t *testing.T) { panic("not ok") } watcher := NewVIPPortmarAcctWatcher(bnc.NewUser(key.ApiKey, key.SecretKey), nil) - err = watcher.Start() - props.PanicIfNotNil(err) - c := watcher.Sub() - for { - props.PrintlnIndent(<-c) + props.PanicIfNotNil(watcher.Start()) + + for e := range watcher.Sub() { + props.PrintlnIndent(e) } } diff --git a/go.mod b/go.mod index c03d05a..7db43bb 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,30 @@ module github.com/dwdwow/frkit go 1.22.1 require ( - github.com/dwdwow/cex v0.0.48 // indirect - github.com/dwdwow/mathy v0.0.1 // indirect - github.com/go-resty/resty/v2 v2.11.0 // indirect + github.com/dwdwow/cex v0.0.48 + github.com/dwdwow/mathy v0.0.1 + github.com/dwdwow/props v0.0.4 + github.com/go-resty/resty/v2 v2.13.1 +) + +require ( + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dwdwow/s2m v0.0.4 // indirect + github.com/dwdwow/spub v0.0.1 // indirect + github.com/dwdwow/ws v0.0.1 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/redis/go-redis/v9 v9.5.1 // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.3 // indirect github.com/shopspring/decimal v1.3.1 // indirect - golang.org/x/net v0.21.0 // indirect + github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca // indirect + github.com/xuri/excelize/v2 v2.8.0 // indirect + github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/text v0.16.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ce265f7..bb06bcc 100644 --- a/go.sum +++ b/go.sum @@ -1,27 +1,68 @@ -github.com/dwdwow/cex v0.0.41 h1:tdoo5J7mVEvorkAa5XNX4BWDRefKSm3NOsQm6epvElE= -github.com/dwdwow/cex v0.0.41/go.mod h1:OWs8mT/zq5ZvzQ20wZFM90zOFMOqpef9LdMrIsgna1Q= -github.com/dwdwow/cex v0.0.42 h1:yUTRAMf/cDLG65l/w5Gq1tlXFUqUE/axRAzorrkl44k= -github.com/dwdwow/cex v0.0.42/go.mod h1:OWs8mT/zq5ZvzQ20wZFM90zOFMOqpef9LdMrIsgna1Q= -github.com/dwdwow/cex v0.0.43 h1:VGCVtNyp5XBSxFkP/dp0iYtbvDhhN18p5FN9GgZDQAE= -github.com/dwdwow/cex v0.0.43/go.mod h1:OWs8mT/zq5ZvzQ20wZFM90zOFMOqpef9LdMrIsgna1Q= -github.com/dwdwow/cex v0.0.44 h1:DnNhPCv91Et78eZUce9rKJqTsAy+Hzxz9igD9eHeSfo= -github.com/dwdwow/cex v0.0.44/go.mod h1:OWs8mT/zq5ZvzQ20wZFM90zOFMOqpef9LdMrIsgna1Q= -github.com/dwdwow/cex v0.0.45 h1:9Ir5tCv5ttJb5xMv+m8DJwYpat3E5dY5SjNMBr8efPo= -github.com/dwdwow/cex v0.0.45/go.mod h1:OWs8mT/zq5ZvzQ20wZFM90zOFMOqpef9LdMrIsgna1Q= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dwdwow/cex v0.0.46 h1:i4k1aRy+HvVEKNLYSrUQIgMVt8kmzuZmu1GPp7BMSkI= github.com/dwdwow/cex v0.0.46/go.mod h1:OWs8mT/zq5ZvzQ20wZFM90zOFMOqpef9LdMrIsgna1Q= github.com/dwdwow/cex v0.0.48 h1:DBvPz9onyrQPRKG1epWmIfLc3c+QXtXtflSOlA8SEfw= github.com/dwdwow/cex v0.0.48/go.mod h1:OWs8mT/zq5ZvzQ20wZFM90zOFMOqpef9LdMrIsgna1Q= github.com/dwdwow/mathy v0.0.1 h1:GWcz6JWyyOw595rsgHzCZ/c/pZWYZH/vNTQry7uizP0= github.com/dwdwow/mathy v0.0.1/go.mod h1:h0IWHqLQ3ys+9xgNstJQeaFTexAgVfldHz+FSAruYCc= -github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= -github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/dwdwow/props v0.0.4 h1:4d/zbuaYRTTHwRmIWNDahjMYUUAMH3WDz84wcQVmvB0= +github.com/dwdwow/props v0.0.4/go.mod h1:dWhnhFKeTzqSbKlYuNzY6QhJv93eE9Yq/t3O61QMybA= +github.com/dwdwow/s2m v0.0.4 h1:ruYKpX9u6PR8+YIggR1H224JTjYhtqaj8Dtl6mqIp+8= +github.com/dwdwow/s2m v0.0.4/go.mod h1:vr1f1kNopaaL0GXAUy36sNuRni4XuFPI1cYJdeg503E= +github.com/dwdwow/spub v0.0.1 h1:ys7aCCCOdeqhi+9nuGlosWmhYqnwKAD0fDHuBeTQ2Vw= +github.com/dwdwow/spub v0.0.1/go.mod h1:E8hS715Cw+A8iZnMz86BxsKiUmuJf9I3snB8UL/rsbE= +github.com/dwdwow/ws v0.0.1 h1:334dDjzffSfka+tjyDC8WJC8yRqKKd59r2VYoQutOtU= +github.com/dwdwow/ws v0.0.1/go.mod h1:DRTcM91FameGbc9KyG7t4/cWtFOBiOWQd64Ph5XHfQ8= +github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g= +github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= +github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= +github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca h1:uvPMDVyP7PXMMioYdyPH+0O+Ta/UO1WFfNYMO3Wz0eg= +github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.8.0 h1:Vd4Qy809fupgp1v7X+nCS/MioeQmYVVzi495UCTqB7U= +github.com/xuri/excelize/v2 v2.8.0/go.mod h1:6iA2edBTKxKbZAa7X5bDhcCg51xdOn1Ar5sfoXRGrQg= +github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a h1:Mw2VNrNNNjDtw68VsEj2+st+oCSn4Uz7vZw6TbhcV1o= +github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= +golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -29,9 +70,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -42,24 +85,35 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=