Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 12, 2025

📄 174% (1.74x) speedup for lbank.parse_transaction_status in python/ccxt/lbank.py

⏱️ Runtime : 9.51 milliseconds 3.47 milliseconds (best of 12 runs)

📝 Explanation and details

The optimized code achieves a 174% speedup through two key optimizations that eliminate repetitive overhead in hot path functions:

Key Optimizations

1. Dictionary Hoisting in parse_transaction_status:

  • Moved the statuses dictionary from inside the function to a module-level constant _LBANK_TRANSACTION_STATUSES
  • Why faster: The original version reconstructed the entire nested dictionary on every function call (10,003 times in profiling), consuming 74.6% of execution time. The optimized version eliminates this repeated allocation entirely.
  • Impact: Function runtime drops from 83.67ms to 60.30ms, with the dictionary construction overhead completely removed.

2. Fast-path optimization for safe_string and safe_value:

  • Added direct isinstance(dictionary, dict) and key in dictionary checks before falling back to the slower Exchange.key_exists() method
  • Why faster: Exchange.key_exists() performs generic type checking, exception handling, and supports edge cases like lists with non-integer keys. For the common case of dictionary lookups, this is unnecessary overhead.
  • Impact: Both functions see ~50% runtime reduction (13.25ms→6.65ms for safe_string, 13.07ms→6.77ms for safe_value) by avoiding the generic fallback path.

Performance Benefits by Test Case

The optimization shows consistent 70-200% speedups across all test scenarios:

  • Known status lookups: 70-90% faster (most common case in transaction processing)
  • Unknown statuses/types: 115-200% faster (benefits from optimized safe_* methods)
  • Bulk operations: 145-200% faster (compound benefits from both optimizations)

These optimizations are particularly effective for transaction status parsing in cryptocurrency exchanges where the same status mappings are queried frequently during order processing and transaction monitoring workflows.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 10109 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime

import pytest
from ccxt.lbank import lbank

pytest fixture for the lbank instance

@pytest.fixture
def lbank_instance():
return lbank()

-------------------- BASIC TEST CASES --------------------

def test_deposit_status_pending(lbank_instance):
# Deposit status '1' should map to 'pending'
codeflash_output = lbank_instance.parse_transaction_status('1', 'deposit') # 3.37μs -> 1.97μs (71.0% faster)

def test_deposit_status_ok(lbank_instance):
# Deposit status '2' should map to 'ok'
codeflash_output = lbank_instance.parse_transaction_status('2', 'deposit') # 3.43μs -> 1.82μs (88.3% faster)

def test_deposit_status_failed(lbank_instance):
# Deposit status '3' should map to 'failed'
codeflash_output = lbank_instance.parse_transaction_status('3', 'deposit') # 3.41μs -> 1.84μs (85.6% faster)

def test_deposit_status_canceled(lbank_instance):
# Deposit status '4' should map to 'canceled'
codeflash_output = lbank_instance.parse_transaction_status('4', 'deposit') # 3.55μs -> 1.95μs (82.1% faster)

def test_deposit_status_transfer(lbank_instance):
# Deposit status '5' should map to 'transfer'
codeflash_output = lbank_instance.parse_transaction_status('5', 'deposit') # 3.46μs -> 1.90μs (82.0% faster)

def test_withdrawal_status_pending(lbank_instance):
# Withdrawal status '1' should map to 'pending'
codeflash_output = lbank_instance.parse_transaction_status('1', 'withdrawal') # 3.45μs -> 1.88μs (83.8% faster)

def test_withdrawal_status_canceled(lbank_instance):
# Withdrawal status '2' should map to 'canceled'
codeflash_output = lbank_instance.parse_transaction_status('2', 'withdrawal') # 3.35μs -> 1.86μs (80.6% faster)

def test_withdrawal_status_failed(lbank_instance):
# Withdrawal status '3' should map to 'failed'
codeflash_output = lbank_instance.parse_transaction_status('3', 'withdrawal') # 3.43μs -> 1.83μs (88.0% faster)

def test_withdrawal_status_ok(lbank_instance):
# Withdrawal status '4' should map to 'ok'
codeflash_output = lbank_instance.parse_transaction_status('4', 'withdrawal') # 3.40μs -> 1.93μs (76.3% faster)

-------------------- EDGE TEST CASES --------------------

def test_unknown_type_returns_status(lbank_instance):
# Unknown type should return the status unchanged
codeflash_output = lbank_instance.parse_transaction_status('1', 'unknown') # 3.47μs -> 1.54μs (126% faster)
codeflash_output = lbank_instance.parse_transaction_status('foo', 'unknown') # 1.35μs -> 531ns (155% faster)
codeflash_output = lbank_instance.parse_transaction_status('', 'unknown') # 1.04μs -> 316ns (229% faster)

def test_unknown_status_returns_status(lbank_instance):
# Unknown status for deposit should return the status unchanged
codeflash_output = lbank_instance.parse_transaction_status('999', 'deposit') # 3.55μs -> 1.66μs (115% faster)
# Unknown status for withdrawal should return the status unchanged
codeflash_output = lbank_instance.parse_transaction_status('999', 'withdrawal') # 1.50μs -> 578ns (160% faster)

def test_empty_status_and_type(lbank_instance):
# Empty status and type should return empty string
codeflash_output = lbank_instance.parse_transaction_status('', '') # 3.47μs -> 1.41μs (147% faster)
codeflash_output = lbank_instance.parse_transaction_status('', 'deposit') # 1.74μs -> 751ns (132% faster)
codeflash_output = lbank_instance.parse_transaction_status('', 'withdrawal') # 1.00μs -> 450ns (122% faster)

def test_none_status_and_type(lbank_instance):
# None status and type should return None for status, type not in mapping
codeflash_output = lbank_instance.parse_transaction_status(None, None) # 3.58μs -> 1.57μs (128% faster)
# None status with valid type should return None
codeflash_output = lbank_instance.parse_transaction_status(None, 'deposit') # 1.80μs -> 744ns (142% faster)
codeflash_output = lbank_instance.parse_transaction_status(None, 'withdrawal') # 1.07μs -> 380ns (182% faster)

def test_numeric_status_as_int(lbank_instance):
# Status as int should be converted to string and mapped
codeflash_output = lbank_instance.parse_transaction_status(1, 'deposit') # 3.59μs -> 1.58μs (127% faster)
codeflash_output = lbank_instance.parse_transaction_status(2, 'withdrawal') # 1.40μs -> 587ns (139% faster)
# Unknown int status
codeflash_output = lbank_instance.parse_transaction_status(999, 'deposit') # 1.04μs -> 423ns (147% faster)

def test_type_case_sensitivity(lbank_instance):
# Type is case sensitive, so 'Deposit' is not recognized
codeflash_output = lbank_instance.parse_transaction_status('1', 'Deposit') # 3.55μs -> 1.60μs (122% faster)
codeflash_output = lbank_instance.parse_transaction_status('1', 'WITHDRAWAL') # 1.38μs -> 602ns (129% faster)

def test_status_case_sensitivity(lbank_instance):
# Status is case sensitive, so '1' is recognized, but '1 ' or '01' is not
codeflash_output = lbank_instance.parse_transaction_status('01', 'deposit') # 3.54μs -> 1.68μs (111% faster)
codeflash_output = lbank_instance.parse_transaction_status('1 ', 'deposit') # 1.45μs -> 520ns (179% faster)
codeflash_output = lbank_instance.parse_transaction_status('1', 'deposit') # 1.23μs -> 589ns (109% faster)

def test_status_as_non_string(lbank_instance):
# Status as other types (float, bool, list, dict)
codeflash_output = lbank_instance.parse_transaction_status(1.0, 'deposit')
codeflash_output = lbank_instance.parse_transaction_status(True, 'deposit')
codeflash_output = lbank_instance.parse_transaction_status([1], 'deposit')
codeflash_output = lbank_instance.parse_transaction_status({'foo': 'bar'}, 'deposit')

def test_type_as_non_string(lbank_instance):
# Type as non-string should not match mapping
codeflash_output = lbank_instance.parse_transaction_status('1', 123) # 3.51μs -> 1.61μs (118% faster)
codeflash_output = lbank_instance.parse_transaction_status('1', None) # 1.47μs -> 516ns (185% faster)
codeflash_output = lbank_instance.parse_transaction_status('1', True) # 1.10μs -> 379ns (192% faster)

-------------------- LARGE SCALE TEST CASES --------------------

def test_large_number_of_unknown_statuses(lbank_instance):
# Test performance and correctness for 1000 unknown statuses
for i in range(1000):
status = str(i + 1000) # All are unknown
codeflash_output = lbank_instance.parse_transaction_status(status, 'deposit') # 970μs -> 353μs (174% faster)
codeflash_output = lbank_instance.parse_transaction_status(status, 'withdrawal')

def test_large_number_of_known_statuses(lbank_instance):
# Test correctness for all known statuses repeated 200 times
known_deposit = ['1', '2', '3', '4', '5']
known_withdrawal = ['1', '2', '3', '4']
for i in range(200):
for status in known_deposit:
codeflash_output = lbank_instance.parse_transaction_status(status, 'deposit')
for status in known_withdrawal:
codeflash_output = lbank_instance.parse_transaction_status(status, 'withdrawal')

def test_large_number_of_random_types(lbank_instance):
# Test with 500 random types, all should return status unchanged
for i in range(500):
type_str = f"random_type_{i}"
codeflash_output = lbank_instance.parse_transaction_status('1', type_str) # 501μs -> 165μs (204% faster)
codeflash_output = lbank_instance.parse_transaction_status('foo', type_str)

def test_large_number_of_empty_statuses(lbank_instance):
# Test with 500 empty statuses
for i in range(500):
codeflash_output = lbank_instance.parse_transaction_status('', 'deposit') # 461μs -> 155μs (197% faster)
codeflash_output = lbank_instance.parse_transaction_status('', 'withdrawal')
codeflash_output = lbank_instance.parse_transaction_status('', f"random_type_{i}") # 457μs -> 153μs (197% faster)

def test_large_number_of_none_statuses(lbank_instance):
# Test with 500 None statuses
for i in range(500):
codeflash_output = lbank_instance.parse_transaction_status(None, 'deposit') # 461μs -> 157μs (193% faster)
codeflash_output = lbank_instance.parse_transaction_status(None, 'withdrawal')
codeflash_output = lbank_instance.parse_transaction_status(None, f"random_type_{i}") # 459μs -> 154μs (197% faster)

-------------------- MUTATION TESTING GUARD --------------------

def test_mutation_guard_for_deposit(lbank_instance):
# If mapping changes, this should fail
codeflash_output = lbank_instance.parse_transaction_status('1', 'deposit') # 3.31μs -> 1.84μs (79.2% faster)
codeflash_output = lbank_instance.parse_transaction_status('2', 'deposit') # 1.37μs -> 665ns (106% faster)
codeflash_output = lbank_instance.parse_transaction_status('3', 'deposit') # 977ns -> 436ns (124% faster)
codeflash_output = lbank_instance.parse_transaction_status('4', 'deposit') # 991ns -> 403ns (146% faster)
codeflash_output = lbank_instance.parse_transaction_status('5', 'deposit') # 912ns -> 352ns (159% faster)

def test_mutation_guard_for_withdrawal(lbank_instance):
# If mapping changes, this should fail
codeflash_output = lbank_instance.parse_transaction_status('1', 'withdrawal') # 3.16μs -> 1.84μs (71.7% faster)
codeflash_output = lbank_instance.parse_transaction_status('2', 'withdrawal') # 1.47μs -> 675ns (117% faster)
codeflash_output = lbank_instance.parse_transaction_status('3', 'withdrawal') # 1.04μs -> 411ns (154% faster)
codeflash_output = lbank_instance.parse_transaction_status('4', 'withdrawal') # 1.00μs -> 354ns (183% 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.lbank import lbank

Create a fixture for the lbank instance

@pytest.fixture
def lbank_instance():
return lbank()

-------------------- Basic Test Cases --------------------

def test_deposit_status_pending(lbank_instance):
# Basic: deposit status '1' should be 'pending'
codeflash_output = lbank_instance.parse_transaction_status('1', 'deposit') # 3.37μs -> 1.77μs (90.9% faster)

def test_deposit_status_ok(lbank_instance):
# Basic: deposit status '2' should be 'ok'
codeflash_output = lbank_instance.parse_transaction_status('2', 'deposit') # 3.37μs -> 1.86μs (80.8% faster)

def test_deposit_status_failed(lbank_instance):
# Basic: deposit status '3' should be 'failed'
codeflash_output = lbank_instance.parse_transaction_status('3', 'deposit') # 3.51μs -> 1.93μs (81.7% faster)

def test_deposit_status_canceled(lbank_instance):
# Basic: deposit status '4' should be 'canceled'
codeflash_output = lbank_instance.parse_transaction_status('4', 'deposit') # 3.41μs -> 1.91μs (78.8% faster)

def test_deposit_status_transfer(lbank_instance):
# Basic: deposit status '5' should be 'transfer'
codeflash_output = lbank_instance.parse_transaction_status('5', 'deposit') # 3.46μs -> 1.88μs (83.7% faster)

def test_withdrawal_status_pending(lbank_instance):
# Basic: withdrawal status '1' should be 'pending'
codeflash_output = lbank_instance.parse_transaction_status('1', 'withdrawal') # 3.22μs -> 1.86μs (72.6% faster)

def test_withdrawal_status_canceled(lbank_instance):
# Basic: withdrawal status '2' should be 'canceled'
codeflash_output = lbank_instance.parse_transaction_status('2', 'withdrawal') # 3.33μs -> 1.89μs (76.4% faster)

def test_withdrawal_status_failed(lbank_instance):
# Basic: withdrawal status '3' should be 'failed'
codeflash_output = lbank_instance.parse_transaction_status('3', 'withdrawal') # 3.29μs -> 1.94μs (69.9% faster)

def test_withdrawal_status_ok(lbank_instance):
# Basic: withdrawal status '4' should be 'ok'
codeflash_output = lbank_instance.parse_transaction_status('4', 'withdrawal') # 3.34μs -> 1.77μs (89.2% faster)

-------------------- Edge Test Cases --------------------

def test_unknown_status_in_deposit(lbank_instance):
# Edge: Unknown status code in deposit should return the status code itself
codeflash_output = lbank_instance.parse_transaction_status('999', 'deposit') # 3.51μs -> 1.46μs (141% faster)

def test_unknown_status_in_withdrawal(lbank_instance):
# Edge: Unknown status code in withdrawal should return the status code itself
codeflash_output = lbank_instance.parse_transaction_status('999', 'withdrawal') # 3.71μs -> 1.69μs (119% faster)

def test_unknown_type(lbank_instance):
# Edge: Unknown type should return the status code itself
codeflash_output = lbank_instance.parse_transaction_status('1', 'foobar') # 3.49μs -> 1.42μs (145% faster)

def test_none_status(lbank_instance):
# Edge: None as status should return None
codeflash_output = lbank_instance.parse_transaction_status(None, 'deposit') # 3.43μs -> 1.70μs (101% faster)

def test_none_type(lbank_instance):
# Edge: None as type should return the status code itself
codeflash_output = lbank_instance.parse_transaction_status('1', None) # 3.45μs -> 1.65μs (109% faster)

def test_empty_string_status(lbank_instance):
# Edge: Empty string as status should return empty string
codeflash_output = lbank_instance.parse_transaction_status('', 'deposit') # 3.44μs -> 1.62μs (112% faster)

def test_empty_string_type(lbank_instance):
# Edge: Empty string as type should return the status code itself
codeflash_output = lbank_instance.parse_transaction_status('1', '') # 3.29μs -> 1.46μs (126% faster)

def test_status_as_int_string(lbank_instance):
# Edge: Status given as an integer string, should match string keys
codeflash_output = lbank_instance.parse_transaction_status(str(2), 'deposit') # 3.49μs -> 1.87μs (86.7% faster)

def test_status_as_int(lbank_instance):
# Edge: Status given as an integer, should return the integer itself (no match)
codeflash_output = lbank_instance.parse_transaction_status(2, 'deposit') # 3.70μs -> 1.71μs (117% faster)

def test_type_case_sensitivity(lbank_instance):
# Edge: Type is case-sensitive, so 'Deposit' should not match 'deposit'
codeflash_output = lbank_instance.parse_transaction_status('1', 'Deposit') # 3.43μs -> 1.57μs (118% faster)

def test_status_case_sensitivity(lbank_instance):
# Edge: Status is case-sensitive, so '1' != '1 ' or '01'
codeflash_output = lbank_instance.parse_transaction_status('01', 'deposit') # 3.55μs -> 1.80μs (97.4% faster)
codeflash_output = lbank_instance.parse_transaction_status('1 ', 'deposit') # 1.49μs -> 577ns (158% faster)

def test_status_zero(lbank_instance):
# Edge: Status '0' is not mapped; should return '0'
codeflash_output = lbank_instance.parse_transaction_status('0', 'deposit') # 3.57μs -> 1.64μs (118% faster)
codeflash_output = lbank_instance.parse_transaction_status('0', 'withdrawal') # 1.34μs -> 610ns (120% faster)

def test_type_numeric(lbank_instance):
# Edge: Type as integer should return status itself (no match)
codeflash_output = lbank_instance.parse_transaction_status('1', 123) # 3.43μs -> 1.58μs (117% faster)

def test_status_boolean(lbank_instance):
# Edge: Status as boolean should return the boolean itself (no match)
codeflash_output = lbank_instance.parse_transaction_status(True, 'deposit') # 3.51μs -> 1.59μs (120% faster)
codeflash_output = lbank_instance.parse_transaction_status(False, 'withdrawal') # 1.40μs -> 633ns (121% faster)

def test_type_boolean(lbank_instance):
# Edge: Type as boolean should return status itself (no match)
codeflash_output = lbank_instance.parse_transaction_status('1', True) # 3.54μs -> 1.58μs (123% faster)
codeflash_output = lbank_instance.parse_transaction_status('1', False) # 1.40μs -> 566ns (148% faster)

def test_both_none(lbank_instance):
# Edge: Both status and type are None; should return None
codeflash_output = lbank_instance.parse_transaction_status(None, None) # 3.62μs -> 1.57μs (130% faster)

def test_both_empty(lbank_instance):
# Edge: Both status and type are empty string; should return empty string
codeflash_output = lbank_instance.parse_transaction_status('', '') # 3.21μs -> 1.54μs (108% faster)

def test_all_deposit_statuses(lbank_instance):
# Large: Check all mapped and unmapped deposit statuses in a batch
known = {'1': 'pending', '2': 'ok', '3': 'failed', '4': 'canceled', '5': 'transfer'}
for i in range(1, 11):
key = str(i)
expected = known[key] if key in known else key
codeflash_output = lbank_instance.parse_transaction_status(key, 'deposit') # 12.9μs -> 5.70μs (126% faster)

def test_all_withdrawal_statuses(lbank_instance):
# Large: Check all mapped and unmapped withdrawal statuses in a batch
known = {'1': 'pending', '2': 'canceled', '3': 'failed', '4': 'ok'}
for i in range(1, 11):
key = str(i)
expected = known[key] if key in known else key
codeflash_output = lbank_instance.parse_transaction_status(key, 'withdrawal') # 12.3μs -> 5.52μs (123% faster)

def test_large_batch_unknown_types(lbank_instance):
# Large: 100 unknown types, all should return status as is
for i in range(100):
status = str(i)
type_ = f'unknown_type_{i}'
codeflash_output = lbank_instance.parse_transaction_status(status, type_) # 108μs -> 36.0μs (201% faster)

def test_large_batch_random_statuses_and_types(lbank_instance):
# Large: 1000 random status/type combinations, most should return status as is
for i in range(1000):
status = str(i % 7) # only 1-5 are mapped for deposit, 1-4 for withdrawal
type_ = 'deposit' if i % 2 == 0 else 'withdrawal'
# Expected value
if type_ == 'deposit':
expected = {
'1': 'pending',
'2': 'ok',
'3': 'failed',
'4': 'canceled',
'5': 'transfer',
}.get(status, status)
else:
expected = {
'1': 'pending',
'2': 'canceled',
'3': 'failed',
'4': 'ok',
}.get(status, status)
codeflash_output = lbank_instance.parse_transaction_status(status, type_) # 927μs -> 359μs (158% faster)

def test_performance_large_input(lbank_instance):
# Large: Performance test for 1000 calls (should be fast)
results = []
for i in range(1000):
results.append(lbank_instance.parse_transaction_status(str(i % 5 + 1), 'deposit')) # 902μs -> 367μs (145% faster)

def test_large_batch_edge_types(lbank_instance):
# Large: 1000 calls with edge-case types (None, int, bool, float, empty string)
edge_types = [None, 123, True, False, '', [], {}, 0.0]
for i in range(1000):
status = str(i % 10)
type_ = edge_types[i % len(edge_types)]
codeflash_output = lbank_instance.parse_transaction_status(status, type_)

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-lbank.parse_transaction_status-mhvk1dq2 and push.

Codeflash

The optimized code achieves a **174% speedup** through two key optimizations that eliminate repetitive overhead in hot path functions:

## Key Optimizations

**1. Dictionary Hoisting in `parse_transaction_status`:**
- Moved the `statuses` dictionary from inside the function to a module-level constant `_LBANK_TRANSACTION_STATUSES`
- **Why faster:** The original version reconstructed the entire nested dictionary on every function call (10,003 times in profiling), consuming 74.6% of execution time. The optimized version eliminates this repeated allocation entirely.
- **Impact:** Function runtime drops from 83.67ms to 60.30ms, with the dictionary construction overhead completely removed.

**2. Fast-path optimization for `safe_string` and `safe_value`:**  
- Added direct `isinstance(dictionary, dict)` and `key in dictionary` checks before falling back to the slower `Exchange.key_exists()` method
- **Why faster:** `Exchange.key_exists()` performs generic type checking, exception handling, and supports edge cases like lists with non-integer keys. For the common case of dictionary lookups, this is unnecessary overhead.
- **Impact:** Both functions see ~50% runtime reduction (13.25ms→6.65ms for `safe_string`, 13.07ms→6.77ms for `safe_value`) by avoiding the generic fallback path.

## Performance Benefits by Test Case
The optimization shows consistent 70-200% speedups across all test scenarios:
- **Known status lookups:** 70-90% faster (most common case in transaction processing)
- **Unknown statuses/types:** 115-200% faster (benefits from optimized safe_* methods)
- **Bulk operations:** 145-200% faster (compound benefits from both optimizations)

These optimizations are particularly effective for transaction status parsing in cryptocurrency exchanges where the same status mappings are queried frequently during order processing and transaction monitoring workflows.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 12, 2025 05:23
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Nov 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant