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
StockMarketintradingagents/utils/stock_utils.pywith a unique identifier likeSINGAPORE. - Update detection: Implement a regex pattern in
identify_stock_marketto 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>/implementingBaseProvider. - Wire and register: Import the provider in
providers/__init__.pyand add the dispatch logic inDataSourceManager. - Configure and test: Update
database_export_config.jsonwith 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →