我如何使用 Google Cloud Platform 开始投资股票

2025-05-25

我如何使用 Google Cloud Platform 开始投资股票

我在参加朋友推荐的一次简短讲座后,对投资产生了兴趣。我决定做一些研究,并开始阅读乔尔·格林布拉特(Joel Greenblatt)的《依然战胜市场的小书》(The Little Book That Still Beats The Market)。从书中,我发现了一些公式,在决定是否投资新加坡公司的股票时,它们或许能帮到我。这篇文章并非为了推广这本书或其投资策略,而是为了展示以下内容以及我是如何做到的:

  1. 通过 Python 与 Firestore 交互
  2. 在 Compute Engine 上以特定时间间隔运行 Python 脚本
  3. 使用 Cloud Functions 从 Firestore 检索数据

起初,我编写了一个 Python 脚本,用于将新加坡交易所网站上上市公司的财务详情和自行计算的比率填充到 Google Sheet中。我发现这很麻烦,因为我必须每天运行这个 Python 脚本来获取股票的最新价格。后来,我决定将这个日常流程迁移到 Google Cloud Platform,这样我就不用再亲自处理这些日常流程了,只需交给云端来帮我处理 :D

下面将解释我如何做我所做的事情,希望能帮助那些可能想要以与我类似的方式使用 Google Cloud Platform 的人。

先决条件

在继续之前,我想提醒大家,为了保持文章简洁,请先完成以下事项。我已附上一些入门链接。

  1. 创建 Google Cloud Platform 项目
  2. 检索服务帐户密钥
  3. 创建 Cloud Engine 虚拟机实例
  4. 设置 Firebase Cloud Functions

概述

概述

从上图可以看出,我唯一需要做的就是通过 Cloud Functions HTTP API 发出 GET 请求,该请求将返回 Firestore 中存储的所有已计算的公式和值。本质上,步骤 1、2 和 3 涉及我创建的 Python 脚本。步骤 1 和 2 只需使用Requests 库即可完成。

通过 Python 与 Firestore 交互

Firestore 使用集合、文档和字段的概念来存储所需的数据。例如,以图书馆为例,如果您有一架书,那么在 Firestore 看来,这就是一个集合。书籍本身就是文档,书中的每一页都是一个独立的字段。每个文档也可以有自己的集合,但我不会深入探讨这一点。

shelf [collection]
|--book1 [document]
  |-- page1 [field]
  |-- page2 [field]
|--book2 [document]
  |-- page1 [field]
Enter fullscreen mode Exit fullscreen mode

要通过 Python 脚本与 Cloud Firestore 进行交互并更新数据,首先必须通过 安装 Google Cloud Firestore 库pip install google-cloud-firestore。以下是使用您之前检索到的服务帐户密钥初始化 Firestore 的代码片段。

from google.cloud import firestore
db = firestore.Client.from_service_account_json('/path/to/service/key')
Enter fullscreen mode Exit fullscreen mode

嗯,就是这样!要将数据写入 Firestore,只需执行以下操作:

doc_ref = db.collection(u'name_of_collection').document(u'name_of_document')
doc_ref.set(data_to_update)
Enter fullscreen mode Exit fullscreen mode

data_to_update是一个 Python 字典,它包含您希望 Firestore 文档保存的键和相应的值。它.set()允许您更新文档或向文档中插入新字段。就我个人而言,我在这里保存了公司名称、股票价格、财务比率和其他字段。

这里要注意的一点是,即使文档或集合尚不存在,.set()函数也会自动为您创建集合和文档,并使用前面提到的字段填充文档。

在 Compute Engine 上运行 Python 脚本

有几种方法可以将 Python 脚本推送到虚拟机实例。我的做法是在我的 Google Cloud 项目中创建一个代码库,并将其推送到那里。我创建代码库的原因是我仍然需要某种形式的版本控制,因为我了解自己,喜欢在代码中进行修改并探索不同的实现方式,最终让自己感到困惑。虽然这是一个小项目,但我觉得这对我个人来说是一个很好的做法。然后,我通过 SSH 远程访问了虚拟机实例,并将代码库克隆到实例中。

现在来谈谈 Python 脚本的调度。起初,我认为每 30 分钟调用一次 Python 脚本是个好主意。然而,经过一番考虑,我觉得将脚本调度到下午 6 点(GMT +0800)运行是理想的情况,因为新加坡交易所上午 9 点开市,下午 5 点关市,而我实际上只有下班后才有时间查看股票价格。

要安排 Python 脚本以特定时间间隔或特定时间运行,你可以像我一样使用 Cron 作业。在虚拟机实例的 SSH 会话中,使用以下crontab -e命令编辑用户的 Crontab。在文件末尾,使用以下格式的计划

# m h  dom mon dow   command
0 10 * * 1-5 cd /path/to/python/folder && python main.py
Enter fullscreen mode Exit fullscreen mode

上述代码片段会在每个工作日的 UTC 时间上午 10 点(即新加坡时间下午 6 点)运行 Python 脚本,具体时间由时间1-5段指定。如果您希望脚本在每个时间间隔后运行,可以执行以下操作:

# Runs the command every hour at the 0th minute
0 */1 * * * <some command>

# Runs the command at the 0th minute every day
0 * */1 * * <some command>
Enter fullscreen mode Exit fullscreen mode

注意:我在 VM 实例中最初几次使用 Crontab 时犯了一个错误,如下所示:

# Runs the command every minute after every hour
* */1 * * * <some command>
Enter fullscreen mode Exit fullscreen mode

我本来打算每小时运行一次,但我错过了0定时任务的分钟标记。所以它每小时过后的每一分钟都会运行脚本。我的脚本每次调用大约需要 3 分钟。我并不介意相对较长的运行时间。但是,由于脚本每分钟运行一次,每次运行都需要 3 分钟……好吧,你可以算一下。我当时傻乎乎的,还试图弄清楚为什么我的虚拟机实例的 CPU 使用率一直在 150-200% 左右,我甚至无法通过 SSH 访问它。这真是一个有趣的教训 :P

使用 Cloud Functions 从 Firestore 检索数据

在这一步,我将 Google Cloud 项目关联到了 Firebase。这样做是为了将来的版本能够在 Firebase Hosting 上托管一个网站,该网站可以利用 Cloud Firestore 的数据,让任何人都可以一目了然地查看财务详情。另一个原因是,我对 Firebase 以及 Cloud Functions 的要求更加熟悉。

我通过 将 Express.js 安装到我的 Cloud Functions 文件夹中npm install --save express。Express.js 允许我轻松创建 Web API,因为我需要多个端点来从我拥有的 Firestore 中检索各种公司信息。

var  db  =  admin.firestore();

const  express  =  require("express");
const  app  =  express();

app.get('/:nameOfDocument',( req, res)=>{
    const  nameOfDocument  =  req.params.nameOfDocument;
    var  firestoreRef  =  db.collection("name_of_collection").doc(nameOfDocument);
    res.setHeader('Content-Type', 'application/json');
    firestoreRef.get().then((snapshot) => {
    if (snapshot.exists) {
        var  returnObj  =  snapshot.data();
        return  res.status(200).json(returnObj);
    }
    else {
        return  res.status(422).json({error:"Invalid document name"});
    }
    }).catch(errorObject  => {
        return  res.status(500).json({error:"Internal Server Error"});
    });
})
exports.api  =  functions.https.onRequest(app);
Enter fullscreen mode Exit fullscreen mode

以下是对上述代码片段的逐步解释。首先,通过 初始化对 Firestore 的访问var db = admin.firestore();

app.get('/:nameOfDocument',( req, res)=>{
...
}
Enter fullscreen mode Exit fullscreen mode

上述代码告诉 Express,我们希望创建一个包含'/:nameOfDocument'端点的 GET 请求,其中:nameOfDocument是 URL 中的一个参数。reqres分别是已接收和即将发送的请求对象和响应对象。目前,只res使用了 ,稍后会详细介绍。

const nameOfDocument = req.params.nameOfDocument;
Enter fullscreen mode Exit fullscreen mode

此行从 URL 中获取参数(:nameOfDocument在本例中为),并将其存储为名为的变量nameOfDocument,该变量将在下一行中使用。

var firestoreRef = db.collection("name_of_collection").doc(nameOfDocument);
Enter fullscreen mode Exit fullscreen mode

这行代码实际上创建了对 document 的引用nameOfDocument。集合名称目前不是变量。您也可以使用 include 集合名称作为参数,如下所示:

app.get('/:nameOfCollection/:nameOfDocument',( req, res)=>{
    const nameOfDocument = req.params.nameOfDocument;
    const nameOfCollection= req.params.nameOfCollection;
    var firestoreRef = db.collection(nameOfCollection).doc(nameOfDocument);
    ...
}
Enter fullscreen mode Exit fullscreen mode

这样,您可以在 URL 中指定它,而无需更改代码。

firestoreRef.get().then((snapshot)  =>  {
    if  (snapshot.exists)  {  
    var returnObj = snapshot.data();  
    return res.status(200).json(returnObj);  
    }
    ...
}
Enter fullscreen mode Exit fullscreen mode

上述代码段获取前面提到的引用并检查其是否存在。这至关重要,因为用户可能会意外输入错误的文档或集合名称,而我们希望返回正确的响应。snapshot.data()检索所有字段键值对,并将其放入名为 的对象中。returnObj然后,我们将其作为 JSON 对象返回,状态码为 200。

exports.api  =  functions.https.onRequest(app);
Enter fullscreen mode Exit fullscreen mode

此行告诉 Cloud Functions,当发出请求时,<cloudfunctions.net url>/api应将其传递给 Express 对象,app并根据对象本身指定的端点进行相应的调用和处理app

就是这样!现在,您可以从 Firebase Cloud Functions 页面上提供的链接调用您的 Cloud Functions,它将从您的 Firestore 中检索您想要处理的相关数据。

附言:这是我的第一篇教程/个人经验帖。请告诉我哪些地方可以改进,以及如何成为一名更好的程序员。欢迎所有建设性的反馈。感谢您阅读我的文章!:D

文章来源:https://dev.to/dansyuqri/how-i-used-google-cloud-platform-to-start-investing-in-stocks-34mb
PREV
我使用微前端创建 Netflix 克隆版的经历 目录 什么是微前端?为什么要使用微前端?微前端框架 Piral 项目 结语 所有代码的快速链接
NEXT
我如何在仅仅 3 个月的自由职业后每月赚 1 万美元