iT邦幫忙

1

[C#] 取得證交所台股價格的 3 種實用方法(附範例下載)

Mars 2021-08-23 09:43:2621024 瀏覽

想要在網路上取得台股最新的股價有許多種方式,其中一種免費的方式就是直接向證交所網站取得股價資料,這次我會分享 3 種用 C# 向證交所取得股價資料的實用方法。

  1. 取得即時的價格
  2. 取得每日收盤行情的 K 線資料
  3. 取得單一股票在當月各日成交資訊

這 3 種方式分別可以應用在不同的實務上,當了解基本取得資料的方式,就可以寫程式定期取得最新或是歷史的股價資料。

先看一次我這次範例的操作畫面。

Yes

範例建置環境
後端架構: C# ASP.Net MVC .Net Framework
前端架構: Vue.js, jQuery, Bootstrap

我網頁上只講解重要的程式碼部份,完整範例可至文末下載。

範例1 取得即時股價

範例畫面輸入要查詢的股票代碼,可輸入單一股票或是用 , (逗號)分隔輸入代碼後按查詢。

按下查詢後,程式會組合查詢網址,例如

https://mis.twse.com.tw/stock/api/getStockInfo.jsp?json=1&delay=0&ex_ch=tse_2330.tw|tse_0050.tw|

網址中的 tse_2330.tw|tse_0050.tw| 就是要查詢的股票名稱,大家可以換成自己需要的股票。
當此網址在瀏覽器查詢時,即會回傳 Json 格式的價格資料

{"msgArray":[{"tv":"-","ps":"-","pz":"-","a":"592.0000_593.0000_594.0000_595.0000_596.0000_","b":"591.0000_590.0000_589.0000_588.0000_587.0000_","c":"2330","d":"20210510","ch":"2330.tw","tlong":"1620614180000","f":"332_721_423_419_366_","ip":"0","g":"586_1044_261_577_263_","mt":"866821","h":"597.0000","i":"24","it":"12","l":"591.0000","n":"台積電","o":"596.0000","p":"0","ex":"tse","s":"-","t":"10:36:20","u":"658.0000","v":"8071","w":"540.0000","nf":"台灣積體電路製造股份有限公司","y":"599.0000","z":"-","ts":"0"},{"tv":"-","ps":"-","nu":"http://www.yuantaetfs.com/#/RtNav/Index","pz":"-","a":"138.7000_138.7500_138.8000_138.8500_138.9000_","b":"138.6500_138.6000_138.5500_138.5000_138.4500_","c":"0050","d":"20210510","ch":"0050.tw","tlong":"1620614179000","f":"13_5_8_120_100_","ip":"0","g":"5_97_4_25_104_","mt":"995922","h":"139.5000","it":"02","l":"138.1500","n":"元大台灣50","o":"139.5000","p":"0","ex":"tse","s":"-","t":"10:36:19","u":"153.3000","v":"2229","w":"125.5000","nf":"元大台灣卓越50證券投資信託基金","y":"139.4000","z":"-","ts":"0"}],"referer":"","userDelay":5000,"rtcode":"0000","queryTime":{"sysDate":"20210510","stockInfoItem":1201,"stockInfo":629093,"sessionStr":"UserSession","sysTime":"10:36:25","showChart":false,"sessionFromTime":-1,"sessionLatestTime":-1},"rtmessage":"OK"}

接下來就可以用程式解析 Json 來取得價格資料。
我列出 Json 內容中價格資訊常用的欄位

在此範例中我只顯示「股票代號/當盤成交價/最低委賣價/最高委買價」做示範,
大家可以依自己的需求調整要顯示的資料。

在呼叫證交所的即時價格時,如果是盤中呼叫,"偶爾" 會發生沒有當盤成交價的問題。

而這問題在逐筆交易上線(2020-3-23)以前,是不會發生的,當時是每 5 秒撮合一次,一定有成交價。

但在逐筆交易上線後,從證交所呼叫即時價格,其實是得到每 5 秒行情快照的結果,也就是 5 秒才會更新一個價格。

可能在快照那當下 0.1 秒時,就是沒有人成交,所以也就沒有看到當盤成交價。

如果沒有當盤成交價的話,只能從委買 1 或委賣 1 來推算可能的成交價了。

如果需要非常即時的逐筆交易價格,我是透過群益 API 來取得即時報價的,可參考這篇文章

Html 網頁版面

<div class="panel panel-default">
	<div class="panel-heading">範例1 即時股價</div>
	<div class="panel-body">
		<div class="form-group">
			<label class="control-label col-sm-2">股票:</label>
			<div class="col-sm-10">
				<input type="text" class="form-control" v-model="form.Q_SYMBOL_1.value">
			</div>
		</div>
		<button type="button" class="btn btn-primary" v-on:click="GetRealtimePrice()">查詢</button>

	</div>
	<div class="panel-footer">
		<span v-html="realPrice"></span>
	</div>
</div>

Javascript 程式碼

只向後端執行呼叫動作

// 即時股價
, GetRealtimePrice: function () {
	var self = this;
	var postData = self._GetPostData(self.form, "Q_");
	showProcess();
	$.DoAjax({
		url: '@Url.Content("~/Home/GetRealtimePrice")',
		data: { inModel: postData, __RequestVerificationToken: self.GetToken() },
		success: function (datas) {
			if (self._CheckReturnErrorMsg(datas)) {
				self.realPrice = datas.realPrice;
				hideProcess();
			}
		}
	});
}

C# Controller 程式

利用程式組出要查詢的網址,接著使用 WebClient 取得回傳資料,取得回傳的 Json 資料後就依欄位取出資料。

/// <summary>
/// 即時股價
/// </summary>
/// <param name="inModel"></param>
/// <returns></returns>
[ValidateAntiForgeryToken]
public ActionResult GetRealtimePrice(FormIn inModel)
{
	GetRealtimePriceOut outModel = new GetRealtimePriceOut();
	outModel.ErrorMsg = "";

	StringBuilder ExCode = new StringBuilder();
	string[] symbols = inModel.Q_SYMBOL_1.Split(',');
	foreach (string symbol in symbols)
	{
		ExCode.Append("tse_" + symbol + ".tw|");
	}

	// 呼叫網址
	string url = "https://mis.twse.com.tw/stock/api/getStockInfo.jsp";
	url += "?json=1&delay=0&ex_ch=" + ExCode;

	string downloadedData = "";
	using (WebClient wClient = new WebClient())
	{
		// 取得網頁資料
		wClient.Encoding = Encoding.UTF8;
		downloadedData = wClient.DownloadString(url);
	}
	TwsePriceSchema jsonPrice = null;
	if (downloadedData.Trim().Length > 0)
	{
		jsonPrice = JsonConvert.DeserializeObject<TwsePriceSchema>(downloadedData);
		if (jsonPrice.rtcode != "0000")
		{
			throw new Exception("取商品價格失敗: " + jsonPrice.rtmessage);
		}
	}

	StringBuilder sbRealPrice = new StringBuilder();
	for (int i = 0; i < jsonPrice.msgArray.Count; i++)
	{
		// 代碼
		string code = jsonPrice.msgArray[i].c;

		// z = 收盤價
		string close = jsonPrice.msgArray[i].z;
		// a = 最低委賣價
		string ask = "";
		if (jsonPrice.msgArray[i].a.IndexOf("_") > -1)
		{
			ask = jsonPrice.msgArray[i].a.Split('_')[0];
		}
		// b = 最高委買價
		string bid = "";
		if (jsonPrice.msgArray[i].b.IndexOf("_") > -1)
		{
			bid = jsonPrice.msgArray[i].b.Split('_')[0];
		}
		sbRealPrice.Append("代碼: " + code + " 收盤價: " + close + " 最低委賣價: " + ask + " 最高委買價: " + bid + "<br>");
	}
	outModel.realPrice = sbRealPrice.ToString();

	// 輸出json
	ContentResult result = new ContentResult();
	result.ContentType = "application/json";
	result.Content = JsonConvert.SerializeObject(outModel);
	return result;
}

程式碼上有簡單的註解說明,若需要實際操作可至下方下載完整範例。

範例2 取得每日收盤行情

在範例畫面上輸入要查詢的日期,查詢後就會列出當日所有股價的 K 線價格。

當查詢之後,程式會組合查詢網址:

https://www.twse.com.tw/exchangeReport/MI_INDEX?response=csv&date=20210507&type=ALL

替換參數 「20210507」 後就可以改成需要查詢的日期
此查詢結果會回傳 csv 格式的內容,內容包含所有上市交易的清單,以及類股指數的價格,

此檔案比較大有 2 萬多筆資料回傳。
回傳資料分 2 部份,上半部是類股價格而下半部是個股價格,會在同一份檔案中。
我自己是用欄位數量來分辨類股或是個股的。
那接下來就來看程式碼的部份

Html 網頁版面

<div class="panel panel-default">
	<div class="panel-heading">範例2 每日收盤行情</div>
	<div class="panel-body">
		<div class="form-group">
			<label class="control-label col-sm-2">日期:</label>
			<div class="col-sm-10">
				<input type="text" class="form-control" v-model="form.Q_DATE_2.value">
			</div>
		</div>
		<button type="button" class="btn btn-primary" v-on:click="GetDayPrice()">查詢</button>
	</div>
	<div class="panel-footer">
		<div class="table-responsive" style="overflow-y:auto;height:400px;">
			<table class="table">
				<tr>
					<th>代碼</th>
					<th>名稱</th>
					<th>開盤價</th>
					<th>最高價</th>
					<th>最低價</th>
					<th>收盤價</th>
					<th>成交量</th>
				</tr>
				<tr v-for="(item, index) in gridDay.datas">
					<td>
						{{item.symbolCode}}
					</td>
					<td>
						{{item.symbolName}}
					</td>
					<td>
						{{item.open}}
					</td>
					<td>{{item.high}}</td>
					<td>{{item.low}}</td>
					<td>{{item.close}}</td>
					<td>{{item.volume}}</td>
				</tr>
			</table>
		</div>
	</div>
</div>

Javascript 程式碼

只向後端執行呼叫動作

// 每日收盤行情
, GetDayPrice: function () {
	var self = this;
	var postData = self._GetPostData(self.form, "Q_");
	showProcess();
	$.DoAjax({
		url: '@Url.Content("~/Home/GetDayPrice")',
		data: { inModel: postData, __RequestVerificationToken: self.GetToken() },
		success: function (datas) {
			if (self._CheckReturnErrorMsg(datas)) {
				self.gridDay.datas = [];
				self._RowDataBound(self.gridDay.datas, datas.gridList);
				hideProcess();
			}
		}
	});
}

C# Controller 程式

利用程式組出要查詢的網址,接著使用 WebClient 取得回傳資料,取得資料是 CSV 格式,將 CSV 解析成string[] 格式資料,就可以依索引位置來取得指定的資料。

/// <summary>
/// 每日收盤行情
/// </summary>
/// <param name="inModel"></param>
/// <returns></returns>
[ValidateAntiForgeryToken]
public ActionResult GetDayPrice(FormIn inModel)
{
	GetDayPriceOut outModel = new GetDayPriceOut();
	outModel.ErrorMsg = "";

	// 呼叫網址
	string twseUrl = "https://www.twse.com.tw/exchangeReport/MI_INDEX";
	string download_url = twseUrl + "?response=csv&date=" + inModel.Q_DATE_2 + "&type=ALL";
	string downloadedData = "";
	using (WebClient wClient = new WebClient())
	{
		// 網頁回傳
		downloadedData = wClient.DownloadString(download_url);
	}
	if (downloadedData.Trim().Length > 0)
	{
		// 回傳前端的資料集
		outModel.gridList = new List<StockPriceRow>();
		string[] lineStrs = downloadedData.Split('\n');
		for (int i = 0; i < lineStrs.Length; i++)
		{
			string strline = lineStrs[i];
			if (strline.Trim().Length == 0)
			{
				continue;
			}

			// 排除非價格部份
			if (strline.IndexOf("證券代號") > -1 || strline.IndexOf("(元,股)") > -1)
			{
				continue;
			}
			if (strline.Substring(0, 1) == "=")
			{
				strline = strline.TrimStart('=');
			}

			ArrayList resultLine = new ArrayList();
			// 解析資料
			this.ParseCSVData(resultLine, strline);
			string[] datas = (string[])resultLine.ToArray(typeof(string));

			//檢查資料內容
			if (datas.Length != 17)
			{
				continue;
			}

			// 股票代碼
			string symbolCode = datas[0];

			if (symbolCode.Length == 4)
			{
				// 輸出資料
				StockPriceRow row = new StockPriceRow();
				row.symbolCode = symbolCode; //股票代碼 
				row.symbolName = datas[1]; //股票名稱
				row.open = datas[5]; //開盤價
				row.high = datas[6]; //最高價
				row.low = datas[7]; //最低價
				row.close = datas[8]; //收盤價
				row.volume = datas[2]; //成交量
				outModel.gridList.Add(row);
			}

		}
	}

	// 輸出json
	ContentResult result = new ContentResult();
	result.ContentType = "application/json";
	result.Content = JsonConvert.SerializeObject(outModel); ;
	return result;
}

範例3 取得當月各日成交資訊

在範例畫面上輸入股票代碼及查詢日期,就可以查詢該股票當月的所有日期價格。

當查詢之後,程式會組合查詢網址:

http://www.twse.com.tw/exchangeReport/STOCK_DAY?response=csv&date=20210510&stockNo=2330

替換參數 「20210510」及「2330」 後就可以改變回傳內容。
此查詢結果會回傳 csv 格式的內容,內容包含股票當月各日成交資訊。

資料欄位包含日期/成交股數/成交金額/開盤價/最高價/最低價/收盤價/漲跌價差/成交筆數

接下來我就示範如何用程式取出欄位

Html 網頁版面

<div class="panel panel-default">
	<div class="panel-heading">範例3 當月各日成交資訊</div>
	<div class="panel-body">
		<div class="form-group">
			<label class="control-label col-sm-2">股票:</label>
			<div class="col-sm-10">
				<input type="text" class="form-control" v-model="form.Q_SYMBOL_3.value">
			</div>
		</div>
		<div class="form-group">
			<label class="control-label col-sm-2">日期:</label>
			<div class="col-sm-10">
				<input type="text" class="form-control" v-model="form.Q_MONTH_3.value">
			</div>
		</div>
		<button type="button" class="btn btn-primary" v-on:click="GetMonthPrice()">查詢</button>
	</div>
	<div class="panel-footer">
		<div class="table-responsive">
			<table class="table">
				<tr>
					<th>日期</th>
					<th>開盤價</th>
					<th>最高價</th>
					<th>最低價</th>
					<th>收盤價</th>
					<th>成交量</th>
				</tr>
				<tr v-for="(item, index) in gridMonthPirce.datas">
					<td>{{item.date}}</td>
					<td>{{item.open}}</td>
					<td>{{item.high}}</td>
					<td>{{item.low}}</td>
					<td>{{item.close}}</td>
					<td>{{item.volume}}</td>
				</tr>
			</table>
		</div>
	</div>
</div>

Javascript 程式碼

只向後端執行呼叫動作

// 當月各日成交資訊
, GetMonthPrice: function () {
	var self = this;
	var postData = self._GetPostData(self.form, "Q_");
	showProcess();
	$.DoAjax({
		url: '@Url.Content("~/Home/GetMonthPrice")',
		data: { inModel: postData, __RequestVerificationToken: self.GetToken() },
		success: function (datas) {
			if (self._CheckReturnErrorMsg(datas)) {
				self.gridMonthPirce.datas = [];
				self._RowDataBound(self.gridMonthPirce.datas, datas.gridList);
				hideProcess();
			}
		}
	});
}

C# Controller 程式

利用程式組出要查詢的網址,接著使用 WebClient 取得回傳資料,取得資料是 CSV 格式,將 CSV 解析成 string[] 格式資料,就可以依索引位置來取得指定的資料。

/// <summary>
/// 當月各日成交資訊
/// </summary>
/// <param name="inModel"></param>
/// <returns></returns>
[ValidateAntiForgeryToken]
public ActionResult GetMonthPrice(FormIn inModel)
{
	GetMonthPriceOut outModel = new GetMonthPriceOut();
	outModel.ErrorMsg = "";

	// 呼叫網址
	string download_url = "http://www.twse.com.tw/exchangeReport/STOCK_DAY?response=csv&date=" + inModel.Q_MONTH_3 + "&stockNo=" + inModel.Q_SYMBOL_3;
	string downloadedData = "";
	using (WebClient wClient = new WebClient())
	{
		// 網頁回傳
		downloadedData = wClient.DownloadString(download_url);
	}
	if (downloadedData.Trim().Length > 0)
	{
		outModel.gridList = new List<StockPriceRow>();
		string[] lineStrs = downloadedData.Split('\n');
		for (int i = 0; i < lineStrs.Length; i++)
		{
			string strline = lineStrs[i];
			if (i == 0 || i == 1 || strline.Trim().Length == 0)
			{
				continue;
			}
			// 排除非價格部份
			if (strline.IndexOf("說明") > -1 || strline.IndexOf("符號") > -1 || strline.IndexOf("統計") > -1 || strline.IndexOf("ETF") > -1)
			{
				continue;
			}

			ArrayList resultLine = new ArrayList();
			// 解析資料
			this.ParseCSVData(resultLine, strline);
			string[] datas = (string[])resultLine.ToArray(typeof(string));

			//檢查資料內容
			if (Convert.ToInt32(datas[1].Replace(",", "")) == 0 || datas[3] == "--" || datas[4] == "--" || datas[5] == "--" || datas[6] == "--")
			{
				continue;
			}

			// 輸出資料
			StockPriceRow row = new StockPriceRow();
			row.date = datas[0]; //日期
			row.open = datas[3]; //開盤價
			row.high = datas[4]; //最高價
			row.low = datas[5]; //最低價
			row.close = datas[6]; //收盤價
			row.volume = datas[1]; //成交量
			outModel.gridList.Add(row);

		}
	}
	// 輸出json
	ContentResult resultJson = new ContentResult();
	resultJson.ContentType = "application/json";
	resultJson.Content = JsonConvert.SerializeObject(outModel); ;
	return resultJson;
}

以上就是 3 種向證交所取得股價的實用方式,網頁上的程式碼中有些方法寫在共用方法區內,沒有展示出來,若需要了解更多可以下載完整範例。

重點整理

  1. 取得證交所即時價格是每 5 秒更新一次
  2. 要取得逐筆交易的 Tick 價格可串接券商 API 取得證交所即時價格是每
  3. 知道證交所查詢網址即可自寫程式查詢
  4. 一天更新一次價格可利用查詢證交所每日收盤行情
  5. 當月各日成交資訊可回溯非常久的歷史資料

範例下載

付費後可下載此篇文章教學程式碼

相關學習文章
[C#] 取得證交所上市及上櫃的股票及ETF清單(附範例)
[C#] 取得公開資訊觀測站股票基本資料(上市、上櫃、興櫃、公開發行) (附範例下載)
【C# 群益 API 開發教學】取得商品報價、Tick、最佳 5 檔教學 (附範例下載)

相關網站
WinTog 雲投資


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
海綿寶寶
iT邦大神 1 級 ‧ 2021-08-23 10:04:42

如果都打算要寫了
建議你報名參加鐵人賽
可以報名永豐金API組
人比較少
比較有機會拿到大奬
/images/emoticon/emoticon08.gif

Mars iT邦新手 4 級 ‧ 2021-08-23 12:42:53 檢舉

可是我還沒用過永豐金的API,要馬上挑戰這 30 天 PO,有一點難度...

我相信以你群益API的經驗
一定沒問題的
(兩者都有提供 python 使用介面)

我要留言

立即登入留言