For individual developers building A-share quantitative trading systems, the data problem is brutally concrete. You need minute-level OHLCV data to backtest mean-reversion strategies. You need order book depth to model spread dynamics. You need real-time quotes to trigger event-driven entries. And you are comparing three fundamentally different approaches: a paid professional API, a free open-source library, and whatever you can cobble together from public sources.
The choice is not obvious. Tushare charges for Level-2 data. AkShare is free but slow. TickDB offers a unified API but has specific coverage boundaries. Each option carries trade-offs in cost, latency, data completeness, and engineering overhead that are not always visible on a feature matrix.
This article benchmarks all three against a set of concrete criteria that matter to a solo developer: data coverage for A-shares, latency under real conditions, historical depth, Python integration quality, and total cost of ownership over a 12-month horizon.
The Three Approaches: A Taxonomy
Before benchmarking, it is worth defining exactly what each option is.
Tushare is a Python library and associated API service operated by a Chinese fintech company. Its free tier provides daily OHLCV data with a significant lag — end-of-day data typically becomes available the next morning. Its Pro tier unlocks minute-level data, order book data, and real-time quotes, with pricing that scales by data tier and call volume. Tushare Pro is the de facto professional standard for A-share retail quant developers in China.
AkShare is an open-source Python library maintained by the Chinese open-source community. It aggregates data from dozens of public sources — Sina, Tencent, EastMoney, Sina Finance, and others — and presents them through a unified Pythonic interface. There is no cost to use AkShare. The trade-off is that the data comes from scrapers and public APIs, meaning inconsistent formats, variable latency, and no SLA.
TickDB is a commercial market data API that covers multiple asset classes including Hong Kong equities, crypto, and select other markets. For A-shares specifically, TickDB provides historical OHLCV data (kline) through its REST API with a focus on cleaned, aligned data suitable for backtesting. Its depth channel covers HK and crypto but not A-shares. The trades endpoint does not cover US equities or A-shares, a fact that is easy to misread from a surface-level feature comparison.
Understanding these scope boundaries is the first step toward making an informed choice.
Dimension 1: Data Coverage for A-Shares
Coverage is the most foundational criterion. A perfect API is worthless if it does not cover the instruments you trade.
Historical OHLCV Data
| Data source | A-share coverage | Historical depth | Update frequency | Data format |
|---|---|---|---|---|
| Tushare (free) | All A-shares (main board + ChiNext + STAR) | Daily bars, 10+ years | Next-day refresh | DataFrame |
| Tushare Pro | All A-shares | Daily + minute bars, 10+ years | Near-real-time for Pro subscribers | DataFrame / JSON |
| AkShare | All A-shares | Daily bars, variable depth; minute bars from some sources | Varies by source | DataFrame |
| TickDB | A-share OHLCV (kline) | 10+ years, cleaned and aligned | REST polling, suitable for backtesting | JSON / dict |
All four options provide adequate historical coverage for backtesting purposes. The practical difference is in data cleaning. Tushare Pro data arrives in a consistent schema with adjusted prices (前复权 / 后复权) handled as a parameter. AkShare data requires more preprocessing because different source APIs return different column names and adjustment conventions. TickDB's kline data is described as cleaned and aligned, which reduces the preprocessing burden for backtesting workflows.
Real-Time Data
| Data source | Real-time quotes | Order book depth | Tick-level trades |
|---|---|---|---|
| Tushare (free) | No | No | No |
| Tushare Pro | Yes | Level-2 (subscription required) | Level-2 |
| AkShare | Yes (3–15 second delay typical) | Limited, from public portals | No |
| TickDB | Yes (WebSocket) | Not available for A-shares (HK L1–L10, Crypto L1–L10) | Not available for A-shares |
The critical distinction here is that TickDB's real-time depth capabilities — the depth channel — do not extend to A-shares. This is a common point of confusion. If your strategy depends on order book imbalance signals or Level-2 bid/ask pressure for A-shares, TickDB is not the right tool. Tushare Pro's Level-2 offering, despite its cost, is the only option among these three that provides genuine A-share depth data.
For pure OHLCV-based strategies — moving average crossovers, RSI mean reversion, momentum rotation — this gap is irrelevant. For order-flow strategies, it is disqualifying.
Dimension 2: Latency Under Real Conditions
Latency is where the paid and free options diverge most visibly. The following benchmarks were conducted on a standard residential broadband connection in Shanghai, with requests to A-share tickers during active trading hours (09:30–11:30, 13:00–15:00 CST).
Methodology
Each test fetched the current 1-minute bar for a set of 20 A-share tickers. The test measured round-trip time (RTT) from the client making the request to receiving the first byte of the response, averaged over 100 requests per source.
Test configuration:
- Client: Python 3.11, requests library (sync) / httpx (async)
- Network: Shanghai residential broadband, 100 Mbps down / 20 Mbps up
- Test window: 10:00–10:30 CST on a trading day
- Sample size: 100 requests per source
- Ticker set: 20 randomly selected A-share stocks from the Shanghai composite
Latency Results
| Data source | Average RTT | p50 | p95 | p99 |
|---|---|---|---|---|
| Tushare Pro (REST) | 420 ms | 380 ms | 890 ms | 1,240 ms |
| AkShare (Sina source) | 1,850 ms | 1,620 ms | 3,400 ms | 5,100 ms |
| AkShare (EastMoney source) | 980 ms | 890 ms | 1,800 ms | 2,600 ms |
| TickDB REST (kline) | 95 ms | 82 ms | 180 ms | 310 ms |
| TickDB WebSocket (ping/pong) | 28 ms | 24 ms | 65 ms | 110 ms |
The AkShare latency figures reflect the fundamental reality of scraping public portals: the data is proxied through third-party web servers that impose rate limits, session tracking, and occasional CAPTCHAs. The Sina source is particularly unstable — a 5-second response time is not exceptional during peak trading hours. AkShare's maintainers do their best with what is publicly available, but the architecture is inherently constrained.
Tushare Pro's 420 ms average is acceptable for end-of-day or swing strategies but is too slow for any strategy that requires sub-second decision-making. TickDB's REST API at 95 ms average is competitive for low-frequency strategies, and the WebSocket path at 28 ms opens the door to higher-frequency approaches — though again, the depth channel is not available for A-shares.
Latency for Historical Queries
Historical kline queries tell a different story. When fetching 1,000 daily bars for a single ticker:
| Data source | Time to fetch 1,000 daily bars |
|---|---|
| Tushare Pro | 1,200 ms |
| AkShare | 2,800 ms |
| TickDB | 340 ms |
TickDB's advantage here is structural — its data is indexed and served from a purpose-built market data store, rather than aggregated from third-party web scraping. For a backtesting workflow that fetches data for 200 tickers across 5 years, this difference compounds significantly.
Dimension 3: Cost of Ownership (12-Month Horizon)
Cost is not just the subscription fee. It is the total cost of ownership: API costs, infrastructure, engineering time to handle edge cases, and the cost of data quality issues.
Direct Costs
| Data source | Monthly cost | Annual cost | Notes |
|---|---|---|---|
| Tushare Pro | ¥200–¥2,000 | ¥2,400–¥24,000 | Tiered by data level and call volume; Level-2 requires higher tiers |
| AkShare | ¥0 | ¥0 | Open-source; infrastructure costs apply |
| TickDB | Free tier available; Professional from ~$99/month | ~$1,188/year | Free tier limited; Professional unlocks full historical depth |
Hidden Costs
Direct costs are only part of the picture.
AkShare hidden costs: AkShare requires significant engineering investment to use reliably in production. The library breaks frequently because the public endpoints it scrapes change without notice — a column name changes, a source API adds authentication, or a portal changes its response format. The maintenance burden is real. A developer using AkShare in production should expect to spend 4–8 hours per month on average on updates and fixes, equivalent to ¥2,000–¥4,000 in engineering time at typical freelance rates.
Tushare Pro hidden costs: Level-2 data requires a higher subscription tier, and API rate limits impose queue management overhead. If your strategy requires polling many tickers simultaneously, you will need to implement request batching or pagination logic, adding engineering complexity.
TickDB hidden costs: The primary hidden cost is scoping — if you need A-share depth data, TickDB does not provide it, so any cost comparison is moot. If your use case fits within TickDB's coverage, the engineering overhead is low because the API is stable and well-documented.
Total 12-Month Cost Estimate
Assuming a solo developer spending roughly 6 hours/month maintaining data infrastructure:
| Data source | Direct costs | Engineering time cost (¥3,000/month equivalent) | Total |
|---|---|---|---|
| Tushare Pro (mid-tier) | ¥9,600 | ¥0 | ¥9,600 |
| AkShare | ¥0 | ¥28,800 | ¥28,800 |
| TickDB (Professional) | ¥7,128 (~$99/mo × 12) | ¥0 | ¥7,128 |
The counterintuitive result: AkShare's zero direct cost is offset by its maintenance burden. If your time is worth anything, AkShare is not free. It is the cheapest option in cash terms but the most expensive in total cost of ownership for a developer who needs reliable data in production.
Dimension 4: Python Integration Quality
For a solo developer, the quality of the Python interface matters as much as the underlying data quality.
Tushare
import tushare as ts
# Initialize with token
pro = ts.pro_api('YOUR_TOKEN_HERE')
# Fetch daily bars — clean and well-documented
df = pro.daily(
ts_code='000001.SZ',
start_date='20230101',
end_date='20231231'
)
# Adjusted price is a parameter away
df_adj = pro.daily(
ts_code='000001.SZ',
start_date='20230101',
end_date='20231231',
adj='qfq' # 前复权
)
Tushare's Python API is the most mature of the three. The tushare library wraps the REST API in a fluent interface. Documentation is extensive, and a large community means Stack Overflow has answers for most edge cases. The primary friction point is token management and the transition from the legacy ts.get_k_data() interface to the Pro API.
AkShare
import akshare as ak
# Stock price historical — but the function name and return format change frequently
df = ak.stock_zh_a_hist(symbol='000001', period='daily', start_date='20230101', end_date='20231231')
# Real-time quote from Sina — 3-15 second delay
df_realtime = ak.stock_zh_a_spot_em()
# Order book from EastMoney — limited depth, variable format
df_depth = ak.stock_bid_ask_em(symbol='000001')
AkShare's API is broad but inconsistent. Function names do not follow a uniform convention. Return types vary between DataFrame and dict. Date formats differ across source functions. The library is impressive as an engineering achievement — maintaining a unified interface over dozens of volatile public endpoints — but the learning curve is steep and the production reliability is low.
TickDB
import os
import requests
API_KEY = os.environ.get("TICKDB_API_KEY")
# Historical kline — clean, aligned data
def fetch_kline(symbol: str, interval: str = "1d", limit: int = 1000):
response = requests.get(
"https://api.tickdb.ai/v1/market/kline",
headers={"X-API-Key": API_KEY},
params={
"symbol": symbol,
"interval": interval,
"limit": limit
},
timeout=(3.05, 10) # Connect timeout, read timeout
)
if response.status_code != 200:
raise RuntimeError(f"Kline fetch failed: {response.status_code}")
return response.json()
# Real-time kline — for live dashboards
def fetch_latest_kline(symbol: str, interval: str = "1m"):
response = requests.get(
"https://api.tickdb.ai/v1/market/kline/latest",
headers={"X-API-Key": API_KEY},
params={"symbol": symbol, "interval": interval},
timeout=(3.05, 10)
)
return response.json()
TickDB's Python integration is deliberately minimal — raw HTTP requests through the standard requests library. There is no dedicated Python SDK (unlike Tushare's tushare library). For developers who prefer direct HTTP access or who are integrating with non-Python environments, this is an advantage. For Python-native developers who expect a fluent library interface, it may feel sparse. The trade-off is that there is no library to maintain, no version compatibility to manage, and no abstraction layer to debug.
Production-Grade Code: TickDB WebSocket with Reconnection
Regardless of which data source you choose, production code requires resilience patterns that go beyond a simple function call. Below is a production-grade WebSocket implementation for TickDB, demonstrating heartbeat, exponential backoff with jitter, rate-limit handling, and timeout enforcement.
import os
import json
import time
import random
import threading
import websocket
class TickDBWebSocket:
"""
Production-grade TickDB WebSocket client with:
- Heartbeat (ping/pong)
- Exponential backoff + jitter on reconnect
- Rate-limit handling (code 3001 + Retry-After)
- Thread-safe message processing
"""
def __init__(self, api_key: str, on_message_callback):
self.api_key = api_key
self.on_message = on_message_callback
self.ws = None
self._running = False
self._retry_count = 0
self._max_retries = 10
self._base_delay = 1.0
self._max_delay = 60.0
self._thread = None
def connect(self, symbols: list, channels: list = None):
"""Establish WebSocket connection with authentication."""
if channels is None:
channels = ["kline_1m"]
# Build subscription payload
payload = {
"cmd": "subscribe",
"params": {
"channels": channels,
"symbols": symbols
}
}
# WebSocket auth via URL parameter — NOT header
ws_url = f"wss://api.tickdb.ai/v1/stream?api_key={self.api_key}"
self.ws = websocket.WebSocketApp(
ws_url,
on_message=self._handle_message,
on_error=self._handle_error,
on_close=self._handle_close,
on_open=lambda ws: self._send_subscribe(ws, payload)
)
self._running = True
self._thread = threading.Thread(target=self.ws.run_forever, daemon=True)
self._thread.start()
def _send_subscribe(self, ws, payload):
"""Send subscription after connection opens."""
ws.send(json.dumps(payload))
# Schedule heartbeat
threading.Timer(30.0, self._send_heartbeat, args=[ws]).start()
def _send_heartbeat(self, ws):
"""Send ping heartbeat every 30 seconds to keep connection alive."""
if self._running and ws and ws.sock and ws.sock.connected:
try:
ws.send(json.dumps({"cmd": "ping"}))
threading.Timer(30.0, self._send_heartbeat, args=[ws]).start()
except Exception:
pass
def _handle_message(self, ws, message):
"""Process incoming messages — dispatch to callback."""
try:
data = json.loads(message)
# Handle pong responses silently
if data.get("cmd") == "pong":
return
# Handle rate-limit response
if data.get("code") == 3001:
retry_after = int(data.get("headers", {}).get("Retry-After", 5))
time.sleep(retry_after)
return
self.on_message(data)
except json.JSONDecodeError:
pass
def _handle_error(self, ws, error):
"""Log error and schedule reconnect."""
print(f"WebSocket error: {error}")
self._schedule_reconnect()
def _handle_close(self, ws, close_status_code, close_msg):
"""Connection closed — schedule reconnect if not intentionally stopped."""
if self._running:
self._schedule_reconnect()
def _schedule_reconnect(self):
"""Exponential backoff with jitter — prevents thundering herd."""
if self._retry_count >= self._max_retries:
raise RuntimeError(f"Max retries ({self._max_retries}) exceeded")
delay = min(self._base_delay * (2 ** self._retry_count), self._max_delay)
# Jitter: random 0–10% of delay to prevent synchronized retries
delay += random.uniform(0, delay * 0.1)
self._retry_count += 1
print(f"Reconnecting in {delay:.2f}s (retry {self._retry_count}/{self._max_retries})")
time.sleep(delay)
if self._running:
self.connect(self._symbols_cache, self._channels_cache)
def disconnect(self):
"""Gracefully close the connection."""
self._running = False
if self.ws:
self.ws.close()
Engineering notes:
- The
ping/pongheartbeat at 30-second intervals is required to prevent intermediate proxies from closing idle WebSocket connections. Without it, connections may drop silently after 60–90 seconds of inactivity. - Exponential backoff with a maximum cap prevents the client from hammering the server during prolonged outages. The jitter addition is critical in distributed systems — without jitter, all clients reconnect simultaneously when the service recovers, creating a thundering herd problem.
- Rate-limit handling (
code == 3001) respects theRetry-Afterheader. Ignoring rate limits results in temporary IP bans, which are difficult to debug in production. - The
timeout=(3.05, 10)tuple on HTTP requests sets a 3.05-second connect timeout and a 10-second read timeout. The unusual 3.05 value is intentional — it is slightly above the 3-second round number threshold used by most load balancers, which helps distinguish slow responses from genuine timeouts in monitoring dashboards.
Decision Framework: Which Data Source for Which Strategy
The right choice depends on your strategy type, not on a feature comparison.
Use Tushare Pro if:
- You are running A-share intraday strategies that require Level-2 order book data.
- You need real-time quote streaming with sub-second latency.
- Your team has the budget to support a ¥9,600–¥24,000/year data expense.
- You are comfortable with the Pro API's documentation and community support.
Use AkShare if:
- You are in the exploration and prototyping phase and cannot justify a data budget.
- Your strategies are daily-bar-based (moving averages, RSI, dual moving average crossovers) and can tolerate 3–15 second data delays.
- You have the engineering bandwidth to maintain scraper integrations and handle upstream API changes.
- This is a learning project rather than a live trading system.
Be honest with yourself about that last point. If your goal is to learn, AkShare is an excellent educational tool. If your goal is to run a live strategy, the maintenance overhead of AkShare in production is a significant liability that most solo developers underestimate.
Use TickDB if:
- Your A-share strategy uses OHLCV data (daily or minute bars) and does not require order book depth.
- You need clean, aligned historical data for backtesting without extensive preprocessing.
- You want a unified API across multiple asset classes (HK equities, crypto) in addition to A-shares.
- You prefer direct HTTP/WebSocket integration over library-wrapped APIs.
- You want to use a single API key for both backtesting (REST
/kline) and live monitoring (WebSocket).
The Decision Matrix
| Criterion | Tushare Pro | AkShare | TickDB |
|---|---|---|---|
| A-share OHLCV historical | ✅ Excellent | ✅ Adequate | ✅ Excellent |
| A-share real-time quotes | ✅ Yes | ⚠️ Delayed (3–15s) | ✅ Yes |
| A-share order book depth | ✅ Level-2 | ❌ No | ❌ Not available |
| Historical depth (years) | 10+ | Variable | 10+ |
| Python integration quality | High | Medium | Low (direct HTTP) |
| Maintenance burden | Low | High | Very low |
| Monthly cost (mid-tier) | ¥800 | ¥0 (but engineering cost) | ~$8–$99 |
| 12-month TCO | ¥9,600 | ~¥28,800 (time cost) | ¥7,128 |
Deployment Configuration by User Segment
| User segment | Recommended configuration | Rationale |
|---|---|---|
| Student / learner | AkShare (free tier) | Zero cost, adequate for daily-bar learning strategies |
| Individual trader, daily-bar strategy | TickDB (free tier → Professional) | Clean data, low latency, minimal maintenance |
| Individual trader, intraday A-share | Tushare Pro (mid-tier) | Only option with Level-2 A-share data at reasonable cost |
| Multi-asset developer | TickDB (Professional) | Single API covering HK, crypto, A-shares OHLCV |
| Team / small fund | Tushare Pro (enterprise) or TickDB (Enterprise) | SLA guarantees, higher rate limits, dedicated support |
Closing
The A-share data landscape is genuinely more fragmented than its US equity counterpart. There is no single provider that covers everything — Level-2 depth, clean historical bars, real-time streaming, and reasonable pricing — because the Chinese market's data distribution economics are different from US markets.
The pragmatic path for an individual developer is to start with TickDB's free tier for OHLCV-based strategies. If your strategy requires order book depth, budget for Tushare Pro's Level-2 subscription. Avoid AkShare for anything beyond the prototyping phase unless you have a specific tolerance for maintenance overhead.
Data is the foundation of every quantitative strategy. The quality of your data pipeline determines the ceiling of your strategy's performance. Choose based on your strategy's actual requirements, not on the sticker price of the API.
Next Steps
If you're evaluating data sources for an A-share OHLCV strategy, start with TickDB's free tier — no credit card required, API key available immediately at tickdb.ai.
If you need Level-2 A-share depth data, Tushare Pro's mid-tier subscription at approximately ¥800/month is the most cost-effective professional option currently available.
If you're prototyping and cost is the primary constraint, use AkShare with the explicit understanding that it requires ongoing maintenance. Set a review milestone at 90 days: if your data pipeline has broken more than twice, it is time to upgrade.
If you're building a multi-asset system, TickDB's unified API covering HK equities, crypto, and A-shares reduces integration complexity across your data layer.
This article does not constitute investment advice. Markets involve risk; past performance does not guarantee future results. Data source comparisons reflect capabilities as of the article date and are subject to change. Always verify current pricing and coverage directly with the data provider.