Skip to content

Error Handling

Exception hierarchy and error handling patterns.

Exception Hierarchy

QldataError (base)
├── ConnectionError
│   ├── WebSocketError
│   └── TimeoutError
├── RateLimitError
├── ValidationError
│   ├── SymbolNotFoundError
│   └── InvalidParameterError
├── ConfigurationError
└── DataError
    ├── EmptyDataError
    └── CorruptedDataError

Common Exceptions

QldataError

Base exception for all qldata errors.

from qldata.errors import QldataError

try:
    df = qd.data("INVALID", source="binance").last(1).get()
except QldataError as e:
    print(f"qldata error: {e}")

ConnectionError

Network and connection issues.

from qldata.errors import ConnectionError

try:
    df = qd.data("BTCUSDT", source="binance").last(1).get()
except ConnectionError as e:
    print(f"Network error: {e}")
    # Retry or use fallback

RateLimitError

API rate limit exceeded.

from qldata.errors import RateLimitError
import time

try:
    df = qd.data("BTCUSDT", source="binance").last(1).get()
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after}s")
    time.sleep(e.retry_after)
    # Retry

ValidationError

Data validation failed.

from qldata.errors import ValidationError

try:
    df = qd.data("BTCUSDT", source="binance").last(1).get()
except ValidationError as e:
    print(f"Validation failed: {e}")
    # Check data quality

SymbolNotFoundError

Symbol doesn't exist on exchange.

from qldata.errors import SymbolNotFoundError

try:
    info = qd.get_symbol_info("INVALIDPAIR", source="binance")
except SymbolNotFoundError as e:
    print(f"Symbol not found: {e.symbol}")

Error Handling Patterns

Basic Try-Except

from qldata.errors import QldataError

try:
    df = qd.data("BTCUSDT", source="binance").last(30).get()
except QldataError as e:
    print(f"Error: {e}")
    df = fallback_data()

Specific Exceptions

from qldata.errors import (
    ConnectionError,
    RateLimitError,
    ValidationError,
    QldataError
)

try:
    df = qd.data("BTCUSDT", source="binance").last(30).get()
except RateLimitError as e:
    time.sleep(e.retry_after)
    df = qd.data("BTCUSDT", source="binance").last(30).get()
except ConnectionError as e:
    logger.error(f"Network error: {e}")
    df = load_cached_data()
except ValidationError as e:
    logger.warning(f"Validation issue: {e}")
    df = qd.data("BTCUSDT", source="binance").last(30).get(validate=False)
except QldataError as e:
    logger.error(f"Unexpected error: {e}")
    raise

Retry with Backoff

from tenacity import retry, stop_after_attempt, wait_exponential
from qldata.errors import ConnectionError, RateLimitError

@retry(
    retry=retry_if_exception_type((ConnectionError, RateLimitError)),
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=10)
)
def fetch_data(symbol, days):
    return qd.data(symbol, source="binance").last(days).get()

Fallback Chain

def get_data_with_fallback(symbol, days):
    """Try multiple sources with fallback."""

    sources = [
        ("binance", "spot"),
        ("bybit", "spot"),
    ]

    for source, category in sources:
        try:
            return qd.data(symbol, source=source, category=category) \
                .last(days).resolution("1h").get()
        except QldataError as e:
            logger.warning(f"Failed {source}: {e}")
            continue

    raise QldataError(f"All sources failed for {symbol}")

Streaming Error Handling

import qldata as qd

def on_error(error):
    """Handle streaming errors."""
    if isinstance(error, ConnectionError):
        logger.warning("Connection lost, will auto-reconnect")
    elif isinstance(error, RateLimitError):
        logger.error(f"Rate limited: {error}")
    else:
        logger.error(f"Streaming error: {error}")

stream = qd.stream(["BTCUSDT"], source="binance") \
    .resolution("tick") \
    .on_data(process) \
    .on_error(on_error) \
    .get(start=True)

Logging Errors

import logging
from qldata.errors import QldataError

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger("trading")

try:
    df = qd.data("BTCUSDT", source="binance").last(30).get()
except QldataError as e:
    logger.exception("Data fetch failed")
    raise

Best Practices

1. Catch Specific Exceptions

# ✓ Good - handle specific cases
try:
    df = fetch_data()
except RateLimitError:
    wait_and_retry()
except ConnectionError:
    use_cached_data()

# ✗ Avoid - too broad
try:
    df = fetch_data()
except Exception:
    pass  # Swallows all errors

2. Always Log Errors

except QldataError as e:
    logger.error(f"Data error: {e}")  # ✓ Log it
    raise

3. Provide Fallbacks

except ConnectionError:
    df = load_cached_data()  # ✓ Fallback to cache

4. Use Retry Logic

@retry(stop=stop_after_attempt(3))
def reliable_fetch():
    return qd.data(...).get()

See Also