From d80c208004c4972e9d93ba6b590705189d4d8c8a Mon Sep 17 00:00:00 2001 From: wang Date: Wed, 1 Jul 2026 06:39:40 +0000 Subject: [PATCH] Initial commit: A-share stock analysis project with screening, backtesting, and multi-factor analysis tools --- .gitignore | 12 ++ .opencode/skills/market-overview/SKILL.md | 77 ++++++++ .opencode/skills/stock-analyzer/SKILL.md | 97 ++++++++++ .opencode/skills/stock-backtest/SKILL.md | 78 ++++++++ .opencode/skills/stock-screener/SKILL.md | 79 +++++++++ .opencode/tools/stock-backtest.ts | 53 ++++++ .opencode/tools/stock-data.ts | 83 +++++++++ .opencode/tools/stock-screen.ts | 61 +++++++ AGENTS.md | 149 ++++++++++++++++ README.md | 38 ++++ docs/FOUNDATIONS.md | 128 +++++++++++++ docs/PLAN.md | 207 ++++++++++++++++++++++ docs/PRINCIPLES.md | 138 +++++++++++++++ requirements.txt | 3 + scripts/backtest_engine.py | 89 ++++++++++ scripts/market_data.py | 81 +++++++++ scripts/sentiment.py | 47 +++++ scripts/stock_analyzer.py | 54 ++++++ scripts/stock_screener.py | 68 +++++++ 19 files changed, 1542 insertions(+) create mode 100644 .gitignore create mode 100644 .opencode/skills/market-overview/SKILL.md create mode 100644 .opencode/skills/stock-analyzer/SKILL.md create mode 100644 .opencode/skills/stock-backtest/SKILL.md create mode 100644 .opencode/skills/stock-screener/SKILL.md create mode 100644 .opencode/tools/stock-backtest.ts create mode 100644 .opencode/tools/stock-data.ts create mode 100644 .opencode/tools/stock-screen.ts create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 docs/FOUNDATIONS.md create mode 100644 docs/PLAN.md create mode 100644 docs/PRINCIPLES.md create mode 100644 requirements.txt create mode 100644 scripts/backtest_engine.py create mode 100644 scripts/market_data.py create mode 100644 scripts/sentiment.py create mode 100644 scripts/stock_analyzer.py create mode 100644 scripts/stock_screener.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d916563 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +__pycache__/ +*.py[cod] +*.egg-info/ +dist/ +.venv/ +venv/ +.env +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store diff --git a/.opencode/skills/market-overview/SKILL.md b/.opencode/skills/market-overview/SKILL.md new file mode 100644 index 0000000..faaf7b3 --- /dev/null +++ b/.opencode/skills/market-overview/SKILL.md @@ -0,0 +1,77 @@ +--- +name: market-overview +description: Assess current A-share market environment including index trends, market breadth, sector rotation, and position sizing recommendation +--- + +## What I Do + +Evaluate the overall A-share market environment to determine: +1. Whether the market is favorable for medium-term trading +2. Suggested position sizing in RMB (capital-aware, for ~5万 account) +3. Current hot sectors and rotation patterns +4. Key risk factors + +## Capital Context (CRITICAL) + +**The user has ~5万 RMB capital.** Position sizing must be translated to concrete RMB amounts, not abstract percentages alone. + +### Position Sizing for 5万 Account + +| Signal | Total Position | Max Per Stock | Holding Count | +|--------|---------------|---------------|---------------| +| **Heavy (bullish)** | ¥35,000–40,000 (70–80%) | ¥10,000–15,000 each | 3–4 positions | +| **Moderate (neutral)** | ¥15,000–25,000 (30–50%) | ¥5,000–10,000 each | 2–3 positions | +| **Light (bearish)** | <¥15,000 (<30%) | <¥5,000 each | 1–2 positions | + +Always present both: percentage of capital AND the corresponding RMB range. + +## Assessment Dimensions + +### Index Trend +- Shanghai Composite (000001), Shenzhen Component (399001), ChiNext (399006) +- Direction, strength, and stage of trend +- Key support/resistance levels +- 20/60-day MA relationship + +### Market Breadth +- Advancing vs declining stocks ratio +- Volume trend (expanding / contracting) +- New highs vs new lows +- % of stocks above 20-day MA + +### Sector Rotation +- Leading and lagging sectors +- Sector fund flow +- Continuity of sector trends (how many days in a row?) + +## When to Use Me + +Use when the user asks to: +- Check market conditions +- Decide position size +- Understand sector rotation +- Before running any stock screening + +## Workflow + +1. Fetch major index data via `stock-data index` +2. Fetch sector performance via `stock-data sector` +3. Analyze market breadth indicators +4. Identify sector hotspots and rotation signals +5. Output a structured market assessment: + +### Output Format + +1. **Market Signal**: Strong / Moderate / Weak — with justification +2. **Index Summary**: each index trend + key level +3. **Market Breadth**: breadth reading and what it means +4. **Hot Sectors**: top 3–5 sectors with momentum +5. **Position Recommendation**: + - Signal-based allocation in RMB and % + - Max per stock in RMB + - Suggested holding count +6. **Key Risks**: specific market risks to watch this week + +## Risk Disclaimer + +Always remind the user: results are for reference only. Market conditions can change rapidly. Position sizing is a suggestion, not financial advice. diff --git a/.opencode/skills/stock-analyzer/SKILL.md b/.opencode/skills/stock-analyzer/SKILL.md new file mode 100644 index 0000000..3b7a185 --- /dev/null +++ b/.opencode/skills/stock-analyzer/SKILL.md @@ -0,0 +1,97 @@ +--- +name: stock-analyzer +description: Deep analysis of a single A-share stock covering technical, fundamental, capital flow, and sentiment dimensions +--- + +## What I Do + +Generate a comprehensive analysis report for a single stock: +1. Fetch all relevant data for the stock +2. Analyze across four dimensions +3. Score each dimension (0-100) +4. Provide overall rating and actionable insights + +## Capital Context (CRITICAL) + +**The user has ~5万 RMB capital.** Every analysis must include: + +- **Affordability check**: confirm the stock price × 100 shares is within reach +- **Position sizing advice**: based on market conditions and stock risk profile, suggest how many lots the user should buy +- **Risk in RMB**: always show stop-loss and take-profit levels in both percentage AND absolute RMB amount +- **Transaction cost**: calculate estimated round-trip cost for the suggested position + +### Affordability Check +``` +这股票一手要多少钱? 股价 × 100 = ____ 元 +用户能买几手? 分配的仓位金额 ÷ (股价 × 100) = ____ 手 +``` + +## Analysis Dimensions + +### Technical Analysis +- Trend positioning: identify if stock is in uptrend/downtrend/sideways +- Moving average status: check MA alignment (5/10/20/60 day) +- Support/Resistance levels: identify key price levels +- Volume analysis: volume trend, volume-price divergence signals +- RSI/MACD: oscillator readings and signals + +### Capital Flow Analysis +- Main fund flow: net inflow trend over 3/5/10 days +- Northbound capital: recent changes (if available) +- Large order ratio: institutional activity signals + +### Fundamental Analysis +- Valuation: PE/PB historical percentile +- Profitability: ROE trend +- Growth: revenue and profit YoY growth +- Quick health check: debt ratio, cash flow + +### Sentiment Analysis +- Recent news: key announcements in past 30 days +- Analyst ratings: direction of recent rating changes + +## Scoring + +Weighted composite score (0-100): +- Technical: 35% +- Capital Flow: 30% +- Fundamental: 25% +- Sentiment: 10% + +## When to Use Me + +Use when the user asks to: +- Analyze a specific stock by name or code +- Evaluate whether a stock is worth buying +- Get a detailed report on a stock + +## Workflow + +1. Get stock code (ask user if not provided) +2. Call `stock-data` tools to fetch all data +3. Analyze each dimension, calculate scores +4. Perform affordability check (price × 100 vs 5万 budget) +5. Present a structured report with: + +### Report Sections + +1. **Basic Info**: name, code, price, sector, market cap, 1手 cost +2. **Overall Score**: composite rating with grade (A/B/C/D) +3. **Dimension Breakdown**: each dimension's score with key observations +4. **Technical Check**: trend, support/resistance, volume signal +5. **Capital Flow Check**: recent money flow pattern and what it means +6. **Fundamental Check**: valuation context, growth trajectory +7. **News & Sentiment**: key events, sentiment direction +8. **Position Suggestion**: + - Suggested lot count (based on market signal + stock risk) + - Total cost in RMB + - Estimated round-trip transaction cost +9. **Risk Management**: + - Stop-loss level: price (¥) and loss amount (¥) with percentage + - Take-profit target: price (¥) and gain amount (¥) with percentage +10. **Verdict**: Watch / Consider / Caution / Avoid — with one-sentence reason +11. **Risk Disclaimer**: results for reference only + +## Risk Disclaimer + +Always remind the user: results are for reference only. Past performance does not guarantee future results. The user bears all trading risk. diff --git a/.opencode/skills/stock-backtest/SKILL.md b/.opencode/skills/stock-backtest/SKILL.md new file mode 100644 index 0000000..0d6e521 --- /dev/null +++ b/.opencode/skills/stock-backtest/SKILL.md @@ -0,0 +1,78 @@ +--- +name: stock-backtest +description: Design, run, and interpret strategy backtests for A-share stocks +--- + +## What I Do + +Guide the agent through backtesting trading strategies: +1. Help design strategy rules from user descriptions +2. Run backtests on historical data +3. Interpret results and identify potential issues +4. Suggest improvements + +## Capital Context (CRITICAL) + +**The user has ~5万 RMB capital.** Backtest interpretation must be capital-aware: + +- When presenting metrics, translate key numbers to the user's context: + - "Annualized return 15% = ¥7,500 on 5万" + - "Max drawdown 12% = ¥6,000 worst-case loss" + - "Average win ¥800, average loss ¥1,200 per trade" +- Assess whether a strategy is **practically usable** with 5万 considering position sizing constraints +- Flag if a strategy requires more positions than 5万 can support + +## Key Backtest Metrics + +- **Cumulative return**: total return over the period +- **Annualized return**: normalized yearly return (translate to ¥ on 5万) +- **Win rate**: percentage of profitable trades +- **Max drawdown**: worst peak-to-trough decline (translate to ¥) +- **Sharpe ratio**: risk-adjusted return +- **Benchmark comparison**: vs CSI 300 index + +## Strategy Parameters + +Common strategy elements to help users define: +- Entry conditions (e.g., MA crossover, breakout, pullback) +- Exit conditions (e.g., stop loss %, take profit %, trailing stop, time-based) +- Position sizing (e.g., fixed %, Kelly fraction) +- Test period and stock universe + +## When to Use Me + +Use when the user asks to: +- Backtest a trading strategy +- Verify a strategy's historical performance +- Compare multiple strategies +- Evaluate whether a strategy is worth using + +## Workflow + +1. Clarify strategy rules with user +2. Translate rules into testable parameters +3. Run backtest via `stock-backtest` tools +4. Present results with interpretation: + +### Output Format + +1. **Strategy Summary**: rules in plain language +2. **Performance Metrics**: all key metrics in table form +3. **Capital Context Translation**: + | Metric | Value | On 5万 Capital | + |--------|-------|----------------| + | Annualized Return | 18% | ¥9,000/year | + | Max Drawdown | 15% | ¥7,500 worst case | + | Avg Win per Trade | ¥450 | — | + | Avg Loss per Trade | ¥600 | — | +4. **Equity Curve Description**: shape, drawdown periods, recovery time +5. **Benchmark Comparison**: outperformance/underperformance vs CSI 300 +6. **Practicality Assessment for 5万**: + - Can the user afford the position sizes? + - Does the strategy require too many concurrent positions? + - Is the max drawdown psychologically tolerable? +7. **Potential Concerns**: overfitting, look-ahead bias, survivorship bias, regime dependence + +## Risk Disclaimer + +Always remind: backtest results reflect historical data. Live performance will differ. Strategies can fail in new market regimes. The user bears all trading risk. diff --git a/.opencode/skills/stock-screener/SKILL.md b/.opencode/skills/stock-screener/SKILL.md new file mode 100644 index 0000000..dbe88f0 --- /dev/null +++ b/.opencode/skills/stock-screener/SKILL.md @@ -0,0 +1,79 @@ +--- +name: stock-screener +description: Multi-factor A-share stock screening workflow for medium-to-short-term selection +--- + +## What I Do + +Guide the agent through a structured stock screening process: +1. Understand user preferences (industry, market cap, holding period) +2. Assess market environment via `market-overview` skill +3. Call screening tools to find candidates +4. Score and rank candidates by multi-factor model +5. Output **Top 3-5** picks with rationale, position sizing, and cost estimates + +## Capital Context (CRITICAL) + +**The user has ~5万 RMB capital.** Every recommendation must account for this: + +- **Stock price filter**: each recommended stock must cost ¥10–¥50 per share so the user can afford at least 1手 (100 shares). Stocks >¥200 need explicit justification (concentrated bet). Stocks >¥500 are completely inaccessible — never recommend them. +- **Portfolio concentration**: default output is 3–5 stocks, not 10+. The user holds 2–4 stocks max. +- **Position sizing**: for each recommended stock, suggest a specific lot count and total cost in RMB. +- **Transaction cost**: for each recommended position, calculate estimated one-way cost (commission + stamp tax). Surface it alongside the position suggestion. + +### Position Sizing Table (5万 context) + +| Market Signal | Total Exposure | Max Per Stock | Holding Count | +|---------------|---------------|---------------|---------------| +| Strong uptrend | 35,000–40,000 (70–80%) | 10,000–15,000 | 3–4 positions | +| Neutral/sideways | 15,000–25,000 (30–50%) | 5,000–10,000 | 2–3 positions | +| Weak/downtrend | <15,000 (<30%) | <5,000 | 1–2 positions | + +### Affordability Check + +Before recommending any stock, verify: +``` +股价 × 100 shares ≤ 计划分配给该股的仓位金额 +``` +Example: ¥38 stock × 100 = ¥3,800. With ¥10,000 allocation, the user can buy 2手 (200 shares, ¥7,600). + +## Screening Dimensions + +- **Technical (35%)**: Trend strength, moving average alignment, volume-price coordination, RSI position +- **Capital Flow (30%)**: Main fund net inflow trend, northbound capital, large order ratio +- **Fundamental (25%)**: PE percentile, ROE, profit growth, revenue growth +- **Sentiment (10%)**: News heat, analyst rating direction + +## When to Use Me + +Use when the user asks to: +- Find/screen stocks for investment +- Get stock recommendations +- Filter stocks by any criteria + +## Workflow + +1. Ask the user about preferences if not specified (sector, style, time horizon) +2. Load `market-overview` skill to check if market is favorable and get position sizing signal +3. Use `stock-screen` tools to filter candidates (default top_n=5) +4. For each top candidate, verify affordability: + - Check share price, calculate minimum buy cost (price × 100) + - Cross-check against the position sizing budget + - Filter out any stock the user cannot afford +5. For remaining candidates, run quick analysis with relevant tools +6. Present a ranked list with: + - Rank and composite score (0-100) + - Key strengths (why buy?) and risks (what can go wrong?) + - Suggested position: lot count × share price = total cost + - Estimated transaction cost for this position + - Stop-loss price in both ¥ and percentage + +### Output Format Example + +| Rank | Stock | Price | Score | Position | Cost | Stop-Loss | Rationale | +|------|-------|-------|-------|----------|------|-----------|-----------| +| 1 | 600XXX | ¥28.5 | 82 | 3手 (300股) = ¥8,550 | 交易费~¥14 | ¥25.7 (-10%, -¥855) | 放量突破+主力净流入 | + +## Risk Disclaimer + +Always remind: results are for reference only. Past performance does not guarantee future results. The user bears all trading risk. diff --git a/.opencode/tools/stock-backtest.ts b/.opencode/tools/stock-backtest.ts new file mode 100644 index 0000000..bc7a0dc --- /dev/null +++ b/.opencode/tools/stock-backtest.ts @@ -0,0 +1,53 @@ +import { tool } from "@opencode-ai/plugin"; +import path from "path"; + +const SCRIPTS_DIR = (ctx: { worktree: string }) => + path.join(ctx.worktree, "scripts"); + +async function runPython( + ctx: { worktree: string }, + fn: string, + args: Record, +): Promise { + const scriptPath = path.join(SCRIPTS_DIR(ctx), "backtest_engine.py"); + const argsJson = JSON.stringify(args); + const result = await Bun.$`python3 ${scriptPath} ${fn} ${argsJson}`.text(); + return result.trim(); +} + +export const run = tool({ + description: "Run a backtest for a trading strategy on specified stock universe and period", + args: { + strategy_name: tool.schema.string().optional().describe("Name of a predefined strategy, or omit to use custom rules"), + entry_rule: tool.schema.string().optional().describe("Custom entry condition (Python expression using df columns like close, ma20, ma60, volume, etc.)"), + exit_rule: tool.schema.string().optional().describe("Custom exit condition (Python expression) or 'stop_loss:0.05,take_profit:0.15,max_hold:20'"), + universe: tool.schema.enum(["hs300", "zz500", "all", "custom"]).default("hs300").describe("Stock universe to test on"), + symbols: tool.schema.string().optional().describe("Comma-separated stock codes for custom universe"), + start_date: tool.schema.string().default("20210101").describe("Start date YYYYMMDD"), + end_date: tool.schema.string().default("20251231").describe("End date YYYYMMDD"), + }, + async execute(args, context) { + return runPython(context, "run", args); + }, +}); + +export const predefined = tool({ + description: "List all predefined backtest strategies with descriptions", + args: {}, + async execute(args, context) { + return runPython(context, "predefined", args); + }, +}); + +export const compare = tool({ + description: "Compare multiple predefined strategies on the same universe and period", + args: { + strategies: tool.schema.string().describe("Comma-separated strategy names to compare"), + universe: tool.schema.enum(["hs300", "zz500", "all"]).default("hs300").describe("Stock universe"), + start_date: tool.schema.string().default("20210101").describe("Start date YYYYMMDD"), + end_date: tool.schema.string().default("20251231").describe("End date YYYYMMDD"), + }, + async execute(args, context) { + return runPython(context, "compare", args); + }, +}); diff --git a/.opencode/tools/stock-data.ts b/.opencode/tools/stock-data.ts new file mode 100644 index 0000000..48e130c --- /dev/null +++ b/.opencode/tools/stock-data.ts @@ -0,0 +1,83 @@ +import { tool } from "@opencode-ai/plugin"; +import path from "path"; + +const SCRIPTS_DIR = (ctx: { worktree: string }) => + path.join(ctx.worktree, "scripts"); + +async function runPython( + ctx: { worktree: string }, + script: string, + fn: string, + args: Record, +): Promise { + const scriptPath = path.join(SCRIPTS_DIR(ctx), script); + const argsJson = JSON.stringify(args); + const result = await Bun.$`python3 ${scriptPath} ${fn} ${argsJson}`.text(); + return result.trim(); +} + +export const quote = tool({ + description: "Get daily/weekly K-line and real-time quotes for a stock", + args: { + symbol: tool.schema.string().describe("Stock code (e.g. 600519 for Kweichow Moutai)"), + period: tool.schema.enum(["daily", "weekly", "monthly"]).default("daily").describe("K-line period"), + start_date: tool.schema.string().optional().describe("Start date YYYYMMDD, defaults to 90 days ago"), + end_date: tool.schema.string().optional().describe("End date YYYYMMDD, defaults to today"), + }, + async execute(args, context) { + return runPython(context, "market_data.py", "quote", args); + }, +}); + +export const financial = tool({ + description: "Get financial statement indicators for a stock", + args: { + symbol: tool.schema.string().describe("Stock code (e.g. 600519)"), + }, + async execute(args, context) { + return runPython(context, "market_data.py", "financial", args); + }, +}); + +export const moneyflow = tool({ + description: "Get capital flow data (main fund, northbound) for a stock", + args: { + symbol: tool.schema.string().describe("Stock code (e.g. 600519)"), + days: tool.schema.number().default(10).describe("Number of recent days"), + }, + async execute(args, context) { + return runPython(context, "market_data.py", "moneyflow", args); + }, +}); + +export const news = tool({ + description: "Get recent news and announcements for a stock", + args: { + symbol: tool.schema.string().describe("Stock code (e.g. 600519)"), + limit: tool.schema.number().default(20).describe("Max number of news items"), + }, + async execute(args, context) { + return runPython(context, "sentiment.py", "news", args); + }, +}); + +export const index = tool({ + description: "Get major A-share index data (Shanghai Composite, Shenzhen Component, ChiNext)", + args: { + index_code: tool.schema.enum(["sh", "sz", "cyb", "all"]).default("all").describe("Index to fetch"), + days: tool.schema.number().default(30).describe("Number of recent trading days"), + }, + async execute(args, context) { + return runPython(context, "market_data.py", "index", args); + }, +}); + +export const sector = tool({ + description: "Get sector/industry performance and ranking", + args: { + date: tool.schema.string().optional().describe("Date YYYYMMDD, defaults to latest"), + }, + async execute(args, context) { + return runPython(context, "market_data.py", "sector", args); + }, +}); diff --git a/.opencode/tools/stock-screen.ts b/.opencode/tools/stock-screen.ts new file mode 100644 index 0000000..a93260c --- /dev/null +++ b/.opencode/tools/stock-screen.ts @@ -0,0 +1,61 @@ +import { tool } from "@opencode-ai/plugin"; +import path from "path"; + +const SCRIPTS_DIR = (ctx: { worktree: string }) => + path.join(ctx.worktree, "scripts"); + +async function runPython( + ctx: { worktree: string }, + fn: string, + args: Record, +): Promise { + const scriptPath = path.join(SCRIPTS_DIR(ctx), "stock_screener.py"); + const argsJson = JSON.stringify(args); + const result = await Bun.$`python3 ${scriptPath} ${fn} ${argsJson}`.text(); + return result.trim(); +} + +export const multi_factor = tool({ + description: "Screen stocks using multi-factor scoring model (technical + capital flow + fundamental + sentiment)", + args: { + strategy: tool.schema.enum(["comprehensive", "momentum", "value", "breakout"]).default("comprehensive").describe("Screening strategy"), + sector: tool.schema.string().optional().describe("Filter by sector/industry name"), + market_cap: tool.schema.enum(["large", "medium", "small", "all"]).default("all").describe("Market cap filter"), + top_n: tool.schema.number().default(5).describe("Number of top stocks to return (user has 5万 capital, keep it tight)"), + }, + async execute(args, context) { + return runPython(context, "multi_factor", args); + }, +}); + +export const strong = tool({ + description: "Screen for strong-trend stocks (MA bull alignment + relative strength)", + args: { + sector: tool.schema.string().optional().describe("Filter by sector/industry name"), + top_n: tool.schema.number().default(5).describe("Number of top stocks to return (user has 5万 capital, keep it tight)"), + }, + async execute(args, context) { + return runPython(context, "strong", args); + }, +}); + +export const breakout = tool({ + description: "Screen for volume breakout stocks (price breaking resistance with expanding volume)", + args: { + lookback_days: tool.schema.number().default(60).describe("Lookback period for resistance identification"), + top_n: tool.schema.number().default(5).describe("Number of top stocks to return (user has 5万 capital, keep it tight)"), + }, + async execute(args, context) { + return runPython(context, "breakout", args); + }, +}); + +export const oversold = tool({ + description: "Screen for oversold stocks with potential rebound signals", + args: { + top_n: tool.schema.number().default(5).describe("Number of top stocks to return (user has 5万 capital, keep it tight)"), + }, + async execute(args, context) { + return runPython(context, "oversold", args); + }, +}); diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7a50b8f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,149 @@ +# Stock Analysis Project - Agent Instructions + +This project is an A-share (Chinese stock market) stock selection assistant built on opencode with Skills and Custom Tools. + +## Project Purpose + +Help users make informed medium-to-short-term stock selection decisions through multi-factor quantitative analysis. +All analysis is data-driven using the AKShare Python library. No automated trading - users make their own execution decisions. + +## Architecture + +- **Python scripts** in `scripts/` — core data fetching, screening, backtesting logic (using AKShare) +- **Custom Tools** in `.opencode/tools/` — TypeScript wrappers that expose Python scripts as LLM-callable tools +- **Skills** in `.opencode/skills/` — structured workflows guiding the agent through analysis steps + +## Guiding Philosophy (READ FIRST) + +This project is a **laboratory for learning, not a money printer.** Its core value is accelerating the user's learning loop: question → hypothesis → backtest → revise → repeat. All agent behavior, tool output, and skill workflows must align with the principles in: + +- `docs/FOUNDATIONS.md` — universal cognitive framework (how to think, reason, and act effectively) +- `docs/PRINCIPLES.md` — FOUNDATIONS.md applied to stock investment (10 domain-specific principles) + +## Agent Thinking Framework + +When analyzing any problem or proposing any solution, structure your thinking using the framework from `docs/FOUNDATIONS.md`: + +### 1. Direction First +Before jumping to solutions, ask: **what is the right thing to pursue?** What > How. Selection > Effort. The direction decision determines everything that follows. + +### 2. Think Before Acting +Apply the thinking tools from FOUNDATIONS.md before proposing any plan: +- **Completeness**: are we missing any people, variables, or relationships? An incomplete analysis is wrong no matter how refined. +- **Time scale**: over what period is this judgment valid? Short-term beneficial may be long-term harmful. +- **Asymmetric check**: if A implies B, does B imply A? If not, why? We naturally overlook asymmetry. +- **People & interests**: who are the stakeholders? What are their relationships and incentives? Surface = data. Underlying = people and interests. +- **First principles**: reduce to the most basic, indisputable facts. Rebuild reasoning from there. +- **Simplicity**: is there a simpler explanation or approach? Complexity must be forced by data, not pursued for its own sake. + +### 3. Action with Feedback +- **Start with Why**: before executing, clarify the purpose. Action without clear motivation drifts. +- **Define measurable outcomes**: what does success look like? +- **After action**: compare results to expectations. Use the gap to revise direction. No feedback loop = no learning. + +### 4. Output Structure +When presenting a proposal or analysis, organize it as: +1. **Direction & Goal** — what are we trying to achieve, and why? +2. **Method** — how will we achieve it? +3. **Action** — what are the specific, executable steps? +4. **Feedback** — how will we measure success and correct course? + +## Agent Behavioral Constraints (MANDATORY) + +Every analysis, recommendation, and backtest output MUST comply with these rules: + +1. **Fact vs. Inference**: clearly separate data (fact) from scoring/interpretation (inference). Never present an opinion as a fact. +2. **Out-of-sample labeling**: every backtest result must explicitly label which period was in-sample (training) and which was out-of-sample (testing). If no split was used, state: "Warning: no out-of-sample validation — results may be overfit." +3. **Trade count**: every backtest result must report the number of actual trades. If trade count < 30, append: "Warning: sample size too small for statistical significance." +4. **Parameter count warning**: state the number of tunable parameters in any strategy. If parameter count ≥ 5, append: "High overfitting risk — requires strict out-of-sample validation." +5. **RMB translation**: all risk metrics (drawdown, stop-loss, position size) must be displayed in both percentage AND absolute RMB terms based on 5万 capital. +6. **Position sizing first**: before any stock selection output, state the recommended position size. Position sizing beats stock selection. +7. **User fitness**: for each strategy recommendation, include a brief "Fitness for You" section covering: (a) max single-trade loss in RMB, (b) psychological difficulty rating (easy/medium/hard), (c) required monitoring frequency. +8. **Uncertainty**: when data is insufficient to conclude, say "data insufficient" rather than fabricating an answer. Uncertainty is honest; false confidence is harmful. +9. **Learning stage awareness**: the user is a beginner. Prefer simple strategies (1-3 parameters) over complex ones. Explain concepts before using jargon. The user's learning is more important than any single trade recommendation. +10. **Reminder cadence**: at least every 5 interactions, remind the user: "You make the final decision. No analysis can guarantee profits. This project is for learning support, not investment advice." + +## Key Principles + +1. **Verify before trusting** — every strategy must be backtested before recommendation +2. **Data-driven** — all conclusions must reference quantifiable metrics, not subjective judgment +3. **Auxiliary decision-making** — output analysis reports, not trading orders +4. **Risk disclaimer** — always remind the user that results are for reference only + +## User Context + +**Target user has ~5万 RMB capital, trading A-shares with manual execution.** + +Capital constraints affect every recommendation: + +- **Stock accessibility**: the user must afford at least 1手 (100 shares). A stock priced at ¥500+ is completely out of reach. Prefer stocks in ¥10–¥50 range. +- **Concentrated portfolio**: with 5万, hold 2–4 stocks max (not dozens). Screening output should default to Top 3–5, not Top 10–20. +- **Cost awareness**: one round-trip (buy + sell) costs roughly ¥60 (commission ¥5+¥5 + stamp tax ~¥50 at ¥5万 volume). This is 0.12% of capital, manageable if turnover is low. However, splitting into tiny positions amplifies cost. Always calculate and surface the estimated transaction cost for each proposed trade. +- **Psychological reality**: a 5% loss = ¥2500, which feels significant. Risk metrics (stop-loss levels, max drawdown) must be presented in both percentage AND absolute RMB terms. +- **Return expectations**: annualized 20% = ¥10,000. This is a meaningful learning goal, not a wealth-building strategy. Set expectations accordingly. + +## Available Skills + +- `stock-screener` — multi-factor stock screening workflow (outputs Top 3-5, with position sizing) +- `stock-analyzer` — single stock deep analysis (includes affordability check) +- `market-overview` — market environment assessment (capital-aware position sizing) +- `stock-backtest` — strategy backtesting guidance + +## Data Sources + +All market data comes from AKShare (Python library). Script location: `scripts/market_data.py` +If data fetching fails, suggest the user to run `pip install akshare --upgrade`. + +## Tool Usage Pattern + +Custom tools execute Python scripts via `Bun.$`. When a tool fails, check: +1. Python environment is active +2. AKShare is installed and up to date +3. Network is accessible + +## Coding Guidelines + +**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks (typo fixes, one-liners), use judgment. + +### 1. Think Before Coding + +Before implementing: +- State assumptions explicitly. If uncertain, ask. +- If multiple interpretations exist, present them — don't pick silently. +- If a simpler approach exists, say so. Push back when warranted. +- If something is unclear, stop. Name what's confusing. Ask. + +### 2. Simplicity First + +- No features beyond what was asked. +- No abstractions for single-use code. +- No "flexibility" or "configurability" that wasn't requested. +- No error handling for impossible scenarios. +- If 200 lines could be 50, rewrite it. + +### 3. Surgical Changes + +When editing existing code: +- Don't "improve" adjacent code, comments, or formatting. +- Don't refactor things that aren't broken. +- Match existing style, even if you'd do it differently. +- If you notice unrelated dead code, mention it — don't delete it. + +When your changes create orphans: +- Remove imports/variables/functions that YOUR changes made unused. +- Don't remove pre-existing dead code unless asked. + +### 4. Goal-Driven Execution + +Transform tasks into verifiable goals: +- "Add validation" → "Write tests for invalid inputs, then make them pass" +- "Fix the bug" → "Write a test that reproduces it, then make it pass" +- "Refactor X" → "Ensure tests pass before and after" + +For multi-step tasks, state a brief plan with verification steps. + +**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes. + +--- + +*Guidelines adapted from [andrej-karpathy-skills](https://github.com/multica-ai/andrej-karpathy-skills) (MIT)* diff --git a/README.md b/README.md new file mode 100644 index 0000000..fbf053c --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# A股中短期选股辅助系统 + +基于 opencode + Skill/Custom Tool 的 A股多因子选股分析工具。 + +## 文档体系 + +``` +docs/FOUNDATIONS.md 通用认知框架(跨领域方法论 — 如何正确思考与行动) + ↓ 领域应用 +docs/PRINCIPLES.md 股票投资原则(10条领域专属原则) + ↓ 翻译为可执行规则 +AGENTS.md Agent行为约束 + 思维框架(Agent强制执行规则) + ↓ 指导 +.opencode/skills/ 工作流技能(选股/分析/回测/大盘) + ↓ 调用 +.opencode/tools/ + scripts/ 工具与数据层 +``` + +## 快速开始 + +```bash +# 安装 Python 依赖 +pip install -r requirements.txt + +# 启动 opencode +opencode +``` + +## 使用方式 + +在 opencode 中直接对话: + +- "帮我筛选本周有潜力的中短期股票" +- "分析一下贵州茅台 600519" +- "当前大盘环境怎么样,适合操作吗?" +- "回测一下 20日均线上穿60日均线 这个策略过去3年的表现" + +详细计划见 [docs/PLAN.md](docs/PLAN.md) diff --git a/docs/FOUNDATIONS.md b/docs/FOUNDATIONS.md new file mode 100644 index 0000000..4617dd4 --- /dev/null +++ b/docs/FOUNDATIONS.md @@ -0,0 +1,128 @@ +# 认知操作系统 — 通用思想与方法论 + +> 本文档位于文档体系(见 `README.md`)的**顶层**, +> 是纯粹的元认知框架,不针对任何特定领域。 +> 它回答一个根本问题:**我们如何正确地认识世界、思考问题和有效行动?** +> +> 领域应用:`docs/PRINCIPLES.md` +> 执行约束:`AGENTS.md` +> 完整文档层次:`README.md` + +--- + +## 元层:知识组织法则 + +所有知识分为三层,但不是单向堆叠,而是循环关系: + +``` +价值观(应该追求什么) + ↑↓ 决定并接受修正 +方法论(如何追求) + ↑↓ 指导并接受修正 +实践经验(做了什么、学到了什么) +``` + +三层不断循环。实践修正方法论,方法论重塑价值观。**没有反馈循环的知识体系是死的。** + +--- + +## 第一脉:战略链 — 方向 > 目标 > 行动 > 反馈 + +### 方向:选择大于努力 + +想做什么比能做什么更重要。一个冲突出现时,选择"小事化了"还是"升级事态"——这个方向选择决定了后续一切目标、行动和最终结果。 + +方向正确,慢也是快。方向错误,快也是倒退。 + +### 目标:Why 与 What 的顺序 + +- **研究事物从 What 开始**:先搞清楚"这是什么",再追问"为什么"。先描述,后解释。 +- **做事情从 Why 开始**:先想清楚"为什么要做",再决定"做什么"和"怎么做"。 + +### 行动:可执行的步骤 + +方向和目标不落地到每天的具体动作,就是幻觉。每个目标必须有一个对应的、明天就可以做的事。 + +### 反馈:闭环才是学习 + +观察行动的后果 → 判断目标是否达成 → 判断方向是否正确 → 修正。没有反馈循环,任何方法论都是纸上谈兵。 + +--- + +## 第二脉:思维工具 — 理解世界的武器 + +### 1. 完备性(思考前的第一道检查) + +在分析任何问题之前,先问:我考虑到的因素是不是全的?有没有遗漏的人、关系、变量? + +不完备的分析再精致也是错的。最难的往往就是做到完备。 + +### 2. 时间尺度(每个判断的隐含前提) + +任何判断都有一个隐含的有效时间范围。短期有利的可能长期有害,长期有效的可能短期难受。没有标注时间尺度的判断是不完整的。 + +每当你下一个结论,必须追问:这个结论在什么时间范围内成立? + +### 3. 非对称检查(正向成立 ≠ 反向成立) + +A 在 B 条件下成立,不意味着 B 在 A 条件下成立。我们天然容易忽略这种不对称性。 + +每次得出一个结论,反问:反过来成立吗?如果不成立,原因是什么? + +### 4. 人是最重要的 + +梳理任何一件事,把参与人一个不漏地列出来。搞清楚人物之间的关系,每个人的利益诉求。做到这一步,事情的来龙去脉自然清晰,走向也能预测。 + +表面是数字和逻辑,底层是人和利益。 + +### 5. 性质-关系法(关系 > 性质) + +事物的性质是孤立的标签,关系揭示结构。不要只问"它是什么属性",更要问"它和其他事物是什么关系"。 + +### 6. 第一性原理 + +不依靠类比和他人结论。把问题拆到最基本的不可再拆的事实,从那里重新推理。不是不能借鉴前人经验,而是不能不经验证地接受。 + +### 7. 简洁原则(单一原则 + 奥卡姆剃刀) + +每个体系里存在一个主导性的组织原则。找到它,其余都是推导。不必要的复杂度不应引入——复杂应该被数据逼出来,不是主动追求的。 + +--- + +## 第三脉:行动法则 — 如何有效做事 + +### 1. 复习比学习更重要 + +学新东西的快感容易上瘾。但真正改变行为的不是"第一次知道",而是"反复消化"。读完一篇好文章,花三倍时间反复思考它,比再读三篇新文章更有价值。 + +### 2. 知行合一 + +知道而做不到,等于不知道。真正的"知道"是你的行为已经发生了改变。如果知道一个道理后你的行为没有任何变化,那不是知识,是信息。 + +### 3. 刻意练习 + +不是重复做已经会的,而是:聚焦弱点 → 接受反馈 → 不断增加难度。每一次练习之后必须回答:我练习了什么?哪里不够好?下次怎么做? + +### 4. 逆向求解 + +与其问"如何成功",先问"保证失败的做法是什么",然后反着做。逆向思维揭示正面思考永远发现不了的路径。 + +--- + +## 桥接层:不确定性下的理性 + +### 1. 可证伪性 + +一个说法如果无论如何都不能被证明是错的,那它不是知识,是信仰。科学知识的特点是:它告诉你在什么条件下它就不成立了。 + +### 2. 概率思维 + +世界是不确定的。用概率而非确定性思考。新证据到来时,更新你的信念,而不是固守旧观念。一次结果说明不了问题,大量重复试验的统计分布才是真相。 + +### 3. 复利思维 + +微小的持续改进在时间作用下产生指数级效果。每天进步 1%,一年 37 倍。不需要某一天突然变强。需要的是每天都不倒退。 + +--- + +*最后更新:2026-07-01* diff --git a/docs/PLAN.md b/docs/PLAN.md new file mode 100644 index 0000000..79bf276 --- /dev/null +++ b/docs/PLAN.md @@ -0,0 +1,207 @@ +# 中短期A股选股辅助系统 — 项目计划 + +## 一、项目定位 + +基于 opencode + Skill/Custom Tool 机制构建的 **A股选股分析辅助工具**。利用 AI 辅助多维度分析, +帮助用户做出更有信息量的选股决策。**不涉及自动交易,仅做分析推荐,人工下单。** + +## 二、核心原则 + +1. **验证优先** — 任何策略上线前必须经过历史回测,不依赖未经数据验证的经验法则 +2. **辅助决策,而非替代决策** — 输出结构化分析报告,给出评分和依据,最终决策权归用户 +3. **数据驱动** — 所有分析基于可量化的数据指标,避免主观模糊的语言 +4. **增量迭代** — 先建回测引擎,再建选股工具,最后补舆情分析 + +## 三、技术架构 + +``` +┌──────────────────────────────────────────────┐ +│ opencode TUI │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ +│ │ Skills │ │ Skills │ │ Skills │ │ +│ │ Screener │ │ Analyzer │ │ Overview/ │ │ +│ │ │ │ │ │ Backtest │ │ +│ └────┬─────┘ └────┬─────┘ └──────┬──────┘ │ +│ │ │ │ │ +│ ┌────▼──────────────▼───────────────▼──────┐ │ +│ │ Custom Tools (TypeScript) │ │ +│ │ stock-data / stock-screen / backtest │ │ +│ └──────────────────┬───────────────────────┘ │ +└─────────────────────┼─────────────────────────┘ + │ Bun.$ `python3 ...` +┌─────────────────────▼─────────────────────────┐ +│ Python Scripts │ +│ ┌───────────┐ ┌──────────┐ ┌──────────────┐ │ +│ │ AKShare │ │ Screener │ │ Backtest │ │ +│ │ Data │ │ Engine │ │ Engine │ │ +│ └───────────┘ └──────────┘ └──────────────┘ │ +└───────────────────────────────────────────────┘ +``` + +### 数据源 + +| 数据类别 | 来源 | 说明 | +|----------|------|------| +| 日K/周K行情 | AKShare | `stock_zh_a_hist` | +| 实时行情 | AKShare | `stock_zh_a_spot_em` | +| 财务报表 | AKShare | `stock_financial_analysis_indicator` | +| 资金流向 | AKShare | `stock_individual_fund_flow` | +| 龙虎榜 | AKShare | `stock_sina_lhb_detail_daily` | +| 新闻舆情 | AKShare + 自建爬虫 | `stock_news_em` | +| 板块数据 | AKShare | `stock_board_industry_*` | + +## 四、分阶段实施计划 + +### 阶段 0:环境与框架搭建 ✅ + +- [x] 初始化项目目录结构 +- [ ] 安装 Python 环境与依赖 +- [x] 编写 AGENTS.md 供 opencode 理解项目 +- [x] 搭建 Custom Tool 骨架(TypeScript → Python 桥接) +- [x] 搭建 Skill 文件骨架 +- [x] 适配小资金(5万)场景约束 + +### 阶段 1:回测引擎(高优先级) + +**目标:** 任何策略可以输入规则 + 参数,输出回测报告。 + +- [ ] 实现回测引擎核心 `backtest_engine.py` + - 模拟交易(按收盘价成交) + - 计算收益曲线、胜率、最大回撤、夏普比率 + - 支持多策略对比 +- [ ] 实现 `stock-backtest` 工具(TypeScript 包装) +- [ ] 实现回测 Skill(指导 LLM 如何设计、运行、解读回测) +- [ ] 用历史数据跑几个经典策略验证引擎正确性 + +**验证标准:** 能跑出 60日均线上穿120日均线 这个经典策略在沪深300上的回测结果。 + +### 阶段 2:选股引擎 + +**目标:** 多因子打分,输出 Top N 推荐列表。 + +- [ ] 实现选股筛选器 `stock_screener.py` + - 技术因子:趋势强度、均线排列、量价配合、RSI、MACD + - 资金因子:主力净流入(3/5/10日)、北向资金变化 + - 基本面因子:PE 分位、ROE、营收/利润增速 + - 消息因子:近期新闻数量、机构评级变化(后续补充) + - 加权打分公式 +- [ ] 实现 `stock-screen` 工具 +- [ ] 实现选股 Skill(工作流:大盘评估 → 多因子筛选 → 排序输出) + +### 阶段 3:单股深度分析 + +**目标:** 对任意股票生成结构化分析报告。 + +- [ ] 实现个股分析器 `stock_analyzer.py` + - 技术面:趋势定位、均线状态、支撑/阻力位、技术形态识别 + - 资金面:近期主力资金行为模式 + - 基本面:PE/PB 历史分位、杜邦分析摘要 + - 消息面:近30天关键公告、新闻情绪评分 +- [ ] 实现 `stock-analyzer` Skill + +### 阶段 4:大盘环境评估 + +**目标:** 每次选股前判断当前是否适合操作。 + +- [ ] 实现大盘分析 `market_overview.py` + - 主要指数趋势判断 + - 市场宽度(上涨/下跌家数比) + - 成交量趋势 + - 板块轮动热力图 + - 仓位建议(轻仓/中等/重仓) +- [ ] 实现 `market-overview` Skill + +### 阶段 5:舆情分析增强 + +- [ ] 新闻爬取与解析 +- [ ] 关键词/情绪评分 +- [ ] 机构研报监控 + +## 五、因子体系设计(初版) + +### 多因子打分权重 + +| 维度 | 权重 | 子因子 | +|------|------|--------| +| 技术面 | 35% | 趋势强度、均线排列、量价配合、RSI位置 | +| 资金面 | 30% | 主力净流入趋势、北向资金、大单占比 | +| 基本面 | 25% | PE分位、ROE、利润增速、营收增速 | +| 消息面 | 10% | 新闻热度、机构评级方向 | + +### 每个因子标准化为 0-100 分,加权求和得综合分。 + +## 六、风险与应对 + +| 风险 | 应对方案 | +|------|----------| +| AKShare 接口变动 | 封装统一数据层,多数据源 fallback | +| LLM 分析幻觉 | 所有数据由 Python 脚本计算,LLM 仅做解读和呈现 | +| 策略过拟合 | 回测引擎支持样本外检验(滚动窗口) | +| 市场风格切换 | 多因子框架允许动态调整权重 | + +## 七、目录结构 + +``` +stock/ +├── .opencode/ +│ ├── tools/ # Custom Tools (LLM 可调用的函数) +│ │ ├── stock-data.ts # 数据获取工具组 +│ │ ├── stock-screen.ts # 选股筛选工具组 +│ │ └── stock-backtest.ts # 回测工具组 +│ └── skills/ # Agent Skills (工作流指令) +│ ├── stock-screener/ # 选股工作流 +│ ├── stock-analyzer/ # 单股分析工作流 +│ ├── market-overview/ # 大盘评估工作流 +│ └── stock-backtest/ # 回测工作流 +├── scripts/ # Python 实现 +│ ├── market_data.py # 数据获取 (AKShare) +│ ├── stock_screener.py # 多因子选股引擎 +│ ├── stock_analyzer.py # 个股深度分析 +│ ├── backtest_engine.py # 回测引擎 +│ └── sentiment.py # 舆情分析 +├── docs/ +│ └── PLAN.md # 本文件 +├── requirements.txt +├── AGENTS.md # opencode 项目理解 +└── README.md +``` + +## 八、小资金(5万)场景适配 + +### 约束条件 + +| 约束 | 影响 | 设计调整 | +|------|------|----------| +| 每股单价限制 | 股价 >500 元直接买不起1手 | 选股池自动过滤高价股,优先 10-50 元区间 | +| 集中持仓 | 5万只能分散到 2-4 只 | 选股输出 Top 3-5,附带仓位分配建议 | +| 手续费敏感 | 单次交易 ~60 元(0.12%) | 每次推荐附带交易成本估算,避免过度拆分 | +| 心理因素 | 5% = 2500 元,感受强烈 | 所有风控指标同时用百分比和绝对金额展示 | +| 收益预期 | 年化 20% = 10000 元 | 目标定位为学习验证,非财富增长 | + +### 仓位分配参考 + +| 大盘环境 | 总仓位 | 单只最大 | 持有数量 | +|----------|--------|----------|----------| +| 强势(重仓) | 3.5-4万(70-80%) | 1-1.5万/只 | 3-4只 | +| 中性(中等) | 1.5-2.5万(30-50%) | 0.5-1万/只 | 2-3只 | +| 弱势(轻仓) | <1.5万(<30%) | <0.5万/只 | 1-2只 | + +### 价格可买性校验 + +每只推荐股票前校验:`股价 × 100 ≤ 计划仓位` + +## 九、里程碑时间线 + +| 阶段 | 预计可交付 | 里程碑 | +|------|-----------|--------| +| 阶段 0 | ✅ 框架搭建 | 项目骨架可运行,上下文齐全 | +| 阶段 1 | 回测引擎 | 可验证任意策略历史表现 | +| 阶段 2 | 选股引擎 | 每天可生成 Top N 推荐 | +| 阶段 3 | 单股分析 | 可对任意股票出详细分析报告 | +| 阶段 4 | 大盘评估 | 判断市场可操作性 | +| 阶段 5 | 舆情分析 | 消息面因子补充 | + +--- + +*最后更新:2026-07-01* diff --git a/docs/PRINCIPLES.md b/docs/PRINCIPLES.md new file mode 100644 index 0000000..f8e6343 --- /dev/null +++ b/docs/PRINCIPLES.md @@ -0,0 +1,138 @@ +# 股票投资原则 — 引导项目走向正确方向的元原则 + +> 本文档是项目的"宪法"。所有设计决策、策略开发和 Agent 行为都必须对齐这些原则。 +> 当直觉与原则冲突时,服从原则。 +> +> 本文档是通用认知框架(`docs/FOUNDATIONS.md`)在股票投资领域的具体应用。 +> 阅读本文之前,建议先理解 FOUNDATIONS.md 中的元层思维。 + +--- + +## 一、关于市场本身的认知 + +### 原则 1:市场是复杂适应系统,不是物理定律 + +股票价格由无数参与者的买卖行为决定,参与者在学习、适应、进化。这意味着: + +- **任何策略的有效性都是暂时的。** 今天赚钱的策略明天可能失效,不是因为策略错了,而是因为太多人发现了它。 +- 我们的目标不是"找到一个永远有效的策略",而是"建立一套能持续发现临时有效策略的方法论"。 +- 策略失效时,不抱怨市场,而是回到方法论检查哪里出了问题。 + +### 原则 2:我们赚的每一分钱都来自别人的错误 + +超额收益 = 你的判断正确 × 市场定价错误。在 4000+ 只 A 股中,每天有百万级的活跃交易者在买卖。 + +- 如果你不能清晰说出"为什么市场错了而我对了",就不要交易。 +- 推论:买股票前,必须有一句话解释你的信息优势或视角差异。说不出 = 不是机会 = 不买。 + +--- + +## 二、关于回测的科学伦理 + +### 原则 3:回测的黄金法则 — 样本外验证 + +单次回测 = 讲故事,不是科学。正确的做法是**时间序列交叉验证**: + +> 用历史数据的前段(如 2019-2022)优化策略参数,用后段(如 2023-2025)检验。优化和验证永远不能看同一批数据。 + +- Agent 每次输出回测结果时,必须明确标注哪些时间段是样本内(训练),哪些是样本外(检验)。 +- 如果有人(包括你自己)给你看一个回测曲线但没有区分样本内/样本外,那个曲线是垃圾。 + +### 原则 4:过拟合是最大的敌人 + +给你 1200 个交易日的历史数据,你可以拟合出任何一条你想要的曲线。 + +- 参数越多,过拟合风险指数级上升: + - 1 个参数(如单均线) → 几乎不过拟合 + - 2-3 个参数(如均线交叉 + 成交量过滤) → 可控 + - 5+ 个参数(多因子加权) → 99%在过拟合,必须严格样本外验证 +- 推论:永远从最简策略开始。最简版本通过了,才考虑加复杂度。 + +### 原则 5:你其实没有那么多数据 + +5 年日线 ≈ 1200 个交易日。但如果一个策略只触发了 15 次交易,你的实际样本量是 15,不是 1200。 + +- 用 15 个样本得出"胜率 60%"——统计学上毫无意义。 +- Agent 输出回测结果时,必须报告**实际交易次数**,交易次数 < 30 的结果标注"样本不足,结论仅供参考"。 + +--- + +## 三、关于策略设计 + +### 原则 6:简单优于复杂(奥卡姆剃刀) + +均线金叉策略(1 个规则)和深度学习模型(1000 万参数)相比,前者在实盘中更不容易翻车。 + +| 策略复杂度 | 过拟合风险 | 实盘翻车概率 | 适合阶段 | +|-----------|-----------|-------------|----------| +| 单规则(均线、突破) | 极低 | 低 | 入门 | +| 2-3 因子组合 | 低 | 中低 | 进阶 | +| 5+ 因子打分 | 高 | 高 | 需严格样本外验证 | +| 机器学习 | 极高 | 极高 | 不适合个人投资者 | + +### 原则 7:仓位管理 > 选股 > 择时 + +学术研究表明,资产配置(投多少钱)解释了约 90% 的收益方差。但散户 90% 的精力花在"明天买哪只"上。 + +- 5 万本金时,满仓一只还是分三只,这个决策的影响远大于选的是茅台还是五粮液。 +- Agent 每次推荐策略或选股时,必须在选股结论之前先给出仓位建议。 +- 选股评分不能替代仓位决策。 + +--- + +## 四、关于心理与行为 + +### 原则 8:策略必须适合你自己,而不是理论上最优 + +5 万本金时: +- 策略要求持有 10 只股票 → 你做不到 → 别选这个策略 +- 策略最大回撤 30%(¥15,000)→ 你心理承受上限 10%(¥5,000)→ 你会提前止损,破坏策略 + +- Agent 展示每个策略时,必须附加一个"用户适配度"维度的评估。 +- 每个策略的评估必须包含:单笔最大亏损(RMB)和心理冲击等级。 + +### 原则 9:区分"运气"和"能力" + +| 场景 | 是运气还是能力? | +|------|-----------------| +| 第一次交易赚了 10% | 大概率运气 | +| 前 3 个月都在盈利 | 大概率运气 + 牛市 | +| 持续 2 年跑赢指数 | 可能有能力 | +| 单次交易亏了 20% | 正常,不是能力差 | +| 连续 10 次交易亏损后不失控 | 这才是真正的能力 | + +- 不要用前 3 个月的结果评价自己或任何策略。 +- 给自己至少 1 年学习期。前 6 个月的目标不是赚钱,是学会不犯致命错误。 + +--- + +## 五、关于这个项目的定位 + +### 原则 10:项目是实验室,不是印钞机 + +这个系统的核心价值是**加速你的学习循环**:提问 → 假设 → 回测 → 修正 → 再提问。 + +它不应该成为"每天打开看推荐然后买入"的工具——那会让你跳过思考,变成被动使用者。 + +- 每当你准备基于项目的输出做一笔交易,你必须能口头解释: + 1. 为什么这笔交易会赚钱? + 2. 我用什么数据验证过这个判断? + 3. 如果亏了,止损在哪? +- 三个答案任何一个说不出来,不交易。 + +--- + +## 附录 A:Agent 行为约束 + +以下需要更新到 AGENTS.md 作为 Agent 的强制行为标准(见 AGENTS.md 的 Agent Behavioral Constraints 章节)。 + +## 附录 B:用户自查清单 + +每次看项目输出时,问自己三个问题: +1. 我看懂这个推荐背后的逻辑了吗?——看不懂,暂停,不买。 +2. 我愿意持有这个判断到止损触发吗?——不愿意,不买。 +3. 这笔交易如果全亏了,对我的生活有影响吗?——有影响,不买。 + +--- + +*最后更新:2026-07-01* diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a66b29a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +akshare>=1.14.0 +pandas>=2.0.0 +numpy>=1.24.0 diff --git a/scripts/backtest_engine.py b/scripts/backtest_engine.py new file mode 100644 index 0000000..58bf081 --- /dev/null +++ b/scripts/backtest_engine.py @@ -0,0 +1,89 @@ +""" +Strategy backtesting engine for A-share stocks. + +Supports: +- Predefined strategies (MA crossover, momentum, mean reversion, etc.) +- Custom entry/exit rules +- Multi-strategy comparison +- Performance metrics (returns, drawdown, Sharpe, win rate) + +Usage: python3 backtest_engine.py +""" + +import sys +import json + + +def run(strategy_name=None, entry_rule=None, exit_rule=None, + universe="hs300", symbols=None, start_date="20210101", end_date="20251231"): + """Run a single backtest. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": f"Backtest (strategy={strategy_name or 'custom'}, universe={universe}) — not yet implemented", + "metrics": { + "cumulative_return": None, + "annualized_return": None, + "win_rate": None, + "max_drawdown": None, + "sharpe_ratio": None, + "benchmark_return": None, + }, + }, ensure_ascii=False) + + +def predefined(): + """List available predefined strategies.""" + strategies = [ + { + "name": "ma_cross", + "description": "MA Golden Cross: buy when 20-day MA crosses above 60-day MA", + "params": {"fast": 20, "slow": 60}, + }, + { + "name": "momentum_breakout", + "description": "Momentum Breakout: buy when price breaks 20-day high with volume > 1.5x avg", + "params": {"lookback": 20, "volume_multiplier": 1.5}, + }, + { + "name": "mean_reversion", + "description": "Mean Reversion: buy when price deviates >2 std below 20-day MA, sell at MA", + "params": {"ma_period": 20, "std_dev": 2}, + }, + { + "name": "turtle", + "description": "Turtle Trading: breakout of 20-day high, exit at 10-day low", + "params": {"entry_period": 20, "exit_period": 10}, + }, + ] + return json.dumps({"strategies": strategies}, ensure_ascii=False) + + +def compare(strategies, universe="hs300", start_date="20210101", end_date="20251231"): + """Compare multiple strategies. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": f"Strategy comparison ({strategies}) — not yet implemented", + "comparison": [], + }, ensure_ascii=False) + + +FUNCTIONS = { + "run": run, + "predefined": predefined, + "compare": compare, +} + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: backtest_engine.py [json_args]") + sys.exit(1) + + func_name = sys.argv[1] + args = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {} + + if func_name not in FUNCTIONS: + print(f"Unknown function: {func_name}") + sys.exit(1) + + result = FUNCTIONS[func_name](**args) + print(result) diff --git a/scripts/market_data.py b/scripts/market_data.py new file mode 100644 index 0000000..7fcb4de --- /dev/null +++ b/scripts/market_data.py @@ -0,0 +1,81 @@ +""" +A-share market data fetching module. +Uses AKShare as the primary data source. + +Usage: python3 market_data.py +""" + +import sys +import json +from datetime import datetime, timedelta + + +def get_quote(symbol, period="daily", start_date=None, end_date=None): + """Get K-line data for a stock. Stub - to be implemented with AKShare.""" + # TODO: Implement with akshare.stock_zh_a_hist() + return json.dumps({ + "status": "stub", + "message": f"Quote for {symbol} ({period}) — not yet implemented", + "symbol": symbol, + "period": period, + }, ensure_ascii=False) + + +def get_financial(symbol): + """Get financial indicators. Stub - to be implemented with AKShare.""" + # TODO: Implement with akshare.stock_financial_analysis_indicator() + return json.dumps({ + "status": "stub", + "message": f"Financial data for {symbol} — not yet implemented", + }, ensure_ascii=False) + + +def get_moneyflow(symbol, days=10): + """Get capital flow data. Stub - to be implemented with AKShare.""" + # TODO: Implement with akshare.stock_individual_fund_flow() + return json.dumps({ + "status": "stub", + "message": f"Money flow for {symbol} ({days}d) — not yet implemented", + }, ensure_ascii=False) + + +def get_index(index_code="all", days=30): + """Get index data. Stub - to be implemented with AKShare.""" + # TODO: Implement with akshare.stock_zh_index_daily() + return json.dumps({ + "status": "stub", + "message": f"Index data ({index_code}, {days}d) — not yet implemented", + }, ensure_ascii=False) + + +def get_sector(date=None): + """Get sector performance. Stub - to be implemented with AKShare.""" + # TODO: Implement with akshare.stock_board_industry_name_em() + return json.dumps({ + "status": "stub", + "message": "Sector data — not yet implemented", + }, ensure_ascii=False) + + +FUNCTIONS = { + "quote": get_quote, + "financial": get_financial, + "moneyflow": get_moneyflow, + "index": get_index, + "sector": get_sector, +} + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: market_data.py [json_args]") + sys.exit(1) + + func_name = sys.argv[1] + args = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {} + + if func_name not in FUNCTIONS: + print(f"Unknown function: {func_name}") + sys.exit(1) + + result = FUNCTIONS[func_name](**args) + print(result) diff --git a/scripts/sentiment.py b/scripts/sentiment.py new file mode 100644 index 0000000..8db12bd --- /dev/null +++ b/scripts/sentiment.py @@ -0,0 +1,47 @@ +""" +News and sentiment analysis for A-share stocks. + +Usage: python3 sentiment.py +""" + +import sys +import json + + +def news(symbol, limit=20): + """Get recent news for a stock. Stub - to be implemented with AKShare.""" + return json.dumps({ + "status": "stub", + "message": f"News for {symbol} — not yet implemented", + "items": [], + }, ensure_ascii=False) + + +def market_sentiment(): + """Get overall market sentiment score. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": "Market sentiment — not yet implemented", + "score": None, + }, ensure_ascii=False) + + +FUNCTIONS = { + "news": news, + "market_sentiment": market_sentiment, +} + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: sentiment.py [json_args]") + sys.exit(1) + + func_name = sys.argv[1] + args = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {} + + if func_name not in FUNCTIONS: + print(f"Unknown function: {func_name}") + sys.exit(1) + + result = FUNCTIONS[func_name](**args) + print(result) diff --git a/scripts/stock_analyzer.py b/scripts/stock_analyzer.py new file mode 100644 index 0000000..5de0bbb --- /dev/null +++ b/scripts/stock_analyzer.py @@ -0,0 +1,54 @@ +""" +Single stock deep analysis module. +Analyzes a stock across technical, fundamental, capital flow, and sentiment dimensions. + +Usage: python3 stock_analyzer.py +""" + +import sys +import json + + +def analyze(symbol): + """Full analysis report. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": f"Analysis for {symbol} — not yet implemented", + "symbol": symbol, + "overall_score": None, + "dimensions": { + "technical": {"score": None, "details": None}, + "capital_flow": {"score": None, "details": None}, + "fundamental": {"score": None, "details": None}, + "sentiment": {"score": None, "details": None}, + }, + }, ensure_ascii=False) + + +def technical(symbol): + """Technical analysis only. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": f"Technical analysis for {symbol} — not yet implemented", + }, ensure_ascii=False) + + +FUNCTIONS = { + "analyze": analyze, + "technical": technical, +} + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: stock_analyzer.py [json_args]") + sys.exit(1) + + func_name = sys.argv[1] + args = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {} + + if func_name not in FUNCTIONS: + print(f"Unknown function: {func_name}") + sys.exit(1) + + result = FUNCTIONS[func_name](**args) + print(result) diff --git a/scripts/stock_screener.py b/scripts/stock_screener.py new file mode 100644 index 0000000..51e25ad --- /dev/null +++ b/scripts/stock_screener.py @@ -0,0 +1,68 @@ +""" +Multi-factor A-share stock screening engine. + +Usage: python3 stock_screener.py +""" + +import sys +import json + + +def multi_factor(strategy="comprehensive", sector=None, market_cap="all", top_n=10): + """Multi-factor scoring screen. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": f"Multi-factor screen (strategy={strategy}, sector={sector}) — not yet implemented", + "strategy": strategy, + "results": [], + }, ensure_ascii=False) + + +def strong(sector=None, top_n=20): + """Strong trend screen. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": "Strong trend screen — not yet implemented", + "results": [], + }, ensure_ascii=False) + + +def breakout(lookback_days=60, top_n=20): + """Volume breakout screen. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": f"Breakout screen ({lookback_days}d) — not yet implemented", + "results": [], + }, ensure_ascii=False) + + +def oversold(top_n=20): + """Oversold rebound screen. Stub - to be implemented.""" + return json.dumps({ + "status": "stub", + "message": "Oversold screen — not yet implemented", + "results": [], + }, ensure_ascii=False) + + +FUNCTIONS = { + "multi_factor": multi_factor, + "strong": strong, + "breakout": breakout, + "oversold": oversold, +} + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: stock_screener.py [json_args]") + sys.exit(1) + + func_name = sys.argv[1] + args = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {} + + if func_name not in FUNCTIONS: + print(f"Unknown function: {func_name}") + sys.exit(1) + + result = FUNCTIONS[func_name](**args) + print(result)