⚡️ Speed up method poloniex.parse_balance by 32%
#44
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
📄 32% (0.32x) speedup for
poloniex.parse_balanceinpython/ccxt/poloniex.py⏱️ Runtime :
76.4 milliseconds→57.7 milliseconds(best of39runs)📝 Explanation and details
The optimized code achieves a 32% speedup by implementing several targeted performance improvements in hot-path functions:
Key Optimizations:
Eliminated Expensive Key Existence Checks: Replaced
Exchange.key_exists()calls insafe_string,safe_integer, andsafe_valuewith direct try/catch blocks using dictionary access. The originalkey_existsmethod had complex type checking and exception handling that was unnecessary for typical dictionary operations.Optimized
safe_balanceMethod:self.omit()for filtering keysself.safe_string,Precise.string_add, etc.) to avoid repeated attribute lookups in the main loopcodesinstead of using range-based indexingImproved
iso8601String Formatting:int()conversion that was already handledCached Method References in
parse_balance:safe_string,safe_currency_code, etc.) as local variables to eliminate repeated attribute lookups within loopsWhy This Matters:
The line profiler shows that
safe_stringalone was called 80,582 times and took 94.6ms in the original version, reduced to 55.4ms in the optimized version. Similarly,safe_balanceprocessing improved from 274ms to 261ms despite handling the same volume of data.Test Case Performance:
The optimizations show consistent improvements across all test scenarios:
This optimization is particularly valuable for cryptocurrency exchanges where balance parsing happens frequently during trading operations, portfolio updates, and API responses.
✅ Correctness verification report:
🌀 Generated Regression Tests and Runtime
from typing import Any, List
imports
import pytest
from ccxt.poloniex import poloniex
class Precise:
@staticmethod
def string_add(string1, string2):
if string1 is None and string2 is None:
return None
if string1 is None:
return string2
elif string2 is None:
return string1
return str(float(string1) + float(string2))
from ccxt.poloniex import poloniex
Fixtures for reuse
@pytest.fixture
def poloniex_instance():
return poloniex()
-----------------
BASIC TEST CASES
-----------------
def test_spot_single_currency_basic(poloniex_instance):
# Test basic spot balance with one currency
response = [
{
'accountType': 'spot',
'balances': [
{'currency': 'BTC', 'available': '1.23', 'hold': '0.45'}
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 30.9μs -> 26.8μs (15.0% faster)
def test_spot_multiple_currencies(poloniex_instance):
# Test spot balance with multiple currencies
response = [
{
'accountType': 'spot',
'balances': [
{'currency': 'BTC', 'available': '0.5', 'hold': '0.1'},
{'currency': 'ETH', 'available': '2.0', 'hold': '0.0'},
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 42.4μs -> 35.1μs (20.8% faster)
def test_swap_single_currency_basic(poloniex_instance):
# Test basic swap balance with one currency
response = {
'uTime': 1680000000000,
'details': [
{'ccy': 'BTC', 'avail': '3.5', 'im': '1.0'}
]
}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 49.0μs -> 42.5μs (15.3% faster)
def test_swap_multiple_currencies(poloniex_instance):
# Test swap balance with multiple currencies
response = {
'uTime': 1680000000000,
'details': [
{'ccy': 'BTC', 'avail': '1.0', 'im': '0.4'},
{'ccy': 'ETH', 'avail': '10.0', 'im': '2.0'},
]
}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 60.2μs -> 51.2μs (17.5% faster)
-----------------
EDGE TEST CASES
-----------------
def test_spot_empty_balances(poloniex_instance):
# Test empty spot balances list
response = [
{'accountType': 'spot', 'balances': []}
]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 7.80μs -> 5.62μs (39.0% faster)
def test_swap_empty_details(poloniex_instance):
# Test empty swap details list
response = {'uTime': 1680000000000, 'details': []}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 25.6μs -> 21.4μs (19.4% faster)
def test_spot_missing_fields(poloniex_instance):
# Test spot balances with missing 'available' or 'hold'
response = [
{
'accountType': 'spot',
'balances': [
{'currency': 'BTC', 'hold': '0.2'}, # missing 'available'
{'currency': 'ETH', 'available': '1.5'}, # missing 'hold'
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 29.0μs -> 22.7μs (27.5% faster)
def test_swap_missing_fields(poloniex_instance):
# Test swap details with missing 'avail' or 'im'
response = {
'uTime': 1680000000000,
'details': [
{'ccy': 'BTC', 'im': '0.2'}, # missing 'avail'
{'ccy': 'ETH', 'avail': '1.5'}, # missing 'im'
]
}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 46.1μs -> 37.7μs (22.3% faster)
def test_spot_non_string_numbers(poloniex_instance):
# Test spot balances with numeric values as floats/ints
response = [
{
'accountType': 'spot',
'balances': [
{'currency': 'BTC', 'available': 2, 'hold': 0.5},
{'currency': 'ETH', 'available': 0, 'hold': 0},
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 45.9μs -> 38.9μs (17.9% faster)
def test_swap_non_string_numbers(poloniex_instance):
# Test swap balances with numeric values as floats/ints
response = {
'uTime': 1680000000000,
'details': [
{'ccy': 'BTC', 'avail': 5, 'im': 2},
]
}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 47.5μs -> 40.4μs (17.6% faster)
def test_spot_currency_case_and_alias(poloniex_instance):
# Test currency code case insensitivity and alias mapping
response = [
{
'accountType': 'spot',
'balances': [
{'currency': 'xbt', 'available': '1', 'hold': '0'},
{'currency': 'BCHSV', 'available': '2', 'hold': '1'},
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 39.3μs -> 31.7μs (23.9% faster)
def test_swap_currency_case_and_alias(poloniex_instance):
# Test swap currency code case insensitivity and alias mapping
response = {
'uTime': 1680000000000,
'details': [
{'ccy': 'bcc', 'avail': '1', 'im': '0'},
]
}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 48.1μs -> 40.6μs (18.6% faster)
def test_swap_missing_uTime(poloniex_instance):
# Test swap response missing uTime
response = {'details': [{'ccy': 'BTC', 'avail': '1', 'im': '0.1'}]}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 32.6μs -> 29.1μs (12.2% faster)
def test_spot_multiple_accounts(poloniex_instance):
# Test spot with multiple accountType objects
response = [
{
'accountType': 'spot',
'balances': [
{'currency': 'BTC', 'available': '1', 'hold': '0.1'},
]
},
{
'accountType': 'margin',
'balances': [
{'currency': 'ETH', 'available': '5', 'hold': '2'},
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 44.2μs -> 36.3μs (21.5% faster)
def test_spot_empty_response(poloniex_instance):
# Test empty spot response (empty list)
response = []
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 5.30μs -> 5.11μs (3.58% faster)
def test_swap_empty_response(poloniex_instance):
# Test empty swap response (empty dict)
response = {}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 8.73μs -> 8.42μs (3.68% faster)
-----------------
LARGE SCALE TEST CASES
-----------------
def test_spot_large_number_of_currencies(poloniex_instance):
# Test spot with many currencies (up to 999)
balances = [{'currency': f'CUR{i}', 'available': str(i), 'hold': str(i/2)} for i in range(1, 1000)]
response = [{'accountType': 'spot', 'balances': balances}]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 7.56ms -> 5.65ms (33.9% faster)
def test_swap_large_number_of_currencies(poloniex_instance):
# Test swap with many currencies (up to 999)
details = [{'ccy': f'CUR{i}', 'avail': str(i), 'im': str(i/4)} for i in range(1, 1000)]
response = {'uTime': 1680000000000, 'details': details}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 7.72ms -> 5.88ms (31.4% faster)
def test_spot_performance_large_balances(poloniex_instance):
# Test spot with 999 currencies, all zeros
balances = [{'currency': f'CUR{i}', 'available': '0', 'hold': '0'} for i in range(1, 1000)]
response = [{'accountType': 'spot', 'balances': balances}]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 6.81ms -> 4.89ms (39.1% faster)
def test_swap_performance_large_balances(poloniex_instance):
# Test swap with 999 currencies, all zeros
details = [{'ccy': f'CUR{i}', 'avail': '0', 'im': '0'} for i in range(1, 1000)]
response = {'uTime': 1680000000000, 'details': details}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 6.97ms -> 5.21ms (33.9% faster)
def test_spot_mixed_types_large(poloniex_instance):
# Test spot with 999 currencies, mix of int, float, str
balances = []
for i in range(1, 1000):
available = i if i % 3 == 0 else float(i) if i % 3 == 1 else str(i)
hold = (i/2) if i % 2 == 0 else str(i/2)
balances.append({'currency': f'CUR{i}', 'available': available, 'hold': hold})
response = [{'accountType': 'spot', 'balances': balances}]
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 7.75ms -> 5.81ms (33.3% faster)
def test_swap_mixed_types_large(poloniex_instance):
# Test swap with 999 currencies, mix of int, float, str
details = []
for i in range(1, 1000):
avail = i if i % 3 == 0 else float(i) if i % 3 == 1 else str(i)
im = (i/4) if i % 2 == 0 else str(i/4)
details.append({'ccy': f'CUR{i}', 'avail': avail, 'im': im})
response = {'uTime': 1680000000000, 'details': details}
codeflash_output = poloniex_instance.parse_balance(response); bal = codeflash_output # 7.88ms -> 6.08ms (29.6% faster)
codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import pytest
from ccxt.poloniex import poloniex
Instance for tests
poloniex_instance = poloniex()
-------------------- Basic Test Cases --------------------
def test_parse_balance_spot_basic_single_currency():
# Single currency, normal values
response = [
{
'balances': [
{'currency': 'BTC', 'available': '1.5', 'hold': '0.5'},
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 31.9μs -> 27.5μs (16.1% faster)
def test_parse_balance_spot_basic_multiple_currencies():
# Multiple currencies, normal values
response = [
{
'balances': [
{'currency': 'BTC', 'available': '1.5', 'hold': '0.5'},
{'currency': 'ETH', 'available': '10', 'hold': '2'}
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 36.9μs -> 31.1μs (18.7% faster)
def test_parse_balance_swap_basic_single_currency():
# Swap format, single currency
response = {
'uTime': 1710000000000,
'details': [
{'ccy': 'BTC', 'avail': '2.5', 'im': '0.5'}
]
}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 43.3μs -> 36.3μs (19.1% faster)
def test_parse_balance_swap_basic_multiple_currencies():
# Swap format, multiple currencies
response = {
'uTime': 1710000000000,
'details': [
{'ccy': 'BTC', 'avail': '2.5', 'im': '0.5'},
{'ccy': 'ETH', 'avail': '10', 'im': '2'}
]
}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 46.1μs -> 40.1μs (14.7% faster)
def test_parse_balance_spot_currency_code_mapping():
# Test commonCurrencies mapping
response = [
{
'balances': [
{'currency': 'XBT', 'available': '1', 'hold': '0'}
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 22.3μs -> 18.4μs (21.4% faster)
-------------------- Edge Test Cases --------------------
def test_parse_balance_spot_zero_balances():
# Zero balances
response = [
{
'balances': [
{'currency': 'BTC', 'available': '0', 'hold': '0'},
{'currency': 'ETH', 'available': '0', 'hold': '0'}
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 31.3μs -> 25.2μs (24.1% faster)
def test_parse_balance_spot_missing_hold():
# Missing 'hold' field (should treat as None, total = free)
response = [
{
'balances': [
{'currency': 'BTC', 'available': '1.5'} # no 'hold'
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 16.5μs -> 12.8μs (29.1% faster)
def test_parse_balance_spot_missing_available():
# Missing 'available' field (should treat as None, total = used)
response = [
{
'balances': [
{'currency': 'BTC', 'hold': '0.5'} # no 'available'
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 16.3μs -> 12.7μs (29.1% faster)
def test_parse_balance_spot_missing_both():
# Missing both 'available' and 'hold'
response = [
{
'balances': [
{'currency': 'BTC'} # no 'available', no 'hold'
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 15.7μs -> 11.9μs (31.7% faster)
def test_parse_balance_swap_missing_im():
# Swap format, missing 'im' (used)
response = {
'uTime': 1710000000000,
'details': [
{'ccy': 'BTC', 'avail': '2.5'} # no 'im'
]
}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 31.6μs -> 27.0μs (17.2% faster)
def test_parse_balance_swap_missing_avail():
# Swap format, missing 'avail' (total)
response = {
'uTime': 1710000000000,
'details': [
{'ccy': 'BTC', 'im': '0.5'} # no 'avail'
]
}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 27.1μs -> 23.8μs (14.0% faster)
def test_parse_balance_empty_spot():
# Empty spot response
response = []
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 4.03μs -> 3.94μs (2.31% faster)
def test_parse_balance_empty_swap():
# Empty swap response
response = {'uTime': 1710000000000, 'details': []}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 18.4μs -> 15.5μs (18.6% faster)
def test_parse_balance_swap_negative_values():
# Swap format, negative values
response = {
'uTime': 1710000000000,
'details': [
{'ccy': 'BTC', 'avail': '-2.5', 'im': '-0.5'}
]
}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 38.0μs -> 34.6μs (9.71% faster)
def test_parse_balance_swap_extra_fields():
# Swap format, extra fields (should ignore)
response = {
'uTime': 1710000000000,
'details': [
{'ccy': 'BTC', 'avail': '2.5', 'im': '0.5', 'extra': 'ignored'}
],
'extra_top_level': 'ignored'
}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 52.6μs -> 46.1μs (14.2% faster)
def test_parse_balance_spot_multiple_accounts():
# Multiple accounts in spot response (should merge balances)
response = [
{
'balances': [
{'currency': 'BTC', 'available': '1', 'hold': '0.5'}
]
},
{
'balances': [
{'currency': 'BTC', 'available': '2', 'hold': '0.5'},
{'currency': 'ETH', 'available': '5', 'hold': '1'}
]
}
]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 42.4μs -> 34.8μs (21.9% faster)
-------------------- Large Scale Test Cases --------------------
def test_parse_balance_spot_large_scale():
# Large number of currencies in spot format
currencies = [f"C{i}" for i in range(1000)]
balances = [{'currency': c, 'available': str(i), 'hold': str(i/2)} for i, c in enumerate(currencies)]
response = [{'balances': balances}]
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 7.63ms -> 5.72ms (33.3% faster)
for i, c in enumerate(currencies):
pass
def test_parse_balance_swap_large_scale():
# Large number of currencies in swap format
currencies = [f"C{i}" for i in range(1000)]
details = [{'ccy': c, 'avail': str(i), 'im': str(i/2)} for i, c in enumerate(currencies)]
response = {'uTime': 1710000000000, 'details': details}
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 7.66ms -> 5.94ms (29.1% faster)
for i, c in enumerate(currencies):
pass
def test_parse_balance_spot_performance():
# Spot format, performance test with 1000 currencies
import time
currencies = [f"C{i}" for i in range(1000)]
balances = [{'currency': c, 'available': str(i), 'hold': str(i/2)} for i, c in enumerate(currencies)]
response = [{'balances': balances}]
start = time.time()
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 7.64ms -> 5.67ms (34.8% faster)
duration = time.time() - start
def test_parse_balance_swap_performance():
# Swap format, performance test with 1000 currencies
import time
currencies = [f"C{i}" for i in range(1000)]
details = [{'ccy': c, 'avail': str(i), 'im': str(i/2)} for i, c in enumerate(currencies)]
response = {'uTime': 1710000000000, 'details': details}
start = time.time()
codeflash_output = poloniex_instance.parse_balance(response); result = codeflash_output # 7.71ms -> 5.94ms (29.8% faster)
duration = time.time() - start
codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
To edit these changes
git checkout codeflash/optimize-poloniex.parse_balance-mhulpslsand push.