BulletTrade Quantitative Trading System

repository·main·Indexed 42 days ago

https://github.com/bullettrade/bullet-trade

A professional quantitative trading system for Python designed to bridge backtesting and live execution. It is compatible with the JoinQuant (聚宽) ecosystem and supports multiple data sources including JQData, MiniQMT, TuShare, and RQData. BulletTrade provides a comprehensive suite for strategy development, featuring a BacktestEngine with detailed reporting, a LiveEngine for real-world trading via QMT, and a dedicated JupyterLab CLI for environment management.

Tokens
58.4K
Snippets
141
Records
242
Agent score
52%

What's inside BulletTrade

  1. Overview of BulletTrade

    main

    BulletTrade is a professional quantitative trading system Python package designed to provide complete solutions for both backtesting and live trading. It is highly compatible with JoinQuant (聚宽) and supports multiple data sources and execution methods.

    Key Features

    • JoinQuant Compatibility: Supports from jqdata import * syntax.
    • Multi-Data Source Support: Switch between JQData, MiniQMT, TuShare, local cache, remote QMT server, and Beta versions of RQData/easy_tdx.
    • Backtesting & Reporting: Supports minute/daily line backtesting with real price matching and one-click HTML/PDF report generation.
    • Live Trading Integration: Supports local QMT, remote QMT server, and simulated brokers.
    • Extensibility: Data and broker interfaces are based on abstract base classes, allowing for custom implementations.
  2. Compare JoinQuant Integration Schemes

    main

    Use this comparison to decide which modification method to use for your JoinQuant strategy:

    FeatureScheme 1: Explicit HelperScheme 2: Function Takeover
    Primary MethodCall bt.order(...) directlyCall bt.install_jq_compat(...)
    Best ForUsers wanting explicit control; minimal order points; notebooksLegacy strategies; many order points; heavy use of context.portfolio
    Code ChangesHigh (every order must be changed)Low (install once in process_initialize)
    Portfolio SyncManual (must call helper for cash/positions)Automatic (intercepts context.portfolio)
    ComplexityExplicit and predictableAutomatic and "magical"
    LimitationsRequires manual branching for backtestingLimited function support (no shorts/stop orders)
  3. Understand the JoinQuant running strategy architecture

    main

    In the JoinQuant route, you continue using the JoinQuant research environment, data, and scheduled tasks. You only connect your account queries and order placement to your local Big QMT via the internet.

    Data Flow: JoinQuant Strategy -> Internet -> BulletTrade server -> Big QMT -> Broker

    This route supports two implementation methods:

    1. Explicit calling: Manually calling API methods.
    2. Function hijacking: Intercepting existing functions.

    Note: Backtesting handling differs between these two methods. Refer to the JoinQuant Running Strategy Guide for specific instructions.

  4. Understand RQData data formats and mapping

    main

    When using the RQDataProvider, note the following data conventions and mappings:

    • Security Codes: Uses JoinQuant (JQData) format, e.g., 000001.XSHE or 600519.XSHG.
    • Money/Turnover: The money field maps to rqdatac.total_turnover and is returned in Yuan (CNY).
    • Price Limits: high_limit and low_limit map to limit_up and limit_down respectively.
    • Data Shape:
      • If panel=True: Returns a MultiIndex column structure of (field, code) for multiple securities.
      • If panel=False: Returns a long-format table with time/code indices.
    • Minute Data: If minute-level data lacks limit-up/down fields, the provider automatically fetches daily limit data and merges it by date.
  5. Understand SDK fallback rules for data providers

    main

    If a provider instance does not implement a specific method, the system attempts to fall back to the underlying SDK/client for that same data source. It will not switch to a different data provider.

    Fallback Mapping:

    provider_nameFallback Target (Priority)Notes
    jqdatajqdatasdk moduleDirect SDK call
    tusharetushare.pro_api() client $\rightarrow$ tushare moduleRequires TUSHARE_TOKEN configuration
    qmt / miniqmtxtquant.xtdata moduleRequires miniQMT/xtquant installation
    remote_qmtNoneOnly throws a missing method error

    If fallback fails, an error is raised indicating the attempt, e.g.: jqdata 未实现 special_method,已尝试回退到同名 provider 的 SDK/客户端: jqdatasdk.

  6. How MiniQMT handles price adjustment and dividends

    main

    The MiniQMTProvider implements price adjustment (factorization) and dividend processing to ensure backtesting accuracy.

    Price Adjustment (Factorization)

    • Uses xtdata.get_local_data with dividend_type set to none, front_ratio, or back_ratio.
    • If the official interface lacks adjusted data, the provider dynamically generates a forward-adjusted (pre) sequence by backtracking through split/dividend events.
    • To support pre_factor_ref_date, it reads unadjusted data to calculate a scaling coefficient, ensuring the adjusted price matches the actual transaction price on the reference date.
    • Setting skip_paused=True uses volume > 0 as an approximation for detecting trading halts.

    Dividends and Splits

    • Extracts information (dividends, bonus shares, rights issues) via xtdata.get_divid_factors and converts them into the unified event format used by the backtesting engine (standardized to "per 1 share").
    • If xtquant returns empty results or lacks the interface, it falls back to Tushare if a tushare_token is configured.

    Index Constituents

    • Attempts to use xtdata.get_index_stocks first.
    • Falls back to Tushare's index_weight if the xtquant interface is missing or empty and a tushare_token is provided.
  7. Understand the Local running strategy architecture

    main

    In the Local route, your strategy files, Python environment, and logs all reside on your own machine. This provides maximum control over the execution environment.

    Data Flow: Local Strategy -> bullet-trade live -> BulletTrade server -> Big QMT -> Broker

    Key Details:

    • By default, it uses the local address 127.0.0.1:58620.
    • No additional address or port configuration is required for the local connection.

    Refer to the Local Running Strategy Guide for setup.

  8. How Dynamic Adjustment (复权) works in JQData

    main

    When using the real price mode (use_real_price=True), the provider passes prefer_engine=True and pre_factor_ref_date (set to the backtest timestamp) to the underlying engine.

    The JQDataProvider calls a patched version of get_price_engine. The jqdatasdk itself handles the adjustment based on the specified reference date, so no manual scaling is required by the user.

  9. How Tick Data Subscription Works (Polling vs. Push)

    main

    It is important to understand the underlying mechanism of tick data delivery to manage latency and server load:

    Polling Mechanism

    In remote mode, the subscription does not trigger a push from the server. Instead, the client (LiveEngine) actively pulls data from the server at every tick_sync_interval.

    Key Constraints

    • Subscription Limit: There is a tick_subscription_limit (default is 100). Increasing the number of subscribed symbols increases the request pressure on the server and may increase latency/bandwidth usage.
    • Sync Interval: The frequency of data updates is governed by tick_sync_interval.
    • No Compression: The server does not currently implement batching, compression, or throttling for tick data. High-frequency scenarios should be handled with caution.
  10. Manage Order Waiting Times (Sync vs Async)

    main

    The engine defaults to a synchronous wait of up to 16 seconds for an order to reach a terminal state before returning a result. You can control this behavior to implement asynchronous trading or adjust timeouts.

    Configuration Options

    1. Global (via .env): Set TRADE_MAX_WAIT_TIME.
      • 16 (default): Synchronous wait.
      • 0: Purely asynchronous (returns immediately).
    2. Per-call (via function argument): Use wait_timeout in order functions. This has higher priority than the environment variable.

    Important Timeout Relationships

    To prevent RPC timeouts while the order is still being processed, ensure your RPC timeout is greater than the order wait window plus a margin:

    • Recommended RPC Timeout: QMT_SERVER_RPC_TIMEOUT=60
    • Recommended Margin: QMT_PLACE_ORDER_TIMEOUT_MARGIN=30
    • Formula: RPC_TIMEOUT > wait_timeout + QMT_PLACE_ORDER_TIMEOUT_MARGIN

    If you see status=open/submitted and timed_out=true, the order was successfully submitted but the wait window expired. You must verify the final status via order/trade queries.

    # Asynchronous: returns immediately
    order_target('000001.XSHE', 0, wait_timeout=0)
    
    # Synchronous: waits up to 10 seconds
    order('600519.XSHG', 100, wait_timeout=10)
  11. Choose a BulletTrade execution route

    main

    BulletTrade offers different execution models depending on your strategy environment and connectivity requirements. The primary recommended route is Big QMT.

    1. Joiner/Cloud Route (聚宽运行策略)

    Strategies run on the JoinQuant (聚宽) platform and connect to your own BulletTrade service via the internet. The BulletTrade service then uses Big QMT to handle account queries and order placement.

    2. Local Route (本地运行策略)

    Strategies run locally using the bullet-trade live command. This local process connects to a Big QMT instance running on the same machine via qmt-remote.

  12. Understand the QMT server architecture

    main

    The QMT server acts as a remote provider for market data and order execution. It is designed to run on Windows to bridge MiniQMT/xtquant to remote strategies.

    There are three distinct layers to understand:

    1. qmt: The local direct connection to MiniQMT/xtquant data sources and trading channels.
    2. big_qmt: A backend for the Big QMT helper server (not a local direct provider).
    3. qmt-remote: The client-side remote protocol that can connect to either a MiniQMT server or a Big QMT server.

    Note: This guide focuses on the MiniQMT/xtquant backend using QMT_DATA_PATH. For Big QMT, refer to the Big QMT Server Guide.