AlphaEar Stock Skill Overview Search A-Share/HK/US stock tickers and retrieve historical price data (OHLCV). Capabilities 1. Stock Search & Data Use via . - Search : - Fuzzy search by code or name (e.g., "Moutai", "600519"). - Returns: List of . - Get Price : - Returns DataFrame with OHLCV data. - Dates format: "YYYY-MM-DD". - Get Fundamentals : - Returns dict with sector, industry, market cap, PE ratio, and summary. - Supports A-Share/HK/US stocks. Dependencies - , , , - (stock tables) Notes - Proxy : For US stock data (via ), you may need to set environment variables if your network cannot…

, '', query, flags=re.IGNORECASE)\n \n # 常见缩写映射\n aliases = {\n \"CATL\": \"宁德时代\",\n \"BYD\": \"比亚迪\",\n \"TSLA\": \"特斯拉\",\n \"Moutai\": \"贵州茅台\",\n \"Tencent\": \"腾讯\",\n \"Alibaba\": \"阿里巴巴\",\n \"Meituan\": \"美团\",\n }\n \n search_query = aliases.get(clean_query.upper(), clean_query)\n \n # Robustness: if regex-like ticker code is embedded in query (e.g. \"300364 中文在线\"), try to extract it\n if not search_query.isdigit():\n # Extract explicit 5-6 digit codes\n match = re.search(r'\\b(\\d{5,6})\\b', clean_query)\n if match:\n search_query = match.group(1)\n\n res = self.db.search_stock(search_query, limit)\n if not res and search_query.isalpha():\n # Robustness: mock search hit for alphabetic US tickers\n return [{\"code\": search_query.upper(), \"name\": search_query.upper()}]\n return res\n\n def get_stock_price(\n self,\n ticker: str,\n start_date: Optional[str] = None,\n end_date: Optional[str] = None,\n force_sync: bool = False,\n ) -> pd.DataFrame:\n \"\"\"\n 获取指定股票的历史价格数据。优先从本地缓存读取,缺失时自动从网络补齐。\n \n Args:\n ticker: 股票代码,如 \"600519\"(贵州茅台)或 \"000001\"(平安银行)。\n start_date: 开始日期,格式 \"YYYY-MM-DD\"。默认为 90 天前。\n end_date: 结束日期,格式 \"YYYY-MM-DD\"。默认为今天。\n \n Returns:\n 包含 date, open, close, high, low, volume, change_pct 列的 DataFrame。\n \"\"\"\n now = datetime.now()\n if not end_date:\n end_date = now.strftime('%Y-%m-%d')\n if not start_date:\n start_date = (now - timedelta(days=90)).strftime('%Y-%m-%d')\n\n df_db = self.db.get_stock_prices(ticker, start_date, end_date)\n \n need_update = False\n if df_db.empty:\n need_update = True\n else:\n db_latest = pd.to_datetime(df_db['date'].max())\n req_latest = pd.to_datetime(end_date)\n if (req_latest - db_latest).days > 2:\n need_update = True\n\n if force_sync:\n need_update = True\n\n if need_update:\n logger.info(f\"📡 Data stale or missing for {ticker}, syncing from network...\")\n \n is_us_stock = bool(re.search(r'[a-zA-Z]', ticker)) and not bool(re.search(r'\\d{5,6}', ticker))\n \n if is_us_stock:\n clean_ticker = ticker.upper()\n else:\n # 清洗 ticker,确保只包含数字(Akshare A 股接口通常只需要数字代码)\n clean_ticker = \"\".join(filter(str.isdigit, ticker))\n if not clean_ticker:\n logger.warning(f\"⚠️ Unsupported ticker format: {ticker}\")\n return df_db\n\n try:\n s_fmt = start_date.replace(\"-\", \"\")\n e_fmt = end_date.replace(\"-\", \"\")\n \n df_remote = None\n \n def fetch_data_akshare():\n \"\"\"主路径: akshare\"\"\"\n if is_us_stock:\n return _fetch_data_yfinance()\n if len(clean_ticker) == 5:\n return ak.stock_hk_hist(\n symbol=clean_ticker, period=\"daily\",\n start_date=s_fmt, end_date=e_fmt,\n adjust=\"qfq\"\n )\n else:\n return ak.stock_zh_a_hist(\n symbol=clean_ticker, period=\"daily\",\n start_date=s_fmt, end_date=e_fmt,\n adjust=\"qfq\"\n )\n\n def _fetch_data_yfinance():\n \"\"\"美股路径: yfinance\"\"\"\n yf_ticker = yf.Ticker(clean_ticker)\n end_dt = datetime.strptime(end_date, \"%Y-%m-%d\") + timedelta(days=1)\n df_us = yf_ticker.history(start=start_date, end=end_dt.strftime(\"%Y-%m-%d\"))\n if df_us.empty:\n return pd.DataFrame()\n \n df_us = df_us.reset_index()\n date_col = 'Date' if 'Date' in df_us.columns else df_us.columns[0]\n df_us = df_us.rename(columns={\n 'Open': 'open', 'Close': 'close',\n 'High': 'high', 'Low': 'low', 'Volume': 'volume'\n })\n \n if pd.api.types.is_datetime64_any_dtype(df_us[date_col]):\n df_us['date'] = df_us[date_col].dt.strftime('%Y-%m-%d')\n else:\n df_us['date'] = pd.to_datetime(df_us[date_col]).dt.strftime('%Y-%m-%d')\n \n df_us['change_pct'] = df_us['close'].pct_change() * 100\n df_us['change_pct'] = df_us['change_pct'].fillna(0)\n \n return df_us[['date', 'open', 'close', 'high', 'low', 'volume', 'change_pct']]\n\n def fetch_data_eastmoney():\n \"\"\"降级路径: 东方财富直接 HTTP\"\"\"\n logger.info(f\"📡 Trying EastMoney direct for {clean_ticker}...\")\n return EastMoneyDirect.fetch_kline(clean_ticker, s_fmt, e_fmt)\n\n # === 多源尝试: akshare → 东方财富直接 ===\n try:\n try:\n df_remote = fetch_data_akshare()\n except (RequestException, Exception) as e:\n if \"Proxy\" in str(e) or \"proxy\" in str(e):\n logger.warning(f\"⚠️ Proxy error detected: {e}. Retrying with proxy disabled...\")\n with temporary_no_proxy():\n df_remote = fetch_data_akshare()\n else:\n raise e\n except Exception as e:\n logger.warning(f\"⚠️ akshare failed for {clean_ticker}: {e}\")\n if not is_us_stock:\n try:\n df_remote = fetch_data_eastmoney()\n except Exception as e2:\n logger.warning(f\"⚠️ EastMoney direct also failed for {clean_ticker}: {e2}\")\n raise e # 抛出原始错误\n \n if df_remote is not None and not df_remote.empty:\n if not is_us_stock:\n df_remote = df_remote.rename(columns={\n '日期': 'date', '开盘': 'open', '收盘': 'close',\n '最高': 'high', '最低': 'low', '成交量': 'volume',\n '涨跌幅': 'change_pct'\n })\n # 确保日期格式正确\n df_remote['date'] = pd.to_datetime(df_remote['date']).dt.strftime('%Y-%m-%d')\n \n # 只有在获取到有意义的数据时才保存\n self.db.save_stock_prices(clean_ticker, df_remote) # 保存时使用清洗后的 clean_ticker\n \n # 重新查询数据库返回结果,保证一致性\n return self.db.get_stock_prices(clean_ticker, start_date, end_date)\n else:\n logger.warning(f\"⚠️ Akshare returned empty data for {clean_ticker}\")\n \n except KeyError as e:\n # Akshare 有时在某些股票无数据时会抛出 KeyError\n logger.warning(f\"⚠️ Akshare data missing for {clean_ticker}: {e}\")\n except (RequestException, ConnectionError) as e:\n logger.error(f\"❌ Network error during Akshare sync for {clean_ticker}: {e}\")\n except sqlite3.Error as e:\n logger.error(f\"❌ Database error during Akshare sync for {clean_ticker}: {e}\")\n except Exception as e:\n logger.error(f\"❌ Unexpected error during Akshare sync for {clean_ticker}: {e}\")\n \n return df_db\n\n def get_stock_fundamentals(self, ticker: str) -> Dict:\n \"\"\"\n 获取公司基本面数据(市值、行业、市盈率、财务摘要等)。\n \n Args:\n ticker: 股票代码,如 \"600519\"、\"AAPL\" 或 \"00700\"。\n \n Returns:\n 包含基本面字段的字典。\n \"\"\"\n is_us_stock = bool(re.search(r'[a-zA-Z]', ticker)) and not bool(re.search(r'\\d{5,6}', ticker))\n \n # 清洗 ticker\n if is_us_stock:\n clean_ticker = ticker.upper()\n else:\n clean_ticker = \"\".join(filter(str.isdigit, ticker))\n\n if is_us_stock:\n # 美股路径: yfinance\n try:\n tk = yf.Ticker(clean_ticker)\n info = tk.info\n if not info or 'longName' not in info:\n logger.warning(f\"⚠️ No fundamental data found for US stock: {clean_ticker}\")\n return {}\n return {\n \"name\": info.get(\"longName\"),\n \"sector\": info.get(\"sector\"),\n \"industry\": info.get(\"industry\"),\n \"market_cap\": info.get(\"marketCap\"),\n \"pe_ratio\": info.get(\"trailingPE\"),\n \"summary\": info.get(\"longBusinessSummary\", \"\")[:300] + \"...\" if info.get(\"longBusinessSummary\") else \"\",\n \"currency\": info.get(\"currency\")\n }\n except Exception as e:\n logger.error(f\"❌ yfinance fundamentals failed for {clean_ticker}: {e}\")\n return {}\n else:\n # A股/港股路径: akshare\n try:\n # 使用东财接口获取个股基本信息\n df_info = ak.stock_individual_info_em(symbol=clean_ticker)\n if df_info is None or df_info.empty:\n logger.warning(f\"⚠️ No fundamental data found via akshare for: {clean_ticker}\")\n return {}\n \n info_dict = dict(zip(df_info['item'], df_info['value']))\n return {\n \"name\": info_dict.get(\"股票简称\"),\n \"code\": info_dict.get(\"股票代码\"),\n \"sector\": info_dict.get(\"行业\"),\n \"market_cap\": info_dict.get(\"总市值\"),\n \"listing_date\": info_dict.get(\"上市时间\"),\n \"pe_ratio\": info_dict.get(\"市盈率(动)\"),\n }\n except Exception as e:\n logger.error(f\"❌ akshare fundamentals failed for {clean_ticker}: {e}\")\n return {}\n\n\n\ndef get_stock_analysis(ticker: str, db: DatabaseManager) -> str:\n \"\"\"\n 生成指定股票的分析摘要报告。\n \n Args:\n ticker: 股票代码\n db: 数据库管理器实例\n \n Returns:\n Markdown 格式的分析报告,包含价格走势和关键指标。\n \"\"\"\n tools = StockTools(db)\n df = tools.get_stock_price(ticker)\n \n if df.empty:\n return f\"❌ 未能获取 {ticker} 的股价数据。\"\n \n latest = df.iloc[-1]\n change = ((latest['close'] - df.iloc[0]['close']) / df.iloc[0]['close']) * 100\n \n report = [\n f\"## 📊 {ticker} 分析报告\",\n f\"- **查询时段**: {df.iloc[0]['date']} -> {latest['date']}\",\n f\"- **当前价**: ¥{latest['close']:.2f}\",\n f\"- **时段涨跌**: {change:+.2f}%\",\n f\"- **最高/最低**: ¥{df['high'].max():.2f} / ¥{df['low'].min():.2f}\",\n \"\\n### 最近交易概览\",\n \"```\",\n df.tail(5)[['date', 'close', 'change_pct', 'volume']].to_string(index=False),\n \"```\"\n ]\n return \"\\n\".join(report)\n","content_type":"text/x-python; charset=utf-8","language":"python","size":19824,"content_sha256":"2d0d22e18bd343e8fcad07eb189e194eff7bcf1885ec77ce4a6736f204f764b5"},{"filename":"tests/test_stock.py","content":"import sys\nimport os\nimport unittest\n\n# Add skill root to path\nsys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\n\ntry:\n from scripts.stock_tools import StockTools\n from scripts.database_manager import DatabaseManager\nexcept ImportError as e:\n print(f\"Import Error: {e}\")\n sys.exit(1)\n\nclass TestStock(unittest.TestCase):\n def test_init(self):\n print(\"Testing StockTools Iteration...\")\n db = DatabaseManager(\":memory:\")\n tools = StockTools(db)\n self.assertIsNotNone(tools)\n print(\"StockTools Initialized.\")\n\nif __name__ == '__main__':\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":634,"content_sha256":"8ce14eed6e6b2ae04c2ba9482c245be353bfef180cdde39ad04391ebe20df496"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"AlphaEar Stock Skill","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"Search A-Share/HK/US stock tickers and retrieve historical price data (OHLCV).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Capabilities","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Stock Search & Data","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"scripts/stock_tools.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" via ","type":"text"},{"text":"StockTools","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Search","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"search_ticker(query)","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fuzzy search by code or name (e.g., \"Moutai\", \"600519\").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Returns: List of ","type":"text"},{"text":"{code, name}","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Get Price","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"get_stock_price(ticker, start_date, end_date)","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Returns DataFrame with OHLCV data.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dates format: \"YYYY-MM-DD\".","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Get Fundamentals","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"get_stock_fundamentals(ticker)","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Returns dict with sector, industry, market cap, PE ratio, and summary.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Supports A-Share/HK/US stocks.","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Dependencies","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"pandas","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"requests","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"akshare","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"yfinance","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/database_manager.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" (stock tables)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Notes","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Proxy","type":"text","marks":[{"type":"strong"}]},{"text":": For US stock data (via ","type":"text"},{"text":"yfinance","type":"text","marks":[{"type":"code_inline"}]},{"text":"), you may need to set environment variables if your network cannot reach Yahoo Finance directly:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"export HTTP_PROXY=\"http://\u003cproxy_ip>:\u003cport>\"\nexport HTTPS_PROXY=\"http://\u003cproxy_ip>:\u003cport>\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A-Share/HK","type":"text","marks":[{"type":"strong"}]},{"text":": Data is primarily fetched via ","type":"text"},{"text":"akshare","type":"text","marks":[{"type":"code_inline"}]},{"text":" (EastMoney), which usually works best with a direct connection in China. The tool automatically detects proxy issues and attempts direct connection for these markets.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"alphaear-stock","author":"@skillopedia","source":{"stars":2388,"repo_name":"awesome-finance-skills","origin_url":"https://github.com/rkiding/awesome-finance-skills/blob/HEAD/skills/alphaear-stock/SKILL.md","repo_owner":"rkiding","body_sha256":"daffb803d9194ddc31ebfe5134ce162efa6cddd7039a6615cf6bef6c571bc5bc","cluster_key":"14a7d56d85cf83e2f2c6dd26bb82c165a5ed836ab95fe36c704a80cd5121fc86","clean_bundle":{"format":"clean-skill-bundle-v1","source":"rkiding/awesome-finance-skills/skills/alphaear-stock/SKILL.md","attachments":[{"id":"c149b004-99c0-572f-8fd5-ca69a396650b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c149b004-99c0-572f-8fd5-ca69a396650b/attachment.py","path":"scripts/__init__.py","size":0,"sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","contentType":"text/x-python; charset=utf-8"},{"id":"1eb3af2b-5708-5a7f-90a7-b63ec86e990f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1eb3af2b-5708-5a7f-90a7-b63ec86e990f/attachment.py","path":"scripts/database_manager.py","size":4184,"sha256":"bbeb03f924d6739f4d6c58f90c398e13df5f22d07085f41e731b002b2729d6fd","contentType":"text/x-python; charset=utf-8"},{"id":"245773fc-035c-5686-9ab7-fea2717fffd3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/245773fc-035c-5686-9ab7-fea2717fffd3/attachment.py","path":"scripts/stock_tools.py","size":19824,"sha256":"2d0d22e18bd343e8fcad07eb189e194eff7bcf1885ec77ce4a6736f204f764b5","contentType":"text/x-python; charset=utf-8"},{"id":"2f7a6b6c-0150-54c1-b9f0-ef743ac248ec","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2f7a6b6c-0150-54c1-b9f0-ef743ac248ec/attachment.py","path":"tests/test_stock.py","size":634,"sha256":"8ce14eed6e6b2ae04c2ba9482c245be353bfef180cdde39ad04391ebe20df496","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"f3fe78c24c1e1380c04678f773d5db78962ec891600db58e5d811b563ff5dabc","attachment_count":4,"text_attachments":4,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":2,"skill_md_path":"skills/alphaear-stock/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"finance-legal-compliance","category_label":"Finance"},"exact_dupes_collapsed_into_this":1},"version":"v1","category":"finance-legal-compliance","import_tag":"clean-skills-v1","description":"Search A-Share/HK/US finance stock tickers and retrieve finance stock price history. Use when user asks about finance stock codes, recent price changes, or specific company finance stock info."}},"renderedAt":1782981011946}

AlphaEar Stock Skill Overview Search A-Share/HK/US stock tickers and retrieve historical price data (OHLCV). Capabilities 1. Stock Search & Data Use via . - Search : - Fuzzy search by code or name (e.g., "Moutai", "600519"). - Returns: List of . - Get Price : - Returns DataFrame with OHLCV data. - Dates format: "YYYY-MM-DD". - Get Fundamentals : - Returns dict with sector, industry, market cap, PE ratio, and summary. - Supports A-Share/HK/US stocks. Dependencies - , , , - (stock tables) Notes - Proxy : For US stock data (via ), you may need to set environment variables if your network cannot…