
1. 从零到一为什么你需要一个AI选股系统如果你和我一样每天开盘前都要花大量时间翻看财经新闻、浏览研报、盯着K线图试图从海量信息中找出那么一两个“潜力股”那你一定深有体会这活儿太累了而且效率极低。人的精力是有限的面对数千只股票和瞬息万变的市场单靠人力筛选不仅容易错过机会更可能因为情绪波动做出非理性的判断。这就是我决定动手搭建一个AI选股系统的初衷——不是要取代人类的决策而是让机器成为我们最不知疲倦、最客观冷静的“研究助理”。这个系统的核心目标很明确自动化、客观化、高效率。它能在每天收盘后自动抓取最新的市场数据运用预设的量化策略进行分析最终筛选出符合特定条件的股票并推送到我的手机上。整个过程从数据获取到结果推送完全无需人工干预。我给它起了个名字叫“OpenClaw每日潜力股筛选器”。OpenClaw是一个新兴的、基于C#开发的AI Agent开发框架它最大的特点就是轻量、灵活特别适合我们这种想要快速实现自动化工作流的个人开发者。相比于一些庞大的量化平台它更像一把瑞士军刀让你能用最熟悉的代码C#和相对简单的配置构建出功能强大的智能体Agent。那么这个系统具体能做什么简单来说它每天会帮我完成三件事第一自动获取全市场股票的实时行情和基本面数据第二运行我精心设计并可以不断优化的选股策略模型第三将筛选出的Top 5股票列表通过飞书机器人发送给我附上简单的入选理由。这样一来我第二天开盘前只需要花10分钟就能快速掌握经过初筛的标的把宝贵的时间和精力集中在更深度的研究和最终的交易决策上。无论你是编程新手还是有一定经验的开发者只要跟着下面的步骤都能在短时间内拥有属于自己的“AI投研助手”。2. 核心组件拆解OpenClaw Agent框架与选股逻辑设计在动手敲代码之前我们必须先搞清楚整个系统的骨架。一个能稳定运行的AI选股系统绝不是简单的脚本堆砌它需要几个核心组件协同工作。我的设计主要围绕OpenClaw框架展开因为它完美契合了“轻量集成”和“灵活调度”的需求。2.1 OpenClaw框架你的自动化大脑OpenClaw本质上是一个用于构建和运行AI智能体Agent的框架。你可以把它理解为一个“智能体操作系统”。在这个系统里一个智能体就是一个能感知环境、进行决策并执行动作的独立程序单元。对于我们的选股系统我们需要设计一个主智能体它的核心职责是协调调度。这个主智能体内部又包含几个关键的“技能”Skill数据抓取技能负责调用外部API获取股票数据。这部分需要与行情数据源对接。策略分析技能这是选股的核心。它接收原始数据运行我们设定的量化筛选规则计算出每只股票的“得分”或“信号”。结果推送技能将筛选结果格式化并通过消息通道如飞书、钉钉、微信发送出去。OpenClaw的好处在于它用清晰的C#类和方法封装了这些概念。定义一个技能就是继承一个基类并实现几个关键方法组装智能体就是将这些技能像乐高积木一样组合起来。框架会负责智能体的生命周期管理、技能间的消息传递以及异常处理。这意味着我们不需要从零开始写多线程、写事件循环可以专注于业务逻辑本身。2.2 选股策略量化模型的灵魂策略部分是整个系统的灵魂。我采用的是一种多因子综合评分模型它不追求预测股价而是通过多个维度对股票进行“体检”找出当前状态下相对健康、有潜力的标的。我的初版策略主要包含以下几个因子你可以根据自身风险偏好和市场理解进行调整动量因子过去20个交易日的涨幅。寻找处于上升趋势的股票但避免涨幅过大的“高位股”。我设置的条件是涨幅在5%到30%之间。波动率因子过去20个交易日的历史波动率。过高的波动率意味着风险大我倾向于选择波动率处于市场中等偏低水平的股票以控制回撤。估值因子市盈率PE和市净率PB。寻找估值相对合理的股票避免陷入“价值陷阱”或“估值泡沫”。我会设定PE和PB的历史分位数阈值比如要求其处于历史30%到70%的分位区间。流动性因子日均成交额。确保股票有足够的流动性方便进出。通常要求日均成交额大于1亿元。技术形态因子简易版例如当前价格是否站在20日均线之上。这是一个简单的趋势过滤条件。每个因子都会根据规则计算出一个0到1的分数然后根据我赋予的权重进行加权求和得到每只股票的综合得分。最后对所有股票按得分从高到低排序取前5名。这里的关键在于权重不是一成不变的。我会根据市场风格是成长股行情还是价值股行情定期回测和调整权重。OpenClaw的灵活性允许我将策略参数如权重、阈值放在配置文件中修改后无需重新编译代码重启智能体即可生效。2.3 数据源与工具链系统的血液与骨骼没有数据再好的策略也是无米之炊。对于个人开发者有几种性价比高的数据获取方式免费/低成本API如AKShare一个基于Python的财经数据接口库但我们可以通过封装HTTP服务或直接调用其数据文件来与C#交互、Tushare需注册获取Token、新浪财经/东方财富的公开接口。这些接口通常有频率和数量的限制但对于日级别的选股足够用。本地量化平台如果你使用JoinQuant、RiceQuant等平台的本地研究环境可以直接使用它们提供的内置数据API数据质量更高更稳定。我的选择是使用AKShare因为它免费、全面且维护活跃。我写了一个简单的Python Flask服务专门负责按需调用AKShare获取A股列表、日线行情、基本面数据等然后我的C# OpenClaw智能体通过HTTP请求来调用这个服务。这样就将数据获取的复杂性隔离在了Python端C#端只需处理HTTP通信和JSON解析架构更清晰。工具链方面除了OpenClaw框架本身我们还需要开发环境Visual Studio 2022或VS Code安装.NET 6 SDK。包管理器NuGet。我们需要通过它安装OpenClaw的核心库、HTTP客户端库如RestSharp或HttpClient、JSON处理库如Newtonsoft.Json或System.Text.Json。部署环境可以是Windows服务器、Linux服务器Ubuntu或者更简单的——Docker容器。我强烈推荐使用Docker它能将应用及其所有依赖打包确保在任何环境下的运行一致性彻底解决“在我机器上好好的”这类问题。3. 实战部署十分钟搭建OpenClaw选股智能体理论讲完我们进入最激动人心的实操环节。我将以在Ubuntu服务器上使用Docker部署为例展示最清晰、最易复现的路径。如果你用Windows步骤大同小异主要是安装Docker Desktop和修改一下路径。3.1 环境准备与OpenClaw安装首先确保你有一台云服务器如腾讯云、阿里云的轻量应用服务器或本地Linux机器并已安装Docker和Docker Compose。获取OpenClaw Docker镜像OpenClaw社区提供了官方镜像大大简化了部署。打开终端执行以下命令拉取镜像docker pull crestodian/openclaw:latest这里你可能注意到了crestodian这个发布者。根据社区资料Crestodian是OpenClaw项目相关的贡献者或组织其发布的镜像通常是经过测试的相对稳定版本。准备配置文件目录在服务器上创建一个项目目录比如/home/ubuntu/ai_stock_picker并在里面创建两个子目录config和skills。mkdir -p /home/ubuntu/ai_stock_picker/{config,skills,logs}config用于存放智能体和技能的配置文件skills用于存放我们自定义的技能代码编译后的DLL文件logs用于存放运行日志。编写智能体配置文件在config目录下创建StockPickerAgent.json。这个文件定义了我们的智能体。{ AgentName: 每日选股智能体, Description: 自动执行选股策略并推送结果, Skills: [ { SkillName: DataFetcherSkill, AssemblyPath: /app/skills/StockPickerSkills.dll, ClassName: StockPickerSkills.DataFetcherSkill }, { SkillName: StrategyAnalyzerSkill, AssemblyPath: /app/skills/StockPickerSkills.dll, ClassName: StockPickerSkills.StrategyAnalyzerSkill }, { SkillName: NotifierSkill, AssemblyPath: /app/skills/StockPickerSkills.dll, ClassName: StockPickerSkills.NotifierSkill } ], Schedule: 0 30 16 * * 1-5, MaxConcurrentOperations: 1 }Skills数组列出了智能体拥有的三个技能它们都位于同一个DLL文件中。Schedule是Cron表达式这里表示“每周一到周五的下午16:30收盘后执行”。这是关键确保在交易日收盘后运行。MaxConcurrentOperations设为1保证同一时间只执行一个任务避免资源冲突。3.2 开发核心技能数据、策略与通知接下来我们需要在本地开发环境Windows/Mac上用Visual Studio创建C#类库项目编写这三个技能。项目需要引用OpenClaw的NuGet包如OpenClaw.Core。1. DataFetcherSkill数据抓取技能这个技能负责调用我们之前提到的Python数据服务。核心是重写ExecuteAsync方法。using OpenClaw.Core; using System.Net.Http; using System.Threading.Tasks; namespace StockPickerSkills { public class DataFetcherSkill : SkillBase { private readonly HttpClient _httpClient; private readonly string _dataServiceUrl; public DataFetcherSkill(IConfiguration configuration) { // 从配置读取数据服务地址 _dataServiceUrl configuration[DataService:BaseUrl]; _httpClient new HttpClient(); } public override async TaskSkillResult ExecuteAsync(SkillContext context) { LogInfo(开始获取A股市场数据...); // 示例获取全市场股票列表和昨日行情 var stockListResponse await _httpClient.GetAsync(${_dataServiceUrl}/api/stock_basic); if (!stockListResponse.IsSuccessStatusCode) { return SkillResult.Failure($获取股票列表失败: {stockListResponse.StatusCode}); } var stockList await stockListResponse.Content.ReadAsStringAsync(); // 这里可以继续获取市盈率、市净率等基本面数据 // var peResponse await _httpClient.GetAsync(${_dataServiceUrl}/api/stock_valuation); // 将获取到的原始数据存入上下文供下一个技能使用 context.SetData(RawStockList, stockList); // context.SetData(RawValuationData, peData); LogInfo(市场数据获取完成。); return SkillResult.Success(数据抓取成功); } } }注意在实际项目中你需要处理JSON反序列化将字符串转换为强类型的股票对象列表。同时要考虑网络异常、数据缺失的重试机制。我的做法是引入Polly库来实现简单的重试策略。2. StrategyAnalyzerSkill策略分析技能这是最核心的部分接收原始数据运行量化模型。using OpenClaw.Core; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json.Linq; namespace StockPickerSkills { public class StrategyAnalyzerSkill : SkillBase { public override async TaskSkillResult ExecuteAsync(SkillContext context) { LogInfo(开始执行选股策略分析...); // 从上下文中取出上一个技能存储的数据 var rawStockListJson context.GetDatastring(RawStockList); if (string.IsNullOrEmpty(rawStockListJson)) { return SkillResult.Failure(未获取到有效的股票列表数据。); } // 解析JSON数据这里简化处理实际应定义实体类 var stockArray JArray.Parse(rawStockListJson); var scoredStocks new ListStockScore(); foreach (var stock in stockArray) { var code stock[code].ToString(); var name stock[name].ToString(); var closePrice stock[close].ToObjectdouble(); var pe stock[pe].ToObjectdouble?() ?? 0; // 处理空值 var pb stock[pb].ToObjectdouble?() ?? 0; var turnover stock[turnover].ToObjectdouble(); // 成交额单位万 // 计算因子分数示例需根据完整数据实现 double momentumScore CalculateMomentumScore(stock); double volatilityScore CalculateVolatilityScore(stock); double valuationScore CalculateValuationScore(pe, pb); double liquidityScore (turnover 10000) ? 1.0 : 0.0; // 成交额大于1亿得满分 // 加权综合得分权重可配置 double totalScore momentumScore * 0.3 volatilityScore * 0.2 valuationScore * 0.3 liquidityScore * 0.2; scoredStocks.Add(new StockScore { Code code, Name name, Score totalScore }); } // 按得分降序排序取前5 var top5Stocks scoredStocks.OrderByDescending(s s.Score).Take(5).ToList(); // 将结果存入上下文 context.SetData(Top5Stocks, top5Stocks); LogInfo($策略分析完成筛选出{top5Stocks.Count}支潜力股。); return SkillResult.Success(策略分析完成); } // 以下是简化的因子计算函数实际需要更复杂的历史数据计算 private double CalculateMomentumScore(JToken stock) { /* 实现20日涨幅计算逻辑 */ } private double CalculateVolatilityScore(JToken stock) { /* 实现20日波动率计算逻辑 */ } private double CalculateValuationScore(double pe, double pb) { /* 实现PE/PB分位数计算逻辑 */ } } public class StockScore { public string Code { get; set; } public string Name { get; set; } public double Score { get; set; } } }踩坑提示因子计算依赖历史数据。如果你的数据服务只提供最新日期的数据就需要在DataFetcherSkill中一次性获取足够长时间序列的数据比如最近60个交易日并传递给分析技能。数据在技能间的传递要规划好避免频繁的IO操作拖慢速度。3. NotifierSkill通知技能将结果推送到飞书。using OpenClaw.Core; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace StockPickerSkills { public class NotifierSkill : SkillBase { private readonly HttpClient _httpClient; private readonly string _feishuWebhookUrl; public NotifierSkill(IConfiguration configuration) { _feishuWebhookUrl configuration[Feishu:WebhookUrl]; _httpClient new HttpClient(); } public override async TaskSkillResult ExecuteAsync(SkillContext context) { var top5Stocks context.GetDataListStockScore(Top5Stocks); if (top5Stocks null || top5Stocks.Count 0) { LogWarning(未获取到选股结果跳过通知。); return SkillResult.Success(无结果通知跳过); } // 构建飞书富文本消息 var messageContent new { msg_type post, content new { post new { zh_cn new { title 今日AI选股结果, content new Listobject[] { new object[] { new { tag text, text 根据量化策略筛选今日潜力股Top 5如下\n } }, new object[] { new { tag text, text ----------------------------\n } } } } } } }; // 动态添加股票信息 foreach (var stock in top5Stocks) { var stockText $**{stock.Name} ({stock.Code})** - 综合评分: {stock.Score:F2}\n; ((JArray)messageContent.content.post.zh_cn.content).Add(new JArray(new { tag text, text stockText })); } // 添加尾部说明 ((JArray)messageContent.content.post.zh_cn.content).Add(new JArray(new { tag text, text \n----------------------------\n*数据来源: AKShare策略模型: 多因子综合评分* })); var jsonPayload Newtonsoft.Json.JsonConvert.SerializeObject(messageContent); var httpContent new StringContent(jsonPayload, Encoding.UTF8, application/json); var response await _httpClient.PostAsync(_feishuWebhookUrl, httpContent); if (response.IsSuccessStatusCode) { LogInfo(选股结果已成功推送至飞书。); return SkillResult.Success(通知发送成功); } else { var error await response.Content.ReadAsStringAsync(); LogError($飞书消息发送失败: {error}); return SkillResult.Failure($通知发送失败: {response.StatusCode}); } } } }开发完成后将项目编译成StockPickerSkills.dll并上传到服务器的/home/ubuntu/ai_stock_picker/skills/目录下。3.3 配置与启动让智能体跑起来编写Docker Compose文件在项目根目录创建docker-compose.yml这是最优雅的部署方式。version: 3.8 services: openclaw-stock-picker: image: crestodian/openclaw:latest container_name: ai_stock_picker restart: unless-stopped # 自动重启保证服务持续运行 volumes: - ./config:/app/config # 挂载配置文件目录 - ./skills:/app/skills # 挂载技能DLL目录 - ./logs:/app/logs # 挂载日志目录 environment: - ASPNETCORE_ENVIRONMENTProduction - OpenClaw__AgentConfigPath/app/config/StockPickerAgent.json # 如果需要连接宿主机的服务如Python数据服务可能需要配置network_mode: host # network_mode: host关键点在于volumes它将宿主机的目录映射到容器内部这样我们修改配置或更新技能DLL时只需替换宿主机的文件然后重启容器即可无需重新构建镜像。配置应用设置在config目录下创建appsettings.json存放数据服务地址、飞书Webhook等敏感或可配置信息。{ DataService: { BaseUrl: http://你的服务器IP:5000 // Python数据服务的地址 }, Feishu: { WebhookUrl: https://open.feishu.cn/open-apis/bot/v2/hook/你的Webhook令牌 } }安全提醒切勿将包含真实Token或密钥的配置文件提交到Git等版本控制系统。可以通过Docker Compose的environment部分注入环境变量或在生产环境使用密钥管理服务。启动服务在项目根目录执行一条命令一切就绪。docker-compose up -d使用docker-compose logs -f openclaw-stock-picker可以实时查看智能体的启动和运行日志。如果一切正常你会看到智能体加载了配置文件、注册了技能并等待Cron调度触发。4. 调优、排错与进阶思考系统跑起来只是第一步要让其真正可靠、有用还需要持续的观察、调优和问题排查。4.1 日常监控与日志分析OpenClaw框架和我们的技能代码都会产生日志。日志是我们排查问题的第一手资料。我强烈建议将日志级别设置为Information或Debug并定期检查logs目录下的文件。查看定时任务是否触发在设定的时间交易日16:30后查看日志中是否有“开始获取A股市场数据…”等字样。如果没有检查服务器的系统时间、时区设置以及Docker容器的时钟是否与宿主机同步。检查技能执行链路观察三个技能是否按顺序执行成功。如果某个技能失败日志会明确报错例如网络超时、数据解析异常等。验证飞书消息确保消息能成功送达。如果没收到检查飞书机器人的Webhook URL是否正确以及消息内容格式是否符合飞书的要求。一个常见的坑是技能执行超时。如果数据获取或策略分析非常耗时可能会超过OpenClaw框架默认的任务超时时间。这时需要在智能体配置文件中增加TimeoutInSeconds: 300这样的配置为任务预留充足时间。4.2 策略回测与迭代优化初始的策略参数和权重很可能不是最优的。我们需要一个反馈循环来优化它。历史回测编写一个简单的回测脚本可以用Python也可以用C#使用历史数据运行你的选股策略看看如果按照这个策略在过去一段时间内交易收益率、夏普比率、最大回撤等指标如何。这是验证策略有效性的基础。模拟盘跟踪将每日选出的5支股票放入一个模拟股票池虚拟买入并跟踪其后续一段时间的表现如5日、20日涨跌幅。记录每只股票的“命中率”上涨概率和平均收益。分析归因定期比如每季度分析策略的表现。是哪类因子贡献了主要收益在哪种市场环境下牛市、熊市、震荡市策略会失效根据分析结果动态调整因子权重或引入新的过滤条件。我的经验是不要追求一个“圣杯”策略。市场在变有效的因子也在变。这个系统的价值在于提供了一个可以快速迭代、测试想法的自动化平台。你可以轻松地创建StrategyAnalyzerSkillV2、V3通过修改配置文件来切换使用哪个技能从而对比不同策略的效果。4.3 常见问题与故障排除OpenClaw启动失败提示“无法加载技能DLL”检查技能DLL的路径在配置文件中是否正确是否上传到了正确的挂载目录检查技能DLL的依赖项如Newtonsoft.Json是否齐全可以尝试将技能项目发布为“独立部署”模式将所有依赖打包进一个文件夹然后上传整个文件夹。检查Docker容器内的.NET运行时版本是否与编译技能DLL时使用的目标框架兼容确保使用匹配的镜像标签。数据获取技能总是超时或返回空数据检查Python数据服务是否正常运行在宿主机上用curl http://localhost:5000/api/stock_basic测试一下。检查如果数据服务在宿主机Docker容器默认的桥接网络可能无法通过localhost访问。需要改用宿主机的真实IP或将Docker Compose的网络模式改为host注意安全风险。检查AKShare等数据源接口是否有更新免费接口不稳定是常态需要有备用方案或错误重试机制。飞书消息格式错误发送失败检查飞书机器人消息格式非常严格。使用在线JSON格式化验证工具检查你构建的消息体。特别注意富文本内容中content字段的嵌套结构。检查飞书机器人是否已被添加到正确的群聊Webhook URL是否还有效4.4 系统扩展与进阶玩法当基础系统稳定运行后你可以考虑以下扩展方向让它变得更强大多策略并行与投票机制不再是一个策略定乾坤。可以部署多个不同的StrategyAnalyzerSkill例如价值策略、成长策略、技术策略让它们同时对市场进行扫描。最后用一个“投票聚合技能”来综合各策略的推荐结果形成最终的Top 5名单。这能有效分散单一策略失效的风险。集成大语言模型LLM进行解读单纯的股票列表和分数可能不够直观。可以在NotifierSkill之后增加一个LLMCommentSkill。将选股结果和简单的财务指标、近期新闻摘要一起发送给本地部署的Ollama运行Llama 3、Qwen等开源模型让AI生成一段简要的“今日选股逻辑解读”或“风险提示”附在飞书消息里。这能让你的报告更具可读性和洞察力。接入更多数据源除了行情和基本面可以接入舆情数据如股吧热词、产业链数据、宏观经济指标等作为新的因子纳入模型让选股维度更立体。实现简单的自动化交易谨慎这是一个更进阶、风险也更高的领域。你可以让系统在筛选出股票后通过券商提供的条件单API或自动化交易接口执行预设的买入指令。但必须极度谨慎务必加入严格的风控规则如单只股票最大仓位、总体仓位控制、止损条件并先在模拟环境中充分测试。我强烈建议将交易决策权留给人系统只做辅助推荐。搭建这个系统的过程与其说是为了得到一个“黑箱”赚钱工具不如说是一次绝佳的、将软件开发、数据工程和金融知识结合的实践。它强迫你去思考市场的逻辑去理解数据背后的意义去设计稳健的系统架构。每天下午准时收到的选股清单更像是对自己逻辑和代码的一次次小考。经过几个月的运行和迭代我不仅对市场有了更量化的认识C#和Docker的熟练度也大大提升。最重要的是它把我从重复、繁琐的初筛劳动中解放了出来让我能更专注于策略的思考和深度的公司研究。