mirror of
https://github.com/simonlin1212/TradingAgents-astock.git
synced 2026-10-30 09:29:56 +00:00
fix: K线数据源fallback + 侧边栏/主题修复 + 强制中文输出
- a_stock.py: mootdx TCP失败时自动切换akshare新浪源作为备用 (Closes #13) - .streamlit/config.toml: 固定暗色主题配色,解决文字不可见问题 (Closes #9) - web/app.py: 移除过度隐藏sidebar按钮的CSS规则 (Closes #11) - trader.py + research_manager.py: 添加get_language_instruction() 确保投资建议和风控评估输出中文 (Closes #5)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
[theme]
|
||||
base = "dark"
|
||||
primaryColor = "#ff5a1f"
|
||||
backgroundColor = "#0a0a0a"
|
||||
secondaryBackgroundColor = "#0f0f0f"
|
||||
textColor = "#f5f1eb"
|
||||
font = "sans serif"
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from tradingagents.agents.schemas import ResearchPlan, render_research_plan
|
||||
from tradingagents.agents.utils.agent_utils import build_instrument_context
|
||||
from tradingagents.agents.utils.agent_utils import build_instrument_context, get_language_instruction
|
||||
from tradingagents.agents.utils.structured import (
|
||||
bind_structured,
|
||||
invoke_structured_or_freetext,
|
||||
@@ -39,7 +39,7 @@ Commit to a clear stance whenever the debate's strongest arguments warrant one;
|
||||
---
|
||||
|
||||
**Debate History:**
|
||||
{history}"""
|
||||
{history}""" + get_language_instruction()
|
||||
|
||||
investment_plan = invoke_structured_or_freetext(
|
||||
structured_llm,
|
||||
|
||||
@@ -7,7 +7,7 @@ import functools
|
||||
from langchain_core.messages import AIMessage
|
||||
|
||||
from tradingagents.agents.schemas import TraderProposal, render_trader_proposal
|
||||
from tradingagents.agents.utils.agent_utils import build_instrument_context
|
||||
from tradingagents.agents.utils.agent_utils import build_instrument_context, get_language_instruction
|
||||
from tradingagents.agents.utils.structured import (
|
||||
bind_structured,
|
||||
invoke_structured_or_freetext,
|
||||
@@ -62,6 +62,7 @@ def create_trader(llm):
|
||||
f"Proposed Investment Plan:\n{investment_plan}\n\n"
|
||||
+ (f"Additional A-Stock Analyst Context:\n{astock_context}\n\n" if astock_context else "")
|
||||
+ "Leverage these insights to craft a precise transaction proposal."
|
||||
+ get_language_instruction()
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -227,27 +227,55 @@ def _load_ohlcv_astock(symbol: str, curr_date: str) -> pd.DataFrame:
|
||||
return data[data["Date"] <= cutoff]
|
||||
|
||||
# Fetch from mootdx — 800 daily bars (~3 years of trading days)
|
||||
client = _get_mootdx_client()
|
||||
df = client.bars(symbol=code, category=4, offset=800)
|
||||
try:
|
||||
client = _get_mootdx_client()
|
||||
df = client.bars(symbol=code, category=4, offset=800)
|
||||
|
||||
if df is None or df.empty:
|
||||
raise ValueError(f"No OHLCV data from mootdx for {code}")
|
||||
if df is None or df.empty:
|
||||
raise ValueError(f"No OHLCV data from mootdx for {code}")
|
||||
|
||||
# mootdx returns index named 'datetime' AND a column named 'datetime'
|
||||
# (plus year/month/day/hour/minute/volume). Drop duplicates before reset.
|
||||
df = df.drop(columns=["datetime", "year", "month", "day", "hour", "minute"], errors="ignore")
|
||||
df = df.reset_index() # moves index 'datetime' → column 'datetime'
|
||||
rename_map = {
|
||||
"datetime": "Date",
|
||||
"open": "Open",
|
||||
"close": "Close",
|
||||
"high": "High",
|
||||
"low": "Low",
|
||||
"volume": "Volume",
|
||||
}
|
||||
df = df.rename(columns=rename_map)
|
||||
df = df[["Date", "Open", "High", "Low", "Close", "Volume"]]
|
||||
df["Date"] = pd.to_datetime(df["Date"])
|
||||
# mootdx returns index named 'datetime' AND a column named 'datetime'
|
||||
# (plus year/month/day/hour/minute/volume). Drop duplicates before reset.
|
||||
df = df.drop(columns=["datetime", "year", "month", "day", "hour", "minute"], errors="ignore")
|
||||
df = df.reset_index() # moves index 'datetime' → column 'datetime'
|
||||
rename_map = {
|
||||
"datetime": "Date",
|
||||
"open": "Open",
|
||||
"close": "Close",
|
||||
"high": "High",
|
||||
"low": "Low",
|
||||
"volume": "Volume",
|
||||
}
|
||||
df = df.rename(columns=rename_map)
|
||||
df = df[["Date", "Open", "High", "Low", "Close", "Volume"]]
|
||||
df["Date"] = pd.to_datetime(df["Date"])
|
||||
except Exception as e:
|
||||
logger.warning("mootdx OHLCV failed for %s: %s, trying akshare fallback", code, e)
|
||||
# Fallback: akshare sina source
|
||||
try:
|
||||
import akshare as ak
|
||||
|
||||
prefix = "sh" if code.startswith("6") else "sz"
|
||||
sina_symbol = f"{prefix}{code}"
|
||||
# Calculate date range for akshare query (~3 years back)
|
||||
end_dt = datetime.now()
|
||||
start_dt = end_dt - relativedelta(years=3)
|
||||
df_ak = ak.stock_zh_a_daily(
|
||||
symbol=sina_symbol,
|
||||
start_date=start_dt.strftime("%Y%m%d"),
|
||||
end_date=end_dt.strftime("%Y%m%d"),
|
||||
adjust="qfq",
|
||||
)
|
||||
if df_ak is None or df_ak.empty:
|
||||
raise ValueError(f"No OHLCV data from akshare for {code}")
|
||||
df_ak = df_ak.rename(columns={
|
||||
"date": "Date", "open": "Open", "close": "Close",
|
||||
"high": "High", "low": "Low", "volume": "Volume",
|
||||
})
|
||||
df_ak["Date"] = pd.to_datetime(df_ak["Date"])
|
||||
df = df_ak[["Date", "Open", "High", "Low", "Close", "Volume"]].copy()
|
||||
except Exception:
|
||||
raise ValueError(f"No OHLCV data from mootdx for {code}")
|
||||
|
||||
# Cache to disk
|
||||
df.to_csv(cache_file, index=False, encoding="utf-8")
|
||||
@@ -273,15 +301,13 @@ def get_stock_data(
|
||||
"""Get OHLCV stock price data via mootdx."""
|
||||
code = _normalize_ticker(symbol)
|
||||
|
||||
data_source = "mootdx (TCP)"
|
||||
try:
|
||||
client = _get_mootdx_client()
|
||||
df = client.bars(symbol=code, category=4, offset=800)
|
||||
|
||||
if df is None or df.empty:
|
||||
return (
|
||||
f"No data found for A-stock '{code}' "
|
||||
f"between {start_date} and {end_date}"
|
||||
)
|
||||
raise ValueError(f"No data from mootdx for {code}")
|
||||
|
||||
# Drop duplicate datetime column + extra columns before reset_index
|
||||
df = df.drop(
|
||||
@@ -300,39 +326,65 @@ def get_stock_data(
|
||||
"amount": "Amount",
|
||||
}
|
||||
)
|
||||
|
||||
# Filter by date range
|
||||
df["Date"] = pd.to_datetime(df["Date"])
|
||||
start_dt = pd.to_datetime(start_date)
|
||||
end_dt = pd.to_datetime(end_date)
|
||||
df = df[(df["Date"] >= start_dt) & (df["Date"] <= end_dt)]
|
||||
|
||||
if df.empty:
|
||||
return (
|
||||
f"No data found for A-stock '{code}' "
|
||||
f"between {start_date} and {end_date}"
|
||||
)
|
||||
|
||||
for col in ["Open", "High", "Low", "Close"]:
|
||||
if col in df.columns:
|
||||
df[col] = df[col].round(2)
|
||||
|
||||
df["Date"] = df["Date"].dt.strftime("%Y-%m-%d")
|
||||
csv_out = df[["Date", "Open", "High", "Low", "Close", "Volume"]].to_csv(
|
||||
index=False
|
||||
)
|
||||
|
||||
header = f"# Stock data for {code} (A-stock) from {start_date} to {end_date}\n"
|
||||
header += f"# Total records: {len(df)}\n"
|
||||
header += "# Data source: mootdx (TCP)\n"
|
||||
header += (
|
||||
f"# Data retrieved on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
|
||||
)
|
||||
|
||||
return header + csv_out
|
||||
|
||||
except Exception as e:
|
||||
return f"Error retrieving A-stock data for {code}: {str(e)}"
|
||||
logger.warning("mootdx K-line failed for %s: %s, trying akshare fallback", code, e)
|
||||
# Fallback: akshare sina source
|
||||
try:
|
||||
import akshare as ak
|
||||
|
||||
prefix = "sh" if code.startswith("6") else "sz"
|
||||
sina_symbol = f"{prefix}{code}"
|
||||
start_str = start_date.replace("-", "")
|
||||
end_str = end_date.replace("-", "")
|
||||
df_ak = ak.stock_zh_a_daily(
|
||||
symbol=sina_symbol,
|
||||
start_date=start_str,
|
||||
end_date=end_str,
|
||||
adjust="qfq",
|
||||
)
|
||||
if df_ak is not None and not df_ak.empty:
|
||||
df_ak = df_ak.rename(columns={
|
||||
"date": "Date", "open": "Open", "close": "Close",
|
||||
"high": "High", "low": "Low", "volume": "Volume",
|
||||
})
|
||||
df_ak["Date"] = pd.to_datetime(df_ak["Date"])
|
||||
df = df_ak
|
||||
data_source = "akshare (sina fallback)"
|
||||
else:
|
||||
return "K线数据获取失败:mootdx和akshare备用源均不可用,请检查网络连接"
|
||||
except Exception:
|
||||
return "K线数据获取失败:mootdx和akshare备用源均不可用,请检查网络连接"
|
||||
|
||||
# Filter by date range
|
||||
start_dt = pd.to_datetime(start_date)
|
||||
end_dt = pd.to_datetime(end_date)
|
||||
df = df[(df["Date"] >= start_dt) & (df["Date"] <= end_dt)]
|
||||
|
||||
if df.empty:
|
||||
return (
|
||||
f"No data found for A-stock '{code}' "
|
||||
f"between {start_date} and {end_date}"
|
||||
)
|
||||
|
||||
for col in ["Open", "High", "Low", "Close"]:
|
||||
if col in df.columns:
|
||||
df[col] = df[col].round(2)
|
||||
|
||||
df["Date"] = df["Date"].dt.strftime("%Y-%m-%d")
|
||||
csv_out = df[["Date", "Open", "High", "Low", "Close", "Volume"]].to_csv(
|
||||
index=False
|
||||
)
|
||||
|
||||
header = f"# Stock data for {code} (A-stock) from {start_date} to {end_date}\n"
|
||||
header += f"# Total records: {len(df)}\n"
|
||||
header += f"# Data source: {data_source}\n"
|
||||
header += (
|
||||
f"# Data retrieved on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
|
||||
)
|
||||
|
||||
return header + csv_out
|
||||
|
||||
|
||||
# ---- 2. get_indicators ----
|
||||
|
||||
+2
-1
@@ -43,8 +43,9 @@ st.markdown(
|
||||
/* Hide Streamlit chrome for clean video recording */
|
||||
#MainMenu, header[data-testid="stHeader"],
|
||||
footer, div[data-testid="stDecoration"],
|
||||
button[data-testid="stBaseButton-header"],
|
||||
div[data-testid="stToolbar"] { display: none !important; }
|
||||
/* Ensure sidebar collapse/expand control is always visible */
|
||||
button[data-testid="collapsedControl"] { display: flex !important; }
|
||||
|
||||
html, body, [class*="css"] {
|
||||
font-family: 'Inter', -apple-system, sans-serif;
|
||||
|
||||
Reference in New Issue
Block a user