HK Stock Data Source Selection: One Misstep in Backtesting Makes It All Fake (Futu/IBKR/TickDB)

TickDB Research ·

A certain HK stock strategy showed stable performance in backtesting. After running live for a month, the returns were nearly half off.

It wasn't a complete mismatch—it was the kind of deviation that feels "not significant but persistent." The team spent three weeks investigating: strategy logic, slippage model, commissions, execution latency, all checked. Finally, they found the problem at the data layer: the data source returned HK stock code "700", while the team's database stored "00700". The data retrieved with the two formats partially overlapped and partially didn't. The sample used for backtesting was missing a batch of actually tradeable instruments.

This isn't an isolated operational mistake—it's a universal problem with HK stock data access.

Four pitfalls, one chain, your backtesting has been off from the very first step.

What Does This Chain Look Like

What you want: Full market coverage
        │
        ▼
┌───────────────────┐
│ Pitfall 1: Code Format │  Wrong code format → Sample missing a batch
└────────┬──────────┘
         │  Starting point is off
         ▼
┌───────────────────┐
│ Pitfall 2: Volume Unit │  Inconsistent units → Liquidity judgment reversed
└────────┬──────────┘
         │  Denominator is wrong
         ▼
┌───────────────────┐
│ Pitfall 3: Time Breakpoints │  Time periods not aligned → Phantom K-lines
└────────┬──────────┘
         │  Time is wrong
         ▼
┌───────────────────┐
│ Pitfall 4: Forward Adjustment │  Adjustment standards inconsistent → Signals contain future info
└────────┬──────────┘
         │  Signal is dirty
         ▼
    Backtest results unreliable

Each link's error becomes the input for the next. Pitfall 1 biased the sample. Pitfall 2 judged liquidity on the biased sample. Pitfall 3 performed time alignment on the wrong units. Pitfall 4 calculated adjustment signals on the wrong timeline. The four pitfalls aren't four independent small problems—they're a production line. Once the starting point is off, every subsequent step runs on a biased foundation.

This Article Answers Three Questions

  • What are the underlying mechanics of the four HK stock data pitfalls?
  • How do these pitfalls affect your backtesting and live trading?
  • How can you verify if your data source has stepped on these pitfalls?

This Article Is Written for Three Types of Readers

Reader TypeYour Core TaskWhat This Article Helps You Solve
Quant Team PM/Data Selection Decision MakerChoose data sources and manage risks for the teamHow significant are the risks of the four pitfalls, how to write them into procurement contracts
Investment Researcher/Strategy DeveloperRun backtests, build factors, develop strategiesWhy backtesting and live trading don't match, how to troubleshoot data layer definition issues
Financial Product Application TeamBuild market dashboards, data productsHow to assess whether a data source's field definitions are reliable, how to accept deliverables

Pitfall 1: HK Stock Codes Are Not Numbers, But Every Tool Wants to Treat Them as Numbers

The chain starts with which stocks you see.

Your backtest shows 30% annualized returns. Live trading lost 12%. You thought it was strategy overfitting. Upon review, you found Tencent's code (0700) became integer 700 in the CSV. Using "700" to query the interface, some data returned, some didn't—not all wrong, but partially right and partially wrong.

HK stock codes are the five-digit numeric security codes mandated by HKEX starting April 7, 2008. Tencent in HKEX's system is 00700, not 700. Excel converting it to integer 700 isn't "ugly formatting"—the code is no longer the identifier HKEX specified. Complete errors you'd notice immediately. Partial correctness makes you think you're working normally.

Even more insidious is code reuse: industry observers note that after delisting, HK stock codes may be reassigned to another company after 12 months. If your data source simply concatenates historical price data by code, delisted company's historical data might be incorrectly spliced onto the new company—resulting in a ghost of a delisted company "rising from the dead" in your backtest.

Verification Action: Read in the HK stock code list with Python, check if code length stays at 5 digits; if converted to integer, see if you can restore it with str(int_code).zfill(5). Storage solutions that fail both tests should be completely废弃 (discarded).

What's the cost: You reported to your boss "full market HK stock strategy", but your data foundation isn't complete. Those "disappeared" stocks, you never had a chance to see them. When the boss asks "Why did we miss that stock that tripled?", your answer is "the strategy didn't select it", not "the strategy never saw it". This distinction determines whether others question your strategy or your data.

The starting point is off, and every subsequent step runs on a biased foundation.

Pitfall 2: HK Stock Volume Is in "Lots", and Every Stock Has Different Shares Per Lot

The first link has already biased your sample. The second link determines whether you can correctly assess trading activity of these stocks.

Your strategy judged a certain HK stock "breakout with high volume", but after buying live you couldn't get in at all—daily volume was much smaller than you expected. Why could you buy the quantity in backtesting that you couldn't in live trading?

Tencent (0700) one lot = 100 shares. But other HK instruments have different shares per lot: 200, 500, even 1000 shares per lot. The volume field "1000 lots" represents completely different actual share counts for different instruments. HKEX is consulting on reform: simplifying over 40 types of lot sizes to 8. This means historical data contains over 40 different lot sizes—data sources must maintain a "historical lot size table" and cannot use today's values to recalculate volume from three years ago.

Even more insidious: lot size is time-varying. Companies may adjust shares per lot due to share consolidations or splits. The same instrument may have different lot sizes before and after a corporate action. Cross-market comparisons are even more dangerous: US stock volume units are shares, HK stock is lots—direct comparison equals comparing Guan Gong and Qin Qiong.

Verification Action: Take volume for the same instrument on the same day from two different interfaces and compare values. If they differ by an order of magnitude, check the units. Then do a conversion test: lots × shares per lot must result in an integer; if you get a decimal, something is wrong with one of the data sources.

What's the cost: Your strategy tells you a stock "broke out with high volume", you buy at backtest position size. In live trading, market capacity is only one-tenth of your calculated value. Your position management rules are built on a wrong denominator. This problem won't throw errors in backtesting—the numbers all look reasonable. When you go live, your buy order only fills a small portion, costs far exceeding expectations.

Wrong units, and all your judgments about these stocks are built on a wrong denominator.

Pitfall 3: HK Stock Time Series Have Three Breakpoints—Continuous Processing Creates Phantom K-lines

The sample is biased, the units are wrong—the third link determines when your strategy makes decisions.

Your intraday strategy "caught a trading opportunity" every noon in backtesting, but nothing happened at noon in live trading. Why were there trading signals at noon in backtesting but not live?

HK stocks have a lunch break from 12:00-13:00 daily, several half-day trading sessions throughout the year (Christmas Eve, New Year's Eve, Chinese New Year's Eve), plus pre-open and closing auction phases. If you treat HK stock time series as continuous, you'll get "phantom K-lines" during the lunch gap—strategy triggers signals in a period when no trading exists.

Before September 23, 2024, Typhoon No. 8 or Black Rainstorm Signal could result in full-day suspension or half-day trading; after that, HKEX implemented SWT, adverse weather trading continues. If the data source calendar doesn't distinguish this boundary, backtests will show false missing data or false trading days.

Verification Action: Pull HK stock minute K-lines for a complete day. Check: whether there are K-lines returned between 12:00-13:00; whether the K-line timestamp is the interval start or end; whether the first K-line timestamp of the next trading day is continuous.

What's the cost: Your intraday strategy "caught an opportunity" every noon in backtesting. In live trading, nothing happened at noon. You thought the strategy failed—actually the market wasn't even open during that time. Your risk system treated the lunch break as a trading halt, triggering an alarm every noon. You spent two weeks troubleshooting system bugs, only to discover the data source didn't clearly mark it—the market was resting, not the system malfunctioning.

Time is wrong, your strategy makes decisions in non-existent time periods.

Pitfall 4: HK Stock Adjustment Is "Looking at the Future"—The Hidden Trap of Forward Adjustment

All three previous links are wrong, so the signals won't be clean either. The fourth link determines whether your signals are peeking at the answers.

Your strategy precisely bought a HK stock before its dividend in backtesting, but always bought at peaks in live trading. Why could you "precisely bottom fish" in backtesting but not live?

After HK stock ex-dividend events, historical prices are adjusted (split-adjusted). Forward adjustment adjusts historical prices to "how they look now"—but "how they look now" is calculated using ex-dividend factors you only know today. Suppose a HK stock had a HKD 0.5 per share dividend during your backtest period. The forward adjustment factor would downward-adjust all historical prices before this dividend. Your strategy sees "lower historical prices", judges "that was a buying opportunity"—but at that point in time, the dividend hadn't happened yet, that price didn't exist.

Different data sources use different adjustment algorithms. Developers have reported approximately 0.1% errors between Wind and East Money's backward adjustment percentage changes. 0.1% seems small on a single day, but could significantly impact net value curves over years of compounding.

Verification Action: Pull daily K-lines for the same instrument over the same historical period three times in a row, specifying adjust=none. Check whether the raw prices returned are exactly identical across all three pulls. Then pull forward-adjusted data and check whether forward-adjusted prices are consistent. If forward-adjusted prices are inconsistent, the factors are changing—your backtest is not reproducible.

What's the cost: Your strategy "precisely bottom fished" a dividend-paying stock before the dividend in backtesting, but always bought at peaks in live trading. You thought it was execution delay—actually you used ex-dividend factors you only know today to "peek" at future answers in the backtest. Part of your annualized returns only exists in the backtest report. When showing这部分收益 to investors, you can't explain why live tracking doesn't match.

The signal is dirty—part of the returns in backtesting don't actually exist.

Three-Provider横向比较: Capability Boundaries and Suitable Scenarios

All three can give you HK stock data. Choosing wrong doesn't cost you extra service fees—it costs your strategy making decisions based on data with the four problems above.

Why these six dimensions: Symbol normalization determines whether the sample is biased, volume definitions determine whether liquidity judgments are credible, time consistency determines whether signals trigger at the correct times, adjustment auditability determines whether backtests are reproducible, historical data coverage determines sample survivorship bias, and production reliability determines post-launch stability.

Evaluation DimensionWhy This Dimension MattersTickDB (Tested)Futu OpenAPIIBKR
Symbol NormalizationWrong code format → Sample biased700.HK standard; 0700.HK tested compatible but undocumentedSDK handles, format per developer docsconid identification, requires additional contract queries
Volume DefinitionWrong unit → Liquidity judgment reversedVolume tested consistent with Yahoo Finance shares; unit not explicitly documentedPer official docs, confirm before integrationPer official docs, confirm before integration
Time ConsistencyWrong period → Phantom K-linesNo K-lines from 12:00-12:59:59; half-day closes at 12:00Lunch break/half-day handling needs verificationLunch break/half-day handling needs verification
Adjustment AuditabilityWrong adjustment → Signals contain future infonone/forward/backward tested effectiveDefault AuType.QFQ, requires explicit NONETRADES/MIDPOINT/BID_ASK need differentiation
Historical Data CoverageDelisted stocks missing → Sample survivorship biasDelisted stock test samples not passed, need per-instrument confirmationMinutes ≈ 8 years/Daily ≈ 20 years (need per-instrument verification)Under 30 seconds ≤ 6 months; delisted stocks not accessible
Production ReliabilitySilent failures → Not reproducibleREST snapshots/WebSocket streams need distinctionQuote mutual exclusion limits (one OpenD maximum permissions)TWS session management required

TickDB: Best suited for teams whose primary needs are market data ingestion, large-scale subscriptions, and data service integration. Testing covered lunch breaks, half-days, adjustment parameters, and symbol compatibility. Note: volume unit not explicitly documented, delisted stock coverage needs per-instrument confirmation.

Futu OpenAPI: Most friendly for small teams with existing Futu accounts wanting to quickly verify HK stock dashboards or research tools. Note: Market data visible in the app may not have equivalent API permissions; quote mutual exclusion limits exist.

IBKR: Best suited for institutions already using IBKR for cross-market algorithmic trading needing HK stocks and other markets on one unified workflow. Note: K-lines under 30 seconds only go back 6 months; delisted stocks not accessible. For teams doing multi-year full-market backtests, this alone may disqualify it as a sole data source.

TickDB test data in the table comes from Codex verification report (2026-10-08); Futu and IBKR adjustment parameters and historical data limitations come from respective official docs (access date 2026-10-08); items not explicitly specified like volume units and lunch break handling are marked as "per official docs" or "need per-instrument confirmation".

What We Verified with Code (Take It and Use It)

The TickDB test data in the table above comes from the following runnable verification code. You can copy this directly to your local environment, swap in your own API Key, and run it.

import requests

API_KEY = "your_api_key"
BASE_URL = "https://api.tickdb.ai/v1"
headers = {"X-API-Key": API_KEY}

# Verification 1: Code format—whether 700.HK and 0700.HK both match
print("=== Verification 1: Code Format Compatibility ===")
for symbol in ["700.HK", "0700.HK"]:
    resp = requests.get(
        f"{BASE_URL}/market/ticker",
        params={"symbols": symbol},
        headers=headers
    )
    data = resp.json()
    print(f"{symbol}: code={data.get('code')}, symbol={data.get('data', {}).get('symbol')}")

# Verification 2: Adjustment parameters—whether none / forward / backward are effective
print("\n=== Verification 2: Adjustment Parameters ===")
for adjust in ["none", "forward", "backward", "qfq", "hfq"]:
    resp = requests.get(
        f"{BASE_URL}/market/kline",
        params={"symbol": "700.HK", "interval": "1d", "adjust": adjust, "limit": 1},
        headers=headers
    )
    data = resp.json()
    print(f"adjust={adjust}: code={data.get('code')}, adjust={data.get('data', {}).get('adjust')}")

# Verification 3: Lunch break—whether there are K-lines from 12:00-12:59:59
print("\n=== Verification 3: Lunch Break ===")
resp = requests.get(
    f"{BASE_URL}/market/kline",
    params={
        "symbol": "700.HK",
        "interval": "1m",
        "adjust": "none",
        "limit": 1000,
        "start_time": 1791336600000,  # 2026-10-07 09:30 HKT
        "end_time": 1791360000000,    # 2026-10-07 16:00 HKT
    },
    headers=headers
)
klines = resp.json()["data"]["klines"]
noon_klines = [k for k in klines if "12:00:00" <= k["time_hkt"][11:19] <= "12:59:59"]
print(f"Total K-lines: {len(klines)}")
print(f"Lunch break K-lines: {len(noon_klines)}")

# Verification 4: Half-day—how many K-lines for 2025-12-24
print("\n=== Verification 4: Half-Day Trading ===")
resp = requests.get(
    f"{BASE_URL}/market/kline",
    params={
        "symbol": "700.HK",
        "interval": "1m",
        "adjust": "none",
        "limit": 1000,
        "start_time": 1766539800000,  # 2025-12-24 09:30 HKT
        "end_time": 1766563200000,    # 2025-12-24 16:00 HKT
    },
    headers=headers
)
klines = resp.json()["data"]["klines"]
print(f"Half-day K-lines: {len(klines)}")
if klines:
    print(f"First: {klines[0]['time_hkt']}")
    print(f"Last: {klines[-1]['time_hkt']}")

# Verification 5: Historical reproducibility—whether two queries return the same result
print("\n=== Verification 5: Historical Reproducibility ===")
params = {"symbol": "700.HK", "interval": "1d", "adjust": "none", "limit": 10}
resp1 = requests.get(f"{BASE_URL}/market/kline", params=params, headers=headers)
resp2 = requests.get(f"{BASE_URL}/market/kline", params=params, headers=headers)
print(f"Two query results identical: {resp1.json() == resp2.json()}")

Test Results Summary (Codex, 2026-10-08):

Verification ItemTest Result
700.HK and 0700.HKBoth return 200, data identical except echoed symbol
adjust=none/forward/backwardAll return 200
adjust=qfq/hfqReturns 400, error code 2001
Lunch break 12:00-12:59:59K-line count is 0
Half-day 2025-12-24Returns 151 K-lines, 09:30-12:00, no K-lines after 12:00
Two query resultsFully consistent

Note: TickDB's REST interface is on-demand snapshots, not real-time streaming; if you need tick-by-tick real-time updates, use WebSocket subscriptions instead. Fields like bid_price, ask_price, spread come from the depth interface, not the ticker interface.

Acceptance Checklist (Send Directly to Vendors)

Fill out this table and send it to your data source vendor—include the answers in your procurement or integration documentation. Clauses you can't clarify are to be treated as unconfirmed and cannot enter production.

Acceptance DimensionQuestions You Should Ask VendorsCorresponding Pitfall
Code FormatDoes symbol format support HK stock codes with leading zeros? Which matches—700.HK or 0700.HK?Pitfall 1
Volume DefinitionIs the volume field unit lots or shares? Where do we get shares per lot?Pitfall 2
Time SeriesAre K-line data returned for the lunch break period (12:00-13:00)? How are half-days marked?Pitfall 3
Adjustment ControlDoes it default to adjusted or raw prices? Can adjustment method be explicitly specified?Pitfall 4
ReproducibilityDo two calls for the same historical period return exactly identical results?All
Delisted Stock CoverageFor delisted stocks within the historical backtest range, is historical data accessible?All

The value of this checklist isn't "what to ask"—it's "whether you dare to ask." If you send these questions to a vendor and they answer "these aren't important", then you already have your answer.

FAQ

Q1: How do I choose a HK stock data source?

A: The core is confirming agreements on the four pitfalls: code format, volume unit, time breakpoints, adjustment method. Until these four are confirmed, backtesting and live trading mismatch is just a matter of time.

Q2: Are HK stock codes four or five digits?

A: HKEX adopted five-digit numeric security codes starting April 7, 2008. Tencent in HKEX's system is 00700. If your data source returns "700", you need to confirm whether it's normalized to a five-digit string.

Q3: Is HK stock volume in lots or shares?

A: Depends on the data source. HKEX specifies "lot" as the minimum trading unit, with shares per lot varying by instrument. Before integration, you must confirm: is the volume field your data source returns in lots or shares? Use price × volume = turnover as a checksum.

Q4: How should I handle HK stock lunch breaks?

A: Lunch break is 12:00-13:00, during which there are no K-lines. If your data source returns K-lines for the lunch period, you need to investigate. Half-day trading has no afternoon K-lines—don't fill gaps with interpolation.

Q5: What problems does forward adjustment have in backtesting?

A: Forward adjustment uses ex-dividend factors only known today to adjust historical prices, constituting look-ahead bias. For signal generation, use backward adjustment or raw prices; for simulating fills, use raw prices. Different data sources use different adjustment algorithms—when reconciling across sources, you must agree on identical definitions.

Q6: Should delisted stock data be covered?

A: For multi-year full-market backtests, delisted stock coverage directly impacts sample survivorship bias. IBKR official docs explicitly state delisted stocks not accessible; TickDB testing shows Haitong International 665.HK returns symbol not found—need per-instrument confirmation. Write delisted stock coverage into the acceptance checklist during procurement.

What Is TickDB and What Advantages Does It Have in Data Source Selection

All these verifications above, I ran through TickDB's interface.

TickDB provides unified global market data APIs for developers and AI applications, allowing teams to access multi-market real-time and historical data through a single integration, faster building market data, analysis, and monitoring products.

TickDB covers A-shares, HK stocks, US stocks, futures, forex, crypto, and precious metals, with free tier available.

In the data source selection scenario, TickDB has three verifiable advantages:

AdvantageSpecific Performance
Multi-market UnifiedOne API covers all markets above, instrument directory is queryable and classifiable
Deterministic Field StructureBoundary states like suspension and auction have explicit field behavior—not "data appears to exist"
Transparent Adjustment LogicAdjustment factors have independent interfaces, formulas verifiable—not dependent on data source black box

If you're using an MCP-enabled tool (like Cursor, Claude Code), you can also have the Agent directly call TickDB's market data tools:

# After configuring TickDB's MCP service in an MCP-capable AI tool
# You can directly call market data tools with natural language, for example:

# "Help me pull AAPL.US last 15 1-minute K-lines"
# Agent will call get_kline tool, return structured data with timestamps and OHLCV

# "Check 700.HK current order book depth"
# Agent will call get_order_book tool, return bid/ask levels

# "600519.SH capital flow for last 5 trading days"
# Agent will call get_capital_flow tool, return capital flow data

The value of MCP tools lies in: letting the model first get facts with instrument, field, and timestamp, then analyze—instead of having the model use training data historical prices to answer "how much is it now".

Closing

Does your data source dare let you ask: "How did this number become what it is now"?

No code needed. Start with the smallest thing: add a "data source definition confirmation" clause to your procurement contract or product documentation. Confirm four things—code format is how many-digit string, volume unit is lots or shares, how lunch breaks are marked, which adjustment method is the default.

I now treat "connection successful" as the start of integration testing. Whether each data segment can be explained—how to identify, how to reconcile, how to reproduce discrepancies—is the acceptance answer for a pricing source to enter the next phase.

Comments: Which pitfall has your team encountered when integrating HK stock data? Or is there a fifth pitfall?

References

  1. Hong Kong Exchanges and Clearing. Announcement on Five-Digit Numeric Security Codes. 2008-04-07.
  2. Hong Kong Exchanges and Clearing. Consultation Paper on Standardization of Board Lot Sizes. 2026.
  3. Hong Kong Exchanges and Clearing. Trading Hours and Adverse Weather Arrangements. Access date: 2026-10-08.
  4. China Securities Depository and Clearing. Half-Day Settlement Arrangements. Access date: 2026-10-08.
  5. Futu OpenAPI. Historical K-line Interface Documentation. Access date: 2026-10-08.
  6. Interactive Brokers. Historical Data Limitations. Access date: 2026-10-08.
  7. TickDB. REST API Official Documentation and Test Data. Access date: 2026-10-08.
  8. Codex. TickDB HK Stock Interface Verification Report. 2026-10-08.
  9. Codex. TickDB HK Stock Interface Supplementary Test Report (r2). 2026-10-08.
  10. Quant Community Discussion. Wind and East Money Backward Adjustment Percentage Error. Access date: 2026-10-08.

Related articles