iT邦幫忙

2022 iThome 鐵人賽

DAY 15
1

前面我們已經介紹評估大盤狀況的方法,並透過相對強度分析和市場資金流向來找出強勢和弱勢的產業類股,接下來我們要進入由上而下投資法的 第三階段,也就是 個股 的主題。每天股票市場開盤至收盤,都會產生四個價位,即 開盤價最高價最低價 以及 收盤價,而 K線 正是由這四個價格所組成。今天我們會介紹投資人應該具備的K線基礎,然後示範如何從證交所及櫃買中心網站取得上市櫃股票行情資訊。

什麼是K線?

K線 也有人稱為 K棒,因為形狀像蠟燭,也有人稱為 蠟燭線陰陽線紅黑線 等。一根單一K線,是由一天或某一週期的股價走勢所形成的四個價位繪製而成,分別是「開盤價」、「最高價」、「最低價」、「收盤價」。

根據開盤價與收盤價的高低,K線又可以分為「紅K線」與「黑K線」。當收盤價高於開盤價,代表當天走勢開低走高,會以 紅K線 表示,也稱為 陽線;而收盤價低於開盤價,代表當天走勢開高走低,則以 黑K線 表示,也稱作 陰線

如果當日最高價高於紅K線的收盤價,或是高於黑K線的開盤價,就會出現「上影線」;如果當日最低價低於紅K線的開盤價,或是低於黑K線的收盤價,就會出現「下影線」。

https://ithelp.ithome.com.tw/upload/images/20220915/201501501YgMCDGNGG.png

只有少部分的亞洲股市採用紅漲綠跌;歐美股市都是採用綠漲紅跌。因為漲跌顏色不同,K棒的顏色意義也是反過來的,所以在觀察外國股市走勢的時候,需要注意提供資訊的網站或看盤軟體的設定。

K線依據時間架構可以分為「日K線」、「週K線」、「月K線」:

  • 日K線:記錄「當日」股價的最高價、最低價、開盤價和收盤價。
  • 週K線:記錄「當週」股價的最高價、最低價、第一個交易日的開盤價和最後交易日的收盤價。
  • 月K線:記錄「當月」股價的最高價、最低價、第一個交易日的開盤價和最後交易日的收盤價。

不同週期的投資者會參考不同時間架構的K線,通常波段投資人較長參考的是 日K線週K線,如果是當沖或極短線交易者,可能還會參考 1 分K線5 分K線15 分K線30 分K線 等。

什麼是K線圖?

K線圖是由多個K線所組成,將某段期間內的相同時間單位的K線組合起來,就可以畫出K線圖,橫軸為時間,縱軸為價格。除了價格外,成交量也透露出重要訊息,反映出該商品交易的熱度以及群眾的參與程度。成交量通常以柱狀圖表示並畫在K線圖下方,柱狀圖的高度就代表成交量的大小。

https://ithelp.ithome.com.tw/upload/images/20220915/201501507gqNQb1ZhM.png

Source:TradingView

由於K線是技術分析的基礎,價格也是所有買賣交易人最在意的事。在進行選股之前,我們務必瞭解如何取得上市櫃股票行情,包含開盤價、最高價、最低價、收盤價以及成交量等資訊。

查詢集中市場股票行情

在證交所 每日收盤行情 頁面,可以按日查詢集中市場個股收盤行情。

證交所首頁 > 交易資訊 > 盤後資訊 > 每日收盤行情

在「每日收盤行情」頁面選取要查詢的「日期」,因為我們是要查詢個股不需包含權證,在「分類項目」選擇「全部(不含權證、牛熊證、可展延牛熊證)」,然後按下「查詢」。

https://ithelp.ithome.com.tw/upload/images/20220915/20150150ZyyznZj4uw.png

拉到最下面可以看到每檔股票的收盤行情:

https://ithelp.ithome.com.tw/upload/images/20220915/20150150cGhtiQllDb.png

回到頁面上方,點擊「列印 / HTML」連結,瀏覽器會開新分頁將資訊輸出成可列印的 HTML 頁面。假設資料日期為「民國 111 年 07 月 01 日」,我們會得到以下 URL:

https://www.twse.com.tw/exchangeReport/MI_INDEX?response=html&date=20220701&type=ALLBUT0999

以上 URL 可設定的參數如下:

  • response:回應資料的格式。指定 html 輸出 HTML 文件;改為 csv 可以另存 CSV 檔案;設定成 json 或不指定則回應 JSON 格式資料。
  • date:資料日期。接受的日期格式為 yyyyMMdd,如 20220701
  • type:分類項目。 ALLBUT0999 表示分類項目是「全部(不含權證、牛熊證、可展延牛熊證)」。

我們將 URL 查詢參數改為 response=json&date=20220701&type=ALLBUT0999,證交所就會以 JSON 格式資料回應 2022 年 7 月 1 日的個股收盤行情:

{
  ......
  "fields9": [
    "證券代號",
    "證券名稱",
    "成交股數",
    "成交筆數",
    "成交金額",
    "開盤價",
    "最高價",
    "最低價",
    "收盤價",
    "漲跌(+/-)",
    "漲跌價差",
    "最後揭示買價",
    "最後揭示買量",
    "最後揭示賣價",
    "最後揭示賣量",
    "本益比"
  ],
  ......
  "data9": [
    [
      "0050",
      "元大台灣50",
      "36,878,756",
      "51,585",
      "4,168,809,309",
      "115.65",
      "115.65",
      "111.20",
      "111.55",
      "<p style= color:green>-</p>",
      "4.25",
      "111.55",
      "29",
      "111.60",
      "4",
      "0.00"
    ],
    ......
  ]
}

因為回應資料較長,以上我們簡化了資料只顯示重要的部分。我們要取得的個股行情是放在 JSON 欄位 data9 的內容,其陣列的每個元素即代表一檔個股行情資訊。

實作:取得集中市場股票行情

在專案目錄下開啟 src/scraper/twse-scraper.service.ts 檔案,在 TwseScraperService 實作 fetchEquitiesQuotes() 方法,取得集中市場個股行情:

import * as _ from 'lodash';
import * as cheerio from 'cheerio';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { DateTime } from 'luxon';
import { firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { getTwseIndexSymbolByName } from '@speculator/common';

@Injectable()
export class TwseScraperService {
  constructor(private httpService: HttpService) {}

  ...

  async fetchEquitiesQuotes(date: string) {
    // 將 `date` 轉換成 `yyyyMMdd` 格式
    const formattedDate = DateTime.fromISO(date).toFormat('yyyyMMdd');

    // 建立 URL 查詢參數
    const query = new URLSearchParams({
      response: 'json',     // 指定回應格式為 JSON
      date: formattedDate,  // 指定資料日期
      type: 'ALLBUT0999',   // 指定分類項目為全部(不含權證、牛熊證、可展延牛熊證)
    });
    const url = `https://www.twse.com.tw/exchangeReport/MI_INDEX?${query}`;

    // 取得回應資料
    const responseData = await firstValueFrom(this.httpService.get(url))
      .then(response => (response.data.stat === 'OK') ? response.data : null);

    // 若該日期非交易日或尚無成交資訊則回傳 null
    if (!responseData) return null;

    // 整理回應資料
    const data = responseData.data9.map(row => {
      const [ symbol, name, ...values ] = row;
      const [
        tradeVolume,  // 成交股數
        transaction,  // 成交筆數
        tradeValue,   // 成交金額
        openPrice,    // 開盤價
        highPrice,    // 最高價
        lowPrice,     // 最低價
        closePrice,   // 收盤價
      ] = values.slice(0, 7).map(value => numeral(value).value());

      // 計算漲跌
      const change = values[7].includes('green')
        ? numeral(values[8]).multiply(-1).value()
        : numeral(values[8]).value();

      // 回推參考價
      const referencePrice = closePrice && numeral(closePrice).subtract(change).value();

      // 計算漲跌幅
      const changePercent = closePrice  && +numeral(change).divide(referencePrice).multiply(100).format('0.00');

      return {
        date,
        symbol,
        name,
        openPrice,
        highPrice,
        lowPrice,
        closePrice,
        change,
        changePercent,
        tradeVolume,
        tradeValue,
        transaction,
      };
    });

    return data;
  }
}

fetchEquitiesQuotes() 方法中,需要指定 date 參數,表示要取得集中市場個股行情的日期。我們定義回傳的型別是一個陣列,每個陣列元素代表一檔股票的物件,物件欄位包含如下:

  • date:日期
  • symbol:股票代號
  • name:股票名稱
  • openPrice:開盤價
  • highPrice:最高價
  • lowPrice:最低價
  • closePrice:收盤價
  • change:漲跌
  • changePercent:漲跌幅
  • tradeVolume:成交股數
  • tradeValue:成交金額
  • transaction:成交筆數

完成後,我們只要呼叫 TwseScraperServicefetchEquitiesQuotes() 方法,就可以按日期取得集中市場個股行情。以日期 2022-07-01 為例:

[
  {
    date: '2022-07-01',
    symbol: '0050',
    name: '元大台灣50',
    openPrice: 115.65,
    highPrice: 115.65,
    lowPrice: 111.2,
    closePrice: 111.55,
    change: -4.25,
    changePercent: -3.67,
    tradeVolume: 36878756,
    tradeValue: 4168809309,
    transaction: 51585
  },
  ... 1167 more items
]

查詢櫃買市場股票行情

在櫃買中心網站的 上櫃股票行情 頁面,可以按日查詢上櫃股票收盤行情。

櫃買中心首頁 > 上櫃 > 盤後資訊 > 上櫃股票行情

在「上櫃股票行情」頁面選取「資料日期」後,就會列出該日上櫃股票收盤行情。

https://ithelp.ithome.com.tw/upload/images/20220915/20150150JRBB1vromK.png

點擊「列印/匯出HTML」連結,瀏覽器會開新分頁將資訊輸出成可列印的 HTML 頁面。假設資料日期為「111/07/01」,我們會得到以下 URL:

https://www.tpex.org.tw/web/stock/aftertrading/daily_close_quotes/stk_quote_result.php?l=zh-tw&o=htm&d=111/07/01&s=0,asc,0

以上 URL 可設定的參數如下:

  • l:輸出資料的語系。zh-tw 為正體中文;en-us 為英文。
  • d:資料日期。接受 民國年/月/日 的日期格式。需要注意,若 l 參數指定為 en-us,則 d 參數需改成 西元年/月/日 的日期格式。
  • s:指定欄位要依照升冪或降冪排序。例如 0,asc,0 是按代號升冪排序;1,desc,0 則按名稱降冪排序,依此類推。
  • o:資料輸出的格式。指定 htm 表示輸出 HTML 文件;改為 csv 可以另存 CSV 檔案;設定成 json 或不指定則回應 JSON 格式資料。

我們將 URL 查詢參數改為 l=zh-tw&o=json&d=111/07/01,櫃買中心就會以 JSON 格式資料回應 2022 年 7 月 1 日的上櫃股票行情:

{
  "reportDate": "111/07/01",
  "reportTitle": "上櫃股票行情(含等價、零股、盤後、鉅額交易)",
  "iTotalRecords": 7747,
  "iTotalDisplayRecords": 7747,
  "colNum": 19,
  "listNum": "799",
  "totalAmount": "67,619,220,358",
  "totalVolumn": "845,320,570",
  "totalCount": "577,205",
  "mmData": [],
  "aaData": [
    [
      "006201",
      "元大富櫃50",
      "15.26",
      "-0.84 ",
      "16.10",
      "16.10",
      "15.26",
      "15.58",
      "210,613",
      "3,281,721",
      "167",
      "15.25",
      "7",
      "15.26",
      "6",
      "17,446,000",
      "15.26",
      "16.78",
      "13.74"
    ],
    ......
  ]
}

以上回應資料 aaData 欄位的陣列中每個索引值的元素,依序表示為股票代號、股票名稱、收盤價、漲跌、開盤價、最高價、最低價、成交均價、成交股數、成交金額(元)、成交筆數、最後買價、最後買量(千股)、最後賣價、最後賣量(千股)、發行股數、次日參考價、次日漲停價、次日跌停價。

實作:取得櫃買市場股票行情

因為櫃買中心回傳的上櫃股票收盤行情包含權證,所以我們先實作一個工具函式用來判斷證券代號為權證與否。

我們在 libs/common/src/utils 目錄下新增 is-warrant.util.ts 檔案,實作 isWarrant() 工具函示:

export function isWarrant(symbol: string): boolean {
  const rules = [
    /^0[3-8][0-9][0-9][0-9][0-9]$/, // 上市國內標的認購權證
    /^0[3-8][0-9][0-9][0-9]P$/,     // 上市國內標的認售權證
    /^0[3-8][0-9][0-9][0-9]F$/,     // 上市外國標的認購權證
    /^0[3-8][0-9][0-9][0-9]Q$/,     // 上市外國標的認售權證
    /^0[3-8][0-9][0-9][0-9]C$/,     // 上市國內標的下限型認購權證
    /^0[3-8][0-9][0-9][0-9]B$/,     // 上市國內標的上限型認售權證
    /^0[3-8][0-9][0-9][0-9]X$/,     // 上市國內標的可展延下限型認購權證
    /^0[3-8][0-9][0-9][0-9]Y$/,     // 上市國內標的可展延上限型認售權證
    /^7[0-3][0-9][0-9][0-9][0-9]$/, // 上櫃國內標的認購權證
    /^7[0-3][0-9][0-9][0-9]P$/,     // 上櫃國內標的認售權證
    /^7[0-3][0-9][0-9][0-9]F$/,     // 上櫃外國標的認購權證
    /^7[0-3][0-9][0-9][0-9]Q$/,     // 上櫃外國標的認售權證
    /^7[0-3][0-9][0-9][0-9]C$/,     // 上櫃國內標的下限型認購權證
    /^7[0-3][0-9][0-9][0-9]B$/,     // 上櫃國內標的上限型認售權證
    /^7[0-3][0-9][0-9][0-9]X$/,     // 上櫃國內標的可展延下限型認購權證
    /^7[0-3][0-9][0-9][0-9]Y$/,     // 上櫃國內標的可展延上限型認售權證
  ]
  return rules.some(regex => regex.test(symbol));
}

因為權證代號有一定的編碼規則,我們實作 isWarrant() 函式接受有價證券代號,依照有價證券代號的規則判斷是否為權證。

完成後,開啟 libs/common/src/utils/index.ts 檔案,將 is-warrant.util.ts 檔案匯出:

export * from './is-warrant.util';

之後就可以在 Nest 應用程式透過以下方式引用 isWarrant() 函式:

import { isWarrant } from '@speculator/common'

完成 isWarrant() 工具函式後,我們要實作取得上櫃個股行情。開啟 src/scraper/tpex-scraper.service.ts 檔案,在 TpexScraperService 實作 fetchEquitiesQuotes() 方法,取得櫃買市場個股行情:

import * as _ from 'lodash';
import * as numeral from 'numeral';
import { DateTime } from 'luxon';
import { firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { Index, getTpexIndexSymbolByName, isWarrant } from '@speculator/common';

@Injectable()
export class TpexScraperService {
  constructor(private httpService: HttpService) {}

  async fetchEquitiesQuotes(date: string) {
    // `date` 轉換成 `民國年/MM/dd` 格式
    const dt = DateTime.fromISO(date);
    const year = dt.get('year') - 1911;
    const formattedDate = `${year}/${dt.toFormat('MM/dd')}`;

    // 建立 URL 查詢參數
    const query = new URLSearchParams({
      l: 'zh-tw',       // 指定語系為正體中文
      d: formattedDate, // 指定資料日期
      o: 'json',        // 指定回應格式為 JSON
    });
    const url = `https://www.tpex.org.tw/web/stock/aftertrading/daily_close_quotes/stk_quote_result.php?${query}`;

    // 取得回應資料
    const responseData = await firstValueFrom(this.httpService.get(url))
      .then(response => (response.data.iTotalRecords > 0) ? response.data : null);

    // 若該日期非交易日或尚無成交資訊則回傳 null
    if (!responseData) return null;

    // 整理回應資料
    const data = responseData.aaData
      .filter(row => !isWarrant(row[0]))  // 過濾權證
      .map(row => {
        const [ symbol, name, ...values ] = row;
        const [
          closePrice,   // 收盤價
          change,       // 漲跌
          openPrice,    // 開盤價
          highPrice,    // 最高價
          lowPrice,     // 最低價
          avgPrice,     // 均價
          tradeVolume,  // 成交股數
          tradeValue,   // 成交金額
          transaction,  // 成交筆數
        ] = values.slice(0, 9).map(value => numeral(value).value());

        // 回推參考價
        const referencePrice = (closePrice && change !== null) && numeral(closePrice).subtract(change).value() || null;

        // 計算漲跌幅
        const changePercent = (closePrice && change !== null) && +numeral(change).divide(referencePrice).multiply(100).format('0.00') || null;

        return {
          date,
          symbol,
          name,
          openPrice,
          highPrice,
          lowPrice,
          closePrice,
          change,
          changePercent,
          tradeVolume,
          tradeValue,
          transaction,
        };
      });

    return data;
  }
}

fetchEquitiesQuotes() 方法中,需要指定 date 參數,表示要取得櫃買市場個股行情的日期。我們定義回傳的型別是一個陣列,每個陣列元素代表一檔股票的物件,物件欄位包含如下:

  • date:日期
  • symbol:股票代號
  • name:股票名稱
  • openPrice:開盤價
  • highPrice:最高價
  • lowPrice:最低價
  • closePrice:收盤價
  • change:漲跌
  • changePercent:漲跌幅
  • tradeVolume:成交股數
  • tradeValue:成交金額
  • transaction:成交筆數

完成後,我們只要呼叫 TpexScraperServicefetchEquitiesQuotes() 方法,就可以按日期取得櫃買市場個股行情。以日期 2022-07-01 為例:

[
  {
    date: '2022-07-01',
    symbol: '006201',
    name: '元大富櫃50',
    openPrice: 16.1,
    highPrice: 16.1,
    lowPrice: 15.26,
    closePrice: 15.26,
    change: -0.84,
    changePercent: -5.22,
    tradeVolume: 210613,
    tradeValue: 3281721,
    transaction: 167
  },
  ... 905 more items
]

本日小結

  • K線是由開盤價、最高價、最低價、收盤價所組成。
  • K線分為紅K線與黑K線,當收盤價高於開盤價以紅K線表示;當收盤價低於開盤價以黑K線表示。
  • 當最高價高於紅K線的收盤價或高於黑K線的開盤價,就會出現上影線;當最低價低於紅K線開盤價或低於黑K線收盤價,就會出現下影線。
  • K線依照時間架構可分成日K線、週K線、月K線等。
  • 將某段期間內的相同時間單位的K線組合起來,就可以畫出K線圖。
  • 瞭解如何在證交所網站上查詢並實作取得上市股票行情的方法。
  • 瞭解如何在櫃買中心網站上查詢並實作取得上櫃股票行情的方法。

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


上一篇
Day 14 - 市場資金去哪裡:產業資金流向
下一篇
Day 16 - 找出法人認養股:三大法人買賣明細
系列文
從 Node.js 開發者到量化交易者:打造屬於自己的投資系統31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言