Skip to main content
Glama
ymylive
by ymylive

get_recent_trades

Retrieve recent public trades (the tape) for any symbol on a specified exchange. Use to inspect order flow, identify large trades, or compute short-term trade-driven metrics.

Instructions

Get recent public trades (the tape) for a symbol on a specific exchange.

Use to inspect order flow, identify large prints, or compute very short-term trade-driven metrics.

Args: exchange_id: CCXT exchange ID. symbol: Unified symbol, e.g. "BTC/USDT". limit: Number of recent trades to return (max ~1000 depending on venue).

Returns: Array of trades with id, timestamp, datetime, symbol, side, price, amount, cost, takerOrMaker.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
exchange_idYes
symbolYes
limitNo

Implementation Reference

  • Handler function for the get_recent_trades tool. Decorated with @mcp.tool(), it fetches recent public trades from a given exchange using CCXT's fetch_trades, with limit clamped to 1-1000.
    @mcp.tool()
    async def get_recent_trades(
        exchange_id: str,
        symbol: str,
        limit: int = 50,
    ) -> Any:
        """Get recent public trades (the tape) for a symbol on a specific exchange.
    
        Use to inspect order flow, identify large prints, or compute very
        short-term trade-driven metrics.
    
        Args:
            exchange_id: CCXT exchange ID.
            symbol: Unified symbol, e.g. "BTC/USDT".
            limit: Number of recent trades to return (max ~1000 depending on venue).
    
        Returns:
            Array of trades with `id`, `timestamp`, `datetime`, `symbol`, `side`,
            `price`, `amount`, `cost`, `takerOrMaker`.
        """
        def _do() -> Any:
            ex = _get_ccxt_exchange(exchange_id)
            return ex.fetch_trades(symbol, limit=max(1, min(limit, 1000)))
    
        return await _ccxt_call(_do)
  • Registration via @mcp.tool() decorator on the FastMCP instance imported from core.
    @mcp.tool()
  • Shared helper _ccxt_call that runs blocking CCXT calls in a thread executor, with optional per-exchange locking and structured error handling.
    async def _ccxt_call(fn, *args, exchange_id: str | None = None, **kwargs):
        """Run a blocking CCXT call in a thread; return structured error on failure.
    
        When `exchange_id` is supplied, the call is serialized through the
        per-exchange lock so concurrent fetches against the same instance don't
        corrupt CCXT's internal session/rate-limit state. When omitted (legacy
        callers), no lock is taken — preserving prior behavior.
        """
        loop = asyncio.get_running_loop()
    
        def _runner():
            if exchange_id:
                with _per_lock(exchange_id):
                    return fn(*args, **kwargs)
            return fn(*args, **kwargs)
    
        try:
            return await loop.run_in_executor(None, _runner)
        except ccxt.BaseError as e:
            return {"error": f"{type(e).__name__}: {e}"}
        except Exception as e:  # pragma: no cover - safety net
            return {"error": f"unexpected error: {type(e).__name__}: {e}"}
  • Helper that gets or builds a cached CCXT exchange instance with LRU eviction and thread-safe construction.
    def _get_ccxt_exchange(exchange_id: str) -> ccxt.Exchange:
        """Get (or build & cache) a CCXT exchange instance by id.
    
        The cache is bounded (LRU, max `_CCXT_CACHE_MAX` entries) and thread-safe.
        On first construction `load_markets()` is pre-called so concurrent method
        calls don't race on the lazy-initialized markets table.
        """
        exchange_id = exchange_id.lower().strip()
        cls = getattr(ccxt, exchange_id, None)
        if cls is None or not isinstance(cls, type) or not issubclass(cls, ccxt.Exchange):
            raise ValueError(
                f"Unknown CCXT exchange id: {exchange_id!r}. "
                f"Use list_supported_exchanges to see valid ids."
            )
    
        with _exchange_cache_lock:
            if exchange_id in _exchange_cache:
                _exchange_cache.move_to_end(exchange_id)
                return _exchange_cache[exchange_id]
    
        # Build outside the cache lock; serialize construction per-id.
        lock = _per_lock(exchange_id)
        with lock:
            # Re-check after acquiring per-id lock — another thread may have built
            # and cached it while we waited.
            with _exchange_cache_lock:
                if exchange_id in _exchange_cache:
                    _exchange_cache.move_to_end(exchange_id)
                    return _exchange_cache[exchange_id]
    
            inst = cls({"enableRateLimit": True, "timeout": int(DEFAULT_TIMEOUT * 1000)})
            # Pre-load markets so the first per-method call doesn't race on lazy
            # initialization. Failures (geo-block, no markets endpoint) are
            # ignored here; the actual call site will surface a clean error.
            try:
                inst.load_markets()
            except Exception:
                pass
    
            with _exchange_cache_lock:
                _exchange_cache[exchange_id] = inst
                _exchange_cache.move_to_end(exchange_id)
                while len(_exchange_cache) > _CCXT_CACHE_MAX:
                    evicted_id, evicted_inst = _exchange_cache.popitem(last=False)
                    try:
                        if hasattr(evicted_inst, "close"):
                            evicted_inst.close()
                    except Exception:
                        pass
                return inst
  • Schema definition via type annotations: exchange_id (str), symbol (str), limit (int, default 50). Returns Any (array of trade dicts).
    @mcp.tool()
    async def get_recent_trades(
        exchange_id: str,
        symbol: str,
        limit: int = 50,
    ) -> Any:
        """Get recent public trades (the tape) for a symbol on a specific exchange.
    
        Use to inspect order flow, identify large prints, or compute very
        short-term trade-driven metrics.
    
        Args:
            exchange_id: CCXT exchange ID.
            symbol: Unified symbol, e.g. "BTC/USDT".
            limit: Number of recent trades to return (max ~1000 depending on venue).
    
        Returns:
            Array of trades with `id`, `timestamp`, `datetime`, `symbol`, `side`,
            `price`, `amount`, `cost`, `takerOrMaker`.
        """
        def _do() -> Any:
            ex = _get_ccxt_exchange(exchange_id)
            return ex.fetch_trades(symbol, limit=max(1, min(limit, 1000)))
    
        return await _ccxt_call(_do)
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description partially discloses behavior: it mentions the return format and that limit has a maximum of ~1000 depending on venue. However, it omits other behavioral traits like rate limits, data freshness, authentication requirements, or idempotency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is organized into a brief intro, usage line, Args section, and Returns. It is concise without being terse, though the structure could be slightly more streamlined.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description includes a Returns section detailing the array of trade fields. It also covers all parameters adequately. Missing context like authentication or rate limits, but overall complete for a public data retrieval tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description compensates well by providing an Args section that explains each parameter (exchange_id, symbol, limit) with examples and constraints (e.g., default 50, max ~1000). Adds meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'Get recent public trades (the tape) for a symbol on a specific exchange.' It uses a specific verb-resource pair and outlines use cases (inspect order flow, identify large prints, compute metrics), distinguishing it from other get_* siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Description explicitly says 'Use to inspect order flow, identify large prints, or compute very short-term trade-driven metrics.' This gives clear guidance on when to use, but lacks explicit when-not-to-use or differentiation from other similar tools in the sibling list.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ymylive/coin-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server