iT邦幫忙

2022 iThome 鐵人賽

DAY 30
2

終於走到第 30 天,在本系列文的內容中,我們基於 Node.js 的 Nest Framework 實作 Scraper 應用程式,介紹收集各項股市數據的方法,應用於投資分析;開發 Monitor 應用程式,進行即時行情監控,實現到價提醒以及觸價委託;打造 Trader 應用程式,實現程式交易系統,可下單委託、帳務查詢,以及定期定額投資。

當完成軟體系統的開發後,最後就是應用程式的部署及上線。我們會將開發完成的程式 dockerize,將應用程式與執行環境打包成為映像檔,只要部署的環境支援 Docker,就可以執行我們的應用程式。

什麼是 Docker?

Docker 是一個容器化的軟體平台,可快速建立、測試和部署應用程式。

在容器化技術尚未出現以前,在準備部署環境的過程繁瑣複雜,經常需要花時間在配置系統環境。因為作業系統環境不同或是設定問題,經常出現應用程式在開發環境可以執行,但部署到伺服器上就運行不了的狀況。為了解決這個問題,使用 Docker 可以將應用程式與執行環境打包成為映像檔(image),並以容器(container)方式執行,解決作業系統環境不同可能遇到的問題。只要部署平台支援 Docker,應用程式就可以這些平台上運作,也利於不同環境之間的轉換與部署。

安裝 Docker 環境

我們要將應用程式打包成為映像檔,請確認好已經安裝 Docker 環境。

Docker Engine

Docker 的安裝方式,可參考官方文件 Install Docker Engine 的說明。在開發環境下,可以選用 Docker Desktop 版本,支援 Linux、Mac (macOS)、Windows 作業系統環境。

Docker Compose

Docker Compose 是用於定義多容器 Docker 應用程式的工具,透過 YAML 檔設定要運行的服務,並且使用 docker-compose 指令啟動配置檔中定義的服務。

如果安裝的 Docker Engine 是 Desktop 版本,它已經內置了 Docker Compose;如果 Docker Engine 安裝的是 Server 版本,則需要另安裝 Docker Compose。Docker Compose 的安裝方式,可參考官方文件 Install Docker Compose 的說明。

應用程式 Docker 化

將應用程式 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

Dockerfile 是 Docker 用來建構 Docker 映像檔的檔案,我們需要將每個應用程式分別建立各自的 Dockerfile 檔案。

建構 Scraper 應用程式映像檔

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 映像檔。

建構 Monitor 應用程式映像檔

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 映像檔。

建構 Trader 應用程式映像檔

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 配置我們的容器服務。在專案目錄下新增 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:

以上的配置定義了 scrapermonitortrader 容器服務,並使用 .env 檔案設定環境變數。此外,我們也使用 Docker Hub 提供的 mongoredis 映像檔快速建立 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 應用程式:

  • Scraper:設定排程任務定時從交易所取得資料,建構個人化股市資料庫,並產出每日盤後報告,運用於投資分析。
  • Monitor:使用富果行情 API 進行即時行情監控,並實作到價提醒與觸價委託功能,打造自動化下單平台。
  • Trader:整合富果交易 SDK 實作程式交易系統,可進行下單委託、帳務查詢,以及定期定額投資功能。

經過這 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 量化投資全攻略:從資料收集到自動化交易系統建構實戰
本系列文已正式出版為《Node.js 量化投資全攻略:從資料收集到自動化交易系統建構實戰》。本書新增了全新內容和實用範例,為你提供更深入的學習體驗!歡迎參考選購,開始你的量化投資之旅!
天瓏網路書店連結:https://www.tenlong.com.tw/products/9786263336070


上一篇
Day 29 - 長線獲利之道:定期定額投資系統
下一篇
Day 31 - 設定交易訊號:與 TradingView Webhooks 共舞
系列文
從 Node.js 開發者到量化交易者:打造屬於自己的投資系統31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
json_liang
iT邦研究生 5 級 ‧ 2022-09-30 17:04:49

恭喜完賽,感謝分享 nestjs 實作投資系統!真的很精彩!

謝謝!這 30 天真的不容易 XD

我要留言

立即登入留言