mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
Kucoin: Margin subscription fix and improvements (#1761)
As a GCT user with spot and margin assets enabled, but only margin asset enabled websocket subscriptions, I should still get subscriptions for all the pairs in margin which are also in spot Currently it only works when spot subscriptions are enabled. Otherwise the spot pairs are ignored. Fixes #1755
This commit is contained in:
@@ -41,6 +41,9 @@ Default Authenticated Subscriptions:
|
||||
|
||||
Subscriptions are subject to enabled assets and pairs.
|
||||
|
||||
Margin subscriptions for ticker, orderbook and All trades are merged into Spot subscriptions because duplicates are not allowed,
|
||||
unless Spot subscription does not exist, i.e. Spot asset not enabled, or subscription configured only for Margin
|
||||
|
||||
Limitations:
|
||||
- 100 symbols per subscription
|
||||
- 300 symbols per connection
|
||||
|
||||
@@ -2266,52 +2266,53 @@ func TestGenerateSubscriptions(t *testing.T) {
|
||||
// Only in Spot: BTC-USDT, ETH-USDT
|
||||
// In Both: ETH-BTC, LTC-USDT
|
||||
// Only in Margin: TRX-BTC, SOL-USDC
|
||||
subPairs := currency.Pairs{}
|
||||
for _, pp := range [][]string{
|
||||
{"BTC", "USDT", "-"}, {"ETH", "BTC", "-"}, {"ETH", "USDT", "-"}, {"LTC", "USDT", "-"}, // Spot
|
||||
{"ETH", "BTC", "-"}, {"LTC", "USDT", "-"}, {"SOL", "USDC", "-"}, {"TRX", "BTC", "-"}, // Margin
|
||||
{"ETH", "USDCM", ""}, {"SOL", "USDTM", ""}, {"XBT", "USDCM", ""}, // Futures
|
||||
pairs := map[string]currency.Pairs{}
|
||||
for a, ss := range map[string][]string{
|
||||
"spot": {"BTC-USDT", "ETH-BTC", "ETH-USDT", "LTC-USDT"},
|
||||
"margin": {"ETH-BTC", "LTC-USDT", "SOL-USDC", "TRX-BTC"},
|
||||
"futures": {"ETHUSDCM", "SOLUSDTM", "XBTUSDCM"},
|
||||
} {
|
||||
subPairs = append(subPairs, currency.NewPairWithDelimiter(pp[0], pp[1], pp[2]))
|
||||
for _, s := range ss {
|
||||
p, err := currency.NewPairFromString(s)
|
||||
require.NoError(t, err, "NewPairFromString must not error")
|
||||
pairs[a] = pairs[a].Add(p)
|
||||
}
|
||||
}
|
||||
pairs["both"] = common.SortStrings(pairs["spot"].Add(pairs["margin"]...))
|
||||
|
||||
exp := subscription.List{
|
||||
{Channel: subscription.TickerChannel, Asset: asset.Spot, Pairs: subPairs[0:4], QualifiedChannel: "/market/ticker:" + subPairs[0:4].Join()},
|
||||
{Channel: subscription.TickerChannel, Asset: asset.Margin, Pairs: subPairs[6:8], QualifiedChannel: "/market/ticker:" + subPairs[6:8].Join()},
|
||||
{Channel: subscription.TickerChannel, Asset: asset.Futures, Pairs: subPairs[8:], QualifiedChannel: "/contractMarket/tickerV2:" + subPairs[8:].Join()},
|
||||
{Channel: subscription.OrderbookChannel, Asset: asset.Spot, Pairs: subPairs[0:4], QualifiedChannel: "/spotMarket/level2Depth5:" + subPairs[0:4].Join(),
|
||||
{Channel: subscription.TickerChannel, Asset: asset.Spot, Pairs: pairs["both"], QualifiedChannel: "/market/ticker:" + pairs["both"].Join()},
|
||||
{Channel: subscription.TickerChannel, Asset: asset.Futures, Pairs: pairs["futures"], QualifiedChannel: "/contractMarket/tickerV2:" + pairs["futures"].Join()},
|
||||
{Channel: subscription.OrderbookChannel, Asset: asset.Spot, Pairs: pairs["both"], QualifiedChannel: "/spotMarket/level2Depth5:" + pairs["both"].Join(),
|
||||
Interval: kline.HundredMilliseconds},
|
||||
{Channel: subscription.OrderbookChannel, Asset: asset.Margin, Pairs: subPairs[6:8], QualifiedChannel: "/spotMarket/level2Depth5:" + subPairs[6:8].Join(),
|
||||
{Channel: subscription.OrderbookChannel, Asset: asset.Futures, Pairs: pairs["futures"], QualifiedChannel: "/contractMarket/level2Depth5:" + pairs["futures"].Join(),
|
||||
Interval: kline.HundredMilliseconds},
|
||||
{Channel: subscription.OrderbookChannel, Asset: asset.Futures, Pairs: subPairs[8:], QualifiedChannel: "/contractMarket/level2Depth5:" + subPairs[8:].Join(),
|
||||
Interval: kline.HundredMilliseconds},
|
||||
{Channel: subscription.AllTradesChannel, Asset: asset.Spot, Pairs: subPairs[0:4], QualifiedChannel: "/market/match:" + subPairs[0:4].Join()},
|
||||
{Channel: subscription.AllTradesChannel, Asset: asset.Margin, Pairs: subPairs[6:8], QualifiedChannel: "/market/match:" + subPairs[6:8].Join()},
|
||||
{Channel: subscription.AllTradesChannel, Asset: asset.Spot, Pairs: pairs["both"], QualifiedChannel: "/market/match:" + pairs["both"].Join()},
|
||||
}
|
||||
|
||||
subs, err := ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions must not error")
|
||||
require.NoError(t, err, "generateSubscriptions must not error")
|
||||
testsubs.EqualLists(t, exp, subs)
|
||||
|
||||
ku.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
|
||||
var loanPairs currency.Pairs
|
||||
loanCurrs := common.SortStrings(subPairs[0:8].GetCurrencies())
|
||||
loanCurrs := common.SortStrings(pairs["both"].GetCurrencies())
|
||||
for _, c := range loanCurrs {
|
||||
loanPairs = append(loanPairs, currency.Pair{Base: c})
|
||||
}
|
||||
|
||||
exp = append(exp, subscription.List{
|
||||
{Asset: asset.Futures, Channel: futuresTradeOrderChannel, QualifiedChannel: "/contractMarket/tradeOrders", Pairs: subPairs[8:]},
|
||||
{Asset: asset.Futures, Channel: futuresStopOrdersLifecycleEventChannel, QualifiedChannel: "/contractMarket/advancedOrders", Pairs: subPairs[8:]},
|
||||
{Asset: asset.Futures, Channel: futuresAccountBalanceEventChannel, QualifiedChannel: "/contractAccount/wallet", Pairs: subPairs[8:]},
|
||||
{Asset: asset.Margin, Channel: marginPositionChannel, QualifiedChannel: "/margin/position", Pairs: subPairs[4:8]},
|
||||
{Asset: asset.Futures, Channel: futuresTradeOrderChannel, QualifiedChannel: "/contractMarket/tradeOrders", Pairs: pairs["futures"]},
|
||||
{Asset: asset.Futures, Channel: futuresStopOrdersLifecycleEventChannel, QualifiedChannel: "/contractMarket/advancedOrders", Pairs: pairs["futures"]},
|
||||
{Asset: asset.Futures, Channel: futuresAccountBalanceEventChannel, QualifiedChannel: "/contractAccount/wallet", Pairs: pairs["futures"]},
|
||||
{Asset: asset.Margin, Channel: marginPositionChannel, QualifiedChannel: "/margin/position", Pairs: pairs["margin"]},
|
||||
{Asset: asset.Margin, Channel: marginLoanChannel, QualifiedChannel: "/margin/loan:" + loanCurrs.Join(), Pairs: loanPairs},
|
||||
{Channel: accountBalanceChannel, QualifiedChannel: "/account/balance"},
|
||||
}...)
|
||||
|
||||
subs, err = ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions with Auth must not error")
|
||||
require.NoError(t, err, "generateSubscriptions with Auth must not error")
|
||||
testsubs.EqualLists(t, exp, subs)
|
||||
}
|
||||
|
||||
@@ -2320,21 +2321,16 @@ func TestGenerateTickerAllSub(t *testing.T) {
|
||||
|
||||
ku := testInstance(t) //nolint:govet // Intentional shadow to avoid future copy/paste mistakes
|
||||
avail, err := ku.GetAvailablePairs(asset.Spot)
|
||||
assert.NoError(t, err, "GetAvailablePairs must not error")
|
||||
for i := 0; i <= 10; i++ {
|
||||
err = ku.CurrencyPairs.EnablePair(asset.Spot, avail[i])
|
||||
assert.NoError(t, common.ExcludeError(err, currency.ErrPairAlreadyEnabled), "EnablePair must not error")
|
||||
}
|
||||
|
||||
enabled, err := ku.GetEnabledPairs(asset.Spot)
|
||||
assert.NoError(t, err, "GetEnabledPairs must not error")
|
||||
require.NoError(t, err, "GetAvailablePairs must not error")
|
||||
err = ku.CurrencyPairs.StorePairs(asset.Spot, avail[:11], true)
|
||||
require.NoError(t, err, "StorePairs must not error")
|
||||
|
||||
ku.Features.Subscriptions = subscription.List{{Channel: subscription.TickerChannel, Asset: asset.Spot}}
|
||||
exp := subscription.List{
|
||||
{Channel: subscription.TickerChannel, Asset: asset.Spot, QualifiedChannel: "/market/ticker:all", Pairs: enabled},
|
||||
{Channel: subscription.TickerChannel, Asset: asset.Spot, QualifiedChannel: "/market/ticker:all", Pairs: avail[:11]},
|
||||
}
|
||||
subs, err := ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions with Auth must not error")
|
||||
require.NoError(t, err, "generateSubscriptions with Auth must not error")
|
||||
testsubs.EqualLists(t, exp, subs)
|
||||
}
|
||||
|
||||
@@ -2353,7 +2349,7 @@ func TestGenerateOtherSubscriptions(t *testing.T) {
|
||||
ku.Features.Subscriptions = subscription.List{s}
|
||||
got, err := ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions should not error")
|
||||
assert.Len(t, got, 1, "Should generate just one sub")
|
||||
require.Len(t, got, 1, "Must generate just one sub")
|
||||
assert.NotEmpty(t, got[0].QualifiedChannel, "Qualified Channel should not be empty")
|
||||
if got[0].Channel == subscription.CandlesChannel {
|
||||
assert.Equal(t, "/market/candles:BTC-USDT_4hour,ETH-BTC_4hour,ETH-USDT_4hour,LTC-USDT_4hour", got[0].QualifiedChannel, "QualifiedChannel should be correct")
|
||||
@@ -2361,6 +2357,43 @@ func TestGenerateOtherSubscriptions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGenerateMarginSubscriptions is a regression test for #1755 and ensures margin subscriptions work without spot subs
|
||||
func TestGenerateMarginSubscriptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ku := testInstance(t) //nolint:govet // Intentional shadow to avoid future copy/paste mistakes
|
||||
|
||||
avail, err := ku.GetAvailablePairs(asset.Spot)
|
||||
require.NoError(t, err, "GetAvailablePairs must not error storing spot pairs")
|
||||
avail = common.SortStrings(avail)
|
||||
err = ku.CurrencyPairs.StorePairs(asset.Margin, avail[:6], true)
|
||||
require.NoError(t, err, "StorePairs must not error storing margin pairs")
|
||||
err = ku.CurrencyPairs.StorePairs(asset.Spot, avail[:3], true)
|
||||
require.NoError(t, err, "StorePairs must not error storing spot pairs")
|
||||
|
||||
ku.Features.Subscriptions = subscription.List{{Channel: subscription.TickerChannel, Asset: asset.Margin}}
|
||||
subs, err := ku.Features.Subscriptions.ExpandTemplates(ku)
|
||||
require.NoError(t, err, "ExpandTemplates must not error")
|
||||
require.Len(t, subs, 1, "Must generate just one sub")
|
||||
assert.Equal(t, asset.Margin, subs[0].Asset, "Asset should be correct")
|
||||
assert.Equal(t, "/market/ticker:"+avail[:6].Join(), subs[0].QualifiedChannel, "QualifiedChannel should be correct")
|
||||
|
||||
require.NoError(t, ku.CurrencyPairs.SetAssetEnabled(asset.Margin, false), "SetAssetEnabled Spot must not error")
|
||||
require.NoError(t, err, "SetAssetEnabled must not error")
|
||||
ku.Features.Subscriptions = subscription.List{{Channel: subscription.TickerChannel, Asset: asset.All}}
|
||||
subs, err = ku.Features.Subscriptions.ExpandTemplates(ku)
|
||||
require.NoError(t, err, "mergeMarginPairs must not cause errAssetRecords by adding an empty asset when Margin is disabled")
|
||||
require.NotEmpty(t, subs, "ExpandTemplates must return some subs")
|
||||
|
||||
require.NoError(t, ku.CurrencyPairs.SetAssetEnabled(asset.Margin, true), "SetAssetEnabled Margin must not error")
|
||||
require.NoError(t, ku.CurrencyPairs.SetAssetEnabled(asset.Spot, false), "SetAssetEnabled Spot must not error")
|
||||
require.NoError(t, ku.CurrencyPairs.SetAssetEnabled(asset.Futures, false), "SetAssetEnabled Futures must not error")
|
||||
ku.Features.Subscriptions = subscription.List{{Channel: subscription.TickerChannel, Asset: asset.All}}
|
||||
subs, err = ku.Features.Subscriptions.ExpandTemplates(ku)
|
||||
require.NoError(t, err, "mergeMarginPairs must not cause errAssetRecords by adding an empty asset when Spot is disabled")
|
||||
require.NotEmpty(t, subs, "ExpandTemplates must return some subs")
|
||||
}
|
||||
|
||||
// TestCheckSubscriptions ensures checkSubscriptions upgrades user config correctly
|
||||
func TestCheckSubscriptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -2934,8 +2967,8 @@ func TestSubscribeBatches(t *testing.T) {
|
||||
}
|
||||
|
||||
subs, err := ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions must not error")
|
||||
assert.Len(t, subs, len(ku.Features.Subscriptions), "Must generate batched subscriptions")
|
||||
require.NoError(t, err, "generateSubscriptions must not error")
|
||||
require.Len(t, subs, len(ku.Features.Subscriptions), "Must generate batched subscriptions")
|
||||
|
||||
err = ku.Subscribe(subs)
|
||||
assert.NoError(t, err, "Subscribe to small batches should not error")
|
||||
@@ -2953,32 +2986,32 @@ func TestSubscribeBatchLimit(t *testing.T) {
|
||||
testexch.SetupWs(t, ku)
|
||||
|
||||
avail, err := ku.GetAvailablePairs(asset.Spot)
|
||||
assert.NoError(t, err, "GetAvailablePairs must not error")
|
||||
require.NoError(t, err, "GetAvailablePairs must not error")
|
||||
|
||||
err = ku.CurrencyPairs.StorePairs(asset.Spot, avail[:299], true)
|
||||
assert.NoError(t, err, "StorePairs must not error")
|
||||
require.NoError(t, err, "StorePairs must not error")
|
||||
|
||||
ku.Features.Subscriptions = subscription.List{{Asset: asset.Spot, Channel: subscription.AllTradesChannel}}
|
||||
subs, err := ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions must not error")
|
||||
assert.Len(t, subs, 3, "Must get 3 subs")
|
||||
require.NoError(t, err, "generateSubscriptions must not error")
|
||||
require.Len(t, subs, 3, "Must get 3 subs")
|
||||
|
||||
err = ku.Subscribe(subs)
|
||||
assert.NoError(t, err, "Subscribe must not error")
|
||||
require.NoError(t, err, "Subscribe must not error")
|
||||
|
||||
err = ku.Unsubscribe(subs)
|
||||
assert.NoError(t, err, "Unsubscribe must not error")
|
||||
require.NoError(t, err, "Unsubscribe must not error")
|
||||
|
||||
err = ku.CurrencyPairs.StorePairs(asset.Spot, avail[:320], true)
|
||||
assert.NoError(t, err, "StorePairs must not error")
|
||||
require.NoError(t, err, "StorePairs must not error")
|
||||
|
||||
ku.Features.Subscriptions = subscription.List{{Asset: asset.Spot, Channel: subscription.AllTradesChannel}}
|
||||
subs, err = ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions must not error")
|
||||
assert.Len(t, subs, 4, "Must get 4 subs")
|
||||
require.NoError(t, err, "generateSubscriptions must not error")
|
||||
require.Len(t, subs, 4, "Must get 4 subs")
|
||||
|
||||
err = ku.Subscribe(subs)
|
||||
assert.ErrorContains(t, err, "exceed max subscription count limitation of 300 per session", "Subscribe to MarketSnapshot must error above connection symbol limit")
|
||||
assert.ErrorContains(t, err, "exceed max subscription count limitation of 300 per session", "Subscribe to MarketSnapshot should error above connection symbol limit")
|
||||
}
|
||||
|
||||
func TestSubscribeTickerAll(t *testing.T) {
|
||||
@@ -2989,20 +3022,20 @@ func TestSubscribeTickerAll(t *testing.T) {
|
||||
testexch.SetupWs(t, ku)
|
||||
|
||||
avail, err := ku.GetAvailablePairs(asset.Spot)
|
||||
assert.NoError(t, err, "GetAvailablePairs must not error")
|
||||
require.NoError(t, err, "GetAvailablePairs must not error")
|
||||
|
||||
err = ku.CurrencyPairs.StorePairs(asset.Spot, avail[:500], true)
|
||||
assert.NoError(t, err, "StorePairs must not error")
|
||||
require.NoError(t, err, "StorePairs must not error")
|
||||
|
||||
ku.Features.Subscriptions = subscription.List{{Asset: asset.Spot, Channel: subscription.TickerChannel}}
|
||||
|
||||
subs, err := ku.generateSubscriptions()
|
||||
assert.NoError(t, err, "generateSubscriptions must not error")
|
||||
assert.Len(t, subs, 1, "Must generate one subscription")
|
||||
assert.Equal(t, "/market/ticker:all", subs[0].QualifiedChannel, "QualifiedChannel must be correct")
|
||||
require.NoError(t, err, "generateSubscriptions must not error")
|
||||
require.Len(t, subs, 1, "Must generate one subscription")
|
||||
assert.Equal(t, "/market/ticker:all", subs[0].QualifiedChannel, "QualifiedChannel should be correct")
|
||||
|
||||
err = ku.Subscribe(subs)
|
||||
assert.NoError(t, err, "Subscribe to must not error")
|
||||
assert.NoError(t, err, "Subscribe to should not error")
|
||||
}
|
||||
|
||||
func TestSeedLocalCache(t *testing.T) {
|
||||
@@ -3957,7 +3990,7 @@ func TestGetCurrencyTradeURL(t *testing.T) {
|
||||
func testInstance(tb testing.TB) *Kucoin {
|
||||
tb.Helper()
|
||||
kucoin := new(Kucoin)
|
||||
assert.NoError(tb, testexch.Setup(kucoin), "Test instance Setup must not error")
|
||||
require.NoError(tb, testexch.Setup(kucoin), "Test instance Setup must not error")
|
||||
kucoin.obm = &orderbookManager{
|
||||
state: make(map[currency.Code]map[currency.Code]map[asset.Item]*update),
|
||||
jobs: make(chan job, maxWSOrderbookJobs),
|
||||
|
||||
@@ -1070,11 +1070,8 @@ func (ku *Kucoin) generateSubscriptions() (subscription.List, error) {
|
||||
func (ku *Kucoin) GetSubscriptionTemplate(_ *subscription.Subscription) (*template.Template, error) {
|
||||
return template.New("master.tmpl").
|
||||
Funcs(template.FuncMap{
|
||||
"channelName": channelName,
|
||||
"removeSpotFromMargin": func(s *subscription.Subscription, ap map[asset.Item]currency.Pairs) string {
|
||||
spotPairs, _ := ku.GetEnabledPairs(asset.Spot)
|
||||
return removeSpotFromMargin(s, ap, spotPairs)
|
||||
},
|
||||
"channelName": channelName,
|
||||
"mergeMarginPairs": ku.mergeMarginPairs,
|
||||
"isCurrencyChannel": isCurrencyChannel,
|
||||
"isSymbolChannel": isSymbolChannel,
|
||||
"channelInterval": channelInterval,
|
||||
@@ -1686,13 +1683,45 @@ func channelName(s *subscription.Subscription, a asset.Item) string {
|
||||
return s.Channel
|
||||
}
|
||||
|
||||
// removeSpotFromMargin removes spot pairs from margin pairs in the supplied AssetPairs map for subscriptions to non-margin endpoints
|
||||
func removeSpotFromMargin(s *subscription.Subscription, ap map[asset.Item]currency.Pairs, spotPairs currency.Pairs) string {
|
||||
// mergeMarginPairs merges margin pairs into spot pairs for shared subs (ticker, orderbook, etc) if Spot asset and sub are enabled,
|
||||
// because Kucoin errors on duplicate pairs in separate subs, and doesn't have separate subs for spot and margin
|
||||
func (ku *Kucoin) mergeMarginPairs(s *subscription.Subscription, ap map[asset.Item]currency.Pairs) string {
|
||||
if strings.HasPrefix(s.Channel, "/margin") {
|
||||
return ""
|
||||
}
|
||||
if p, ok := ap[asset.Margin]; ok {
|
||||
ap[asset.Margin] = p.Remove(spotPairs...)
|
||||
wantKey := &subscription.IgnoringAssetKey{Subscription: s}
|
||||
switch s.Asset {
|
||||
case asset.All:
|
||||
_, marginEnabled := ap[asset.Margin]
|
||||
_, spotEnabled := ap[asset.Spot]
|
||||
if marginEnabled && spotEnabled {
|
||||
marginPairs, _ := ku.GetEnabledPairs(asset.Margin)
|
||||
ap[asset.Spot] = common.SortStrings(ap[asset.Spot].Add(marginPairs...))
|
||||
ap[asset.Margin] = currency.Pairs{}
|
||||
}
|
||||
case asset.Spot:
|
||||
// If there's a margin sub then we should merge the pairs into spot
|
||||
hasMarginSub := slices.ContainsFunc(ku.Features.Subscriptions, func(sB *subscription.Subscription) bool {
|
||||
if sB.Asset != asset.Margin && sB.Asset != asset.All {
|
||||
return false
|
||||
}
|
||||
return wantKey.Match(&subscription.IgnoringAssetKey{Subscription: sB})
|
||||
})
|
||||
if hasMarginSub {
|
||||
marginPairs, _ := ku.GetEnabledPairs(asset.Margin)
|
||||
ap[asset.Spot] = common.SortStrings(ap[asset.Spot].Add(marginPairs...))
|
||||
}
|
||||
case asset.Margin:
|
||||
// If there's a spot sub, all margin pairs are already merged, so empty the margin pairs
|
||||
hasSpotSub := slices.ContainsFunc(ku.Features.Subscriptions, func(sB *subscription.Subscription) bool {
|
||||
if sB.Asset != asset.Spot && sB.Asset != asset.All {
|
||||
return false
|
||||
}
|
||||
return wantKey.Match(&subscription.IgnoringAssetKey{Subscription: sB})
|
||||
})
|
||||
if hasSpotSub {
|
||||
ap[asset.Margin] = currency.Pairs{}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1749,27 +1778,27 @@ func joinPairsWithInterval(b currency.Pairs, s *subscription.Subscription) strin
|
||||
}
|
||||
|
||||
const subTplText = `
|
||||
{{- removeSpotFromMargin $.S $.AssetPairs -}}
|
||||
{{- mergeMarginPairs $.S $.AssetPairs }}
|
||||
{{- if isCurrencyChannel $.S }}
|
||||
{{ channelName $.S $.S.Asset -}} : {{- (assetCurrencies $.S $.AssetPairs).Join -}}
|
||||
{{- channelName $.S $.S.Asset -}} : {{- (assetCurrencies $.S $.AssetPairs).Join }}
|
||||
{{- else if isSymbolChannel $.S }}
|
||||
{{ range $asset, $pairs := $.AssetPairs }}
|
||||
{{- range $asset, $pairs := $.AssetPairs }}
|
||||
{{- with $name := channelName $.S $asset }}
|
||||
{{- if and (eq $name "/market/ticker") (gt (len $pairs) 10) -}}
|
||||
{{- if and (eq $name "/market/ticker") (gt (len $pairs) 10) }}
|
||||
{{- $name -}} :all
|
||||
{{- with $i := channelInterval $.S -}}_{{- $i -}}{{- end -}}
|
||||
{{- $.BatchSize -}} {{ len $pairs }}
|
||||
{{- else -}}
|
||||
{{- range $b := batch $pairs 100 -}}
|
||||
{{- $name -}} : {{- joinPairsWithInterval $b $.S -}}
|
||||
{{ $.PairSeparator }}
|
||||
{{- end -}}
|
||||
{{- with $i := channelInterval $.S }}_{{ $i }}{{ end }}
|
||||
{{- $.BatchSize }} {{- len $pairs }}
|
||||
{{- else }}
|
||||
{{- range $b := batch $pairs 100 }}
|
||||
{{- $name -}} : {{- joinPairsWithInterval $b $.S }}
|
||||
{{- $.PairSeparator }}
|
||||
{{- end }}
|
||||
{{- $.BatchSize -}} 100
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ $.AssetSeparator }}
|
||||
{{- $.AssetSeparator }}
|
||||
{{- end }}
|
||||
{{- else -}}
|
||||
{{ channelName $.S $.S.Asset }}
|
||||
{{- else }}
|
||||
{{- channelName $.S $.S.Asset }}
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user