Skip to content

Adapters

Adapter registry and factory functions.

adapters

Broker adapters for TradeTracer Executor.

This module provides the adapter registry and factory functions for creating broker adapter instances.

Available Adapters
  • sandbox: Paper trading with SQLite (always available)
  • ibkr: Interactive Brokers via TWS/Gateway (requires ib_insync)
Example
from adapters import get_adapter, list_adapters

# See available adapters
print(list_adapters())  # ["sandbox", "ibkr"]

# Create an adapter
adapter = get_adapter("sandbox", {"initial_cash": 100000})
adapter.connect()

# Execute an order
result = adapter.execute_order({
    "action": "buy",
    "symbol": "AAPL",
    "volume": 10,
    "price": 186.50
})

Bar

Bases: TypedDict

Single OHLCV bar with a timestamp.

Used in the bars array sent to TradeTracer.

Source code in adapters/base.py
class Bar(TypedDict, total=False):
    """
    Single OHLCV bar with a timestamp.

    Used in the ``bars`` array sent to TradeTracer.
    """

    t: int
    o: float | None
    h: float | None
    l: float | None
    c: float | None
    v: int | None

BaseAdapter

Bases: ABC

Abstract base class for broker adapters.

An adapter is the bridge between the executor and a specific broker. It knows how to connect to the broker, execute buy/sell orders, and optionally fetch real-time quotes. The executor owns the lifecycle — it calls connect() on start, fetch_quote() and execute_buy()/ execute_sell() during each tick, and disconnect() on stop.

Adapters should never raise exceptions for expected failures like insufficient funds or rejected orders. Instead, return a result dict with "success": False and an "error" message. The executor logs these and continues to the next order.

Source code in adapters/base.py
class BaseAdapter(ABC):
    """
    Abstract base class for broker adapters.

    An adapter is the bridge between the executor and a specific broker.
    It knows how to connect to the broker, execute buy/sell orders, and
    optionally fetch real-time quotes. The executor owns the lifecycle —
    it calls `connect()` on start, `fetch_quote()` and `execute_buy()`/
    `execute_sell()` during each tick, and `disconnect()` on stop.

    Adapters should never raise exceptions for expected failures like
    insufficient funds or rejected orders. Instead, return a result dict
    with `"success": False` and an `"error"` message. The executor logs
    these and continues to the next order.
    """

    @classmethod
    @abstractmethod
    def get_config_fields(cls) -> list[ConfigField]:
        """
        Return configuration fields for the web UI.

        Called by the web UI when rendering the adapter configuration form.
        Each field describes one input the user needs to fill in to connect
        to this broker — for example, an API key, a hostname, or a port number.

        The web UI dynamically renders form inputs based on these field
        definitions. When the user saves, the values are stored in
        `config.json` under `adapter_config` and passed as keyword arguments
        to the adapter's `__init__()`. The `name` of each field must match
        an `__init__` parameter name so the value gets passed through.

        Returns:
            List of field configuration dicts. Each dict has `name`, `label`,
            `type` (text/password/number/checkbox/select), and optionally
            `required`, `default`, and `options`.

        Example:
            ```python
            @classmethod
            def get_config_fields(cls):
                return [
                    {
                        "name": "api_key",
                        "label": "API Key",
                        "type": "password",
                        "required": True
                    },
                    {
                        "name": "sandbox",
                        "label": "Paper Trading",
                        "type": "checkbox",
                        "default": True
                    },
                ]
            ```
        """

    @abstractmethod
    def connect(self) -> bool:
        """
        Connect to the broker and verify the session is usable.

        Called exactly once when the user starts the executor. This is where
        the adapter should establish a network connection, authenticate with
        credentials, and verify that the session is ready to accept orders.

        If the connection fails — wrong credentials, broker offline, network
        error — return False. The executor will log the failure, stay in the
        stopped state, and the user can fix their config and try again.

        For adapters that don't need a persistent connection (like the sandbox
        adapter), this can simply return True.

        Returns:
            True if the connection was established and the adapter is ready
            to execute orders. False if something went wrong.
        """

    @abstractmethod
    def disconnect(self) -> None:
        """
        Disconnect from the broker and clean up resources.

        Called once when the user stops the executor, or when the container
        shuts down (via SIGTERM/SIGINT). This is where the adapter should
        close network connections, cancel any pending requests, and release
        resources.

        This method should never raise exceptions. If the connection is
        already closed or was never established, it should silently do nothing.
        The executor calls this unconditionally during shutdown regardless of
        the adapter's current state.
        """

    @abstractmethod
    def execute_buy(self, symbol: str, shares: int, price: float) -> FillResult:
        """
        Execute a buy order on the broker.

        Called by the executor when TradeTracer issues a buy order during a
        tick. The executor receives orders from the TradeTracer API response,
        then calls this method for each buy order. The adapter should submit
        the order to the broker and return the fill details.

        The `price` parameter is the ask price that TradeTracer used to size
        the order. Depending on your broker integration, you can use it as a
        limit price, a sanity check against slippage, or ignore it entirely
        and use a market order. The fill price you report back is what
        TradeTracer records as the actual execution price.

        On success, you must return all four fields: `success`, `fill_price`,
        `fill_shares`, and `commission`. The executor builds a transaction
        from these and reports it to TradeTracer on the next tick.

        On failure (insufficient funds, symbol not found, broker rejected the
        order), return `"success": False` with an `"error"` message. Do not
        raise exceptions — the executor logs the error and moves on to the
        next order.

        Args:
            symbol: Stock ticker symbol (e.g., `"AAPL"`, `"TSLA"`). Always
                uppercase, as provided by TradeTracer.
            shares: Number of shares to buy. Always a positive integer.
                This is the net consolidated volume from TradeTracer — if
                a strategy called `buy(50)` then `sell(20)` in the same
                tick, you receive `shares=30`.
            price: The ask price TradeTracer used when generating this
                order. Use as a limit price or for slippage validation.

        Returns:
            A dict with `success`, `fill_price`, `fill_shares`, and `commission`
            on success, or `success` and `error` on failure.

        Example:
            ```python
            # Success
            {"success": True, "fill_price": 186.50, "fill_shares": 100, "commission": 1.00}

            # Failure
            {"success": False, "error": "Insufficient funds"}
            ```
        """

    @abstractmethod
    def execute_sell(self, symbol: str, shares: int, price: float) -> FillResult:
        """
        Execute a sell order on the broker.

        Called by the executor when TradeTracer issues a sell order during a
        tick. Behaves identically to `execute_buy()` but for the sell side.

        The `price` parameter is the bid price that TradeTracer used to
        calculate expected proceeds. As with buys, you can use it as a limit,
        a sanity check, or ignore it.

        Args:
            symbol: Stock ticker symbol (e.g., `"AAPL"`, `"TSLA"`). Always
                uppercase, as provided by TradeTracer.
            shares: Number of shares to sell. Always a positive integer.
                The executor only sends sell orders for positions the strategy
                holds, but the adapter should still validate against its own
                position tracking in case of desync.
            price: The bid price TradeTracer used when generating this
                order. Use as a limit price or for slippage validation.

        Returns:
            Same format as `execute_buy()` — a dict with `success`, `fill_price`,
            `fill_shares`, and `commission` on success, or `success` and `error`
            on failure.
        """

    @abstractmethod
    def fetch_quote(self, symbol: str, eod_last_price: float | None) -> Quote | None:
        """
        Fetch a price quote for a symbol.

        Called by the executor at the start of every tick, once per tracked
        symbol. The executor sends the returned quotes to TradeTracer, which
        uses them for intraday strategy evaluation and order pricing.

        The `eod_last_price` is the most recent end-of-day close price from
        TradeTracer's data. Adapters can use it however they need — for
        example, the sandbox adapter uses it to simulate intraday price
        movement via random walk. Broker adapters like IBKR can ignore it
        and fetch real-time data from the broker instead.

        The EOD price also serves as a sanity check — users can compare
        their broker's price against TradeTracer's data to verify accuracy.
        TradeTracer's data is not always correct, and this transparency
        helps users catch discrepancies.

        Args:
            symbol: Stock ticker symbol (e.g., `"AAPL"`). Always uppercase.
            eod_last_price: Last known EOD close price from TradeTracer,
                or None if not yet available (first tick).

        Returns:
            A Quote dict with price fields, or None if unavailable.

        Example:
            ```python
            # Broker adapter — fetch real data, ignore EOD
            def fetch_quote(self, symbol, eod_last_price):
                ticker = self.client.get_ticker(symbol)
                return {"close": ticker.last, "bid": ticker.bid, "ask": ticker.ask}

            # Sandbox adapter — simulate from EOD
            def fetch_quote(self, symbol, eod_last_price):
                if not eod_last_price:
                    return None
                price = eod_last_price * (1 + random.uniform(-0.005, 0.005))
                return {"close": price, "bid": price - 0.01, "ask": price + 0.01}
            ```
        """

    def fetch_bars(self, symbol: str, count: int) -> list[Bar]:
        """
        Fetch historical OHLCV bars for warmup.

        Called by the executor when the strategy-runner requests warmup
        data for a symbol. The executor sends the returned bars in the
        ``bars`` array instead of a single bar derived from ``fetch_quote``.

        Override this method if your adapter can provide historical bars.
        The default implementation returns an empty list, which causes the
        executor to fall back to a single bar from ``fetch_quote``.

        Args:
            symbol: Stock ticker symbol (e.g., ``"AAPL"``). Always uppercase.
            count: Maximum number of bars to return (up to 1000).

        Returns:
            List of Bar dicts with keys ``t``, ``o``, ``h``, ``l``, ``c``, ``v``,
            ordered oldest-first. Empty list if historical data is unavailable.
        """
        return []

    def execute_order(self, order: Order) -> FillResult:
        """
        Dispatch an order from TradeTracer to the appropriate handler.

        This is the method the executor loop actually calls. It reads the
        `action` field from the order dict and routes to `execute_buy()` or
        `execute_sell()`. You do not need to override this method — it
        exists as a convenience so the executor doesn't need to branch on
        order type itself.

        The order dict comes directly from the TradeTracer API response.
        Each order represents a consolidated net trade for one symbol from
        one worker during one tick. TradeTracer handles consolidation — if
        a strategy called `buy(50)` then `sell(20)`, you receive a single
        order with `action: "buy"` and `volume: 30`.

        Args:
            order: Order dict from the TradeTracer API response with keys:

                - `action` (str): `"buy"` or `"sell"`.
                - `symbol` (str): Stock ticker symbol, uppercase.
                - `volume` (int): Number of shares (always positive).
                - `price` (float): Ask price for buys, bid price for sells.
                - `order_id` (str): UUID that links this order to the
                  transaction reported back on the next tick.

        Returns:
            The result dict from `execute_buy()` or `execute_sell()`.

        Example:
            ```python
            order = {
                "action": "buy",
                "symbol": "AAPL",
                "volume": 100,
                "price": 186.50,
                "order_id": "abc-123"
            }
            result = adapter.execute_order(order)
            ```
        """
        if order["action"] == "buy":
            return self.execute_buy(order["symbol"], order["volume"], order["price"])
        else:
            return self.execute_sell(order["symbol"], order["volume"], order["price"])

get_config_fields() abstractmethod classmethod

Return configuration fields for the web UI.

Called by the web UI when rendering the adapter configuration form. Each field describes one input the user needs to fill in to connect to this broker — for example, an API key, a hostname, or a port number.

The web UI dynamically renders form inputs based on these field definitions. When the user saves, the values are stored in config.json under adapter_config and passed as keyword arguments to the adapter's __init__(). The name of each field must match an __init__ parameter name so the value gets passed through.

Returns:

Type Description
list[ConfigField]

List of field configuration dicts. Each dict has name, label, type (text/password/number/checkbox/select), and optionally required, default, and options.

Example
@classmethod
def get_config_fields(cls):
    return [
        {
            "name": "api_key",
            "label": "API Key",
            "type": "password",
            "required": True
        },
        {
            "name": "sandbox",
            "label": "Paper Trading",
            "type": "checkbox",
            "default": True
        },
    ]
Source code in adapters/base.py
@classmethod
@abstractmethod
def get_config_fields(cls) -> list[ConfigField]:
    """
    Return configuration fields for the web UI.

    Called by the web UI when rendering the adapter configuration form.
    Each field describes one input the user needs to fill in to connect
    to this broker — for example, an API key, a hostname, or a port number.

    The web UI dynamically renders form inputs based on these field
    definitions. When the user saves, the values are stored in
    `config.json` under `adapter_config` and passed as keyword arguments
    to the adapter's `__init__()`. The `name` of each field must match
    an `__init__` parameter name so the value gets passed through.

    Returns:
        List of field configuration dicts. Each dict has `name`, `label`,
        `type` (text/password/number/checkbox/select), and optionally
        `required`, `default`, and `options`.

    Example:
        ```python
        @classmethod
        def get_config_fields(cls):
            return [
                {
                    "name": "api_key",
                    "label": "API Key",
                    "type": "password",
                    "required": True
                },
                {
                    "name": "sandbox",
                    "label": "Paper Trading",
                    "type": "checkbox",
                    "default": True
                },
            ]
        ```
    """

connect() abstractmethod

Connect to the broker and verify the session is usable.

Called exactly once when the user starts the executor. This is where the adapter should establish a network connection, authenticate with credentials, and verify that the session is ready to accept orders.

If the connection fails — wrong credentials, broker offline, network error — return False. The executor will log the failure, stay in the stopped state, and the user can fix their config and try again.

For adapters that don't need a persistent connection (like the sandbox adapter), this can simply return True.

Returns:

Type Description
bool

True if the connection was established and the adapter is ready to execute orders. False if something went wrong.

Source code in adapters/base.py
@abstractmethod
def connect(self) -> bool:
    """
    Connect to the broker and verify the session is usable.

    Called exactly once when the user starts the executor. This is where
    the adapter should establish a network connection, authenticate with
    credentials, and verify that the session is ready to accept orders.

    If the connection fails — wrong credentials, broker offline, network
    error — return False. The executor will log the failure, stay in the
    stopped state, and the user can fix their config and try again.

    For adapters that don't need a persistent connection (like the sandbox
    adapter), this can simply return True.

    Returns:
        True if the connection was established and the adapter is ready
        to execute orders. False if something went wrong.
    """

disconnect() abstractmethod

Disconnect from the broker and clean up resources.

Called once when the user stops the executor, or when the container shuts down (via SIGTERM/SIGINT). This is where the adapter should close network connections, cancel any pending requests, and release resources.

This method should never raise exceptions. If the connection is already closed or was never established, it should silently do nothing. The executor calls this unconditionally during shutdown regardless of the adapter's current state.

Source code in adapters/base.py
@abstractmethod
def disconnect(self) -> None:
    """
    Disconnect from the broker and clean up resources.

    Called once when the user stops the executor, or when the container
    shuts down (via SIGTERM/SIGINT). This is where the adapter should
    close network connections, cancel any pending requests, and release
    resources.

    This method should never raise exceptions. If the connection is
    already closed or was never established, it should silently do nothing.
    The executor calls this unconditionally during shutdown regardless of
    the adapter's current state.
    """

execute_buy(symbol, shares, price) abstractmethod

Execute a buy order on the broker.

Called by the executor when TradeTracer issues a buy order during a tick. The executor receives orders from the TradeTracer API response, then calls this method for each buy order. The adapter should submit the order to the broker and return the fill details.

The price parameter is the ask price that TradeTracer used to size the order. Depending on your broker integration, you can use it as a limit price, a sanity check against slippage, or ignore it entirely and use a market order. The fill price you report back is what TradeTracer records as the actual execution price.

On success, you must return all four fields: success, fill_price, fill_shares, and commission. The executor builds a transaction from these and reports it to TradeTracer on the next tick.

On failure (insufficient funds, symbol not found, broker rejected the order), return "success": False with an "error" message. Do not raise exceptions — the executor logs the error and moves on to the next order.

Parameters:

Name Type Description Default
symbol str

Stock ticker symbol (e.g., "AAPL", "TSLA"). Always uppercase, as provided by TradeTracer.

required
shares int

Number of shares to buy. Always a positive integer. This is the net consolidated volume from TradeTracer — if a strategy called buy(50) then sell(20) in the same tick, you receive shares=30.

required
price float

The ask price TradeTracer used when generating this order. Use as a limit price or for slippage validation.

required

Returns:

Type Description
FillResult

A dict with success, fill_price, fill_shares, and commission on success, or success and error on failure.

Example
# Success
{"success": True, "fill_price": 186.50, "fill_shares": 100, "commission": 1.00}

# Failure
{"success": False, "error": "Insufficient funds"}
Source code in adapters/base.py
@abstractmethod
def execute_buy(self, symbol: str, shares: int, price: float) -> FillResult:
    """
    Execute a buy order on the broker.

    Called by the executor when TradeTracer issues a buy order during a
    tick. The executor receives orders from the TradeTracer API response,
    then calls this method for each buy order. The adapter should submit
    the order to the broker and return the fill details.

    The `price` parameter is the ask price that TradeTracer used to size
    the order. Depending on your broker integration, you can use it as a
    limit price, a sanity check against slippage, or ignore it entirely
    and use a market order. The fill price you report back is what
    TradeTracer records as the actual execution price.

    On success, you must return all four fields: `success`, `fill_price`,
    `fill_shares`, and `commission`. The executor builds a transaction
    from these and reports it to TradeTracer on the next tick.

    On failure (insufficient funds, symbol not found, broker rejected the
    order), return `"success": False` with an `"error"` message. Do not
    raise exceptions — the executor logs the error and moves on to the
    next order.

    Args:
        symbol: Stock ticker symbol (e.g., `"AAPL"`, `"TSLA"`). Always
            uppercase, as provided by TradeTracer.
        shares: Number of shares to buy. Always a positive integer.
            This is the net consolidated volume from TradeTracer — if
            a strategy called `buy(50)` then `sell(20)` in the same
            tick, you receive `shares=30`.
        price: The ask price TradeTracer used when generating this
            order. Use as a limit price or for slippage validation.

    Returns:
        A dict with `success`, `fill_price`, `fill_shares`, and `commission`
        on success, or `success` and `error` on failure.

    Example:
        ```python
        # Success
        {"success": True, "fill_price": 186.50, "fill_shares": 100, "commission": 1.00}

        # Failure
        {"success": False, "error": "Insufficient funds"}
        ```
    """

execute_sell(symbol, shares, price) abstractmethod

Execute a sell order on the broker.

Called by the executor when TradeTracer issues a sell order during a tick. Behaves identically to execute_buy() but for the sell side.

The price parameter is the bid price that TradeTracer used to calculate expected proceeds. As with buys, you can use it as a limit, a sanity check, or ignore it.

Parameters:

Name Type Description Default
symbol str

Stock ticker symbol (e.g., "AAPL", "TSLA"). Always uppercase, as provided by TradeTracer.

required
shares int

Number of shares to sell. Always a positive integer. The executor only sends sell orders for positions the strategy holds, but the adapter should still validate against its own position tracking in case of desync.

required
price float

The bid price TradeTracer used when generating this order. Use as a limit price or for slippage validation.

required

Returns:

Type Description
FillResult

Same format as execute_buy() — a dict with success, fill_price, fill_shares, and commission on success, or success and error on failure.

Source code in adapters/base.py
@abstractmethod
def execute_sell(self, symbol: str, shares: int, price: float) -> FillResult:
    """
    Execute a sell order on the broker.

    Called by the executor when TradeTracer issues a sell order during a
    tick. Behaves identically to `execute_buy()` but for the sell side.

    The `price` parameter is the bid price that TradeTracer used to
    calculate expected proceeds. As with buys, you can use it as a limit,
    a sanity check, or ignore it.

    Args:
        symbol: Stock ticker symbol (e.g., `"AAPL"`, `"TSLA"`). Always
            uppercase, as provided by TradeTracer.
        shares: Number of shares to sell. Always a positive integer.
            The executor only sends sell orders for positions the strategy
            holds, but the adapter should still validate against its own
            position tracking in case of desync.
        price: The bid price TradeTracer used when generating this
            order. Use as a limit price or for slippage validation.

    Returns:
        Same format as `execute_buy()` — a dict with `success`, `fill_price`,
        `fill_shares`, and `commission` on success, or `success` and `error`
        on failure.
    """

fetch_quote(symbol, eod_last_price) abstractmethod

Fetch a price quote for a symbol.

Called by the executor at the start of every tick, once per tracked symbol. The executor sends the returned quotes to TradeTracer, which uses them for intraday strategy evaluation and order pricing.

The eod_last_price is the most recent end-of-day close price from TradeTracer's data. Adapters can use it however they need — for example, the sandbox adapter uses it to simulate intraday price movement via random walk. Broker adapters like IBKR can ignore it and fetch real-time data from the broker instead.

The EOD price also serves as a sanity check — users can compare their broker's price against TradeTracer's data to verify accuracy. TradeTracer's data is not always correct, and this transparency helps users catch discrepancies.

Parameters:

Name Type Description Default
symbol str

Stock ticker symbol (e.g., "AAPL"). Always uppercase.

required
eod_last_price float | None

Last known EOD close price from TradeTracer, or None if not yet available (first tick).

required

Returns:

Type Description
Quote | None

A Quote dict with price fields, or None if unavailable.

Example
# Broker adapter — fetch real data, ignore EOD
def fetch_quote(self, symbol, eod_last_price):
    ticker = self.client.get_ticker(symbol)
    return {"close": ticker.last, "bid": ticker.bid, "ask": ticker.ask}

# Sandbox adapter — simulate from EOD
def fetch_quote(self, symbol, eod_last_price):
    if not eod_last_price:
        return None
    price = eod_last_price * (1 + random.uniform(-0.005, 0.005))
    return {"close": price, "bid": price - 0.01, "ask": price + 0.01}
Source code in adapters/base.py
@abstractmethod
def fetch_quote(self, symbol: str, eod_last_price: float | None) -> Quote | None:
    """
    Fetch a price quote for a symbol.

    Called by the executor at the start of every tick, once per tracked
    symbol. The executor sends the returned quotes to TradeTracer, which
    uses them for intraday strategy evaluation and order pricing.

    The `eod_last_price` is the most recent end-of-day close price from
    TradeTracer's data. Adapters can use it however they need — for
    example, the sandbox adapter uses it to simulate intraday price
    movement via random walk. Broker adapters like IBKR can ignore it
    and fetch real-time data from the broker instead.

    The EOD price also serves as a sanity check — users can compare
    their broker's price against TradeTracer's data to verify accuracy.
    TradeTracer's data is not always correct, and this transparency
    helps users catch discrepancies.

    Args:
        symbol: Stock ticker symbol (e.g., `"AAPL"`). Always uppercase.
        eod_last_price: Last known EOD close price from TradeTracer,
            or None if not yet available (first tick).

    Returns:
        A Quote dict with price fields, or None if unavailable.

    Example:
        ```python
        # Broker adapter — fetch real data, ignore EOD
        def fetch_quote(self, symbol, eod_last_price):
            ticker = self.client.get_ticker(symbol)
            return {"close": ticker.last, "bid": ticker.bid, "ask": ticker.ask}

        # Sandbox adapter — simulate from EOD
        def fetch_quote(self, symbol, eod_last_price):
            if not eod_last_price:
                return None
            price = eod_last_price * (1 + random.uniform(-0.005, 0.005))
            return {"close": price, "bid": price - 0.01, "ask": price + 0.01}
        ```
    """

fetch_bars(symbol, count)

Fetch historical OHLCV bars for warmup.

Called by the executor when the strategy-runner requests warmup data for a symbol. The executor sends the returned bars in the bars array instead of a single bar derived from fetch_quote.

Override this method if your adapter can provide historical bars. The default implementation returns an empty list, which causes the executor to fall back to a single bar from fetch_quote.

Parameters:

Name Type Description Default
symbol str

Stock ticker symbol (e.g., "AAPL"). Always uppercase.

required
count int

Maximum number of bars to return (up to 1000).

required

Returns:

Type Description
list[Bar]

List of Bar dicts with keys t, o, h, l, c, v, ordered oldest-first. Empty list if historical data is unavailable.

Source code in adapters/base.py
def fetch_bars(self, symbol: str, count: int) -> list[Bar]:
    """
    Fetch historical OHLCV bars for warmup.

    Called by the executor when the strategy-runner requests warmup
    data for a symbol. The executor sends the returned bars in the
    ``bars`` array instead of a single bar derived from ``fetch_quote``.

    Override this method if your adapter can provide historical bars.
    The default implementation returns an empty list, which causes the
    executor to fall back to a single bar from ``fetch_quote``.

    Args:
        symbol: Stock ticker symbol (e.g., ``"AAPL"``). Always uppercase.
        count: Maximum number of bars to return (up to 1000).

    Returns:
        List of Bar dicts with keys ``t``, ``o``, ``h``, ``l``, ``c``, ``v``,
        ordered oldest-first. Empty list if historical data is unavailable.
    """
    return []

execute_order(order)

Dispatch an order from TradeTracer to the appropriate handler.

This is the method the executor loop actually calls. It reads the action field from the order dict and routes to execute_buy() or execute_sell(). You do not need to override this method — it exists as a convenience so the executor doesn't need to branch on order type itself.

The order dict comes directly from the TradeTracer API response. Each order represents a consolidated net trade for one symbol from one worker during one tick. TradeTracer handles consolidation — if a strategy called buy(50) then sell(20), you receive a single order with action: "buy" and volume: 30.

Parameters:

Name Type Description Default
order Order

Order dict from the TradeTracer API response with keys:

  • action (str): "buy" or "sell".
  • symbol (str): Stock ticker symbol, uppercase.
  • volume (int): Number of shares (always positive).
  • price (float): Ask price for buys, bid price for sells.
  • order_id (str): UUID that links this order to the transaction reported back on the next tick.
required

Returns:

Type Description
FillResult

The result dict from execute_buy() or execute_sell().

Example
order = {
    "action": "buy",
    "symbol": "AAPL",
    "volume": 100,
    "price": 186.50,
    "order_id": "abc-123"
}
result = adapter.execute_order(order)
Source code in adapters/base.py
def execute_order(self, order: Order) -> FillResult:
    """
    Dispatch an order from TradeTracer to the appropriate handler.

    This is the method the executor loop actually calls. It reads the
    `action` field from the order dict and routes to `execute_buy()` or
    `execute_sell()`. You do not need to override this method — it
    exists as a convenience so the executor doesn't need to branch on
    order type itself.

    The order dict comes directly from the TradeTracer API response.
    Each order represents a consolidated net trade for one symbol from
    one worker during one tick. TradeTracer handles consolidation — if
    a strategy called `buy(50)` then `sell(20)`, you receive a single
    order with `action: "buy"` and `volume: 30`.

    Args:
        order: Order dict from the TradeTracer API response with keys:

            - `action` (str): `"buy"` or `"sell"`.
            - `symbol` (str): Stock ticker symbol, uppercase.
            - `volume` (int): Number of shares (always positive).
            - `price` (float): Ask price for buys, bid price for sells.
            - `order_id` (str): UUID that links this order to the
              transaction reported back on the next tick.

    Returns:
        The result dict from `execute_buy()` or `execute_sell()`.

    Example:
        ```python
        order = {
            "action": "buy",
            "symbol": "AAPL",
            "volume": 100,
            "price": 186.50,
            "order_id": "abc-123"
        }
        result = adapter.execute_order(order)
        ```
    """
    if order["action"] == "buy":
        return self.execute_buy(order["symbol"], order["volume"], order["price"])
    else:
        return self.execute_sell(order["symbol"], order["volume"], order["price"])

ConfigField

Bases: TypedDict

Configuration field for the web UI form.

Displayed as a form input in the executor's web interface. The name must match an __init__ parameter on the adapter.

Source code in adapters/base.py
class ConfigField(TypedDict, total=False):
    """
    Configuration field for the web UI form.

    Displayed as a form input in the executor's web interface.
    The `name` must match an `__init__` parameter on the adapter.
    """

    name: str
    label: str
    type: Literal["text", "password", "number", "checkbox", "select"]
    required: bool
    default: Any
    options: list[dict[str, str]]

FillResult

Bases: TypedDict

Result of executing a buy or sell order.

On success, success is True and fill_price, fill_shares, and commission are set. On failure, success is False and error describes what went wrong.

Source code in adapters/base.py
class FillResult(TypedDict, total=False):
    """
    Result of executing a buy or sell order.

    On success, `success` is True and `fill_price`, `fill_shares`,
    and `commission` are set. On failure, `success` is False and
    `error` describes what went wrong.
    """

    success: bool
    fill_price: float
    fill_shares: int
    commission: float
    error: str

Order

Bases: TypedDict

Order from the TradeTracer API.

Passed to execute_order(), which dispatches to execute_buy() or execute_sell().

Source code in adapters/base.py
class Order(TypedDict):
    """
    Order from the TradeTracer API.

    Passed to `execute_order()`, which dispatches to
    `execute_buy()` or `execute_sell()`.
    """

    order_id: str
    action: Literal["buy", "sell"]
    symbol: str
    volume: int
    price: float

Quote

Bases: TypedDict

Price quote for a symbol.

Returned by fetch_quote(). The executor maps this into the TradeTracer bars format (with bid/ask) before sending. All fields are optional — return whatever the broker provides.

Source code in adapters/base.py
class Quote(TypedDict, total=False):
    """
    Price quote for a symbol.

    Returned by `fetch_quote()`. The executor maps this into the
    TradeTracer ``bars`` format (with `bid`/`ask`) before sending.
    All fields are optional — return whatever the broker provides.
    """

    open: float | None
    high: float | None
    low: float | None
    close: float | None
    volume: int | None
    bid: float | None
    ask: float | None
    time: int | None

SandboxAdapter

Bases: BaseAdapter

Paper trading adapter.

Every order fills immediately at the requested price. Intraday prices are simulated as a random walk from the EOD close.

Source code in adapters/sandbox.py
class SandboxAdapter(BaseAdapter):
    """
    Paper trading adapter.

    Every order fills immediately at the requested price.
    Intraday prices are simulated as a random walk from the EOD close.
    """

    def __init__(self, **kwargs):
        self._last_prices: dict[str, float] = {}
        self._session: dict[str, dict] = {}  # {symbol: {open, high, low}}

    @classmethod
    def get_config_fields(cls) -> list[ConfigField]:
        """
        Return configuration fields for web UI.

        Returns:
            Empty list — sandbox needs no configuration.
        """
        return []

    def connect(self) -> bool:
        """
        Connect to sandbox.

        Returns:
            Always True.
        """
        return True

    def disconnect(self) -> None:
        """Disconnect from sandbox."""
        self._last_prices.clear()
        self._session.clear()

    def execute_buy(self, symbol: str, shares: int, price: float) -> FillResult:
        """
        Execute a buy order.

        Fills immediately at the given price with zero commission.

        Args:
            symbol: Stock symbol.
            shares: Number of shares to buy.
            price: Price per share.

        Returns:
            Fill result with the requested price and shares.
        """
        return {
            "success": True,
            "fill_price": price,
            "fill_shares": shares,
            "commission": 0.0,
        }

    def execute_sell(self, symbol: str, shares: int, price: float) -> FillResult:
        """
        Execute a sell order.

        Fills immediately at the given price with zero commission.

        Args:
            symbol: Stock symbol.
            shares: Number of shares to sell.
            price: Price per share.

        Returns:
            Fill result with the requested price and shares.
        """
        return {
            "success": True,
            "fill_price": price,
            "fill_shares": shares,
            "commission": 0.0,
        }

    def fetch_quote(self, symbol: str, eod_last_price: float | None) -> Quote | None:
        """
        Simulate an intraday quote via random walk.

        On the first call for a symbol, starts from the EOD close price.
        On subsequent calls, walks from the previous simulated price.
        Uses a random walk to simulate realistic intraday movement.

        Args:
            symbol: Stock symbol.
            eod_last_price: Last EOD close from TradeTracer.

        Returns:
            Simulated quote, or None if no EOD price available.
        """
        if not eod_last_price:
            return None

        base = self._last_prices.get(symbol, eod_last_price)
        step = random.uniform(-0.005, 0.005)
        price = round(base * (1 + step), 2)
        self._last_prices[symbol] = price

        # Track intraday session (open/high/low)
        if symbol not in self._session:
            self._session[symbol] = {"open": price, "high": price, "low": price}
        sess = self._session[symbol]
        sess["high"] = max(sess["high"], price)
        sess["low"] = min(sess["low"], price)

        # Spread: 0.01-0.05% of price, varies per tick
        spread_pct = random.uniform(0.0001, 0.0005)
        half_spread = round(price * spread_pct, 2) or 0.01

        # Bid/ask: last trade (close) is between bid and ask
        bid = round(price - half_spread, 2)
        ask = round(price + half_spread, 2)

        return {
            "open": sess["open"],
            "high": sess["high"],
            "low": sess["low"],
            "close": price,
            "volume": random.randint(100, 5000),
            "bid": bid,
            "ask": ask,
            "time": int(time.time()),
        }

    def fetch_bars(self, symbol: str, count: int) -> list[Bar]:
        """
        Generate simulated historical bars via random walk.

        Walks backwards from the last known price (or a neutral base)
        to produce ``count`` 1-minute bars ending at the current time.
        """
        base = self._last_prices.get(symbol)
        if not base:
            return []

        now = int(time.time())
        bars: list[Bar] = []
        price = base

        # Walk backwards to generate prices, then reverse
        prices = [price]
        for _ in range(count - 1):
            step = random.uniform(-0.003, 0.003)
            price = round(price / (1 + step), 2)
            prices.append(price)
        prices.reverse()

        for i, p in enumerate(prices):
            t = now - (len(prices) - 1 - i) * 60
            high = round(p * (1 + random.uniform(0, 0.002)), 2)
            low = round(p * (1 - random.uniform(0, 0.002)), 2)
            bars.append({
                "t": t,
                "o": round(p + random.uniform(-0.1, 0.1), 2),
                "h": high,
                "l": low,
                "c": p,
                "v": random.randint(100, 5000),
            })

        return bars

get_config_fields() classmethod

Return configuration fields for web UI.

Returns:

Type Description
list[ConfigField]

Empty list — sandbox needs no configuration.

Source code in adapters/sandbox.py
@classmethod
def get_config_fields(cls) -> list[ConfigField]:
    """
    Return configuration fields for web UI.

    Returns:
        Empty list — sandbox needs no configuration.
    """
    return []

connect()

Connect to sandbox.

Returns:

Type Description
bool

Always True.

Source code in adapters/sandbox.py
def connect(self) -> bool:
    """
    Connect to sandbox.

    Returns:
        Always True.
    """
    return True

disconnect()

Disconnect from sandbox.

Source code in adapters/sandbox.py
def disconnect(self) -> None:
    """Disconnect from sandbox."""
    self._last_prices.clear()
    self._session.clear()

execute_buy(symbol, shares, price)

Execute a buy order.

Fills immediately at the given price with zero commission.

Parameters:

Name Type Description Default
symbol str

Stock symbol.

required
shares int

Number of shares to buy.

required
price float

Price per share.

required

Returns:

Type Description
FillResult

Fill result with the requested price and shares.

Source code in adapters/sandbox.py
def execute_buy(self, symbol: str, shares: int, price: float) -> FillResult:
    """
    Execute a buy order.

    Fills immediately at the given price with zero commission.

    Args:
        symbol: Stock symbol.
        shares: Number of shares to buy.
        price: Price per share.

    Returns:
        Fill result with the requested price and shares.
    """
    return {
        "success": True,
        "fill_price": price,
        "fill_shares": shares,
        "commission": 0.0,
    }

execute_sell(symbol, shares, price)

Execute a sell order.

Fills immediately at the given price with zero commission.

Parameters:

Name Type Description Default
symbol str

Stock symbol.

required
shares int

Number of shares to sell.

required
price float

Price per share.

required

Returns:

Type Description
FillResult

Fill result with the requested price and shares.

Source code in adapters/sandbox.py
def execute_sell(self, symbol: str, shares: int, price: float) -> FillResult:
    """
    Execute a sell order.

    Fills immediately at the given price with zero commission.

    Args:
        symbol: Stock symbol.
        shares: Number of shares to sell.
        price: Price per share.

    Returns:
        Fill result with the requested price and shares.
    """
    return {
        "success": True,
        "fill_price": price,
        "fill_shares": shares,
        "commission": 0.0,
    }

fetch_quote(symbol, eod_last_price)

Simulate an intraday quote via random walk.

On the first call for a symbol, starts from the EOD close price. On subsequent calls, walks from the previous simulated price. Uses a random walk to simulate realistic intraday movement.

Parameters:

Name Type Description Default
symbol str

Stock symbol.

required
eod_last_price float | None

Last EOD close from TradeTracer.

required

Returns:

Type Description
Quote | None

Simulated quote, or None if no EOD price available.

Source code in adapters/sandbox.py
def fetch_quote(self, symbol: str, eod_last_price: float | None) -> Quote | None:
    """
    Simulate an intraday quote via random walk.

    On the first call for a symbol, starts from the EOD close price.
    On subsequent calls, walks from the previous simulated price.
    Uses a random walk to simulate realistic intraday movement.

    Args:
        symbol: Stock symbol.
        eod_last_price: Last EOD close from TradeTracer.

    Returns:
        Simulated quote, or None if no EOD price available.
    """
    if not eod_last_price:
        return None

    base = self._last_prices.get(symbol, eod_last_price)
    step = random.uniform(-0.005, 0.005)
    price = round(base * (1 + step), 2)
    self._last_prices[symbol] = price

    # Track intraday session (open/high/low)
    if symbol not in self._session:
        self._session[symbol] = {"open": price, "high": price, "low": price}
    sess = self._session[symbol]
    sess["high"] = max(sess["high"], price)
    sess["low"] = min(sess["low"], price)

    # Spread: 0.01-0.05% of price, varies per tick
    spread_pct = random.uniform(0.0001, 0.0005)
    half_spread = round(price * spread_pct, 2) or 0.01

    # Bid/ask: last trade (close) is between bid and ask
    bid = round(price - half_spread, 2)
    ask = round(price + half_spread, 2)

    return {
        "open": sess["open"],
        "high": sess["high"],
        "low": sess["low"],
        "close": price,
        "volume": random.randint(100, 5000),
        "bid": bid,
        "ask": ask,
        "time": int(time.time()),
    }

fetch_bars(symbol, count)

Generate simulated historical bars via random walk.

Walks backwards from the last known price (or a neutral base) to produce count 1-minute bars ending at the current time.

Source code in adapters/sandbox.py
def fetch_bars(self, symbol: str, count: int) -> list[Bar]:
    """
    Generate simulated historical bars via random walk.

    Walks backwards from the last known price (or a neutral base)
    to produce ``count`` 1-minute bars ending at the current time.
    """
    base = self._last_prices.get(symbol)
    if not base:
        return []

    now = int(time.time())
    bars: list[Bar] = []
    price = base

    # Walk backwards to generate prices, then reverse
    prices = [price]
    for _ in range(count - 1):
        step = random.uniform(-0.003, 0.003)
        price = round(price / (1 + step), 2)
        prices.append(price)
    prices.reverse()

    for i, p in enumerate(prices):
        t = now - (len(prices) - 1 - i) * 60
        high = round(p * (1 + random.uniform(0, 0.002)), 2)
        low = round(p * (1 - random.uniform(0, 0.002)), 2)
        bars.append({
            "t": t,
            "o": round(p + random.uniform(-0.1, 0.1), 2),
            "h": high,
            "l": low,
            "c": p,
            "v": random.randint(100, 5000),
        })

    return bars

get_adapter(adapter_type, config)

Create an adapter instance by type.

Parameters:

Name Type Description Default
adapter_type str

Adapter identifier (e.g., "sandbox", "ibkr").

required
config dict[str, Any]

Adapter-specific configuration dict.

required

Returns:

Type Description
BaseAdapter

Configured adapter instance.

Raises:

Type Description
ValueError

If adapter_type is unknown.

Example
adapter = get_adapter("sandbox", {"initial_cash": 50000})
Source code in adapters/__init__.py
def get_adapter(adapter_type: str, config: dict[str, Any]) -> BaseAdapter:
    """
    Create an adapter instance by type.

    Args:
        adapter_type: Adapter identifier (e.g., "sandbox", "ibkr").
        config: Adapter-specific configuration dict.

    Returns:
        Configured adapter instance.

    Raises:
        ValueError: If adapter_type is unknown.

    Example:
        ```python
        adapter = get_adapter("sandbox", {"initial_cash": 50000})
        ```
    """
    adapter_class = ADAPTERS.get(adapter_type)
    if not adapter_class:
        available = ", ".join(ADAPTERS.keys())
        raise ValueError(f"Unknown adapter: {adapter_type}. Available: {available}")
    return adapter_class(**config)

get_adapter_fields(adapter_type)

Get configuration fields for an adapter's web UI.

Parameters:

Name Type Description Default
adapter_type str

Adapter identifier.

required

Returns:

Type Description
list[dict[str, Any]]

List of field configuration dicts, or empty list if unknown.

Example
fields = get_adapter_fields("sandbox")
# [{"name": "initial_cash", "type": "number", ...}]
Source code in adapters/__init__.py
def get_adapter_fields(adapter_type: str) -> list[dict[str, Any]]:
    """
    Get configuration fields for an adapter's web UI.

    Args:
        adapter_type: Adapter identifier.

    Returns:
        List of field configuration dicts, or empty list if unknown.

    Example:
        ```python
        fields = get_adapter_fields("sandbox")
        # [{"name": "initial_cash", "type": "number", ...}]
        ```
    """
    adapter_class = ADAPTERS.get(adapter_type)
    if not adapter_class:
        return []
    return adapter_class.get_config_fields()

list_adapters()

List available adapter types.

Returns:

Type Description
list[str]

List of adapter identifiers.

Example
adapters = list_adapters()
# ["sandbox", "ibkr"]
Source code in adapters/__init__.py
def list_adapters() -> list[str]:
    """
    List available adapter types.

    Returns:
        List of adapter identifiers.

    Example:
        ```python
        adapters = list_adapters()
        # ["sandbox", "ibkr"]
        ```
    """
    return list(ADAPTERS.keys())