上周五收盘后,我照例打开Wind查看持仓股票的技术面,突然想起这个月的订阅费又要扣了。作为量化研究员,我们对实时数据的依赖就像程序员离不开GitHub,但每年动辄几万的数据服务费,真的是必需品吗?
直到我在GitHub上发现了OpenStock——一个完全开源的股票追踪平台。它不仅提供实时行情、技术图表、公司基本面,还支持个性化自选股和AI新闻摘要。最关键的是:完全免费,代码开源,可以部署在自己的服务器上。
这让我想起Open Dev Society的那句宣言:"Technology should belong to everyone"。在金融数据被商业巨头垄断的今天,这样的项目显得格外珍贵。
今天就带大家深入体验这个项目,看看它能否成为我们日常研究的得力助手。
关注「alphaFind」公众号,获取更多量化交易开源项目解析
一、实战体验:从注册到盯盘的完整流程
1.1 首次登录:个性化投资画像
打开OpenStock的第一印象是简洁——没有花里胡哨的广告,没有弹窗推销,只有一个清爽的登录界面。
Market Overview
1.2 自选股管理:你的专属观察池
进入主界面后,第一件事就是构建自选股列表。点击顶部的搜索框(或按下Cmd/Ctrl + K
快捷键),输入股票代码或公司名称:
- 实时搜索:输入"AAPL"立即显示苹果公司
- 模糊匹配:输入"Tesla"也能找到TSLA
- 热门推荐:搜索框空白时显示当日热门股票
Search
找到目标股票后,点击右侧的"⭐ Add to Watchlist"按钮即可加入自选。我测试了添加10只股票(AAPL、MSFT、TSLA、NVDA、GOOGL等),整个过程不到1分钟。
Add to Watchlist
自选股列表会实时显示:
- 当前价格
- 涨跌幅(红涨绿跌,符合国际惯例)
- 日内最高/最低价
- 成交量
实测体验:由于使用的是Finnhub免费API,数据有15分钟延迟。对于日内高频交易者来说不够用,但对于做日频策略或基本面研究的人来说完全够了。如果需要实时数据,可以自己申请Finnhub付费版API,替换环境变量即可。
1.3 股票详情页:专业级的数据看板
点击任意一只股票,进入详情页。这里的信息密度堪比专业终端:
① TradingView图表集成
页面顶部是一个完整的TradingView嵌入式图表,支持:
- 多种图表类型(K线、美国线、面积图)
- 20+种技术指标(RSI、MACD、布林带、KDJ等)
- 时间周期切换(1分钟到月线)
- 绘图工具(趋势线、斐波那契回撤等)
Market Kline
我试着在AAPL的日线图上添加了MACD和布林带,响应速度很快,没有卡顿。这个功能对于技术分析爱好者来说是刚需——不用再开多个窗口切换了。
② 公司基本面信息
图表下方是公司的核心数据:
- 估值指标:PE、PB、PS、市值
- 财务数据:营收、净利润、ROE
- 分红信息:股息率、派息比率
- 公司简介:业务范围、CEO、员工数
Financials
这些数据来自Finnhub的Company Profile接口,更新频率为季度。我对比了几只股票的PE值,与Wind的数据误差在5%以内,可信度不错。
③ 市场新闻聚合
页面底部会显示与该股票相关的最新新闻,包括:
- 新闻标题和来源
- 发布时间
- 情绪标签(正面/中性/负面)
Top Stories
点击新闻标题会跳转到原文链接。这个功能对于事件驱动策略的研究者很有用——可以快速追踪公司的重大事件(财报、并购、高管变动等)。
1.4 市场总览:全局视角的热力图
除了个股追踪,OpenStock还提供了市场总览页面,包含:
- 市场热力图:直观显示各板块涨跌情况,颜色越深代表涨跌幅越大
- 行业轮动:科技、金融、医疗等板块的资金流向
- Top Movers:当日涨幅榜和跌幅榜
Stock Heatmap
这个功能特别适合做行业轮动策略的朋友。我经常用热力图快速判断市场情绪——如果科技股全线飘红,说明风险偏好上升;如果公用事业板块领涨,可能是避险情绪升温。
二、动手部署:30分钟搭建你的专属平台
OpenStock最大的优势是完全可控——你可以部署在自己的服务器上,替换数据源,甚至二次开发添加自己的策略模块。下面我会详细讲解部署过程。
2.1 准备工作:注册必要的API密钥
在开始之前,需要准备以下账号(全部免费):
① MongoDB数据库(存储用户数据和自选股)
② Finnhub API(股票行情数据)
③ Google Gemini API(AI新闻生成,可选)
④ Gmail账号(发送邮件通知)
- 开启两步验证后生成应用专用密码
- 路径:Google账号 → 安全性 → 应用专用密码
2.2 本地部署:5步启动项目
Step 1:克隆代码
git clone https://github.com/Open-Dev-Society/OpenStock.git
cd OpenStock
Step 2:安装依赖
npm install
# 或者使用pnpm(推荐,速度更快)
pnpm install
Step 3:配置环境变量
在项目根目录创建.env
文件,填入以下内容:
# 数据库连接
MONGODB_URI=mongodb+srv://你的连接字符串
# 认证密钥(随机生成一个32位字符串)
BETTER_AUTH_SECRET=your_random_secret_key_here
BETTER_AUTH_URL=http://localhost:3000
# Finnhub API
FINNHUB_API_KEY=你的API密钥
FINNHUB_BASE_URL=https://finnhub.io/api/v1
# Gemini AI(可选)
GEMINI_API_KEY=你的Gemini密钥
# 邮件配置
NODEMAILER_EMAIL=你的Gmail地址
NODEMAILER_PASSWORD=应用专用密码
Step 4:测试数据库连接
npm run test:db
如果看到"✅ MongoDB连接成功",说明配置正确。
Step 5:启动开发服务器
npm run dev
打开浏览器访问 http://localhost:3000
,你应该能看到登录页面了。
2.3 生产部署:一键发布到Vercel
如果想让朋友也能访问,可以部署到云端:
① Fork项目到你的GitHub
② 访问 https://vercel.com
- 使用GitHub账号登录
- 点击"Import Project"
- 选择刚才Fork的仓库
③ 配置环境变量
- 在Vercel的项目设置中,找到"Environment Variables"
- 把
.env
文件中的所有变量复制过去
- 注意:
BETTER_AUTH_URL
要改成Vercel分配的域名(如https://your-app.vercel.app
)
④ 部署
- 点击"Deploy"按钮
- 等待3-5分钟,部署完成
现在你有了一个公网可访问的股票追踪平台,可以分享给团队使用。
2.4 进阶配置:替换为实时数据源
如果你有付费的数据源(如Polygon.io、Alpha Vantage),可以修改数据获取逻辑:
① 找到数据获取函数
// lib/actions/finnhub.actions.ts
export async function getStockQuote(symbol: string) {
// 原始代码使用Finnhub
const response = await fetch(
`${process.env.FINNHUB_BASE_URL}/quote?symbol=${symbol}&token=${process.env.FINNHUB_API_KEY}`
);
return response.json();
}
② 替换为你的数据源
export async function getStockQuote(symbol: string) {
// 改用Polygon.io(实时数据)
const response = await fetch(
`https://api.polygon.io/v2/aggs/ticker/${symbol}/prev?apiKey=${process.env.POLYGON_API_KEY}`
);
const data = await response.json();
// 转换为统一格式
return {
c: data.results[0].c, // 收盘价
h: data.results[0].h, // 最高价
l: data.results[0].l, // 最低价
o: data.results[0].o, // 开盘价
pc: data.results[0].c, // 前收价
};
}
这样就能获取实时数据了,而且不需要改动前端代码。
三、从数据展示到量化研究:我的改造实践
作为量化,我不满足于只看行情。在使用OpenStock一周后,我基于它的架构做了一些扩展。
3.1 添加历史数据下载脚本
原项目没有批量下载历史数据的功能,我写了一个脚本:
// scripts/download-history.ts
import fs from 'fs';
async function downloadOHLCV(symbol: string, days: number) {
const to = Math.floor(Date.now() / 1000);
const from = to - days * 24 * 60 * 60;
const response = await fetch(
`https://finnhub.io/api/v1/stock/candle?symbol=${symbol}&resolution=D&from=${from}&to=${to}&token=${process.env.FINNHUB_API_KEY}`
);
const data = await response.json();
// 转换为CSV格式
const csv = data.t.map((timestamp: number, i: number) =>
`${new Date(timestamp * 1000).toISOString()},${data.o[i]},${data.h[i]},${data.l[i]},${data.c[i]},${data.v[i]}`
).join('\n');
fs.writeFileSync(`./data/${symbol}.csv`, `Date,Open,High,Low,Close,Volume\n${csv}`);
console.log(`✅ ${symbol} 数据已保存`);
}
// 批量下载
const symbols = ['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'NVDA'];
for (const symbol of symbols) {
await downloadOHLCV(symbol, 365); // 下载一年数据
}
运行后会在data/
目录生成CSV文件,可以直接导入到回测框架中。
3.2 集成技术指标计算
我添加了一个技术指标库,用于计算常见的量化因子:
// lib/indicators/technical.ts
export class Indicators {
// RSI相对强弱指标
static RSI(prices: number[], period = 14): number {
const changes = prices.slice(1).map((p, i) => p - prices[i]);
const gains = changes.filter(c => c > 0);
const losses = changes.filter(c => c < 0).map(Math.abs);
const avgGain = gains.reduce((a, b) => a + b, 0) / period;
const avgLoss = losses.reduce((a, b) => a + b, 0) / period;
const rs = avgGain / (avgLoss || 0.0001);
return 100 - (100 / (1 + rs));
}
// 布林带
static BollingerBands(prices: number[], period = 20, stdDev = 2) {
const sma = prices.slice(-period).reduce((a, b) => a + b) / period;
const variance = prices.slice(-period)
.reduce((sum, p) => sum + Math.pow(p - sma, 2), 0) / period;
const std = Math.sqrt(variance);
return {
upper: sma + stdDev * std,
middle: sma,
lower: sma - stdDev * std
};
}
}
现在可以在股票详情页显示这些指标的实时计算结果了。
3.3 价格预警系统
利用Inngest的定时任务功能,我实现了价格预警:
// lib/inngest/functions/price-alert.ts
export const checkPriceAlerts = inngest.createFunction(
{ id: "price-alert" },
{ cron: "*/5 * * * *" }, // 每5分钟检查
async ({ step }) => {
// 从数据库获取所有激活的预警
const alerts = await step.run("fetch-alerts", () =>
Alert.find({ status: 'ACTIVE' })
);
for (const alert of alerts) {
const quote = await getStockQuote(alert.symbol);
// 检查触发条件
if (alert.type === 'PRICE_ABOVE' && quote.c > alert.targetPrice) {
await sendAlertEmail(alert.userId, `${alert.symbol}已突破${alert.targetPrice}`);
await Alert.updateOne({ _id: alert._id }, { status: 'TRIGGERED' });
}
}
}
);
设置好预警后,股价达到目标就会收到邮件通知,再也不用盯盘了。
四、冷静思考:这个项目适合你吗?
在使用OpenStock两周后,我总结了它的适用场景和局限性:
✅ 适合这些人:
- 个人投资者:需要免费的行情追踪工具
- 量化研究员:需要可定制的数据基础设施
- 学生/爱好者:学习全栈开发和金融数据处理
- 小型团队:预算有限,不想买商业终端
⚠️ 不适合这些场景:
- 日内高频交易:免费数据有延迟
- 期货/期权交易:目前只支持股票
- 生产级交易系统:缺少风控和监控模块
- 完全不懂技术的用户:需要一定的部署能力
我的建议:
如果你只是想看看行情,直接用官方Demo( openstock-ods.vercel.app ) 就够了。如果你想深度定制、添加自己的策略,那么部署一套自己的实例是值得的。
写在最后
OpenStock让我想起多年前刚入行时的困境——想做量化研究,却被数据成本挡在门外。那时候我只能用Yahoo Finance的免费接口,数据质量参差不齐,经常出现缺失值。
现在开源社区给了我们更好的选择。OpenStock不是完美的,它没有Bloomberg的全面,也没有Wind的本土化,但它有一个最重要的特质:开放。
你可以看到每一行代码的逻辑,可以替换任何一个模块,可以根据自己的需求改造。这种自由度,是任何商业软件都无法给予的。
更重要的是,它代表了一种理念:金融数据不应该被垄断,技术工具应该属于每一个人。
如果你认同这个理念,不妨给这个项目一个Star,或者贡献你的代码。也许有一天,我们能一起构建一个真正开放、免费、强大的金融数据平台。
那时候,我们就不用再为每年几万块的订阅费发愁了。
项目地址:
标签:#OpenStock #Github #开源项目 #量化交易 #金融科技 #股票分析 #Next.js
关注「alphaFind」,发现更多改变交易方式的开源工具。