How Daily Stock Analysis Results Are Visualized and Presented in Text Reports
The daily-stock-analysis project converts AnalysisResult objects into human-readable text reports using Jinja2 templates, generating Markdown, WeChat, or brief summary outputs without embedded graphical charts.
The daily-stock-analysis repository by ZhuLinsen takes a template-driven approach to visualization, transforming raw stock analysis data into formatted text reports instead of traditional charts. Rather than embedding a graphical charting library, the system relies on Jinja2 templating to render analysis results into multiple output formats suitable for command-line interfaces, chat bots, and web displays. This architecture ensures that complex financial signals—buy recommendations, sentiment scores, and market snapshots—remain accessible across platforms that prioritize text-based communication.
The Template-Driven Rendering Architecture
The visualization system centers on src/services/report_renderer.py, which serves as the core rendering engine. This module transforms lists of AnalysisResult objects into polished reports by selecting platform-specific templates and injecting rich contextual data.
Platform-Specific Templates
The system supports three distinct output formats, each optimized for its destination channel:
- Markdown (
templates/report_markdown.j2): Full-featured reports with tables, headings, and emojis for CLI output or GitHub comments. - WeChat (
templates/report_wechat.j2): Compact, line-oriented layouts designed for chat bubble readability. - Brief (
templates/report_brief.j2): Ultra-short summaries for push notifications or Slack alerts.
Template selection occurs dynamically based on the platform parameter passed to the renderer. As implemented in src/services/report_renderer.py:
template_name = f"report_{platform}.j2" # ← src/services/report_renderer.py:L100‑L102
The Rendering Pipeline
The render() function executes a three-stage pipeline to generate the final report:
-
Template Selection: Loads the appropriate Jinja2 template from the configured templates directory using
FileSystemLoader. -
Context Assembly: Builds a comprehensive context dictionary containing:
- Report metadata (
report_date) - Sorted analysis results and enriched data
- Signal counts (
buy_count,sell_count) - Localization labels from
src/report_language.py - Helper functions for text processing (
escape_md,clean_sniper)
context = { "report_date": report_date, "results": sorted_results, "enriched": sorted_enriched, "buy_count": buy_count, "sell_count": sell_count, "labels": labels, "escape_md": _escape_md, "clean_sniper": _clean_sniper_value, ... } # src/services/report_renderer.py:L40‑L62 - Report metadata (
-
Jinja2 Execution: Renders the template with automatic escaping disabled for Markdown compatibility. If the template is missing or Jinja2 cannot be imported, the function returns
Noneto allow fallback to plain-text formatters.env = Environment(loader=FileSystemLoader(str(templates_dir)), autoescape=select_autoescape(default=False)) template = env.get_template(template_name) return template.render(**context) # src/services/report_renderer.py:L65‑L72
Template Structure and Content Layout
Each template follows a consistent structural pattern defined in templates/_macros.j2, ensuring that critical financial data appears regardless of output format.
Markdown Reports
The report_markdown.j2 template produces comprehensive reports containing:
- Header block: Report date with title emojis (e.g.,
🎯) - Executive summary: Total stock count, buy/hold/sell tallies, and one-liner per stock including signal emojis and sentiment scores
- Detailed analysis: For each security:
- Core conclusion (one-sentence investment thesis)
- Intelligence blocks covering earnings outlook, sentiment summaries, risk alerts, and catalysts
- Battle-plan tables displaying ideal buy prices, stop-loss levels, take-profit targets, and position sizing advice
- Market snapshot macros showing price action, moving averages, volume data, and chip analysis
WeChat and Brief Formats
The WeChat template strips down the Markdown formatting to fit mobile chat constraints, using emoji-rich, single-line layouts that render cleanly in message bubbles. The brief template condenses everything into a notification-friendly list showing only stock names, codes, and primary signals—ideal for SMS or push notifications requiring immediate scanning.
Integrating the Report Renderer in Your Workflow
To generate reports programmatically, import the renderer and invoke it with your analysis results:
from src.services.report_renderer import render
from src.analyzer import AnalysisResult
# Assume `analysis_results` is a List[AnalysisResult] produced by the pipeline.
markdown_report = render(
platform="markdown", # "markdown", "wechat", or "brief"
results=analysis_results,
report_date="2026-04-30",
summary_only=False,
)
if markdown_report:
print(markdown_report) # prints a ready-to-copy Markdown report
else:
# Fallback to a simple text formatter
for r in analysis_results:
print(f"{r.name} ({r.code}): {r.operation_advice} – {r.sentiment_score}")
CLI integration (src/main.py) calls this renderer to output reports to stdout or forwards them to notification services. Bot integrations such as src/notification_sender/wechat_sender.py invoke the same function but pass "wechat" as the platform parameter to ensure mobile-optimized formatting. The optional Web UI (apps/dsa-web) bypasses the Jinja2 renderer for its React/Vite frontend, though src/webui_frontend.py handles asset preparation for this alternative visualization path.
Key Files and Configuration
Understanding the visualization pipeline requires familiarity with these specific source files:
| File | Role |
|---|---|
src/services/report_renderer.py |
Core Jinja2 rendering engine (selects template, builds context, handles errors). |
templates/report_markdown.j2 |
Markdown-styled full report with tables and hierarchical headings. |
templates/report_wechat.j2 |
WeChat-friendly version (compact, emoji-rich, line-oriented). |
templates/report_brief.j2 |
Ultra-short summary for push notifications and quick alerts. |
templates/_macros.j2 |
Re-usable Jinja2 macros (e.g., market_snapshot). |
src/report_language.py |
Provides localized label dictionaries and helper functions used in templates. |
src/analyzer.py & src/services/analysis_service.py |
Produce AnalysisResult objects that feed the renderer. |
src/config.py |
Holds report_templates_dir configuration for custom template locations. |
src/webui_frontend.py |
Prepares static front-end assets for the optional React-based web UI. |
Summary
- Text-based visualization: The project uses Jinja2 templates rather than graphical charting libraries to present analysis results.
- Three output formats: Markdown (full detail), WeChat (mobile-optimized), and Brief (notifications) templates serve different consumption channels.
- Centralized rendering:
src/services/report_renderer.pyorchestrates template selection, context assembly, and final text generation. - Rich data context: Templates receive not just raw results, but also enriched data, signal counts, localization labels, and text-processing helpers.
- Flexible deployment: Rendered outputs feed CLI tools, chat bots, and web interfaces through a unified Python API.
Frequently Asked Questions
Does the daily-stock-analysis project support graphical charts?
No. According to the source code, the project intentionally avoids embedded graphical charting libraries. Instead, it relies on text-based Jinja2 templates to visualize data through formatted tables, emojis, and hierarchical Markdown headings that display price data, moving averages, and technical indicators as structured text.
How do I customize the report templates?
Modify the Jinja2 files located in the templates/ directory, or override the report_templates_dir setting in src/config.py to point to a custom location. The renderer uses FileSystemLoader to discover templates at runtime, allowing you to swap the entire template suite without changing core logic.
What platforms can receive these reports?
The system supports three primary destinations: command-line terminals (via the Markdown template), WeChat chat bots (via src/notification_sender/wechat_sender.py using the WeChat template), and notification systems (via the Brief template for Slack, Telegram, or SMS-like alerts). Additionally, the React/Vite web frontend in apps/dsa-web presents the same data through a separate browser-based interface.
How does the web UI display results differently?
While the Jinja2 templates generate static text for bots and terminals, the Web UI (apps/dsa-web) renders the same AnalysisResult data through a React-based frontend. The src/webui_frontend.py module handles static asset preparation for this interface, which provides interactive browsing rather than the linear text format produced by the template renderer.
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 →