三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

基于OpenClaw与Ollama的AI选股系统:10分钟构建自动化量化分析工具

基于OpenClaw与Ollama的AI选股系统:10分钟构建自动化量化分析工具

1. 项目概述:为什么我们需要一个AI选股系统?

在信息爆炸的今天,个人投资者每天面对的是海量的财经新闻、公司财报、技术指标和社交媒体情绪。手动筛选和分析这些数据,不仅耗时耗力,还极易受到情绪和认知偏差的影响。你是否有过这样的经历:看到某只股票大涨后追悔莫及,或者因为错过一个关键信息而做出错误决策?这正是传统人工选股的痛点所在。

“10分钟打造你的AI选股系统”这个项目,正是为了解决这个核心痛点。它不是一个复杂的量化交易策略,而是一个轻量级、自动化、基于规则的“潜力股侦察兵”。其核心价值在于,利用OpenClaw这个新兴的AI Agent框架,结合实时行情数据,构建一个能够每日自动运行、执行预设筛选逻辑,并最终将结果推送给你的自动化流程。整个过程,从环境搭建到收到第一份股票清单,目标是在10分钟内完成初始化。之后,系统便会像一个不知疲倦的助手,在每天收盘后(或你设定的任何时间)自动工作,为你从茫茫股海中初步筛选出值得进一步关注的5支标的。

这个系统适合谁?它非常适合有一定编程基础、对金融市场感兴趣、希望将重复性分析工作自动化,并想亲身实践AI Agent落地的个人开发者或量化爱好者。你不需要是金融专家或AI科学家,只需要跟着步骤操作,就能拥有一个属于你自己的、7x24小时待命的初级分析工具。接下来,我将带你一步步拆解这个系统的每一个环节,从工具选型到核心逻辑,再到避坑指南,让你不仅能复现,更能理解其背后的“所以然”。

2. 核心工具与框架选型解析

构建这个系统的基石是选择合适的工具。我们的目标是快速、稳定、可扩展,同时兼顾学习成本和部署难度。经过对当前技术生态的评估,我选择了以下核心组件,并解释为什么是它们。

2.1 为什么选择OpenClaw作为AI Agent框架?

在众多AI Agent框架(如LangChain、AutoGen、CrewAI)中,我最终选择了OpenClaw,主要是基于以下几点实战考量:

第一,开发语言亲和力。OpenClaw是基于C#开发的。对于广大使用Windows环境、熟悉.NET生态的开发者来说,这是一个巨大的优势。你不需要额外配置复杂的Python环境(虽然它也支持),在Visual Studio或VS Code里就能获得一流的开发体验和调试支持。这大大降低了入门门槛。

第二,设计理念清晰。OpenClaw强调“技能(Skill)”和“操作(Operation)”的模块化。在我们的选股场景中,“获取股票列表”、“计算技术指标”、“评估基本面”、“生成报告”都可以被设计成独立的Skill。这种设计让系统的逻辑非常清晰,易于维护和扩展。比如,未来你想增加“舆情分析”功能,只需要开发一个新的Skill并注入到工作流中即可,不会影响原有逻辑。

第三,对大模型的良好支持。OpenClaw可以灵活配置后端大模型。无论是通过API调用云端模型(如OpenAI GPT-4、DeepSeek),还是本地部署的Ollama(运行Llama 3、Qwen等开源模型),都能轻松接入。这给了我们成本控制和数据隐私上的灵活性。对于选股这种对实时性要求高、可能需要频繁调用模型的任务,使用本地Ollama+轻量化模型是性价比极高的方案。

第四,社区与热度。从提供的热搜词可以看出,OpenClaw近期关注度很高,围绕其安装、部署、技能开发的教程和讨论正在快速增长。选择一个处于上升期的框架,意味着你能更快地找到解决方案和灵感,踩坑时也更容易获得帮助。

注意:网络热词中提到了“openclaw llamap svr operator(): got exception: { “error“: { “code“: 400” 这类错误。这通常是配置不当,特别是Ollama服务地址或模型名称错误导致的。在后续配置环节我们会重点规避。

2.2 数据源:实时行情API的选择与考量

AI选股,数据是燃料。我们需要稳定、准确、及时的股票数据。这里有几个主流选择:

  1. 免费公开API(如AKShare、Yahoo Finance):优点是免费,适合学习和轻度使用。缺点是稳定性无法保证(雅虎API经常变动),数据可能延迟,且国内访问可能不畅。AKShare是一个优秀的国内开源库,数据源丰富,但需要一定的Python知识来搭建一个简单的数据服务接口供OpenClaw调用。
  2. 专业金融数据服务商(如Tushare、Baostock、JoinQuant):这类数据源通常提供更稳定、更全面的数据,包括历史行情、财务数据、板块信息等。Tushare和Baostock有免费的基础接口,但高级功能需要积分或付费。对于严肃的选股系统,投资一个可靠的数据源是值得的。
  3. 券商API:部分券商为量化交易提供API,数据直接、实时,但通常需要开户并有资金门槛,且绑定特定交易终端,灵活性较差。

我的建议与选择:为了平衡快速启动和系统可靠性,本项目采用“AKShare + 备用数据源”的策略。我们将用AKShare作为主要数据获取工具,因为它免费、开源、支持A股全市场数据,且社区活跃。同时,我们会设计一个简单的数据访问层(Data Access Layer),这样未来如果需要切换到Tushare或其他付费API,只需修改这个层的实现,而不会影响上层的选股逻辑。这是系统可扩展性的一个关键设计。

2.3 自动化调度:让系统自己“醒来”工作

一个不能自动运行的系统,价值减半。我们需要一个调度器,在每天下午收盘后(例如15:30)自动触发整个选股流程。

  • Windows系统:可以使用系统自带的任务计划程序(Task Scheduler)。我们可以创建一个任务,定时执行一个批处理(.bat)文件或PowerShell脚本,这个脚本负责启动我们的OpenClaw选股程序。
  • Linux/macOS系统:首选Cron。这是类Unix系统上最经典、最强大的定时任务工具。一行Cron表达式就能搞定每日定时执行。
  • 跨平台方案(推荐):使用Docker容器来封装整个应用,然后在任何系统上使用Docker的调度工具或结合系统级Cron/任务计划来定时启动容器。这是最干净、最一致的部署方式,避免了“在我机器上好好的”这类环境问题。这也是为什么“docker部署openclaw”成为热词的原因。

在本指南中,我将以Docker + Cron(Linux为例)作为最终部署方案,因为它能确保环境一致性,并且部署步骤清晰。对于Windows用户,我会额外说明如何使用任务计划程序达到相同效果。

3. 系统架构与核心工作流设计

在动手写代码之前,我们必须理清系统的骨架。一个好的架构能让开发事半功倍,也便于后续迭代。我们的AI选股系统核心工作流可以分为四个阶段,如下图所示(概念图):

[定时触发器] -> [数据获取与预处理] -> [AI智能筛选与评分] -> [结果生成与通知]

3.1 第一阶段:定时触发与任务初始化

每天下午3点30分(A股收盘后),Cron服务(或Windows任务计划)被触发。它执行一个简单的Shell脚本或直接命令。这个命令的核心是启动我们的Docker容器,或者直接运行编译好的OpenClaw应用程序。

关键设计点:我们需要确保每次任务都是“无状态”的。即每次运行都从干净的数据开始,避免上次运行的结果影响本次判断。这可以通过在程序开始时清理临时数据、从API重新拉取最新数据来实现。OpenClaw的Agent可以设计成每次启动都重新加载所有Skill和上下文。

3.2 第二阶段:数据获取与预处理

这是整个系统的基石。Agent被触发后,首先调用“数据获取Skill”。这个Skill会执行以下操作:

  1. 获取全市场股票列表:通过AKShare接口,获取当前沪深两市所有上市公司的基本列表,包括股票代码、名称、所属板块等。
  2. 筛选基础池:根据一些硬性规则进行初筛,例如:
    • 剔除ST、*ST股票(高风险警示)。
    • 剔除上市不足N天(例如60天)的次新股(数据不稳定)。
    • 剔除当前停牌的股票。
    • 可选:剔除某些特定板块或股价过低(如低于1元)的股票。 这一步的目的是将几千支股票缩小到一个几百支的“基础股票池”,减少后续计算量。
  3. 获取行情与指标数据:对基础股票池中的每一支股票,并行或批量获取其近期(例如最近60个交易日)的日K线数据。然后,利用这些数据计算出一系列技术指标,例如:
    • 趋势类:MA(移动平均线,如5日、10日、20日)、MACD。
    • 动量类:RSI(相对强弱指数)、KDJ。
    • 波动类:Bollinger Bands(布林带)。 这些计算可以在数据获取Skill中完成,也可以拆分成独立的“指标计算Skill”。

实操心得:批量获取数据时一定要注意API的频率限制。AKShare虽然免费,但过于频繁的请求可能导致IP被临时屏蔽。建议在代码中加入适当的延时(例如time.sleep(0.5))。更好的做法是,如果数据量允许,可以一次性获取全市场数据再在内存中处理,这比逐支股票调用API高效得多。

3.3 第三阶段:AI智能筛选与评分

这是OpenClaw大显身手的环节。我们将设计一个“选股策略Skill”,它接收预处理后的股票数据(DataFrame或对象列表),并应用我们的核心筛选逻辑。

重要认知:这里的“AI”并非指一个黑箱模型凭空生成股票代码,而是用大模型的语言理解能力来执行我们预先定义好的、可解释的量化筛选规则,并可以对结果进行简单的自然语言总结。这是一种“规则引擎+AI润色”的模式,兼顾了确定性和智能性。

我们的筛选逻辑可以分层级:

  1. 量化规则层(硬性过滤):这是一系列明确的、可编程的条件。例如:

    • 条件A:收盘价站上20日均线,且20日均线向上。
    • 条件B:RSI(6) 大于50但小于70(表明处于强势但不过热)。
    • 条件C:成交量大于5日均量线的1.2倍(放量)。
    • 条件D:市值在50亿到500亿之间(中盘股)。 程序会遍历基础股票池,选出同时满足所有(或满足其中若干组合)条件的股票,形成一个“候选清单”。
  2. AI评分与排序层(柔性评估):将“候选清单”连同每支股票的关键指标(价格、涨跌幅、所属板块、市盈率等)一起,构造一段Prompt,提交给OpenClaw配置的大模型(如Llama 3)。Prompt可以这样设计:

    “你是一个专业的股票分析师。请根据以下股票列表及其关键数据,综合考虑技术面强度、基本面健康度(以小市值、低市盈率为佳)和近期市场热度,从中选出你认为未来一周最具上涨潜力的5支股票。请严格按照JSON格式输出,包含股票代码、名称和简要的推荐理由。” 模型会根据其“知识”和上下文理解,对候选股票进行排序和筛选,最终输出5支股票。

为什么这样做?纯量化规则可能选出几十支股票,我们还需要最后一道“人工智能”的关口来压缩到5支,并给出一个像样的理由。这模拟了专业分析师在初步筛选后做最终决策的过程。

3.4 第四阶段:结果生成与推送

“选股策略Skill”输出结果后,由“通知推送Skill”接手。它将AI输出的结构化数据(JSON)格式化成易读的消息。

  • 消息模板:“【AI选股日报】YYYY-MM-DD\n今日为您筛选的5支潜力股如下:\n1. [代码][名称]:推荐理由...\n2...”
  • 推送渠道:根据热搜词,我们可以接入飞书微信。飞书提供了开放的机器人Webhook接口,非常简单。微信推送相对复杂,通常需要借助企业微信机器人或ServerChan等第三方服务。我强烈建议从飞书机器人开始,配置简单,成功率高。
  • 最终动作:推送完成后,Agent记录本次任务日志,然后正常退出。Docker容器停止,等待下一次被Cron唤醒。

4. 详细实现步骤与代码剖析

理论讲完,我们进入实战环节。请跟随以下步骤,一步步搭建你的系统。我将以在Ubuntu服务器上使用Docker部署为例,同时兼顾本地开发调试的场景。

4.1 第一步:基础环境准备与OpenClaw部署

首先,我们需要一个运行环境。使用Docker可以最大程度避免环境冲突。

1. 安装Docker与Docker Compose:如果你的系统没有安装,请参考Docker官方文档。在Ubuntu上,通常几条命令就能搞定。

2. 获取OpenClaw镜像与配置:OpenClaw的Docker镜像可能不在官方仓库,我们需要从其项目地址获取。根据网络热词,我们需要关注配置细节。

# 假设我们从某个Registry拉取镜像,这里用示例名 # docker pull some-registry/openclaw:latest # 更常见的做法是克隆源码,使用提供的Dockerfile自行构建 git clone <OpenClaw的Git仓库地址> cd openclaw

3. 关键配置:连接大模型(Ollama)这是最容易出错的一步。我们需要配置OpenClaw使用本地运行的Ollama服务。

  • 启动Ollama服务:确保Ollama已在后台运行,并下载了所需模型(如llama3.1:8b)。

    ollama run llama3.1:8b # 第一次会自动拉取模型 # 服务默认运行在 http://localhost:11434
  • 配置OpenClaw:在OpenClaw的项目目录中,找到配置文件(通常是appsettings.jsonconfig.json)。你需要修改大模型连接设置。

    { "OpenClaw": { "ModelProvider": "Ollama", // 指定使用Ollama "Ollama": { "BaseUrl": "http://host.docker.internal:11434", // 关键!从容器内访问宿主机服务 "DefaultModel": "llama3.1:8b" // 你下载的模型名 } // ... 其他配置 } }

    致命坑点详解:很多人在Docker中部署时,会遇到openclaw llamap svr operator(): got exception: 400错误。这几乎都是因为BaseUrl配置不对。在Docker容器内部,localhost指向容器自己,而不是宿主机。因此,不能配http://localhost:11434。正确做法是:

    • 在Linux/macOS的Docker Desktop中:使用http://host.docker.internal:11434
    • 在原生Linux Docker环境中:可能需要使用宿主机的真实IP(如http://172.17.0.1:11434),或者创建自定义网络。最稳妥的方式是在docker-compose.yml中将Ollama和OpenClaw放在同一个自定义网络内,然后通过服务名访问。

4. 编写Dockerfile与docker-compose.yml为了管理方便,我们使用Docker Compose来定义Ollama和OpenClaw两个服务。

docker-compose.yml示例:

version: '3.8' services: ollama: image: ollama/ollama:latest container_name: ollama ports: - "11434:11434" volumes: - ollama_data:/root/.ollama restart: unless-stopped openclaw-stock: build: . container_name: openclaw-stock depends_on: - ollama environment: - ASPNETCORE_ENVIRONMENT=Production # 将本地项目目录挂载到容器,方便开发调试 volumes: - ./:/app - ./data:/app/data # 用于持久化数据或配置 working_dir: /app command: ["dotnet", "run", "--project", "YourOpenClawProject.csproj"] restart: "no" # 我们由Cron控制启动,所以不自动重启 volumes: ollama_data:

Dockerfile(位于OpenClaw项目根目录):

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY . . RUN dotnet publish "YourOpenClawProject.csproj" -c Release -o /app/publish FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final WORKDIR /app COPY --from=build /app/publish . ENTRYPOINT ["dotnet", "YourOpenClawProject.dll"]

4.2 第二步:开发选股核心Skills

现在,我们在OpenClaw项目中创建我们的核心Skill。假设项目结构如下:

OpenClawStock/ ├── Skills/ │ ├── DataFetchSkill.cs │ ├── StockFilterSkill.cs │ └── NotificationSkill.cs ├── Services/ │ └── MarketDataService.cs (封装AKShare调用) ├── appsettings.json └── Program.cs

1. DataFetchSkill(数据获取技能):这个Skill负责与外界数据源交互。它内部会调用一个MarketDataService,这个服务封装了所有对AKShare的调用逻辑。

// Skills/DataFetchSkill.cs using OpenClaw.Core.Skills; using System.Threading.Tasks; namespace OpenClawStock.Skills { [Skill(Name = "DataFetch", Description = "获取A股市场数据并进行初步清洗")] public class DataFetchSkill { private readonly IMarketDataService _dataService; public DataFetchSkill(IMarketDataService dataService) { _dataService = dataService; } [SkillOperation] public async Task<StockDataSet> ExecuteAsync() { // 1. 获取全市场股票列表 var allStocks = await _dataService.GetAllStockListAsync(); // 2. 应用硬性规则初筛 var basicPool = allStocks .Where(s => !s.Name.Contains("ST")) // 去ST .Where(s => s.ListingDays > 60) // 上市超60天 .Where(s => s.MarketCap > 5_0000_0000) // 市值大于5亿 .ToList(); // 3. 批量获取这些股票的日K线数据 var symbols = basicPool.Select(s => s.Symbol).ToList(); var historyData = await _dataService.GetBatchDailyHistoryAsync(symbols, 60); // 最近60天 // 4. 计算技术指标 (这里可以调用专门的指标计算库,如Ta-Lib的.NET绑定) foreach (var data in historyData) { data.Indicators.MA5 = CalculateMA(data.ClosePrices, 5); data.Indicators.MA20 = CalculateMA(data.ClosePrices, 20); data.Indicators.RSI6 = CalculateRSI(data.ClosePrices, 6); // ... 计算其他指标 } // 返回一个包含基础池和行情数据的数据集 return new StockDataSet { BasicPool = basicPool, HistoryData = historyData }; } private double CalculateMA(List<double> prices, int period) { /* 实现略 */ } private double CalculateRSI(List<double> prices, int period) { /* 实现略 */ } } }

2. StockFilterSkill(股票筛选技能):这是大脑,它依赖DataFetchSkill获取数据,然后应用规则并调用大模型。

// Skills/StockFilterSkill.cs using OpenClaw.Core.Chat; using OpenClaw.Core.Skills; using System.Linq; using System.Text.Json; using System.Threading.Tasks; namespace OpenClawStock.Skills { [Skill(Name = "StockFilter", Description = "应用量化规则和AI模型筛选潜力股")] public class StockFilterSkill { private readonly IChatClient _chatClient; // OpenClaw注入的聊天客户端,用于调用大模型 private readonly DataFetchSkill _dataFetchSkill; public StockFilterSkill(IChatClient chatClient, DataFetchSkill dataFetchSkill) { _chatClient = chatClient; _dataFetchSkill = dataFetchSkill; } [SkillOperation] public async Task<List<SelectedStock>> ExecuteAsync() { // 1. 获取数据 var dataSet = await _dataFetchSkill.ExecuteAsync(); // 2. 应用量化规则进行硬筛选 var candidates = new List<StockWithIndicator>(); foreach (var stockData in dataSet.HistoryData) { var last = stockData.DailyBars.Last(); var indicators = stockData.Indicators; // 示例规则:价格上穿20日线,且20日线趋势向上,RSI健康,放量 bool rule1 = last.Close > indicators.MA20 && indicators.MA20 > indicators.MA20_PreviousDay; bool rule2 = indicators.RSI6 > 50 && indicators.RSI6 < 75; bool rule3 = last.Volume > indicators.VolumeMA5 * 1.2; if (rule1 && rule2 && rule3) { candidates.Add(new StockWithIndicator { Stock = stockData.Stock, Indicators = indicators, LatestBar = last }); } } if (candidates.Count == 0) return new List<SelectedStock>(); // 3. 如果候选太多(>10),先按某些规则排序截断,减少token消耗 var topCandidates = candidates .OrderByDescending(c => c.LatestBar.ChangePercent) // 按当日涨幅排序 .Take(15) .ToList(); // 4. 构造Prompt,调用大模型做最终5选 var prompt = BuildPrompt(topCandidates); var modelResponse = await _chatClient.GetChatCompletionAsync(prompt); // 5. 解析模型的JSON输出 var selectedStocks = ParseModelResponse(modelResponse); return selectedStocks.Take(5).ToList(); // 确保只返回5支 } private string BuildPrompt(List<StockWithIndicator> candidates) { var candidateInfo = candidates.Select(c => $"- 代码:{c.Stock.Symbol},名称:{c.Stock.Name},最新价:{c.LatestBar.Close},涨跌幅:{c.LatestBar.ChangePercent:F2}%,所属板块:{c.Stock.Industry},市盈率(PE):{c.Stock.PE:F1}").ToList(); string infoString = string.Join("\n", candidateInfo); return $@"你是一个专业的A股投资顾问。请从以下股票列表中,综合考虑技术面表现(趋势、动量)、基本面健康度(优先选择市盈率较低、市值适中的)以及所属板块的近期热度,筛选出5支你认为在未来一周(5个交易日内)最具上涨潜力的股票。 请严格按照以下JSON格式输出,不要有任何额外的解释或标记: {{ ""selected_stocks"": [ {{ ""symbol"": ""股票代码"", ""name"": ""股票名称"", ""reason"": ""不超过20字的简要推荐理由"" }}, ... ] }} 候选股票列表: {infoString}"; } private List<SelectedStock> ParseModelResponse(string response) { // 使用System.Text.Json解析响应 try { var jsonDoc = JsonDocument.Parse(response); var stocks = new List<SelectedStock>(); // ... 解析逻辑 return stocks; } catch { // 如果模型没有返回标准JSON,可以启用一个备用规则(例如按涨幅前5名) return new List<SelectedStock>(); } } } }

4.3 第三步:组装Agent与配置工作流

在OpenClaw中,我们需要在Program.cs或启动配置中注册这些Skill,并定义它们的工作流(Orchestration)。

// Program.cs 或 Startup.cs 的简化示例 using OpenClaw.Core.DependencyInjection; using OpenClawStock.Skills; var builder = WebApplication.CreateBuilder(args); // 1. 添加OpenClaw核心服务 builder.Services.AddOpenClaw(config => { config.ModelProvider = ModelProvider.Ollama; config.OllamaBaseUrl = builder.Configuration["OpenClaw:Ollama:BaseUrl"]; config.DefaultModel = builder.Configuration["OpenClaw:Ollama:DefaultModel"]; }); // 2. 注册我们自定义的服务和Skill builder.Services.AddSingleton<IMarketDataService, AkShareMarketDataService>(); builder.Services.AddSkill<DataFetchSkill>(); builder.Services.AddSkill<StockFilterSkill>(); builder.Services.AddSkill<NotificationSkill>(); // 3. 定义一个简单的顺序工作流 builder.Services.AddHostedService<StockSelectionWorkflow>(); var app = builder.Build(); app.Run(); // 工作流服务 public class StockSelectionWorkflow : BackgroundService { private readonly StockFilterSkill _filterSkill; private readonly NotificationSkill _notificationSkill; public StockSelectionWorkflow(StockFilterSkill filterSkill, NotificationSkill notificationSkill) { _filterSkill = filterSkill; _notificationSkill = notificationSkill; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 这个服务会在应用启动时运行。对于定时任务,我们更希望它被外部触发一次。 // 因此,我们可以把主要逻辑放在一个可调用的方法里,并由Cron启动的进程调用。 // 这里为了示例,我们假设是手动触发或一次性任务。 await RunDailySelectionAsync(); } public async Task RunDailySelectionAsync() { Console.WriteLine($"[{DateTime.Now}] 开始执行每日选股任务..."); try { var selectedStocks = await _filterSkill.ExecuteAsync(); await _notificationSkill.ExecuteAsync(selectedStocks); Console.WriteLine($"[{DateTime.Now}] 每日选股任务完成。"); } catch (Exception ex) { Console.WriteLine($"[{DateTime.Now}] 任务执行失败: {ex.Message}"); // 这里可以添加失败通知 } } }

为了让Cron能调用,我们需要一个简单的入口点。可以在项目中创建一个控制台应用项目,或者直接暴露一个HTTP API端点来触发RunDailySelectionAsync方法。更简单的方式是,将上述工作流编译成控制台程序,由Cron直接执行。

4.4 第四步:配置自动化调度与通知

1. 创建执行脚本:在项目根目录创建run_selection.sh(Linux)或run_selection.bat(Windows)。

#!/bin/bash # run_selection.sh cd /path/to/your/openclaw-stock-docker-compose-directory # 启动服务,执行任务,然后关闭。使用docker-compose run可以运行一次性命令。 docker-compose run --rm openclaw-stock dotnet /app/YourOpenClawProject.dll --task select

假设我们的程序支持一个命令行参数--task select来触发选股工作流。

2. 配置Cron定时任务(Linux):使用crontab -e编辑当前用户的Cron任务。

# 每天下午15点35分(收盘后5分钟)执行选股任务 35 15 * * 1-5 /bin/bash /path/to/your/run_selection.sh >> /path/to/your/stock_selection.log 2>&1
  • 1-5表示周一到周五。
  • >> ...log 2>&1将标准输出和错误都重定向到日志文件,便于排查。

3. 配置飞书机器人通知:NotificationSkill中实现飞书Webhook调用。

  • 首先,在飞书群里添加一个“群机器人”,获取它的Webhook URL。
  • 然后在Skill中发送HTTP POST请求。
// Skills/NotificationSkill.cs using System.Net.Http.Json; using System.Threading.Tasks; public class NotificationSkill { private readonly string _feishuWebhookUrl; private readonly HttpClient _httpClient; public NotificationSkill(IConfiguration config, HttpClient httpClient) { _feishuWebhookUrl = config["Feishu:WebhookUrl"]; _httpClient = httpClient; } [SkillOperation] public async Task ExecuteAsync(List<SelectedStock> stocks) { if (stocks == null || !stocks.Any()) { await SendMessage("【AI选股日报】今日未筛选出符合条件的潜力股。"); return; } var message = new { msg_type = "text", content = new { text = $"【AI选股日报】{DateTime.Now:yyyy-MM-dd}\n" + string.Join("\n", stocks.Select((s, i) => $"{i + 1}. {s.Symbol} {s.Name}:{s.Reason}")) } }; var response = await _httpClient.PostAsJsonAsync(_feishuWebhookUrl, message); response.EnsureSuccessStatusCode(); } }

5. 部署、调试与常见问题排查

将代码部署到服务器并让整个流程自动运转起来,可能会遇到各种问题。以下是实战中总结的关键步骤和避坑指南。

5.1 完整部署清单

  1. 服务器准备:一台有公网IP的Linux服务器(如Ubuntu 22.04),安装好Docker和Docker Compose。
  2. 代码上传:将你的OpenClawStock项目文件上传到服务器,例如/opt/openclaw-stock
  3. 环境变量配置:在服务器上创建.env文件或直接修改docker-compose.yml,填入正确的Ollama BaseUrl(在服务器上,容器间通信通常用服务名http://ollama:11434)。
  4. 构建与测试:
    cd /opt/openclaw-stock docker-compose build docker-compose up -d ollama # 先启动Ollama # 等待Ollama启动并拉取模型 docker-compose run --rm openclaw-stock dotnet run --task select
    观察日志,看是否成功运行并收到飞书消息。
  5. 配置Cron:编辑crontab -e,添加定时任务,指向你的run_selection.sh脚本。记得给脚本添加执行权限(chmod +x run_selection.sh)。
  6. 日志监控:定期检查stock_selection.log文件,确保任务正常运行。

5.2 高频问题与解决方案速查表

问题现象可能原因排查步骤与解决方案
运行报错:openclaw llamap svr operator(): got exception: 4001. Ollama服务未启动。
2.BaseUrl配置错误,容器无法访问Ollama。
3. 指定的DefaultModel不存在。
1.docker ps检查Ollama容器是否运行。
2. 进入OpenClaw容器内部(docker exec -it openclaw-stock bash),尝试curl http://ollama:11434/api/tags看能否连通。
3. 在Ollama容器内执行ollama list确认模型名。
程序运行后无任何输出或立即退出1. 程序本身有未处理的异常导致崩溃。
2. Cron环境变量与交互式Shell不同。
1. 在run_selection.sh脚本中增加set -x开启调试,或直接手动执行脚本看详细错误。
2. 在Cron命令中指定完整的路径,或在脚本开头设置必要的环境变量(如PATH)。
飞书/微信消息未收到1. Webhook URL错误或已失效。
2. 网络问题,服务器无法访问外网。
3. 消息内容格式不符合机器人要求。
1. 重新在飞书群添加机器人,获取新URL。
2. 在服务器上curl -X POST <webhook_url>测试连通性。
3. 查阅飞书机器人文档,确保JSON格式正确,特别是msg_typecontent结构。
AKShare获取数据失败或很慢1. 数据源网站访问不稳定或已更新。
2. 请求频率过高被限制。
1. 检查AKShare库版本,更新到最新。查看其GitHub Issues看是否有类似问题。
2. 在代码中增加请求间隔(Task.Delay)。考虑使用本地缓存,避免重复获取同一数据。
模型返回的内容不是JSON格式1. Prompt指令不够清晰。
2. 模型能力不足或“不听话”。
1. 强化Prompt,使用“严格按JSON输出”、“不要有任何额外文本”等指令。在Prompt中给出更精确的JSON示例。
2. 尝试换用更强大的模型(如llama3.1:70b)。或者在代码中增加后处理,使用正则表达式从模型输出中提取JSON部分。
选出的股票总是那几支,或结果不合理1. 量化筛选规则过于宽松或严格。
2. 模型Prompt引导性不强。
3. 数据预处理有问题(如复权处理)。
1. 回测你的筛选规则。调整均线周期、RSI阈值等参数。可以引入更多维度,如换手率、资金流向等。
2. 优化Prompt,明确告诉模型你青睐的股票类型(如“中小盘成长股”、“估值修复型”)。
3. 确保使用的股价数据是前复权数据,以保证技术指标计算的连续性。

5.3 性能优化与扩展建议

当系统稳定运行后,你可以考虑以下优化和扩展方向:

  1. 引入缓存:股票的基本信息、板块分类等变化不频繁的数据,可以缓存到本地数据库(如SQLite)或文件中,避免每次任务都从API拉取,大幅提升速度。
  2. 并行处理:获取多支股票的历史数据和计算指标是CPU密集型任务,可以使用C#的Parallel.ForEachTask.WhenAll进行并行处理,充分利用多核性能。
  3. 策略回测框架:将筛选规则抽象成可配置的策略,并实现一个简单的回测引擎。这样你就可以用历史数据验证不同规则组合的有效性,而不仅仅是凭感觉调整参数。
  4. 多因子评分系统:替代简单的规则过滤,为每支股票在技术、基本面、资金、舆情等多个维度打分,加权求和得到总分,再选择排名靠前的股票。这能让筛选逻辑更精细。
  5. 接入更多数据源:除了行情,可以接入财经新闻、社交媒体情绪分析(通过大模型总结)、龙虎榜数据等,让AI的决策依据更全面。
  6. 实现简单的监控告警:除了每日推送,还可以监控任务是否成功执行。如果Cron任务失败(通过检查日志或进程状态),可以发送一条告警消息到你的手机。

这个“10分钟打造”的系统,是一个强大的起点而非终点。它为你打开了一扇门,让你能够将想法快速转化为一个自动运行的智能体。最重要的是,你通过亲手搭建,彻底理解了从数据获取、策略设计、AI调用到任务调度的完整链条。接下来,你可以根据自己的投资理念,不断迭代和优化其中的每一个模块,让它真正成为你投资路上的得力助手。记住,所有工具和代码都是为你服务的,核心永远是你对市场的独立思考和判断。

← 返回列表