How to Add a New Stock Market (e.g., Singapore) to TradingAgents-CN

Adding a new stock market to TradingAgents-CN requires extending the StockMarket enum, updating ticker detection regex patterns, mapping the market to an internal category ID, and implementing a dedicated data provider module.

TradingAgents-CN is an open-source multi-agent trading system that supports multiple stock markets through a modular data-source architecture. Whether you need to add Hong Kong, Singapore, or any other exchange, the process follows a consistent three-layer pattern involving market detection, category mapping, and provider registration.

Understanding the Three-Layer Architecture

The codebase treats every market as a market category derived from the stock ticker format. Adding a new market involves three architectural layers:

Layer Purpose Key File
Ticker-to-market detection Decides whether a ticker belongs to China A-shares, Hong Kong, US, or the new market tradingagents/utils/stock_utils.py
Market-category mapping Maps the detected StockMarket value to the internal market-category ID used by the data-source manager tradingagents/dataflows/data_source_manager.py
Data-source registration Provides a concrete provider for the new market and makes it selectable in the priority-order logic tradingagents/dataflows/providers/<market>/

Step-by-Step Implementation Guide

Extend the StockMarket Enum in stock_utils.py

The StockMarket enum lives in tradingagents/utils/stock_utils.py (lines 15-20). Add a member for your new market:

from enum import Enum

class StockMarket(Enum):
    """股票市场枚举"""
    CHINA_A = "china_a"      # 中国A股

    HONG_KONG = "hong_kong"  # 港股

    US = "us"                # 美股

    SINGAPORE = "singapore"  # 新增:新加坡股市

    UNKNOWN = "unknown"      # 未知

Update Ticker Detection Logic

Singapore tickers typically use 4-letter codes ending with .SI (e.g., D05.SI). Update the identify_stock_market function in the same file to check this pattern before the generic US-ticker rule:

import re

def identify_stock_market(ticker: str) -> StockMarket:
    # Existing checks for HK and China A...

    
    # Singapore: 4-letter + .SI (case-insensitive)

    if re.match(r'^[A-Z]{4}\.SI$', ticker):
        return StockMarket.SINGAPORE
    
    # US and other markets...

    return StockMarket.US

Place this block after the Hong Kong check (around lines 46-52) to maintain correct precedence.

Map the Market to Internal Category ID

DataSourceManager._identify_market_category in tradingagents/dataflows/data_source_manager.py (lines 86-100) converts StockMarket values to internal category IDs (a_shares, us_stocks, hk_stocks). Add the Singapore mapping:

def _identify_market_category(self, market: StockMarket) -> str:
    market_mapping = {
        StockMarket.CHINA_A: 'a_shares',
        StockMarket.US: 'us_stocks',
        StockMarket.HONG_KONG: 'hk_stocks',
        StockMarket.SINGAPORE: 'sg_stocks',   # ← 新增

    }
    return market_mapping.get(market, 'unknown')

This mapping is critical because _get_data_source_priority_order queries the system_configs collection for entries whose market_categories field matches this ID. Without it, Singapore tickers would fall back to China A-share providers.

Create a Dedicated Provider Module

Create a new package under tradingagents/dataflows/providers/sg/ and add yahoo_finance.py. This minimal implementation reuses Yahoo Finance logic:


# tradingagents/dataflows/providers/sg/yahoo_finance.py

from ..base_provider import BaseProvider
import yfinance as yf

class SingaporeYahooProvider(BaseProvider):
    """Yahoo Finance provider for Singapore tickers (.SI)"""

    def get_stock_data(self, ticker: str, start: str, end: str):
        # Ensure the ticker ends with .SI

        if not ticker.upper().endswith('.SI'):
            ticker = f"{ticker.upper()}.SI"
        data = yf.download(ticker, start=start, end=end)
        return data.to_dict(orient='records')

Register the Provider and Wire It Into the Manager

Register the provider in tradingagents/dataflows/providers/__init__.py:

from .sg.yahoo_finance import SingaporeYahooProvider

Then wire it into DataSourceManager by adding a conditional branch in the data-fetching method:

elif self.current_source == SGDataSource.YAHOO_FINANCE:
    result = SingaporeYahooProvider().get_stock_data(symbol, start_date, end_date)

Update Configuration and UI

Add "sg_stocks" to the market selector in your configuration schema (e.g., install/database_export_config.json or the frontend admin console). This allows operators to enable or disable Singapore data sources independently.

Validate with Unit Tests

Add a test file to verify detection and provider plumbing:


# tests/test_singapore_market.py

def test_singapore_market_detection():
    from tradingagents.utils.stock_utils import StockUtils, StockMarket
    assert StockUtils.identify_stock_market('D05.SI') == StockMarket.SINGAPORE
    info = StockUtils.get_market_info('D05.SI')
    assert info['market'] == 'singapore'

Run pytest to ensure existing tests for China A-shares, Hong Kong, and US markets still pass.

Complete Code Examples

Detecting a Singapore Ticker

from tradingagents.utils.stock_utils import StockUtils

ticker = "D05.SI"
info = StockUtils.get_market_info(ticker)

print(info)

# {

#   "ticker": "D05.SI",

#   "market": "singapore",

#   "market_name": "新加坡股市",

#   "currency_name": "美元",

#   "currency_symbol": "$",

#   "data_source": "yahoo_finance",

#   "is_china": False,

#   "is_hk": False,

#   "is_us": False,

#   "is_sg": True

# }

Fetching Data via the Unified Manager

from tradingagents.dataflows.data_source_manager import DataSourceManager

manager = DataSourceManager()

# Optional: force SG Yahoo provider

manager.current_source = manager.SGDataSource.YAHOO_FINANCE

# Fetch DBS Group data

data = manager.get_stock_data("D05.SI", "2023-01-01", "2023-12-31")
print(data[:3])  # First 3 records

Key Files Reference

File Purpose Location
tradingagents/utils/stock_utils.py Contains StockMarket enum and identify_stock_market detection logic View on GitHub
tradingagents/dataflows/data_source_manager.py Maps markets to category IDs and manages provider priority View on GitHub
tradingagents/dataflows/providers/sg/ New directory for Singapore-specific providers Create under providers/
tradingagents/dataflows/providers/__init__.py Registers provider classes for import View on GitHub
install/database_export_config.json Configuration schema for market categories View on GitHub

Summary

  • Extend the enum: Add your market to StockMarket in tradingagents/utils/stock_utils.py with a unique identifier like SINGAPORE.
  • Update detection: Implement a regex pattern in identify_stock_market to recognize tickers (e.g., ^[A-Z]{4}\.SI$ for Singapore).
  • Map the category: Add the market-to-ID mapping in DataSourceManager._identify_market_category (e.g., StockMarket.SINGAPORE: 'sg_stocks').
  • Build the provider: Create a new module under tradingagents/dataflows/providers/<market>/ implementing BaseProvider.
  • Wire and register: Import the provider in providers/__init__.py and add the dispatch logic in DataSourceManager.
  • Configure and test: Update database_export_config.json with the new category and write unit tests to verify detection logic.

Frequently Asked Questions

What file contains the StockMarket enum?

The StockMarket enum is defined in tradingagents/utils/stock_utils.py (lines 15-20). This file also contains the identify_stock_market function that uses regex patterns to classify tickers into their respective markets.

Do I need to create a new provider for every new market?

Yes, you should create a dedicated provider module under tradingagents/dataflows/providers/<market>/. While you can reuse existing data-fetching libraries like yfinance, you need a concrete class implementing BaseProvider to handle market-specific ticker formatting (e.g., ensuring .SI suffix for Singapore) and any unique API requirements.

How does the system handle tickers that don't match any pattern?

If a ticker does not match any of the defined regex patterns in identify_stock_market, the function returns StockMarket.UNKNOWN. According to the source code in data_source_manager.py, unknown markets typically fall back to default China A-share providers or raise a configuration error depending on the strictness of your DataSourceManager implementation.

Can I use Yahoo Finance for markets other than Singapore?

Yes, Yahoo Finance supports numerous international exchanges. You can reuse the yfinance library for other markets by creating similar provider classes in tradingagents/dataflows/providers/<market>/. Simply adjust the ticker suffix logic (e.g., .HK for Hong Kong, .TO for Toronto) in the provider's get_stock_data method to match Yahoo Finance's formatting requirements for that specific exchange.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →