"Every market data platform has a ceiling. The question is whether that ceiling matches your strategy."
That line is from a conversation with a quant researcher who spent three weeks building a tick-level order flow strategy before discovering his vendor of choice didn't support US equity trades. He switched platforms, lost momentum, and restarted his backtesting pipeline from scratch. His words captured the core frustration: product boundary confusion is not a documentation problem. It is a strategic betrayal.
This article exists to prevent that betrayal. If you are evaluating TickDB for US equity strategies, you deserve a clear, honest explanation of where TickDB's capabilities end — and where they begin. No marketing gloss. No vague assurances. Just the technical reality.
The Question Everyone Asks
Before we answer "why," we need to establish "what."
What does TickDB not support for US equities?
TickDB does not provide tick-level trade data (trades endpoint) for US stocks. The /v1/market/trades endpoint covers HK equities and crypto assets, but it explicitly excludes US equities and A-shares.
This means if your strategy requires individual trade prints — the raw sequence of transactions that power order flow analysis, transaction cost analysis, and microstructure signal research — TickDB is not your platform.
That is a fact. Here is the context behind it.
Why TickDB Made This Choice
The Economics of US Equity Data
US equity market data is expensive. Not "moderately priced" expensive. Fundamentally, structurally expensive.
The primary consolidated tape for US equities — operated by the Financial Industry Regulatory Authority (FINRA) via the Securities Information Processor (SIP) — does not distribute raw tick data for free. The SIP provides NBBO (National Best Bid and Offer) quotes and last-sale data, but at aggregated levels and with latency. Raw trade-by-trade data with full tape attribution (which exchange reported the trade, what was the condition code, was it a print or a correction) requires direct licensing from exchanges.
There are 16 registered exchanges for US equities. Each charges licensing fees for their proprietary data feeds. The CTA and UTP plans add additional costs. A platform ingesting full US equity tick data at exchange-level granularity faces licensing fees that scale into six figures annually before a single line of server code is written.
TickDB's product decision was not a failure of engineering will. It was a recognition that the unit economics of US equity tick data do not align with a developer-friendly, subscription-priced API. The moment TickDB tried to offer "cheap US tick data," it would either be operating at a loss or subsidizing one market segment at the expense of others.
Product Focus: What TickDB Actually Built
TickDB chose to be the best at what it could be the best at.
The decision to focus on cleaned, aligned, long-horizon OHLCV (kline) data was a deliberate strategic pivot. The /v1/market/kline endpoint provides 10+ years of hourly and daily bar data for US equities — data that is suitable for cross-cycle strategy backtesting, factor research, and regime analysis.
This is not a consolation prize. It is a different product.
| Data Type | US Equities (TickDB) | US Equities (Competitors) |
|---|---|---|
| Tick-level trades | ❌ Not supported | ✅ Full tape access |
| Order book depth | L1 (best bid/ask) | L2/L3 in some cases |
| OHLCV kline data | ✅ 10+ years, cleaned | ✅ Available, varies by vendor |
| Historical latency | <100 ms (WebSocket) | Polling: 1–5 sec |
| Cross-asset coverage | 6 asset classes, single API | Requires multiple vendors |
The OHLCV data that TickDB does provide is not "basic" or "inferior" for the use cases it serves. Cross-cycle backtesting, factor portfolio construction, and regime detection do not require tick-level granularity. What they require is long-horizon data integrity — clean pricing, proper dividend adjustments, split handling, and aligned timestamps across symbols.
TickDB invested in that pipeline. It did not invest in the raw tape ingestion infrastructure required for tick data.
What You Actually Get: US Equity Capabilities
Let us be precise about what TickDB does support for US equities, because the boundary is narrower than some users assume — but not as limiting as they fear.
Supported Capabilities
1. OHLCV Kline Data (10+ Years)
The /v1/market/kline endpoint provides historical candlestick data going back 10+ years for major US equity symbols. This includes adjusted close prices that account for splits and dividends, proper alignment across the symbol universe, and multiple interval options (1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w).
2. L1 Order Book Depth
TickDB provides real-time best bid and best ask for US equities via the /v1/market/depth endpoint. This is Level 1 data — the top of the book only. It does not include queue position, hidden orders, or deep book levels. For strategies that need bid/ask spread dynamics and pressure ratios at the top level, this is sufficient. For strategies requiring deep book reconstruction or queue modeling, it is not.
3. Real-Time WebSocket Streaming
Both kline and depth data are available via WebSocket push with sub-100ms latency. The implementation includes native ping/pong heartbeat support, automatic reconnection with exponential backoff, and rate-limit handling per the standard error code schema.
4. Cross-Asset Unified API
A single API covers US equities, HK equities, crypto, forex, precious metals, and indices. For users building multi-asset strategies, this eliminates the multi-vendor complexity that dominates enterprise data stacks.
Unsupported Capabilities
| Feature | Status | Notes |
|---|---|---|
| Tick-level trades | ❌ Not supported | trades endpoint excludes US equities |
| L2/L3 order book | ❌ Not supported | US depth limited to L1 |
| Exchange attribution | ❌ Not available | Which exchange reported a print |
| FINRA CAT | ❌ Not accessible | Consolidated audit trail data |
| Short interest | ❌ Not provided | Separate data source required |
| Options chain | ❌ Not provided | Separate vendor required |
The Practical Implication: Strategy Design Constraints
Understanding product boundaries is not an academic exercise. It directly shapes which strategies you can run and which you cannot.
Strategies That Work Well on TickDB
Long-horizon systematic strategies — Factor portfolios, cross-sectional momentum, mean reversion over daily bars, macro regime rotation. These strategies need long data histories, not tick granularity. TickDB's 10+ year kline dataset is purpose-built for this.
Event-driven strategies with OHLCV context — Earnings surprise analysis, macro announcement response, sector rotation. You can construct event windows from kline data and overlay microstructure reasoning from L1 depth.
Multi-asset portfolio construction — Building diversified portfolios across equities, crypto, and forex with a single data API. TickDB's cross-asset coverage simplifies the infrastructure.
Automated trading with L1 depth signals — Strategies that react to bid/ask pressure at the top of book, spread widening, or depth imbalance. L1 data captures these signals adequately for many algorithmic approaches.
Strategies That Require Tick Data (and Cannot Run on TickDB)
Order flow analysis — Buy/sell ratio calculations from individual prints, volume-weighted average price (VWAP) participation analysis, transaction cost analysis (TCA) at the print level.
Market maker strategies — Real-time quote management requiring knowledge of printed volume, trade direction, and exchange attribution to manage inventory risk.
Latency-sensitive microstructure arbitrage — Strategies that exploit quote fade, stale quote detection, or cross-exchange arbitrage requiring sub-second print visibility.
High-frequency statistical arbitrage — Strategies that depend on covariance matrices estimated from intraday tick data or short-window microstructure signals.
If your strategy falls into the second category, you need a different vendor. That is not a failure of TickDB — it is a mismatch of product and use case. Choosing the right tool is part of quant engineering.
Code Example: What TickDB Actually Provides
Here is a production-grade example demonstrating how to access the data that TickDB does provide for US equities — the /v1/market/kline and /v1/market/depth endpoints. This code follows the production-grade standards: heartbeat, exponential backoff with jitter, rate-limit handling, timeout enforcement, and environment-variable authentication.
Fetching 10 Years of US Equity OHLCV Data
import os
import time
import requests
import random
from datetime import datetime, timedelta
class TickDBKlineClient:
"""Production-grade client for TickDB kline endpoint."""
def __init__(self, api_key: str = None):
self.api_key = api_key or os.environ.get("TICKDB_API_KEY")
if not self.api_key:
raise ValueError("TICKDB_API_KEY environment variable is required")
self.base_url = "https://api.tickdb.ai/v1/market/kline"
self.max_retries = 5
self.base_delay = 1.0
self.max_delay = 32.0
self.request_timeout = (3.05, 30) # Connect timeout, read timeout
def _handle_api_error(self, response, retry_count: int):
"""Standard TickDB error handler per handbook error code reference."""
data = response.json() if response.headers.get("content-type", "").startswith("application/json") else {}
code = data.get("code", 0)
if code == 0:
return response # Success
if code in (1001, 1002):
raise ValueError("Invalid API key — check your TICKDB_API_KEY env var")
if code == 2002:
raise KeyError(f"Symbol not found — verify via /v1/symbols/available")
if code == 3001:
# Rate limit: respect Retry-After header
retry_after = int(response.headers.get("Retry-After", 5))
print(f"Rate limited. Waiting {retry_after}s before retry.")
time.sleep(retry_after)
return None
raise RuntimeError(f"Unexpected error {code}: {data.get('message')}")
def _request_with_backoff(self, method: str, url: str, **kwargs) -> requests.Response:
"""Exponential backoff with jitter for production resilience."""
kwargs.setdefault("timeout", self.request_timeout)
kwargs.setdefault("headers", {})
kwargs["headers"]["X-API-Key"] = self.api_key
for attempt in range(self.max_retries):
try:
response = requests.request(method, url, **kwargs)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
delay = retry_after
else:
error_handled = self._handle_api_error(response, attempt)
if error_handled is None:
delay = min(self.base_delay * (2 ** attempt), self.max_delay)
else:
return response
# Apply exponential backoff with jitter
if attempt < self.max_retries - 1:
jitter = random.uniform(0, delay * 0.1)
sleep_time = min(delay + jitter, self.max_delay)
print(f"Retry {attempt + 1}/{self.max_retries} in {sleep_time:.2f}s")
time.sleep(sleep_time)
delay *= 2
except requests.exceptions.Timeout:
if attempt == self.max_retries - 1:
raise
delay = min(self.base_delay * (2 ** attempt), self.max_delay)
time.sleep(delay)
raise RuntimeError(f"Failed after {self.max_retries} attempts")
def fetch_historical_bars(
self,
symbol: str,
interval: str = "1d",
start_time: int = None,
end_time: int = None,
limit: int = 1000
) -> list:
"""
Fetch historical OHLCV bars for US equity symbol.
Parameters:
symbol: Ticker symbol with exchange suffix (e.g., "AAPL.US")
interval: Candle interval ("1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w")
start_time: Unix timestamp (ms) for period start
end_time: Unix timestamp (ms) for period end
limit: Maximum bars returned (max 1000 per request)
Returns:
List of OHLCV bars with timestamp, open, high, low, close, volume
"""
params = {
"symbol": symbol,
"interval": interval,
"limit": limit
}
if start_time:
params["start"] = start_time
if end_time:
params["end"] = end_time
response = self._request_with_backoff("GET", self.base_url, params=params)
data = response.json()
if data.get("code") != 0:
raise RuntimeError(f"API error {data.get('code')}: {data.get('message')}")
return data.get("data", [])
# Usage example: fetch 10 years of daily bars for AAPL
if __name__ == "__main__":
client = TickDBKlineClient()
# Calculate start time: 10 years ago
end_ts = int(datetime.now().timestamp() * 1000)
start_ts = int((datetime.now() - timedelta(days=365 * 10)).timestamp() * 1000)
# Fetch in batches (max 1000 per request)
all_bars = []
current_end = end_ts
while current_end > start_ts:
batch = client.fetch_historical_bars(
symbol="AAPL.US",
interval="1d",
start_time=start_ts,
end_time=current_end,
limit=1000
)
if not batch:
break
all_bars.extend(batch)
# Move the end cursor to the earliest timestamp in this batch
current_end = batch[-1]["t"] # 't' is timestamp field
print(f"Fetched {len(batch)} bars. Total: {len(all_bars)}")
print(f"\nTotal bars retrieved: {len(all_bars)}")
print(f"Date range: {datetime.fromtimestamp(all_bars[0]['t']/1000)} to {datetime.fromtimestamp(all_bars[-1]['t']/1000)}")
Real-Time L1 Depth Monitoring
import os
import json
import time
import random
import threading
import websocket
class TickDBDepthWebSocket:
"""
Production-grade WebSocket client for US equity L1 depth data.
⚠️ For production HFT workloads, consider aiohttp/asyncio for non-blocking I/O.
This implementation is synchronous and suitable for strategy prototyping and
low-to-medium frequency trading systems.
"""
def __init__(self, api_key: str = None):
self.api_key = api_key or os.environ.get("TICKDB_API_KEY")
if not self.api_key:
raise ValueError("TICKDB_API_KEY environment variable is required")
self.ws_url = "wss://api.tickdb.ai/v1/ws/market/depth"
self.ws = None
self.connected = False
self.reconnect_delay = 1.0
self.max_reconnect_delay = 32.0
self.heartbeat_interval = 20 # seconds
self.running = False
self.subscription_callback = None
def on_message(self, ws, message):
"""Handle incoming WebSocket messages."""
try:
data = json.loads(message)
# Handle ping/pong heartbeat
if data.get("type") == "pong":
return
# Handle depth update
if data.get("type") == "depth":
self._process_depth_update(data)
# Handle subscription confirmation
if data.get("type") == "subscribe":
print(f"Subscription confirmed: {data}")
except json.JSONDecodeError:
print(f"Failed to parse message: {message}")
except Exception as e:
print(f"Error processing message: {e}")
def _process_depth_update(self, data: dict):
"""Process and forward depth updates to callback."""
symbol = data.get("symbol", "UNKNOWN")
bids = data.get("b", []) # Best bid
asks = data.get("a", []) # Best ask
# Compute pressure ratio from L1 data
if bids and asks:
bid_size = float(bids[0][1]) if bids else 0.0
ask_size = float(asks[0][1]) if asks else 0.0
pressure_ratio = bid_size / ask_size if ask_size > 0 else float('inf')
update = {
"symbol": symbol,
"timestamp": data.get("t"),
"best_bid": float(bids[0][0]),
"best_bid_size": bid_size,
"best_ask": float(asks[0][0]),
"best_ask_size": ask_size,
"spread_bps": ((float(asks[0][0]) - float(bids[0][0])) / float(bids[0][0])) * 10000,
"pressure_ratio": pressure_ratio
}
if self.subscription_callback:
self.subscription_callback(update)
def on_error(self, ws, error):
print(f"WebSocket error: {error}")
def on_close(self, ws, close_status_code, close_msg):
print(f"WebSocket closed: {close_status_code} - {close_msg}")
self.connected = False
# Automatic reconnection with exponential backoff + jitter
if self.running:
self._schedule_reconnect()
def on_open(self, ws):
"""Called when WebSocket connection is established."""
print("WebSocket connection established")
self.connected = True
self.reconnect_delay = 1.0 # Reset delay on successful connection
# Subscribe to depth for US equity symbols
symbols = ["AAPL.US", "NVDA.US", "TSLA.US"]
subscribe_msg = {
"type": "subscribe",
"symbols": symbols
}
ws.send(json.dumps(subscribe_msg))
print(f"Subscribed to depth for: {symbols}")
def _schedule_reconnect(self):
"""Schedule reconnection with exponential backoff + jitter."""
jitter = random.uniform(0, self.reconnect_delay * 0.1)
sleep_time = min(self.reconnect_delay + jitter, self.max_reconnect_delay)
print(f"Scheduling reconnect in {sleep_time:.2f}s (delay={self.reconnect_delay:.2f}s)")
# Run reconnect in background thread
reconnect_thread = threading.Thread(
target=lambda: self._delayed_reconnect(sleep_time)
)
reconnect_thread.daemon = True
reconnect_thread.start()
def _delayed_reconnect(self, delay: float):
"""Delay wrapper for reconnection."""
time.sleep(delay)
self.reconnect_delay = min(self.reconnect_delay * 2, self.max_reconnect_delay)
self._connect()
def _connect(self):
"""Establish WebSocket connection with auth in URL parameter."""
auth_url = f"{self.ws_url}?api_key={self.api_key}"
self.ws = websocket.WebSocketApp(
auth_url,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
on_open=self.on_open
)
# Run in background thread
ws_thread = threading.Thread(target=self.ws.run_forever)
ws_thread.daemon = True
ws_thread.start()
def start(self, callback=None):
"""
Start the WebSocket client with optional callback for depth updates.
Args:
callback: Function called with each depth update dict
"""
self.subscription_callback = callback
self.running = True
self._connect()
def stop(self):
"""Stop the WebSocket client gracefully."""
self.running = False
if self.ws:
self.ws.close()
print("WebSocket client stopped")
# Usage example
if __name__ == "__main__":
def handle_depth(update: dict):
"""Callback to process real-time depth updates."""
print(
f"{update['symbol']} | "
f"Bid: ${update['best_bid']:.2f} x {update['best_bid_size']:,.0f} | "
f"Ask: ${update['best_ask']:.2f} x {update['best_ask_size']:,.0f} | "
f"Spread: {update['spread_bps']:.1f} bps | "
f"Pressure: {update['pressure_ratio']:.2f}"
)
client = TickDBDepthWebSocket()
try:
print("Starting TickDB depth monitoring (press Ctrl+C to stop)...")
client.start(callback=handle_depth)
while client.running:
time.sleep(1)
except KeyboardInterrupt:
print("\nShutting down...")
client.stop()
Comparison Table: What You Sacrifice, What You Gain
This table is not designed to convince you that TickDB is superior to tick-data platforms. It is designed to help you make an informed choice based on your actual requirements.
| Dimension | TickDB | Polygon | Databento |
|---|---|---|---|
| US equity tick trades | ❌ Not supported | ✅ Full tape | ✅ Full tape |
| US equity OHLCV (10+ yr) | ✅ Supported | ✅ Supported | ✅ Supported |
| US equity L1 depth | ✅ Supported | ✅ Supported | ✅ Supported |
| US equity L2/L3 depth | ❌ Not supported | ✅ Supported | ✅ Supported |
| HK equity trades | ✅ Supported | ❌ Limited | ❌ Limited |
| Crypto trades | ✅ Supported | ✅ Supported | ✅ Supported |
| Cross-asset single API | ✅ 6 asset classes | ❌ Multi-product | ❌ Multi-product |
| Pricing model | Subscription | Tiered by data volume | Tiered by data volume |
| WebSocket latency | <100 ms | Polling (1–5s) on free tier | <100 ms |
| API authentication | Header (X-API-Key) |
Header | Header |
| Heartbeat / reconnect | Native | DIY | Native |
The honest summary: If you need US equity tick trades, go to Polygon or Databento for that data. If you need long-horizon OHLCV, cross-asset coverage, and a developer-friendly API, TickDB is purpose-built for that. Do not pay for a Ferrari when you need a moving truck — and do not buy a moving truck expecting it to win races.
Deployment Guidance by User Segment
Individual Retail Traders
If you are running end-of-day strategies, daily mean reversion, or multi-day momentum on US equities, TickDB's OHLCV data covers your needs. The free tier provides sufficient access for strategy prototyping. Do not over-engineer your data stack.
Quantitative Developers
If you are building a strategy that requires tick-level order flow analysis, use a dedicated tick-data vendor for that specific data stream, and use TickDB for historical bar backtesting and cross-asset context data. The two can coexist in your pipeline.
Institutional Teams
If your strategy requires tick data as a first-class input (market making, TCA, HFT), you need a dedicated tick-data platform. Evaluate Polygon, Databento, or direct exchange feeds. For portfolio-level systematic strategies that operate on daily bars, TickDB's long-horizon dataset and cross-asset API reduce infrastructure complexity significantly.
Strategic Takeaways
Three principles for navigating market data product boundaries:
1. Define your data requirements before selecting a vendor. A strategy that requires tick prints cannot be retrofitted to run on bar data. Know your inputs before you commit to a platform.
2. Understand that product focus is not a limitation — it is a design choice. TickDB chose to be excellent at long-horizon OHLCV and cross-asset coverage. That focus has a cost: it cannot also be excellent at tick data ingestion at the same price point. Every platform makes tradeoffs. Align yourself with platforms that made tradeoffs in your favor.
3. Multi-vendor data stacks are the industry norm for a reason. No single vendor covers every data need. The professional quant stack includes TickDB for historical bars and cross-asset context, plus a tick-data vendor for order flow analysis, plus a separate data source for short interest or options flow. Build infrastructure that can ingest from multiple sources, and choose vendors based on data quality for specific use cases.
Next Steps
If you are evaluating TickDB for long-horizon strategy backtesting, visit tickdb.ai and sign up for a free API key to access 10+ years of US equity OHLCV data with no credit card required.
If you need tick-level trade data for US equities, explore Polygon or Databento — both offer US equity tick data as a core capability. Use TickDB alongside for historical bar data and cross-asset context.
If you are building a multi-asset strategy, TickDB's unified API covering equities, crypto, forex, and indices simplifies your data infrastructure significantly. Install the tickdb-market-data SKILL in your AI coding assistant to streamline integration.
This article does not constitute investment advice. Market data products have specific capability boundaries; always verify current API documentation before building production systems. Past data coverage does not guarantee future availability.