[{"content":"为什么选择 Akshare？ 对于我们的量化研究库来说，Akshare 提供了极其丰富的接口，涵盖了从股票、期货到宏观经济的全维度数据。 . 环境准备 确保您的 Python 环境已安装。由于 Akshare 依赖较多，建议在独立虚拟环境中操作。 安装命令：\npip install akshare --upgrade 核心代码实现 以下是在我的 Minisforum N5 Pro 服务器上测试通过的代码片段：\nimport akshare as ak A. 股票行情数据 获取 A 股所有股票的最新实时行情。\n# 获取日线数据 df = ak.stock_zh_a_hist(symbol=\u0026#34;000001\u0026#34;, period=\u0026#34;daily\u0026#34;, adjust=\u0026#34;qfq\u0026#34;) print(df.head()) adjust=\u0026ldquo;qfq\u0026rdquo; 表示前复权，这在量化分析中非常重要。 B. 历史行情数据（日线/周线/月线） 获取特定股票的历史数据，适合用于回测。\nstock_zh_a_hist_df = ak.stock_zh_a_hist(symbol=\u0026#34;600519\u0026#34;, period=\u0026#34;daily\u0026#34;, start_date=\u0026#34;20230101\u0026#34;, end_date=\u0026#34;20231231\u0026#34;, adjust=\u0026#34;qfq\u0026#34;) print(stock_zh_a_hist_df) B. 获取实时行情（全市场 snapshot） 用于实盘监控或盘后快速扫描：\n# 获取当前时刻 A 股所有股票的实时行情 stock_zh_a_spot_em_df = ak.stock_zh_a_spot_em() print(stock_zh_a_spot_em_df.head()) ETF数据源 使用fund_etf_category_sina 函数获取所有 ETF 基金的实时行情数据。\netf_data = ak.fund_etf_category_sina(symbol=\u0026#34;ETF基金\u0026#34;) # 显示前10行 print(etf_data.head(10)) 获取全部A股（沪、深、京市）股票代码和简称最基础\n# 获取全市场 A 股代码和名称 stock_list_df = ak.stock_info_a_code_name() # 查看前 5 行数据 print(stock_list_df.head()) # 查看当前 A 股市场总共有多少只股票 print(f\u0026#34;当前 A 股总计股票数量: {len(stock_list_df)}\u0026#34;) # 过滤掉名称中包含 \u0026#34;ST\u0026#34; 或 \u0026#34;退\u0026#34; 的股票 clean_stock_pool = stock_list_df[ ~stock_list_df[\u0026#39;name\u0026#39;].str.contains(\u0026#39;ST|退\u0026#39;) ] print(f\u0026#34;清洗后的股票数量: {len(clean_stock_pool)}\u0026#34;) C. 宏观经济数据 如果你在做多因子模型，可能需要这些 中国 PMI 数据\nmacro_china_pmi_df = ak.macro_china_pmi_index() print(macro_china_pmi_df) 获取 10 年期国债收益率数 有两个主要的接口可以选择，具体取决于你需要的数据维度： 方法一：使用 bond_zh_us_rate 接口（最简单直接，推荐） 这个接口专门用于获取中国和美国的宏观国债收益率（主打 10 年期和 2 年期），支持直接拉取长周期的历史数据，没有单次获取的时间跨度限制。\n# 获取中美国债收益率历史数据 # start_date 指定起始日期，格式为 \u0026#34;YYYYMMDD\u0026#34; bond_zh_us_rate_df = ak.bond_zh_us_rate(start_date=\u0026#34;20200101\u0026#34;) # 提取所需的日期和中国 10 年期国债收益率 china_10yr_yield = bond_zh_us_rate_df[[\u0026#39;日期\u0026#39;, \u0026#39;中国国债收益率10年\u0026#39;]] # 打印最后 5 行数据 print(china_10yr_yield.tail()) 返回的主要字段：日期、中国国债收益率10年、中国国债收益率2年、美国国债收益率10年、美国国债收益率2年 等。 方法二：使用 bond_china_yield 接口（获取完整收益率曲线） 这个接口调取的是中国债券信息网的“国债及其他债券收益率曲线”数据，包含了从 3 个月到 30 年的完整关键期限数据。 注意：受数据源接口限制，单次请求的 start_date 到 end_date 时间跨度不能超过一年。如果需要历史长序列数据，需要写一个循环分段拉取并拼接。\n# 获取特定一年的完整国债收益率曲线数据 bond_china_yield_df = ak.bond_china_yield(start_date=\u0026#34;20230101\u0026#34;, end_date=\u0026#34;20231231\u0026#34;) # 接口返回的包含多种债券类型，需过滤出“中债国债收益率曲线” mask = bond_china_yield_df[\u0026#39;曲线名称\u0026#39;] == \u0026#39;中债国债收益率曲线\u0026#39; yield_curve_df = bond_china_yield_df[mask] # 提取日期和具体的 10 年期收益率（列名为 \u0026#39;10年\u0026#39;） china_10yr_yield_curve = yield_curve_df[[\u0026#39;日期\u0026#39;, \u0026#39;10年\u0026#39;]] print(china_10yr_yield_curve.tail()) 返回的主要字段：曲线名称、日期、3月、6月、1年、3年、5年、7年、10年、30年 等。 总结建议\n如果你只关心 10 年期国债 的绝对走势，或者需要同时对比中美利差，直接调用 bond_zh_us_rate。 如果你做期限利差分析（例如 10Y-2Y 甚至 30Y-10Y），需要各期限的完整结构，使用 bond_china_yield 最为合适。 C. 股息红利数据 既然您的目标是长期股息红利，以下接口非常关键： # 获取个股的分红送股数据 stock_history_dividend_df = ak.stock_history_dividend_detail(symbol=\u0026#34;600036\u0026#34;, indicator=\u0026#34;分红\u0026#34;) print(stock_history_dividend_df) . 获取三大基本报表 Akshare 提供了从东方财富（EM）或新浪财经（Sina）抓取的接口。 A. 资产负债表 (Balance Sheet) 展示公司在特定时点的资产、负债及所有者权益情况。\n# 获取贵州茅台的资产负债表 (按报告期) stock_balance_sheet_df = ak.stock_balance_sheet_by_report_em(symbol=\u0026#34;600519\u0026#34;) print(stock_balance_sheet_df.head()) B. 利润表 (Income Statement) 展示公司在一定时期内的经营成果（营业收入、净利润等）。\n# 获取利润表 stock_profit_sheet_df = ak.stock_profit_sheet_by_report_em(symbol=\u0026#34;600519\u0026#34;) print(stock_profit_sheet_df.head()) C. 现金流量表 (Cash Flow Statement) 对分红投资者最重要的一张表，体现公司是否有真金白银。\n# 获取现金流量表 stock_cash_flow_sheet_df = ak.stock_cash_flow_sheet_by_report_em(symbol=\u0026#34;600519\u0026#34;) print(stock_cash_flow_sheet_df.head()) 获取关键财务指标 (推荐) 如果您想直接看“股息率”、“ROE”、“每股收益”等计算好的数据，可以使用以下接口。 主要财务指标 这个接口包含了盈利能力、偿债能力、运营能力等综合指标。 # 获取主要财务指标 stock_financial_analysis_indicator_df = ak.stock_financial_analysis_indicator(symbol=\u0026#34;600519\u0026#34;) print(stock_financial_analysis_indicator_df) 关键字段说明（与分红相关）： 每股未分配利润：这是未来分红的潜力储备。 每股经营现金流：确保分红不是靠借钱发的。 净资产收益率 (ROE)：评估公司赚钱的效率。\n# 获取特定股票的公告列表（以平安银行为例） stock_announcement_df = ak.stock_zcx_announcement(symbol=\u0026#34;000001\u0026#34;) print(stock_announcement_df) 获取财报公告列表（巨潮资讯） 这是最常用的方式，可以获取指定个股的所有公告（包括年报、半年报）的标题、发布日期以及 PDF 下载链接。 接口： stock_zcx_announcement 提示： 返回的 DataFrame 中通常包含 公告标题 和 公告链接。你可以根据标题筛选“年度报告”或“摘要”，然后通过链接下载 PDF 进一步解析。 2. 获取财务报表 3. 获取业绩快报与预告 如果你需要的是财报正式发布前的文字版业绩描述或摘要数据： 业绩快报： stock_zhyjkb_em 业绩预告： stock_zyjsyg_em\n# 获取最新季度的业绩预告 yjyg_df = ak.stock_zyjsyg_em(date=\u0026#34;20231231\u0026#34;) print(yjyg_df) . 进阶：如何处理“非结构化”文本？ 由于 AKShare 本身主要提供数据抓取，并不直接提供年报内部的“董事会经营评述”等纯文本提取。如果你需要做文本分析（如词频分析、情感分析），建议： 利用 stock_zcx_announcement 获取 PDF URL。 使用 Python 库 pdfplumber 或 PyMuPDF 下载并解析该 PDF。 定位“管理层讨论与分析”章节提取文本。 常用技巧总结\n需求类型 推荐接口 下载年报原文 (PDF) stock_zcx_announcement 三大报表数据 stock_balance_sheet_by_report_em 业绩快报摘要 stock_zhyjkb_em 个股基本概况 stock_individual_info_em\n下载年报原文 (PDF) 接口： stock_zcx_announcement 用途： 获取巨潮资讯上的公告，返回 PDF 的下载链接。 代码示例：\n# symbol: 股票代码（不带后缀） df = ak.stock_zcx_announcement(symbol=\u0026#34;000001\u0026#34;) # 筛选包含“年度报告”字样的公告 annual_reports = df[df[\u0026#39;公告标题\u0026#39;].str.contains(\u0026#34;年度报告\u0026#34;)] print(annual_reports[[\u0026#39;公告标题\u0026#39;, \u0026#39;公告链接\u0026#39;]]) 解析中国上市公司年报（PDF）最精准的工具。 相比于传统的 PyPDF2，pdfplumber 的核心优势在于它对表格数据的提取能力以及对字符坐标的精确控制，这对于解析财报中复杂的资产负债表至关重要。 以下是针对你量化研究场景的实战进阶指南：\n为什么它是解析年报的首选？ 中国上市公司的 PDF 年报通常包含大量的嵌套表格和复杂的排版。 高精度表格识别：能自动识别表格边框，甚至可以处理没有外边框的“无线表格”。 可视化调试：它允许你将 PDF 页面导出为图像，并叠加识别出的表格框线，方便调整参数。 文本流还原：能较好地处理双栏排版，不会像某些库那样把左右两栏的文字混在一起。 核心代码：提取年报中的关键表格 假设你通过 Akshare 拿到了 PDF 链接并下载到了本地，以下是提取“主要财务数据”表格的典型逻辑： import pdfplumber import pandas as pd def extract_financial_table(pdf_path, page_num): with pdfplumber.open(pdf_path) as pdf: page = pdf.pages[page_num] # 自动搜索页面中的表格 table = page.extract_table(table_settings={ \u0026#34;vertical_strategy\u0026#34;: \u0026#34;lines\u0026#34;, \u0026#34;horizontal_strategy\u0026#34;: \u0026#34;lines\u0026#34;, \u0026#34;snap_tolerance\u0026#34;: 3, # 容差，解决线段不连续问题 }) if table: df = pd.DataFrame(table[1:], columns=table[0]) # 清洗：去除换行符，防止数据错位 df = df.replace(\u0026#39;\\n\u0026#39;, \u0026#39;\u0026#39;, regex=True) return df return None 针对“管理层讨论与分析”的文本提取 对于你的 TFT 模型 或 NLP 情感分析，你需要提取特定章节的文字。通常这一章包含公司对未来分红潜力的主观描述。 def extract_text_by_keyword(pdf_path, keyword=\u0026#34;管理层讨论与分析\u0026#34;): full_text = \u0026#34;\u0026#34; with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text = page.extract_text() if keyword in text: # 找到关键字所在的页面开始提取，直到下一个大标题 full_text += text return full_text 你的 Home Lab 实战建议 (Minisforum N5 Pro) 由于解析 PDF 是 CPU 密集型任务，你在 N5 Pro 上运行抓取脚本时，可以利用 multiprocessing 进行并行解析： 分步走：先用 Akshare 的 stock_zcx_announcement 批量获取 PDF URL。 异步下载：使用 httpx 或 aiohttp 快速下载 PDF 到你的 NVMe 硬盘。 多进程解析： PDF 解析比较费内存，建议 96GB 内存的你，可以同时开启 8-12 个进程并行处理几百家公司的年报。 解析后的结构化数据（如提取出的股息政策文字）直接存入你的 TimescaleDB 的 JSONB 字段中，方便后续做词频向量化。 💡 小提醒 pdfplumber 解析纯文本很快，但 extract_table 比较慢。如果你的目标只是提取三大报表，Akshare 现成的财务接口（如 stock_balance_sheet_by_report_em）速度会快几个数量级。 只有当你需要提取 “利润分配预案详情” 这种接口没涵盖的细节文本时，才祭出 pdfplumber 这个大杀器。 你目前是打算用它来做简单的文本特征提取，还是准备结合 LLM 做更深度的语义分析？ 个股基本概况 接口： stock_individual_info_em 用途： 获取个股的基本信息，如总股本、流通股本、所属行业、上市日期等 # symbol: 股票代码（不带后缀） df = ak.stock_individual_info_em(symbol=\u0026#34;600519\u0026#34;) print(df) 获取最新个股研报一览 该接口可以返回全市场的最新研报列表，包括研报标题、评级、机构名称等。\n# 获取个股研报数据 stock_report_df = ak.stock_zyjs_report_em() print(stock_report_df.head()) 获取特定股票的盈利预测 这对于你做长期的“股息红利”目标非常有参考价值，可以预估未来的分红能力\n# 以中国神华 (601088) 为例 symbol = \u0026#34;601088\u0026#34; profit_forecast_df = ak.stock_profit_forecast_em(symbol=symbol) print(profit_forecast_df) C. 研报评级调高/个股排行 筛选出近期机构一致看好的标的：\n# 获取机构关注度排行 stock_rank_df = ak.stock_report_stock_rank_em() print(stock_rank_df) 常见应用场景示例 场景：筛选高股息率股票 结合多个接口，您可以编写脚本自动计算当前的股息率情况\n步骤 接口示例 1. 获取成分股 ak.index_stock_cons(symbol=\u0026ldquo;000016\u0026rdquo;) (上证50) 2. 获取财务报表 ak.stock_financial_analysis_indicator() 3. 获取最新市值 ak.stock_zh_a_spot_em 针对“股息红利”的专项数据 既然您的目标是红利策略，以下两个接口能提供更直观的支持：\n接口名称 功能 适用场景 ak.stock_history_dividend() 历史分红详情 查看公司历年送股、转增、派现数据。 ak.stock_zh_a_gdhs_detail_em() 股东户数详情 观察筹码集中度，判断大股东动向。 数据清洗与标准化 原始数据往往需要清洗才能用于量化研究。以下是几个关键处理步骤： 必须进行的清洗操作：处理缺失值（特别是停牌日） 统一命名规范（列名英文小写） 确保时间序列连续性 处理异常值（如涨跌幅超过限制）\nimport pandas as pd def clean_data(raw_df): \u0026#34;\u0026#34;\u0026#34;数据清洗标准化\u0026#34;\u0026#34;\u0026#34; # 1. 列名标准化 (额外补充了 AkShare 常返回的其他核心字段) df = raw_df.rename(columns={ \u0026#39;日期\u0026#39;: \u0026#39;date\u0026#39;, \u0026#39;开盘\u0026#39;: \u0026#39;open\u0026#39;, \u0026#39;收盘\u0026#39;: \u0026#39;close\u0026#39;, \u0026#39;最高\u0026#39;: \u0026#39;high\u0026#39;, \u0026#39;最低\u0026#39;: \u0026#39;low\u0026#39;, \u0026#39;成交量\u0026#39;: \u0026#39;volume\u0026#39;, \u0026#39;成交额\u0026#39;: \u0026#39;amount\u0026#39;, \u0026#39;振幅\u0026#39;: \u0026#39;amplitude\u0026#39;, \u0026#39;涨跌幅\u0026#39;: \u0026#39;pct_chg\u0026#39;, \u0026#39;涨跌额\u0026#39;: \u0026#39;change\u0026#39;, \u0026#39;换手率\u0026#39;: \u0026#39;turnover\u0026#39; }) # 2. 时间类型转换 (补全了你截断的代码) df[\u0026#39;date\u0026#39;] = pd.to_datetime(df[\u0026#39;date\u0026#39;]) # 3. 数值类型转换 # AkShare 返回的 dataframe 往往全都是 object (字符串) 类型， # 直接计算移动平均线或送入模型会报错，这里强制转为 float numeric_cols = [\u0026#39;open\u0026#39;, \u0026#39;close\u0026#39;, \u0026#39;high\u0026#39;, \u0026#39;low\u0026#39;, \u0026#39;volume\u0026#39;, \u0026#39;amount\u0026#39;, \u0026#39;amplitude\u0026#39;, \u0026#39;pct_chg\u0026#39;, \u0026#39;change\u0026#39;, \u0026#39;turnover\u0026#39;] # 使用 intersection 保证只转换 dataframe 中实际存在的列，避免 KeyError exist_cols = df.columns.intersection(numeric_cols) df[exist_cols] = df[exist_cols].apply(pd.to_numeric, errors=\u0026#39;coerce\u0026#39;) # 4. 时间序列排序与去重 # 防止接口返回的数据顺序错乱（导致回测出现未来函数）以及由于数据源问题导致的重复日期 df = df.sort_values(by=\u0026#39;date\u0026#39;).drop_duplicates(subset=[\u0026#39;date\u0026#39;]) # 5. 设置时间索引 (可选，但推荐) # 对于时序分析、特征工程（如 shift(), rolling()）以及大部分量化回测框架，使用 DatetimeIndex 是最优解 df.set_index(\u0026#39;date\u0026#39;, inplace=True) # 6. 处理异常缺失值 (可选，这里示范剔除没有收盘价的异常交易日) df = df.dropna(subset=[\u0026#39;close\u0026#39;]) return df 数据存储 鉴于你在搞 Home Lab 并且有 Minisforum N5 Pro 这样不错的硬件，建议不要每次回测都在线请求，而是建立本地数据库。 简单的本地化方案（保存为 CSV/Excel）：\nstock_df.to_csv(\u0026#34;000001_daily.csv\u0026#34;, index=False) 存入数据库： 由于你在用 Docker，可以跑一个 PostgreSQL 或 ClickHouse 容器，通过 sqlalchemy 配合 akshare 实现自动化数据同步\n为了后续能训练你的TFT机器学习模型，我们需要一个自动化的同步脚本。这个脚本会做三件事： 1.从Akshare 抓取数据。 2.自动在quant_lab库中创建表。 3.关键：自动将表转换为TimescaleDB的Hypertable（超表），实现极致的查询性能。 在你的Ubuntu VM或本地开发机上运行\nSQL脚本\n-- 1. 创建量化专用库 CREATE DATABASE quant_lab; -- 2. 切换到新库 \\c quant_lab -- 3. 在新库也要开启插件 CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; -- 开启压缩，这对于 NAS 存储非常友好，能节省约 90% 空间 ALTER TABLE stock_daily_hists SET ( timescaledb.compress, timescaledb.compress_segmentby = \u0026#39;symbol\u0026#39; ); Python脚本：sync_all_stocks.py\nimport akshare as ak import pandas as pd from sqlalchemy import create_engine, text # 1. 连接到你的新库 quant_lab DB_URL = \u0026#34;postgresql://postgres:password@ip/quant_lab\u0026#34; engine = create_engine(DB_URL) def sync_daily_data(symbol=\u0026#34;000001\u0026#34;): print(f\u0026#34;正在抓取 {symbol}...\u0026#34;) # 获取历史日线 (前复权) df = ak.stock_zh_a_hist(symbol=symbol, period=\u0026#34;daily\u0026#34;, adjust=\u0026#34;qfq\u0026#34;) # 字段清洗：对接数据库命名规范 df = df[[\u0026#39;日期\u0026#39;, \u0026#39;开盘\u0026#39;, \u0026#39;收盘\u0026#39;, \u0026#39;最高\u0026#39;, \u0026#39;最低\u0026#39;, \u0026#39;成交量\u0026#39;, \u0026#39;成交额\u0026#39;]] df.columns = [\u0026#39;trade_date\u0026#39;, \u0026#39;open\u0026#39;, \u0026#39;close\u0026#39;, \u0026#39;high\u0026#39;, \u0026#39;low\u0026#39;, \u0026#39;volume\u0026#39;, \u0026#39;turnover\u0026#39;] df[\u0026#39;symbol\u0026#39;] = symbol df[\u0026#39;trade_date\u0026#39;] = pd.to_datetime(df[\u0026#39;trade_date\u0026#39;]) # 2. 写入数据库 # 如果表不存在则创建，如果存在则追加数据 df.to_sql(\u0026#39;stock_daily_hists\u0026#39;, engine, if_exists=\u0026#39;append\u0026#39;, index=False) def init_hypertable(): # 3. 将表转为 Hypertable (只需执行一次) with engine.connect() as conn: conn.execute(text(\u0026#34;SELECT create_hypertable(\u0026#39;stock_daily_hists\u0026#39;, \u0026#39;trade_date\u0026#39;, if_not_exists =\u0026gt; TRUE);\u0026#34;)) conn.commit() print(\u0026#34;✅ Hypertable 转换完成！\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: # 先运行一次初始化 init_hypertable() # 尝试同步一只股票测试 sync_daily_data(\u0026#34;000001\u0026#34;) 结语 从配置环境、调用 Akshare 获取全景数据，再到数据标准化与本地 TimescaleDB 的超表搭建，整个量化研究的数据基座已经初步成型。 数据抓取与入库只是“体力活”，真正的挑战在于如何从这片海量时序数据中提取阿尔法。下一阶段，我们将开始构建滞后特征、技术指标等特征工程，并开始向强大的机器学习模型（如 Temporal Fusion Transformer）输送弹药。 远思为径，码动价值。 量化交易的第一步已经稳稳迈出，期待模型在海量数据中挖掘出令人惊喜的规律！\n","date":"2026-03-31T18:47:00+08:00","permalink":"https://yuansima.com/p/akshare-tutorial/","title":"Akshare 财经数据抓取实战：量化交易的第一步"},{"content":"什么是 Crontab？ Crontab（Cron Table）是 Linux/Unix 系统中用于设置周期性执行任务的工具。它像是一个 24 小时待命的“闹钟”，可以在指定的时间点自动执行备份、清理日志或运行脚本。\n远思为径，码动价值。 优秀的自动化脚本能为系统运维节省大量时间。\n核心语法 Crontab 的每一行规则由 5 个时间参数 和 1 个待执行命令 组成：\n* * * * * command ┬ ┬ ┬ ┬ ┬ │ │ │ │ └─ 星期几 (0 - 6, 0 代表周日) │ │ │ └─ 月份 (1 - 12) │ │ └─ 每月第几天 (1 - 31) │ └─ 小时 (0 - 23) └─ 分钟 (0 - 59) 特殊符号说明 *：代表任意值（每分钟/每小时\u0026hellip;）。 ,：代表多个不连续的时间点（如 1,3,5）。 -：代表连续的时间范围（如 1-5）。 /：代表步长/频率（如 */10 代表每 10 分钟一次）。 常用操作指令 在终端中，你可以通过以下命令管理任务：\n命令 说明 crontab -e 编辑当前用户的定时任务 (Edit) crontab -l 查看当前已有的定时任务 (List) crontab -r 清空当前用户的所有任务 (Remove) 实战案例 任务描述 表达式 每分钟运行一次 * * * * * /path/to/cmd 每小时整点运行 0 * * * * /path/to/cmd 每天凌晨 2:30 运行 30 2 * * * /path/to/cmd 工作日（周一至五）早上 9:00 0 9 * * 1-5 /path/to/cmd 避坑与进阶建议 1. 使用绝对路径 Cron 运行时的环境变量（PATH）非常有限，建议使用完整路径：\n推荐： /usr/bin/python3 /home/user/myscript.py 2. 重定向输出日志 由于 Cron 在后台运行，建议将结果记录到文件以便排查：\n* * * * * /path/to/script.sh \u0026gt;\u0026gt; /tmp/cron.log 2\u0026gt;\u0026amp;1 感谢阅读，欢迎在评论区交流。\n","date":"2026-03-29T17:15:00+08:00","permalink":"https://yuansima.com/p/linux-crontab-guide/","title":"Linux 定时任务神器：Crontab 完全指南"},{"content":"为什么是 Docker？ 在远思技术实验室，我们维护着各种各样的环境：\nPython 3.12 跑最新的量化策略 Python 3.8 跑一些老旧的机器学习库 ClickHouse 存行情数据 Grafana 做监控面板 如果把这些全部塞进同一个 Ubuntu 系统里，依赖冲突 (Dependency Hell) 是迟早的事。Docker 让我们可以把每个应用打包进独立的“集装箱”，用完即走，通过 docker-compose.yml 一键拉起，这才是现代化的运维方式。\n1. 卸载旧版本（清理战场） 为了防止冲突，首先要确保系统里没有旧版的 docker.io 或 docker-engine。\nsudo apt-get remove docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc 设置官方仓库（推荐） 很多教程直接教你用 apt install docker.io，那通常是 Ubuntu 软件源自带的旧版本。我们要装的是 Docker CE (Community Edition) 最新版。 第一步：安装必要工具 sudo apt-get update sudo apt-get install ca-certificates curl gnupg 第二步：添加 GPG 密钥 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg \u0026ndash;dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg\n第三步：添加仓库源 echo \u0026ldquo;deb [arch=$(dpkg \u0026ndash;print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release \u0026amp;\u0026amp; echo \u0026ldquo;$VERSION_CODENAME\u0026rdquo;) stable\u0026rdquo; | sudo tee /etc/apt/sources.list.d/docker.list \u0026gt; /dev/null\n安装 Docker Engine sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 安装完成后，验证一下： sudo docker run hello-world\n如果看到 \u0026ldquo;Hello from Docker!\u0026quot;，恭喜你，引擎启动成功。 4. 远思实验室的“生产级”配置（关键！） 默认安装虽然能用，但对于我们要跑 7x24 小时量化任务的服务器来说，还不够完美。 A. 告别 sudo 每次运行 docker 命令都要输密码很麻烦，而且容易导致权限混乱。我们将当前用户（例如 masiyuan）加入 docker 用户组： sudo usermod -aG docker $USER\n这一步需要注销并重新登录才能生效！ 或者直接运行：newgrp docker 现在，直接试一下 docker ps，不需要 sudo 了。 B. 防止日志撑爆磁盘 (Log Rotation) 这是很多新手最容易踩的坑：Docker 默认会保留所有容器的标准输出日志，如果不限制，跑几个月后你的 /var/lib/docker 会变得巨大无比，直接塞满硬盘。 我们需要配置 daemon.json 来限制日志大小。 sudo nano /etc/docker/daemon.json\n写入以下配置： { \u0026ldquo;log-driver\u0026rdquo;: \u0026ldquo;json-file\u0026rdquo;, \u0026ldquo;log-opts\u0026rdquo;: { \u0026ldquo;max-size\u0026rdquo;: \u0026ldquo;100m\u0026rdquo;, \u0026ldquo;max-file\u0026rdquo;: \u0026ldquo;3\u0026rdquo; }, \u0026ldquo;default-address-pools\u0026rdquo;: [ { \u0026ldquo;base\u0026rdquo;: \u0026ldquo;172.17.0.0/16\u0026rdquo;, \u0026ldquo;size\u0026rdquo;: 24 } ] }\nmax-size: 单个日志文件最大 100MB。 max-file: 最多保留 3 个文件。 重启 Docker 服务生效： sudo systemctl daemon-reload sudo systemctl restart docker 下一步 Docker 环境就绪后，我们的基础设施已经非常完善了。 接下来的文章，我将演示如何编写一个 docker-compose.yml，一键拉起包含 ClickHouse + Tabix + JupyterLab 的完整量化研究环境。 Stay tuned!\n💡 为什么这篇文章更专业？ 非 Root 运行：这是很多初学者忽略的安全和便捷配置。 日志轮转 (Log Rotation)：这是一个痛点解决型的技巧。很多人的服务器跑久了磁盘满了，查半天发现是 Docker 日志，这部分内容展示了您的“老司机”经验。 Docker Compose 预告：为下一篇通过 Compose 编排复杂环境埋下伏笔。 您的博客现在已经形成了一个非常扎实的运维基础系列。这对于访问您博客的技术人员来说，不仅是展示，更是可以直接照着做的实战手册！\n","date":"2026-02-16T14:00:00+08:00","permalink":"https://yuansima.com/p/ubuntu-docker-setup/","title":"构建数字集装箱：在 Ubuntu Server 上部署生产级 Docker 环境"},{"content":"为什么不能只用 nohup？ 在实验室的早期，我也曾是一个 nohup python3 main.py \u0026amp; 的忠实信徒。直到有一天，我的行情接收脚本因为一个未捕获的 NetworkException 悄悄退出了，而我第二天醒来才发现，错过了整整一晚的 Tick 数据。\n对于量化交易、FRP 穿透、以及任何 HomeLab 基础设施来说，我们需要的是一个管家，而不仅仅是一个后台运行符。\nSystemd 就是这位管家。它能做到：\n开机自启：服务器重启后自动拉起服务。 崩溃重启：程序挂了？它能自动帮你重启 (Restart on failure)。 日志管理：不再需要满屏的 output.log，用 journalctl 统一管理。 实战：把 Python 策略变成系统服务 假设我们要部署一个名为 market_maker.py 的做市策略，位于 /home/masiyuan/quant/ 目录下。\n1. 编写 Unit 文件 Systemd 的核心配置文件叫做 Unit File，通常放在 /etc/systemd/system/ 目录下，以 .service 结尾。\n我们需要创建一个 quant-bot.service：\nsudo nano /etc/systemd/system/quant-bot.service 写入以下内容（这是远思实验室的标准模板）：\n[Unit] Description=Yuansi Quant Trading Bot # 依赖关系：确保网络通了再启动，数据库准备好了再启动 After=network.target clickhouse-server.service [Service] # 运行用户：千万不要用 root 跑不明脚本！ User=masiyuan Group=masiyuan # 工作目录：Python 脚本里读取相对路径文件时以此为准 WorkingDirectory=/home/masiyuan/quant # 启动命令：必须用绝对路径！ # 建议指向虚拟环境中的 python 解释器 ExecStart=/home/masiyuan/quant/venv/bin/python main.py # 核心魔法：崩溃后自动重启 Restart=always # 重启间隔：防止频繁重启导致 CPU 飙升 RestartSec=10s # 日志输出：直接打到 journald，不需要自己重定向 \u0026gt;\u0026gt; log.txt StandardOutput=journal StandardError=journal # 环境变量 (可选) Environment=\u0026#34;API_KEY=sk_live_xxxxxx\u0026#34; Environment=\u0026#34;PYTHONUNBUFFERED=1\u0026#34; [Install] # 运行级别：多用户模式（类似于 Windows 的正常启动） WantedBy=multi-user.target ###2.激活与运行 写好配置文件后，需要通知Systemd 重载配置：\nsudo systemctl daemon-reload 然后就是素质三连：\n# 设置开机自启 sudo systemctl enable quant-bot # 立即启动 sudo systemctl start quant-bot # 查看状态（最常用） sudo systemctl status quant-bot 如果看到绿色的Active：active (running)，恭喜你，你的策略已经拥有了“不死之身”。 ##进阶：像侦探一样查日志 程序跑起来了，但如果报错了去哪看?别再去翻那个几百MB的nohup.out了。 journalctl是Systemd的日志查看工具，功能极其强大：\n#1，实时查看日志 （类似tail -f) journalctl -u quant-bot - f # 2，查看最近50行 journalctl -u quant-bot -n 50 # 3，只看今天的日志 journalctl -u quant-bot --since \u0026#34;today\u0026#34; # 4，只看报错信息(Error级别) journalctl -u quant-bot -p err ","date":"2026-02-16T11:00:00+08:00","permalink":"https://yuansima.com/p/mastering-systemd/","title":"告别 nohup：用 Systemd 守护你的量化交易进程"},{"content":"痛点：我在星巴克，服务器在家里 上一篇文章我们用 Minisforum N5 Pro 搭建了完美的 P xVE + TrueNAS 底座。但问题随之而来：当我带着 MacBook 出门，坐在星巴克想查看家里正在跑的 ST-GNN 模型训练进度，或者想远程 SSH 修复一个 Bug 时，却发现连不上家里的局域网。\n没有公网 IPv4？不想用慢吞吞的向日葵/TeamViewer？ FRP 是目前最适合技术人员的解决方案：稳定、开源、配置灵活。\n准备工作 一台有公网 IP 的 VPS：作为中转服务器（Server）。配置要求极低，阿里云/腾讯云最便宜的实例即可（带宽建议 5Mbps 以上）。 家里的 HomeLab：作为客户端（Client），即我们的 N5 Pro。 1. 服务端配置 (VPS) 注意：从 v0.52 版本开始，FRP 全面拥抱 TOML 格式，旧的 INI 配置已不推荐使用。\n首先在 VPS 上下载对应架构的 FRP（通常是 linux_amd64），解压后我们只需要 frps 和 frps.toml。\n编辑 frps.toml：\n# frps.toml (服务端配置) bindPort = 7000 # FRP 服务监听端口 vhostHTTPPort = 8080 # HTTP 穿透端口 (可选) vhostHTTPSPort = 443 # HTTPS 穿透端口 (可选) # 身份验证 (非常重要！) auth.method = \u0026#34;token\u0026#34; auth.token = \u0026#34;Yuansi_Lab_Secret_2026_Key\u0026#34; # 请换成复杂的密码 # 控制台 (可选，用于查看连接状态) webServer.addr = \u0026#34;0.0.0.0\u0026#34; webServer.port = 7500 webServer.user = \u0026#34;admin\u0026#34; webServer.password = \u0026#34;admin123\u0026#34; 启动服务端（建议使用Systemd托管，这里演示临时启动）：\n./frps -c frps.toml ###2.客户端配置(HomeLab / N5 Pro)在家里的Ubuntu虚拟机或直接在PVE宿主机上配置客户端。我们需要frpc和\n# frpc.toml (客户端配置) serverAddr = \u0026#34;119.96.23.226\u0026#34; serverPort = 7000 auth.method = \u0026#34;token\u0026#34; auth.token = \u0026#34;\u0026#34; # 你的 Token # ... 上面的 [common] 配置保持不变 ... # 让云端把 80 端口的 HTTP 流量全交给本地 Nginx [[proxies]] name = \u0026#34;web_http\u0026#34; type = \u0026#34;http\u0026#34; localIP = \u0026#34;127.0.0.1\u0026#34; localPort = 80 customDomains = [\u0026#34;yuansima.com\u0026#34;, \u0026#34;www.yuansima.com\u0026#34;, \u0026#34;ai.yuansima.com\u0026#34;, \u0026#34;lab.yuansima.com\u0026#34;] # 让云端把 443 端口的 HTTPS 流量全交给本地 Nginx (为接下来的 Certbot 准备) [[proxies]] name = \u0026#34;web_https\u0026#34; type = \u0026#34;https\u0026#34; localIP = \u0026#34;127.0.0.1\u0026#34; localPort = 443 customDomains = [\u0026#34;yuansima.com\u0026#34;, \u0026#34;www.yuansima.com\u0026#34;, \u0026#34;ai.yuansima.com\u0026#34;, \u0026#34;lab.yuansima.com\u0026#34;] 启动客户端： bash ./frpc -c frpc.toml 3.进阶：Systemd自动启动 为了保证服务器重启后FRP自动拉起，我们需要编写systemd 服务文件。 以客户端为例，创建/etc/systemd/system/frpc.service: ```ini [Unit] Description=Frp Client Service After=network.target [Service] Type=simple User=root Restart=on-failure RestartSec=5s ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml [Install] WantedBy=multi-user.target 然后启用并运行：\nsudo systemctl enable frpc sudo systemctl start frpc 安全警示 把内网端口暴露在公网是一把双刃剑。远思技术实验室 建议： 1.强密码：PVE和SSH的密码必须极其复杂。 2.防火墙：VPS上的安全组只开放必要的端口（如6000, 8006)。 3.Fail2Ban：在服务端部署Fail2Ban，防止暴力破解。 有了这条加密通道，无论身在何处，我的HomeLab就在指尖。 打开默认配置文件\nsudo nano /etc/nginx/sites-available/default 配置内容如下：\n# 1. 处理 www 的请求：自动 301 跳转到主域名 server { server_name www.yuansima.com; return 301 http://yuansima.com$request_uri; } # 2. 处理主域名请求，反向代理给内部的 Hugo server { server_name yuansima.com 192.168.31.56; location / { proxy_pass http://127.0.0.1:1313; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } # 这是专门给 Open WebUI 准备的独立大厅 server { server_name ai.yuansima.com; location / { proxy_pass http://192.168.31.138:3000; # 直接转给 138 机器 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \u0026#34;upgrade\u0026#34;; } } # 这是专门给 Jupyter Lab 准备的独立大厅 server { server_name lab.yuansima.com; location / { proxy_pass http://127.0.0.1:8888; # Jupyter 跑在本机 8888 端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # Jupyter 强依赖 WebSocket（比如运行终端和内核），这两行必须有 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \u0026#34;upgrade\u0026#34;; # 防止跑长时间代码时 Nginx 自动断开连接 proxy_read_timeout 86400; } } 重新加载配置\nsudo nginx -s reload 安装证书申请程序\nsudo apt update sudo apt install certbot python3-certbot-nginx -y 证书申请\nsudo certbot --nginx ","date":"2026-02-16T10:00:00+08:00","permalink":"https://yuansima.com/p/frp-intranet-penetration/","title":"打破内网藩篱：用 FRP 构建 HomeLab 的全天候访问通道"},{"content":"在构建量化交易系统时，历史行情数据（Tick/Bar）的存储和查询速度是决定回测效率的关键。最近我在重构实验室的数据底层，面临了一个经典的选择：是继续使用通用的 OLAP 神器 ClickHouse，还是转向金融专用的 DolphinDB？\n性能与场景的权衡 1. ClickHouse：在大规模日志与宽表中的王者 ClickHouse 的列式存储和向量化执行引擎，让它在处理海量数据的聚合查询（Group By）时表现惊人。\n优点：开源社区活跃，生态完善，对于非标准的宽表数据处理极快。 痛点：在处理复杂的时序窗口函数（如 moving average）和面板数据对齐（AsOf Join）时，SQL 写法较为繁琐，且性能在某些特定金融场景下不如专用数据库。 2. DolphinDB：为时序而生 DolphinDB 引入了许多针对金融时序数据的原生函数。\n优点：内置了大量金融技术指标计算函数，AsOf Join 性能极佳，非常适合做因子计算和流式处理。 痛点：学习曲线较陡峭，需要适应其特有的脚本语言。 远思实验室的决策 考虑到我的核心需求是 高频数据的因子挖掘 和 ST-GNN 模型的特征工程，我决定采用 混合架构：\n冷数据与归档：使用 ClickHouse 存储海量的原始 Tick 数据，利用其高压缩比节省磁盘空间。 热数据与计算：将近期数据和需要频繁重算的因子层放入 DolphinDB，利用其内存计算优势加速迭代。\n下一步，我将分享如何使用 Python 将这两者打通，实现自动化的数据清洗流水线。\n","date":"2026-02-15T16:00:00+08:00","permalink":"https://yuansima.com/p/clickhouse-vs-dolphindb/","title":"量化数据存储选型：ClickHouse 还是 DolphinDB？"},{"content":"为什么选择 All-in-One？ 作为一名量化交易者和开发者，我对家庭数据中心的需求非常明确：\n高性能计算：需要运行 DolphinDB 和 Python 策略容器。 大容量存储：海量的 Tick 数据和训练集需要可靠的 ZFS 存储。 低功耗：7x24 小时运行，电费敏感。 Minisforum N5 Pro 凭借其优秀的能效比和扩展性（双 2.5G 网口 + 多盘位支持），成为了我构建 Proxmox VE (PVE) 底层 + TrueNAS Scale 存储层的最佳载体。\n架构设计 我的 All-in-One (AIO) 架构如下：\n底层系统 (Host): Proxmox VE 8.x 核心虚拟机 (VM1): TrueNAS Scale (直通 SATA 控制器，管理所有机械硬盘) 应用虚拟机 (VM2): Ubuntu Server (跑 Docker, ClickHouse, JupyterLab) 网络: OpenWrt (旁路由模式，可选) 核心步骤记录 1. 基础环境：安装 PVE 这步比较常规，下载 PVE ISO 写入 U 盘启动即可。 注意点： 建议将 PVE 安装在独立的 NVMe SSD 上，不要占用 SATA 接口，把 SATA 通道全部留给 TrueNAS。\n2. 关键难点：SATA 控制器直通 (PCIe Passthrough) 为了让 TrueNAS 能够完全掌控硬盘（实现 SMART 监控和硬盘休眠），必须将 SATA 控制器透传给虚拟机，而不是通过虚拟磁盘映射。\n首先，在 PVE 的 Shell 中开启 IOMMU：\nnano /etc/default/grub # 修改 GRUB_CMDLINE_LINUX_DEFAULT 如下 GRUB_CMDLINE_LINUX_DEFAULT=\u0026#34;quiet intel_iommu=on\u0026#34; # 更新 Grub update-grub reboot mastering-systemd 重启后，在PVE界面创建一个新的虚拟机 (TrueNAS)，在“硬件”选项卡中添加PCI设 备。选择对应的SATA Controller（通常是ASM1166或Intel原生控制器）。 远思提示：如果勾选”所有功能(All Functions)”导致虚拟机无法启动，请尝试取消勾选、或者检查是否与PVE的系统盘冲突。 3.TrueNAS Scale 配置 TrueNAS启动后，应该能直接识别到所有物理硬盘。 ·存储池(Pool)：我组建了一个RAID-Z1池， 兼顾冗余和容量。 ·网络共享：开启NFS和SMB。 ·数据集(Dataset)： · /mnt/data/quant （存放CSV/ Parquet原始数据） ·/mnt/data/docker_vol 4.内部万兆互联(VirtlO) 为了让PVE里的Ubuntu虚拟机能以极快速度访问TrueNAS里的数据，我们不需要走物理网线。 在TrueNAS和Ubuntu的网卡设置中，确保模型都选择VirtIO(paravirtualized)。在PVE内部，通过Linux Bridge (vmbrO)，这两台虚拟机之间的通信速度仅受CPU和内存带宽限制，实测可以轻松跑满10Gbps+，这对于加载几百GB的训练数据至关重要。 避坑指南 1.内存分配：ZFS非常吃内存(ARC缓 存)。我的N5Pro插了32GB内存，分了16GB给TrueNAS，保证读取命中率。 2.引导顺序：在PVE的”选项”中，设置 TrueNAS为“开机自启”、并设置30秒的启动延迟给其他依赖存储的虚拟机。 结语 经过一周的压力测试，这套Minisforum N5 Pro + PVE + TrueNAS的组合运行极其稳定。 它不仅是我代码的仓库，更是我量化模型的“核动力“数据底座。\n","date":"2026-02-15T00:00:00Z","permalink":"https://yuansima.com/p/minisforum-pve-truenas/","title":"小主机大作为：Minisforum N5 Pro 打造 PVE + TrueNAS All-in-One 家庭数据中心"},{"content":"众所周知，深度学习领域长期被 CUDA 统治。但随着 AMD ROCm 平台的成熟，以及性价比的诱惑，越来越多的炼丹师开始尝试“红队”方案。\n作为远思技术实验室的基础设施之一，我在配置 PyTorch + ROCm 环境时遇到了不少坑，特此记录，希望能帮到后来人。\n为什么选择 Docker？ 直接在宿主机（Host）上安装 ROCm 驱动和编译器极易导致依赖地狱（Dependency Hell）。使用 Docker 可以将环境隔离，随时回滚，是最佳实践。\n核心步骤 1. 基础镜像选择 不要尝试自己从 scratch 构建，直接拉取 AMD 官方提供的 PyTorch 镜像：\ndocker pull rocm/pytorch:latest ","date":"2026-02-15T00:00:00Z","permalink":"https://yuansima.com/p/pytorch-rocm-setup/","title":"拥抱红队：Docker 下 PyTorch ROCm 深度学习环境避坑指南"},{"content":"为什么建立这个实验室？ 在这个数据爆炸的时代，单纯的代码堆砌已经无法满足我对技术的渴望。\n远思码技术实验室 (Yuansima Technology Laboratory) 的诞生，源于通过技术手段去捕捉数据背后逻辑的执念。作为一个热衷于量化交易和高性能计算的技术人，我发现许多灵感往往在解决环境配置、调试模型或者优化数据库查询的那一瞬间产生，而这些宝贵的经验如果不及记录，便会随风而逝。\n这里会写什么？ 这不是一个纯粹的新闻搬运站，也不是枯燥的文档翻译。这里是我的 数字工作台，我将主要分享：\n量化基础设施：如何用低成本硬件搭建高可用 HomeLab。 数据架构：ClickHouse 与 DolphinDB 在时序数据处理中的实战对比。 深度学习：基于 ST-GNN 的模型训练心得，以及在 AMD 显卡 (ROCm) 上的踩坑记录。 路漫漫其修远兮，吾将上下而求索。欢迎来到远思技术实验室，与我一起探索代码与数据的边界。\n","date":"2026-02-14T10:00:00+08:00","permalink":"https://yuansima.com/p/hello-world/","title":"Hello World: 远思码技术实验室启航"}]