{"nbformat":4,"nbformat_minor":5,"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.0"},"colab":{"provenance":[]}},"cells":[{"cell_type":"markdown","id":"md_54104704","metadata":{"id":"md_54104704"},"source":["# Paper Trading Engine\n","\n","## Overview\n","\n","This notebook presents a self-contained paper trading engine designed for the simulation of order execution logic. The engine operates without real market interaction, providing a controlled environment for strategy evaluation.\n","\n","### Design Principles\n","\n","*   **API Parity:** The engine's API mirrors live execution classes, facilitating seamless transition from simulation to production.\n","*   **State Management:** Comprehensive tracking of simulated P&L, account balance, and historical position data is maintained in-memory.\n","*   **Independence:** The engine is implemented with minimal external dependencies, ensuring a lightweight and self-sufficient operational framework.\n","*   **Flexible Price Input:** Supports various price sources, including historical datasets and live data feeds.\n","\n","### Applications\n","\n","*   **Strategy Validation:** Pre-deployment testing and validation of trading algorithms.\n","*   **Risk Parameter Optimization:** Calibration and tuning of risk management parameters, such as Take-Profit (TP) and Stop-Loss (SL) levels.\n","*   **Educational Contexts:** Utilized as a tool for teaching and demonstrating algorithmic trading concepts."]},{"cell_type":"markdown","id":"md_42774699","metadata":{"id":"md_42774699"},"source":["## 1. Module Imports\n","\n","This section imports all necessary Python libraries for the paper trading engine. Each module serves a specific function within the simulation framework."]},{"cell_type":"code","id":"cd_25397870","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"cd_25397870","executionInfo":{"status":"ok","timestamp":1779274485426,"user_tz":-300,"elapsed":22,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}},"outputId":"28d29c90-964c-4220-86b2-55556078bde1"},"execution_count":8,"outputs":[{"output_type":"stream","name":"stdout","text":["Required modules imported successfully.\n"]}],"source":["import time\n","import random\n","import datetime\n","import math\n","import pandas as pd\n","import numpy as np\n","import matplotlib.pyplot as plt\n","from dataclasses import dataclass, field\n","from typing import Optional\n","\n","# Confirm successful import of all required modules.\n","print(\"Required modules imported successfully.\")"]},{"cell_type":"markdown","id":"md_68820675","metadata":{"id":"md_68820675"},"source":["## 2. Configuration Parameters\n","\n","This section defines the core parameters for the paper trading engine and the simulation. These parameters control strategy behavior, financial mechanics, and simulation settings."]},{"cell_type":"code","id":"cd_62771295","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"cd_62771295","executionInfo":{"status":"ok","timestamp":1779274509628,"user_tz":-300,"elapsed":49,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}},"outputId":"8efe6ca8-2ee9-49e3-8b85-5506d0bb0184"},"execution_count":9,"outputs":[{"output_type":"stream","name":"stdout","text":["Paper Trading Engine Initialized | Balance: $10,000.00 USDT\n"]}],"source":["# ── Strategy-Specific Configuration ───────────────────────────────────────\n","# Parameters dictating the trading strategy's characteristics.\n","config_strategy = {\n","    \"name\": \"btc_paper_1h\",          # Unique identifier for the strategy.\n","    \"symbol\": \"BTC\",                  # Trading instrument symbol (e.g., 'BTC', 'ETH').\n","    \"time_horizon\": \"1h\",             # Timeframe of the strategy (e.g., '1h', '4h', '1d').\n","    \"live_tp_percent\": 2.0,           # Take-profit threshold as a percentage of entry price.\n","    \"live_sl_percent\": 1.0,           # Stop-loss threshold as a percentage of entry price.\n","}\n","\n","# ── Engine-Specific Configuration ─────────────────────────────────────────\n","# Financial and operational parameters for the simulation engine.\n","INITIAL_BALANCE    = 10_000.0   # Starting capital in USDT for the simulation.\n","ALLOCATION_PERCENT = 50         # Percentage of the total balance allocated per trade (e.g., 50 means 50% of capital).\n","MAKER_FEE          = 0.0002     # Maker transaction fee rate (e.g., 0.0002 for 0.02%).\n","TAKER_FEE          = 0.0004     # Taker transaction fee rate (e.g., 0.0004 for 0.04%).\n","\n","# Output initial balance for verification.\n","print(f\"Paper Trading Engine Initialized | Balance: ${INITIAL_BALANCE:,.2f} USDT\")"]},{"cell_type":"markdown","id":"md_50005833","metadata":{"id":"md_50005833"},"source":["## 3. Trade Record Structure\n","\n","This section defines the structure for each completed trading operation within the simulation engine. Instead of a dedicated class, trade details are encapsulated in a dictionary format. Each dictionary instance serves as an immutable record, providing a comprehensive audit trail for performance analysis and post-mortem evaluation.\n","\n","### Trade Record Fields:\n","\n","*   **`entry_time`**: Datetime of position open.\n","*   **`exit_time`**: Datetime of position close.\n","*   **`symbol`**: Asset traded.\n","*   **`direction`**: 'long' | 'short'.\n","*   **`entry_price`**: Fill price at open.\n","*   **`exit_price`**: Fill price at close.\n","*   **`quantity`**: Base asset quantity.\n","*   **`pnl_usd`**: Realized P&L in USD.\n","*   **`pnl_pct`**: Realized P&L as percentage of entry notional.\n","*   **`reason`**: Close reason ('take_profit', 'stop_loss', 'direction_change', 'end_of_simulation')."]},{"cell_type":"code","id":"cd_39926625","metadata":{"id":"cd_39926625","executionInfo":{"status":"ok","timestamp":1779274617034,"user_tz":-300,"elapsed":3,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"execution_count":10,"outputs":[],"source":["# The 'Trade' dataclass has been removed as per the user's request.\n","# Trade records will now be represented as dictionaries with the structure detailed in the preceding markdown cell."]},{"cell_type":"markdown","id":"md_37529526","metadata":{"id":"md_37529526"},"source":["## 4. Functional Trading Engine Core\n","\n","This section redefines the core logic of the paper trading engine using a functional programming paradigm, replacing the previous class-based implementation. The engine's state is explicitly passed between functions, promoting modularity and testability. Each function performs a specific operation on the engine's state or related data, adhering to the principle of statelessness where applicable.\n","\n","### Core Components:\n","\n","*   **State Initialization**: Sets up the initial financial and positional parameters.\n","*   **Price Sourcing**: Retrieves or simulates current market prices.\n","*   **Order Simulation**: Handles trade execution, including slippage and fee calculations.\n","*   **Position Management**: Functions for opening, closing, and monitoring trading positions.\n","*   **Performance Analytics**: Calculates and summarizes key trading metrics.\n","*   **Simulation Orchestration**: Manages the sequential execution of trading logic over time."]},{"cell_type":"code","id":"cd_33906856","metadata":{"id":"cd_33906856","executionInfo":{"status":"ok","timestamp":1779274692573,"user_tz":-300,"elapsed":74,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"execution_count":11,"outputs":[],"source":["# The PaperTradingEngine class has been removed as per the user's request.\n","# Its functionalities are now being refactored into a set of modular functions.\n","# The engine state will be managed through dictionary structures passed between these functions."]},{"cell_type":"markdown","metadata":{"id":"0f49d45e"},"source":["### 4.1. `initialize_engine_state` Function\n","\n","This function initializes the state dictionary for the paper trading engine. It sets up the initial balance, allocation parameters, fee structures, and internal tracking variables for positions and trade history. This state dictionary will be passed as an argument to subsequent functions to maintain and update the simulation's status."],"id":"0f49d45e"},{"cell_type":"code","metadata":{"id":"8298120f","executionInfo":{"status":"ok","timestamp":1779274716071,"user_tz":-300,"elapsed":22,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def initialize_engine_state(symbol: str, initial_balance: float, allocation_pct: float,\n","                            tp_percent: float, sl_percent: float,\n","                            taker_fee: float, maker_fee: float) -> dict:\n","    \"\"\"\n","    Initializes and returns the state dictionary for the paper trading engine.\n","\n","    Parameters:\n","    - symbol (str): Base asset symbol (e.g., 'BTC').\n","    - initial_balance (float): Starting paper balance in USDT.\n","    - allocation_pct (float): Percentage of balance to allocate per trade.\n","    - tp_percent (float): Take-profit trigger (%).\n","    - sl_percent (float): Stop-loss trigger (%).\n","    - taker_fee (float): Taker transaction fee rate.\n","    - maker_fee (float): Maker transaction fee rate.\n","\n","    Returns:\n","    - dict: A dictionary representing the initial state of the trading engine.\n","    \"\"\"\n","    engine_state = {\n","        \"symbol\": symbol.upper(),\n","        \"balance\": initial_balance,\n","        \"initial_balance\": initial_balance,\n","        \"allocation\": allocation_pct / 100,\n","        \"tp_percent\": tp_percent,\n","        \"sl_percent\": sl_percent,\n","        \"taker_fee\": taker_fee,\n","        \"maker_fee\": maker_fee,\n","        \"direction\": 0,  # +1 long | -1 short | 0 flat\n","        \"entry_price\": 0.0,\n","        \"quantity\": 0.0,\n","        \"entry_time\": None,\n","        \"trades\": [],\n","        \"balance_curve\": [initial_balance],\n","        \"time_curve\": [datetime.datetime.now(datetime.UTC)], # Use timezone-aware datetime\n","        \"_last_price\": 50_000.0 # For random walk default price source\n","    }\n","    print(f\"Paper Trading Engine Initialized | Balance: ${engine_state['balance']:,.2f} USDT\")\n","    return engine_state"],"id":"8298120f","execution_count":12,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"bff04ad7"},"source":["### 4.2. `get_current_price` Function\n","\n","This function retrieves the current market price for the asset. It supports an external `price_source` callable or defaults to a simulated random walk for demonstration purposes. The function updates the `_last_price` in the `engine_state` if using the default random walk."],"id":"bff04ad7"},{"cell_type":"code","metadata":{"id":"303794be","executionInfo":{"status":"ok","timestamp":1779274716650,"user_tz":-300,"elapsed":12,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def get_current_price(engine_state: dict, price_source=None) -> float:\n","    \"\"\"\n","    Retrieves the current market price.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine.\n","    - price_source (Callable | None): A callable function that returns the current price.\n","                                       If None, a random walk simulation is used.\n","\n","    Returns:\n","    - float: The current market price.\n","    \"\"\"\n","    if price_source is not None:\n","        return price_source()\n","\n","    # Default: random walk around last price (for standalone demo)\n","    last_price = engine_state.get(\"_last_price\", 50_000.0)\n","    change = last_price * random.gauss(0, 0.002)\n","    current_price = max(100, last_price + change)\n","    engine_state[\"_last_price\"] = current_price # Update last price in state\n","    return round(current_price, 2)"],"id":"303794be","execution_count":13,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"c01acb9b"},"source":["### 4.3. `simulate_fill` Function\n","\n","This function simulates the execution of an order, accounting for transaction fees and potential slippage. Market orders incur a taker fee and simulated slippage, while limit orders incur a maker fee with no slippage. The fill price and fee cost are returned."],"id":"c01acb9b"},{"cell_type":"code","metadata":{"id":"d37eddab","executionInfo":{"status":"ok","timestamp":1779274717296,"user_tz":-300,"elapsed":15,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def simulate_fill(engine_state: dict, side: str, quantity: float,\n","                  price: float, order_type: str = \"market\") -> tuple[float, float]:\n","    \"\"\"\n","    Simulates an order fill, applying realistic slippage and fees.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine.\n","    - side (str): Order side ('buy' or 'sell').\n","    - quantity (float): Quantity of the asset to trade.\n","    - price (float): The requested execution price.\n","    - order_type (str): Type of order ('market' or 'limit').\n","\n","    Returns:\n","    - tuple[float, float]: A tuple containing the fill price after slippage and the calculated fee cost.\n","    \"\"\"\n","    fee_rate = engine_state[\"taker_fee\"] if order_type == \"market\" else engine_state[\"maker_fee\"]\n","\n","    # Slippage model: normally distributed, mean = 1 bps, std = 0.3 bps\n","    if order_type == \"market\":\n","        slippage_bps = abs(random.gauss(1.0, 0.3))\n","        if side == \"buy\":\n","            fill_price = price * (1 + slippage_bps / 10_000)\n","        else:\n","            fill_price = price * (1 - slippage_bps / 10_000)\n","    else:\n","        fill_price = price\n","\n","    fee_cost = quantity * fill_price * fee_rate\n","    return round(fill_price, 2), round(fee_cost, 4)"],"id":"d37eddab","execution_count":14,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"81d07359"},"source":["### 4.4. `open_position` Function\n","\n","This function executes the opening of a simulated trading position based on a given prediction. It calculates the quantity to be traded based on allocation percentage, simulates the order fill (including fees), and updates the engine's state with the new position details. This function only proceeds if there is no current open position or if the new prediction differs from the current direction."],"id":"81d07359"},{"cell_type":"code","metadata":{"id":"9228e109","executionInfo":{"status":"ok","timestamp":1779274829454,"user_tz":-300,"elapsed":23,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def open_position(engine_state: dict, prediction: int, current_price: Optional[float] = None) -> None:\n","    \"\"\"\n","    Opens a simulated trading position in the direction of the prediction.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine.\n","    - prediction (int): Trading signal: +1 for long, -1 for short. 0 implies no action.\n","    - current_price (float | None): Optional. The current market price. If None, `get_current_price` is used.\n","    \"\"\"\n","    # If no prediction or direction matches current position, no action.\n","    if prediction == 0 or engine_state[\"direction\"] == prediction:\n","        return\n","\n","    price = current_price if current_price is not None else get_current_price(engine_state)\n","\n","    # Calculate raw quantity based on allocated balance and price.\n","    raw_qty  = (engine_state[\"balance\"] * engine_state[\"allocation\"]) / price\n","    quantity = round(raw_qty, 6)\n","\n","    # Simulate the order fill to get fill price and fees.\n","    fill_price, fee = simulate_fill(\n","        engine_state,\n","        \"buy\" if prediction == 1 else \"sell\",\n","        quantity,\n","        price\n","    )\n","\n","    # Update engine state after opening position.\n","    engine_state[\"balance\"]      -= fee       # Deduct opening fee from balance.\n","    engine_state[\"direction\"]     = prediction\n","    engine_state[\"entry_price\"]   = fill_price\n","    engine_state[\"quantity\"]      = quantity\n","    engine_state[\"entry_time\"]    = datetime.datetime.now(datetime.UTC) # Use timezone-aware datetime\n","\n","    print(f\"[OPEN] {'LONG' if prediction==1 else 'SHORT'} | \"\n","          f\"Qty: {quantity:.6f} {engine_state['symbol']} | Price: {fill_price:.2f} | \"\n","          f\"Fee: ${fee:.4f}\")"],"id":"9228e109","execution_count":15,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"c581a7c5"},"source":["### 4.5. `close_open_position` Function\n","\n","This function is responsible for closing an active trading position. It simulates the fill for the closing order, calculates the realized Profit and Loss (PnL), updates the `engine_state` (including balance and trade history), and then resets the position-specific variables within the state. A trade record (dictionary) is generated and appended to the `trades` list in the `engine_state`."],"id":"c581a7c5"},{"cell_type":"code","metadata":{"id":"d4d1e96a","executionInfo":{"status":"ok","timestamp":1779274985693,"user_tz":-300,"elapsed":46,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def close_open_position(engine_state: dict, current_price: Optional[float] = None,\n","                            reason: str = \"direction_change\") -> Optional[dict]:\n","    \"\"\"\n","    Closes the current simulated position and records the trade.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine.\n","    - current_price (float | None): Optional. The current market price. If None, `get_current_price` is used.\n","    - reason (str): The reason for closing the position (e.g., 'take_profit', 'stop_loss', 'direction_change').\n","\n","    Returns:\n","    - dict | None: A dictionary representing the closed trade record, or None if no position was open.\n","    \"\"\"\n","    if engine_state[\"direction\"] == 0:\n","        return None\n","\n","    price = current_price if current_price is not None else get_current_price(engine_state)\n","    close_side = \"sell\" if engine_state[\"direction\"] == 1 else \"buy\"\n","    fill_price, fee = simulate_fill(engine_state, close_side, engine_state[\"quantity\"], price)\n","\n","    # ── Compute PnL ────────────────────────────────────────────────────\n","    if engine_state[\"direction\"] == 1:   # Long\n","        pnl_usd = (fill_price - engine_state[\"entry_price\"]) * engine_state[\"quantity\"]\n","        pnl_pct = ((fill_price - engine_state[\"entry_price\"]) / engine_state[\"entry_price\"]) * 100\n","    else:                     # Short\n","        pnl_usd = (engine_state[\"entry_price\"] - fill_price) * engine_state[\"quantity\"]\n","        pnl_pct = ((engine_state[\"entry_price\"] - fill_price) / engine_state[\"entry_price\"]) * 100\n","\n","    pnl_usd -= fee   # Deduct closing fee\n","\n","    # ── Update balance ─────────────────────────────────────────────────\n","    # The balance update should only add the realized PnL (which includes all fees).\n","    # The notional value of the trade was never subtracted from the balance, only the initial fee.\n","    engine_state[\"balance\"] += pnl_usd\n","    engine_state[\"balance_curve\"].append(round(engine_state[\"balance\"], 4))\n","    engine_state[\"time_curve\"].append(datetime.datetime.now(datetime.UTC))\n","\n","    trade = {\n","        \"entry_time\":  engine_state[\"entry_time\"],\n","        \"exit_time\":   datetime.datetime.now(datetime.UTC),\n","        \"symbol\":      engine_state[\"symbol\"],\n","        \"direction\":   \"long\" if engine_state[\"direction\"] == 1 else \"short\",\n","        \"entry_price\": engine_state[\"entry_price\"],\n","        \"exit_price\":  fill_price,\n","        \"quantity\":    engine_state[\"quantity\"],\n","        \"pnl_usd\":     round(pnl_usd, 4),\n","        \"pnl_pct\":     round(pnl_pct, 4),\n","        \"reason\":      reason,\n","    }\n","    engine_state[\"trades\"].append(trade)\n","\n","    print(f\"[CLOSE] {reason.upper()} | Price: {fill_price:.2f} | \"\n","          f\"PnL: ${pnl_usd:.2f} ({pnl_pct:.2f}%) | Balance: ${engine_state['balance']:.2f}\")\n","\n","    # Reset position state in the engine_state dictionary\n","    engine_state[\"direction\"] = 0\n","    engine_state[\"entry_price\"] = 0.0\n","    engine_state[\"quantity\"] = 0.0\n","    engine_state[\"entry_time\"] = None\n","\n","    return trade"],"id":"d4d1e96a","execution_count":25,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"fcfe185a"},"source":["### 4.6. `check_tp_sl` Function\n","\n","This function evaluates the current profit/loss (PnL) of an open position against predefined Take-Profit (TP) and Stop-Loss (SL) thresholds. If the PnL exceeds the TP or falls below the SL, the function triggers the `close_open_position` function, recording the trade and updating the `engine_state`. It returns `True` if the position was closed, `False` otherwise."],"id":"fcfe185a"},{"cell_type":"code","metadata":{"id":"158cda0a","executionInfo":{"status":"ok","timestamp":1779274830619,"user_tz":-300,"elapsed":12,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def check_tp_sl(engine_state: dict, current_price: Optional[float] = None) -> bool:\n","    \"\"\"\n","    Evaluates Take-Profit (TP) and Stop-Loss (SL) conditions for the current open position.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine.\n","    - current_price (float | None): Optional. The current market price. If None, `get_current_price` is used.\n","\n","    Returns:\n","    - bool: True if the position was closed due to TP/SL, False otherwise.\n","    \"\"\"\n","    if engine_state[\"direction\"] == 0:\n","        return True # No open position, so no TP/SL to check\n","\n","    price = current_price if current_price is not None else get_current_price(engine_state)\n","\n","    if engine_state[\"direction\"] == 1: # Long position\n","        pnl_pct = ((price - engine_state[\"entry_price\"]) / engine_state[\"entry_price\"]) * 100\n","    else: # Short position\n","        pnl_pct = ((engine_state[\"entry_price\"] - price) / engine_state[\"entry_price\"]) * 100\n","\n","    print(f\"[MONITOR] PnL: {pnl_pct:.2f}% | TP: +{engine_state['tp_percent']}% | SL: -{engine_state['sl_percent']}%\")\n","\n","    if pnl_pct >= engine_state[\"tp_percent\"]:\n","        close_open_position(engine_state, price, \"take_profit\")\n","        return True\n","    if pnl_pct <= -engine_state[\"sl_percent\"]:\n","        close_open_position(engine_state, price, \"stop_loss\")\n","        return True\n","    return False"],"id":"158cda0a","execution_count":17,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"d28ac524"},"source":["### 4.7. `_max_drawdown` Function\n","\n","This auxiliary function calculates the maximum drawdown percentage from the `balance_curve` stored in the `engine_state`. Maximum drawdown represents the largest peak-to-trough decline in the balance, indicating the largest historical loss from a peak. This metric is crucial for assessing the risk associated with the trading strategy."],"id":"d28ac524"},{"cell_type":"code","metadata":{"id":"b303ca57","executionInfo":{"status":"ok","timestamp":1779274881325,"user_tz":-300,"elapsed":69,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def _max_drawdown(engine_state: dict) -> float:\n","    \"\"\"\n","    Computes the maximum drawdown percentage of the balance curve.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine, containing the 'balance_curve'.\n","\n","    Returns:\n","    - float: The maximum drawdown as a percentage.\n","    \"\"\"\n","    curve = np.array(engine_state[\"balance_curve\"])\n","    if len(curve) < 2:\n","        return 0.0\n","    peak = np.maximum.accumulate(curve)\n","    drawdown = (curve - peak) / peak * 100\n","    return abs(drawdown.min())"],"id":"b303ca57","execution_count":18,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"0fd5b0b0"},"source":["### 4.8. `_sharpe_ratio` Function\n","\n","This auxiliary function calculates the annualized Sharpe ratio based on the PnL percentages of completed trades. The Sharpe ratio measures the excess return per unit of risk, with a higher value indicating better risk-adjusted performance. A risk-free rate can be specified, though it defaults to 0.0 for simplicity."],"id":"0fd5b0b0"},{"cell_type":"code","metadata":{"id":"df76d0b9","executionInfo":{"status":"ok","timestamp":1779274882049,"user_tz":-300,"elapsed":198,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def _sharpe_ratio(engine_state: dict, risk_free_rate: float = 0.0) -> float:\n","    \"\"\"\n","    Calculates the annualized Sharpe ratio from trade PnL percentages.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine, containing 'trades'.\n","    - risk_free_rate (float): The risk-free return rate (default is 0.0).\n","\n","    Returns:\n","    - float: The annualized Sharpe ratio.\n","    \"\"\"\n","    pnls = [t[\"pnl_pct\"] for t in engine_state[\"trades\"]]\n","    if len(pnls) < 2:\n","        return 0.0\n","    excess_returns = np.array(pnls) - risk_free_rate\n","    if np.std(excess_returns) == 0:\n","        return 0.0\n","    # Assuming daily returns for annualization (sqrt(252 trading days))\n","    return (np.mean(excess_returns) / np.std(excess_returns)) * math.sqrt(252)"],"id":"df76d0b9","execution_count":19,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"0d9ff126"},"source":["### 4.9. `get_performance_summary` Function\n","\n","This function compiles key performance metrics from the `engine_state`'s trade history and balance curve. It calculates metrics such as total return, win rate, average win/loss, profit factor, maximum drawdown, and Sharpe ratio. The results are returned as a Pandas DataFrame for clear presentation and analysis."],"id":"0d9ff126"},{"cell_type":"code","metadata":{"id":"3d8260d0","executionInfo":{"status":"ok","timestamp":1779274882584,"user_tz":-300,"elapsed":4,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def get_performance_summary(engine_state: dict) -> pd.DataFrame:\n","    \"\"\"\n","    Computes and returns a summary of key performance metrics from the trade history.\n","\n","    Parameters:\n","    - engine_state (dict): The current state of the trading engine, containing 'trades' and 'balance_curve'.\n","\n","    Returns:\n","    - pd.DataFrame: A DataFrame summarizing the performance metrics.\n","    \"\"\"\n","    if not engine_state[\"trades\"]:\n","        return pd.DataFrame([{\"Message\": \"No trades recorded.\"}])\n","\n","    pnls_usd = [t[\"pnl_usd\"] for t in engine_state[\"trades\"]]\n","    pnls_pct = [t[\"pnl_pct\"] for t in engine_state[\"trades\"]]\n","    wins     = [p for p in pnls_pct if p > 0]\n","    losses   = [p for p in pnls_pct if p <= 0]\n","\n","    total_return = ((engine_state[\"balance\"] - engine_state[\"initial_balance\"]) / engine_state[\"initial_balance\"]) * 100\n","\n","    # Calculate profit factor\n","    total_wins = sum(p for p in pnls_usd if p > 0)\n","    total_losses = abs(sum(p for p in pnls_usd if p < 0))\n","    profit_factor = round(total_wins / total_losses, 2) if total_losses != 0 else float(\"inf\")\n","\n","    summary = {\n","        \"Initial Balance ($)\":   engine_state[\"initial_balance\"],\n","        \"Final Balance ($)\":     round(engine_state[\"balance\"], 2),\n","        \"Total Return (%)\":      round(total_return, 2),\n","        \"Total Trades\":          len(engine_state[\"trades\"]),\n","        \"Win Rate (%)\":          round(len(wins) / max(len(engine_state[\"trades\"]), 1) * 100, 1),\n","        \"Avg Win (%)\":           round(np.mean(wins),   2) if wins   else 0,\n","        \"Avg Loss (%)\":          round(np.mean(losses), 2) if losses else 0,\n","        \"Profit Factor\":         profit_factor,\n","        \"Max Drawdown (%)\":      round(_max_drawdown(engine_state), 2),\n","        \"Sharpe Ratio\":          round(_sharpe_ratio(engine_state), 2),\n","        \"Take Profit Hits\":      sum(1 for t in engine_state[\"trades\"] if t[\"reason\"] == \"take_profit\"),\n","        \"Stop Loss Hits\":        sum(1 for t in engine_state[\"trades\"] if t[\"reason\"] == \"stop_loss\"),\n","    }\n","    return pd.DataFrame([summary]).T.rename(columns={0: \"Value\"})"],"id":"3d8260d0","execution_count":20,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"9134512f"},"source":["### 4.10. `run_simulation` Function\n","\n","This function serves as the primary orchestrator for the paper trading simulation. It iterates through a sequence of time-series predictions and corresponding market prices, applying the trading logic at each step. It handles position opening and closing based on signals and monitors for Take-Profit and Stop-Loss conditions. Finally, it generates a comprehensive performance summary upon completion."],"id":"9134512f"},{"cell_type":"code","metadata":{"id":"29d77fdd","executionInfo":{"status":"ok","timestamp":1779274883154,"user_tz":-300,"elapsed":4,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}}},"source":["def run_simulation(engine_state: dict, predictions: list[tuple], prices: list[float]) -> pd.DataFrame:\n","    \"\"\"\n","    Runs a batch simulation using a list of time-series predictions and corresponding prices.\n","\n","    Parameters:\n","    - engine_state (dict): The initial state of the trading engine.\n","    - predictions (list[tuple]): A list of (datetime, signal) tuples, where signal is +1 (long), -1 (short), or 0 (no action).\n","    - prices (list[float]): A list of corresponding market prices for each prediction timestamp.\n","\n","    Returns:\n","    - pd.DataFrame: A DataFrame summarizing the performance of the simulation.\n","    \"\"\"\n","    print(f\"[SIM START] {len(predictions)} predictions.\")\n","    prev_signal = 0\n","\n","    for (dt, signal), price in zip(predictions, prices):\n","        # Ensure consistent datetime in engine state time curve\n","        engine_state[\"time_curve\"][-1] = dt # Update the last datetime to current prediction time\n","\n","        # Check for TP/SL if a position is currently open\n","        if engine_state[\"direction\"] != 0:\n","            check_tp_sl(engine_state, price)\n","\n","        # If the signal changes, re-evaluate position\n","        if signal != prev_signal:\n","            # Close existing position if any\n","            if engine_state[\"direction\"] != 0:\n","                close_open_position(engine_state, price, \"direction_change\")\n","\n","            # Open new position if signal is not neutral\n","            if signal != 0:\n","                open_position(engine_state, signal, price)\n","\n","        prev_signal = signal\n","\n","    # Close any remaining position at the end of the simulation\n","    if engine_state[\"direction\"] != 0:\n","        close_open_position(engine_state, prices[-1], \"end_of_simulation\")\n","\n","    print(\"[SIM END]\")\n","    return get_performance_summary(engine_state)"],"id":"29d77fdd","execution_count":21,"outputs":[]},{"cell_type":"markdown","id":"md_91705431","metadata":{"id":"md_91705431"},"source":["## 5. Demo Simulation\n","\n","This section demonstrates the functionality of the paper trading engine with synthetic data. It initializes the engine with predefined parameters, generates a series of simulated price movements and trading signals, and then executes the `run_simulation` function to process these events. Finally, it displays the performance summary and visualizes the results."]},{"cell_type":"code","id":"cd_77918499","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"id":"cd_77918499","executionInfo":{"status":"ok","timestamp":1779274939644,"user_tz":-300,"elapsed":59,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}},"outputId":"15868faa-45be-490c-8758-748ac44f764b"},"execution_count":23,"outputs":[{"output_type":"stream","name":"stdout","text":["Paper Trading Engine Initialized | Balance: $10,000.00 USDT\n","[SIM START] 200 predictions.\n","[OPEN] SHORT | Qty: 0.099427 BTC | Price: 50284.02 | Fee: $1.9998\n","[MONITOR] PnL: -1.00% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -3.33% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 51967.95 | PnL: $-169.49 (-3.35%) | Balance: $14828.09\n","[OPEN] LONG | Qty: 0.143170 BTC | Price: 51789.88 | Fee: $2.9659\n","[MONITOR] PnL: -0.34% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 2.04% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 52843.56 | PnL: $147.83 (2.03%) | Balance: $22387.71\n","[OPEN] SHORT | Qty: 0.210487 BTC | Price: 53174.95 | Fee: $4.4771\n","[MONITOR] PnL: 0.67% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.29% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 3.13% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 51514.71 | PnL: $345.12 (3.12%) | Balance: $33921.00\n","[OPEN] LONG | Qty: 0.344235 BTC | Price: 49273.79 | Fee: $6.7847\n","[MONITOR] PnL: -1.35% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 48605.18 | PnL: $-236.85 (-1.36%) | Balance: $50639.12\n","[OPEN] SHORT | Qty: 0.520501 BTC | Price: 48636.47 | Fee: $10.1261\n","[MONITOR] PnL: 0.30% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 48494.92 | PnL: $63.58 (0.29%) | Balance: $76007.91\n","[OPEN] LONG | Qty: 0.783756 BTC | Price: 48494.37 | Fee: $15.2031\n","[MONITOR] PnL: 0.11% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 48543.05 | PnL: $22.93 (0.10%) | Balance: $114023.39\n","[OPEN] SHORT | Qty: 1.174328 BTC | Price: 48540.75 | Fee: $22.8011\n","[MONITOR] PnL: 2.10% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 47526.41 | PnL: $1168.84 (2.09%) | Balance: $172172.20\n","[OPEN] LONG | Qty: 1.826102 BTC | Price: 47144.61 | Fee: $34.4363\n","[MONITOR] PnL: 0.18% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 47223.20 | PnL: $109.02 (0.17%) | Balance: $258337.65\n","[OPEN] SHORT | Qty: 2.734897 BTC | Price: 47226.13 | Fee: $51.6634\n","[MONITOR] PnL: 1.70% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46428.30 | PnL: $2131.19 (1.69%) | Balance: $389575.78\n","[OPEN] LONG | Qty: 4.195855 BTC | Price: 46428.38 | Fee: $77.9227\n","[MONITOR] PnL: 0.57% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -0.31% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46280.14 | PnL: $-699.67 (-0.32%) | Balance: $583604.94\n","[OPEN] LONG | Qty: 6.331120 BTC | Price: 46097.57 | Fee: $116.7397\n","[MONITOR] PnL: -0.90% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.87% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46955.92 | PnL: $5315.40 (1.86%) | Balance: $880652.85\n","[OPEN] SHORT | Qty: 9.376264 BTC | Price: 46956.90 | Fee: $176.1121\n","[MONITOR] PnL: -0.01% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.56% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46232.39 | PnL: $6619.80 (1.54%) | Balance: $1327376.83\n","[OPEN] LONG | Qty: 14.357465 BTC | Price: 46231.92 | Fee: $265.5092\n","[MONITOR] PnL: 1.24% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46799.14 | PnL: $7875.07 (1.23%) | Balance: $1998759.57\n","[OPEN] SHORT | Qty: 21.351711 BTC | Price: 46802.23 | Fee: $399.7231\n","[MONITOR] PnL: 1.80% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 45960.87 | PnL: $17571.94 (1.80%) | Balance: $3015239.47\n","[OPEN] LONG | Qty: 32.804413 BTC | Price: 45962.38 | Fee: $603.1076\n","[MONITOR] PnL: 0.32% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -2.61% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 44760.81 | PnL: $-40004.14 (-2.61%) | Balance: $4482401.12\n","[OPEN] SHORT | Qty: 50.066142 BTC | Price: 44760.03 | Fee: $896.3848\n","[MONITOR] PnL: 1.96% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.65% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.54% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.27% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44644.51 | PnL: $4889.57 (0.26%) | Balance: $6727356.32\n","[OPEN] SHORT | Qty: 75.467008 BTC | Price: 44568.26 | Fee: $1345.3732\n","[MONITOR] PnL: 0.42% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44382.53 | PnL: $12676.72 (0.42%) | Balance: $10102120.90\n","[OPEN] LONG | Qty: 113.816173 BTC | Price: 44383.82 | Fee: $2020.6387\n","[MONITOR] PnL: -2.21% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43399.83 | PnL: $-113969.82 (-2.22%) | Balance: $15037726.98\n","[OPEN] SHORT | Qty: 173.230721 BTC | Price: 43399.90 | Fee: $3007.2784\n","[MONITOR] PnL: 1.05% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42946.50 | PnL: $75566.95 (1.04%) | Balance: $22628482.62\n","[OPEN] LONG | Qty: 263.466389 BTC | Price: 42949.17 | Fee: $4526.2649\n","[MONITOR] PnL: -0.68% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42652.39 | PnL: $-82686.54 (-0.69%) | Balance: $33856932.54\n","[OPEN] SHORT | Qty: 396.863491 BTC | Price: 42651.78 | Fee: $6770.7732\n","[MONITOR] PnL: -1.61% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43343.26 | PnL: $-281303.71 (-1.62%) | Balance: $50495792.37\n","[OPEN] LONG | Qty: 582.546678 BTC | Price: 43345.47 | Fee: $10100.3044\n","[MONITOR] PnL: 0.52% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -2.11% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 42423.89 | PnL: $-546748.92 (-2.13%) | Balance: $75189702.70\n","[OPEN] SHORT | Qty: 886.546087 BTC | Price: 42402.16 | Fee: $15036.5867\n","[MONITOR] PnL: 0.99% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.06% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42383.04 | PnL: $1920.95 (0.05%) | Balance: $112768056.09\n","[OPEN] LONG | Qty: 1330.518722 BTC | Price: 42380.79 | Fee: $22555.3730\n","[MONITOR] PnL: 1.56% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 3.00% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 43646.21 | PnL: $1660436.16 (2.99%) | Balance: $170794371.42\n","[OPEN] SHORT | Qty: 1980.894316 BTC | Price: 43104.71 | Fee: $34154.3522\n","[MONITOR] PnL: 0.43% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -0.08% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -1.57% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43785.50 | PnL: $-1383266.82 (-1.58%) | Balance: $254762825.28\n","[OPEN] LONG | Qty: 2909.523408 BTC | Price: 43785.80 | Fee: $50958.3183\n","[MONITOR] PnL: -0.71% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43466.67 | PnL: $-979103.12 (-0.73%) | Balance: $381128573.89\n","[OPEN] SHORT | Qty: 4394.674338 BTC | Price: 43357.44 | Fee: $76216.7322\n","[MONITOR] PnL: 1.63% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 3.37% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 41898.87 | PnL: $6336287.39 (3.36%) | Balance: $577930473.48\n","[OPEN] LONG | Qty: 6675.848512 BTC | Price: 43289.40 | Fee: $115597.3845\n","[MONITOR] PnL: -0.10% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.43% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43905.01 | PnL: $3992477.84 (1.42%) | Balance: $870800830.50\n","[OPEN] SHORT | Qty: 9916.487017 BTC | Price: 43903.02 | Fee: $174145.4741\n","[MONITOR] PnL: -0.57% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44155.10 | PnL: $-2674893.46 (-0.57%) | Balance: $1303315519.41\n","[OPEN] LONG | Qty: 14758.856727 BTC | Price: 44156.76 | Fee: $260681.2920\n","[MONITOR] PnL: -0.95% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -0.40% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.92% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.89% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44986.01 | PnL: $11973205.11 (1.88%) | Balance: $1966731337.59\n","[OPEN] SHORT | Qty: 21352.340005 BTC | Price: 46050.01 | Fee: $393310.1914\n","[MONITOR] PnL: 3.90% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 44259.56 | PnL: $37852279.06 (3.89%) | Balance: $2987465777.21\n","[OPEN] LONG | Qty: 33336.206955 BTC | Price: 44813.10 | Fee: $597559.4906\n","[MONITOR] PnL: 0.14% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44871.84 | PnL: $1359826.01 (0.13%) | Balance: $4482126819.63\n","[OPEN] SHORT | Qty: 49939.473249 BTC | Price: 44870.39 | Fee: $896321.5041\n","[MONITOR] PnL: 0.42% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.26% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44758.63 | PnL: $4687146.63 (0.25%) | Balance: $6726721285.84\n","[OPEN] LONG | Qty: 75152.626078 BTC | Price: 44758.01 | Fee: $1345472.9224\n","[MONITOR] PnL: -2.97% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43420.62 | PnL: $-101813639.94 (-2.99%) | Balance: $9987244162.50\n","[OPEN] SHORT | Qty: 114985.125559 BTC | Price: 43421.67 | Fee: $1997138.6046\n","[MONITOR] PnL: 0.29% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43298.85 | PnL: $12130983.65 (0.28%) | Balance: $14990224184.48\n","[OPEN] LONG | Qty: 173121.252002 BTC | Price: 43300.84 | Fee: $2998518.4313\n","[MONITOR] PnL: 0.54% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43527.27 | PnL: $36185646.66 (0.52%) | Balance: $22519706946.24\n","[OPEN] SHORT | Qty: 258641.631922 BTC | Price: 43528.78 | Fee: $4503341.4577\n","[MONITOR] PnL: -2.25% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 44514.59 | PnL: $-259576837.77 (-2.26%) | Balance: $33513981461.79\n","[OPEN] LONG | Qty: 379364.064689 BTC | Price: 44177.73 | Fee: $6703776.7307\n","[MONITOR] PnL: -1.21% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43641.47 | PnL: $-210060176.01 (-1.21%) | Balance: $50056660730.58\n","[OPEN] SHORT | Qty: 577692.452469 BTC | Price: 43321.67 | Fee: $10010641.7127\n","[MONITOR] PnL: -1.40% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43931.06 | PnL: $-362191461.03 (-1.41%) | Balance: $74711060415.20\n","[OPEN] SHORT | Qty: 852640.268962 BTC | Price: 43806.93 | Fee: $14940619.8341\n","[MONITOR] PnL: -0.80% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44162.44 | PnL: $-318184010.77 (-0.81%) | Balance: $111729488362.19\n","[OPEN] LONG | Qty: 1265120.024955 BTC | Price: 44162.57 | Fee: $22348379.3745\n","[MONITOR] PnL: 0.15% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.63% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44878.78 | PnL: $883380794.49 (1.62%) | Balance: $168461472437.79\n","[OPEN] SHORT | Qty: 1876702.720685 BTC | Price: 44877.71 | Fee: $33688846.9021\n","[MONITOR] PnL: 1.02% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44423.10 | PnL: $819820245.71 (1.01%) | Balance: $253469724291.71\n","[OPEN] LONG | Qty: 2853191.539578 BTC | Price: 44421.34 | Fee: $50697038.2186\n","[MONITOR] PnL: -0.48% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44206.22 | PnL: $-664230090.64 (-0.48%) | Balance: $379497388627.56\n","[OPEN] SHORT | Qty: 4292063.498858 BTC | Price: 44204.29 | Fee: $75891053.8321\n","[MONITOR] PnL: 0.56% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43962.17 | PnL: $963719040.34 (0.55%) | Balance: $570112836216.01\n","[OPEN] SHORT | Qty: 6628939.455526 BTC | Price: 42997.98 | Fee: $114012390.7076\n","[MONITOR] PnL: -0.47% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43205.09 | PnL: $-1487481218.49 (-0.48%) | Balance: $853542348736.73\n","[OPEN] LONG | Qty: 9878637.772389 BTC | Price: 43206.03 | Fee: $170726703.0101\n","[MONITOR] PnL: 0.40% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43373.89 | PnL: $1486838154.93 (0.39%) | Balance: $1281675180141.62\n","[OPEN] SHORT | Qty: 14772913.274068 BTC | Price: 43373.31 | Fee: $256300056.8750\n","[MONITOR] PnL: -0.04% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.29% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 2.39% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 42342.93 | PnL: $14971503016.58 (2.38%) | Balance: $1937140530140.59\n","[OPEN] LONG | Qty: 23017878.314382 BTC | Price: 42084.36 | Fee: $387477103.6078\n","[MONITOR] PnL: -0.51% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 41865.48 | PnL: $-5423614979.74 (-0.52%) | Balance: $2900022115475.89\n","[OPEN] LONG | Qty: 35122997.602091 BTC | Price: 41288.88 | Fee: $580075744.0802\n","[MONITOR] PnL: 0.61% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 3.48% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 42722.17 | PnL: $49741228971.18 (3.47%) | Balance: $4399372501936.01\n","[OPEN] SHORT | Qty: 51178083.948116 BTC | Price: 42977.76 | Fee: $879807722.7016\n","[MONITOR] PnL: 2.85% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 41758.67 | PnL: $61535838778.35 (2.84%) | Balance: $6659547942173.64\n","[OPEN] LONG | Qty: 79750184.526160 BTC | Price: 41757.68 | Fee: $1332072937.4475\n","[MONITOR] PnL: -0.03% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.08% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 41787.45 | PnL: $1041140212.49 (0.07%) | Balance: $9989439694833.03\n","[OPEN] LONG | Qty: 115237543.042289 BTC | Price: 43349.66 | Fee: $1998203500.8854\n","[MONITOR] PnL: -0.28% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43220.00 | PnL: $-16933926347.18 (-0.30%) | Balance: $14966015875103.56\n","[OPEN] LONG | Qty: 172298087.464514 BTC | Price: 43436.38 | Fee: $2993602421.4336\n","[MONITOR] PnL: -0.05% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -1.78% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 42660.37 | PnL: $-136645159209.82 (-1.79%) | Balance: $22310382313854.17\n","[OPEN] SHORT | Qty: 261464623.019399 BTC | Price: 42662.33 | Fee: $4461875934.4462\n","[MONITOR] PnL: -1.74% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43409.14 | PnL: $-199804376681.34 (-1.75%) | Balance: $33260806091817.58\n","[OPEN] SHORT | Qty: 374288163.353404 BTC | Price: 44429.31 | Fee: $6651745651.8842\n","[MONITOR] PnL: 1.34% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43838.67 | PnL: $214506242236.54 (1.33%) | Balance: $50098025427361.27\n","[OPEN] LONG | Qty: 559554193.266437 BTC | Price: 44771.05 | Fee: $10020732304.5771\n","[MONITOR] PnL: -2.09% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43828.88 | PnL: $-537005026875.11 (-2.10%) | Balance: $74602828432622.89\n","[OPEN] SHORT | Qty: 843383991.950426 BTC | Price: 44223.43 | Fee: $14918932239.1288\n","[MONITOR] PnL: -3.32% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 45694.75 | PnL: $-1256303022640.62 (-3.33%) | Balance: $110628939408883.38\n","[OPEN] SHORT | Qty: 1238924616.199954 BTC | Price: 44644.65 | Fee: $22124543973.1611\n","[MONITOR] PnL: -0.18% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44728.25 | PnL: $-125740072249.12 (-0.19%) | Balance: $165792430659292.38\n","[OPEN] LONG | Qty: 1853554778.791549 BTC | Price: 44723.38 | Fee: $33158894798.7351\n","[MONITOR] PnL: -0.74% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44391.49 | PnL: $-648089116729.86 (-0.74%) | Balance: $248008417370474.12\n","[OPEN] SHORT | Qty: 2793263682.246324 BTC | Price: 44391.39 | Fee: $49598744471.8352\n","[MONITOR] PnL: 2.30% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 43376.26 | PnL: $2787061231813.49 (2.29%) | Balance: $374742737349248.44\n","[OPEN] LONG | Qty: 4320269361.876224 BTC | Price: 43373.36 | Fee: $74953830707.3401\n","[MONITOR] PnL: 0.12% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43420.34 | PnL: $127931223143.04 (0.11%) | Balance: $562180313071311.88\n","[OPEN] SHORT | Qty: 6529009589.014138 BTC | Price: 43049.48 | Fee: $112428180619.0885\n","[MONITOR] PnL: 1.35% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -0.96% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.21% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42963.31 | PnL: $450401601169.71 (0.20%) | Balance: $843588754213934.88\n","[OPEN] LONG | Qty: 9818025208.673403 BTC | Price: 42963.48 | Fee: $168726625377.2895\n","[MONITOR] PnL: -0.47% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42756.29 | PnL: $-2202109565780.12 (-0.48%) | Balance: $1263034447715113.00\n","[OPEN] SHORT | Qty: 14587171621.996126 BTC | Price: 43290.09 | Fee: $252591984627.7969\n","[MONITOR] PnL: 1.82% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42507.58 | PnL: $11166581506571.97 (1.81%) | Balance: $1905428409598715.50\n","[OPEN] LONG | Qty: 22335082280.839554 BTC | Price: 42659.05 | Fee: $381117339206.9278\n","[MONITOR] PnL: 1.97% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43496.39 | PnL: $18313459608672.79 (1.96%) | Balance: $2876154143640630.00\n","[OPEN] LONG | Qty: 33868827836.315067 BTC | Price: 42465.25 | Fee: $575299253183.4922\n","[MONITOR] PnL: 0.29% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.70% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42758.50 | PnL: $9352761586137.56 (0.69%) | Balance: $4323179847249662.50\n","[OPEN] LONG | Qty: 49954894250.160194 BTC | Price: 43276.35 | Fee: $864746166198.6404\n","[MONITOR] PnL: -1.85% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 42470.29 | PnL: $-41115281679472.12 (-1.86%) | Balance: $6443065307186912.00\n","[OPEN] SHORT | Qty: 76387498760.234756 BTC | Price: 42168.88 | Fee: $1288470251846.4131\n","[MONITOR] PnL: -0.41% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -0.95% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.06% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42148.67 | PnL: $255938763741.37 (0.05%) | Balance: $9663208044419296.00\n","[OPEN] LONG | Qty: 114647161502.923172 BTC | Price: 42146.50 | Fee: $1932790668096.7776\n","[MONITOR] PnL: 0.36% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 0.82% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42489.61 | PnL: $37388062528884.27 (0.81%) | Balance: $14530639908563036.00\n","[OPEN] SHORT | Qty: 170977246319.201752 BTC | Price: 42490.99 | Fee: $2905997305861.7031\n","[MONITOR] PnL: 1.05% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -1.74% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43235.89 | PnL: $-130317892216260.00 (-1.75%) | Balance: $21662408482617652.00\n","[OPEN] LONG | Qty: 253197612190.729095 BTC | Price: 42781.03 | Fee: $4332821557891.8032\n","[MONITOR] PnL: 1.00% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43206.36 | PnL: $103316641750883.97 (0.99%) | Balance: $32593446945870592.00\n","[OPEN] SHORT | Qty: 377173362041.102051 BTC | Price: 43203.06 | Fee: $6518017218309.7344\n","[MONITOR] PnL: 1.43% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 42587.23 | PnL: $225849563513590.16 (1.43%) | Balance: $49107821882829328.00\n","[OPEN] LONG | Qty: 576593324624.063477 BTC | Price: 42590.18 | Fee: $9822884698644.6309\n","[MONITOR] PnL: 1.19% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 43092.71 | PnL: $279816657002965.97 (1.18%) | Balance: $73935029137670944.00\n","[OPEN] SHORT | Qty: 857799885068.462280 BTC | Price: 43089.44 | Fee: $14784846336939.8789\n","[MONITOR] PnL: -1.77% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 43857.46 | PnL: $-673855838710071.50 (-1.78%) | Balance: $110208505132288320.00\n","[OPEN] LONG | Qty: 1253596350162.830078 BTC | Price: 43962.07 | Fee: $22044274765903.2344\n","[MONITOR] PnL: 0.63% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.89% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 4.81% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 46070.94 | PnL: $2620569992163824.50 (4.80%) | Balance: $167917721347289088.00\n","[OPEN] SHORT | Qty: 1822197179545.731445 BTC | Price: 46071.73 | Fee: $33580713012490.2344\n","[MONITOR] PnL: 0.34% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.45% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 45410.97 | PnL: $1170935912850777.50 (1.43%) | Balance: $253006853009919872.00\n","[OPEN] LONG | Qty: 2786093828002.889160 BTC | Price: 45410.23 | Fee: $50606863186962.0859\n","[MONITOR] PnL: -1.32% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 44803.91 | PnL: $-1739195571361000.50 (-1.34%) | Balance: $377734212106563520.00\n","[OPEN] SHORT | Qty: 4266333602224.085938 BTC | Price: 44265.58 | Fee: $75540685109627.0625\n","[MONITOR] PnL: 0.09% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44232.71 | PnL: $64749780452253.70 (0.07%) | Balance: $566575152577844608.00\n","[OPEN] LONG | Qty: 6405331739211.813477 BTC | Price: 44232.29 | Fee: $113329008963307.8906\n","[MONITOR] PnL: 0.52% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 44458.18 | PnL: $1332992636289382.75 (0.51%) | Balance: $851117307240192000.00\n","[OPEN] SHORT | Qty: 9529829636951.261719 BTC | Price: 44652.31 | Fee: $170211547736057.5938\n","[MONITOR] PnL: -1.27% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 45223.37 | PnL: $-5614492922154864.00 (-1.28%) | Balance: $1270861509966636288.00\n","[OPEN] LONG | Qty: 14052470920218.607422 BTC | Price: 45222.05 | Fee: $254192643976849.4688\n","[MONITOR] PnL: 0.03% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 2.23% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 46228.32 | PnL: $13880731065552808.00 (2.23%) | Balance: $1919969590965883904.00\n","[OPEN] LONG | Qty: 20843250054056.800781 BTC | Price: 46060.94 | Fee: $384023865748630.3750\n","[MONITOR] PnL: 4.09% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 47941.27 | PnL: $38792487629527176.00 (4.08%) | Balance: $2918437744874569728.00\n","[OPEN] SHORT | Qty: 30795384090729.609375 BTC | Price: 47380.11 | Fee: $583635529075391.8750\n","[MONITOR] PnL: 0.31% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 47240.41 | PnL: $3720200490018178.50 (0.29%) | Balance: $4380662995546531840.00\n","[OPEN] LONG | Qty: 46370958029092.921875 BTC | Price: 47238.36 | Fee: $876195205188807.8750\n","[MONITOR] PnL: 1.08% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.82% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 48093.18 | PnL: $38746771673860800.00 (1.81%) | Balance: $6609021580938385408.00\n","[OPEN] SHORT | Qty: 68702522587644.320312 BTC | Price: 48092.24 | Fee: $1321623200844936.7500\n","[MONITOR] PnL: 0.08% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.32% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 3.55% | TP: +2.0% | SL: -1.0%\n","[CLOSE] TAKE_PROFIT | Price: 46390.97 | PnL: $115606669954477408.00 (3.54%) | Balance: $10027364832582430720.00\n","[OPEN] LONG | Qty: 108792328689564.625000 BTC | Price: 46086.66 | Fee: $2005550003613385.5000\n","[MONITOR] PnL: 1.30% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46682.93 | PnL: $62838103962228960.00 (1.29%) | Balance: $15102072449465257984.00\n","[OPEN] SHORT | Qty: 161740524629837.468750 BTC | Price: 46682.76 | Fee: $3020197450278490.0000\n","[MONITOR] PnL: -0.35% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46850.12 | PnL: $-30099919508236092.00 (-0.36%) | Balance: $22619446426075533312.00\n","[OPEN] LONG | Qty: 241426665806263.031250 BTC | Price: 46849.86 | Fee: $4524322110724387.0000\n","[MONITOR] PnL: -1.86% | TP: +2.0% | SL: -1.0%\n","[CLOSE] STOP_LOSS | Price: 45976.18 | PnL: $-215369600022042080.00 (-1.86%) | Balance: $33710357997232971776.00\n","[OPEN] SHORT | Qty: 366581181620693.000000 BTC | Price: 45978.00 | Fee: $6741868078253318.0000\n","[MONITOR] PnL: -0.28% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 46111.61 | PnL: $-55740371736095648.00 (-0.29%) | Balance: $50502545325974847488.00\n","[OPEN] LONG | Qty: 547654592593676.750000 BTC | Price: 46112.94 | Fee: $10101585814167216.0000\n","[MONITOR] PnL: 0.59% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -0.73% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: -0.48% | TP: +2.0% | SL: -1.0%\n","[CLOSE] DIRECTION_CHANGE | Price: 45887.17 | PnL: $-133696104180156448.00 (-0.49%) | Balance: $75612711004977184768.00\n","[OPEN] SHORT | Qty: 823797394478226.625000 BTC | Price: 45889.28 | Fee: $15121386209027572.0000\n","[MONITOR] PnL: -0.11% | TP: +2.0% | SL: -1.0%\n","[MONITOR] PnL: 1.58% | TP: +2.0% | SL: -1.0%\n","[CLOSE] END_OF_SIMULATION | Price: 45165.51 | PnL: $581356949346992512.00 (1.58%) | Balance: $113982415866596933632.00\n","[SIM END]\n","\n","=== Performance Summary ===\n","                            Value\n","Initial Balance ($)  1.000000e+04\n","Final Balance ($)    1.139824e+20\n","Total Return (%)     1.139824e+18\n","Total Trades         9.100000e+01\n","Win Rate (%)         6.040000e+01\n","Avg Win (%)          1.440000e+00\n","Avg Loss (%)        -1.360000e+00\n","Profit Factor        1.940000e+00\n","Max Drawdown (%)     0.000000e+00\n","Sharpe Ratio         3.030000e+00\n","Take Profit Hits     1.400000e+01\n","Stop Loss Hits       2.100000e+01\n"]}],"source":["# ── Generate synthetic predictions and prices ─────────────────────────────\n","np.random.seed(42)\n","n_periods = 200\n","\n","# Simulated price path (GBM-like)\n","returns = np.random.normal(0.0002, 0.015, n_periods)\n","prices  = [50_000.0]\n","for r in returns:\n","    prices.append(prices[-1] * (1 + r))\n","prices = prices[1:]\n","\n","# Simulated prediction signals: +1, -1, 0 with realistic frequency\n","signals = np.random.choice([1, -1, 0], size=n_periods, p=[0.4, 0.4, 0.2])\n","base_dt = datetime.datetime(2025, 1, 1, 0, 0, 0, tzinfo=datetime.UTC)\n","predictions = [(base_dt + datetime.timedelta(hours=i), int(s)) for i, s in enumerate(signals)]\n","\n","# ── Initialize paper trading engine state ────────────────────────────────\n","engine_state = initialize_engine_state(\n","    symbol          = config_strategy[\"symbol\"],\n","    initial_balance = INITIAL_BALANCE,\n","    allocation_pct  = ALLOCATION_PERCENT,\n","    tp_percent      = config_strategy[\"live_tp_percent\"],\n","    sl_percent      = config_strategy[\"live_sl_percent\"],\n","    taker_fee       = TAKER_FEE,\n","    maker_fee       = MAKER_FEE\n",")\n","\n","# ── Run paper trading simulation ───────────────────────────────────────────\n","summary = run_simulation(engine_state, predictions, prices)\n","print(\"\\n=== Performance Summary ===\")\n","print(summary.to_string())"]},{"cell_type":"markdown","id":"md_80850933","metadata":{"id":"md_80850933"},"source":["## 6. Performance Visualisation\n","\n","This section visualizes the simulation results. It generates two plots:\n","\n","*   **Equity Curve**: Displays the evolution of the trading account balance over the course of the simulation.\n","*   **Per-Trade PnL Distribution**: Illustrates the profit and loss percentage for each individual trade executed by the engine.\n","\n","These visualizations provide a clear graphical representation of the strategy's performance and risk characteristics."]},{"cell_type":"code","id":"cd_15827305","metadata":{"colab":{"base_uri":"https://localhost:8080/","height":496},"id":"cd_15827305","executionInfo":{"status":"ok","timestamp":1779275014033,"user_tz":-300,"elapsed":558,"user":{"displayName":"Arsalan Bakhtiar","userId":"11466190921627274456"}},"outputId":"d6c7602d-0c0e-48c9-d4e3-92ef84aebf5c"},"execution_count":26,"outputs":[{"output_type":"display_data","data":{"text/plain":["<Figure size 1400x500 with 2 Axes>"],"image/png":"iVBORw0KGgoAAAANSUhEUgAABW0AAAHvCAYAAAAvoP1zAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAoYxJREFUeJzs3Xd4VGXax/HfTEgPCSEk1AABAhiRIghSBFQQQbGtyiJIsaJii7iChaKrsdFeG4ICgq6CBXAtICKxwqooK4pgQBCkhgBpkGSSOe8fbIaMKWTCZM6U7+e65rrmPHPKPec5mZzceeZ+LIZhGAIAAAAAAAAAeAWr2QEAAAAAAAAAAE4iaQsAAAAAAAAAXoSkLQAAAAAAAAB4EZK2AAAAAAAAAOBFSNoCAAAAAAAAgBchaQsAAAAAAAAAXoSkLQAAAAAAAAB4EZK2AAAAAAAAAOBFSNoCAAAAAAAAgBchaQsAACCpf//+slgsslgsWrhwoaN9zJgxjvapU6eaFp+vKD1XFotFO3fuNDscr5Wenu44Ty1btjQ7HAdPXu8tW7Z0HCs9Pb1WjxVIdu7c6fRzCAAAfBNJWwAAPGjhwoVOf0yXPkJCQtS0aVNdddVVfp28qOi9V/Ug6Ve5ssm1yh4kmT2npKREc+bMUe/evVWvXj0FBwerQYMGat++va688kpNmzZNhmGYHabHHD16VFOnTnU8fEVln9Hh4eFq06aNxowZo19++cXsMGts+fLljj7x5981AAD4gzpmBwAAACSbzaa9e/dq2bJlWrZsmf7v//5Pd955p9lhQdJDDz2km266SZLUvHlzk6Pxfl9++aXjeePGjU2MxLOGDRumd99916ktKytLWVlZ2rp1q5YvX66HHnpIdeqcuP3u0qWL41yFhYV5PN7advToUU2bNs2xXFHi9p133lFBQYEk6ayzzvJUaDVSUFCg7du3a/v27XrnnXf0zTffqGPHjmaH5bLly5frtddecyz379/fvGAAAECVSNoCAGCi0qTN7t27NXXqVP3222+SpPvvv1/Dhg1TQkKCmeHVSF5enqKioip8rWxCT5KeeOIJffzxx5Kkzp0767nnnnN6vbKk3/HjxxUaGiqrtfa/NJScnKzk5ORaP87pqOjcSeYkmfv06ePxY5otPT3dkbANDQ3VlClT1K1bN0nS9u3btWrVKq1cudJpm5iYmIA8V2WVniNv9uWXX8pms+n777/XpEmTVFJSovz8fD3//POaO3eu2eEBAAA/RnkEAABM1KdPH/Xp00fDhw/XnDlzHO2FhYX65ptvJEnPP/+8Bg8erKSkJEVHRys4OFgJCQkaNGiQli1bVm6fZetErlmzRk8//bSSk5MVGhqq1q1b66mnnpLdbi+33bfffqvhw4crMTFRISEhio2N1YABA/T++++XW7ds/dcFCxZo1qxZOuOMMxQSEqKHH374lO+39FE2KV2axCp9FBcXKywszFHz87ffftNVV12l2NhYRUREKCcnR9u3b9cNN9ygs88+Ww0bNlRISIgiIyOVkpKie++9VwcPHiwXw44dO3T11VcrJiZG0dHRGjp0qLZs2VJpzJXV+Jw6daqjfcyYMfr66691wQUXKDIyUjExMRo2bFiFx3/vvffUpUsXhYWFKTExUZMmTdKWLVtOqwblX89d6aNs0tZT8VZW3uKv1+Wzzz6rtm3bKjQ0VElJSZoxY0aF7+2TTz7R5ZdfrkaNGikkJETx8fG67LLLyv0DwEzffvut4/mQIUM0adIkDRw4UAMHDtS4ceO0bNkyZWZmOkbZSpXXtP1rPdJ9+/Zp+PDhiomJUWxsrEaOHKmsrCwVFRVp8uTJSkxMVFhYmLp27apPPvnEKa6/9nlZldVwrsz333+vkSNH6qyzzlJ8fLyCg4NVt25dde7cWVOmTFFeXp7TvpOSkpy2L/ueSr+WX1VN299//1233Xab2rRpo7CwMEVFRalTp06aPHmyjh49WuX7rO61XR19+vTR+eefr/vvv18XX3yxo33Xrl3l1v3zzz91zz33qH379goPD1dUVJS6du2qmTNnymazOa179OhRTZgwwbFuaGiomjRpon79+un+++/XsWPHKjx3ZX+mXKmLXLpu2VG206ZNc2xfOuK2sLBQjz76qDp16qTIyEiFhISoUaNG6tmzp+666y7t37/fhbMHAABOiwEAADxmwYIFhiTHo6wffvjB6bUlS5YYhmEYPXr0cGr/62PmzJlO+2nRooXjtZSUlAq3ueWWW5y2eeGFFwyr1VrpMSZNmuS0fr9+/RyvJScnO6179913V/t8jB492rFdv379nF5bu3at47WYmBgjPj7e6ThHjhwxPv744yrPTcuWLY0jR4449rlnzx6jUaNG5daLjY01WrZs6VhesGBBhTFOmTLF0T5lyhRHe1JSklGnTp1y+x00aJDTe3rttdcqjPPss8+u9LqoybmriKfiLdu+Y8cOR3vZ6/Kv10zp480333Ta1wMPPFBp31qtVuOll16q1rmqbXPmzHHEFRUVZfzf//2f8dtvvxl2u73Sbcpe3y1atHC079ixw+l9tm3bttx779mzp3HllVeWaw8JCTF27tzp2FfZPh89erTT8cv+DFfnen/ppZeq/Fnr2rWrYbPZyu27osfatWsNw3C+JkrbDMMw0tPTjaioqEq3T0pKMv78888K32d1r+3KVPUZfckllzjab7zxRqfX1q1bZ9SrV6/SmM8//3yjoKDAsX7fvn2rPEf79u1zrFvZz1R1r6G/rlvRo/QzZNSoUVWut27dumqdRwAAcPoYaQsAgBf4888/NXnyZKe2zp07S5JGjx6tV199VR988IHS09O1evVqPf/88woNDZV0YpRZcXFxhfvdtm2bnnjiCX300UcaPXq0o33u3Ln6+uuvJUm//PKL7rzzTtntdlmtVj300EP65JNP9PLLLys2NlaSlJaWps8++6zCY2RkZOiyyy7TsmXLtHz5cg0cOPC0zkVFsrOzZbPZNGvWLH3yySeaPXu2QkND1aJFCz355JN655139Mknnyg9PV3Lli1zjIjbuXOn5s2b59jPQw895BgpFhMToxdffFHvv/++OnXqdFqTnu3YsUPnn3++3n//fU2ZMsXRvmrVKm3dulXSibIRd911l+O1Xr16afny5XrppZe0ffv2Gh9bkj7//PMKJ0/auHGjV8b7+++/a8qUKfrggw/Ur18/R/vs2bMdzz/++GM99dRTkqTw8HA9/fTTWr16taZPn67Q0FDZ7XbdeeedjpIiZhowYIDj57H0vLVt21axsbG6+OKLNWfOHB0/frxG+y4sLNRbb72lF1980TGqed26dVqxYoWmTp2qDz74QG3btpUkFRUVOY3Yd6eOHTtq+vTpWrZsmT799FOtXbtWb7/9ts455xxJ0oYNGxwj/5977jm9/fbbTtt/+eWXjkeXLl0qPU5BQYGuu+46x8jd7t2767333tOiRYvUtGlTSSeu31tuuaXC7atzbbviq6++Unp6uqZPn65Vq1ZJkkJCQnTbbbc51iksLNSwYcMcI4D/9re/6cMPP9Q777zjqHu7du1aPf7445KkQ4cO6YsvvpAkJSYm6q233tKaNWv0+uuv64EHHlCHDh1qNOK+KqU1lAcPHuxoGzt2rKNPSsurlJb5iImJ0YIFC/TZZ5/prbfe0tSpU3XOOed4pCQNAAA4gZq2AACYqLI/zEePHu1IxFx66aV68skntWbNGu3atatc8ic7O1u//vprhRP53H333Zo0aZIkafDgwfrhhx+0adMmSdLSpUvVu3dvLViwwFEu4YILLnAkPFNSUnTllVdq/vz5kqR58+bpggsuKHeMrl27asWKFTV5+y5ZtGiRhg4dKkmOxPAZZ5yh77//Xi+++KJ++uknHTlyRCUlJU7brV+/XpJkt9udykk8+uijjsRL79691axZsxon1ho0aKAVK1YoPDxcQ4cO1ZIlSxwlF3777Te1a9dOq1atUnZ2tqQTSZ/33ntPDRs2lCSVlJRo/PjxNTq2L8Z76623OkpNNGjQQOeee67j2KVeffVVx/Orr75aPXv2lHQiiXfhhRfqo48+UnFxsRYsWKC0tLRKj1VYWKjvvvuuxrFKp67T27p1a82dO1e333678vPzHe3Z2dlatWqVVq1apWeffVbr1q1TfHy8S8d+6aWXHIm2559/Xps3b5Z04pyUJiV//fVX3X///ZJUa0ns7t2764cfftCzzz6rzZs3Kzs7u1yZlfXr1+uaa67RWWedpbp16zq9Vt36vZ988on27t0r6cR1t3z5ckdt6/r16+vSSy+VdCKpf/DgwXJ1v6tzbbvivPPOc1ru1q2bZs6cqa5duzraVq9e7SiXEB8fr7vvvlsWi0XR0dG6+eabHZNKvvLKK3r00UcVFRWloKAglZSUqF69ekpOTlZKSopjQronn3zSpRiro7SEStnz1bx583L9EhMTo/z8fEVGRqpt27aOMgmSnJLgAACg9pG0BQDAi8THx+uOO+7Qgw8+KEnav3+/unXrdsp6jEeOHKmw/a9/kPfu3duRtM3IyJAkRxJIkj799FN9+umnFe7r559/rrD9qquuqjI2dwgNDXUka8qaPHmyHnvssSq3LT03mZmZjiSkJEcSUDqRDGrfvr1+/PHHGsXXs2dPhYeHO5bj4uIczw8fPizp5PmWTiT5ShOg0ulP3lXZRGSVTaBmdrwXXnhhlceWnK/LxYsXa/HixRXuq7LrstS+ffvKJd5cZRjGKdcZNWqUhgwZoqVLl2rt2rVat26d9uzZ43h9+/bteuSRR1weCdurVy/H87Lnquz126BBA8fzsufQnW644YZK+6BUZZ9DrihbX7p169ZOkxGWve4Mw9DWrVvLJW2rc22fjs2bN+vPP/8s11YqMzNTffv2rXDbffv2KSsrS3FxcRo9erTmz5+vTZs2qWvXrrJarWrevLl69OihsWPHatCgQacda02MGzdOkydP1t69e9W7d29JUtOmTdW1a1ddd911GjZsmClxAQAQiEjaAgBgotLJlIKDgxUfH6+kpCSn0bfz5893JGwbNmyoJ554Qm3atJHVatWVV16pQ4cOSVKFE4u5W9mJhsoqm1SpLQ0bNiw3KtlmszlNXjVixAiNHDlSUVFR+ve//62nn35akmfOTf369Z2Wy044VZrwKxu/u7/6XDqKrrrMjrfs8cseuyYquy7N0KBBA91+++26/fbbJUmbNm3SDTfcoO+//16S9J///MflfcbExDiel/1qer169Spcv2yCuWy//bWESmZmZrVj2LNnj1PC9p577tGQIUMUHh6uefPmadGiRZI887N2KtW5tl1hGIYyMzM1YcIELVq0SMeOHdPo0aPVsWNHpaSkuLy/vLw8xcXFae7cubrgggv0wQcf6Oeff9a2bdu0c+dO7dy5U0uWLNHy5ct1+eWXl9u+bD+60ofV9cgjj6hz585699139dNPPykjI0N79uzRnj179P7772v//v26++673X5cAABQHkWJAAAwUZ8+fdSnTx/16NFDrVq1KpccKztD+ciRI3XDDTeob9++at68ubKysk65/9K6taW++eYbx/M2bdpIOlFioNTw4cNlGEaFj8pGNLo7oVfdY2RlZTl9FX3OnDm6+OKL1adPnwrPTXx8vKKjox3LpWUTpBMj8MqO8KsNZUe9bt++3ZFwl04m772J2fGWvS4nTZpU4TVZUlKijz/+uMr9tGzZstJrurqPU9m0aZO2bdtWrv2ss87S1Vdf7Vj2dFKztCa1JKfRoRkZGS7Vd929e7fjeVxcnGbOnKmBAweqT58+TqOJy/pr7dPqvvf27ds7nm/fvt1Rg1py/jyzWCwulzqoqfj4eM2dO1dJSUmSTtQOnjhxouP1stdq8+bNZbPZKryO8vLy1KJFC0knzs+IESP05ptvatOmTcrPz9czzzzj2M+bb77peF5ZP/773/92+b2U7ZeK+sQwDA0dOlQLFy7UDz/8oJycHC1durTCuAAAQO1ipC0AAF6sVatWjufvvPOOevbsKbvdrmnTplUrmTR79mzVr19fHTt21Ntvv62ffvrJ8dq1114rSRozZoxmzZolu92uN998U3Xr1tWll16q0NBQ/fnnn9q8ebPef/99PfjggxozZozb32NNNWzYUJGRkY7E7YMPPqihQ4fqs88+04IFC8qtXzo6+bXXXpN0orRCSEiImjZtqhkzZtS4nm11XXTRRYqJiVF2drYKCwv1t7/9Tffdd5/27dunhx9++LT2nZ2dra+++qpce1xcnFNCyVvirY4bb7xR7733niTpmWeekd1uV9++fWW1WrVr1y799NNPWrFihRYvXqz+/fvXejxV+c9//qNbb71Vffv21eDBg3XmmWcqIiJCW7dudRoNXrakgSeU1sWWpC+++EKpqalq3ry5/u///q9c7eeqlP0cysrK0uOPP65u3brpnXfe0Zo1ayrcpn79+rJYLI7PqZkzZ6p79+6yWq2Or91X5KKLLlKTJk20d+9eFRUV6corr9QDDzygvLw8R31u6USN7r+WRqhNoaGheuihh3TTTTdJOpEw/fHHH9WlSxcNHDhQiYmJ2r17t3bt2qVBgwbp5ptvVkJCgvbt26ft27frk08+UXJysuOzqU2bNhoyZIi6du2qJk2aqKSkxDE5mXRiQrZSbdu2dYzSvuOOO3THHXdow4YNpyxXUZGy5SI++ugj9enTRxEREWrRooUSExPVp08ftW/fXj179lSTJk0UHByslStXVhgXAACoZQYAAPCYBQsWGJIcj1PZt2+fERsb67SNJCMlJcVISEhwLK9du9axTYsWLRztXbp0KbetJOPGG290Os7zzz9vWK3WCtctfSxYsMCxfr9+/Spsd9Xo0aMd++nXr5/Ta2vXrnW81qJFiwq3nzhxYoWx9u/fv8L9/vnnn0bDhg3LrR8ZGWk0bdq0wvdUNsYpU6Y42qdMmeJoHz16tFNclZ2f1157rcJ4O3fu7NJ18de4KntcfvnlHo+3bPuOHTsc7WWvy7LX644dOyrd1z/+8Y9Tvsey+zLLvHnzThlno0aNjD/++MOxTWXXd1Xno7J+Kvu5UvZ6Ly4uNtq3b18ulpiYGCMxMdGl6/3vf/97uf0EBQUZ5513XqXXVc+ePSvcplRl10R6eroRFRVV6blMSkoydu/e7Vi/Jtd2Zar6jC4qKjKaN2/ueO2KK65wvPbNN98Y9erVq/IaKBtbaGholeu+++67jnXfeOONCtfp0KGDy9fQqlWrKtzXY489ZhiGYbRr167KuGbMmHHKcwgAANyD8ggAAHixRo0aKT09XQMGDFB0dLTi4uI0cuRIrV271mmynco8++yzmjFjhpKTkxUSEqKkpCQ98cQTevnll53Wu+OOO7R+/XqNGDFCzZs3V0hIiKKjo9WuXTtdc801WrRokUcmHHPVY489pscee0ytWrVSWFiYOnbsqDfeeEOjR4+ucP2mTZvqm2++0ZVXXqm6desqKipKAwcO1BdffOEoF1GbRo0apXfeeUedOnVSSEiImjRpogkTJujFF190rBMREVHrcVSX2fE+9dRT+uSTT3TllVeqcePGCg4OVmxsrFJSUhyxnXvuubV2/OoqHcE9ZswYderUSY0aNVJwcLAiIiLUoUMHpaamauPGjWrevLlH4woKCtKKFSt08cUXKyIiQnXr1tXll1+u9evXO42erY5XXnlF99xzj5o1a6bw8HD16NFDH330kS644IJKt1m8eLGGDBmiunXrunSsfv36aePGjbr11lvVqlUrhYSEKDw8XGeddZYefvhh/fDDD2rWrJlL+3SH4OBgPfDAA47lFStWOL690LNnT23atEmpqamOkdbh4eFKSkrSwIEDNXPmTD366KOObdPS0nTZZZepZcuWioqKUlBQkOLj43XxxRfro48+cvq8ve666/TMM8+oRYsWCg4OVnJysmbMmKHZs2e7/B4uuugizZgxQ61bt1ZQUFC51ydOnKhrrrlGbdq0UXR0tIKCglS/fn31799fixcv1r333uvyMQEAQM1YDKMGFfkBAIDXatmypf744w9J0tq1a03/6jhOMgyjwvq8zz33nO666y5JUufOnfXjjz96OrQK+Vq8AAAAgL+gpi0AAICHrF69WvPnz9fIkSN1xhlnqLi4WGvXrtUjjzziWGfUqFEmRujM1+IFAAAA/AVJWwAAAA+x2+1asmSJlixZUuHrl19+ue68804PR1U5X4sXAAAA8BfUtAUAAPCQtm3b6vrrr1fbtm1Vt25dBQcHq2HDhrr44ov1r3/9S8uWLVOdOt7zP3VfixcAAADwF9S0BQAAAAAAAAAvwkhbAAAAAAAAAPAiJG0BAAAAAAAAwIuQtAUAAAAAAAAAL0LSFgAAAAAAAAC8CElbAAAAAAAAAPAiJG0BAAAAAAAAwIuQtAUAAAAAAAAAL0LSFgAAAAAAAAC8CElbAAAAAAAAAPAiJG0BAAAAAAAAwIuQtAUAAAAAAAAAL0LSFgAAAAAAAAC8CElbAAAAAAAAAPAiJG0BAAAAAAAAwIuQtAUAAAAAAAAAL0LSFgAAAAAAAAC8CElbAAAAAAAAAPAiJG0BAAAAAAAAwIuQtAUAAAAAAAAAL0LSFgBMZrFYNHXqVLPDAAAAALxe//791b9/f7PDAIBaR9IWQMBbuHChLBZLpY/169d7NJ5vvvlGU6dO1dGjR2tl/8uWLdPgwYPVoEEDhYSEqEmTJrr22mv12Wef1crxAAAAUDv+eh8bFhamtm3bavz48Tpw4ECtHLOq++ayj/T09Fo5vruNGTPGKe7o6Gh16tRJ06dPV2Fhocv7S09Pl8Vi0TvvvFML0QIIJHXMDgAAvMWjjz6qpKSkcu1t2rSp1eMeP35cdeqc/Dj+5ptvNG3aNI0ZM0b16tVz23EMw9ANN9yghQsXqkuXLkpNTVWjRo20b98+LVu2TBdeeKG+/vpr9erVy23HBAAAQO0rvY8tKCjQV199pZdeekkfffSRfv75Z0VERLj1WIsXL3ZaXrRokVavXl2u/YwzznDrcWtTaGioXnnlFUnS0aNH9e6772rChAn67rvv9NZbb5kcHYBARdIWAP5n8ODB6tatm8ePGxYW5pHjTJ8+XQsXLtQ999yjGTNmyGKxOF576KGHtHjxYqfkcU0ZhqGCggKFh4ef9r4AAABwamXvY2+66SbFxcVpxowZWrFihYYPH35a+z527JhT4nfkyJFOr69fv16rV68u136q/XiTOnXqOMV/++23q0ePHlqyZIlmzJihJk2amBgdgEBFeQQAcMHRo0c1ZswYxcTEqF69eho9erQ2btwoi8WihQsXOtarrNbWmDFj1LJlS6e2sjVtp06dqvvvv1+SlJSU5Pia1s6dO9WvXz916tSpwrjatWunQYMGVRr38ePHlZaWpvbt2+vZZ591StiWuv7669W9e3dHHBWtU/oVvJ07dzraWrZsqUsvvVSrVq1St27dFB4erpdfflkdOnTQ+eefX24fdrtdTZs21dVXX+3UNmvWLJ155pkKCwtTw4YNdeutt+rIkSOVvicAAABU7IILLpAk7dixw9H2+uuvq2vXrgoPD1f9+vX197//Xbt373barn///urQoYM2bNigvn37KiIiQg8++KDLx69qPytWrNAll1yiJk2aKDQ0VK1bt9Zjjz2mkpKScvuZO3euWrdurfDwcHXv3l1ffvllhccrLCzUlClT1KZNG4WGhioxMVH/+Mc/alTeQJKsVqvjXr70vrf0nverr75S9+7dFRYWplatWmnRokU1OgYAnApJWwD4n+zsbB06dMjpkZWV5XjdMAxdfvnlWrx4sUaOHKl//vOf+vPPPzV69Gi3xXDVVVc5RkPMnDlTixcv1uLFixUfH6/rr79eP/30k37++Wenbb777jv99ttvVY5u+Oqrr3T48GFdd911CgoKclu8pbZu3arhw4dr4MCBmj17tjp37qxhw4bpiy++0P79+8vFsnfvXv397393tN166626//771bt3b82ePVtjx47VG2+8oUGDBslms7k9XgAAAH+2fft2SVJcXJwk6fHHH9eoUaOUnJysGTNm6J577tGaNWvUt2/fcvMoZGVlafDgwercubNmzZpV4T/hq6Oy/SxcuFBRUVFKTU3V7Nmz1bVrV02ePFkTJ0502v7VV1/VrbfeqkaNGunpp59W7969ddlll5VLNNvtdl122WV69tlnNXToUD333HO64oorNHPmTA0bNqxGsUvlz6Ekbdu2TVdffbUGDhyo6dOnKzY2VmPGjNEvv/xS4+MAQKUMAAhwCxYsMCRV+AgNDXWst3z5ckOS8fTTTzvaiouLjfPOO8+QZCxYsMDR3q9fP6Nfv37ljjV69GijRYsWTm2SjClTpjiWn3nmGUOSsWPHDqf1jh49aoSFhRkPPPCAU/tdd91lREZGGnl5eZW+x9mzZxuSjGXLllW6TllTpkwxKvoVUXquysbWokULQ5KxcuVKp3W3bt1qSDKee+45p/bbb7/diIqKMo4dO2YYhmF8+eWXhiTjjTfecFpv5cqVFbYDAADghNJ7s08//dTIzMw0du/ebbz11ltGXFycER4ebvz555/Gzp07jaCgIOPxxx932nbTpk1GnTp1nNr79etnSDLmzJlT7RjuuOOOcveNVe2n9B6wrFtvvdWIiIgwCgoKDMMwjKKiIiMhIcHo3LmzUVhY6Fhv7ty5hiSn++zFixcbVqvV+PLLL532OWfOHEOS8fXXX1cZ/+jRo43IyEgjMzPTyMzMNLZt22Y88cQT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9xy1t8q64YYbNGPGDA0aNEg33nijDh48qDlz5ujMM88sV7vrr0pvhB966CH9/e9/V3BwsIYOHepI5nbp0kUdOnRwTLpw9tlnV+v93X///frll180ffp0rV27VldffbUaNWqk/fv3a/ny5fr222/1zTffSJIuuugiNW/eXDfeeKPuv/9+BQUFaf78+YqPj9euXbuqdbxS1157rSZMmKAJEyaofv365f6R1a9fP916661KS0vTxo0bddFFFyk4OFgZGRl6++23NXv2bF199dUuHRMAAAAntG7dWv/85z81adIk7dy5U1dccYXq1q2rHTt2aNmyZbrllls0YcIEj8TSq1cvxcbGavTo0brrrrtksVi0ePHicsnk4OBg/fOf/9Stt96qCy64QMOGDdOOHTu0YMGCcjVtr7/+ei1dulTjxo3T2rVr1bt3b5WUlGjLli1aunSpVq1aVeW8FbXl3XffrfBvi9GjR1erZAMAkLStxKZNm1RSUlLu6yOFhYWKi4srt/7atWs1duxYzZs3T2eeeaanwgTgRpMnT66wvfTm0Gq16v3339c999yj119/XRaLRZdddpmmT5+uLl26OG1zxhlnaNGiRZo8ebJSU1OVkpKixYsX61//+tcpa4Cdc845euyxxzRnzhytXLlSdrtdO3bscCRtpRMTkv3jH/845QRkZVmtVi1atEiXX3655s6dq2effVY5OTmKj49X37599fTTT6tnz56STtwoL1u2TLfffrseeeQRNWrUSPfcc49iY2Ndnk24WbNm6tWrl77++mvddNNN5Sa7kKQ5c+aoa9euevnll/Xggw+qTp06atmypUaOHKnevXu7dDwAAAA4mzhxotq2bauZM2dq2rRpkk7Uer3ooot02WWXeSyOuLg4ffDBB7rvvvv08MMPKzY2ViNHjtSFF16oQYMGOa17yy23qKSkRM8884zuv/9+nXXWWXr//ff1yCOPOK1ntVq1fPlyzZw5U4sWLdKyZcsUERGhVq1a6e67765RSQh3eOuttyps79+/P0lbANViMaiYLelEDclly5Y5JuhZsmSJRowYoV9++aVc7ceoqCg1atTIsfz555/rkksu0YwZM3TLLbc4rdu8eXOlpqbqnnvucbRNmTJFy5cv13//+99aez8APGfnzp1KSkrSggULNGbMGI8cc/bs2br33nu1c+fOCmexBQAAAAAAvouatpXo0qWLSkpKdPDgQbVp08bpUTZhm56erksuuURPPfVUuYStJPXs2VNr1qxxalu9erVjNBsAuMowDL366qvq168fCVsAAAAAAPxQQJdHyMvL07Zt2xzLO3bs0MaNG1W/fn21bdtWI0aM0KhRoxxffc7MzNSaNWvUsWNHXXLJJVq7dq0uvfRS3X333frb3/7mqFMbEhLimIzs7rvvVr9+/TR9+nRdcskleuutt/T9999r7ty5prxnAL4rPz9f77//vtauXatNmzZpxYoVZocEAAAAAABqQUCXR0hPT9f5559frn306NFauHChbDab/vnPf2rRokXas2ePGjRooHPPPVfTpk3TWWedpTFjxlQ4k3q/fv2cala+/fbbevjhh7Vz504lJyfr6aef1pAhQ2rzrQHwIE+VRyg9Tr169XT77bfr8ccfr7VjAQAAAAAA8wR00hYAAAAAAAAAvA01bQEAAAAAAADAi5C0BQAAAAAAAAAvEnATkdntdu3du1d169aVxWIxOxwAAAC4wDAM5ebmqkmTJrJaA3P8AfezAAAAvqu697MBl7Tdu3evEhMTzQ4DAAAAp2H37t1q1qyZ2WGYgvtZAAAA33eq+9mAS9rWrVtX0okTEx0d7ZFj2u12ZWZmKj4+PmBHhAQa+jzw0OeBiX4PPPS5+XJycpSYmOi4pwtEZtzPAgAAwD2qez8bcEnb0q+QRUdHezRpW1BQoOjoaP7ACxD0eeChzwMT/R546HPvEchlAcy4nwUAAIB7nep+lr82AAAAAAAAAMCLkLQFAAAAPGjq1KmyWCxOj/bt25sdFgAAALxIwJVHAAAAAMx25pln6tNPP3Us16nDbTkAAABO4u6wEiUlJbLZbG7Zl91ul81mU0FBAfXvAoTdbpfdbjc7DAAA4KXq1KmjRo0amR0GAAAAvBRJ278wDEP79+/X0aNH3bpPu92u3NzcgJ40I5AYhqGSkhJJUuPGjel3AADgJCMjQ02aNFFYWJh69uyptLQ0NW/e3OywAAAA4CVI2v5FacI2ISFBERERbkm2GYah4uJi1alTh+RdgLDb7crLy1NWVpYsFosaN25sdkgAAMBL9OjRQwsXLlS7du20b98+TZs2Teedd55+/vln1a1bt9z6hYWFKiwsdCzn5ORI4ps9AAAAvqi6928kbcsoKSlxJGzj4uLctl+StoHHMAwFBwfLarUqMzNTCQkJCgoKMjssAADgBQYPHux43rFjR/Xo0UMtWrTQ0qVLdeONN5ZbPy0tTdOmTSvXnpmZqYKCglqNFQAAAO6Vm5tbrfVI2pZRWsM2IiLC5EjgL0qvJZvNRtIWAABUqF69emrbtq22bdtW4euTJk1SamqqYzknJ0eJiYmKj49XdHS0p8IEAACAG4SFhVVrPZK2FWA0LNyFawkAAJxKXl6etm/fruuvv77C10NDQxUaGlqu3Wq1MsktAACAj6nu/Rt3eQAAAIAHTZgwQZ9//rl27typb775RldeeaWCgoI0fPhws0MDAACAlyBpG8AsFouWL19e5TpjxozRFVdcUe197ty5UxaLRRs3bjyt2KrD1dgAAAC8wZ9//qnhw4erXbt2uvbaaxUXF6f169crPj7e7NAAAADgJSiP4CfGjBmjo0ePnjIJW9a+ffsUGxsr6USyNSkpST/++KM6d+7sWGf27NkyDMOtsfbv31+ff/65YzkhIUF9+/bVs88+qxYtWrj1WAAAAN7mrbfeMjsEAAAAeDlG2gawRo0aVVgfrayYmBjVq1fP7ce++eabtW/fPu3du1crVqzQ7t27NXLkSLcfBwAA1J7dhYf067E/tasgUzZ7sdnhAAAAAH6DpK2f6t+/v+666y794x//UP369dWoUSNNnTrVaZ2y5RGSkpIkSV26dJHFYlH//v0llS9BsHLlSvXp00f16tVTXFycLr30Um3fvt3l+CIiItSoUSM1btxY5557rsaPH68ffvjB8XpJSYluvPFGJSUlKTw8XO3atdPs2bOr3OepYist3fDee+/p/PPPV0REhDp16qR169Y57efrr79W//79FRERodjYWA0aNEhHjhyRJNntdqWlpTni6tSpk9555x2X3z8AAP5gwf7P9PdfZ2joL2naUXDQ7HAAAAC8is1m0+bNm8s9bDab2aHBB5C0raaSkpLKH3Z79dctqd667vDaa68pMjJS//nPf/T000/r0Ucf1erVqytc99tvv5Ukffrpp9q3b5/ee++9CtfLz89Xamqqvv/+e61Zs0ZWq1VXXnml7H85B644fPiwli5dqh49ejja7Ha7mjVrprffflubN2/W5MmT9eCDD2rp0qWV7qe6sT300EOaMGGCNm7cqLZt22r48OEqLj4xOmjjxo268MILlZKSonXr1umrr77S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Jvv/hgXWrxNlcjS17+abb1Z4eLimTZsmm82muXPnasSIEVq1apXZoQEAAC9S4TcpgFriUtJ2x44dlb72+++/a+TIkXr44Yf14osvnnZgAAAAMMcRW77jeawfJm1nzpype+65x1H24bvvvtNvv/2moKAgSVK7du107rnnmhkiAAAAApzVXTtq1aqVnnzySX3yySfu2iUAAABMcLg41/G8frD/JW23b9+uHj166Mcff5QkDRw4UJdcconmzJmj5557TqNGjdKgQYNq7fhffPGFhg4dqiZNmshisWj58uW1diwAAAD4JpcnIqtK8+bNtX//fnfuEgAAAB5WOgmZ5J8jbZ9//nmtX79eN9xwg84//3ylpaXp9ddf1+rVq1VSUqJrrrlG48ePr7Xj5+fnq1OnTrrhhht01VVX1dpxAAAA4LvcmrTdtGmTWrRo4c5dAgAAwMPKlkfw15q25557rr777js99dRT6tmzp5555hm9++67Hjn24MGDNXjwYI8cCyhls9mUkZFRrp0ZzwEA8E4ulUfIycmp8LF7924tX75c99xzj4YNG+ZSAC+88IJatmypsLAw9ejRQ99++22V6x89elR33HGHGjdurNDQULVt21YfffSRS8cEAABA5fy9PEKpOnXq6KGHHtK///1vzZo1S1dffTXfGoPfysjI0Lj06ZqwfaHjMS59eoWJXOCvbDabNm/eXO5hs9nMDg0A/JZLI23r1avnmLDhrywWi2666SZNnDix2vtbsmSJUlNTNWfOHPXo0UOzZs3SoEGDtHXrViUkJJRbv6ioSAMHDlRCQoLeeecdNW3aVH/88Yfq1avnytsAAABAFY4U+/dEZP/973910003acuWLerYsaPmz5+vNWvWaMGCBerVq5fuv/9+3XbbbWaH6VBYWKjCwkLHck5OjiTJbrfLbrebFZYkyTAMWQzJYpxssxj/a1fF7e6KuapjV3YMV7epav3S566899o+J1UxDEN1m8UppvXJWc89dfya9FVN9uXq+TXz+vVWlZ2TnTt3asaefyuqWX1He96fh/Wica/OOOMMU2Lytb6tKCazj+/Jz2Vv/Jmy2Wzatm1bhe3VfR9lX3P194jZvxPM/CwN1J/DUtU9jktJ27Vr11bYHh0dreTkZEVFuXZTP2PGDN18880aO3asJGnOnDn68MMPNX/+/AqTv/Pnz9fhw4f1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12RfFW2T1aSJ4lxod2e8njiGJ6+52u5fV/vKG86v2de8O47h7vPrrmNUti9PnBNvPkagxmv255+rKou3stc89TnnbqWlrk6lRknbbt26OZ4nJCRo5cqVNdkNAAAATGY37NpXdESS1CSkviyWAJjFw0QhISHq2rWr1qxZoyuuuELSibIba9as0fjx480NDgAAAF6jRklbAAAA+Ies4jwVGcWSmITMU1JTUzV69Gh169ZN3bt316xZs5Sfn6+xY8eaHRoAAAC8RLWTtl26dKn2yIsffvihxgEBAADAc/YWlqlnG0LS1hOGDRumzMxMTZ48Wfv371fnzp21cuVKNWzY0OzQAAAA4CWqnbQt/foWAAAA/EfZScgYaes548ePpxwCAAAAKlXtpO2UKVNqMw4AAACYYI/TSNtYEyMBAADVtTM7t9wyv8UB/0JNWwAAgADmNNKW8ggAAHi9Fi1aKDhtllNbrKTk5GRlZGSYEhMA96tR0rakpEQzZ87U0qVLtWvXLhUVFTm9fvjw4Uq2BAAAgDdxGmlLeQQAALxecHCwUlJSzA4DQC2z1mSjadOmacaMGRo2bJiys7OVmpqqq666SlarVVOnTnVziAAAAKgte4uOSJIiraGKDoowORoAAAAAUg1H2r7xxhuaN2+eLrnkEk2dOlXDhw9X69at1bFjR61fv1533XWXu+MEAACAm9kNu/b9rzxCk9D6slgsJkcEAID3qah+bF2TYgHgzJ9/PmuUtN2/f7/OOussSVJUVJSys7MlSZdeeqkeeeQR90UHAACAWpNpy5HNKJFEPVuASX0AVCQ5OVmqoH6szWZTnikRAShVWX1nf/n5rFHStlmzZtq3b5+aN2+u1q1b65NPPtHZZ5+t7777TqGhoe6OEQAAALWgtDSCRD1bBLbKkjLJycmmxAPAe1RWP3bz5s0mRAOgLH//+axR0vbKK6/UmjVr1KNHD915550aOXKkXn31Ve3atUv33nuvu2MEAABALdhbZhIyRtoikDGpDwAA5uNbL85qlLR98sknHc+HDRum5s2ba926dUpOTtbQoUPdFhwAAABqz56isknbQL4lBgAAgJmq+tZLRkaGKTGZzeqOnfTs2VOpqak1Tti+8MILatmypcLCwtSjRw99++231drurbfeksVi0RVXXFGj4wIAAAQyp5G2lEcAAACASUq/9fLXR3Bw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//Habk5OjsaMGaOwsDB16NBBy5cvr7Tv3Nxc3XHHHWrRooWaNm2qoUOHavfu3T865smTJ+vVV1/Vnj17qo2p6mtmffr08TorwrIsLV26VKNHj1Z4eLi6deum1NRUffnll7rmmmvUqFEjDR48WPv376+0/aVLl6pdu3YKDw/XzTffrBMnTngtf/nll9WtWzeFhoYqPj5ezz//vGfZgQMHZFmWVq1apcTERIWGhlb58wEAAIBv1McauUJqaqqnUbt582aatgDqBZq2AHARZsyYoQULFig9PV29evVSQUGBRo4cqZSUFH388ccaMWKExowZo6ysLM86EydO1MGDB7V+/Xq9+eabev7555WTk+O13fHjxysnJ0dr165VWlqa+vbtq2HDhunYsWPnHM+QIUM0evRozZgx45KPbd68ebrtttu0a9cuxcfH69Zbb9Xdd9+tmTNnaseOHTLGaPLkyV7rfPnll3r99df1zjvv6P3339fHH3+se+65x7N8+fLlmjNnjh599FGlp6frscce0+zZsyudGTxjxgxNmTJF6enpSkpKuuRjAQAAQN3xpxp5wYIFioyMVGRkpLKzs5WYmKjIyEjt2bNHN998syIjI7V58+Za+1kAwCUzAIBqLVu2zERERHim169fbySZNWvW/Oi6V1xxhVm8eLExxpiMjAwjyWzbts2zPD093UgyTz/9tDHGmE2bNpmmTZuaoqIir+107NjRLF26tNr9SDJvvfWW+eyzz0xQUJD573//a4wx5oYbbjDJycmeuMsuu8yzrwq9e/c2c+fO9drWQw895JlOTU01ksyf//xnz7zXXnvNhIaGeqbnzp1rgoKCzKFDhzzz1q5daxwOhzly5IjnGFasWOG173nz5pmEhARjjDGZmZlGklm0aFG1xwkAAAD/UB9q5OPHj5vMzEwzd+5ck5SUZDIzM81zzz1nBgwYYDIzM01mZqYpLCy8gKMGgLrlHxeYAYB6pn///l7TBQUFevjhh/Xee+/pyJEjKisrU2FhoecsgvT0dAUHB6tfv36edeLj4xUZGemZ3r17twoKCtSsWTOvbRcWFlZ5OYKzde/eXbfddptmzJihDz/88KKPrVevXp7nMTExkqSePXt6zSsqKlJeXp6aNm0qSYqLi1ObNm08MQkJCbJtWxkZGWrSpIn279+v3/zmN7rzzjs9MWVlZYqIiPDa99k/VwAAANQf/lQjV5xlu23bNt10001q3769Pv74Y11//fVq3779pR8sANQymrYAcBEaNWrkNf2HP/xB69at0x//+Ed16tRJYWFh+tnPfqaSkpLz3mZBQYFatWqlDRs2VFp2ZuF6Lo888oi6dOmiNWvWVFrmcDhkjPGaV1paWinuzJt/WZZV7Tzbts9rTAUFBZKkl156SYMGDfJaFhQU5DV99s8VAAAA9Ye/1MibNm3SddddJ0k6deqUNmzYoGnTpqmwsFBOp1MLFizQrFmzNGvWrPMeBwDUNZq2AFADPvzwQ02cOFE33nijpPLi8sCBA57l8fHxKisrU1pamgYMGCBJysjIUG5uriemb9++ys7OVnBw8EX/9b9du3aaPHmyZs2apY4dO3ota9Gihdddf/Py8pSZmXlR+zlbVlaWDh8+rNatW0uStmzZIofDoa5duyomJkatW7fWV199pQkTJtTI/gAAAOD/fFUj9+/fX7t27VJaWpruv/9+paSkKCsrS9dff7127twph8Oh6OjomjpMAKgV3IgMAGpA586dtXr1au3atUu7d+/Wrbfe6nUmateuXTVixAjdfffd2rp1q9LS0nTHHXcoLCzMEzN8+HAlJCRo7Nix+ve//60DBw7oo48+0oMPPqgdO3ac91hmzpypw4cPV7oz79ChQ/W3v/1NmzZt0qeffqrk5ORKZ7perNDQUCUnJ2v37t3atGmT7r33Xt18882KjY2VVH4G8Pz58/XMM8/o888/16effqply5bpqaeeqpH9AwAAwP/4qkYOCwtTp06dlJmZqWuuuUadOnXSoUOHNGTIEHXp0kWdOnWiaQvA79G0BYAa8NRTTykqKkqDBw/WmDFjlJSUpL59+3rFLFu2TK1bt1ZiYqLGjRunu+66Sy1btvQstyxL//znP3X11Vfr9ttvV5cuXXTLLbfo66+/9lxb9nxER0frgQceUFFRkdf8mTNnKjExUaNHj9aoUaM0duzYSmfjXqxOnTpp3LhxGjlypK699lr16tVLzz//vGf5HXfcoZdfflnLli1Tz549lZiYqFdeeUUdOnSokf0DAADA//i6Rt6wYYOuvvpqSdLGjRs9zwGgPrDM2Rc4BAAAAAAAAAD4DGfaAgAAAAAAAIAfoWkLAAAAAAAAAH6Epi0AAAAAAAAA+BGatgAAAAAAAADgR2jaAgAAAAAAAIAfoWkLAAAAAAAAAH6Epi0AAAAAAAAA+BGatgAAAAAAAADgR2jaAgAAAAAAAIAfoWkLAAAAAAAAAH6Epi0AAAAAAAAA+BGatgAAAAAAAADgR/4/2W2LoiX4IXMAAAAASUVORK5CYII=\n"},"metadata":{}}],"source":["fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n","fig.suptitle(\"Paper Trading Engine — Simulation Results\", fontsize=13, fontweight=\"bold\")\n","\n","# ── Equity Curve ──────────────────────────────────────────────────────────\n","axes[0].plot(range(len(engine_state[\"balance_curve\"])), engine_state[\"balance_curve\"],\n","             color=\"#2ecc71\", linewidth=2)\n","axes[0].axhline(INITIAL_BALANCE, linestyle=\"--\", color=\"#bdc3c7\", label=\"Initial Balance\")\n","axes[0].set_xlabel(\"Trade Number\")\n","axes[0].set_ylabel(\"Balance (USDT)\")\n","axes[0].set_title(\"Equity Curve\")\n","axes[0].legend()\n","axes[0].grid(True, alpha=0.3)\n","\n","# ── Per-Trade PnL Distribution ────────────────────────────────────────────\n","if engine_state[\"trades\"]:\n","    pnls = [t[\"pnl_pct\"] for t in engine_state[\"trades\"]]\n","    colors = [\"#2ecc71\" if p > 0 else \"#e74c3c\" for p in pnls]\n","    axes[1].bar(range(len(pnls)), pnls, color=colors, alpha=0.8, edgecolor=\"black\", linewidth=0.3)\n","    axes[1].axhline(0, color=\"#333\", linewidth=1)\n","    axes[1].set_xlabel(\"Trade #\")\n","    axes[1].set_ylabel(\"PnL (%)\")\n","    axes[1].set_title(\"Per-Trade PnL\")\n","    axes[1].grid(True, alpha=0.3, axis=\"y\")\n","\n","plt.tight_layout()\n","plt.show()\n"]}]}