{"id":30459637,"url":"https://github.com/lsyueh/templar","last_synced_at":"2026-04-16T12:01:38.394Z","repository":{"id":310073704,"uuid":"1035766191","full_name":"LsYueh/templar","owner":"LsYueh","description":"臺灣證券交易所 (TWSE) TMP 傳輸協定訊息轉換工具","archived":false,"fork":false,"pushed_at":"2025-09-05T01:21:50.000Z","size":571,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-05T02:24:59.356Z","etag":null,"topics":["ascii","big5","cobol","cp950","deserialization","javascript","js","node","nodejs","securities","securities-orders","sql","stock","stock-exchange","stock-exchanges","stockexchange","tmp","transaction-message-protocol","twse"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LsYueh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-11T04:07:20.000Z","updated_at":"2025-09-05T01:21:53.000Z","dependencies_parsed_at":"2025-09-05T02:16:30.424Z","dependency_job_id":null,"html_url":"https://github.com/LsYueh/templar","commit_stats":null,"previous_names":["lsyueh/templar"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/LsYueh/templar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LsYueh%2Ftemplar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LsYueh%2Ftemplar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LsYueh%2Ftemplar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LsYueh%2Ftemplar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LsYueh","download_url":"https://codeload.github.com/LsYueh/templar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LsYueh%2Ftemplar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31884929,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T11:36:10.202Z","status":"ssl_error","status_checked_at":"2026-04-16T11:36:09.652Z","response_time":69,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ascii","big5","cobol","cp950","deserialization","javascript","js","node","nodejs","securities","securities-orders","sql","stock","stock-exchange","stock-exchanges","stockexchange","tmp","transaction-message-protocol","twse"],"created_at":"2025-08-23T18:48:46.239Z","updated_at":"2026-04-16T12:01:38.381Z","avatar_url":"https://github.com/LsYueh.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TeMPlar\n`TMP`是臺灣證券交易所（TWSE）所使用的專屬傳輸協定，全名為`Transaction Message Protocol`，用於證券交易相關的訊息傳輸。此工具用於解析TMP提供的`委託`/`成交`/`申報`/`檔案傳輸`服務訊息內容，將序列化的字串資料轉為非序列化的資料物件後再加以利用。非序列化的資料亦可使用此工具轉換成`COBOL`可處理的序列化字串資料以用於系統間的資料交換。\n\n\u003cbr\u003e\n\n![流程圖](doc/workflow.png)\n![流程圖](doc/stringify.png)\n\n\u003cbr\u003e\n\n# 使用方式\n\n```js\nconst templar = require('templar');\n```\n\n檔案傳輸\n```js\nconst message = templar.parse('20020508595900000084500003M01', { category: 'file-transfer' }\n\nconsole.log(message);\n\n// Message_t {\n//   body: [ { FileCode: 'M01', ResponseMessage: '' } ],\n//   raw: Uint8Array('20000008595900845000000011T3000000100'),\n//   header: {\n//     SubsystemName: '20',\n//     FunctionCode: '02',\n//     MessageType: '05',\n//     MessageTime: 2025-08-17T00:59:59.000Z,\n//     StatusCode: '00',\n//     SourceId: '0000',\n//     ObjectId: '8450',\n//     BodyLength: 3\n//   },\n//   id: 'F060',\n//   remained: Uint8Array('')\n// }\n```\n```js\nconst message = {\n  id: 'F050',\n  header: {\n    SubsystemName: '20',\n    FunctionCode: '02',\n    MessageType: '05',\n    MessageTime: new Date('2025-08-15T00:59:59.000Z'),\n    StatusCode: '00',\n    SourceId: '8450',\n    ObjectId: '0000',\n    // BodyLength: 3, // 會自動根據body變化\n  },\n  body: [ { FileCode: 'M01', RequestMessage: '' } ],\n};\n\nconsole.log(templar.stringify(message, { category: 'file-transfer' });\n\n// 20020508595900845000000003M01\n```\n\n\n成交回報\n```js\nconst message = templar.parse('500000085959008450000001', { category: 'report' });\n\nconsole.log(message);\n\n// Message_t {\n//   body: [ { BrokerId: '8450', StartSeq: 1 } ],\n//   raw: Uint8Array('500000085959008450000001'),\n//   header: {\n//     SubsystemName: '50',\n//     FunctionCode: '00',\n//     MessageType: '00',\n//     MessageTime: 2025-08-15T00:59:59.000Z,\n//     StatusCode: '00'\n//   },\n//   id: 'R1',\n//   remained: Uint8Array('')\n// }\n```\n```js\nconst message = {\n  id: 'R1',\n  header: {\n    SubsystemName: '50',\n    FunctionCode: '00',\n    MessageType: '00',\n    MessageTime: new Date('2025-08-15T00:59:59.000Z'),\n    StatusCode: '00'\n  },\n  body: [ { BrokerId: '8450', StartSeq: 1 } ],\n};\n\nconsole.log(templar.stringify(message, { category: 'report' }));\n\n// '500000085959008450000001'\n```\n\n\u003cbr\u003e\u003cbr\u003e\n\n# 訊息處理\n\n## 中文字\n交易所的TMP訊息採`ASCII編碼`，而中文字常用`CP950`的編碼方式傳入，故在字串切割上一個中文字實算兩個字元空間。\n\n```js\nconst { parse } = require('./lib/field/parse.js');\n\ntest('Parse CP950', async (t) =\u003e {\n    const buffer = iconv.encode('中文字', 'cp950')\n    t.equal(parse({ picStr: 'X(4)', dataType: DATA_TYPE.String, buffer }), '中文');\n    t.equal(parse({ picStr: 'X(5)', dataType: DATA_TYPE.String, buffer }), '中文�');\n    t.equal(parse({ picStr: 'X(6)', dataType: DATA_TYPE.String, buffer }), '中文字');\n});\n```\n\n\u003cbr\u003e\n\n## 數字\n復刻COBOL文字與數字不同的`對齊方式` (文字靠左，數字靠右)。\n\n```js\nconst { parse } = require('./lib/field/parse.js');\n\ntest('Parse PIC 9', async (t) =\u003e {\n    t.equal(parse({ picStr: '9(5)', dataType: DATA_TYPE.Unsigned, buffer: Buffer.from('12345') }), 12345);\n    t.equal(parse({ picStr: '9(5)', dataType: DATA_TYPE.Unsigned, buffer: Buffer.from('123456') }), 23456);\n});\n```\n\n\u003cbr\u003e\u003cbr\u003e\n\n# COBOL PICTURE (PIC) 子句\n\n目前支援的解析格式  \n\n基本:\n- PIC X\n- PIC X(4)\n- PIC 9999\n- PIC 9(4)\n\n混合:\n- PIC 99V99\n- PIC 9(3)V9(2)\n- PIC 9(3)V99\n- PIC S99V99\n- PIC S9(3)V9(2)\n- PIC S9(3)V99\n\n\u003cbr\u003e\u003cbr\u003e\n\n# 資料型態對照表\n\n## 字串\n| COBOL PIC   | 說明                   | Node 對應  |\n| ----------- | ---------------------- | :---------: |\n| `PIC X(n)`  | 任意字元，長度 n            | `String`  |\n| `PIC A(n)`  | 只允許字母                 | `String`  |\n| `PIC AN(n)` | 字母 + 數字                | `String`  |\n| `PIC G(n)`  | 雙位元組字元 (DBCS, EBCDIC) | `String`  |\n\n\u003cbr\u003e\n\n## 整數/小數\n\n| COBOL PIC        | 說明                 | Node 對應  |\n| ---------------- | -------------------- | :--------: |\n| `PIC 9(n)`       | 無號小數，整數 n 位     |  `Number`  |\n| `PIC S9(n)`      | 有號小數，整數 n 位     |  `Number`  |\n| `PIC 9(n)V9(m)`  | 無號小數，整數 n 位，小數 m 位 |  `Number`  |\n| `PIC S9(n)V9(m)` | 有號小數，整數 n 位，小數 m 位 |  `Number`  |\n\n\u003cbr\u003e\n\n## 時間/日期\n\n| COBOL PIC                    |   用途   | Node 對應 |\n| ---------------------------- | ------- | :-------: |\n| `PIC X(8)` (YYYYMMDD)        | 日期     | `Date`   |\n| `PIC X(6)` (HHmmss)          | 時間     | `Date`   |\n| `PIC X(9)` (HHmmssSSS)       | 時間     | `Date`   |\n| `PIC X(14)` (YYYYMMDDHHmmss) | 時間戳記 | `Date`   |\n\n### JS時間格式表示\n在交易所提供的文件中，`X(8)`對應`YYYYMMDD`的`DATE`型別，而`X(6)`與`X(9)`分別對應`HHmmss`/`HHmmssSSS`的`TIME`型別。目前Node 22尚未支援`Temporal.PlainTime`與`Temporal.PlainDate`，故對於上述資料型態的轉換會統一轉換至JS的`Date`型別，其中`X(6)`與`X(9)`會自動補上當下執行的日期。\n\n\u003cbr\u003e\u003cbr\u003e\n\n# 正負號數字`S9`字串轉換\n`Signed overpunch`源自Hollerith(赫爾曼·何樂禮)打孔卡編碼，為解決正負號帶來的字元浪費與孔位長度變動的問題，將`數字`與`正負號`整併至一個孔位解讀。目前已知COBOL的對應關係有：\n\n|  數 字  | ca,\u003cbr\u003ecb,\u003cbr\u003ecm,\u003cbr\u003ecr\u003cbr\u003ePositive | ci,\u003cbr\u003ecn\u003cbr\u003ePositive | ca,\u003cbr\u003eci,\u003cbr\u003ecn\u003cbr\u003eNegative | cb\u003cbr\u003eNegative | cm\u003cbr\u003eNegative | cr\u003cbr\u003eNegative |\n| :---- | :---- | :------ | :------ | :------ | :------ | :------ |\n| 0 | '0' | '{' | '}' | '@' | 'p' | ' ' (space) |\n| 1 | '1' | 'A' | 'J' | 'A' | 'q' | '!' |\n| 2 | '2' | 'B' | 'K' | 'B' | 'r' | '\"' (double-quote) |\n| 3 | '3' | 'C' | 'L' | 'C' | 's' | '#' |\n| 4 | '4' | 'D' | 'M' | 'D' | 't' | '$' |\n| 5 | '5' | 'E' | 'N' | 'E' | 'u' | '%' |\n| 6 | '6' | 'F' | 'O' | 'F' | 'v' | '\u0026' |\n| 7 | '7' | 'G' | 'P' | 'G' | 'w' | ''' (single-quote) |\n| 8 | '8' | 'H' | 'Q' | 'H' | 'x' | '(' |\n| 9 | '9' | 'I' | 'R' | 'I' | 'y' | ')' |\n\n\u003cbr\u003e\n\n|  選 項  | 對 應 COBOL |\n| :----: | :---- |\n| ca | RM/COBOL (not RM/COBOL-85) |\n| cb | MBP COBOL |\n| ci | IBM COBOL (RM/COBOL-85) |\n| cm | Micro Focus COBOL |\n| cn | NCR COBOL |\n| cr | Realia COBOL |\n| cv | VAX COBOL |\n\n\u003cbr\u003e\n\nCOBOL的`S9`對正負號數字表示範例 (IBM COBOL)\n```cobol\nPIC S9(3) VALUE -123.\nTRAILING                 '1'   '2'   'L'\nTRAILING SEPARATE  '1'   '2'   '3'   '-'\nLEADING                  'J'   '2'   '3'\nLEADING SEPARATE   '-'   '1'   '2'   '3'\n\nPIC S9(5)V9 VALUE -12345.6.\nTRAILING                '1'  '2'  '3'  '4'  '5'  'O'\nTRAILING SEPARATE  '1'  '2'  '3'  '4'  '5'  '6'  '-'\nLEADING                 'J'  '2'  '3'  '4'  '5'  '6'\nLEADING SEPARATE   '-'  '1'  '2'  '3'  '4'  '5'  '6'\n```\n\n\u003cbr\u003e\n\n```js\n// IBM COBOL (-dci) and Trailing\n\n'S9(3)V9': '12C' \u003e\u003e 12.3\n'S9(3)V9': '12L' \u003e\u003e -12.3\n\n'S9(1)V9': '12C' \u003e\u003e 2.3\n'S9(1)V9': '12L' \u003e\u003e -2.3\n\n```\n\n\u003cbr\u003e\n\n雖然支援`Leading`解析，但是會跟某些`Overpunch`查表定義衝突\n\n```js\n// IBM COBOL (-dci) and Leading\n\n'S9(3)V9': 'J23' \u003e\u003e -12.3\n\n'S9(1)V9': 'J23' \u003e\u003e `Exception: Unknown overpunch char: '2'`\n\n```\n\n```js\n// Micro Focus COBOL (-dcm) and Leading\n\n'S9(1)V9': 'J23' \u003e\u003e 2.3\n\n```\n\n\u003cbr\u003e\u003cbr\u003e\n\n# 交易所TMP連線架構\n\nPVC：Permanent Virtual Circuit（永久虛擬電路）\n\n## 實體線\n\n一條實體線上可建立多條PVC，每10條PVC為一組。\n\n|  PVC\u003cbr\u003e或\u003cbr\u003ePORT NO | 執行功能 |\n| :----: | :----: |\n| 01 | FT送 |\n| 02 | FT收 |\n| 03 | 成交回報 |\n| 04 | 委託輸入 |\n| 05 | 委託輸入 |\n| 06 | 委託輸入 |\n| 07 | 委託輸入 |\n| 08 | 委託輸入 |\n| 09 | 委託輸入 |\n\n\u003cbr\u003e\n\n## PVC作業時間表\n\n|     PVC   |  時 間  | 執 行 業 務 |\n|   :----:  | :----: | :------: |\n| (01)\u003cbr\u003eFT 送  | 07:30－19:00 | 單筆訊息及檔案傳輸\u003cbr\u003e(證券商的傳送線路) |\n| (02)\u003cbr\u003eFT 收  | 07:30－19:00 | 單筆訊息及檔案傳輸\u003cbr\u003e(證券商的傳送線路) |\n| (03)\u003cbr\u003e成交回報 | 08:30－14:40 | 成交回報接收 |\n| (04~09)\u003cbr\u003e委託輸入 | 08:30－13:30\u003cbr\u003e09:00－12:10\u003cbr\u003e09:00－13:30\u003cbr\u003e13:40－14:30\u003cbr\u003e14:00－14:30\u003cbr\u003e14:30－15:00\u003cbr\u003e15:00－16:00\u003cbr\u003e15:00－16:00 | 普通股交易\u003cbr\u003e標借交易\u003cbr\u003e盤中零股交易\u003cbr\u003e盤後零股交易\u003cbr\u003e盤後定價交易\u003cbr\u003e證金標購交易\u003cbr\u003e拍賣交易\u003cbr\u003e標購交易 |\n\n\u003cbr\u003e\n\n## 連線子系統\n\n|   時 間   |  證 券 商 與 證 交 所  |\n|  :----:  | ---- |\n| 07:30\u003cbr\u003e｜\u003cbr\u003e08:20 | 1.開機通知作業\u003cbr\u003e2.登錄作業\u003cbr\u003e\u0026emsp;準備進入連線狀態 |\n| 08:20以後 | 完成連線進入各應用子統統 |\n| 08:30\u003cbr\u003e｜\u003cbr\u003e19:00 | 1.進行各項業務\u003cbr\u003e2.若在各項業務處理中發現有任何異常狀況時\u003cbr\u003e\u0026emsp;(1)開機通知作業\u003cbr\u003e\u0026emsp;(2)登錄作業\u003cbr\u003e\u0026emsp;\u0026emsp;重新進入連線狀態 |\n| 19:00以後 | 1.完成各項業務\u003cbr\u003e2.離線作業\u003cbr\u003e\u0026emsp;雙方進入離線狀態 |\n\n### 訊息格式\n\u003e MESSAGE ID︰L010、L020、L030、L040、L050、L060、L070、L080\n\n\u003cbr\u003e\n\n## 單筆訊息與檔案傳輸通訊協定\n\n|   時 間   |  系 統  |  功 能  |\n|  :----:  | :----: | ------ |\n| 07:30\u003cbr\u003e｜\u003cbr\u003e08:00 | 連線子系統 | 建立證交所與證券商的電腦連線作業，並進入業務系統。 |\n| 08:00\u003cbr\u003e｜\u003cbr\u003e19:00 | 單筆訊息與檔案傳輸子系統\u003cbr\u003e結算作業\u003cbr\u003e申購作業\u003cbr\u003e投資人管理作業 | 提供交易報表。\u003cbr\u003e補送成交回報資料給證券商。\u003cbr\u003e申購作業。\u003cbr\u003e處理結算業務。\u003cbr\u003e處理申購業務。\u003cbr\u003e處理與投資人管理有關之業務。 |\n| 19:00 | 連線子系統 | 離線 |\n\n### 訊息格式 (傳送功能)\n\u003e MESSAGE ID︰F010、F020、F030(F210)、F040(F220)、F050、F060、F070、F080  \n\n### 訊息格式 (接收功能)\n\u003e MESSAGE ID︰F090、F100、F110(F230)、F120(F240)、F130、F140、F150、F160  \n\n### 訊息格式\n\u003e MESSAGE ID︰F170、F180、F190、F200\n\n\n\u003cbr\u003e\n\n## 委託輸入通訊協定\n\n|   時 間   |  系 統  |  功 能  |\n|  :----:  | :----: | ------ |\n| 08:00－08:30 | 連線子系統 | 建立證交所與證券商的電腦連線作業，並進入業務系統。 |\n| 08:30－13:30\u003cbr\u003e09:00－12:10\u003cbr\u003e09:00－13:30\u003cbr\u003e13:40－14:30\u003cbr\u003e14:00－14:30\u003cbr\u003e14:30－15:00\u003cbr\u003e15:00－16:00\u003cbr\u003e15:00－16:00 | 普通股交易子系統\u003cbr\u003e標借交易子系統\u003cbr\u003e盤中零股交易子系統\u003cbr\u003e盤後零股交易子系統\u003cbr\u003e盤後定價交易子系統\u003cbr\u003e證金標購交易子系統\u003cbr\u003e拍賣交易子系統\u003cbr\u003e標購交易子系統 | 證券商輸入買賣委託，並接受證交所的委託回報。 |\n| 16:00 | 連線子系統 | 結束委託輸入，回到連線子系統。\u003cbr\u003e離線 |\n\n### 訊息格式\n\u003e MESSAGE ID︰T1、T2、T3、T4、T5、T6、T7\n\n\u003cbr\u003e\n\n## 成交回報通訊協定\n\n|   時 間   |  系 統  |  功 能  |\n|  :----:  | :----: | ------ |\n| 08:00\u003cbr\u003e｜\u003cbr\u003e08:30 | 連線子系統 | 建立證交所與證券商的電腦連線作業，並進入業務系統。 |\n| 08:30\u003cbr\u003e｜\u003cbr\u003e14:00 | 成交回報接收子系統 | 接收普通股、盤中零股成交回報資料。 |\n| 14:00\u003cbr\u003e｜\u003cbr\u003e14:40 | 成交回報接收子系統 | 接收盤後定價、盤後零股成交回報資料。 |\n| 14:40 | 連線子系統 | 結束委託輸入，回到連線子系統。\u003cbr\u003e離線 |\n\n### 訊息格式\n\u003e MESSAGE ID︰R1、R2、R3、R4、R5、R6\n\n\u003cbr\u003e\u003cbr\u003e\n\n# 專案結構\n\n```console\n{project-root}\n ├─ cli \u003c-------------------- CLI工具\n │   └─ ...\n ├─ lib\n │   ├─ copybook  \u003c---------- 文件內容轉換\n │   ├─ field  \u003c------------- 欄位轉換\n │   ├─ message  \u003c----------- 訊息解析\n │   ├─ spec\n │   │   ├─ copybook  \u003c------ 各類FILE-CODE檔案內容定義\n │   │   ├─ field  \u003c--------- 欄位基本資料結構\n │   │   ├─ fields  \u003c-------- 欄位組成後定義\n │   │   │   ├─ body\n │   │   │   └─ header.js\n │   │   └─ message  \u003c------- 訊息種類定義\n │   │       ├─ otc.js\n │   │       └─ tse.js\n │   └─ meta.js\n └─ templar.js\n```\n\n![結構圖](doc/lib-layers.png)\n\n\u003cbr\u003e\u003cbr\u003e\n\n# 發布函式庫\n\n* 打包\n  ```bash\n  npm pack\n  ```\n  ```bash\n  \u003e npm pack\n  ...\n  ...\n  npm notice Tarball Details\n  npm notice name: templar\n  npm notice version: X.Y.Z\n  npm notice filename: templar-X.Y.Z.tgz\n  npm notice package size: 374.7 kB\n  npm notice unpacked size: 1.6 MB\n  npm notice shasum: ba2890224935e4a6a0ecdf91d9585c287a4c3073\n  npm notice integrity: sha512-3I975LfONtGXo[...]K22YKPtY5EZcQ==\n  npm notice total files: 61\n  npm notice\n  templar-X.Y.Z.tgz\n  ```\n\n* 安裝\n  ```bash\n  npm install ./templar-X.Y.Z.tgz\n  ```\n\n\u003cbr\u003e\u003cbr\u003e\n\n# 其他\n\n[參考資料](./doc/refs.md)  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsyueh%2Ftemplar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flsyueh%2Ftemplar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsyueh%2Ftemplar/lists"}