Skill: dcsight/stock-scheme-tracker (version 1.1.0)

--- name: stock-scheme-tracker description: Save and track per-stock investment strategies with entry/exit conditions. Triggers on "保存策略", "跟踪策略", "检查股票策略", "核对建仓条件", "scheme", "stock strategy". Two modes: save (extract strategy from analysis → append to tracking file) and check (evaluate all conditions against user-provided market data, output actionable recommendations). version: 1.1.0 requires: anyBins: - python3 - python env: - STOCK_SCHEME_PATH primaryEnv: STOCK_SCHEME_PATH emoji: 📈 --- # Stock Strategy Tracker 📈 Save per-stock investment strategies extracted from analysis reports into a persistent tracking file, then periodically check if market conditions have triggered any entry, stop-loss, or take-profit conditions. Provide actionable next-step recommendations. --- ## Quick Start ### Install Copy the skill directory to your agent's skills folder: ```bash # OpenClaw cp -r stock-scheme-tracker ~/.openclaw/skills/ # WorkBuddy cp -r stock-scheme-tracker ~/.workbuddy/skills/ # Claude Code (project-level) cp -r stock-scheme-tracker .claude/skills/ ``` ### Configure Set the environment variable to point to your tracking file: ```bash # Add to your shell profile (~/.zshrc, ~/.bashrc, etc.) export STOCK_SCHEME_PATH="$HOME/Documents/stock/scheme.md" ``` Or configure via your agent's config: ```json // OpenClaw: ~/.openclaw/openclaw.json { "skills": { "entries": { "stock-scheme-tracker": { "env": { "STOCK_SCHEME_PATH": "~/Documents/stock/scheme.md" } } } } } ``` If `STOCK_SCHEME_PATH` is not set, the skill defaults to `~/Documents/stock/scheme.md`. ### Verify ```bash echo $STOCK_SCHEME_PATH # Should output your configured path ``` --- ## Workflow Decision Tree 1. **Determine mode**: Did the user just complete an analysis (→ save mode), or do they want to review existing strategies (→ check mode)? 2. **Save mode**: Extract strategy elements from the analysis, format them, and append to the scheme file. 3. **Check mode**: Read all strategies from the scheme file, fetch latest market data for each stock, evaluate every condition, and output a summary with next-step recommendations. ## Save Mode ### When to Use - Immediately after completing any stock investment analysis (e.g., DCF valuation, Buffett framework, technical analysis, or any investment report). - The user says "保存策略", "把策略记下来", "跟踪这个票", "save strategy", "track this stock", or any variant. ### Extraction Rules From the analysis text, extract these fields. If a field is not mentioned, mark it as "未明确" rather than fabricating. | Field | Description | Example | |-------|-------------|---------| | `stock_code` | Stock code | 688111 | | `stock_name` | Stock name | 金山办公 | | `analysis_date` | Analysis date | 2026-04-22 | | `recommendation` | Core recommendation | 建议观察，等待更好入场时机 | | `entry_conditions` | Entry conditions (price/valuation/events) | PE≤45-50；股价180-200元 | | `stop_loss` | Stop-loss condition | 跌破前期低点或技术位 | | `target_price` | Target price / take-profit range | 机构目标价400元 | | `exit_signals` | Exit signals (assumption invalidation) | 月活环比下滑；市场份额跌破80% | | `position_size` | Suggested position size | 轻仓试探 | | `time_horizon` | Holding period | 3年 | | `key_assumptions` | Key assumptions | AI商业化持续推进 | | `open_questions` | Open questions | WPS AI付费转化率是多少 | | `current_price` | Price at analysis | ~250元 | | `current_pe` | PE at analysis | 62.46 | | `market_cap` | Market cap at analysis | 1147亿 | ### Output Format Append a new section to the scheme file (path from `STOCK_SCHEME_PATH` env var) using the exact format defined in `references/scheme_format.md`. Use Markdown table for entry conditions so they can be machine-parsed later. ### Important - If the stock already exists in the scheme file, update the existing section rather than duplicating it. Preserve historical entries by appending a date-stamped update note. - After saving, briefly confirm to the user what was saved. ## Check Mode ### When to Use - User says "检查策略", "核对建仓条件", "看看哪些票可以买", "策略跟踪", "check strategy", or any variant. - Can be invoked standalone without any prior analysis. ### Steps 1. Read the scheme file (path from `STOCK_SCHEME_PATH` env var). 2. For each tracked stock, fetch the latest market data (price, PE, key metrics). Use available financial data tools or web search. 3. Evaluate each `entry_conditions` row: compare current value vs target value. 4. Mark each condition as `✅ 已触发`, `❌ 未触发`, or `⚠️ 接近触发`. 5. Check if any `exit_signals` have occurred. 6. Output a clean summary table and specific next-step recommendations per stock. ### Output Format ```markdown ## 策略核对报告 — 2026-04-23 ### 金山办公 (688111) | 条件类型 | 目标 | 当前 | 状态 | |----------|------|------|------| | PE建仓 | ≤45-50 | 62.46 | ❌ 未触发 | | 股价建仓 | 180-200 | 250 | ❌ 未触发 | **建议**: 继续观察，等待PE回落至50以下或股价跌破200再考虑建仓。 ``` ## Core Tasks Here are natural-language prompts that trigger this skill: - "我刚分析完金山办公，帮我保存策略" - "检查一下我跟踪的股票，哪些条件触发了" - "核对建仓条件" - "跟踪策略" - "把刚才的分析结论记录到策略跟踪文件" - "I just finished analyzing Apple, save the strategy" - "Check all my stock strategies" ## Environment Variable Contract | Variable | Purpose | Required | Set Via | |----------|---------|----------|---------| | `STOCK_SCHEME_PATH` | Path to the strategy tracking Markdown file | No (defaults to `~/Documents/stock/scheme.md`) | Shell profile or agent config | ## Security & Guardrails 1. **Local file only**: Both scripts only read/write a local Markdown file specified by `STOCK_SCHEME_PATH`. No network calls are made by `save_scheme.py` or `check_scheme.py`. 2. **Market data source**: During check mode, market data is fetched via the agent's available financial data tools or web search. This involves querying public market quotes (stock codes and metric names only) — no private strategy content or position details are transmitted. 3. **No trading execution**: This skill provides analysis and recommendations only. It does NOT execute any trades or connect to brokerage accounts. 4. **No credential collection**: This skill only requires `STOCK_SCHEME_PATH` (a file path). It never asks for API keys, tokens, passwords, or any other credentials. 5. **Data integrity**: The save script uses atomic write patterns and preserves historical entries. Existing stock sections are updated in-place rather than overwritten. 6. **Path validation**: The scripts validate that `STOCK_SCHEME_PATH` points to a `.md` file and will create parent directories if needed. ## Troubleshooting | Problem | Cause | Fix | |---------|-------|-----| | "No such file or directory" | `STOCK_SCHEME_PATH` not set or points to invalid path | Set env var or rely on default `~/Documents/stock/scheme.md` | | Strategy not found during check | Scheme file is empty or format is corrupted | Verify the file uses the format in `references/scheme_format.md` | | Duplicate entries for same stock | Save mode ran twice without updating | The script auto-detects duplicates; if using manual edit, follow the update convention | | Chinese characters garbled in output | Terminal encoding mismatch | Ensure your terminal uses UTF-8 (`export LANG=en_US.UTF-8` or `zh_CN.UTF-8`) | ## Release Notes - **1.1.0** — Fixed metadata consistency (top-level requires/env), added credential collection disclaimer, clarified market data query scope in security section. - **1.0.0** — Initial release. Save and check modes with Markdown-based tracking file. ## Links - [Scheme Format Reference](references/scheme_format.md) - [Save Script](scripts/save_scheme.py) - [Check Script](scripts/check_scheme.py) ## Publisher * **Publisher:** @dc * **License:** MIT-0 * **Source:** https://github.com/dc-stock/stock-scheme-tracker

path: scripts/save_scheme.py
content: #!/usr/bin/env python3
"""
Save stock strategy to the scheme tracking file.

The file path is determined by:
  1. --path argument
  2. STOCK_SCHEME_PATH environment variable
  3. Default: ~/Documents/stock/scheme.md

Reads JSON from stdin, formats it, and appends/updates the scheme file.

Usage:
    echo '{"stock_code":"688111","stock_name":"金山办公",...}' | python3 save_scheme.py [--path /path/to/scheme.md]
"""

import sys
import json
import os
import re
import argparse
from datetime import datetime
from pathlib import Path


def get_scheme_path():
    """Resolve scheme file path from arg, env var, or default."""
    env_path = os.environ.get("STOCK_SCHEME_PATH", "")
    if env_path:
        return Path(env_path).expanduser()
    return Path.home() / "Documents" / "stock" / "scheme.md"


def ensure_file(scheme_path):
    """Ensure scheme.md exists with proper header."""
    scheme_path.parent.mkdir(parents=True, exist_ok=True)
    if not scheme_path.exists():
        header = f"""# 股票策略跟踪

> 最后更新：{datetime.now().strftime('%Y-%m-%d')}

"""
        scheme_path.write_text(header, encoding="utf-8")


def parse_entry_conditions(conditions):
    """Convert entry_conditions list to markdown table rows."""
    rows = []
    for cond in conditions:
        name = cond.get("name", "")
        target = cond.get("target", "")
        current = cond.get("current", "")
        triggered = cond.get("triggered", "❌ 未触发")
        note = cond.get("note", "")
        rows.append(f"| {name} | {target} | {current} | {triggered} | {note} |")
    return "\n".join(rows)


def format_strategy(data):
    """Format a single strategy entry as markdown."""
    stock_code = data.get("stock_code", "")
    stock_name = data.get("stock_name", "")
    analysis_date = data.get("analysis_date", datetime.now().strftime("%Y-%m-%d"))
    status = data.get("status", "观察中")
    conclusion = data.get("conclusion", "")
    current_price = data.get("current_price", "")
    current_pe = data.get("current_pe", "")
    market_cap = data.get("market_cap", "")

    # Entry conditions table
    entry_conditions = data.get("entry_conditions", [])
    if entry_conditions:
        entry_table = parse_entry_conditions(entry_conditions)
    else:
        entry_table = "| 条件 | 目标值 | 当前值 | 是否触发 | 备注 |\n|------|--------|--------|----------|------|"

    # Optional fields
    stop_loss = data.get("stop_loss", "")
    target_price = data.get("target_price", "")
    exit_signals = data.get("exit_signals", [])
    key_assumptions = data.get("key_assumptions", [])
    open_questions = data.get("open_questions", [])
    position_size = data.get("position_size", "")
    time_horizon = data.get("time_horizon", "")

    # Build markdown
    lines = [
        f"## {stock_name} ({stock_code})",
        "",
        f"- **分析日期**: {analysis_date}",
        f"- **当前状态**: {status}",
        f"- **分析结论**: {conclusion}",
    ]

    if current_price:
        lines.append(f"- **分析时股价**: {current_price}")
    if current_pe:
        lines.append(f"- **分析时PE**: {current_pe}")
    if market_cap:
        lines.append(f"- **分析时市值**: {market_cap}")
    if position_size:
        lines.append(f"- **建议仓位**: {position_size}")
    if time_horizon:
        lines.append(f"- **持有周期**: {time_horizon}")

    lines.extend([
        "",
        "### 建仓条件",
        "",
        "| 条件 | 目标值 | 当前值 | 是否触发 | 备注 |",
        "|------|--------|--------|----------|------|",
    ])

    if entry_conditions:
        lines.append(entry_table)
    else:
        lines.append("| 未明确 | - | - | - | - |")

    if stop_loss:
        lines.extend(["", "### 止损线", "", f"- {stop_loss}"])

    if target_price:
        lines.extend(["", "### 目标价", "", f"- {target_price}"])

    if exit_signals:
        lines.extend(["", "### 退出信号", ""])
        for i, sig in enumerate(exit_signals, 1):
            lines.append(f"{i}. {sig}")

    if key_assumptions:
        lines.extend(["", "### 核心假设", ""])
        for i, assumption in enumerate(key_assumptions, 1):
            lines.append(f"{i}. {assumption}")

    if open_questions:
        lines.extend(["", "### 未解问题", ""])
        for i, q in enumerate(open_questions, 1):
            lines.append(f"{i}. {q}")

    # History tracking
    lines.extend([
        "",
        "### 历史跟踪",
        "",
        f"- {analysis_date}: 分析完成，建议{status}",
    ])

    return "\n".join(lines) + "\n\n"


def extract_existing_section(content, stock_code):
    """Extract existing section for a stock if it exists."""
    pattern = rf"(## [^\n]*\s*\({re.escape(stock_code)}\).*?)(?=\n## |\Z)"
    match = re.search(pattern, content, re.DOTALL)
    if match:
        return match.group(1), match.start(), match.end()
    return None, -1, -1


def update_history(content, start, end, new_entry):
    """Replace old section with updated entry, preserving history."""
    old_section = content[start:end]

    # Extract existing history
    history_match = re.search(r"### 历史跟踪\n\n(.*?)(?=\n### |\Z)", old_section, re.DOTALL)
    existing_history = []
    if history_match:
        existing_history = [line.strip("- ").strip() for line in history_match.group(1).strip().split("\n") if line.strip()]

    # Add new history line
    today = datetime.now().strftime("%Y-%m-%d")
    new_history_line = f"- {today}: 策略更新"

    # Prepend new history to existing
    all_history = [new_history_line] + existing_history
    history_block = "### 历史跟踪\n\n" + "\n".join(all_history)

    # Replace history section in new_entry
    new_entry_with_history = re.sub(
        r"### 历史跟踪\n\n.*?\n\n",
        history_block + "\n\n",
        new_entry,
        flags=re.DOTALL
    )

    return content[:start] + new_entry_with_history + content[end:]


def main():
    parser = argparse.ArgumentParser(description="Save stock strategy to scheme file")
    parser.add_argument("--path", type=str, default=None, help="Path to scheme.md (overrides STOCK_SCHEME_PATH env var)")
    args = parser.parse_args()

    scheme_path = Path(args.path) if args.path else get_scheme_path()

    # Read JSON from stdin
    raw = sys.stdin.read().strip()
    if not raw:
        print("Error: No JSON input provided", file=sys.stderr)
        sys.exit(1)

    try:
        data = json.loads(raw)
    except json.JSONDecodeError as e:
        print(f"Error: Invalid JSON: {e}", file=sys.stderr)
        sys.exit(1)

    ensure_file(scheme_path)
    content = scheme_path.read_text(encoding="utf-8")

    stock_code = data.get("stock_code", "")
    if not stock_code:
        print("Error: stock_code is required", file=sys.stderr)
        sys.exit(1)

    new_entry = format_strategy(data)

    existing, start, end = extract_existing_section(content, stock_code)
    if existing:
        # Update existing section
        content = update_history(content, start, end, new_entry)
        action = "updated"
    else:
        # Append new section
        content = content.rstrip() + "\n\n" + new_entry
        action = "added"

    # Update last_updated timestamp
    today = datetime.now().strftime("%Y-%m-%d")
    content = re.sub(
        r"> 最后更新：\d{4}-\d{2}-\d{2}",
        f"> 最后更新：{today}",
        content
    )

    scheme_path.write_text(content, encoding="utf-8")
    print(f"Successfully {action} strategy for {data.get('stock_name', stock_code)} ({stock_code})")


if __name__ == "__main__":
    main()


path: scripts/check_scheme.py
content: #!/usr/bin/env python3
"""
Check stock strategies in the scheme tracking file.

The file path is determined by:
  1. --path argument
  2. STOCK_SCHEME_PATH environment variable
  3. Default: ~/Documents/stock/scheme.md

Two modes:
1. Parse mode: Extract all tracked stocks and their conditions as JSON
   Usage: python3 check_scheme.py --parse [--path /path/to/scheme.md]

2. Report mode: Generate markdown report from JSON with latest market data
   Usage: echo '{"stocks":[...]}' | python3 check_scheme.py --report

The JSON format for report mode:
{
  "stocks": [
    {
      "code": "688111",
      "name": "金山办公",
      "status": "观察中",
      "conditions": [
        {"name": "PE(TTM)", "target": "≤45-50", "current": "62.46", "triggered": "❌ 未触发", "note": "3年分位点7.33%"}
      ],
      "stop_loss": "跌破前期低点",
      "target_price": "机构目标价400元",
      "exit_signals": ["月活环比下滑"],
      "recommendation": "继续观察，等待PE回落至50以下"
    }
  ]
}
"""

import sys
import json
import re
import os
import argparse
from pathlib import Path
from datetime import datetime


def get_scheme_path():
    """Resolve scheme file path from arg, env var, or default."""
    env_path = os.environ.get("STOCK_SCHEME_PATH", "")
    if env_path:
        return Path(env_path).expanduser()
    return Path.home() / "Documents" / "stock" / "scheme.md"


def parse_scheme(scheme_path):
    """Parse scheme.md and return list of stock strategies."""
    if not scheme_path.exists():
        return []

    content = scheme_path.read_text(encoding="utf-8")

    # Split by stock sections (## Stock Name (CODE))
    pattern = r"##\s+(.+?)\s*\(([A-Z0-9]+)\)\n"
    matches = list(re.finditer(pattern, content))

    stocks = []
    for i, match in enumerate(matches):
        name = match.group(1).strip()
        code = match.group(2).strip()
        start = match.start()
        end = matches[i + 1].start() if i + 1 < len(matches) else len(content)
        section = content[start:end]

        # Parse status
        status_match = re.search(r"\*\*当前状态\*\*:\s*(.+)", section)
        status = status_match.group(1).strip() if status_match else "未知"

        # Parse entry conditions table
        conditions = []
        table_match = re.search(
            r"###\s+建仓条件\n\n\|[^\n]+\|\n\|[-\s|]+\|\n(.*?)(?=\n###|\n\n##|\Z)",
            section,
            re.DOTALL
        )
        if table_match:
            table_body = table_match.group(1).strip()
            for line in table_body.split("\n"):
                line = line.strip()
                if not line or line.startswith("|") is False:
                    continue
                cols = [c.strip() for c in line.strip("|").split("|")]
                if len(cols) >= 4:
                    conditions.append({
                        "name": cols[0],
                        "target": cols[1],
                        "current": cols[2],
                        "triggered": cols[3] if len(cols) > 3 else "",
                        "note": cols[4] if len(cols) > 4 else ""
                    })

        # Parse stop loss
        stop_loss = ""
        sl_match = re.search(r"###\s+止损线\n\n-?\s*(.+?)(?=\n###|\n\n##|\Z)", section, re.DOTALL)
        if sl_match:
            stop_loss = sl_match.group(1).strip()

        # Parse target price
        target_price = ""
        tp_match = re.search(r"###\s+目标价\n\n-?\s*(.+?)(?=\n###|\n\n##|\Z)", section, re.DOTALL)
        if tp_match:
            target_price = tp_match.group(1).strip()

        # Parse exit signals
        exit_signals = []
        es_match = re.search(r"###\s+退出信号\n\n(.*?)(?=\n###|\n\n##|\Z)", section, re.DOTALL)
        if es_match:
            for line in es_match.group(1).strip().split("\n"):
                line = line.strip()
                if line and (line[0].isdigit() or line.startswith("-")):
                    exit_signals.append(re.sub(r"^\d+\.\s*", "", line).strip("- "))

        stocks.append({
            "code": code,
            "name": name,
            "status": status,
            "conditions": conditions,
            "stop_loss": stop_loss,
            "target_price": target_price,
            "exit_signals": exit_signals
        })

    return stocks


def generate_report(data):
    """Generate markdown report from JSON with latest market data."""
    stocks = data.get("stocks", [])
    today = datetime.now().strftime("%Y-%m-%d")

    lines = [
        f"# 策略核对报告 — {today}",
        "",
        f"> 共跟踪 {len(stocks)} 只股票",
        "",
    ]

    triggered_count = 0
    for stock in stocks:
        code = stock.get("code", "")
        name = stock.get("name", "")
        status = stock.get("status", "")
        conditions = stock.get("conditions", [])
        recommendation = stock.get("recommendation", "")
        stop_loss = stock.get("stop_loss", "")
        target_price = stock.get("target_price", "")
        exit_signals = stock.get("exit_signals", [])

        lines.extend([
            f"## {name} ({code})",
            "",
            f"- **当前状态**: {status}",
        ])

        if conditions:
            lines.extend([
                "",
                "| 条件 | 目标 | 当前 | 状态 | 备注 |",
                "|------|------|------|------|------|",
            ])
            for cond in conditions:
                cname = cond.get("name", "")
                target = cond.get("target", "")
                current = cond.get("current", "")
                triggered = cond.get("triggered", "")
                note = cond.get("note", "")
                lines.append(f"| {cname} | {target} | {current} | {triggered} | {note} |")
                if "✅" in triggered or "已触发" in triggered:
                    triggered_count += 1

        if stop_loss:
            lines.extend(["", f"**止损线**: {stop_loss}"])
        if target_price:
            lines.extend(["", f"**目标价**: {target_price}"])
        if exit_signals:
            lines.extend(["", "**退出信号**:", ""])
            for sig in exit_signals:
                lines.append(f"- {sig}")

        if recommendation:
            lines.extend(["", f"**建议**: {recommendation}"])

        lines.append("")

    # Summary
    lines.insert(4, f"> 其中 {triggered_count} 个条件已触发")
    lines.insert(5, "")

    return "\n".join(lines)


def main():
    parser = argparse.ArgumentParser(description="Check stock strategies")
    parser.add_argument("--parse", action="store_true", help="Parse scheme.md and output JSON")
    parser.add_argument("--report", action="store_true", help="Generate report from JSON input")
    parser.add_argument("--path", type=str, default=None, help="Path to scheme.md (overrides STOCK_SCHEME_PATH env var)")
    args = parser.parse_args()

    scheme_path = Path(args.path) if args.path else get_scheme_path()

    if args.parse:
        stocks = parse_scheme(scheme_path)
        print(json.dumps({"stocks": stocks}, ensure_ascii=False, indent=2))
        return

    if args.report:
        raw = sys.stdin.read().strip()
        if not raw:
            print("Error: No JSON input for report mode", file=sys.stderr)
            sys.exit(1)
        try:
            data = json.loads(raw)
        except json.JSONDecodeError as e:
            print(f"Error: Invalid JSON: {e}", file=sys.stderr)
            sys.exit(1)
        report = generate_report(data)
        print(report)
        return

    # Default: just parse and print summary
    stocks = parse_scheme(scheme_path)
    if not stocks:
        print("没有找到任何股票策略。请先使用 save 模式保存策略。")
        return

    print(f"共跟踪 {len(stocks)} 只股票:")
    for s in stocks:
        print(f"  - {s['name']} ({s['code']}): {s['status']}, {len(s['conditions'])} 个条件")


if __name__ == "__main__":
    main()


path: references/scheme_format.md
content: # scheme.md 格式规范

这是策略跟踪文件（`STOCK_SCHEME_PATH` 环境变量指向的文件）的标准格式。所有条目必须遵循此格式，以便脚本能够正确解析。

> 默认路径：`~/Documents/stock/scheme.md`（可通过 `STOCK_SCHEME_PATH` 环境变量覆盖）

## 文件头部

文件以一级标题开头：

```markdown
# 股票策略跟踪

> 最后更新：YYYY-MM-DD
```

## 个股条目格式

每个股票一个二级标题，格式固定为 `## 股票名称 (股票代码)`。

```markdown
## 金山办公 (688111)

- **分析日期**: 2026-04-22
- **当前状态**: 观察中 / 已建仓 / 已清仓 / 暂停跟踪
- **分析结论**: 一句话核心建议

### 建仓条件

| 条件 | 目标值 | 当前值 | 是否触发 | 备注 |
|------|--------|--------|----------|------|
| PE(TTM) | ≤45-50 | 62.46 | ❌ 未触发 | 3年分位点7.33% |
| 股价 | 180-200元 | 250元 | ❌ 未触发 | 需大盘配合 |

### 止损线

- 跌破前期低点
- 月活环比下滑

### 目标价

- 机构目标价：400元

### 退出信号

1. 月活环比下滑
2. 政府IT支出削减
3. 市场份额跌破80%

### 核心假设

- AI商业化持续推进，付费转化率提升

### 未解问题

- WPS AI付费转化率具体是多少？
- 海外扩张进度如何？

### 历史跟踪

- 2026-04-22: 分析完成，PE 62.46，股价250元，建议观察
```

## 字段说明

| 字段 | 必填 | 说明 |
|------|------|------|
| 分析日期 | 是 | 完成分析的日期 |
| 当前状态 | 是 | 观察中/已建仓/已清仓/暂停跟踪 |
| 分析结论 | 是 | 一句话核心建议 |
| 建仓条件 | 是 | 表格形式，必须包含"条件、目标值、当前值、是否触发、备注"列 |
| 止损线 | 否 | 触发止损的具体条件 |
| 目标价 | 否 | 预期目标价位 |
| 退出信号 | 否 | 假设证伪时的退出条件 |
| 核心假设 | 否 | 投资决策依赖的关键假设 |
| 未解问题 | 否 | 影响决策但尚未验证的问题 |
| 历史跟踪 | 是 | 按时间倒序记录每次检查/操作 |

## 状态流转

```
观察中 → 已建仓（条件触发后买入）
已建仓 → 已清仓（止损或止盈）
已建仓 → 观察中（减仓后继续观察）
任何状态 → 暂停跟踪（逻辑证伪或长期不符合条件）
```

## 脚本解析约定

- `## 股票名称 (股票代码)` 作为分隔符
- 建仓条件表格使用 Markdown 标准表格语法
- `当前值` 和 `是否触发` 在 save 时填写分析时的数据
- `check` 模式会读取表格，用最新数据替换 `当前值` 和 `是否触发` 列
- `历史跟踪` 列表按时间倒序，新记录 prepend


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)