"Shanghai's East Yan'an Road branch liquidated its entire position in [-0.03 seconds]."
That timestamp matters. On March 14, 2024, the China Securities Journal published the daily Dragon Tiger List — the official record of A-share securities firms whose proprietary trading desks exceeded statutory thresholds. By 9:15 AM, when pre-market futures were still pricing in a 0.2% gap, two algorithmic desks had already detected the pattern and adjusted their short-volatility positions accordingly.
The Dragon Tiger List (龙虎榜) is China's mandatory disclosure mechanism for unusually large institutional trades. Every trading day at 4:00 PM Beijing time, the Shanghai and Shenzhen exchanges publish the top five buying and selling broker seats for stocks that crossed specific volume or price-change thresholds. For quantitative researchers, this dataset represents the most granular publicly available signal on hot money (游资) behavior — the speculative capital that moves in and out of A-share momentum plays with precision timing.
This article examines whether the Dragon Tiger List can be used as a systematic trading signal. We will cover data acquisition via TickDB, implement a production-grade scraping pipeline, design a seat-tracking event strategy, and backtest it against a three-year dataset.
Understanding the Dragon Tiger List: Data Structure and Disclosure Rules
1.1 What Qualifies for Disclosure
A stock appears on the Dragon Tiger List when it satisfies any of the following conditions during a single trading session:
| Condition | Threshold | Exchange |
|---|---|---|
| Daily price change | ≥ ±7% | Both |
| Daily turnover rate | ≥ 20% | Both |
| Cumulative price change | ≥ ±20% in 3 sessions | Both |
| Abnormal price movement flagged by exchange | Case-by-case | Both |
| Special treatment (ST) stocks | ≥ 5% price change | SZSE |
The disclosure lists the top 5 buying broker seats and top 5 selling broker seats by transaction volume. Each seat is identified by its broker branch name and location (e.g., "Guotai Junan Securities Shanghai East Yan'an Road Branch"). The disclosed data includes:
- Total buy amount (in CNY)
- Total sell amount (in CNY)
- Net position change per seat
1.2 The Seat Identity Problem
The primary analytical challenge is that broker branch names do not directly map to institutional entities. A single broker may have 50 branches across China, and the same branch may handle flows from multiple distinct trading desks — some algorithmic, some manual. Some seats are known to belong to retail-focused online brokerages (万得财富, 东方财富); others belong to large private equity families.
Research firms maintain proprietary seat databases that track which branch codes correspond to known funds. For this article, we use TickDB's annotated seat metadata, which provides a confidence classification for the most liquid A-share names.
Data Acquisition: Building a Production-Grade Pipeline
2.1 TickDB Dragon Tiger List Endpoint
TickDB provides the Dragon Tiger List via the /v1/DragonTigerList REST endpoint. The endpoint returns daily disclosure data for all qualifying stocks, including seat-level amounts and net flow direction.
Endpoint specification:
GET https://api.tickdb.ai/v1/DragonTigerList
Required parameters:
| Parameter | Type | Description |
|---|---|---|
date |
string (YYYYMMDD) | Trading date |
symbol |
string (optional) | Filter by specific stock; omit for full list |
Response structure (abbreviated):
{
"code": 0,
"data": [
{
"symbol": "600519.SH",
"name": "贵州茅台",
"date": "20240314",
"close_price": 1685.00,
"change_pct": 5.23,
"turnover_rate": 0.42,
"seats": [
{
"broker": "华泰证券",
"branch": "上海浦电路营业部",
"buy_amount": 45230000.00,
"sell_amount": 12800000.00,
"net_flow": 32430000.00,
"seat_type": "hot_money",
"confidence": 0.85
}
]
}
]
}
2.2 Production-Grade Code: Daily Pipeline
The following Python module implements a resilient daily ingestion pipeline with automatic reconnection, rate-limit handling, and idempotent writes to a local SQLite database.
import os
import time
import json
import sqlite3
import logging
from datetime import datetime, timedelta
from pathlib import Path
import requests
# ── Configuration ──────────────────────────────────────────────────────────
TICKDB_API_KEY = os.environ.get("TICKDB_API_KEY")
if not TICKDB_API_KEY:
raise RuntimeError("TICKDB_API_KEY environment variable is not set")
BASE_URL = "https://api.tickdb.ai/v1/DragonTigerList"
HEADERS = {"X-API-Key": TICKDB_API_KEY}
# ── Logging ─────────────────────────────────────────────────────────────────
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
# ── Database setup ───────────────────────────────────────────────────────────
DB_PATH = Path(__file__).parent / "dragon_tiger.db"
def init_db():
"""Initialize SQLite schema for Dragon Tiger List storage."""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS daily_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trade_date TEXT NOT NULL,
symbol TEXT NOT NULL,
name TEXT NOT NULL,
close_price REAL,
change_pct REAL,
turnover_rate REAL,
seat_count INTEGER,
total_buy REAL,
total_sell REAL,
net_flow REAL,
collected_at TEXT NOT NULL,
UNIQUE(trade_date, symbol)
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS seat_details (
id INTEGER PRIMARY KEY AUTOINCREMENT,
record_id INTEGER NOT NULL,
broker TEXT NOT NULL,
branch TEXT NOT NULL,
buy_amount REAL,
sell_amount REAL,
net_flow REAL,
seat_type TEXT,
confidence REAL,
FOREIGN KEY (record_id) REFERENCES daily_records(id)
)
""")
cursor.execute("""
CREATE INDEX IF NOT EXISTS idx_symbol_date
ON daily_records(symbol, trade_date)
""")
conn.commit()
return conn
def _request_with_backoff(url: str, params: dict, max_retries: int = 5) -> dict:
"""
Fetch data from TickDB with exponential backoff and jitter.
Handles rate-limit (code 3001) by reading the Retry-After header.
"""
base_delay = 1.0
max_delay = 60.0
for attempt in range(max_retries):
try:
response = requests.get(
url,
headers=HEADERS,
params=params,
timeout=(3.05, 15) # Connect timeout, read timeout
)
response.raise_for_status()
data = response.json()
code = data.get("code", -1)
if code == 0:
return data.get("data", [])
elif code == 3001:
retry_after = int(response.headers.get("Retry-After", 5))
logger.warning(
f"Rate limited (attempt {attempt + 1}/{max_retries}). "
f"Retrying after {retry_after}s."
)
time.sleep(retry_after)
continue
elif code in (1001, 1002):
raise ValueError(
"Invalid API key — verify TICKDB_API_KEY environment variable"
)
elif code == 2002:
logger.info(f"No Dragon Tiger data for {params.get('date')}")
return []
else:
raise RuntimeError(
f"Unexpected error code {code}: {data.get('message')}"
)
except requests.exceptions.Timeout:
logger.warning(
f"Request timeout (attempt {attempt + 1}/{max_retries}). Retrying..."
)
except requests.exceptions.RequestException as e:
logger.warning(f"Request failed: {e}")
# Exponential backoff with jitter
if attempt < max_retries - 1:
delay = min(base_delay * (2 ** attempt), max_delay)
jitter = __import__("random").uniform(0, delay * 0.1)
time.sleep(delay + jitter)
raise RuntimeError(f"Failed after {max_retries} attempts")
def fetch_daily_list(trade_date: str) -> list:
"""
Fetch Dragon Tiger List for a specific trading date.
trade_date format: YYYYMMDD
"""
params = {"date": trade_date}
logger.info(f"Fetching Dragon Tiger List for {trade_date}")
return _request_with_backoff(BASE_URL, params)
def persist_records(conn: sqlite3.Connection, records: list, collected_at: str):
"""Insert or replace Dragon Tiger records into the database."""
cursor = conn.cursor()
inserted = 0
for record in records:
symbol = record.get("symbol")
trade_date = record.get("date")
seats = record.get("seats", [])
total_buy = sum(s.get("buy_amount", 0) for s in seats)
total_sell = sum(s.get("sell_amount", 0) for s in seats)
# Upsert daily record
cursor.execute("""
INSERT OR REPLACE INTO daily_records
(trade_date, symbol, name, close_price, change_pct, turnover_rate,
seat_count, total_buy, total_sell, net_flow, collected_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
trade_date,
symbol,
record.get("name"),
record.get("close_price"),
record.get("change_pct"),
record.get("turnover_rate"),
len(seats),
total_buy,
total_sell,
total_buy - total_sell,
collected_at
))
record_id = cursor.lastrowid
# Insert seat details
for seat in seats:
cursor.execute("""
INSERT INTO seat_details
(record_id, broker, branch, buy_amount, sell_amount,
net_flow, seat_type, confidence)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", (
record_id,
seat.get("broker"),
seat.get("branch"),
seat.get("buy_amount"),
seat.get("sell_amount"),
seat.get("net_flow"),
seat.get("seat_type"),
seat.get("confidence")
))
inserted += 1
conn.commit()
logger.info(f"Persisted {inserted} records with seat details")
return inserted
def run_daily_pipeline(trade_date: str):
"""Main orchestration: fetch, persist, log."""
conn = init_db()
collected_at = datetime.now().isoformat()
try:
records = fetch_daily_list(trade_date)
if records:
persist_records(conn, records, collected_at)
logger.info(f"Pipeline completed: {trade_date}")
else:
logger.info(f"No records to persist for {trade_date}")
finally:
conn.close()
# ── CLI entry point ──────────────────────────────────────────────────────────
if __name__ == "__main__":
import sys
if len(sys.argv) == 2:
target_date = sys.argv[1]
else:
# Default to previous trading day (skip weekends)
target = datetime.now() - timedelta(days=1)
while target.weekday() >= 5:
target -= timedelta(days=1)
target_date = target.strftime("%Y%m%d")
run_daily_pipeline(target_date)
Engineering notes:
- The
_request_with_backofffunction implements exponential backoff with jitter to prevent thundering-herd behavior on rate-limited endpoints. timeout=(3.05, 15)sets a 3.05-second connection timeout and a 15-second read timeout. The 0.05-second padding prevents TCP's natural RTO rounding from triggering a timeout prematurely.- The
INSERT OR REPLACEidiom ensures idempotent writes — re-running the pipeline for the same date does not create duplicates.
2.3 Backfill Strategy
For historical backtesting, a batch backfill function loops over a date range with a 0.3-second inter-request delay to respect server-side rate limits:
def backfill_range(start_date: str, end_date: str, inter_request_delay: float = 0.3):
"""
Backfill Dragon Tiger List for a date range.
start_date / end_date: YYYYMMDD strings
"""
start = datetime.strptime(start_date, "%Y%m%d")
end = datetime.strptime(end_date, "%Y%m%d")
current = start
total_fetched = 0
while current <= end:
# Skip weekends
if current.weekday() < 5:
date_str = current.strftime("%Y%m%d")
try:
records = fetch_daily_list(date_str)
if records:
conn = init_db()
persist_records(conn, records, datetime.now().isoformat())
conn.close()
total_fetched += len(records)
except Exception as e:
logger.error(f"Failed on {date_str}: {e}")
time.sleep(inter_request_delay)
current += timedelta(days=1)
logger.info(f"Backfill complete. Total records: {total_fetched}")
A three-year backfill (2021-01-01 to 2024-03-14) contains approximately 2,400 trading days of data, with an average of 85 stocks per day appearing on the list — roughly 200,000 individual seat-flow observations.
Strategy Logic: Hot Money Seat Tracking
3.1 The Core Hypothesis
Hot money (游资) in A-shares refers to speculative capital that deliberately targets stocks with specific technical or event-driven catalysts. These funds operate with high conviction, short holding periods (typically 1–5 trading sessions), and precise entry/exit timing.
The Dragon Tiger List provides a daily snapshot of which broker seats are net buyers versus net sellers for each disclosed stock. The strategy hypothesis is:
When a known hot money seat appears as a net buyer in a stock with ≥7% intraday gain, there is a measurable probability of a second-day continuation — particularly if the net flow exceeds a threshold relative to average daily volume.
3.2 Seat Classification
Not all seats are equal. TickDB annotates seats into three tiers:
| Seat type | Description | Reliability for tracking |
|---|---|---|
hot_money |
Known speculative desks with short-hold patterns | High |
institutional |
Long-only funds, pension managers | Low for momentum signals |
retail_aggregated |
Online brokerage branches aggregating retail orders | Low signal-to-noise |
unknown |
Unclassified or low-confidence mapping | Exclude |
For seat-tracking purposes, we filter only on hot_money seats with confidence >= 0.75.
3.3 Event Definition
A qualifying signal (signal event) is triggered when:
- Stock appears on the Dragon Tiger List for day
t - At least one
hot_moneyseat has net flow ≥ CNY 20 million - Net flow / average daily turnover (ADT, 20-day rolling) ≥ 0.15
- Day
tprice change ≥ +5% (ensuring momentum alignment)
Signal window:
- Entry: Open of day
t+1(next trading day) - Exit: Close of day
t+3(hold for up to 3 days) or stop-loss at −4% intraday
3.4 Derived Metrics
| Metric | Formula | Signal threshold |
|---|---|---|
| Hot money pressure ratio (HMPR) | Net hot money flow / (Total buy + Total sell) | ≥ 0.25 |
| Flow intensity | Net hot money flow / 20-day ADT | ≥ 0.15 |
| Seat concentration | Top seat net flow / Total net flow | ≥ 0.50 |
Backtest Methodology and Results
4.1 Dataset and Assumptions
| Parameter | Value |
|---|---|
| Backtest period | 2021-01-04 to 2024-03-14 |
| Universe | All A-share stocks on Dragon Tiger List (filtered by seat type) |
| Entry price | Open of next trading day |
| Exit price | Close of day 3 after signal, or stop-loss |
| Slippage | 0.05% per side |
| Commission | 0.03% per side (A-share standard) |
| Position sizing | Equal weight, max 5% per position |
| Max concurrent positions | 10 |
4.2 Performance Summary
The following table summarizes strategy performance across the full backtest period:
| Metric | Full period | 2021 (volatile) | 2022 (bear) | 2023–2024 |
|---|---|---|---|---|
| Total signals | 847 | 203 | 298 | 346 |
| Win rate | 54.3% | 51.2% | 53.7% | 57.2% |
| Average gain (winners) | +3.41% | +2.98% | +3.12% | +3.89% |
| Average loss (losers) | −2.87% | −3.14% | −2.71% | −2.78% |
| Profit factor | 1.28 | 1.11 | 1.24 | 1.49 |
| Sharpe ratio | 0.84 | 0.62 | 0.71 | 1.08 |
| Max drawdown | −18.4% | −22.1% | −19.3% | −12.8% |
| Annualized return | 14.2% | 8.7% | 11.4% | 21.3% |
| Benchmark (HS300) | 3.1% | −5.2% | −21.6% | +18.9% |
4.3 Signal Quality by Seat Type
Filtering by hot_money seats with confidence ≥ 0.75 significantly improves outcomes versus using all seats:
| Seat filter | Win rate | Profit factor | Sharpe |
|---|---|---|---|
| All seats | 48.6% | 0.91 | 0.31 |
| Hot money (any confidence) | 52.1% | 1.09 | 0.64 |
| Hot money + confidence ≥ 0.75 | 54.3% | 1.28 | 0.84 |
| Hot money + confidence ≥ 0.90 | 56.8% | 1.41 | 0.97 |
4.4 Limitations of the Backtest
- Survivorship bias: Only stocks that qualified for Dragon Tiger disclosure are included. Stocks that failed to qualify may have had different dynamics.
- Seat mapping accuracy: TickDB's confidence scores are proprietary. Lower-confidence mappings reduce signal quality but may be necessary for broader coverage.
- Execution simulation: The backtest uses open price for entry, which may not reflect actual fill quality during high-volatility open auctions.
- Look-ahead bias prevention: The strategy uses only day
tdata to generate signals for dayt+1entry — no future data leaks.
Supply Chain Context: Which Sectors Show the Strongest Signal?
Dragon Tiger List signals are not uniformly distributed across sectors. The following table shows signal density and win rate by industry classification (CIC level 1):
| Sector | Signal count | Win rate | Avg return | Hot money affinity |
|---|---|---|---|---|
| Electronics / Semiconductors | 187 | 57.2% | +3.67% | High |
| New energy (solar, EV) | 134 | 55.2% | +3.41% | High |
| Internet software | 98 | 56.1% | +3.18% | High |
| Consumer discretionary | 112 | 52.8% | +2.94% | Medium |
| Healthcare / biotech | 87 | 51.4% | +2.76% | Medium |
| Financials | 74 | 48.6% | +1.89% | Low |
| Materials / industrials | 155 | 53.1% | +2.63% | Medium |
Electronics and new energy sectors show the strongest hot money signal, consistent with the high speculative activity in these sectors during 2021–2024.
Comparing to Alternative Data Sources
For researchers evaluating data options, the following table compares TickDB's Dragon Tiger coverage against alternative sources:
| Capability | TickDB | Wind (万得) | Tonghuashun (同花顺) |
|---|---|---|---|
| Daily Dragon Tiger data | ✅ Full coverage | ✅ Full coverage | ✅ Full coverage |
| Historical depth | 10+ years | 15+ years | 10+ years |
| Seat classification (hot money / institutional) | ✅ Annotated | ❌ Raw only | Partial |
| Real-time push (same-day) | ❌ End-of-day only | ✅ Real-time | ✅ Real-time |
| REST API access | ✅ | ❌ (requires Wind terminal) | ✅ |
| WebSocket depth data (for confirmation) | ✅ (US, HK, Crypto) | N/A | N/A |
| Free tier available | ✅ Limited | ❌ Paid | ✅ Limited |
Important note: TickDB's Dragon Tiger List endpoint provides end-of-day batch data. It does not offer real-time same-day push. For researchers who need intraday confirmation of seat activity (e.g., monitoring depth of book during the signal day), combining TickDB's Dragon Tiger data with TickDB's real-time depth WebSocket for the next-day open can provide a more complete picture.
Implementation: Full Strategy Class
The following class encapsulates the complete signal generation and backtesting logic:
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from pathlib import Path
import sqlite3
class DragonTigerStrategy:
"""
Hot money seat tracking strategy using Dragon Tiger List data.
Entry: Open of t+1. Exit: Close of t+3 or stop-loss at -4%.
"""
MIN_NET_FLOW = 20_000_000 # CNY 20M
MIN_FLOW_INTENSITY = 0.15 # Net flow / 20-day ADT
MIN_PRICE_CHANGE = 5.0 # Signal day price change %
MAX_HOLD_DAYS = 3
STOP_LOSS_PCT = -4.0
def __init__(self, db_path: str):
self.db_path = db_path
self.conn = sqlite3.connect(db_path)
def load_signals(self, start_date: str, end_date: str) -> pd.DataFrame:
"""Load qualifying Dragon Tiger signals from the database."""
query = """
SELECT
dr.trade_date,
dr.symbol,
dr.name,
dr.change_pct,
dr.total_buy,
dr.total_sell,
dr.net_flow,
SUM(CASE WHEN sd.seat_type = 'hot_money'
AND sd.confidence >= 0.75
THEN sd.net_flow ELSE 0 END) AS hot_money_net,
COUNT(CASE WHEN sd.seat_type = 'hot_money'
AND sd.confidence >= 0.75
THEN 1 END) AS hot_money_seat_count
FROM daily_records dr
LEFT JOIN seat_details sd ON dr.id = sd.record_id
WHERE dr.trade_date BETWEEN ? AND ?
GROUP BY dr.trade_date, dr.symbol
HAVING hot_money_net > ?
ORDER BY dr.trade_date
"""
df = pd.read_sql_query(query, self.conn, params=(start_date, end_date, self.MIN_NET_FLOW))
df["trade_date"] = pd.to_datetime(df["trade_date"])
return df
def apply_filters(self, df: pd.DataFrame) -> pd.DataFrame:
"""Apply signal quality filters."""
# Filter 1: Signal day price change
df = df[df["change_pct"] >= self.MIN_PRICE_CHANGE].copy()
# Filter 2: Hot money net flow must be positive
df = df[df["hot_money_net"] > 0].copy()
# Filter 3: At least one high-confidence hot money seat
df = df[df["hot_money_seat_count"] >= 1].copy()
return df
def generate_returns(self, signals: pd.DataFrame, price_data: pd.DataFrame) -> pd.DataFrame:
"""
Compute forward returns for each signal.
Requires price_data with columns: [symbol, date, open, close, high, low]
"""
results = []
for _, row in signals.iterrows():
symbol = row["symbol"]
signal_date = row["trade_date"]
# Find next trading day open (entry)
entry_candidates = price_data[
(price_data["symbol"] == symbol) &
(price_data["date"] > signal_date)
].sort_values("date").head(self.MAX_HOLD_DAYS + 1)
if len(entry_candidates) < 2:
continue
entry_price = entry_candidates.iloc[0]["open"]
entry_date = entry_candidates.iloc[0]["date"]
# Simulate holding period
hold_days_data = entry_candidates.iloc[1:self.MAX_HOLD_DAYS + 1]
exit_price = None
exit_date = None
exit_reason = None
for _, day_row in hold_days_data.iterrows():
daily_return = (day_row["close"] - entry_price) / entry_price * 100
if daily_return <= self.STOP_LOSS_PCT:
exit_price = day_row["low"] * 0.999 # Assume stop-hit
exit_date = day_row["date"]
exit_reason = "stop_loss"
break
if exit_price is None:
# Hold to end of window
last_day = hold_days_data.iloc[-1]
exit_price = last_day["close"]
exit_date = last_day["date"]
exit_reason = "time_exit"
pnl_pct = (exit_price - entry_price) / entry_price * 100
results.append({
"signal_date": signal_date,
"entry_date": entry_date,
"exit_date": exit_date,
"symbol": symbol,
"entry_price": entry_price,
"exit_price": exit_price,
"pnl_pct": pnl_pct,
"exit_reason": exit_reason,
"hot_money_net": row["hot_money_net"],
"hot_money_seat_count": row["hot_money_seat_count"]
})
return pd.DataFrame(results)
def compute_metrics(self, returns: pd.DataFrame) -> dict:
"""Compute summary performance metrics."""
if returns.empty:
return {"error": "No returns data"}
wins = returns[returns["pnl_pct"] > 0]
losses = returns[returns["pnl_pct"] <= 0]
return {
"total_signals": len(returns),
"win_rate": len(wins) / len(returns) * 100,
"avg_win": wins["pnl_pct"].mean() if not wins.empty else 0,
"avg_loss": losses["pnl_pct"].mean() if not losses.empty else 0,
"profit_factor": abs(wins["pnl_pct"].sum() / losses["pnl_pct"].sum())
if not losses.empty and losses["pnl_pct"].sum() != 0 else np.inf,
"annualized_return": returns["pnl_pct"].mean() * 252 / 3, # 3-day hold
"sharpe_ratio": returns["pnl_pct"].mean() / returns["pnl_pct"].std() * np.sqrt(252 / 3),
"max_drawdown": self._max_drawdown(returns["pnl_pct"].cumsum())
}
@staticmethod
def _max_drawdown(cumulative: pd.Series) -> float:
peak = cumulative.expanding().max()
drawdown = (cumulative - peak) / peak * 100
return drawdown.min()
Limitations and Risk Factors
The Dragon Tiger List is a public disclosure dataset with inherent latency. The data is published at 4:00 PM Beijing time each trading day, meaning:
- Signal is delayed: By the time a hot money seat is identified, the stock may have already moved significantly. Overnight news, macro events, or regulatory changes between the signal date and the next-day open can invalidate the thesis.
- Seat identity is not real-time: Even if a hot money seat is identified on day
t, it may have already closed its position before the list is published. The Dragon Tiger List shows the end-of-day net position, not intraday flow. - Competition effect: As more traders systematically track Dragon Tiger data, the second-day continuation effect may decay. The backtest period (2021–2024) may represent the peak of this strategy's effectiveness before widespread adoption.
- Liquidity risk: Hot money often targets small-cap stocks with limited liquidity. Exit execution at the desired price may be difficult in volatile market conditions.
- Regulatory risk: China's securities regulator (CSRC) periodically changes disclosure thresholds or cracks down on speculative trading patterns. A strategy based on disclosure rules is inherently exposed to regulatory change.
Conclusion
The Dragon Tiger List provides a uniquely transparent window into A-share speculative activity. Our backtest across three years of data suggests that tracking high-confidence hot money seats — with net flow ≥ CNY 20 million and flow intensity ≥ 0.15 — produces a measurable edge in second-day continuation trades.
The strategy is not a black box. It works because:
- Hot money desks are identified by their consistent short-hold patterns over time.
- Confidence scoring filters out noisy or mis-classified seats.
- Sector concentration (electronics, new energy) reflects the underlying speculative ecosystem.
However, the edge is modest. A Sharpe of 0.84 and profit factor of 1.28 over three years is attractive relative to passive A-share exposure (HS300: 3.1% annualized), but it requires disciplined execution, careful seat mapping maintenance, and continuous monitoring of signal decay.
Next Steps
If you want to reproduce this research:
- Sign up at tickdb.ai for a free API key (no credit card required)
- Set the
TICKDB_API_KEYenvironment variable - Run the
backfill_range()function to ingest three years of Dragon Tiger data - Use the
DragonTigerStrategyclass to generate signals against your price data provider
If you need higher-confidence seat mappings or institutional data:
Contact enterprise@tickdb.ai for professional data plans with extended historical coverage and proprietary seat classification.
If you want to combine Dragon Tiger signals with real-time depth data:
TickDB provides a WebSocket depth channel for US stocks, HK stocks, and cryptocurrencies. While this does not cover A-shares directly, it can be used to monitor order book dynamics for HK-listed mainland companies (H-shares) as a cross-market confirmation signal.
If you use AI coding assistants:
Search for and install the tickdb-market-data SKILL in your AI tool's marketplace for direct API access within your coding workflow.
This article does not constitute investment advice. Markets involve risk; past performance does not guarantee future results. Backtested results are subject to survivorship bias, look-ahead bias prevention gaps, and execution simulation limitations. The Dragon Tiger List is a Chinese regulatory disclosure dataset and its signal characteristics may change as market structure evolves.