Merge 9ccb1913dd into fa4d01c23a
This commit is contained in:
commit
df37028977
|
|
@ -1,3 +1,6 @@
|
||||||
|
# Claude Code local files
|
||||||
|
CLAUDE.md
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[codz]
|
*.py[codz]
|
||||||
|
|
|
||||||
123
cli/main.py
123
cli/main.py
|
|
@ -463,7 +463,7 @@ def update_display(layout, spinner_text=None, stats_handler=None, start_time=Non
|
||||||
def get_user_selections():
|
def get_user_selections():
|
||||||
"""Get all user selections before starting the analysis display."""
|
"""Get all user selections before starting the analysis display."""
|
||||||
# Display ASCII art welcome message
|
# Display ASCII art welcome message
|
||||||
with open(Path(__file__).parent / "static" / "welcome.txt", "r") as f:
|
with open(Path(__file__).parent / "static" / "welcome.txt", "r", encoding="utf-8") as f:
|
||||||
welcome_ascii = f.read()
|
welcome_ascii = f.read()
|
||||||
|
|
||||||
# Create welcome box content
|
# Create welcome box content
|
||||||
|
|
@ -638,91 +638,70 @@ def get_analysis_date():
|
||||||
|
|
||||||
def save_report_to_disk(final_state, ticker: str, save_path: Path):
|
def save_report_to_disk(final_state, ticker: str, save_path: Path):
|
||||||
"""Save complete analysis report to disk with organized subfolders."""
|
"""Save complete analysis report to disk with organized subfolders."""
|
||||||
|
|
||||||
|
def write_section(directory: Path, items: list[tuple[str, str, str]], source: dict) -> list[tuple[str, str]]:
|
||||||
|
"""Write files for present keys; return (label, text) pairs."""
|
||||||
|
parts = []
|
||||||
|
for key, filename, label in items:
|
||||||
|
if text := source.get(key):
|
||||||
|
directory.mkdir(exist_ok=True)
|
||||||
|
(directory / filename).write_text(text, encoding="utf-8")
|
||||||
|
parts.append((label, text))
|
||||||
|
return parts
|
||||||
|
|
||||||
|
def join_parts(parts):
|
||||||
|
return "\n\n".join(f"### {name}\n{text}" for name, text in parts)
|
||||||
|
|
||||||
save_path.mkdir(parents=True, exist_ok=True)
|
save_path.mkdir(parents=True, exist_ok=True)
|
||||||
sections = []
|
sections = []
|
||||||
|
|
||||||
# 1. Analysts
|
# 1. Analysts
|
||||||
analysts_dir = save_path / "1_analysts"
|
parts = write_section(save_path / "1_analysts", [
|
||||||
analyst_parts = []
|
("market_report", "market.md", "Market Analyst"),
|
||||||
if final_state.get("market_report"):
|
("sentiment_report", "sentiment.md", "Social Analyst"),
|
||||||
analysts_dir.mkdir(exist_ok=True)
|
("news_report", "news.md", "News Analyst"),
|
||||||
(analysts_dir / "market.md").write_text(final_state["market_report"])
|
("fundamentals_report", "fundamentals.md", "Fundamentals Analyst"),
|
||||||
analyst_parts.append(("Market Analyst", final_state["market_report"]))
|
], final_state)
|
||||||
if final_state.get("sentiment_report"):
|
if parts:
|
||||||
analysts_dir.mkdir(exist_ok=True)
|
sections.append(f"## I. Analyst Team Reports\n\n{join_parts(parts)}")
|
||||||
(analysts_dir / "sentiment.md").write_text(final_state["sentiment_report"])
|
|
||||||
analyst_parts.append(("Social Analyst", final_state["sentiment_report"]))
|
|
||||||
if final_state.get("news_report"):
|
|
||||||
analysts_dir.mkdir(exist_ok=True)
|
|
||||||
(analysts_dir / "news.md").write_text(final_state["news_report"])
|
|
||||||
analyst_parts.append(("News Analyst", final_state["news_report"]))
|
|
||||||
if final_state.get("fundamentals_report"):
|
|
||||||
analysts_dir.mkdir(exist_ok=True)
|
|
||||||
(analysts_dir / "fundamentals.md").write_text(final_state["fundamentals_report"])
|
|
||||||
analyst_parts.append(("Fundamentals Analyst", final_state["fundamentals_report"]))
|
|
||||||
if analyst_parts:
|
|
||||||
content = "\n\n".join(f"### {name}\n{text}" for name, text in analyst_parts)
|
|
||||||
sections.append(f"## I. Analyst Team Reports\n\n{content}")
|
|
||||||
|
|
||||||
# 2. Research
|
# 2. Research
|
||||||
if final_state.get("investment_debate_state"):
|
if debate := final_state.get("investment_debate_state"):
|
||||||
research_dir = save_path / "2_research"
|
parts = write_section(save_path / "2_research", [
|
||||||
debate = final_state["investment_debate_state"]
|
("bull_history", "bull.md", "Bull Researcher"),
|
||||||
research_parts = []
|
("bear_history", "bear.md", "Bear Researcher"),
|
||||||
if debate.get("bull_history"):
|
("judge_decision", "manager.md", "Research Manager"),
|
||||||
research_dir.mkdir(exist_ok=True)
|
], debate)
|
||||||
(research_dir / "bull.md").write_text(debate["bull_history"])
|
if parts:
|
||||||
research_parts.append(("Bull Researcher", debate["bull_history"]))
|
sections.append(f"## II. Research Team Decision\n\n{join_parts(parts)}")
|
||||||
if debate.get("bear_history"):
|
|
||||||
research_dir.mkdir(exist_ok=True)
|
|
||||||
(research_dir / "bear.md").write_text(debate["bear_history"])
|
|
||||||
research_parts.append(("Bear Researcher", debate["bear_history"]))
|
|
||||||
if debate.get("judge_decision"):
|
|
||||||
research_dir.mkdir(exist_ok=True)
|
|
||||||
(research_dir / "manager.md").write_text(debate["judge_decision"])
|
|
||||||
research_parts.append(("Research Manager", debate["judge_decision"]))
|
|
||||||
if research_parts:
|
|
||||||
content = "\n\n".join(f"### {name}\n{text}" for name, text in research_parts)
|
|
||||||
sections.append(f"## II. Research Team Decision\n\n{content}")
|
|
||||||
|
|
||||||
# 3. Trading
|
# 3. Trading
|
||||||
if final_state.get("trader_investment_plan"):
|
parts = write_section(save_path / "3_trading", [
|
||||||
trading_dir = save_path / "3_trading"
|
("trader_investment_plan", "trader.md", "Trader"),
|
||||||
trading_dir.mkdir(exist_ok=True)
|
], final_state)
|
||||||
(trading_dir / "trader.md").write_text(final_state["trader_investment_plan"])
|
if parts:
|
||||||
sections.append(f"## III. Trading Team Plan\n\n### Trader\n{final_state['trader_investment_plan']}")
|
sections.append(f"## III. Trading Team Plan\n\n{join_parts(parts)}")
|
||||||
|
|
||||||
# 4. Risk Management
|
# 4. Risk Management
|
||||||
if final_state.get("risk_debate_state"):
|
if risk := final_state.get("risk_debate_state"):
|
||||||
risk_dir = save_path / "4_risk"
|
parts = write_section(save_path / "4_risk", [
|
||||||
risk = final_state["risk_debate_state"]
|
("aggressive_history", "aggressive.md", "Aggressive Analyst"),
|
||||||
risk_parts = []
|
("conservative_history","conservative.md", "Conservative Analyst"),
|
||||||
if risk.get("aggressive_history"):
|
("neutral_history", "neutral.md", "Neutral Analyst"),
|
||||||
risk_dir.mkdir(exist_ok=True)
|
], risk)
|
||||||
(risk_dir / "aggressive.md").write_text(risk["aggressive_history"])
|
if parts:
|
||||||
risk_parts.append(("Aggressive Analyst", risk["aggressive_history"]))
|
sections.append(f"## IV. Risk Management Team Decision\n\n{join_parts(parts)}")
|
||||||
if risk.get("conservative_history"):
|
|
||||||
risk_dir.mkdir(exist_ok=True)
|
|
||||||
(risk_dir / "conservative.md").write_text(risk["conservative_history"])
|
|
||||||
risk_parts.append(("Conservative Analyst", risk["conservative_history"]))
|
|
||||||
if risk.get("neutral_history"):
|
|
||||||
risk_dir.mkdir(exist_ok=True)
|
|
||||||
(risk_dir / "neutral.md").write_text(risk["neutral_history"])
|
|
||||||
risk_parts.append(("Neutral Analyst", risk["neutral_history"]))
|
|
||||||
if risk_parts:
|
|
||||||
content = "\n\n".join(f"### {name}\n{text}" for name, text in risk_parts)
|
|
||||||
sections.append(f"## IV. Risk Management Team Decision\n\n{content}")
|
|
||||||
|
|
||||||
# 5. Portfolio Manager
|
# 5. Portfolio Manager
|
||||||
if risk.get("judge_decision"):
|
if decision := risk.get("judge_decision"):
|
||||||
portfolio_dir = save_path / "5_portfolio"
|
portfolio_dir = save_path / "5_portfolio"
|
||||||
portfolio_dir.mkdir(exist_ok=True)
|
portfolio_dir.mkdir(exist_ok=True)
|
||||||
(portfolio_dir / "decision.md").write_text(risk["judge_decision"])
|
(portfolio_dir / "decision.md").write_text(decision, encoding="utf-8")
|
||||||
sections.append(f"## V. Portfolio Manager Decision\n\n### Portfolio Manager\n{risk['judge_decision']}")
|
sections.append(f"## V. Portfolio Manager Decision\n\n### Portfolio Manager\n{decision}")
|
||||||
|
|
||||||
# Write consolidated report
|
# Write consolidated report
|
||||||
header = f"# Trading Analysis Report: {ticker}\n\nGenerated: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
|
header = f"# Trading Analysis Report: {ticker}\n\nGenerated: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
|
||||||
(save_path / "complete_report.md").write_text(header + "\n\n".join(sections))
|
(save_path / "complete_report.md").write_text(header + "\n\n".join(sections), encoding="utf-8")
|
||||||
return save_path / "complete_report.md"
|
return save_path / "complete_report.md"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -980,7 +959,7 @@ def run_analysis():
|
||||||
func(*args, **kwargs)
|
func(*args, **kwargs)
|
||||||
timestamp, message_type, content = obj.messages[-1]
|
timestamp, message_type, content = obj.messages[-1]
|
||||||
content = content.replace("\n", " ") # Replace newlines with spaces
|
content = content.replace("\n", " ") # Replace newlines with spaces
|
||||||
with open(log_file, "a") as f:
|
with open(log_file, "a", encoding="utf-8") as f:
|
||||||
f.write(f"{timestamp} [{message_type}] {content}\n")
|
f.write(f"{timestamp} [{message_type}] {content}\n")
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
@ -991,7 +970,7 @@ def run_analysis():
|
||||||
func(*args, **kwargs)
|
func(*args, **kwargs)
|
||||||
timestamp, tool_name, args = obj.tool_calls[-1]
|
timestamp, tool_name, args = obj.tool_calls[-1]
|
||||||
args_str = ", ".join(f"{k}={v}" for k, v in args.items())
|
args_str = ", ".join(f"{k}={v}" for k, v in args.items())
|
||||||
with open(log_file, "a") as f:
|
with open(log_file, "a", encoding="utf-8") as f:
|
||||||
f.write(f"{timestamp} [Tool Call] {tool_name}({args_str})\n")
|
f.write(f"{timestamp} [Tool Call] {tool_name}({args_str})\n")
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
@ -1005,7 +984,7 @@ def run_analysis():
|
||||||
if content:
|
if content:
|
||||||
file_name = f"{section_name}.md"
|
file_name = f"{section_name}.md"
|
||||||
text = "\n".join(str(item) for item in content) if isinstance(content, list) else content
|
text = "\n".join(str(item) for item in content) if isinstance(content, list) else content
|
||||||
with open(report_dir / file_name, "w") as f:
|
with open(report_dir / file_name, "w", encoding="utf-8") as f:
|
||||||
f.write(text)
|
f.write(text)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue