iT邦幫忙

2022 iThome 鐵人賽

DAY 9
2
Software Development

從 Node.js 開發者到量化交易者:打造屬於自己的投資系統系列 第 9

Day 09 - 散戶期貨指標:小台散戶多空比

  • 分享至 

  • xImage
  •  

在臺灣股票現貨市場,融資融券餘額 經常被視為散戶指標,而在臺灣期貨市場中,也有衡量散戶對行情看法的方式,這個指標是「小台散戶多空比」。由於散戶經常是市場的輸家,所以小台散戶多空比被視為是市場行情的一項反指標。

什麼是小台?

在臺灣期貨交易所的「股價指數期貨類商品」中,交易標的為「臺灣證券交易所發行量加權股價指數」的商品有「臺股期貨」和「小型臺指期貨」。臺股期貨簡稱「大台」,小型臺指期貨簡稱「小台」。

大台與小台的交易標的都是證交所發行量加權股價指數,不過差別在於商品規格不一樣。大台的契約價值是指數 1 點 200 元;小台的契約價值是指數 1 點 50 元,因此 1 口大台相當於 4 口小台。因為契約價值不同,大台與小台所需的原始保證金也相差 4 倍,目前交易 1 口大台需要原始保證金 184,000 元,小台需要 46,000 元。受限於資金規模的大小,大台主要的交易者是機構法人和大戶,而散戶則主要以操作小台為主。

什麼是小台散戶多空比?

透過計算 小台散戶多空比,可以反映當下散戶對於臺股行情的看法。當小台散戶多空比數字愈大,表示散戶做多部位愈多;當小台散戶多空比負值愈大,表示散戶做空部位愈多。事實上,期交所並沒有直接提供 小台散戶多空比 的數字,必須自行計算,它的計算方式是:

散戶多空比 = (散戶多單 - 散戶空單) / 小型臺指期貨所有契約未沖銷契約量 x 100%

根據上面的公式,我們除了期交所網站查詢期貨行情找到「小型臺指期貨所有契約未沖銷契約量」外,還需要自行計算「散戶多單」以及「散戶空單」的數字,計算方式為:

散戶多單 = 小型臺指期貨所有契約未沖銷契約量 - 三大法人多方未平倉量
散戶空單 = 小型臺指期貨所有契約未沖銷契約量 - 三大法人空方未平倉量

期交所盤後會公佈各期貨契約未沖銷契約量以及三大法人的未平倉口數,我們將小型臺指期貨市場上未沖銷契約量扣除三大法人的未平倉口數,定義為市場上「散戶」的部位。下圖統計 2022 年初至 8 月 31 日的小台散戶多空比,可以看出散戶習慣做逆勢單。

https://ithelp.ithome.com.tw/upload/images/20220909/20150150N4WDYwIXGk.png

在不考慮手續費和交易稅的情況下,期貨交易的結果是零和遊戲,也就是說,參與交易的雙方,有一方賺錢而必然有一方賠錢。從長期的歷史經驗來看,散戶都是輸家的一方,所以散戶多空比被視為是市場反指標,當散戶看多時,指數往往會下跌;當散戶看空時,指數反而容易上漲。

查詢小型臺指期貨未沖銷契約量

計算小台散戶多空比的第一步是取得小型臺指期貨所有契約未沖銷契約量。在期交所網站的 期貨每日交易行情查詢 頁面,可以查詢期貨每日交易行情。

期交所首頁 > 交易資訊 > 期貨 > 期貨每日交易行情查詢

在「期貨每日交易行情查詢」頁面,選取「日期」,然後「交易時段」選擇「一般交易時段」,並在「契約」選擇「小型臺指(MTX)」送出查詢後,就會列出該日小型臺指行情表。

https://ithelp.ithome.com.tw/upload/images/20220909/20150150UVxOquFhr6.png

我們要查詢所有小型臺指期貨契約的「未沖銷契約量」,以 2022 年 7 月 1 日為例,即為「67,659」口。

當遇到結算日時,未沖銷契約量要扣除當日結算的契約。當日結算的契約,在結算價欄位會顯示「-」。

查詢三大法人小型臺指期貨未平倉量

接著我們需要取得三大法人小型臺指期貨未平倉量來計算出散戶部位。在期交所網站的 交易資訊-三大法人-查詢-區分各期貨契約-依日期 頁面,可以按日查詢三大法人在各期貨契約的交易資訊。

期交所首頁 > 交易資訊 > 三大法人 > 查詢 > 區分各期貨契約 > 依日期

在「交易資訊-三大法人-查詢-區分各期貨契約-依日期」頁面,選取要查詢的「日期」,「契約」選擇「小型臺指期貨」送出查詢後,就會列出該日三大法人在小型臺股期貨的交易資訊。

https://ithelp.ithome.com.tw/upload/images/20220909/20150150lOUEaNXLuO.png

我們要查詢三大法人在小型臺股期貨的多方及空方未平倉量總計。以 2022 年 7 月 1 日為例,多方未平倉量為「8,909」口;空方未平倉量為「27,458」口。

計算小台散戶淨部位及小台散戶多空比

根據上述查詢得到的數字,我們就可以計算出小型臺指期貨的 散戶多單散戶空單散戶淨部位 以及 散戶多空比。以 2022 年 7 月 1 日為例,計算出來的數字為:

散戶多單 = 67,659 - 8,909 = 58,750
散戶空單 = 67,659 - 27,458 = 40,201
散戶淨部位 = 58,750 - 40,201 = 18,549
散戶多空比 = 18,549 / 67,659 * 100% = 27.42%

下載小型臺指期貨每日交易行情

上面我們介紹透過手動查詢的方法,計算出小台散戶淨部位及小台散戶多空比。接下來我們要使用自動化的方式來取得這些數據。

在期交所網站的 交易資訊-交易資訊-期貨-期貨每日交易行情下載 頁面,可以下載期貨每日交易行情。

期交所首頁 > 交易資訊 > 交易資訊 > 期貨 > 期貨每日交易行情下載

https://ithelp.ithome.com.tw/upload/images/20220909/201501505RIWzqH22z.png

在「交易資訊-交易資訊-期貨-期貨每日交易行情下載」頁面,選取「日期(起)」、「日期(迄)」,契約選擇「小型臺指(MTX)」,然後點擊下載,就可以取得查詢結果的 CSV 檔案。實際上,這個 HTML Form 表單是使用 POST 方法,向以下位址提交表單請求:

https://www.taifex.com.tw/cht/3/futDataDown

這個表單可設定的欄位如下:

  • down_type:下載類型。1 為每日行情下載。
  • queryStartDate:日期(起)。接受 yyyy/MM/dd 的日期格式,如 2022/07/01
  • queryEndDate:日期(迄)。接受 yyyy/MM/dd 的日期格式,如 2022/07/01
  • commodity_id:商品契約代號。MTX 為小型臺指期貨。

我們可以打開終端機使用 curl 指令模擬表單請求:

$ curl --request POST \
    --url https://www.taifex.com.tw/cht/3/futDataDown \
    --header 'Content-Type: multipart/form-data' \
    --form down_type=1 \
    --form queryStartDate=2022/07/01 \
    --form queryEndDate=2022/07/01 \
    --form commodity_id=MTX

透過下載小型臺指期貨每日交易行情資訊,我們就可以找到小型臺指所有契約未沖銷量。下一步,我們要取得三大法人小型臺指多空未平倉量。

下載三大法人小型臺指多空未平倉量

在期交所網站的 交易資訊-三大法人-下載-區分各期貨契約-依日期 頁面,可以下載三大法人各期貨契約交易資訊的 CSV 檔案。

期交所首頁 > 交易資訊 > 三大法人 > 下載 > 區分各期貨契約 > 依日期


https://ithelp.ithome.com.tw/upload/images/20220909/20150150uVv1Vqa8Tf.png

在「交易資訊-三大法人-下載-區分各期貨契約-依日期」頁面,選取「日期(起)」、「日期(迄)」,契約選擇「小型臺指期貨」後,然後點擊下載,就可以取得查詢結果的 CSV 檔案。實際上,這個 HTML Form 表單是使用 POST 方法,向以下位址提交表單請求:

https://www.taifex.com.tw/cht/3/futContractsDateDown

這個表單可設定的欄位如下:

  • queryStartDate:日期(起)。接受 yyyy/MM/dd 的日期格式,如 2022/07/01
  • queryEndDate:日期(迄)。接受 yyyy/MM/dd 的日期格式,如 2022/07/01
  • commodityId:商品契約代號。小型臺股期貨為 MXF

我們可以打開終端機使用 curl 指令模擬表單請求:

$ curl --request POST \
    --url https://www.taifex.com.tw/cht/3/futContractsDateDown \
    --header 'Content-Type: multipart/form-data' \
    --form queryStartDate=2022/07/01 \
    --form queryEndDate=2022/07/01 \
    --form commodityId=MXF

瞭解如何下載 CSV 檔案取得小型臺指所有契約未沖銷量以及三大法人小型臺指多空未平倉量的方式後,我們就可以算出小台散戶淨部位及小台散戶多空比。

實作:取得小台散戶淨部位以及小台多空比

按上面介紹的順序,我們要計算期散戶小台多空比的第一步,是取得小型臺指所有契約未沖銷量。

開啟 src/scraper/taifex-scraper.service.ts 檔案,在 TaifexScraperService 實作 fetchMxfMarketOi() 方法,取得小型臺指所有契約未沖銷量:

import * as csvtojson from 'csvtojson';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { DateTime } from 'luxon';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';

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

  ...

  private async fetchMxfMarketOi(date: string) {
    // 將 `date` 轉換成 `yyyy/MM/dd` 格式
    const queryDate = DateTime.fromISO(date).toFormat('yyyy/MM/dd');

    // 建立 FormData
    const form = new URLSearchParams({
      down_type: '1',             // 每日行情下載
      queryStartDate: queryDate,  // 日期(起)
      queryEndDate: queryDate,    // 日期(迄)
      commodity_id: 'MTX',        // 小型臺指(MTX)
    });
    const url = 'https://www.taifex.com.tw/cht/3/futDataDown';

    // 取得回應資料並將 CSV 轉換成 JSON 格式及正確編碼
    const responseData = await firstValueFrom(this.httpService.post(url, form, { responseType: 'arraybuffer' }))
      .then(response => csvtojson({ noheader: true, output: 'csv' }).fromString(iconv.decode(response.data, 'big5')));

    // 若該日期非交易日或尚無資料則回傳 null
    const [fields, ...rows] = responseData;
    if (fields[0] !== '交易日期') return null;

    // 計算小型臺指全市場未平倉口數
    const mxfMarketOi = rows
      .filter(row => row[17] === '一般' && !row[18]) // 僅取日盤並排除價差合約
      .reduce((oi, row) => oi + numeral(row[11]).value(), 0);

    return { date, mxfMarketOi };
  }
}

fetchMxfMarketOi() 方法中,需要指定 date 參數,表示要取得小型臺指所有契約未沖銷量的日期。我們定義回傳的物件欄位包含如下:

  • date:日期
  • mxfMarketOi:小型臺指所有契約未沖銷量

完成後,我們只要使用 TaifexScraperServicefetchMxfMarketOi() 方法,就可以按日期取得小型臺指所有契約未沖銷量。以日期 2022-07-01 為例:

{ 
  date: '2022-07-01',
  mxfMarketOi: 67659
}

接下來繼續在 TaifexScraperService 實作 fetchInstInvestorsMxfOi() 方法,取得三大法人小型臺指多空未平倉量:

import * as csvtojson from 'csvtojson';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { DateTime } from 'luxon';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';

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

  ...

  private async fetchInstInvestorsMxfOi(date: string) {
    // 將 `date` 轉換成 `yyyy/MM/dd` 格式
    const queryDate = DateTime.fromISO(date).toFormat('yyyy/MM/dd');

    // 建立 FormData
    const form = new URLSearchParams({
      queryStartDate: queryDate,  // 日期(起)
      queryEndDate: queryDate,    // 日期(迄)
      commodityId: 'MXF',         // 契約-小型臺指
    });
    const url = 'https://www.taifex.com.tw/cht/3/futContractsDateDown';

    // 取得回應資料並將 CSV 轉換成 JSON 格式及正確編碼
    const responseData = await firstValueFrom(this.httpService.post(url, form, { responseType: 'arraybuffer' }))
      .then(response => csvtojson({ noheader: true, output: 'csv' }).fromString(iconv.decode(response.data, 'big5')));

    // 若該日期非交易日或尚無資料則回傳 null
    const [fields, dealers, sitc, fini] = responseData;
    if (fields[0] !== '日期') return null;

    // 合併三大法人交易數據並將 string 型別數字轉換成 number
    const raw = [...dealers.slice(3), ...sitc.slice(3), ...fini.slice(3)]
      .map(data => numeral(data).value());

    const [
      dealersLongTradeVolume,   // 自營商-多方交易口數
      dealersLongTradeValue,    // 自營商-多方交易契約金額(千元)
      dealersShortTradeVolume,  // 自營商-空方交易口數
      dealersShortTradeValue,   // 自營商-空方交易契約金額(千元)
      dealersNetTradeVolume,    // 自營商-多空交易口數淨額
      dealersNetTradeValue,     // 自營商-多空交易契約金額淨額(千元)
      dealersLongOiVolume,      // 自營商-多方未平倉口數
      dealersLongOiValue,       // 自營商-多方未平倉契約金額(千元)
      dealersShortOiVolume,     // 自營商-空方未平倉口數
      dealersShortOiValue,      // 自營商-空方未平倉契約金額(千元)
      dealersNetOiVolume,       // 自營商-多空未平倉口數淨額
      dealersNetOiValue,        // 自營商-多空未平倉契約金額淨額(千元)
      sitcLongTradeVolume,      // 投信-多方交易口數
      sitcLongTradeValue,       // 投信-多方交易契約金額(千元)
      sitcShortTradeVolume,     // 投信-空方交易口數
      sitcShortTradeValue,      // 投信-空方交易契約金額(千元)
      sitcNetTradeVolume,       // 投信-多空交易口數淨額
      sitcNetTradeValue,        // 投信-多空交易契約金額淨額(千元)
      sitcLongOiVolume,         // 投信-多方未平倉口數
      sitcLongOiValue,          // 投信-多方未平倉契約金額(千元)
      sitcShortOiVolume,        // 投信-空方未平倉口數
      sitcShortOiValue,         // 投信-空方未平倉契約金額(千元)
      sitcNetOiVolume,          // 投信-多空未平倉口數淨額
      sitcNetOiValue,           // 投信-多空未平倉契約金額淨額(千元)
      finiLongTradeVolume,      // 外資-多方交易口數
      finiLongTradeValue,       // 外資-多方交易契約金額(千元)
      finiShortTradeVolume,     // 外資-空方交易口數
      finiShortTradeValue,      // 外資-空方交易契約金額(千元)
      finiNetTradeVolume,       // 外資-多空交易口數淨額
      finiNetTradeValue,        // 外資-多空交易契約金額淨額(千元)
      finiLongOiVolume,         // 外資-多方未平倉口數
      finiLongOiValue,          // 外資-多方未平倉契約金額(千元)
      finiShortOiVolume,        // 外資-空方未平倉口數
      finiShortOiValue,         // 外資-空方未平倉契約金額(千元)
      finiNetOiVolume,          // 外資-多空未平倉口數淨額
      finiNetOiValue,           // 外資-多空未平倉契約金額淨額(千元)
    ] = raw;

    // 計算三大法人小型臺指多方未平倉口數
    const instInvestorsMxfLongOi = dealersLongOiVolume + sitcLongOiVolume + finiLongOiVolume;

    // 計算三大法人小型臺指空方未平倉口數
    const instInvestorsMxfShortOi = dealersShortOiVolume + sitcShortOiVolume + finiShortOiVolume;

    return {
      date,
      instInvestorsMxfLongOi,
      instInvestorsMxfShortOi,
    };
  }
}

fetchInstInvestorsMxfOi() 方法中,需要指定 date 參數,表示要取得三大法人小型臺指多空未平倉量的日期。我們定義回傳的物件欄位包含如下:

  • date:日期
  • instInvestorsMxfLongOi:三大法人多方未平倉量
  • instInvestorsMxfShortOi:三大法人空方未平倉量

完成後,我們只要使用 TaifexScraperServicefetchMxfMarketOi() 方法,就可以按日期取得三大法人小型臺指多空未平倉量。以日期 2022-07-01 為例:

{
  date: '2022-07-01',
  instInvestorsMxfLongOi: 8909,
  instInvestorsMxfShortOi: 27458
}

取得小型臺指期貨所有契約未沖銷量以及三大法人多空未平倉量後,我們就可以計算出散戶小台淨部位以及散戶小台多空比,在 TaifexScraperService 實作 fetchRetailMxfPosition() 方法:

import * as csvtojson from 'csvtojson';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { DateTime } from 'luxon';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';

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

  ...

  async fetchRetailMxfPosition(date: string) {
    // 取得全市場及三大法人小型臺指未平倉口數
    const [ fetchedMxfMarketOi, fetchedInstInvestorsMxfOi ] = await Promise.all([
      this.fetchMxfMarketOi(date),
      this.fetchInstInvestorsMxfOi(date),
    ]);

    // 若該日期非交易日或尚無資料則回傳 null
    if (!fetchedMxfMarketOi || !fetchedInstInvestorsMxfOi) return null;

    const { mxfMarketOi } = fetchedMxfMarketOi;
    const { instInvestorsMxfLongOi, instInvestorsMxfShortOi } = fetchedInstInvestorsMxfOi;

    // 計算散戶小型臺指多方未平倉口數
    const retailMxfLongOi = mxfMarketOi - instInvestorsMxfLongOi;

    // 計算散戶小型臺指空方未平倉口數
    const retailMxfShortOi = mxfMarketOi - instInvestorsMxfShortOi;

    // 散戶小型臺指淨未平倉口數
    const retailMxfNetOi = retailMxfLongOi - retailMxfShortOi;

    // 計算散戶小台多空比
    const retailMxfLongShortRatio = Math.round(retailMxfNetOi / mxfMarketOi * 10000) / 10000;

    return {
      date,
      retailMxfLongOi,
      retailMxfShortOi,
      retailMxfNetOi,
      retailMxfLongShortRatio,
    };
  }
}

fetchRetailMxfPosition() 方法中,需要指定 date 參數,表示要取得三大法人小型臺指多空未平倉量的日期。我們定義回傳的物件欄位包含如下:

  • date:日期
  • instInvestorsMxfLongOi:三大法人多方未平倉量
  • instInvestorsMxfShortOi:三大法人空方未平倉量

完成後,我們只要使用 TaifexScraperServicefetchRetailMxfPosition() 方法,就可以按日期取得散戶小台淨部位以及散戶小台多空比。以日期 2022-07-01 為例:

{
  date: '2022-07-01',
  retailMxfLongOi: 58750,
  retailMxfShortOi: 40201,
  retailMxfNetOi: 18549,
  retailMxfLongShortRatio: 0.2742
}

本日小結

  • 在臺灣期貨市場上,臺股期貨簡稱大台,小型臺指期貨簡稱小台。
  • 受限於資金規模的限制,大台主要的交易者是機構法人和大戶,散戶主要操作小台。
  • 散戶多空比被視為是市場反指標,當散戶看多時,指數往往會下跌;當散戶看空時,指數反而容易上漲。
  • 計算散戶小台多空比之前,需要取得小型臺指期貨所有契約未沖銷量以及小型臺指三大法人多空未平倉量。
  • 瞭解如何在期交所網站上查詢並實作取得小型臺指期貨所有契約未沖銷量的方法。
  • 瞭解如何在期交所網站上查詢並實作取得小型臺指三大法人多空未平倉量的方法。
  • 暸解如何計算並實作取得散戶小台淨部位以及散戶多空比的方法。

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


上一篇
Day 08 - 大戶指標:大額交易人未沖銷部位
下一篇
Day 10 - 臺股同時指標:美元兌新臺幣匯率
系列文
從 Node.js 開發者到量化交易者:打造屬於自己的投資系統31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言