CUCKOOTRADE
Guide

How to mock the Alpaca API for local development and tests

Point alpaca-py at a mock Alpaca server by changing one line. No API key, no paper account, no rate limit on your test suite — and the same bars come back every time you run it.

Python alpaca-py 5 minutes

The problem

Any application that reads market data hits the same four problems the moment you try to test it:

  • Credentials. A key has to reach your laptop, your teammates’ laptops, and CI — secrets management for data that is not secret.
  • Rate limits. A test suite that fans out across symbols burns a free-tier quota quickly, and then the build is red for a reason that has nothing to do with your code.
  • Closed markets. Today’s bars do not exist at 9pm, and Saturday has no bars at all. A demo rehearsed on Sunday behaves differently on Monday.
  • Moving data. Real prices change, so an assertion about a specific close is a test with an expiry date.

None of these are problems with Alpaca. They are the consequence of pointing a live production feed at something that is not production. A mock server fixes all four at once.

The one-line change

CuckooTrade serves Alpaca’s market data wire format at https://cuckootrade.com/api/v1/alpaca. alpaca-py already supports pointing at a different host through url_override, so the whole integration is that argument:

python — alpaca-py
from datetime import datetime

from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame

client = StockHistoricalDataClient(
    api_key="any",          # required by the constructor,
    secret_key="any",       # never checked by the server
    url_override="https://cuckootrade.com/api/v1/alpaca",
)

bars = client.get_stock_bars(StockBarsRequest(
    symbol_or_symbols=["AAPL", "MSFT"],
    timeframe=TimeFrame.Day,
    start=datetime(2026, 7, 1),
    end=datetime(2026, 7, 31),
))

for bar in bars["AAPL"]:
    print(bar.timestamp.date(), bar.close)

The keys are positional requirements of the constructor, not authentication. Delete the url_override line and the same code talks to the real Alpaca again — there is no mocking library to remove and no code path that exists only in tests.

Verified, not asserted. CuckooTrade’s CI runs alpaca-py against the live server as an acceptance test on every commit, so “the SDK parses it unmodified” becomes a build failure the moment it stops being true.

Verify it in one command

Before wiring up the SDK, confirm the endpoint from a shell. This works right now, from anywhere, with no setup:

shell — curl
curl -i 'https://cuckootrade.com/api/v1/alpaca/v2/stocks/bars?symbols=AAPL&timeframe=1Day&start=2026-07-01&end=2026-07-31'

The body is Alpaca’s exact shape — {"bars": {"AAPL": […]}, "next_page_token": null} with each bar carrying t, o, h, l, c, v, n, vw. Keep the -i: the response headers say which server answered.

response headers
x-cuckoo-synthetic: true
x-cuckoo-generation: 1
x-cuckoo-docs: https://cuckootrade.com/docs
cache-control: public, max-age=31536000, immutable

The marking rides in headers rather than the JSON body on purpose: strict SDK parsers reject unknown body fields, so a "synthetic": true key would have broken the exact compatibility this page is about.

What matches, and what deliberately does not

AreaBehavior
Bar shapeIdentical — t, o, h, l, c, v, n, vw.
PathsIdentical after the base URL: /v2/stocks/bars, /v2/stocks/{symbol}/bars, /v2/stocks/bars/latest.
Paramssymbols, timeframe, start, end, limit, page_token, sort behave as Alpaca documents them, including real cursor pagination.
ErrorsAlpaca’s {"code", "message"} shape and status codes — but the message states the valid grammar and includes a working example URL.
AuthNone. Keys are accepted and ignored rather than rejected, so client code that sends them works.
SymbolsAny well-formed string returns bars. ~130 well-known tickers sit at plausible price levels; every other string gets a stable hash-derived personality.
Partial barsOnly completed bars are served — no in-progress current day. Determinism requires it.
Extended hoursNot modeled. Intraday bars are regular session only, 09:30–16:00 ET.
feedAccepted and ignored; there is one synthetic feed, so SIP and IEX return the same data.

The data follows the real NYSE calendar — no bars on weekends or market holidays — while the API itself stays up regardless of the hour. That combination is what makes a demo behave the same on Sunday night as on Tuesday morning.

Writing tests that do not expire

The reason to prefer a deterministic server over recorded fixtures is that you can assert on values. Every bar is a pure function of symbol, timestamp, generation, and seed, so this holds indefinitely:

python — pytest
import httpx

BASE = "https://cuckootrade.com/api/v1/alpaca"


def test_bars_are_byte_stable():
    params = {"symbols": "AAPL", "timeframe": "1Day",
              "start": "2026-07-01", "end": "2026-07-31"}
    first = httpx.get(f"{BASE}/v2/stocks/bars", params=params).text
    second = httpx.get(f"{BASE}/v2/stocks/bars", params=params).text
    assert first == second        # identical today, identical next year

Two extension parameters give you control over that world:

  • seed=anything — selects an alternate universe. Same structure and realism, different history. Useful when one test needs a symbol trending up and another needs it trending down.
  • generation=1 — pins the generator version. If the engine is ever improved that becomes generation 2, and generation 1 stays queryable forever. Pinning it in fixtures makes the guarantee explicit.

Both are ignored by real Alpaca, so leaving them in a shared code path is harmless.

The part a real API cannot do

Once you are pointed at a mock server you get something the live API structurally cannot offer: market conditions on demand. Reserved ticker symbols return scripted behavior, anchored to the calendar so the pattern appears in any 30-day window.

shell — a crash, on a Tuesday
curl 'https://cuckootrade.com/api/v1/alpaca/v2/stocks/bars?symbols=CRASH,HALTS,GAPPY&timeframe=1Day'
TickerWhat your code has to survive
CRASHA ~25% drawdown over a few sessions, then a slow recovery.
HALTSMinute bars missing mid-session — gap handling.
STALEBars arriving on time with the price frozen and v=0 — freshness checks.
GAPPY±5–15% overnight gaps most days.
FLATZero-range bars at exactly $100.00 — naive chart autoscaling divides by zero here.
PENNY~$0.30 prices with four decimals — float and rounding bugs.
SPIKEYSingle-minute wicks that instantly revert.
CHOPPYHigh volatility with zero net drift.

Testing a trading bot against a crash, halt, or gap →

Running it yourself

A pipeline that depends on someone else’s server has a new outage mode. The engine is stateless and MIT-licensed, and determinism means a local instance serves byte-identical data to the hosted one for the same generation:

shell — docker
docker run -p 8000:8000 ghcr.io/tj-miller-dev/cuckootrade

# then point the SDK at it:
#   url_override="http://localhost:8000/api/v1/alpaca"

Running locally also removes the 60 requests/minute limit and gives an exact flap:n failure count, which a multi-replica hosted deployment cannot promise. Full CI setup, including a GitHub Actions service container →

One thing this is not for

Synthetic data cannot validate a strategy. CuckooTrade exists to exercise code paths — parsing, pagination, retries, charting, alerting. A backtest that profits against generated prices has learned the generator, not the market. Use real data, with all its friction, for anything that informs a real trade.

Next