Skip to content

Commit 486dab8

Browse files
turtlequantclaude
andcommitted
Showcase: add intro page, fix gate display for exclude_industry
- New intro tab as landing page (what are operators, frameworks, industry routing, verification) - Interactive links to other tabs (switchTab function) - Flow diagram visualization for DAG concept - Industry badge colors (bank/manufacturing/consumer/tech) - Fix gate display: handle both only_industry and exclude_industry - Fix: intro page is now default active tab Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fd9c34f commit 486dab8

3 files changed

Lines changed: 235 additions & 15 deletions

File tree

docs/site/css/style.css

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,3 +647,143 @@ body {
647647
.strategy-header { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
648648
.results-grid { grid-template-columns: repeat(2, 1fr); }
649649
}
650+
651+
/* ---- Intro Page ---- */
652+
.intro-hero {
653+
text-align: center;
654+
padding: 2rem 1rem 1.5rem;
655+
}
656+
657+
.intro-hero h2 {
658+
font-size: 1.6rem;
659+
color: var(--text-primary);
660+
margin-bottom: 0.5rem;
661+
}
662+
663+
.intro-subtitle {
664+
font-size: 1rem;
665+
color: var(--text-secondary);
666+
max-width: 600px;
667+
margin: 0 auto;
668+
line-height: 1.6;
669+
}
670+
671+
.intro-grid {
672+
display: grid;
673+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
674+
gap: 1.25rem;
675+
padding: 0 1.5rem 2rem;
676+
}
677+
678+
.intro-card {
679+
background: var(--bg-card);
680+
border: 1px solid var(--border);
681+
border-radius: 8px;
682+
padding: 1.25rem;
683+
}
684+
685+
.intro-card h3 {
686+
font-size: 1.1rem;
687+
margin-bottom: 0.5rem;
688+
color: var(--text-primary);
689+
}
690+
691+
.intro-card p {
692+
font-size: 0.9rem;
693+
color: var(--text-secondary);
694+
line-height: 1.5;
695+
margin-bottom: 0.75rem;
696+
}
697+
698+
.intro-example {
699+
background: var(--bg-main);
700+
border-radius: 6px;
701+
padding: 0.75rem;
702+
margin-bottom: 0.75rem;
703+
}
704+
705+
.intro-example-title {
706+
font-size: 0.75rem;
707+
color: var(--text-muted);
708+
margin-bottom: 0.4rem;
709+
}
710+
711+
.intro-example-items {
712+
display: flex;
713+
flex-wrap: wrap;
714+
gap: 0.4rem;
715+
}
716+
717+
.intro-tag {
718+
font-size: 0.8rem;
719+
padding: 0.2rem 0.5rem;
720+
border-radius: 4px;
721+
background: var(--accent-light);
722+
color: var(--accent);
723+
}
724+
725+
.intro-tag.bank { background: #e8f5e9; color: #2e7d32; }
726+
.intro-tag.mfg { background: #fff3e0; color: #e65100; }
727+
.intro-tag.consumer { background: #fce4ec; color: #c62828; }
728+
.intro-tag.tech { background: #e3f2fd; color: #1565c0; }
729+
730+
.intro-flow {
731+
display: flex;
732+
align-items: center;
733+
flex-wrap: wrap;
734+
gap: 0.3rem;
735+
margin-bottom: 0.75rem;
736+
padding: 0.5rem;
737+
background: var(--bg-main);
738+
border-radius: 6px;
739+
}
740+
741+
.flow-node {
742+
font-size: 0.8rem;
743+
padding: 0.3rem 0.6rem;
744+
background: var(--accent-light);
745+
color: var(--accent);
746+
border-radius: 4px;
747+
white-space: nowrap;
748+
}
749+
750+
.flow-node.highlight {
751+
background: var(--accent);
752+
color: #fff;
753+
font-weight: 600;
754+
}
755+
756+
.flow-arrow {
757+
color: var(--text-muted);
758+
font-size: 0.9rem;
759+
}
760+
761+
.intro-stats {
762+
display: flex;
763+
justify-content: space-around;
764+
text-align: center;
765+
padding: 0.75rem;
766+
background: var(--bg-main);
767+
border-radius: 6px;
768+
margin-bottom: 0.75rem;
769+
}
770+
771+
.intro-stats strong {
772+
font-size: 1.3rem;
773+
color: var(--accent);
774+
}
775+
776+
.intro-stats span {
777+
font-size: 0.75rem;
778+
color: var(--text-muted);
779+
}
780+
781+
.intro-link {
782+
font-size: 0.85rem;
783+
color: var(--accent);
784+
}
785+
786+
.intro-link a {
787+
color: var(--accent);
788+
text-decoration: underline;
789+
}

docs/site/index.html

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,88 @@ <h1>thesis-backtester</h1>
2020

2121
<!-- ======== Tab Bar ======== -->
2222
<nav class="tab-bar">
23-
<button class="tab-btn active" data-tab="tab-operators">算子库</button>
23+
<button class="tab-btn active" data-tab="tab-intro">介绍</button>
24+
<button class="tab-btn" data-tab="tab-operators">算子库</button>
2425
<button class="tab-btn" data-tab="tab-strategies">分析框架</button>
2526
<button class="tab-btn" data-tab="tab-backtest">回测成果</button>
2627
<button class="tab-btn" data-tab="tab-contribute">参与贡献</button>
2728
</nav>
2829

30+
<!-- ======== Tab: 介绍 ======== -->
31+
<section id="tab-intro" class="tab-panel active">
32+
33+
<div class="intro-hero">
34+
<h2>把定性投资判断变成可回测对象</h2>
35+
<p class="intro-subtitle">不是让 AI 推荐股票,而是让 AI 按专业研究框架逐步分析——然后用历史数据验证这套方法论是否有效。</p>
36+
</div>
37+
38+
<div class="intro-grid">
39+
40+
<div class="intro-card">
41+
<h3>🧩 什么是算子</h3>
42+
<p>算子是分析的<strong>最小单元</strong>——每个算子定义一个分析视角,用 Markdown 编写,告诉 AI 看什么、怎么看、输出什么。</p>
43+
<div class="intro-example">
44+
<div class="intro-example-title">示例算子</div>
45+
<div class="intro-example-items">
46+
<span class="intro-tag">负债结构拆解</span>
47+
<span class="intro-tag">PE 陷阱检测</span>
48+
<span class="intro-tag">管理层诚信评估</span>
49+
<span class="intro-tag">AI 冲击风险</span>
50+
</div>
51+
</div>
52+
<p class="intro-link">→ 查看 <a href="#" onclick="switchTab('tab-operators');return false;">全部 37 个算子</a></p>
53+
</div>
54+
55+
<div class="intro-card">
56+
<h3>🔗 什么是分析框架</h3>
57+
<p>框架把多个算子<strong>编排成有依赖关系的章节</strong>(DAG),后一步建立在前一步结论之上——不是一次性回答,是链式推理。</p>
58+
<div class="intro-flow">
59+
<span class="flow-node">数据核查</span>
60+
<span class="flow-arrow"></span>
61+
<span class="flow-node">基本面</span>
62+
<span class="flow-arrow"></span>
63+
<span class="flow-node">现金流</span>
64+
<span class="flow-arrow"></span>
65+
<span class="flow-node">估值</span>
66+
<span class="flow-arrow"></span>
67+
<span class="flow-node">压力测试</span>
68+
<span class="flow-arrow"></span>
69+
<span class="flow-node highlight">综合研判</span>
70+
</div>
71+
<p class="intro-link">→ 查看 <a href="#" onclick="switchTab('tab-strategies');return false;">5 个预设框架</a></p>
72+
</div>
73+
74+
<div class="intro-card">
75+
<h3>🏭 行业适配</h3>
76+
<p>不同行业用不同的分析方法——银行不看 FCF,制造业看产能周期,消费看品牌护城河。系统<strong>自动识别行业并路由到专用算子</strong></p>
77+
<div class="intro-example">
78+
<div class="intro-example-title">行业专用算子</div>
79+
<div class="intro-example-items">
80+
<span class="intro-tag bank">🏦 银行 × 4</span>
81+
<span class="intro-tag mfg">🏭 制造 × 3</span>
82+
<span class="intro-tag consumer">🍔 消费 × 2</span>
83+
<span class="intro-tag tech">💻 科技 × 2</span>
84+
</div>
85+
</div>
86+
</div>
87+
88+
<div class="intro-card">
89+
<h3>✅ 验证闭环</h3>
90+
<p>不只是分析,还能<strong>回测验证</strong>——用历史数据证明分析方法论是否有效。这是和"直接问 AI"的根本区别。</p>
91+
<div class="intro-stats">
92+
<div><strong>+7.1pp</strong><br><span>Alpha vs 沪深300</span></div>
93+
<div><strong>65%</strong><br><span>买入信号胜率</span></div>
94+
<div><strong>73%</strong><br><span>回避信号准确率</span></div>
95+
</div>
96+
<p class="intro-link">→ 查看 <a href="#" onclick="switchTab('tab-backtest');return false;">完整回测结果</a></p>
97+
</div>
98+
99+
</div>
100+
101+
</section>
102+
29103
<!-- ======== Tab: 算子库 ======== -->
30-
<section id="tab-operators" class="tab-panel active">
104+
<section id="tab-operators" class="tab-panel">
31105
<p style="padding:2rem;color:var(--text-muted);">Loading...</p>
32106
</section>
33107

docs/site/js/app.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,20 @@
3535
function initTabs() {
3636
const btns = document.querySelectorAll(".tab-btn");
3737
btns.forEach((btn) => {
38-
btn.addEventListener("click", () => {
39-
const target = btn.dataset.tab;
40-
btns.forEach((b) => b.classList.remove("active"));
41-
btn.classList.add("active");
42-
document.querySelectorAll(".tab-panel").forEach((p) => {
43-
p.classList.toggle("active", p.id === target);
44-
});
45-
});
38+
btn.addEventListener("click", () => switchTab(btn.dataset.tab));
4639
});
4740
}
4841

42+
// Global tab switch (also called from intro page links)
43+
window.switchTab = function(tabId) {
44+
document.querySelectorAll(".tab-btn").forEach((b) => {
45+
b.classList.toggle("active", b.dataset.tab === tabId);
46+
});
47+
document.querySelectorAll(".tab-panel").forEach((p) => {
48+
p.classList.toggle("active", p.id === tabId);
49+
});
50+
};
51+
4952
// ---- Load JSON data ----
5053
function loadData() {
5154
const base = getBasePath();
@@ -173,11 +176,14 @@
173176
}
174177

175178
// Gate
176-
if (op.gate) {
177-
const gateText = op.gate.only_industry
178-
? "仅限: " + op.gate.only_industry.join(", ")
179-
: JSON.stringify(op.gate);
180-
html += `<div class="gate-badge">${esc(gateText)}</div>`;
179+
if (op.gate && Object.keys(op.gate).length) {
180+
let gateText = "";
181+
if (op.gate.only_industry) {
182+
gateText = "仅限: " + op.gate.only_industry.join(", ");
183+
} else if (op.gate.exclude_industry) {
184+
gateText = "排除: " + op.gate.exclude_industry.join(", ");
185+
}
186+
if (gateText) html += `<div class="gate-badge">${esc(gateText)}</div>`;
181187
}
182188

183189
// Expandable details

0 commit comments

Comments
 (0)