KucoinSubAccount: Sub-Account Management
KucoinSubAccount: Sub-Account Management
Details
Provides methods for managing sub-accounts under a KuCoin master account. Inherits from KucoinBase.
Purpose and Scope
Sub-Account Creation: Create new sub-accounts with configurable permissions (Spot, Futures, Margin).
Sub-Account Listing: Retrieve paginated summaries of all sub-accounts under the master account.
Balance Queries: Fetch detailed balance breakdowns per sub-account across main, trade, and margin wallets.
Batch Balance Overview: Paginated retrieval of Spot balances for all sub-accounts simultaneously (V2 endpoint).
Usage
All methods require authentication (valid API key, secret, passphrase) from a master account.
Sub-account API keys cannot call these endpoints; only the master account that owns the
sub-accounts has permission. The class supports both synchronous and asynchronous (promise-based)
operation depending on the async flag passed to the constructor.
# Synchronous usage
sub <- KucoinSubAccount$new()
summary <- sub$get_sub_account_list()
# Asynchronous usage
sub_async <- KucoinSubAccount$new(async = TRUE)
coro::async(function() {
summary <- await(sub_async$get_sub_account_list())
print(summary)
})()Super class
kucoin::KucoinBase -> KucoinSubAccount
Methods
Inherited methods
Method add_sub_account()
Add Sub-Account
Creates a new sub-account under the master account. The sub-account is assigned a unique UID and can be granted Spot, Futures, or Margin trading permissions. Only master accounts can call this endpoint.
Workflow
Validation:
accessis matched against"Spot","Futures","Margin".Request: Authenticated POST with sub-account creation parameters in JSON body.
Parsing: Returns a single-row
data.tablewith the newly created sub-account details.
Automated Trading Usage
Isolation: Create dedicated sub-accounts per strategy to isolate funds and risk.
Permission Control: Grant only the needed permission (e.g.,
"Spot") to limit exposure.Remarks: Use
remarksto tag sub-accounts by strategy name for easy identification.
curl
curl --location --request POST 'https://api.kucoin.com/api/v2/sub/user/created' \
--header 'Content-Type: application/json' \
--header 'KC-API-KEY: your-api-key' \
--header 'KC-API-SIGN: your-signature' \
--header 'KC-API-TIMESTAMP: 1729176273859' \
--header 'KC-API-PASSPHRASE: your-passphrase' \
--header 'KC-API-KEY-VERSION: 2' \
--data-raw '{"password":"MyPass123","subName":"mysubacct1","access":"Spot","remarks":"bot-alpha"}'Arguments
passwordCharacter; sub-account password (7-24 chars, must contain both letters and numbers, no special characters).
subNameCharacter; sub-account name (7-32 chars, must start with a letter, letters and numbers only, no spaces).
accessCharacter; permission type:
"Spot","Futures", or"Margin". Validated viarlang::arg_match0().remarksCharacter or NULL; optional descriptive remarks for the sub-account (1-24 chars). Default
NULL.
Returns
data.table (or promise<data.table> if constructed with async = TRUE) with columns:
uid(integer): Unique user ID assigned to the new sub-account.sub_name(character): The sub-account login name.remarks(character): The remarks string (if provided).access(character): Permission granted ("Spot","Futures", or"Margin").
Examples
\dontrun{
sub <- KucoinSubAccount$new()
# Create a Spot sub-account
result <- sub$add_sub_account(
password = "MyPass123",
subName = "botaccount1",
access = "Spot",
remarks = "alpha-strategy"
)
print(result$uid)
print(result$sub_name)
# Create a Futures sub-account without remarks
result <- sub$add_sub_account(
password = "SecurePass99",
subName = "futuresbot1",
access = "Futures"
)
}
Method get_sub_account_list()
Get Sub-Account List Summary
Retrieves a paginated summary of all sub-accounts under the master account.
Automatically handles pagination, fetching up to max_pages pages of results.
A datetime_created column is appended by converting the millisecond timestamp.
Workflow
Pagination: Calls the paginated endpoint, fetching
page_sizerecords per page up tomax_pages.Flattening: Combines all pages into a single
data.tableviaflatten_pages().Timestamp Conversion: Converts
created_at(ms epoch) to POSIXct indatetime_created.
Automated Trading Usage
Inventory Check: Periodically poll sub-account lists to verify all strategy sub-accounts are active.
Audit Trail: Use
datetime_createdto track when sub-accounts were provisioned.Filtering: Post-filter the returned
data.tablebyaccesstype to find all Spot-enabled sub-accounts.
curl
curl --location --request GET 'https://api.kucoin.com/api/v2/sub/user?currentPage=1&pageSize=100' \
--header 'KC-API-KEY: your-api-key' \
--header 'KC-API-SIGN: your-signature' \
--header 'KC-API-TIMESTAMP: 1729176273859' \
--header 'KC-API-PASSPHRASE: your-passphrase' \
--header 'KC-API-KEY-VERSION: 2'JSON Response
{
"code": "200000",
"data": {
"currentPage": 1,
"pageSize": 100,
"totalNum": 2,
"totalPage": 1,
"items": [
{
"userId": "641e7f09df0db80001f1e5ac",
"uid": 169630809,
"subName": "mysubacct1",
"status": 2,
"type": 0,
"access": "Spot",
"remarks": "bot-alpha",
"createdAt": 1679726345000
},
{
"userId": "641e8027df0db80001f1e6bb",
"uid": 169630810,
"subName": "futuresbot1",
"status": 2,
"type": 0,
"access": "Futures",
"remarks": null,
"createdAt": 1679726400000
}
]
}
}Arguments
page_sizeInteger; number of results per page, between 1 and 100. Default
100.max_pagesNumeric; maximum number of pages to retrieve. Use
Inf(default) to fetch all available pages.
Returns
data.table (or promise<data.table> if constructed with async = TRUE) with columns:
user_id(character): Internal user ID string.uid(integer): Numeric user ID for the sub-account.sub_name(character): Sub-account login name.status(integer): Account status code (2 = active).type(integer): Account type code.access(character): Permission type ("Spot","Futures","Margin").remarks(character): Optional remarks string.datetime_created(POSIXct): Creation datetime.
Examples
\dontrun{
sub <- KucoinSubAccount$new()
# Fetch all sub-accounts
all_subs <- sub$get_sub_account_list()
print(all_subs)
# Fetch only first page with 10 results
first_page <- sub$get_sub_account_list(page_size = 10, max_pages = 1)
print(first_page[, .(sub_name, access, datetime_created)])
# Filter for Spot sub-accounts
spot_subs <- all_subs[access == "Spot"]
}
Method get_detail_balance()
Get Sub-Account Detail Balance
Retrieves detailed balance information for a specific sub-account, broken down by account type (main, trade, margin). Each currency held in the sub-account is returned as a separate row with balance, available, and holds amounts.
Workflow
Request: Authenticated GET to the sub-account detail endpoint with the
subUserIdin the URL path.Iteration: Loops over
mainAccounts,tradeAccounts, andmarginAccountsarrays in the response.Assembly: Binds all account entries into a single
data.tablewithaccount_type,sub_user_id, andsub_namecolumns appended.
Automated Trading Usage
Pre-Trade Check: Query a sub-account's available balance before placing orders to avoid insufficient-funds errors.
Risk Monitoring: Periodically check
holdsacross sub-accounts to track capital locked in open orders.Rebalancing: Compare
availablebalances across sub-accounts to decide on internal transfers.
curl
curl --location --request GET 'https://api.kucoin.com/api/v1/sub-accounts/169630809?includeBaseAmount=false' \
--header 'KC-API-KEY: your-api-key' \
--header 'KC-API-SIGN: your-signature' \
--header 'KC-API-TIMESTAMP: 1729176273859' \
--header 'KC-API-PASSPHRASE: your-passphrase' \
--header 'KC-API-KEY-VERSION: 2'JSON Response
{
"code": "200000",
"data": {
"subUserId": "169630809",
"subName": "mysubacct1",
"mainAccounts": [
{
"currency": "USDT",
"balance": "1500.00000000",
"available": "1200.00000000",
"holds": "300.00000000",
"baseCurrency": "USDT",
"baseCurrencyPrice": "1",
"baseAmount": "1500.00000000",
"tag": ""
},
{
"currency": "BTC",
"balance": "0.05000000",
"available": "0.05000000",
"holds": "0.00000000",
"baseCurrency": "USDT",
"baseCurrencyPrice": "96500",
"baseAmount": "4825.00000000",
"tag": ""
}
],
"tradeAccounts": [
{
"currency": "USDT",
"balance": "500.00000000",
"available": "450.00000000",
"holds": "50.00000000",
"baseCurrency": "USDT",
"baseCurrencyPrice": "1",
"baseAmount": "500.00000000",
"tag": ""
}
],
"marginAccounts": [
{
"currency": "ETH",
"balance": "2.50000000",
"available": "2.50000000",
"holds": "0.00000000",
"baseCurrency": "USDT",
"baseCurrencyPrice": "3200",
"baseAmount": "8000.00000000",
"tag": ""
}
]
}
}Arguments
subUserIdCharacter; the sub-account user ID (numeric UID as a string, e.g.,
"169630809").includeBaseAmountLogical; if
TRUE, includes currencies with zero balances in the response. DefaultFALSE.
Returns
data.table (or promise<data.table> if constructed with async = TRUE) with columns:
currency(character): Currency code (e.g.,"USDT","BTC").balance(character): Total balance for that currency.available(character): Available (unfrozen) balance.holds(character): Amount held in open orders or pending withdrawals.base_currency(character): Base currency for value conversion.base_currency_price(character): Price of the currency in base currency terms.base_amount(character): Total value in base currency.tag(character): Currency tag (if applicable).account_type(character): One of"main","trade","margin".sub_user_id(character): The sub-account user ID.sub_name(character): The sub-account name.
Examples
\dontrun{
sub <- KucoinSubAccount$new()
# Get balances for a specific sub-account
balances <- sub$get_detail_balance(subUserId = "169630809")
print(balances)
# Include zero-balance currencies
all_balances <- sub$get_detail_balance(
subUserId = "169630809",
includeBaseAmount = TRUE
)
# Filter for trade account balances only
trade_bal <- balances[account_type == "trade"]
print(trade_bal[, .(currency, available, holds)])
}
Method get_all_spot_balances()
Get Spot Sub-Account List (V2)
Retrieves paginated Spot sub-account balance details for all sub-accounts
at once via the V2 endpoint. Each sub-account's balances are broken down
by account type (main, trade, margin) and combined into a single
data.table with sub_user_id and sub_name identifiers.
Workflow
Pagination: Fetches pages of sub-account balance data via the V2 endpoint,
page_sizerecords per page up tomax_pages.Nested Iteration: For each sub-account in each page, iterates over
mainAccounts,tradeAccounts, andmarginAccounts.Assembly: Binds all entries into a single
data.tablewithaccount_type,sub_user_id, andsub_namecolumns appended.
Automated Trading Usage
Portfolio Dashboard: Aggregate balances across all sub-accounts for a unified portfolio view.
Threshold Alerts: Check
availablebalances across all sub-accounts and trigger alerts when below thresholds.Capital Allocation: Compare balances across sub-accounts to identify idle capital for reallocation.
curl
curl --location --request GET 'https://api.kucoin.com/api/v2/sub-accounts?currentPage=1&pageSize=100' \
--header 'KC-API-KEY: your-api-key' \
--header 'KC-API-SIGN: your-signature' \
--header 'KC-API-TIMESTAMP: 1729176273859' \
--header 'KC-API-PASSPHRASE: your-passphrase' \
--header 'KC-API-KEY-VERSION: 2'JSON Response
{
"code": "200000",
"data": {
"currentPage": 1,
"pageSize": 100,
"totalNum": 2,
"totalPage": 1,
"items": [
{
"subUserId": "169630809",
"subName": "mysubacct1",
"mainAccounts": [
{
"currency": "USDT",
"balance": "1500.00000000",
"available": "1200.00000000",
"holds": "300.00000000",
"baseCurrency": "USDT",
"baseCurrencyPrice": "1",
"baseAmount": "1500.00000000",
"tag": ""
}
],
"tradeAccounts": [
{
"currency": "BTC",
"balance": "0.01000000",
"available": "0.01000000",
"holds": "0.00000000",
"baseCurrency": "USDT",
"baseCurrencyPrice": "96500",
"baseAmount": "965.00000000",
"tag": ""
}
],
"marginAccounts": []
},
{
"subUserId": "169630810",
"subName": "futuresbot1",
"mainAccounts": [
{
"currency": "ETH",
"balance": "5.00000000",
"available": "5.00000000",
"holds": "0.00000000",
"baseCurrency": "USDT",
"baseCurrencyPrice": "3200",
"baseAmount": "16000.00000000",
"tag": ""
}
],
"tradeAccounts": [],
"marginAccounts": []
}
]
}
}Arguments
page_sizeInteger; number of results per page, between 10 and 100. Default
100.max_pagesNumeric; maximum number of pages to retrieve. Use
Inf(default) to fetch all available pages.
Returns
data.table (or promise<data.table> if constructed with async = TRUE) with columns:
sub_user_id(character): The sub-account user ID.sub_name(character): The sub-account name.account_type(character): One of"main","trade","margin".currency(character): Currency code (e.g.,"USDT","BTC","ETH").balance(character): Total balance for that currency.available(character): Available (unfrozen) balance.holds(character): Amount held in open orders or pending withdrawals.base_currency(character): Base currency for value conversion.base_currency_price(character): Price of the currency in base currency terms.base_amount(character): Total value in base currency.tag(character): Currency tag (if applicable).
Examples
\dontrun{
sub <- KucoinSubAccount$new()
# Fetch all sub-account Spot balances
all_balances <- sub$get_all_spot_balances()
print(all_balances)
# Fetch first page only with 10 results per page
first_page <- sub$get_all_spot_balances(page_size = 10, max_pages = 1)
# Summarise total available USDT across all sub-accounts
usdt <- all_balances[currency == "USDT"]
total_avail <- sum(as.numeric(usdt$available))
cat("Total available USDT:", total_avail, "\\n")
# Group by sub-account
all_balances[, .(n_currencies = .N), by = .(sub_name, account_type)]
}
Examples
if (FALSE) { # \dontrun{
# Synchronous
sub <- KucoinSubAccount$new()
summary <- sub$get_sub_account_list()
print(summary)
# Asynchronous
sub_async <- KucoinSubAccount$new(async = TRUE)
main <- coro::async(function() {
summary <- await(sub_async$get_sub_account_list())
print(summary)
})
main()
while (!later::loop_empty()) later::run_now()
} # }
## ------------------------------------------------
## Method `KucoinSubAccount$add_sub_account`
## ------------------------------------------------
if (FALSE) { # \dontrun{
sub <- KucoinSubAccount$new()
# Create a Spot sub-account
result <- sub$add_sub_account(
password = "MyPass123",
subName = "botaccount1",
access = "Spot",
remarks = "alpha-strategy"
)
print(result$uid)
print(result$sub_name)
# Create a Futures sub-account without remarks
result <- sub$add_sub_account(
password = "SecurePass99",
subName = "futuresbot1",
access = "Futures"
)
} # }
## ------------------------------------------------
## Method `KucoinSubAccount$get_sub_account_list`
## ------------------------------------------------
if (FALSE) { # \dontrun{
sub <- KucoinSubAccount$new()
# Fetch all sub-accounts
all_subs <- sub$get_sub_account_list()
print(all_subs)
# Fetch only first page with 10 results
first_page <- sub$get_sub_account_list(page_size = 10, max_pages = 1)
print(first_page[, .(sub_name, access, datetime_created)])
# Filter for Spot sub-accounts
spot_subs <- all_subs[access == "Spot"]
} # }
## ------------------------------------------------
## Method `KucoinSubAccount$get_detail_balance`
## ------------------------------------------------
if (FALSE) { # \dontrun{
sub <- KucoinSubAccount$new()
# Get balances for a specific sub-account
balances <- sub$get_detail_balance(subUserId = "169630809")
print(balances)
# Include zero-balance currencies
all_balances <- sub$get_detail_balance(
subUserId = "169630809",
includeBaseAmount = TRUE
)
# Filter for trade account balances only
trade_bal <- balances[account_type == "trade"]
print(trade_bal[, .(currency, available, holds)])
} # }
## ------------------------------------------------
## Method `KucoinSubAccount$get_all_spot_balances`
## ------------------------------------------------
if (FALSE) { # \dontrun{
sub <- KucoinSubAccount$new()
# Fetch all sub-account Spot balances
all_balances <- sub$get_all_spot_balances()
print(all_balances)
# Fetch first page only with 10 results per page
first_page <- sub$get_all_spot_balances(page_size = 10, max_pages = 1)
# Summarise total available USDT across all sub-accounts
usdt <- all_balances[currency == "USDT"]
total_avail <- sum(as.numeric(usdt$available))
cat("Total available USDT:", total_avail, "\\n")
# Group by sub-account
all_balances[, .(n_currencies = .N), by = .(sub_name, account_type)]
} # }