終於走到第 30 天,在本系列文的內容中,我們基於 Node.js 的 Nest Framework 實作 Scraper 應用程式,介紹收集各項股市數據的方法,應用於投資分析;開發 Monitor 應用程式,進行即時行情監控,實現到價提醒以及觸價委託;打造 Trader 應用程式,實現程式交易系統,可下單委託、帳務查詢,以及定期定額投資。
當完成軟體系統的開發後,最後就是應用程式的部署及上線。我們會將開發完成的程式 dockerize,將應用程式與執行環境打包成為映像檔,只要部署的環境支援 Docker,就可以執行我們的應用程式。
Docker 是一個容器化的軟體平台,可快速建立、測試和部署應用程式。
在容器化技術尚未出現以前,在準備部署環境的過程繁瑣複雜,經常需要花時間在配置系統環境。因為作業系統環境不同或是設定問題,經常出現應用程式在開發環境可以執行,但部署到伺服器上就運行不了的狀況。為了解決這個問題,使用 Docker 可以將應用程式與執行環境打包成為映像檔(image),並以容器(container)方式執行,解決作業系統環境不同可能遇到的問題。只要部署平台支援 Docker,應用程式就可以這些平台上運作,也利於不同環境之間的轉換與部署。
我們要將應用程式打包成為映像檔,請確認好已經安裝 Docker 環境。
Docker 的安裝方式,可參考官方文件 Install Docker Engine 的說明。在開發環境下,可以選用 Docker Desktop 版本,支援 Linux、Mac (macOS)、Windows 作業系統環境。
Docker Compose 是用於定義多容器 Docker 應用程式的工具,透過 YAML 檔設定要運行的服務,並且使用 docker-compose
指令啟動配置檔中定義的服務。
如果安裝的 Docker Engine 是 Desktop 版本,它已經內置了 Docker Compose;如果 Docker Engine 安裝的是 Server 版本,則需要另安裝 Docker Compose。Docker Compose 的安裝方式,可參考官方文件 Install Docker Compose 的說明。
將應用程式 Docker 化的過程稱為 dockerize,目的是將應用程式以 Docker 映像檔形式發佈,可快速將應用程式部署至各種環境。
以下是我們完成 Nest 應用程式的主要檔案與專案目錄結構:
├── apps/
│ ├── monitor/
│ │ ├── src/
│ │ ├── Dockerfile
│ │ └── tsconfig.app.json
│ ├── scraper/
│ │ ├── src/
│ │ ├── Dockerfile
│ │ └── tsconfig.app.json
│ └── trader/
│ ├── src/
│ ├── Dockerfile
│ └── tsconfig.app.json
├── libs/
├ └── common/
│ ├── src/
│ └── tsconfig.lib.json
|-- certs/
├── docker-compose.yml
├── nest-cli.json
├── package.json
├── tsconfig.json
└── .env
在專案目錄中,certs
資料夾是存放憑證的位置,我們會使用 Docker Volumes 功能,將憑證掛載到 Trader 應用程式的容器內。
為了建構映像檔,我們需要在每個應用程式目錄下建立 Dockerfile
檔案,並且在專案根目錄建立 docker-compose.yml
,定義運行的容器與服務。
Dockerfile 是 Docker 用來建構 Docker 映像檔的檔案,我們需要將每個應用程式分別建立各自的 Dockerfile
檔案。
在 apps/scraper
目錄下新增 Dockerfile
檔案,將建構映像檔的過程編寫如下:
FROM node:16-alpine as builder
ENV NODE_ENV build
USER node
WORKDIR /home/node
COPY --chown=node:node . /home/node
RUN npm install \
&& npm run build scraper
# ---
FROM node:16-alpine
ENV NODE_ENV production
ENV TZ Asia/Taipei
USER node
WORKDIR /home/node
COPY --from=builder /home/node/package.json /home/node/
COPY --from=builder /home/node/package-lock.json /home/node/
COPY --from=builder /home/node/dist/ /home/node/dist/
RUN npm install --production
CMD ["node", "dist/apps/scraper/main.js"]
建立 Scraper 應用程式的 Dockerfile
後,在專案根目錄,可以使用以下 docker
指令建構映像檔:
$ docker build -t speculator/scraper:latest -f ./apps/scraper/Dockerfile .
執行完成後,會建立名為 speculator/scraper
的 Docker 映像檔。
在 apps/monitor
目錄下新增 Dockerfile
,檔案,將建構映像檔的過程編寫如下:
FROM node:16-alpine as builder
ENV NODE_ENV build
USER node
WORKDIR /home/node
COPY --chown=node:node . /home/node
RUN npm install \
&& npm run build monitor
# ---
FROM node:16-alpine
ENV NODE_ENV production
ENV TZ Asia/Taipei
USER node
WORKDIR /home/node
COPY --from=builder /home/node/package.json /home/node/
COPY --from=builder /home/node/package-lock.json /home/node/
COPY --from=builder /home/node/dist/ /home/node/dist/
RUN npm install --production
CMD ["node", "dist/apps/monitor/main.js"]
建立 Monitor 應用程式的 Dockerfile
後,在專案根目錄,可以使用以下 docker
指令建構映像檔:
$ docker build -t speculator/monitor:latest -f ./apps/monitor/Dockerfile .
執行完成後,會建立名為 speculator/monitor
的 Docker 映像檔。
在 apps/trader
目錄下新增 Dockerfile
,檔案,將建構映像檔的過程編寫如下:
FROM node:16 as builder
ENV NODE_ENV build
USER node
WORKDIR /home/node
COPY --chown=node:node . /home/node
RUN npm install \
&& npm run build trader
# ---
FROM node:16
ENV NODE_ENV production
ENV TZ Asia/Taipei
RUN apt-get update -y \
&& apt-get install libsecret-1-dev -y
USER node
WORKDIR /home/node
COPY --from=builder /home/node/package.json /home/node/
COPY --from=builder /home/node/package-lock.json /home/node/
COPY --from=builder /home/node/dist/ /home/node/dist/
RUN npm install --production
CMD ["node", "dist/apps/trader/main.js"]
建立 Trader 應用程式的 Dockerfile
後,在專案根目錄,可以使用以下 docker
指令建構映像檔:
$ docker build -t speculator/trader:latest -f ./apps/trader/Dockerfile .
執行完成後,會建立名為 speculator/trader
的 Docker 映像檔。
為了管理我們的應用程式,使用 Docker Compose 配置我們的容器服務。在專案目錄下新增 docker-compose.yml
檔案,以下是一個配置容器服務的範例:
version: "3"
services:
scraper:
build:
context: .
dockerfile: ./apps/scraper/Dockerfile
image: speculator/scraper:latest
env_file:
- .env
restart: always
monitor:
build:
context: .
dockerfile: ./apps/monitor/Dockerfile
image: speculator/monitor:latest
ports:
- "3000:3000"
env_file:
- .env
restart: always
trader:
build:
context: .
dockerfile: ./apps/trader/Dockerfile
image: speculator/trader:latest
ports:
- "3001:3001"
env_file:
- .env
volumes:
- ./certs:/home/node/certs
restart: always
mongo:
image: mongo:latest
ports:
- "27017:27017"
redis:
image: redis:latest
ports:
- "6379:6379"
volumes:
certs:
以上的配置定義了 scraper
、monitor
與 trader
容器服務,並使用 .env
檔案設定環境變數。此外,我們也使用 Docker Hub 提供的 mongo 與 redis 映像檔快速建立 MongoDB 與 Redis 環境。以上僅是設定 Docker Compose 的範例,您可以根據自己的需求自行修改配置。
關於 Docker Compose 的詳細配置方式可參考官方文件 Compose specification 的說明。
完成 docker-compose.yml
的配置設定後,就可以透過 docker-compose
指令啟動服務。
使用 docker-compose
指令啟動所有容器服務:
$ docker-compose up -d
執行後,Docker Compose 會啟動所有容器,指定 -p
選項代表容器在背景執行。若服務的映像檔不存在,則 Docker Compose 會先建構或下載映像檔。
使用 docker-compose
指令結束所有容器服務:
$ docker-compose down
執行後,Docker Compose 會停止服務,並刪除所有容器。
以上我們說明了建構應用程式映像檔與啟動容器服務的流程,您可以根據自己的需求自行設計 CI/CD pipeline,將應用程式以 Docker 映像檔形式發佈,然後就可以在各種部署環境中,啟動容器執行應用程式。
回顧本系列文的所有內容,我們說明了股市資料的來源與取得方式,並且介紹由上而下的投資方法,從大盤、產業到個股,說明如何從交易所收集股市資料,應用於投資分析。我們以 Node.js 為例,實際完成了 3 個 Nest 應用程式:
經過這 30 天,我們打造了一套用於投資分析與程式交易的後端系統。至於接下來的工作,您可以在基於本框架的基礎上,根據自己的需求自行擴充功能、串接其他券商 API,或進一步實作應用程式的前端使用者介面,以獲得更佳的自動化投資體驗。
最後,我們也提供本系列文所實作的 範例程式。
從 2022 年 9 月 1 日鐵人賽開賽至今,本系列文也告一段落。最後筆者想聊聊關於最近的金融市場,因為就在這鐵人賽 30 天期間,臺股與全球股市也面臨相當大的動盪,9 月份這波下殺來得又急又猛。正如我們在 Day 03 - 由上而下投資法:先看大盤趨勢再選股 所提及,當大盤面臨系統性風險,除非站在空方,否則選股的難度就相當高。
這波股市下跌主要肇因於美國 9 月中公布的 8 月份通膨數據高於預期,所以 Fed 勢必繼續採取更鷹派的升息手段來抑制通膨。我們在 Day 12 - 景氣循環指標:美國公債殖利率 談到,Fed 的一舉一動是全球市場關注的焦點,9 月份 Fed 利率決策會議宣布調升 3 碼,當美國十年期公債殖利率持續上升,並且長天期與短天期公債殖利率持續倒掛,股市也就反映對於未來經濟衰退的預期。在美股領跌的狀況下,在 Day 11 - 全球經濟火車頭:美股四大指數 我們也提出實際的數據說明,因為臺股與美股的高度連動性,費城半導體指數在 9 月 23 日率先破底,而臺灣加權指數也在 9 月 26 日跌破 7 月份的低點。本系列文完成鐵人賽的這天剛好是 2022 年 9 月的最後一天,光是這個月臺股加權指數就下跌了 -11.07%。今年是空頭市場,大部分的投資人都是賠錢的,不過就像科斯托蘭尼說的:「要真正理解證券交易所,或是稍微掌握證券交易之前,肯定要付很多學費。我再次重複:投機中賺的錢是痛苦錢,先有痛苦,然後才有金錢。」
關於投機,一般人對這個詞語的負面觀感較多,但科斯托蘭尼則認為 投機家是有遠見的戰略家:「投機家是有識之士,是三思而後行的證券交易人士,能夠準確預測經濟、政治和社會的發展趨勢,並且從中獲利。」這也是筆者將實作範例的專案取名為「Speculator」的緣由。特別是一個主動型交易者,判斷市場是處在哪個階段就相當重要。要在市場上成功,除了 交易方法 外、交易心理 以及 資金管理 亦是不可獲缺的關鍵。條條大路通羅馬,每個人都有適合自己的交易方法,但由於市場的不確定性,這個世界上沒有絕對完美的方法或交易系統。身為投資人可以做的就是備妥一套明確的計畫,然後嚴格遵守計畫,成為紀律的交易者。
不過由於人是感情的動物,經常會受行情波動影響誤判情勢,做出不理性的決策。科斯托蘭尼說:「最難的是在證券市場裡承認賠錢,但就像外科手術,在病毒擴散之前,必須把手臂截肢,愈早愈好。要這樣做很難,一百個投機家中也許只有五個人能做到。證券交易所玩家犯下最不可原諒的錯誤,是設定獲利的上限,卻讓虧損不斷膨脹。一位正確操作、且有經驗的投機家會讓利潤增加,然後以相對較小的損失出場。」這說明了交易心理、資金管理與風險控制的重要性,而程式交易的優勢之一,就是幫助我們克服心理障礙,嚴格遵守操作的紀律。
投資理財是人們一生中的課題,尤其在金融市場比的是氣長,投資在人生是一場無限賽局,存活下來的才是贏家。祝福有緣看完 30 天系列文的各位,都能在未來投資路上持續專注成長,自信投資!
本系列文已正式出版為《Node.js 量化投資全攻略:從資料收集到自動化交易系統建構實戰》。本書新增了全新內容和實用範例,為你提供更深入的學習體驗!歡迎參考選購,開始你的量化投資之旅!
天瓏網路書店連結:https://www.tenlong.com.tw/products/9786263336070
恭喜完賽,感謝分享 nestjs 實作投資系統!真的很精彩!
謝謝!這 30 天真的不容易 XD