⚡️ Speed up method digifinex.parse_transaction by 20%
#36
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.
📄 20% (0.20x) speedup for
digifinex.parse_transactioninpython/ccxt/digifinex.py⏱️ Runtime :
36.0 milliseconds→29.9 milliseconds(best of80runs)📝 Explanation and details
The optimization achieves a 20% speedup by eliminating function call overhead and replacing expensive operations with more efficient Python idioms.
Key Optimizations Applied:
Precompiled Regex for
parse8601: The most impactful change moves regex compilation from runtime to module-level initialization. Instead of compiling the regex pattern on every call (8.95ms in profiling), it's now precompiled as_PARSE8601_TZ_REGEX, reducing this to 2.37ms - a 73% improvement in regex operations.Inlined Dictionary Lookups: The
safe_string*methods replacedExchange.key_exists()function calls with directisinstance(dictionary, dict) and key in dictionarychecks. This eliminates function call overhead and uses Python's optimized dictionary membership testing, reducingsafe_stringexecution time from 17.98ms to 8.23ms.Direct Key Access in
safe_string_2: Instead of callingsafe_either()with nested function calls, it now performs directdictionary.get()operations with immediate type checking, reducing execution time from 4.62ms to 1.39ms.Optimized ISO8601 Formatting: Replaced complex string slicing and formatting operations with direct f-string interpolation (
f"{s}.{msec:03d}Z"), improving readability and performance.Performance Impact by Test Case:
Why These Optimizations Work:
key in dictis faster than custom existence checking functionsThese optimizations are particularly beneficial since
parse_transactionappears to be called frequently in cryptocurrency exchange operations where parsing speed directly impacts trading performance.✅ Correctness verification report:
🌀 Generated Regression Tests and Runtime
import copy
import datetime
imports
import pytest
from ccxt.digifinex import digifinex
--- UNIT TESTS ---
@pytest.fixture
def parser():
return digifinex()
1. BASIC TEST CASES
def test_parse_transaction_withdrawal_minimal(parser):
# Test minimal withdrawal response
tx = {"code": 200, "withdraw_id": 700}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 17.9μs -> 14.1μs (27.2% faster)
def test_parse_transaction_deposit_full(parser):
# Test deposit with all fields
tx = {
"id": 1171,
"currency": "xrp",
"hash": "ed03abc",
"chain": "",
"amount": 7.457467,
"address": "r123",
"memo": "100040",
"fee": 0.0,
"state": "3",
"created_date": "2020-04-20 11:23:00",
"finished_date": "2020-04-20 13:23:00"
}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 89.8μs -> 77.8μs (15.3% faster)
def test_parse_transaction_withdrawal_with_fee_and_chain(parser):
# Withdrawal with fee and chain
tx = {
"withdraw_id": 42,
"currency": "eth",
"hash": "0xabc",
"chain": "ERC20",
"amount": "1.23",
"address": "0x123",
"fee": "0.01",
"state": "1",
"created_date": "2021-01-01 12:00:00",
"finished_date": "2021-01-01 13:00:00"
}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 85.6μs -> 73.6μs (16.4% faster)
def test_parse_transaction_currency_case_insensitivity(parser):
# currency should be uppercased
tx = {"id": 1, "currency": "bTc"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 18.6μs -> 15.2μs (22.5% faster)
def test_parse_transaction_with_currency_object(parser):
# currency argument should override currencyId
tx = {"id": 2, "currency": "eth"}
codeflash_output = parser.parse_transaction(tx, currency={'id': 'eth', 'code': 'ETHX'}); parsed = codeflash_output # 18.9μs -> 15.5μs (21.8% faster)
2. EDGE TEST CASES
def test_parse_transaction_missing_all_fields(parser):
# All fields missing
tx = {}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 16.9μs -> 13.2μs (28.2% faster)
def test_parse_transaction_null_values(parser):
# All fields present but set to None
tx = {
"id": None, "currency": None, "hash": None, "chain": None, "amount": None, "address": None,
"memo": None, "fee": None, "state": None, "created_date": None, "finished_date": None
}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 16.1μs -> 13.5μs (19.4% faster)
def test_parse_transaction_unusual_state(parser):
# Unknown state value
tx = {"id": 1, "currency": "btc", "state": "safe"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 18.6μs -> 15.1μs (23.1% faster)
def test_parse_transaction_nonstandard_dates(parser):
# created_date and finished_date in wrong format
tx = {"id": 1, "currency": "btc", "created_date": "notadate", "finished_date": "2021/01/01"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 25.6μs -> 16.6μs (54.0% faster)
def test_parse_transaction_amount_and_fee_as_strings(parser):
# amount and fee as string numbers
tx = {"id": 1, "currency": "btc", "amount": "0.123", "fee": "0.001"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 19.8μs -> 16.5μs (20.2% faster)
def test_parse_transaction_zero_fee(parser):
# Fee is zero (should still be present)
tx = {"id": 1, "currency": "btc", "fee": 0}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 20.1μs -> 16.1μs (25.0% faster)
def test_parse_transaction_negative_amount(parser):
# Negative amount (should be parsed as float)
tx = {"id": 1, "currency": "btc", "amount": "-2.5"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 19.9μs -> 15.9μs (24.7% faster)
def test_parse_transaction_large_id(parser):
# Large integer id
tx = {"id": 2**63, "currency": "btc"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 18.9μs -> 15.3μs (23.7% faster)
def test_parse_transaction_tag_and_address(parser):
# Tag and address fields
tx = {"id": 1, "currency": "btc", "address": "addr", "memo": "tag1"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 18.3μs -> 15.5μs (18.6% faster)
def test_parse_transaction_chain_none(parser):
# chain is None
tx = {"id": 1, "currency": "btc", "chain": None}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 18.6μs -> 14.9μs (24.7% faster)
def test_parse_transaction_fee_none(parser):
# fee is None
tx = {"id": 1, "currency": "btc", "fee": None}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 19.0μs -> 15.5μs (22.6% faster)
def test_parse_transaction_non_numeric_amount_fee(parser):
# Non-numeric amount and fee
tx = {"id": 1, "currency": "btc", "amount": "abc", "fee": "xyz"}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 21.7μs -> 17.9μs (21.0% faster)
def test_parse_transaction_currencyid_none_with_currency_object(parser):
# currencyId is None, currency object provided
tx = {"id": 1, "currency": None}
codeflash_output = parser.parse_transaction(tx, currency={'id': None, 'code': 'USDT'}); parsed = codeflash_output # 12.1μs -> 8.19μs (47.9% faster)
3. LARGE SCALE TEST CASES
def test_parse_transaction_large_batch(parser):
# Test parsing 1000 transactions with unique ids and values
base_tx = {
"id": 0,
"currency": "btc",
"hash": "hash",
"chain": "BTC",
"amount": "1.0",
"address": "addr",
"memo": "memo",
"fee": "0.001",
"state": "3",
"created_date": "2022-01-01 00:00:00",
"finished_date": "2022-01-01 01:00:00"
}
for i in range(1000):
tx = copy.deepcopy(base_tx)
tx['id'] = i
tx['amount'] = str(i * 0.01)
tx['fee'] = str(i * 0.0001)
tx['currency'] = "btc" if i % 2 == 0 else "eth"
tx['state'] = str((i % 4) + 1)
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 34.9ms -> 28.9ms (20.6% faster)
# Status alternates as per the mapping
expected_status = parser.parse_transaction_status(str((i % 4) + 1))
def test_parse_transaction_performance_on_large_input(parser):
# Simulate parsing a large transaction (long strings, large numbers)
tx = {
"id": "x" * 256,
"currency": "usdt",
"hash": "h" * 512,
"chain": "TRC20",
"amount": "1" * 100, # very large number as string
"address": "a" * 128,
"memo": "m" * 64,
"fee": "0.0000001",
"state": "2",
"created_date": "2023-12-31 23:59:59",
"finished_date": "2024-01-01 00:00:00"
}
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 88.5μs -> 77.8μs (13.7% faster)
def test_parse_transaction_batch_varied_fields(parser):
# Batch with missing/extra fields and different types
txs = [
{"withdraw_id": 1, "currency": "btc"}, # minimal
{"id": 2, "currency": "eth", "fee": "0.1", "amount": "100"}, # with fee/amount
{"id": 3, "currency": "ltc", "state": "4", "fee": None}, # failed state, fee None
{"id": 4, "currency": "doge", "amount": "-10", "memo": None}, # negative amount, tag None
{"id": 5, "currency": "ada", "address": "addr", "memo": "tag5", "chain": "ADA", "fee": "0"}, # all tags
]
for tx in txs:
codeflash_output = parser.parse_transaction(tx); parsed = codeflash_output # 53.8μs -> 40.3μs (33.5% faster)
if 'fee' in tx and tx['fee'] not in (None, ''):
pass
else:
pass
if 'amount' in tx and tx['amount'] not in (None, ''):
try:
expected_amount = float(tx['amount'])
except Exception:
expected_amount = None
else:
pass
codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import copy
import datetime
function to test
from types import SimpleNamespace
imports
import pytest
from ccxt.digifinex import digifinex
We'll define a minimal digifinex class that implements parse_transaction and its dependencies,
as in the provided code. We'll also make sure to cover all the helper methods used.
class DigifinexMinimal:
# --- Helper methods and parse_transaction_status as per ccxt/digifinex.py ---
def safe_string(self, dictionary, key, default_value=None):
return str(dictionary[key]) if self.key_exists(dictionary, key) else default_value
Instantiate the minimal digifinex
digifinex = DigifinexMinimal()
------------------- UNIT TESTS -------------------
1. Basic Test Cases
def test_basic_deposit_transaction():
# Typical deposit transaction
tx = {
"id": "1171",
"currency": "xrp",
"hash": "ed03094b84eafbe4bc16e7ef766ee959885ee5bcb265872baaa9c64e1cf86c2b",
"chain": "",
"amount": 7.457467,
"address": "rae93V8d2mdoUQHwBDBdM4NHCMehRJAsbm",
"memo": "100040",
"fee": 0,
"state": "3",
"created_date": "2020-04-20 11:23:00",
"finished_date": "2020-04-20 13:23:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 53.4μs -> 54.4μs (1.78% slower)
def test_basic_withdraw_transaction():
# Typical withdraw response
tx = {
"code": 200,
"withdraw_id": "700"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 8.37μs -> 8.21μs (1.95% faster)
def test_basic_case_insensitive_currency():
# Currency should be uppercased
tx = {
"id": "1",
"currency": "btc",
"hash": "abc",
"amount": "0.5",
"fee": "0.001",
"state": "3",
"created_date": "2022-01-01 00:00:00",
"finished_date": "2022-01-01 01:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 41.5μs -> 41.5μs (0.043% slower)
2. Edge Test Cases
def test_missing_optional_fields():
# Transaction with missing optional fields
tx = {
"id": "2",
"currency": "eth",
"amount": "1.0",
"fee": "0.01",
"state": "1",
"created_date": "2022-01-01 00:00:00"
# missing: hash, address, memo, chain, finished_date
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 29.6μs -> 29.2μs (1.58% faster)
def test_null_and_empty_values():
# Transaction with null and empty string values
tx = {
"id": "",
"currency": "",
"hash": "",
"amount": None,
"fee": "",
"state": "",
"created_date": "",
"finished_date": ""
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 7.84μs -> 7.59μs (3.37% faster)
def test_unexpected_status_code():
# Unknown status code should be returned as is
tx = {
"id": "3",
"currency": "usdt",
"amount": "10",
"fee": "0.1",
"state": "99",
"created_date": "2022-01-01 00:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 32.0μs -> 33.1μs (3.07% slower)
def test_nonstandard_dates():
# Nonstandard date format should yield None for timestamp/updated
tx = {
"id": "4",
"currency": "ltc",
"amount": "1.2",
"fee": "0.02",
"state": "3",
"created_date": "not-a-date",
"finished_date": "2022-01-01T01:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 29.0μs -> 27.9μs (4.11% faster)
def test_fee_none():
# Fee is explicitly None
tx = {
"id": "5",
"currency": "ada",
"amount": "100",
"fee": None,
"state": "3",
"created_date": "2022-01-01 00:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 29.7μs -> 30.1μs (1.43% slower)
def test_amount_string_and_float():
# Amount as string and as float should both work
tx1 = {
"id": "6",
"currency": "doge",
"amount": "123.456",
"fee": "0.01",
"state": "3",
"created_date": "2022-01-01 00:00:00"
}
tx2 = copy.deepcopy(tx1)
tx2['amount'] = 123.456
codeflash_output = digifinex.parse_transaction(tx1); result1 = codeflash_output # 30.1μs -> 29.4μs (2.31% faster)
codeflash_output = digifinex.parse_transaction(tx2); result2 = codeflash_output # 19.7μs -> 19.9μs (0.830% slower)
def test_chain_network_field():
# chain field present
tx = {
"id": "7",
"currency": "usdt",
"amount": "1",
"fee": "0.1",
"state": "3",
"chain": "ERC20",
"created_date": "2022-01-01 00:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 27.9μs -> 27.3μs (2.01% faster)
def test_alternate_id_field():
# Only withdraw_id present
tx = {
"withdraw_id": "9999",
"currency": "btc",
"amount": "0.2",
"fee": "0.0005",
"state": "2",
"created_date": "2022-01-01 00:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 28.3μs -> 28.1μs (0.930% faster)
def test_currency_none():
# Currency field missing
tx = {
"id": "8",
"amount": "1",
"fee": "0.1",
"state": "3",
"created_date": "2022-01-01 00:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 27.8μs -> 27.4μs (1.43% faster)
def test_fee_zero():
# Fee is zero as string
tx = {
"id": "9",
"currency": "eth",
"amount": "2",
"fee": "0",
"state": "3",
"created_date": "2022-01-01 00:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 28.0μs -> 27.4μs (2.51% faster)
def test_tag_and_memo_fields():
# Tag/memo field present
tx = {
"id": "10",
"currency": "xrp",
"amount": "1",
"fee": "0.1",
"state": "3",
"memo": "5555",
"created_date": "2022-01-01 00:00:00"
}
codeflash_output = digifinex.parse_transaction(tx); result = codeflash_output # 28.2μs -> 28.3μs (0.314% slower)
3. Large Scale Test Cases
def test_large_scale_transactions():
# Test a list of 500 transactions with varying fields
transactions = []
for i in range(500):
tx = {
"id": str(i),
"currency": "usdt" if i % 2 == 0 else "btc",
"amount": str(i * 0.01),
"fee": str(i * 0.0001),
"state": str((i % 4) + 1),
"created_date": "2022-01-01 00:00:00",
"finished_date": "2022-01-01 01:00:00"
}
if i % 10 == 0:
tx["memo"] = str(i)
tx["chain"] = "ERC20"
transactions.append(tx)
results = [digifinex.parse_transaction(tx) for tx in transactions] # 45.1μs -> 45.8μs (1.58% slower)
# Check that all ids are correct and status is mapped as expected
for i, result in enumerate(results):
# Status mapping
expected_status = {1: "pending", 2: "pending", 3: "ok", 4: "failed"}[int((i % 4) + 1)]
if i % 10 == 0:
pass
else:
pass
def test_large_scale_missing_fields():
# 500 transactions, half missing currency, half missing fee
transactions = []
for i in range(500):
tx = {
"id": str(i),
"amount": str(i * 0.01),
"state": "3",
"created_date": "2022-01-01 00:00:00"
}
if i % 2 == 0:
tx["currency"] = "btc"
else:
tx["fee"] = str(i * 0.0001)
transactions.append(tx)
results = [digifinex.parse_transaction(tx) for tx in transactions] # 33.6μs -> 34.5μs (2.68% slower)
for i, result in enumerate(results):
if i % 2 == 0:
pass
else:
pass
To edit these changes
git checkout codeflash/optimize-digifinex.parse_transaction-mhu2ta2pand push.