【C# 群益 API 开发教学】取得商品报价、Tick、最佳 5 档教学 #CH3 (附范例)

群益 API 是利用自己开发的程序,结合群益 API 在群益券商下单的一种方式,通常是做程序交易下单,或是单纯读取报价也行。

此范例教学是我看群益 API 的范例,再重新做出我要的程序交易功能。

这个章节是利用 API 取得商品报价的范例,报价回传即时成交资讯、Tick 以及最佳 5 档回传。

如果需要实际操作才好学习的话,在最後我会提供原始码范例下载连结,可以在自己电脑执行看看。

我在写程序码时尽量把注解说明清楚一点,这样可以帮助想学习的人看懂一些,完整的功能还是要看官方的文件和范例喔。

在往下看之前,我还是先提醒一下,我设计的介面是为了教学而简单设计的,给大家学习参考而已,完整的功能还是要看官方的文件和范例喔。

前置作业

群益 API 官网范例下载位置: 群益 API

此篇文章的内容是接续上一篇文章继续说明,在上一篇里面,我们完成了新建专案、注册元件以及群益帐号密码登入测试。

在登入成功之後,可以储存帐号密码,在接下来取得报价之前,就会读取帐号密码自动登入。

设计介面

打开 VS 之後,可以照我的画面,拉出这个设计检视。

上面红色字是控制项与 ID,好方便以下程序码对应。

主要功能是用下拉来指定商品,再拉出 3 个 DataGridView ,分别放最新报价、Tick 以及最佳 5 档。
最後在上面放上按钮来启动报价。
下方的文字区就显示过程讯息。

程序码说明

阅读前提醒一下,我只会将重点语法拿出来讲解喔,想要看完整程序码及操作的话,可以到文末下载范例来看。

初始化动作

在群益 API 呼叫之前,都需要先初始化物件。对於报价订阅後,还需要宣告接受事件。
然後在初始化动作里面,我特别增加了一些交易商品。

// 初始化物件
m_pSKCenter = new SKCenterLib();
m_pSKReply = new SKReplyLib();
m_SKQuoteLib = new SKQuoteLib();

// 注册公告事件
m_pSKReply.OnReplyMessage += new _ISKReplyLibEvents_OnReplyMessageEventHandler(this.m_pSKReply_OnAnnouncement);

// 国内报价连线状态事件
m_SKQuoteLib.OnConnection += new _ISKQuoteLibEvents_OnConnectionEventHandler(m_SKQuoteLib_OnConnection);

// 国内报价事件
m_SKQuoteLib.OnNotifyQuoteLONG += new _ISKQuoteLibEvents_OnNotifyQuoteLONGEventHandler(m_SKQuoteLib_OnNotifyQuoteLONG);

// 国内 Tick 回传事件
m_SKQuoteLib.OnNotifyTicksLONG += new _ISKQuoteLibEvents_OnNotifyTicksLONGEventHandler(m_SKQuoteLib_OnNotifyTicks);

// 国内 Best5 回传事件
m_SKQuoteLib.OnNotifyBest5LONG += new _ISKQuoteLibEvents_OnNotifyBest5LONGEventHandler(m_SKQuoteLib_OnNotifyBest5);

// 加入商品
cboCommID.Items.Add(new ComboboxItem("TX00", "大台指期近月"));
cboCommID.Items.Add(new ComboboxItem("MTX00", "小台指期近月"));
cboCommID.Items.Add(new ComboboxItem("TE00", "电子期近月"));
cboCommID.Items.Add(new ComboboxItem("TF00", "金融期近月"));
cboCommID.SelectedIndex = 0;

群益 API 国内报价连线

要取得报价之前,需要先连线,等待连线成功的回应之後。才能继续下一步。

// 国内报价连线
nCode = m_SKQuoteLib.SKQuoteLib_EnterMonitorLONG();
txtMessage.AppendText(GetMessage("国内报价连线", nCode) + "\n");
if (nCode != 0)
{
	return;
}

等待报价连线回应事件

/// <summary>
/// 国内报价连线回应事件
/// </summary>
/// <param name="nKind"></param>
/// <param name="nCode"></param>
void m_SKQuoteLib_OnConnection(int nKind, int nCode)
{
	if (nKind == 3001)
	{
		if (nCode == 0)
		{
			// 连线中
			lblTwSignal.ForeColor = Color.Blue;
			lblTwSignal.Text = "连线状态:连线中";
		}
	}
	else if (nKind == 3002)
	{
		// 连线中断
		lblTwSignal.ForeColor = Color.Red;
		lblTwSignal.Text = "连线状态:中断";

		btnStartQuote.Enabled = true;
	}
	else if (nKind == 3003)
	{
		// 连线成功
		lblTwSignal.ForeColor = Color.Green;
		lblTwSignal.Text = "连线状态:正常";

		// 订阅最新报价
		RequestQuote();

		// 订阅 Tick & Best5
		RequestTickBest5();

		btnStartQuote.Enabled = false;
	}
	else if (nKind == 3021)
	{
		//网路断线
		lblTwSignal.ForeColor = Color.DarkRed;
	}
}

在连线回应里面当状态为连线成功的时候,我们就直接呼叫订阅报价、Tick 与最佳 5 档。

群益 API 订阅最新报价

取得最新报价是要回传开盘、最高、最低、成交价、成交量等相关资料,并且当有最新价格时,就要触发回应事件。

程序码分为取得商品相关资讯以及订阅报价回传事件。

/// <summary>
/// 订阅最新报价
/// </summary>
private void RequestQuote()
{
	// 取回商品报价的相关资讯
	SKSTOCKLONG pSKStockLONG = new SKSTOCKLONG();
	nCode = m_SKQuoteLib.SKQuoteLib_GetStockByNoLONG(ComboUtil.GetItem(cboCommID).Value, ref pSKStockLONG);
	txtMessage.AppendText(GetMessage("取回商品报价的相关资讯", nCode) + "\n");

	// 将报价资讯物件输出在 DataGridView
	onUpdateQuote(pSKStockLONG);

	if (nCode != 0)
	{
		// 发生错误
		return;
	}

	// 更新价格小数位
	dDigitNum = (Math.Pow(10, pSKStockLONG.sDecimal));

	//订阅商品即时报价,订阅後等待 OnNotifyQuoteLONG 事件回报
	nCode = m_SKQuoteLib.SKQuoteLib_RequestStocks(ref sPage, ComboUtil.GetItem(cboCommID).Value);
	txtMessage.AppendText(GetMessage("订阅商品即时报价", nCode) + "\n");
}

当有新的报价时,就会触发以下程序码:

/// <summary>
/// 国内报价回应事件
/// </summary>
/// <param name="sMarketNo"></param>
/// <param name="nStockIdx"></param>
void m_SKQuoteLib_OnNotifyQuoteLONG(short sMarketNo, int nStockIdx)
{
	// 报价资讯物件
	SKSTOCKLONG pSKStockLONG = new SKSTOCKLONG();

	// 取得最新报价写入报价资讯物件
	m_SKQuoteLib.SKQuoteLib_GetStockByIndexLONG(sMarketNo, nStockIdx, ref pSKStockLONG);

	// 将报价资讯物件输出在 DataGridView
	onUpdateQuote(pSKStockLONG);
}

群益 API 订阅 Tick

Tick 是每次成交的价格记录,回传的资讯就是基本的时间、成交价、数量、委买卖等资讯。

要订阅 Tick 以及最佳 5 档是同一个呼叫函式,只要呼叫这函式後,就可以接收到 2 种回应事件。

/// <summary>
/// 订阅 Tick & Best5
/// </summary>
private void RequestTickBest5()
{
	//订阅 Tick & Best5,订阅後等待 OnNotifyTicks 及 OnNotifyBest5 事件回报
	nCode = m_SKQuoteLib.SKQuoteLib_RequestTicks(ref sPage, ComboUtil.GetItem(cboCommID).Value);
	txtMessage.AppendText(GetMessage("订阅 Tick & Best5", nCode) + "\n");
}

建立 Tick 回应事件等待触发。

/// <summary>
/// 国内 Tick 回传事件
/// </summary>
void m_SKQuoteLib_OnNotifyTicks(short sMarketNo, int nStockIdx, int nPtr, int nDate, int lTimehms, int lTimemillismicros, int nBid, int nAsk, int nClose, int nQty, int nSimulate)
{
	DataRow dr = null;

	// 转化时间格式为 yyyy/MM/dd HH:mm:ss.sss
	string date = (nDate.ToString().Substring(0, 4) + "/" + nDate.ToString().Substring(4, 2) + "/" + nDate.ToString().Substring(6));
	string time = lTimehms.ToString("000000").Substring(0, 2) + ":" + lTimehms.ToString("000000").Substring(2, 2) + ":" + lTimehms.ToString("000000").Substring(4) + "." + lTimemillismicros.ToString("000000").Substring(0, 3);

	if (dtTick == null)
	{
		// 报价物件写入 Datatable
		dtTick = new DataTable();
		dtTick.Columns.Add("TickName");
		dtTick.Columns.Add("TickValue");
		dr = dtTick.NewRow();
		dr["TickName"] = "日期";
		dr["TickValue"] = date;
		dtTick.Rows.Add(dr);

		dr = dtTick.NewRow();
		dr["TickName"] = "时间";
		dr["TickValue"] = time;
		dtTick.Rows.Add(dr);

		dr = dtTick.NewRow();
		dr["TickName"] = "委买价";
		dr["TickValue"] = nBid / dDigitNum;
		dtTick.Rows.Add(dr);

		dr = dtTick.NewRow();
		dr["TickName"] = "委卖价";
		dr["TickValue"] = nAsk / dDigitNum;
		dtTick.Rows.Add(dr);

		dr = dtTick.NewRow();
		dr["TickName"] = "成交价";
		dr["TickValue"] = nClose / dDigitNum;
		dtTick.Rows.Add(dr);

		dr = dtTick.NewRow();
		dr["TickName"] = "数量";
		dr["TickValue"] = nQty;
		dtTick.Rows.Add(dr);

		//输出 GridView
		gvTick.DataSource = dtTick;
	}
	else
	{
		// 报价物件更新 Datatable
		dr = dtTick.Select("TickName='日期'")[0];
		dr["TickValue"] = date;

		dr = dtTick.Select("TickName='时间'")[0];
		dr["TickValue"] = time;

		dr = dtTick.Select("TickName='委买价'")[0];
		dr["TickValue"] = nBid / dDigitNum;

		dr = dtTick.Select("TickName='委卖价'")[0];
		dr["TickValue"] = nAsk / dDigitNum;

		dr = dtTick.Select("TickName='成交价'")[0];
		dr["TickValue"] = nClose / dDigitNum;

		dr = dtTick.Select("TickName='数量'")[0];
		dr["TickValue"] = nQty;
	}
}

我特地把它给的时间转成习惯的格式,这种格式比较好阅读,同时也是可以直接写入资料库的格式。
当接收到最新 Tick 时,就同时显示在 DataGridView 上面。

群益 API 订阅最佳 5 档

最佳 5 档是由最高委买价格前 5 名,以及最低委卖价格前 5 名,合并在一起的资讯。
我将这些资讯合并在同一个排名上显示,这也是大多数券商看盘软件常见的方式。

要订阅最佳 5 档跟刚刚的 Tick 是同一个呼叫函式,只要呼叫这函式後,就可以接收到 2 种回应事件。

/// <summary>
/// 订阅 Tick & Best5
/// </summary>
private void RequestTickBest5()
{
	//订阅 Tick & Best5,订阅後等待 OnNotifyTicks 及 OnNotifyBest5 事件回报
	nCode = m_SKQuoteLib.SKQuoteLib_RequestTicks(ref sPage, ComboUtil.GetItem(cboCommID).Value);
	txtMessage.AppendText(GetMessage("订阅 Tick & Best5", nCode) + "\n");
}

建立最佳 5 档回应事件等待触发。

/// <summary>
/// 国内 Best5 回传事件
/// </summary>
void m_SKQuoteLib_OnNotifyBest5(short sMarketNo, int nStockIdx, int nBestBid1, int nBestBidQty1, int nBestBid2, int nBestBidQty2, int nBestBid3, int nBestBidQty3, int nBestBid4, int nBestBidQty4, int nBestBid5, int nBestBidQty5, int nExtendBid, int nExtendBidQty, int nBestAsk1, int nBestAskQty1, int nBestAsk2, int nBestAskQty2, int nBestAsk3, int nBestAskQty3, int nBestAsk4, int nBestAskQty4, int nBestAsk5, int nBestAskQty5, int nExtendAsk, int nExtendAskQty, int nSimulate)
{
	DataRow dr = null;

	if (dtBest5 == null)
	{
		// 报价物件写入 Datatable
		dtBest5 = new DataTable();
		dtBest5.Columns.Add("Seq");
		dtBest5.Columns.Add("Best5BidQty");
		dtBest5.Columns.Add("Best5BidPrice");
		dtBest5.Columns.Add("Best5AskPrice");
		dtBest5.Columns.Add("Best5AskQty");

		// 价格除以 dDigitNum,是因为来的资料里面会多 2 个 0,而预设 dDigitNum 是 100,所以要除掉 2 个 0

		dr = dtBest5.NewRow();
		dr["Seq"] = "1";
		dr["Best5BidQty"] = nBestBidQty1;
		dr["Best5BidPrice"] = nBestBid1 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty1;
		dr["Best5AskPrice"] = nBestAsk1 / dDigitNum;
		dtBest5.Rows.Add(dr);

		dr = dtBest5.NewRow();
		dr["Seq"] = "2";
		dr["Best5BidQty"] = nBestBidQty2;
		dr["Best5BidPrice"] = nBestBid2 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty2;
		dr["Best5AskPrice"] = nBestAsk2 / dDigitNum;
		dtBest5.Rows.Add(dr);

		dr = dtBest5.NewRow();
		dr["Seq"] = "3";
		dr["Best5BidQty"] = nBestBidQty3;
		dr["Best5BidPrice"] = nBestBid3 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty3;
		dr["Best5AskPrice"] = nBestAsk3 / dDigitNum;
		dtBest5.Rows.Add(dr);

		dr = dtBest5.NewRow();
		dr["Seq"] = "4";
		dr["Best5BidQty"] = nBestBidQty4;
		dr["Best5BidPrice"] = nBestBid4 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty4;
		dr["Best5AskPrice"] = nBestAsk4 / dDigitNum;
		dtBest5.Rows.Add(dr);

		dr = dtBest5.NewRow();
		dr["Seq"] = "5";
		dr["Best5BidQty"] = nBestBidQty5;
		dr["Best5BidPrice"] = nBestBid5 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty5;
		dr["Best5AskPrice"] = nBestAsk5 / dDigitNum;
		dtBest5.Rows.Add(dr);

		//输出 GridView
		gvBest5Merge.DataSource = dtBest5;
	}
	else
	{
		// 报价物件更新 Datatable
		dr = dtBest5.Select("Seq='1'")[0];
		dr["Best5BidQty"] = nBestBidQty1;
		dr["Best5BidPrice"] = nBestBid1 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty1;
		dr["Best5AskPrice"] = nBestAsk1 / dDigitNum;

		dr = dtBest5.Select("Seq='2'")[0];
		dr["Best5BidQty"] = nBestBidQty2;
		dr["Best5BidPrice"] = nBestBid2 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty2;
		dr["Best5AskPrice"] = nBestAsk2 / dDigitNum;

		dr = dtBest5.Select("Seq='3'")[0];
		dr["Best5BidQty"] = nBestBidQty3;
		dr["Best5BidPrice"] = nBestBid3 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty3;
		dr["Best5AskPrice"] = nBestAsk3 / dDigitNum;

		dr = dtBest5.Select("Seq='4'")[0];
		dr["Best5BidQty"] = nBestBidQty4;
		dr["Best5BidPrice"] = nBestBid4 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty4;
		dr["Best5AskPrice"] = nBestAsk4 / dDigitNum;

		dr = dtBest5.Select("Seq='5'")[0];
		dr["Best5BidQty"] = nBestBidQty5;
		dr["Best5BidPrice"] = nBestBid5 / dDigitNum;
		dr["Best5AskQty"] = nBestAskQty5;
		dr["Best5AskPrice"] = nBestAsk5 / dDigitNum;
	}
}

执行画面

以上重点讲解了程序码,接下来就看一下执行後的画面。

在执行「开始报价」之後,就会呼叫报价函式,等待连线成功之後,就会开始即时更新最新价格。

重点整理

  1. 设计报价画面,包函价格、Tick 以及最佳 5 档。
  2. 注册报价回传事件。
  3. 呼叫报价订阅函式。
  4. 即时接收报价并显示在画面上。

范例下载

付费後可下载此篇文章教学程序码

相关学习文章

【C# 群益 API 开发教学】期货演算法交易讯号检查,比较商品价格差异 #CH4 (附范例)
【C# 群益 API 开发教学】帐号登入、取得下单帐号教学 #CH2 (附范例)
[C#] 取得证交所台股价格的 3 种实用方法(附范例)


<<:  GPU程序设计(5) -- Python

>>:  1.SQL简单介绍

.NET Core第4天_middleware是舍麽?

中介软件为组成应用程序管线的软件,用以处理要求与回应, .net core中定义的中介则可以说是用来...

【Day 17】- 手动更新汇率太麻烦了! 汇率爬虫搭配 OpenPyXL 做到自动读取&更新汇率!

前情提要 前一篇介绍了 openpyxl 这项可以操作 excel 的工具。 开始之前 本篇实战 【...

Day 21 LeetCode 198. House Robber

当想不到今天要做什麽时就来解 LeetCode。 You are a professional ro...

SEO排名陷入撞墙期?也许你该试试「语义搜索引擎优化」!

作者:Welly SEO 编辑部 想做好Google SEO排名,除了熟知SEO是什麽之外,还必须充...