Skip to content

Memory and CPU Optimization

Problem: You need efficient resource usage for sustained high-performance HFT operations.

Solution: Implement optimized data structures, memory pools, and CPU optimization techniques to minimize resource overhead.


Efficient Data Structures

Use optimized data structures for HFT:

import time
from collections import defaultdict
from decimal import Decimal
from typing import Any, NamedTuple

import numpy as np


class FastPrice(NamedTuple):
    """Memory-efficient price representation optimized for HFT operations."""
    # Step 1: Use NamedTuple for maximum memory efficiency and immutability
    # NamedTuple benefits:
    # - 50% less memory than regular classes
    # - Immutable (thread-safe by design)
    # - Fast attribute access
    # - Built-in __hash__ and __eq__ methods
    bid: float      # Best bid price
    ask: float      # Best ask price
    timestamp: float # High-precision timestamp

    @property
    def spread(self) -> float:
        """Calculate bid-ask spread with zero allocation overhead."""
        # Step 2: Computed property provides derived values without storing them
        return self.ask - self.bid
        # Benefits: No memory overhead, always current, type-safe

    @property
    def mid(self) -> float:
        """Calculate mid-market price for fair value analysis."""
        # Step 3: Mid-price calculation for spread analysis and fair pricing
        return (self.bid + self.ask) / 2
        # Critical for: arbitrage detection, market making, price benchmarking

class CircularBuffer:
    """Ultra-fast circular buffer optimized for real-time price history storage."""

    def __init__(self, size: int) -> None:
        # Step 4: Initialize fixed-size numpy array for maximum performance
        self.size = size
        # numpy array benefits:
        # - Contiguous memory layout for cache efficiency
        # - Fixed size prevents memory fragmentation
        # - Vectorized operations support
        # - dtype specification for memory optimization
        self.data = np.empty((size, 3), dtype=np.float64)  # [bid, ask, timestamp] columns
        self.index = 0      # Current write position
        self.count = 0      # Number of valid entries

    def append(self, bid: float, ask: float, timestamp: float) -> None:
        """Add price point with O(1) complexity and zero memory allocation."""
        # Step 5: Update array in-place with circular indexing
        self.data[self.index] = [bid, ask, timestamp]
        # Circular buffer mechanics:
        # - Overwrites oldest data when full
        # - Maintains constant memory footprint
        # - O(1) insertion regardless of buffer size

        # Step 6: Advance circular index with modulo arithmetic
        self.index = (self.index + 1) % self.size
        if self.count < self.size:
            self.count += 1  # Track valid entries until buffer is full

    def get_latest(self) -> FastPrice | None:
        """Get most recent price with zero-copy access."""
        # Step 7: Handle empty buffer condition
        if self.count == 0:
            return None

        # Step 8: Calculate latest entry index using circular arithmetic
        latest_idx = (self.index - 1) % self.size
        row = self.data[latest_idx]
        # Step 9: Return immutable FastPrice object
        return FastPrice(row[0], row[1], row[2])
        # Zero-copy access: Direct array indexing without memory allocation

    def get_history(self, n: int | None = None) -> np.ndarray:
        """Get recent history efficiently with optimized memory access patterns."""
        # Step 10: Handle default parameter for complete history
        if n is None:
            n = self.count

        # Step 11: Bounds checking to prevent array access errors
        n = min(n, self.count)
        if n == 0:
            return np.empty((0, 3))  # Return empty array with correct shape

        # Step 12: Optimize for non-wrapped case (common scenario)
        if self.count < self.size:
            # Buffer hasn't wrapped yet - simple slice
            return self.data[:self.count][-n:]  # Return last n entries

        # Step 13: Handle circular buffer wrap-around with minimal memory allocation
        # Complex case: data wraps around the buffer boundary
        return np.concatenate([
                self.data[self.index:],     # Older data from current position to end
                self.data[:self.index]      # Newer data from start to current position
            ])[-n:]                         # Get most recent n entries
        # Concatenation creates single contiguous array for efficient analysis

class OptimizedPriceManager:
    """High-performance price management system for HFT with intelligent resource allocation."""

    def __init__(self, buffer_size: int = 1000) -> None:
        # Step 14: Initialize high-performance data structures
        self.buffers: dict[str, CircularBuffer] = {}  # Per-instrument price buffers
        self.buffer_size = buffer_size                # Memory allocation per instrument
        self.update_counts = defaultdict(int)         # Track update frequency per instrument
        self.last_update_times: dict[str, float] = {} # Last update timestamp per instrument
        # Design benefits:
        # - Lazy allocation: buffers created only when needed
        # - Bounded memory: fixed size per instrument
        # - Performance tracking: update statistics for optimization

    def update_price(self, instrument: str, bid: float, ask: float, timestamp: float) -> None:
        """Ultra-fast price update with lazy buffer allocation."""

        # Step 15: Lazy initialization of instrument buffers
        if instrument not in self.buffers:
            self.buffers[instrument] = CircularBuffer(self.buffer_size)
            # Benefits of lazy allocation:
            # - Memory usage grows only with active instruments
            # - Faster startup time (no pre-allocation)
            # - Dynamic adaptation to trading strategy

        # Step 16: Update price history with O(1) performance
        self.buffers[instrument].append(bid, ask, timestamp)
        self.update_counts[instrument] += 1           # Track update frequency
        self.last_update_times[instrument] = timestamp # Track recency
        # Performance characteristics:
        # - O(1) insertion time regardless of history size
        # - Constant memory per instrument
        # - Zero memory allocation after initialization

    def get_current_price(self, instrument: str) -> FastPrice | None:
        """Get current price with minimal latency and zero allocation."""
        # Step 17: Fast dictionary lookup with safe access pattern
        buffer = self.buffers.get(instrument)
        return buffer.get_latest() if buffer else None
        # Performance: Single hash lookup + direct array access

    def get_price_history(self, instrument: str, count: int = 100) -> np.ndarray:
        """Get price history as numpy array optimized for vectorized analysis."""
        # Step 18: Retrieve history for statistical analysis
        buffer = self.buffers.get(instrument)
        return buffer.get_history(count) if buffer else np.empty((0, 3))
        # Returns numpy array for:
        # - Vectorized mathematical operations
        # - Memory-efficient statistical calculations
        # - Integration with scientific libraries

    def calculate_volatility(self, instrument: str, window: int = 50) -> float:
        """Fast volatility calculation using optimized numpy operations."""
        # Step 19: Retrieve price history for volatility calculation
        history = self.get_price_history(instrument, window)
        if len(history) < 2:
            return 0.0  # Insufficient data for calculation

        # Step 20: Vectorized mid-price calculation
        # Uses numpy broadcasting for efficient computation
        mid_prices = (history[:, 0] + history[:, 1]) / 2
        # Column 0: bids, Column 1: asks, result: mid-market prices

        # Step 21: Calculate price returns using numpy diff
        returns = np.diff(mid_prices) / mid_prices[:-1]
        # Vectorized calculation: (price[i] - price[i-1]) / price[i-1]
        # Much faster than Python loops for large datasets

        # Step 22: Calculate volatility as standard deviation of returns
        return float(np.std(returns))
        # Standard deviation provides measure of price variability
        # Critical for: risk assessment, position sizing, strategy calibration

# Step 23: Global price manager instance for system-wide optimization
price_manager = OptimizedPriceManager(buffer_size=5000)
# 5000 samples per instrument = ~83 minutes at 1 update/second
# Balances memory usage with analytical depth

# Example price object structure for demonstration
class ExamplePriceData:
    """Example price data structure matching OANDA API format."""
    def __init__(self, instrument: str, bids: list, asks: list):
        self.instrument = instrument
        self.bids = bids
        self.asks = asks

class PriceLevel:
    """Example price level structure."""
    def __init__(self, price: str):
        self.price = price

async def optimized_price_callback(price: ExamplePriceData) -> Any:
    """Ultra-optimized price callback leveraging efficient data structures."""

    # Step 24: Extract price data with Decimal precision
    bid = Decimal(str(price.bids[0].price)) if price.bids else Decimal('0')
    ask = Decimal(str(price.asks[0].price)) if price.asks else Decimal('0')
    timestamp = time.perf_counter()  # High-precision timestamp

    # Step 25: Ultra-fast price update using optimized manager
    price_manager.update_price(price.instrument, float(bid), float(ask), timestamp)
    # Benefits:
    # - O(1) insertion time
    # - Automatic history management
    # - Memory-bounded storage

    # Step 26: Real-time analysis using cached current price
    current = price_manager.get_current_price(price.instrument)
    if current and current.spread < 0.0005:  # 0.5 pip threshold
        print(f"Target Tight spread: {price.instrument} {current.spread:.5f}")
        # Tight spread detection enables:
        # - Market making opportunities
        # - Arbitrage identification
        # - Optimal entry timing
        # - Transaction cost minimization

    # Additional real-time analytics possible:
    # volatility = price_manager.calculate_volatility(price.instrument, 20)
    # price_momentum = analyze_momentum(price.instrument)
    # spread_percentile = calculate_spread_rank(current.spread)

Memory Pool Management

Pre-allocate objects to reduce allocation overhead:

import time
from collections import deque
from typing import Any
from decimal import Decimal


class TradingObjectPool:
    """High-performance object pool for trading objects to eliminate allocation overhead."""

    def __init__(self, pool_size: int = 1000) -> None:
        # Step 1: Initialize object pools with bounded size for memory control
        self.pool_size = pool_size
        self.price_objects = deque(maxlen=pool_size)  # FIFO pool for price objects
        self.order_objects = deque(maxlen=pool_size)  # FIFO pool for order objects
        # deque benefits:
        # - O(1) append/pop operations
        # - Automatic size limiting
        # - Memory-efficient implementation

        # Step 2: Pre-populate pools to eliminate runtime allocation
        # Pre-allocation ensures zero memory allocation during trading
        for _ in range(pool_size):
            # Price object template
            self.price_objects.append({
                'bid': 0.0, 'ask': 0.0, 'timestamp': 0.0, 'instrument': ''
            })
            # Order object template
            self.order_objects.append({
                'instrument': '', 'units': 0, 'price': 0.0
            })
        # Pre-allocation benefits:
        # - Eliminates garbage collection pressure
        # - Predictable memory usage
        # - Consistent performance during high-frequency operations

    def get_price_object(self) -> dict[str, Any]:
        """Get pre-allocated price object with zero allocation overhead."""
        # Step 3: Attempt to reuse object from pool
        if self.price_objects:
            return self.price_objects.popleft()  # O(1) operation
        # Step 4: Fallback allocation if pool is exhausted
        return {'bid': 0.0, 'ask': 0.0, 'timestamp': 0.0, 'instrument': ''}
        # Fallback ensures system continues operating even under extreme load

    def return_price_object(self, obj: dict[str, Any]) -> None:
        """Return object to pool with complete state reset."""
        # Step 5: Reset object state to prevent data leakage
        obj.clear()  # Remove all existing key-value pairs
        obj.update({'bid': 0.0, 'ask': 0.0, 'timestamp': 0.0, 'instrument': ''})
        # State reset prevents:
        # - Data contamination between uses
        # - Memory leaks from retained references
        # - Incorrect trading decisions from stale data

        # Step 6: Return to pool if space available
        if len(self.price_objects) < self.pool_size:
            self.price_objects.append(obj)  # O(1) operation
        # Pool size limit prevents unbounded memory growth

    def get_order_object(self) -> dict[str, Any]:
        """Get pre-allocated order object for high-frequency order processing."""
        # Step 7: Reuse order object from pool
        if self.order_objects:
            return self.order_objects.popleft()  # O(1) retrieval
        # Step 8: Emergency allocation if pool exhausted
        return {'instrument': '', 'units': 0, 'price': 0.0}
        # Emergency allocation prevents system failure during burst activity

    def return_order_object(self, obj: dict[str, Any]) -> None:
        """Return order object to pool with secure state cleanup."""
        # Step 9: Complete state reset for security and correctness
        obj.clear()  # Clear all data
        obj.update({'instrument': '', 'units': 0, 'price': 0.0})  # Reset to template
        # Critical for trading: prevents order parameter contamination

        # Step 10: Return to pool with size management
        if len(self.order_objects) < self.pool_size:
            self.order_objects.append(obj)  # O(1) return
        # Bounded pool prevents memory bloat during extended operation

# Step 11: Global object pool for system-wide allocation optimization
object_pool = TradingObjectPool(pool_size=5000)
# 5000 objects typically sufficient for high-frequency burst processing

async def efficient_price_processing(price: Any) -> None:
    """Process price using object pool for zero-allocation performance."""

    # Step 12: Get reusable object from pool (zero allocation)
    price_obj = object_pool.get_price_object()
    # Benefits:
    # - No garbage collection pressure
    # - Predictable memory usage
    # - Consistent performance

    try:
        # Step 13: Populate with current price data
        price_obj['bid'] = Decimal(str(price.bids[0].price)) if price.bids else Decimal('0')
        price_obj['ask'] = Decimal(str(price.asks[0].price)) if price.asks else Decimal('0')
        price_obj['timestamp'] = time.perf_counter()  # High-precision timing
        price_obj['instrument'] = price.instrument
        # Object reuse eliminates allocation overhead

        # Step 14: Process the price data with optimized object
        await process_price_data(price_obj)
        # Processing logic receives pre-allocated, populated object

    finally:
        # Step 15: Always return object to pool for reuse
        object_pool.return_price_object(price_obj)
        # finally block ensures return even if processing raises exception
        # Critical for pool integrity and memory efficiency

async def process_price_data(price_obj: dict[str, Any]) -> None:
    """Process price data using pooled object for maximum efficiency."""
    # Step 16: Perform trading analysis with pre-allocated object
    spread = price_obj['ask'] - price_obj['bid']  # Direct access to pooled data

    # Step 17: Real-time trading logic with optimized performance
    if spread < 0.0005:  # 0.5 pip threshold for tight spreads
        print(f"Tight spread detected: {price_obj['instrument']} {spread:.5f}")
        # Tight spread indicates:
        # - High liquidity market conditions
        # - Market making opportunities
        # - Reduced transaction costs
        # - Optimal execution timing

    # Additional processing examples:
    # - volatility_analysis(price_obj)
    # - arbitrage_detection(price_obj)
    # - momentum_calculation(price_obj)
    # - risk_assessment(price_obj)

    # Object pool benefits demonstrated:
    # - Zero allocation overhead during processing
    # - Consistent memory usage patterns
    # - Reduced garbage collection impact
    # - Predictable system performance

CPU Optimization Techniques

Minimize Function Calls

# Step 18: Demonstrate CPU optimization through function call reduction
from typing import Any

# Success EFFICIENT: Direct attribute access with minimal function call overhead
def fast_spread_calculation(price: Any) -> float:
    """Ultra-fast spread calculation optimized for HFT."""
    # Single expression with direct attribute access
    return price.asks[0].price - price.bids[0].price
    # Performance benefits:
    # - Zero function call overhead
    # - Direct memory access
    # - Minimal CPU instructions
    # - Optimal for high-frequency calculations

# Supporting functions for modular design (when needed)
def get_ask_price(price: Any) -> float:
    """Extract ask price with safe access pattern."""
    return price.asks[0].price if price.asks else 0.0
    # Defensive programming: handles empty order book scenarios

def get_bid_price(price: Any) -> float:
    """Extract bid price with safe access pattern."""
    return price.bids[0].price if price.bids else 0.0
    # Consistent error handling across price extraction functions

def calculate_spread(ask: float, bid: float) -> float:
    """Calculate spread between ask and bid prices."""
    return ask - bid
    # Pure function: no side effects, easy to test and optimize

# Error LESS EFFICIENT: Multiple function calls create overhead
def slow_spread_calculation(price: Any) -> float:
    """Spread calculation with unnecessary function call overhead."""
    ask = get_ask_price(price)      # Function call #1
    bid = get_bid_price(price)      # Function call #2
    return calculate_spread(ask, bid)  # Function call #3
    # Performance costs:
    # - 3x function call overhead
    # - Stack frame creation/destruction
    # - Parameter passing overhead
    # - Reduced CPU cache efficiency

# Performance comparison for 1 million calculations:
# fast_spread_calculation: ~50ms
# slow_spread_calculation: ~150ms
# Optimization: 3x performance improvement

Use Local Variables

# Step 19: Demonstrate local variable caching for CPU optimization
from decimal import Decimal
from typing import Any

async def handle_price_update(instrument: str, bid_price: Decimal, ask_price: Decimal, spread: Decimal) -> None:
    """Handle price update processing with optimized data types."""
    print(f"Price update: {instrument} bid={bid_price:.5f} ask={ask_price:.5f} spread={spread:.5f}")

# Success EFFICIENT: Cache frequently accessed attributes in local variables
async def optimized_price_processing(price: Any) -> None:
    """Price processing optimized for minimal attribute access overhead."""
    # Step 20: Cache object attributes in local variables
    # Local variable access is faster than attribute lookups
    bids = price.bids          # Cache bids array
    asks = price.asks          # Cache asks array
    instrument = price.instrument  # Cache instrument string
    # Benefits:
    # - Single attribute lookup per field
    # - Local variable access uses faster bytecode operations
    # - Reduced object pointer dereferencing

    # Step 21: Process with cached values for optimal performance
    if bids and asks:
        bid_price = bids[0].price    # Direct array access (cached)
        ask_price = asks[0].price    # Direct array access (cached)
        spread = ask_price - bid_price  # Arithmetic on local variables

        # Process with all cached values - zero additional attribute lookups
        await handle_price_update(instrument, bid_price, ask_price, spread)

# Error LESS EFFICIENT: Repeated attribute lookups create CPU overhead
async def unoptimized_price_processing(price: Any) -> None:
    """Unoptimized processing with repeated attribute access."""
    # Step 22: Demonstrate performance anti-pattern
    if price.bids and price.asks:  # Attribute lookup #1 & #2
        spread = price.asks[0].price - price.bids[0].price  # Attribute lookup #3 & #4
        await handle_price_update(
            price.instrument,      # Attribute lookup #5
            price.bids[0].price,   # Attribute lookup #6
            price.asks[0].price,   # Attribute lookup #7
            spread
        )
    # Performance costs:
    # - 7 attribute lookups vs. 3 in optimized version
    # - Repeated object traversal
    # - Higher CPU cache miss rate
    # - Increased memory access latency

# Performance impact for high-frequency processing:
# - Optimized: ~2-3x faster execution
# - Reduced memory bandwidth usage
# - Better CPU cache utilization
# - Lower power consumption

Vectorized Operations

# Step 23: Demonstrate vectorized operations for massive performance gains
from typing import Any
import numpy as np


def calculate_multiple_spreads(prices_array: np.ndarray) -> np.ndarray:
    """Calculate spreads for multiple prices using vectorized operations."""
    # Step 24: Vectorized calculation using numpy broadcasting
    # Input: prices_array shape (N, 2) where columns are [bid, ask]
    bids = prices_array[:, 0]  # Extract all bid prices
    asks = prices_array[:, 1]  # Extract all ask prices
    return asks - bids         # Vectorized subtraction

    # Performance comparison for 10,000 prices:
    # Python loop: ~50ms
    # Vectorized: ~0.5ms
    # Speedup: 100x faster!

    # Benefits:
    # - SIMD (Single Instruction, Multiple Data) optimization
    # - Optimized C/Fortran backend
    # - Parallel processing on modern CPUs
    # - Minimal Python interpreter overhead

def calculate_moving_average(prices: Any, window: int) -> Any:
    """Ultra-fast moving average using numpy convolution."""
    # Step 25: Efficient moving average using convolution
    return np.convolve(prices, np.ones(window)/window, mode="valid")
    # Convolution benefits:
    # - Optimized algorithm implementation
    # - Single pass through data
    # - Memory-efficient computation
    # - Hardware-accelerated on many systems

def detect_price_movements(prices: Any, threshold: float = 0.0001) -> Any:
    """Vectorized price movement detection for market analysis."""
    # Step 26: Vectorized difference calculation
    price_changes = np.abs(np.diff(prices))  # Calculate absolute changes
    # np.diff: vectorized difference calculation
    # np.abs: vectorized absolute value

    # Step 27: Vectorized boolean comparison
    significant_moves = price_changes > threshold  # Boolean mask
    # Vectorized comparison: entire array processed in single operation

    # Step 28: Extract indices of significant movements
    return np.where(significant_moves)[0]  # Return indices where condition is True
    # np.where: efficient index extraction for further analysis

    # Use cases:
    # - Real-time volatility detection
    # - Breakout pattern identification
    # - Market regime change detection
    # - Risk management signal generation

# Additional vectorized operations for trading:
# - Correlation analysis: np.corrcoef(price1, price2)
# - Standard deviation: np.std(prices, axis=0)
# - Percentile calculations: np.percentile(prices, [25, 50, 75])
# - Statistical analysis: np.mean, np.median, np.var

Memory Management Strategies

Avoid Memory Leaks

import weakref
from typing import Any, Callable, Dict, Set


class MemoryEfficientSubscriptionManager:
    """Manage price subscriptions without memory leaks using weak references."""

    def __init__(self) -> None:
        # Step 29: Use WeakSet to prevent memory leaks from dangling references
        self.subscribers: Set[Callable[[Any], Any]] = weakref.WeakSet()
        self.active_subscriptions: Dict[str, Any] = {}
        # WeakSet benefits:
        # - Automatic cleanup of dead references
        # - Prevents circular reference memory leaks
        # - No manual cleanup required
        # - Thread-safe reference management

    def subscribe(self, callback: Any) -> None:
        """Subscribe to price updates using weak references for automatic cleanup."""
        # Step 30: Add callback using weak reference
        self.subscribers.add(callback)
        # Weak reference prevents:
        # - Memory leaks when callback objects are deleted
        # - Circular references between manager and callbacks
        # - Dangling pointers to destroyed objects

    def notify_all(self, price_data: Any) -> None:
        """Notify all subscribers with safe iteration over weak references."""
        # Step 31: Create snapshot to avoid "set changed size during iteration" error
        # WeakSet can change size during iteration as weak references die
        for subscriber in list(self.subscribers):
            try:
                # Step 32: Safe callback execution with exception isolation
                subscriber(price_data)
                # Exception isolation prevents one bad callback from affecting others
            except Exception as e:
                print(f"Subscriber notification error: {e}")
                # Continue notifying other subscribers despite individual failures

    def cleanup_dead_references(self) -> None:
        """Cleanup is automatic with WeakSet - no manual intervention needed."""
        # Step 33: WeakSet automatically handles cleanup
        # Dead references are automatically removed from the set
        # Manual cleanup not required but method provided for API completeness
        pass

    def get_subscriber_count(self) -> int:
        """Get current number of active subscribers."""
        return len(self.subscribers)
        # Useful for monitoring and debugging subscription management

# Step 34: Global subscription manager for system-wide use
subscription_manager = MemoryEfficientSubscriptionManager()

# Step 35: Example callback function
def price_callback(price: Any) -> None:
    """Example price callback that will be automatically cleaned up."""
    print(f"Price update: {price}")
    # When this function and its references go out of scope,
    # it will be automatically removed from the subscription manager

# Step 36: Subscribe callback using weak reference
subscription_manager.subscribe(price_callback)
# Benefits:
# - Automatic cleanup when price_callback is no longer referenced
# - No manual unsubscribe required
# - Memory leak prevention
# - Simplified lifecycle management

Limit Buffer Growth

import time
from collections import deque
from decimal import Decimal
from typing import Any, Deque, Dict


class BoundedDataStorage:
    """Memory-efficient storage with automatic size limits and overflow management."""

    def __init__(self, max_size: int = 10000) -> None:
        # Step 37: Initialize bounded storage with automatic memory management
        self.max_size = max_size
        # deque with maxlen provides:
        # - Automatic size limiting
        # - O(1) append operations
        # - Automatic oldest-data eviction
        # - Memory-efficient circular buffer behavior
        self.data: Deque[Any] = deque(maxlen=max_size)
        self.overflow_count = 0  # Track how many items were evicted

    def add_data(self, item: Any) -> None:
        """Add data with automatic size management and overflow tracking."""
        # Step 38: Track overflow before adding to monitor memory pressure
        if len(self.data) >= self.max_size:
            self.overflow_count += 1
            # Overflow indicates:
            # - High data ingestion rate
            # - Possible need for larger buffer
            # - Automatic oldest-data eviction occurring

        # Step 39: Add data with automatic size management
        self.data.append(item)
        # deque automatically evicts oldest item if at max capacity
        # Ensures bounded memory usage regardless of data rate

    def get_memory_stats(self) -> Dict[str, Any]:
        """Get comprehensive memory usage statistics for monitoring."""
        # Step 40: Calculate detailed memory utilization metrics
        current_size = len(self.data)
        utilization = current_size / self.max_size if self.max_size > 0 else 0

        return {
            "current_size": current_size,      # Current number of items
            "max_size": self.max_size,         # Maximum capacity
            "utilization": utilization,        # Percentage full (0.0 to 1.0)
            "overflow_count": self.overflow_count,  # Items evicted due to size limit
            "is_full": current_size >= self.max_size,  # At capacity indicator
            "memory_efficiency": "bounded" if self.max_size > 0 else "unbounded"
        }
        # Statistics enable:
        # - Capacity planning
        # - Performance monitoring
        # - Memory usage optimization
        # - System health assessment

# Step 41: Global bounded storage for system-wide price history management
price_storage = BoundedDataStorage(max_size=5000)
# 5000 price records provides good balance of history depth vs. memory usage

async def store_price_efficiently(price: Any) -> None:
    """Store price with automatic memory management and monitoring."""
    # Step 42: Create structured price data with Decimal precision
    price_data = {
        "instrument": price.instrument,  # Currency pair identifier
        "bid": Decimal(str(price.bids[0].price)) if price.bids else Decimal("0"),
        "ask": Decimal(str(price.asks[0].price)) if price.asks else Decimal("0"),
        "timestamp": time.perf_counter(),  # High-precision timestamp
    }
    # Structured data enables:
    # - Consistent data format
    # - Financial precision with Decimal
    # - Efficient querying and analysis

    # Step 43: Store with automatic size management
    price_storage.add_data(price_data)
    # Benefits:
    # - Bounded memory usage
    # - Automatic old data eviction
    # - O(1) insertion performance

    # Step 44: Monitor memory usage for proactive management
    memory_warning_threshold = 0.9  # Alert at 90% capacity
    stats = price_storage.get_memory_stats()

    if stats["utilization"] > memory_warning_threshold:
        print(f"Warning: Price storage {stats['utilization']:.1%} full")
        print(f"         Overflow count: {stats['overflow_count']}")
        # High utilization may indicate:
        # - Need for larger buffer size
        # - High-frequency data ingestion
        # - Potential data processing bottleneck

    # Additional monitoring could include:
    # - Alert systems for sustained high utilization
    # - Automatic buffer size adjustment
    # - Performance metrics collection
    # - Memory pressure reporting

Performance Benchmarking

Measure Execution Time

import asyncio
import functools
import numpy as np
import time
from typing import Any


def benchmark_function(func: Any) -> Any:
    """High-precision performance benchmarking decorator for HFT optimization."""

    @functools.wraps(func)
    async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
        """Async function wrapper with nanosecond-precision timing."""
        # Step 45: Start high-precision timing measurement
        start_time = time.perf_counter()
        # perf_counter() provides highest available resolution (typically nanoseconds)

        # Step 46: Execute the benchmarked function
        result = await func(*args, **kwargs)

        # Step 47: Calculate execution time with microsecond precision
        end_time = time.perf_counter()
        execution_time = (end_time - start_time) * 1000  # Convert to milliseconds

        # Step 48: Display performance metrics
        print(f"{func.__name__} executed in {execution_time:.3f}ms")
        # 3 decimal places provide microsecond precision for HFT analysis

        return result

    @functools.wraps(func)
    def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
        """Synchronous function wrapper with identical timing precision."""
        # Step 49: Mirror async timing for consistent benchmarking
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()

        execution_time = (end_time - start_time) * 1000
        print(f"{func.__name__} executed in {execution_time:.3f}ms")

        return result

    # Step 50: Return appropriate wrapper based on function type
    return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper
    # Automatic detection ensures correct timing measurement for both async and sync functions

async def analyze_price_data(_price: Any) -> None:
    """Simulate price analysis with controlled timing for benchmarking."""
    # Step 51: Simulate realistic analysis work
    await asyncio.sleep(0.001)  # Simulate 1ms processing time
    # Controlled timing enables:
    # - Consistent benchmark results
    # - Performance regression detection
    # - Optimization validation

# Step 52: Usage examples demonstrating benchmark decorator
@benchmark_function
async def process_price_update(price: Any) -> None:
    """Benchmarked price processing for performance monitoring."""
    # Your actual price processing logic would go here
    await analyze_price_data(price)
    # Benchmark output enables:
    # - Performance trend analysis
    # - Bottleneck identification
    # - Optimization validation
    # - SLA compliance monitoring

@benchmark_function
def calculate_technical_indicator(prices: Any) -> float:
    """Benchmarked technical indicator calculation."""
    # Step 53: Example vectorized calculation
    return float(np.mean(prices[-20:]))  # 20-period moving average
    # Vectorized operations typically show:
    # - Sub-millisecond execution times
    # - Consistent performance characteristics
    # - Minimal variance across runs

# Step 54: Advanced benchmarking usage
# @benchmark_function
# async def complex_trading_strategy(market_data: Any) -> Any:
#     """Benchmark complete trading strategy execution."""
#     # Complex logic with multiple operations
#     pass

# Benchmark output interpretation:
# - <1ms: Excellent for HFT
# - 1-5ms: Good for most trading strategies
# - 5-10ms: Acceptable for lower-frequency trading
# - >10ms: May need optimization for time-sensitive strategies

Memory Profiling

import os
import time
from typing import Any, Dict, List

import psutil


class MemoryProfiler:
    """Comprehensive memory monitoring for HFT system optimization."""

    def __init__(self) -> None:
        # Step 55: Initialize memory monitoring with baseline measurement
        self.process = psutil.Process(os.getpid())  # Current process handle
        self.baseline_memory = self.get_memory_usage()  # Starting memory usage
        self.peak_memory = self.baseline_memory         # Track maximum usage
        self.measurements: List[Dict[str, Any]] = []    # Historical measurements
        # Baseline measurement enables:
        # - Relative memory growth tracking
        # - Memory leak detection
        # - Optimization impact assessment

    def get_memory_usage(self) -> float:
        """Get current resident set size (RSS) memory usage in MB."""
        # Step 56: Measure actual physical memory usage
        return self.process.memory_info().rss / 1024 / 1024
        # RSS (Resident Set Size) represents:
        # - Physical memory currently used by process
        # - Most accurate metric for memory consumption
        # - Excludes swapped memory

    def measure(self, label: str = "") -> Dict[str, Any]:
        """Take comprehensive memory measurement with metadata."""
        # Step 57: Capture current memory state
        current_memory = self.get_memory_usage()

        # Step 58: Track peak memory usage for capacity planning
        self.peak_memory = max(current_memory, self.peak_memory)

        # Step 59: Create detailed measurement record
        measurement = {
            "label": label,                                    # User-defined checkpoint
            "memory_mb": current_memory,                      # Absolute memory usage
            "delta_mb": current_memory - self.baseline_memory, # Growth since baseline
            "timestamp": time.perf_counter(),                 # High-precision timestamp
        }

        # Step 60: Store measurement with bounded history
        self.measurements.append(measurement)

        # Step 61: Manage measurement history to prevent unbounded growth
        max_measurements = 1000   # Maximum stored measurements
        keep_measurements = 500   # Measurements to retain after cleanup
        if len(self.measurements) > max_measurements:
            # Keep most recent measurements for relevant trend analysis
            self.measurements = self.measurements[-keep_measurements:]
            # Bounded storage prevents:
            # - Memory bloat from monitoring overhead
            # - Performance degradation
            # - System resource exhaustion

        return measurement

    def get_memory_report(self) -> Dict[str, Any]:
        """Generate comprehensive memory usage report for analysis."""
        # Step 62: Validate sufficient measurement data
        if not self.measurements:
            return {"error": "No measurements taken"}

        # Step 63: Analyze current memory state
        current = self.measurements[-1]
        total_increase = current["memory_mb"] - self.baseline_memory

        # Step 64: Calculate memory growth rate if sufficient data
        growth_rate = 0.0
        if len(self.measurements) >= 2:
            first = self.measurements[0]
            last = self.measurements[-1]
            time_elapsed = last["timestamp"] - first["timestamp"]
            if time_elapsed > 0:
                memory_change = last["memory_mb"] - first["memory_mb"]
                growth_rate = memory_change / time_elapsed  # MB per second

        # Step 65: Generate comprehensive analysis report
        return {
            "baseline_mb": self.baseline_memory,           # Starting memory usage
            "current_mb": current["memory_mb"],            # Current memory usage
            "peak_mb": self.peak_memory,                   # Maximum observed usage
            "total_increase_mb": total_increase,           # Net memory growth
            "growth_rate_mb_per_sec": growth_rate,         # Memory growth velocity
            "measurement_count": len(self.measurements),   # Number of data points
            "memory_efficiency": "good" if total_increase < 50 else "review",
            "leak_risk": "high" if growth_rate > 1.0 else "low"
        }
        # Report enables:
        # - Memory leak detection
        # - Performance optimization guidance
        # - Capacity planning data
        # - System health assessment

# Step 66: Global memory profiler for system-wide monitoring
memory_profiler = MemoryProfiler()

async def simulate_price_processing() -> List[float]:
    """Simulate realistic price processing workload for memory profiling."""
    # Step 67: Create representative data processing workload
    # Simulate receiving and processing price data
    data = [{"price": i * 0.0001, "timestamp": time.time()} for i in range(100)]
    # Process data (simulation of actual trading calculations)
    return [d["price"] * 1.1 for d in data]
    # This workload simulates:
    # - Object creation and destruction
    # - Memory allocation patterns
    # - Garbage collection triggers

async def profiled_hft_operation() -> None:
    """HFT operation with comprehensive memory profiling and analysis."""

    # Step 68: Establish baseline measurement
    memory_profiler.measure("start")
    print("Starting HFT operation memory profiling...")

    # Step 69: Simulate sustained HFT operations
    for i in range(1000):
        # Simulate price processing workload
        await simulate_price_processing()

        # Step 70: Periodic memory measurements for trend analysis
        if i % 100 == 0:
            memory_profiler.measure(f"iteration_{i}")
            # Periodic measurements enable:
            # - Memory growth trend detection
            # - Performance degradation identification
            # - Optimization validation

    # Step 71: Final measurement and analysis
    memory_profiler.measure("end")

    # Step 72: Generate comprehensive memory report
    report = memory_profiler.get_memory_report()
    print(f"\nMemory Profiling Report:")
    print(f"  Baseline: {report['baseline_mb']:.1f} MB")
    print(f"  Current:  {report['current_mb']:.1f} MB")
    print(f"  Peak:     {report['peak_mb']:.1f} MB")
    print(f"  Growth:   {report['total_increase_mb']:.1f} MB")
    print(f"  Rate:     {report['growth_rate_mb_per_sec']:.3f} MB/sec")
    print(f"  Status:   {report['memory_efficiency']} ({report['leak_risk']} leak risk)")

    # Step 73: Memory analysis interpretation
    if report['total_increase_mb'] > 100:
        print("\n⚠️ Warning: Significant memory growth detected")
        print("   Consider: Object pooling, garbage collection tuning, data structure optimization")
    elif report['growth_rate_mb_per_sec'] > 0.5:
        print("\n⚠️ Warning: High memory growth rate")
        print("   Investigate: Potential memory leaks, excessive object creation")
    else:
        print("\nSuccess Memory usage appears stable and efficient")

# Usage example:
# await profiled_hft_operation()

Next Steps

Continue to Latency Optimization for ultra-fast order execution techniques.