Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Chinese A-share stock analysis via natural language using AKShare — real-time quotes, technicals, fundamentals, sectors, and derivatives.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
main.py
1#!/usr/bin/env python32# -*- coding: utf-8 -*-34import argparse5from typing import Any, Dict67from adapters import AkshareAdapter8from formatter import render_output9from router import (10DERIVATIVES,11FUND_BOND,12FUNDAMENTAL,13HK_US_MARKET,14INDEX_REALTIME,15INTRADAY_ANALYSIS,16KLINE_ANALYSIS,17KLINE_CHART,18LIMIT_STATS,19MARGIN_LHB,20MONEY_FLOW,21NEWS,22RESEARCH_REPORT,23SECTOR_ANALYSIS,24STOCK_OVERVIEW,25STOCK_PICK,26VOLUME_ANALYSIS,27HELP,28PORTFOLIO,29parse_query,30)313233def dispatch(intent_obj, adapter: AkshareAdapter) -> Dict[str, Any]:34if intent_obj.intent == INDEX_REALTIME:35return adapter.index_spot(top_n=300)3637if intent_obj.intent == KLINE_ANALYSIS:38top_n = intent_obj.top_n or 1039symbol = intent_obj.symbol or "000001"40period = intent_obj.period or "daily"41return adapter.stock_kline(symbol=symbol, period=period, top_n=top_n)4243if intent_obj.intent == KLINE_CHART:44symbol = intent_obj.symbol or "000001"45period = intent_obj.period or "daily"46days = intent_obj.top_n or 3047return adapter.stock_chart(symbol=symbol, period=period, days=days)4849if intent_obj.intent == INTRADAY_ANALYSIS:50top_n = intent_obj.top_n or 3051symbol = intent_obj.symbol or "000001"52period = intent_obj.period if intent_obj.period in {"1", "5", "15", "30", "60"} else "1"53return adapter.stock_intraday(symbol=symbol, period=period, top_n=top_n)5455if intent_obj.intent == VOLUME_ANALYSIS:56# 调用 a-stock-analysis 脚本进行量能分析57symbol = intent_obj.symbol or "000001"58import subprocess59import os60script_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "a-stock-analysis", "scripts", "analyze.py")61result = subprocess.run(62["python3", script_path, symbol, "--minute"],63capture_output=True, text=True, timeout=3064)65if result.returncode == 0:66return {"ok": True, "text": result.stdout}67else:68return {"ok": False, "error": result.stderr}6970if intent_obj.intent == LIMIT_STATS:71top_n = intent_obj.top_n or 2072return adapter.limit_pool(date=intent_obj.date, top_n=top_n)7374if intent_obj.intent == STOCK_OVERVIEW:75symbol = intent_obj.symbol76if not symbol:77return {78"ok": False,79"error": "请输入股票代码或名称,如:茅台怎么样、宁德时代分析",80"intent": "STOCK_OVERVIEW",81}82return adapter.stock_overview(symbol=symbol)8384if intent_obj.intent == MONEY_FLOW:85top_n = intent_obj.top_n or 1086query = intent_obj.query or ""87if any(k in query for k in ["北向", "南向", "东向", "市场资金", "大盘资金"]):88return adapter.market_money_flow(top_n=top_n, date=intent_obj.date)89if any(k in query for k in ["行业资金", "板块资金", "行业流入", "板块流入"]):90return adapter.sector_money_flow(top_n=top_n)91symbol = intent_obj.symbol92if not symbol:93return {94"ok": False,95"error": "请输入股票代码或名称,如:茅台资金流向、600519资金流",96"intent": "MONEY_FLOW",97}98return adapter.money_flow(symbol=symbol, top_n=top_n)99100if intent_obj.intent == FUNDAMENTAL:101top_n = intent_obj.top_n or 20102symbol = intent_obj.symbol103if not symbol:104return {105"ok": False,106"error": "请输入股票代码或名称,如:茅台财务指标、600519基本面",107"intent": "FUNDAMENTAL",108}109return adapter.fundamental(symbol=symbol, top_n=top_n)110111if intent_obj.intent == MARGIN_LHB:112top_n = intent_obj.top_n or 10113return adapter.margin_lhb(symbol=intent_obj.symbol, date=intent_obj.date, top_n=top_n)114115if intent_obj.intent == NEWS:116top_n = min(intent_obj.top_n or 10, 10)117return adapter.news(top_n=top_n)118119if intent_obj.intent == RESEARCH_REPORT:120top_n = min(intent_obj.top_n or 10, 10)121symbol = intent_obj.symbol122if not symbol:123return {124"ok": False,125"error": "请输入股票代码或名称,如:宁德时代研报、300750机构评级",126"intent": "RESEARCH_REPORT",127}128return adapter.research_report(symbol=symbol, top_n=top_n)129130if intent_obj.intent == STOCK_PICK:131query = intent_obj.query or ""132# 提取板块关键词133sector = None134sector_keywords = [135"半导体", "电子", "汽车", "医药生物", "医药",136"银行", "保险", "证券", "金融",137"房地产", "地产", "电力", "传媒",138"锂电池", "电池", "光伏", "光伏设备",139"软件", "军工", "食品", "饮料", "白酒", "家电", "纺织"140]141for kw in sector_keywords:142if kw in query:143sector = kw144break145return adapter.stock_pick(top_n=5, sector=sector)146147if intent_obj.intent == SECTOR_ANALYSIS:148top_n = intent_obj.top_n or 10149query = intent_obj.query or ""150if any(k in query for k in ["概念", "题材"]):151return adapter.sector_analysis(sector_type="concept", top_n=top_n)152return adapter.sector_analysis(sector_type="industry", top_n=top_n)153154if intent_obj.intent == FUND_BOND:155top_n = intent_obj.top_n or 10156query = (intent_obj.query or "").lower()157scope = "bond" if any(k in query for k in ["可转债", "转债", "债"]) else "fund"158return adapter.fund_bond(scope=scope, symbol=intent_obj.symbol, top_n=top_n)159160if intent_obj.intent == HK_US_MARKET:161top_n = intent_obj.top_n or 10162query = (intent_obj.query or "").lower()163us_tokens = ["美股", "nasdaq", "dow", "道琼斯", "标普", "sp500", "s&p", "纳指", "us"]164market = "us" if any(token in query for token in us_tokens) else "hk"165return adapter.hk_us_market(market=market, top_n=top_n, symbol=intent_obj.symbol)166167if intent_obj.intent == DERIVATIVES:168top_n = intent_obj.top_n or 10169query = intent_obj.query or ""170scope = "options" if any(k in query for k in ["期权", "option", "Option", "OPTIONS"]) else "futures"171return adapter.derivatives(scope=scope, symbol=intent_obj.symbol, top_n=top_n)172173if intent_obj.intent == HELP:174return {175"ok": True,176"source": "help",177"text": """📈 A股分析 Skill 使用指南178179| 类型 | 示例 |180|------|------|181| 大盘 | A股大盘、上证指数 |182| 分时量能 | 茅台量能分析、600519放量分析 |183| K线 | 茅台近30日K线、600519周线 |184| K线图 | 茅台走势图、宁德时代K线图 |185| 涨跌停 | 今日涨停、跌停统计 |186| 资金流 | 茅台资金流向、市场资金流向 |187| 基本面 | 茅台财务指标、ROE |188| 个股综合 | 茅台怎么样、宁德时代分析 |189| 板块 | 行业板块涨跌、概念板块涨跌 |190| 股票推荐 | 推荐股票、半导体股票推荐 |191| 基金/可转债 | 基金净值、可转债行情 |192| 港股 | 港股行情 |193| 新闻 | 财经新闻、宁德时代研报 |194| 持仓管理 | 我的持仓、添加持仓 600519 --cost 10.5 --qty 1000、持仓分析 |195196直接发给我就能查~"""197}198199if intent_obj.intent == PORTFOLIO:200import subprocess201import os202portfolio_script = os.path.join(os.path.dirname(__file__), "..", "a-stock-analysis", "scripts", "portfolio.py")203204query = intent_obj.query or ""205206# 解析持仓命令207if "添加" in query or "add" in query.lower():208# 提取代码、成本、数量209import re210code_match = re.search(r"\b(\d{6})\b", query)211cost_match = re.search(r"--?cost\s*(\d+\.?\d*)", query)212qty_match = re.search(r"--?qty\s*(\d+)", query) or re.search(r"数量\s*(\d+)", query)213214if code_match and cost_match and qty_match:215code = code_match.group(1)216cost = cost_match.group(1)217qty = qty_match.group(1)218result = subprocess.run(219["python3", portfolio_script, "add", code, "--cost", cost, "--qty", qty],220capture_output=True, text=True, timeout=10221)222return {"ok": True, "source": "portfolio", "text": result.stdout or "已添加持仓"}223else:224return {"ok": False, "error": "请输入:添加持仓 代码 --cost 成本价 --qty 数量\n例如:添加持仓 600519 --cost 10.5 --qty 1000"}225226elif "分析" in query:227result = subprocess.run(228["python3", portfolio_script, "analyze"],229capture_output=True, text=True, timeout=60230)231return {"ok": True, "source": "portfolio", "text": result.stdout or "暂无持仓"}232233elif "删除" in query or "移除" in query:234import re235code_match = re.search(r"\b(\d{6})\b", query)236if code_match:237code = code_match.group(1)238result = subprocess.run(239["python3", portfolio_script, "remove", code],240capture_output=True, text=True, timeout=10241)242return {"ok": True, "source": "portfolio", "text": result.stdout or "已删除"}243else:244return {"ok": False, "error": "请输入要删除的股票代码"}245246else:247# 显示持仓248result = subprocess.run(249["python3", portfolio_script, "show"],250capture_output=True, text=True, timeout=10251)252return {"ok": True, "source": "portfolio", "text": result.stdout or "暂无持仓"}253254return {255"ok": True,256"source": "framework",257"message": "该意图已识别,当前阶段先返回基础占位结果",258"intent": intent_obj.intent,259"parsed": {260"symbol": intent_obj.symbol,261"date": intent_obj.date,262"period": intent_obj.period,263"top_n": intent_obj.top_n,264},265}266267268def main() -> None:269parser = argparse.ArgumentParser(description="A股分析 Skill 基础框架")270parser.add_argument("--query", required=True, help="自然语言请求,例如:分析 600519 最近 30 天 K线")271parser.add_argument("--platform", default="qq", choices=["qq", "telegram"], help="输出平台")272args = parser.parse_args()273274intent_obj = parse_query(args.query)275adapter = AkshareAdapter()276result = dispatch(intent_obj, adapter)277output = render_output(intent_obj, result, platform=args.platform)278print(output)279280281if __name__ == "__main__":282main()283