Current version:
v1.10.0(2026-06-09)
Moomoo Quant is a dual-market quantitative trading and research framework built on top of the moomoo OpenD gateway, formerly known as the Futu OpenAPI gateway. It covers both US equities and Hong Kong equities, with shared strategy architecture and market-specific execution rules.
The project is designed as a full research-to-simulation loop:
market data -> factor scoring -> decision engine -> simulated execution
-> persistence -> forward IC calibration -> research reports
This repository is intended for research, simulation, data collection, and engineering reference. It is not investment advice and is not a turnkey live-trading system.
Supported markets:
| Market | Package | Code format | Examples |
|---|---|---|---|
| US equities | us_strategy/ |
US.SYMBOL |
US.AAPL, US.TSLA, US.NVDA |
| Hong Kong equities | hk_strategy/ |
HK.00000 |
HK.00700, HK.09988, HK.03690 |
This project does not cover A-shares, futures execution, cryptocurrency execution, or wallet-based trading.
The repository includes:
- Strategy code for US and HK simulated trading.
- Shared research, backtesting, IC diagnostics, and reporting tools.
- Forward signal collection and scheduled-task validation tools.
- Microstructure feature collection helpers.
- Local development instructions and safety rules.
The repository intentionally excludes:
- Live account credentials and trading passwords.
- Local SQLite databases.
- OpenD runtime logs.
- Generated backtest reports and charts.
- SDK vendor directories such as
MMAPI4Python_*. - Any proprietary data export that should not be redistributed.
The default runtime environment is simulation:
TRADE_ENV=SIMULATE
Live trading is guarded by three independent requirements:
TRADE_ENV=REALALLOW_REAL_TRADING=yesTRADE_PASSWORDmust be set so the moomoo trade context can be unlocked.
The simulation launcher scripts explicitly set TRADE_ENV=SIMULATE and remove live-trading unlock
variables. Do not bypass this behavior unless you have reviewed the code, OpenD account state,
broker permissions, market data permissions, and all order-size controls.
Expected buy failures caused by insufficient cash or budget are treated as log-only events to avoid alert spam. They are not silently converted into successful orders.
All broker and market-data calls go through the local OpenD gateway:
strategy code -> moomoo Python SDK -> TCP 127.0.0.1:11111 -> OpenD -> market data / broker API
The two core SDK contexts are:
| SDK context | Purpose |
|---|---|
OpenQuoteContext |
Quotes, historical candles, market snapshots, order book, ticker, broker queue, screeners |
OpenSecTradeContext |
Orders, account info, positions, fills, order history |
Every SDK call returns a (ret_code, data) tuple. Code must check ret_code == RET_OK before
using data; on failure, data is usually an error string.
.
|-- us_strategy/ # US equity strategy package
|-- hk_strategy/ # Hong Kong equity strategy package
|-- research/ # IC diagnostics, walk-forward research, backtest reports
|-- tools/ # Data collection, health checks, task validators, utilities
|-- skills/ # Codex local skill metadata for project workflows
|-- moomoo_rate_limits.py # Conservative moomoo API rate-limit facts
|-- dark_pool_proxy.py # Large-print proxy tracker based on visible moomoo ticks
|-- order_book_l2.py # L2 order-book imbalance and pressure utilities
|-- ipo_runtime.py # Today-IPO runtime helpers
|-- ipo_watchlist.py # IPO watchlist persistence helpers
|-- AGENTS.md # Repository-specific development rules
|-- CLAUDE.md # Additional local development notes
`-- VERSION # Current project version
Both us_strategy/ and hk_strategy/ follow the same shape:
| Module | Responsibility |
|---|---|
main.py |
Runtime entrypoint. Single-threaded event consumption for decisions and order submission |
config.py |
Environment-driven StrategyConfig, feature switches, weights, thresholds |
data_access.py |
TTL cache and token-bucket facade for quote/trade data |
features.py |
Pure factor scoring functions shared by live/sim and backtest logic |
signals.py |
Data retrieval, score composition, SignalResult, universe profiling |
strategy.py |
Decision engine: weighted cost, PDT/min-hold rules, circuit breaker, stop logic |
trader.py |
Marketable-limit execution, fill polling, add/open distinction |
persistence.py |
SQLite position recovery and forward signal logging |
monitor.py |
Realtime quote subscription |
alerts.py |
Email, Telegram, and Feishu/Lark alert adapters |
market_calendar.py |
Market holiday and trading-day logic |
backtest.py |
Source-aligned backtest engine with cost model and risk metrics |
analysis.py |
IC/IR, quantile tests, event studies, forward IC from logs |
probe.py |
Data availability probe before running strategies |
forward_monitor.py |
Forward scoring logger. It does not place orders |
ic_report.py |
Daily forward IC health report |
| Dimension | US strategy | HK strategy |
|---|---|---|
| Package | us_strategy |
hk_strategy |
| Code prefix | US. |
HK. with five-digit code |
| Time zone | America/New_York |
Asia/Hong_Kong |
| Regular session | 09:30-16:00 continuous | 09:30-12:00 and 13:00-16:00 |
| Lunch break | No | Yes |
| Calendar | NYSE calendar | HKEX API first, hard-coded holiday fallback |
| Minimum holding | PDT-aware, default MIN_HOLD_DAYS=1 |
No PDT, default MIN_HOLD_DAYS=0 |
| Cost model | Per-share commission | Turnover percentage, stamp duty, exchange fees |
| Benchmark | US.SPY |
HK.800000 |
| Lot size | 1 share | Broker lot size is queried and respected |
| Default position DB | us_strategy/positions.db |
hk_strategy/positions.db |
HK-specific factors must be calibrated on HK samples. Do not reuse US IC conclusions directly.
OpenD must be running before any SDK calls can connect.
- Default host:
127.0.0.1 - Default port:
11111 - OpenD documentation: https://openapi.moomoo.com/moomoo-api-doc/en/quick/opend-base.html
git clone https://github.com/BraveOldMan/moomoo-quant.git
Set-Location .\moomoo-quant
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pipInstall the official moomoo API package:
python -m pip install moomoo-apiIf you have a local SDK checkout, you can install it instead:
python -m pip install -e .\MMAPI4Python_10.7.6708Core runtime dependencies used by the repository include:
pandassimplejsonprotobuf>=3.20.0PyCryptodomemoomoo-api
Research, testing, and optional feature dependencies include:
pytestruffnumpymatplotlibtalibor a compatible TA-Lib installation, if you enable TA featuresoptuna,quantstats, andvectorbt, if you run optional research steps
This repository currently does not ship a pinned requirements.txt. Use an isolated virtual
environment and pin versions in your own deployment if you need reproducibility.
python -m pytest -q
python -m ruff check .Run market-specific tests:
python -m pytest us_strategy/tests -q
python -m pytest hk_strategy/tests -q
python -m pytest research/tests tools/tests -qThese commands require OpenD and the relevant market-data permissions:
python -m us_strategy.probe US.AAPL US.TSLA
python -m hk_strategy.probe HK.00700 HK.09988Run probes before enabling a symbol in a watchlist. Some moomoo endpoints are not uniformly available across markets, accounts, data packages, or trading sessions.
Recommended launchers:
powershell -ExecutionPolicy Bypass -File us_strategy\run_simulate.ps1
powershell -ExecutionPolicy Bypass -File hk_strategy\run_simulate.ps1Direct module entrypoints also default to simulation:
python -m us_strategy.main
python -m hk_strategy.mainOverride watchlists temporarily:
$env:WATCHLIST = "US.AAPL,US.TSLA"
python -m us_strategy.main
$env:WATCHLIST = "HK.00700,HK.09988"
python -m hk_strategy.mainUse US. prefixes for US symbols and HK. prefixes for HK symbols.
Runtime universe:
today IPOs + watchlist + current broker/local positions
Watchlist sources:
| Source | Behavior |
|---|---|
WATCHLIST env var |
Highest priority, comma-separated codes |
WATCHLIST_FILE env var |
Custom watchlist file path |
us_strategy/watchlist.txt |
Default US watchlist |
hk_strategy/watchlist.txt |
Default HK watchlist |
Watchlist file format:
# comments are allowed
US.AAPL
US.TSLA,US.NVDA
IPO watchlist files use:
YYYY-MM-DD<TAB>CODE<TAB>NAME<TAB>LIST_TIME
Today-IPO entries are stored separately from mature-stock watchlists:
us_strategy/ipo_watchlist.txthk_strategy/ipo_watchlist.txt
Today IPOs have their own sizing and risk profile:
| Setting | Default |
|---|---|
IPO_POSITION_RATIO |
0.05 |
IPO_ENTRY_TRANCHES |
2 |
IPO_TAKE_PROFIT_PCT |
0.12 |
IPO_STOP_LOSS_PCT |
0.06 |
IPO_TRAILING_STOP_PCT |
0.08 |
If real prices or turnover are not ready, the IPO candidate is observed and blocked from trading until it becomes analyzable.
All *_score values are 0-100 risk scores:
0 = low risk / more bullish
100 = high risk / more bearish
For a useful factor, forward IC should usually be significantly negative.
Default active decision weights:
| Factor | Default weight |
|---|---|
capital |
0.55 |
turnover |
0.25 |
momentum |
0.20 |
Extended factors are usually disabled or weight-zero until forward IC validates them:
| Group | Example score keys | Main source | Default state |
|---|---|---|---|
| Core | turnover, capital, momentum |
snapshot, capital distribution, candles | Active |
| Technical | orb, rs, vwap |
historical K-line, intraday candles | Off / zero weight |
| Microstructure | order_flow, dark_pool_proxy, obi, l2_imbalance, intraday_flow |
ticker, order book, capital flow | Off / zero weight |
| HK-specific | broker, hk_status |
broker queue, dark_status, sec_status |
Off / zero weight |
| Short side | short |
short interest, short volume | Off / zero weight |
| Options | option_iv |
option chain and snapshots | Warning-only by default |
dark_pool_proxy is only a proxy based on large visible moomoo real-time ticks. It is not FINRA TRF
dark-pool data.
Microstructure factors generally do not have a full historical replay source. They are collected forward and calibrated over time:
forward_monitor.py
-> writes signal_log with scores@T and price@T
ic_report.py
-> computes daily cross-sectional forward IC for 15/30 minute horizons
-> writes ic_history
IC gate
-> >= 20 trading days
-> |mean IC| > 0.03
-> |IR| > 0.5
-> expected sign is negative
Commands:
python -m us_strategy.forward_monitor
python -m hk_strategy.forward_monitor
python -m us_strategy.ic_report
python -m hk_strategy.ic_reportForward monitors only log scores. They do not place orders.
Backtest report CLI:
python -m research.run_backtest_report --market us --codes US.AAPL,US.MSFT --start 2024-01-01 --end 2024-03-31
python -m research.run_backtest_report --market hk --codes HK.00700,HK.09988 --start 2024-01-01 --end 2024-03-31Use local SQLite history as a research data source:
python -m research.run_backtest_report --market us --codes US.AAPL,US.MSFT --start 2024-01-01 --end 2024-03-31 --source sqlite --sqlite-db us_strategy/history_data.dbSignal research CLI:
python -m research.signal_lab --market us --codes US.AAPL,US.MSFT --start 2025-01-01 --end 2025-12-31 --steps ic,walkforward
python -m research.signal_lab --market hk --codes HK.00700,HK.09988 --start 2025-01-01 --end 2025-12-31 --steps ic,walkforwardOptional research steps:
icwalkforwardoptunaquantstatsvectorbt
Default research outputs:
| CLI | Default output directory |
|---|---|
research.signal_lab |
report/outputs/signal_research |
research.run_backtest_report |
report/outputs/backtest_report |
report/outputs/ is ignored by Git.
Strategy SQLite databases are local runtime state and are ignored by Git:
| Default DB | Market |
|---|---|
us_strategy/positions.db |
US |
hk_strategy/positions.db |
HK |
Key strategy tables:
| Table | Purpose |
|---|---|
positions |
Position recovery with quantity, weighted cost, and origin |
signal_log |
Forward signal log: timestamp, code, price, score JSON, market session |
ic_history |
Daily factor IC: date, factor, horizon, IC, sample size |
Historical market and microstructure data is usually stored in us_strategy/history_data.db:
| Table family | Purpose |
|---|---|
history_kline, market_snapshot |
Historical candles and after-hours snapshots |
hk_market_status_snapshots |
HK dark_status and sec_status snapshots |
realtime_ticks, realtime_quote_snapshots |
Intraday tick and quote snapshots |
order_book_snapshots, order_book_levels, order_book_metrics |
L2 order book data |
broker_queue_snapshots, broker_queue_levels, broker_queue_metrics |
HK broker queue data |
dark_pool_proxy_events, dark_pool_proxy_metrics |
Large visible-trade proxy events |
l2_imbalance_signals, microstructure_alerts |
Imbalance signals and alerts |
microstructure_daily_features |
Daily aggregated microstructure features |
backfill_runs, tick_runs |
Collection audit records |
Historical backfill:
python -m tools.daily_moomoo_watchlist_backfill --markets US,HK --db us_strategy/history_data.dbRealtime tick collection:
python -m tools.collect_moomoo_ticks --codes US.AAPL,HK.00700 --markets US,HK --duration-seconds 60 --db us_strategy/history_data.dbMicrostructure features:
python -m tools.microstructure_features --db us_strategy/history_data.db --codes US.AAPL,HK.00700Read-only health checks:
python -m tools.check_moomoo_data_health --db us_strategy/history_data.db --markets US,HK
python -m tools.check_moomoo_tasks --root D:\moomoo-quant --json --stricttools.check_moomoo_tasks validates Windows Task Scheduler metadata without modifying tasks.
Scheduled tasks are not part of the Git repository, but the PowerShell launchers are.
| Task | Trigger | Purpose |
|---|---|---|
MoomooForwardCollect |
Mon-Fri 16:00 Beijing | US forward signal collection across PRE/AFTER sessions |
MoomooICReport |
Tue-Sat 06:30 Beijing | US IC report after US close |
MoomooHKForwardCollect |
Mon-Fri 09:15 Beijing | HK forward signal collection |
MoomooHKICReport |
Mon-Fri 16:30 Beijing | HK IC report after HK close |
MoomooUSDailyWatchlistBackfill |
Daily 06:30 Beijing | US/HK watchlist historical backfill |
MoomooUSTickCollect |
Mon-Fri 21:00 Beijing | US intraday tick, quote, order-book, imbalance collection |
MoomooUSSimTrade |
Mon-Fri 21:15 Beijing | US simulated strategy runtime |
MoomooHKTickCollect |
Mon-Fri 09:15 Beijing | HK intraday tick, quote, order-book, broker-queue collection |
MoomooHKSimTrade |
Mon-Fri 09:15 Beijing | HK simulated strategy runtime |
PowerShell scripts are launchers only. Complex JSON, report rendering, Feishu/Lark payloads, data parsing, and validation should live in Python CLIs.
Alerting is optional and environment-driven:
| Variable | Purpose |
|---|---|
FEISHU_CHAT_ID |
Target Feishu/Lark chat ID |
LARK_CLI |
Path or command name for lark-cli |
ALERT_EMAIL |
Email alert target |
TELEGRAM_TOKEN |
Telegram bot token |
TELEGRAM_CHAT_ID |
Telegram chat ID |
For long reports, use the established pattern:
full Markdown report -> Feishu cloud document
summary -> interactive card in group chat
online readback -> verify message_id, card title, key fields, and document URL
Do not send long Markdown reports directly as chat text; that path is prone to truncation in Windows PowerShell and CLI quoting contexts.
Most settings are optional and have defaults.
| Category | Variables |
|---|---|
| OpenD | OPEND_HOST, OPEND_PORT |
| Trading mode | TRADE_ENV, ALLOW_REAL_TRADING, TRADE_PASSWORD |
| Universe | WATCHLIST, WATCHLIST_FILE, IPO_DAYS_WINDOW, IPO_WATCHLIST_FILE |
| HK liquidity | MIN_DAILY_TURNOVER |
| Position sizing | POSITION_RATIO, MAX_POSITIONS, ENTRY_TRANCHES, ORDER_LOTS_PER_TRADE, USE_ATR_SIZING |
| IPO sizing | IPO_POSITION_RATIO, IPO_ENTRY_TRANCHES |
| Risk | STOP_LOSS_PCT, TRAILING_STOP_PCT, MIN_HOLD_DAYS, DAILY_LOSS_LIMIT_PCT, CIRCUIT_BREAKER_BASELINE |
| IPO risk | IPO_TAKE_PROFIT_PCT, IPO_STOP_LOSS_PCT, IPO_TRAILING_STOP_PCT |
| Execution | USE_LIMIT_ORDERS, LIMIT_PRICE_TOLERANCE_PCT, TRADE_FAILURE_ALERT_COOLDOWN_S |
| Factor switches | USE_RS, USE_ORB, USE_VWAP_SIGNAL, USE_ORDER_FLOW, USE_DARK_POOL_PROXY, USE_ORDER_BOOK_IMBALANCE, USE_L2_IMBALANCE_TRACKER, USE_INTRADAY_FLOW, USE_SHORT_METRICS, USE_OPTION_IV, USE_BROKER_SIGNAL, USE_BROKER_GATE, USE_HK_STATUS_SIGNAL |
| Alerts | ALERT_EMAIL, TELEGRAM_TOKEN, TELEGRAM_CHAT_ID, FEISHU_CHAT_ID, LARK_CLI |
| Calibration | MONITOR_INTERVAL_S, MONITOR_MAX_ROUNDS, IC_HORIZONS, IC_MIN_DAYS, DB_PATH |
See the docstrings at the top of us_strategy/main.py and hk_strategy/main.py for the most
current runtime list.
Repository-level rate-limit facts live in moomoo_rate_limits.py. Strategy configs default to a
conservative global token bucket of 28 requests per 30 seconds for DataAccess, leaving margin
below known 30-per-30-second endpoints.
| API family | Conservative rule | Notes |
|---|---|---|
get_market_snapshot |
60 / 30s | Single call supports many symbols, subject to data package limits |
request_history_kline |
60 / 30s | First page is rate-limited; pagination behavior differs |
get_capital_flow, get_capital_distribution |
30 / 30s | Covered by the default 28 / 30s global bucket |
get_option_expiration_date |
60 / 30s | Pre-query for option chain |
get_option_chain |
10 / 30s | Default tooling sleeps around 3 seconds |
Trade queries with refresh_cache=True |
10 / 30s / account | Strategy defaults usually read OpenD cache |
place_order |
15 / 30s / account | Live use still requires manual review and unlock gates |
| Subscribed quote/order-book/ticker reads | OpenD cache | Not counted as server requests, but subject to subscription quota |
Unknown or low-frequency endpoints should be treated conservatively unless the official current documentation says otherwise.
Recommended local validation:
python -m pytest -q
python -m ruff check .
python -m tools.check_moomoo_tasks --root D:\moomoo-quant --json --strict
git diff --checkFor low-side-effect syntax validation on Windows, prefer an AST parse pass instead of commands that
write __pycache__ files:
python -c "import ast, pathlib; [ast.parse(p.read_text(encoding='utf-8-sig'), filename=str(p)) for p in pathlib.Path('.').rglob('*.py') if '.venv' not in p.parts]"Before changing strategy logic, validate at least:
- Unit tests for the affected market package.
- Backtest report on the affected universe.
- Forward IC or historical IC evidence for any factor weight change.
- No new live-trading bypass.
- No PowerShell JSON or report-generation logic added outside Python.
- Audited the US daily-report skill and hardened it without changing its outputs: structured (not substring) interactive-card readback validation, a single
nowshared by trading-day inference and the fail-closed readiness check, ATM tie-break preferring the strike at or below spot, exponential option-API backoff, and an explicit warning when every watchlist name lacks option data. - Aligned the backtest with the live strategy and documented the boundary of "what you test is what you trade":
capital(the highest-weight core factor) is only scored in the backtest/analysis when real per-bar institutional-flow data exists; otherwise it is dropped so the composite renormalizes exactly as the live path does when capital distribution is unavailable. Live usescapital_outflow_score(snapshot); the backtest proxycapital_flow_scoreis documented as calibration-by-forward-IC only.- Added a daily-loss circuit breaker to the backtest loop mirroring the live
check_and_update_circuit_breaker(blocks new opens/add-ons when the day's drawdown exceedsdaily_loss_limit_pct). - Documented the kline
turnover_rate×100 unit alignment and the short-factor graceful degradation.
- Extracted
features.kline_factor_scoresas the single shared scorer for the kline-derivable factors (turnover/momentum/rs), called by both livesignalsandbacktestso those factors can never drift apart. - Added a factor-consistency drift guard: a test that pins the full weighted-factor universe from
active_weights()and forces every factor to be classified as backtestable or forward-only, so any new weighted factor fails CI until it is consciously classified (and, if backtestable, mirrored into the backtest). - All changes applied to both US and HK packages; full suite green (428 tests),
ruffclean.
- Deep audit of the main strategy, signals, and backtest logic; most agent-flagged "critical" issues were verified against source as false positives (textbook Sortino downside deviation, Wilder ATR, intentional PCR fallback, filter-style macro/crypto gates, no look-ahead in the daily-close fill model) and deliberately left unchanged.
- Fixed
option_ivbeing computed but silently dropped:active_weights()now registers it whenuse_option_ivis on, consistent with sibling factors (US and HK). - Fixed walk-forward segments sharing one boundary trading day; adjacent splits no longer overlap (US and HK).
- Order-book imbalance now aggregates levels with
1/leveldistance decay instead of an equal mean, reducing level-1 quote noise. - Added an opt-in intraday staleness gate for the order-flow factor (
ORDER_FLOW_MAX_STALENESS_SECONDS, default0= disabled). - Added an opt-in IPO-origin lifecycle downgrade (
IPO_ORIGIN_MAX_HOLD_DAYS, default0= disabled): aged IPO positions revert to regular exit rules. - Hardened partial-fill accounting in the limit-order executor: prefer the re-queried real filled quantity over assuming the full request size after a failed cancel.
- Sharpe/Sortino now support a configurable annual risk-free rate (
ANNUAL_RISK_FREE_RATE), enabled by default at ≈4% (US) and ≈3.5% (HK); set0to restore the prior zero-rate reporting. - Added focused unit tests for every change; full suite green (US + HK).
- Added independent today-IPO simulation flow for US and HK.
- Today IPOs no longer mix into the regular watchlist.
- Added IPO-specific position sizing, take profit, stop loss, and trailing stop profiles.
- IPO positions persist with
origin=ipo, so restarts keep the correct risk profile. - Added Feishu/Lark alerts for IPO discovery, first analyzable state, trades, risk exits, and key blocks.
tools.check_moomoo_tasksaccepts running Windows Task Scheduler status values that are not errors.- Local validation for the release recorded full pytest, ruff, and whitespace checks, with one known historical task-state warning for
MoomooHKTickCollect.
- Improved US/HK simulation execution loop and account snapshot alerts.
- Added separate simulated-account sizing for US and HK.
- Added fill-timeout cancellation checks.
- Added
tools/liquidate_us_sim_positions.pywith simulation-only safeguards. - Extended task validation for simulation launcher parameters.
- Added US/HK scheduled automation loop.
- Added HK simulated trading scheduler.
- Added Feishu interactive-card alerting.
- Added SQLite research source support for
research.signal_labandresearch.run_backtest_report. - Added HK benchmark support through
HK.800000. - Expanded task and data-health checks.
Repository-specific development guidance lives in AGENTS.md.
Important project rules:
- Use UTF-8 for text, Markdown, Python, configs, and generated report content.
- PowerShell files should remain launchers; business logic belongs in Python CLIs.
- Prefer
subprocess.run([...])argument arrays for external CLI calls. - Do not hard-code API keys, passwords, chat IDs, or account credentials.
- Do not add dependencies without an explicit reason and installation notes.
- Do not enable live trading by default.
- Do not treat
receipt=sentas final alert validation; use online readback when sending Feishu/Lark messages.
- OpenD, market-data permission, account type, and region-specific endpoint availability determine what can actually run.
- Some moomoo endpoints differ between US and HK markets.
- Microstructure factors need forward collection before they can be trusted.
- Local SQLite data and generated reports are not included in the repository.
- No pinned lockfile is currently provided.
- The repository currently has no open-source license file.
No open-source license has been declared yet. Public visibility on GitHub does not automatically grant permission to copy, modify, redistribute, or use this project commercially.
Contact the repository owner before reusing the code outside personal research.