Skip to content

Asynchronous Client (AsyncCotClient)

The asynchronous client (AsyncCotClient) provides high-throughput, non-blocking network I/O powered by httpx.AsyncClient.

It is designed for real-time market dashboards, FastAPI or Quart backends, algorithmic trading bots, and high-performance quantitative data ingestion pipelines.


⚡ Quick Example

import asyncio
from cotbias import AsyncCotClient


async def main():
    async with AsyncCotClient() as client:
        # Fetch multiple markets concurrently
        es_task = client.inference.get("ES")
        nq_task = client.inference.get("NQ")
        cl_task = client.inference.get("CL")

        es, nq, cl = await asyncio.gather(es_task, nq_task, cl_task)

        print(f"ES: {es.ai_bias_score} | NQ: {nq.ai_bias_score} | CL: {cl.ai_bias_score}")


asyncio.run(main())

🚀 Concurrent Batch Ingestion

When pulling timeseries or inference for dozens of contracts simultaneously, asyncio.gather dramatically reduces total fetch time:

import asyncio
import pandas as pd
from cotbias import AsyncCotClient

SYMBOLS = ["ES", "NQ", "YM", "RTY", "GC", "SI", "CL", "NG", "6E", "6J", "ZB", "ZN"]


async def fetch_timeseries(client: AsyncCotClient, symbol: str) -> tuple[str, pd.DataFrame]:
    df = await client.timeseries.get(symbol, lookback_weeks=52, as_dataframe=True)
    return symbol, df


async def main():
    async with AsyncCotClient() as client:
        tasks = [fetch_timeseries(client, sym) for sym in SYMBOLS]
        results = await asyncio.gather(*tasks)

    for symbol, df in results:
        latest = df.iloc[-1]
        print(f"{symbol:<4}: 52w COT Index = {latest['cot_52w_commercial']:.1f}")


asyncio.run(main())

🚦 Rate Limit Protection with Semaphores

To avoid overwhelming local network sockets or triggering transient rate limit spikes when querying hundreds of records, bound your concurrency using asyncio.Semaphore:

import asyncio
from cotbias import AsyncCotClient

SEMAPHORE_LIMIT = 5  # Max 5 simultaneous HTTP requests


async def worker(sem: asyncio.Semaphore, client: AsyncCotClient, symbol: str):
    async with sem:
        return await client.inference.get(symbol)


async def main():
    sem = asyncio.Semaphore(SEMAPHORE_LIMIT)
    async with AsyncCotClient() as client:
        all_markets = await client.markets.list()
        symbols = [m.symbol for m in all_markets]

        tasks = [worker(sem, client, sym) for sym in symbols]
        results = await asyncio.gather(*tasks, return_exceptions=True)

    successes = [r for r in results if not isinstance(r, Exception)]
    print(f"Successfully processed {len(successes)} / {len(symbols)} markets.")


asyncio.run(main())

🌐 FastAPI Integration Example

In modern web applications, instantiate a shared AsyncCotClient within the application lifespan:

from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends
from cotbias import AsyncCotClient

state = {}


@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: create client pool
    state["cot_client"] = AsyncCotClient()
    yield
    # Shutdown: close connections cleanly
    await state["cot_client"].aclose()


app = FastAPI(lifespan=lifespan)


def get_cot_client() -> AsyncCotClient:
    return state["cot_client"]


@app.get("/api/market/{symbol}")
async def get_market_analysis(symbol: str, client: AsyncCotClient = Depends(get_cot_client)):
    regime = await client.inference.get(symbol)
    return {
        "symbol": regime.symbol,
        "bias": regime.ai_bias_score,
        "regime": regime.regime_status,
        "rationale": regime.qualitative_attribution.macro_rationale,
    }