群益api 版本: 2.13.6
編譯環境: visual studio community 2017
架構上面盡量與 python 相同, 多了一個 Job::Quote_Connect 使用連線回應用
這範例程式記憶體什麼的使用上也沒有很注意,
只是拿出來說有一個 c++ 的範例可以串接使用
使用需修改
1. #import 需要修改目錄位置
2. 由於使用 x64 的 dll, 所以程式也要編 x64 版本
3. 修正帳號/密碼
編譯環境: visual studio community 2017
架構上面盡量與 python 相同, 多了一個 Job::Quote_Connect 使用連線回應用
這範例程式記憶體什麼的使用上也沒有很注意,
只是拿出來說有一個 c++ 的範例可以串接使用
使用需修改
1. #import 需要修改目錄位置
2. 由於使用 x64 的 dll, 所以程式也要編 x64 版本
3. 修正帳號/密碼
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdafx.h" | |
#include <iostream> | |
#include <atlbase.h> | |
#include <atlcom.h> | |
#include <atlcomcli.h> | |
#include <map> | |
#include <queue> | |
#include <mutex> | |
//#import "SKCOM.tlb" | |
#import "D:\\dll\\x64\\SKCOM.dll" | |
using namespace SKCOMLib; | |
template <class T> | |
class SafeQueue | |
{ | |
public: | |
SafeQueue(void) | |
: q() | |
, m() | |
, c() | |
{} | |
~SafeQueue(void) | |
{} | |
// Add an element to the queue. | |
void enqueue(T t) | |
{ | |
std::lock_guard<std::mutex> lock(m); | |
q.push(t); | |
c.notify_one(); | |
} | |
// Get the "front"-element. | |
// If the queue is empty, wait till a element is avaiable. | |
T dequeue(void) | |
{ | |
std::unique_lock<std::mutex> lock(m); | |
while (q.empty()) | |
{ | |
// release lock as long as the wait and reaquire it afterwards. | |
c.wait(lock); | |
} | |
T val = q.front(); | |
q.pop(); | |
return val; | |
} | |
bool empty() { return q.empty (); } | |
private: | |
std::queue<T> q; | |
mutable std::mutex m; | |
std::condition_variable c; | |
}; | |
class Job | |
{ | |
public: | |
enum { | |
Quote_Connect = 1, | |
Quote_EnterMonitor = 2, | |
GetStockByIndex = 3, | |
GetStockByIndexResult = 4, | |
OnNotifyQuote = 5, | |
}; | |
int do_type; | |
int nValue1; | |
int nValue2; | |
SKSTOCK* Stock; | |
Job(int value) | |
: do_type (value) | |
, nValue1 (0) | |
, nValue2 (0) | |
, Stock (NULL) | |
{ | |
} | |
}; | |
SafeQueue<Job> q; | |
class StockBot; | |
class SKCenterLibEventWrapper : public IDispEventSimpleImpl<1, SKCenterLibEventWrapper, &__uuidof (_ISKCenterLibEvents) > | |
{ | |
public: | |
// now you need to declare a sink map - a map of methods handling the events | |
BEGIN_SINK_MAP(SKCenterLibEventWrapper) | |
SINK_ENTRY_INFO(1, __uuidof (_ISKCenterLibEvents), 0x1, OnTimer, &CallBackOnTimer) | |
END_SINK_MAP() | |
static _ATL_FUNC_INFO CallBackOnTimer; | |
StockBot* Bot; | |
SKCenterLibEventWrapper(StockBot* bot) | |
: IDispEventSimpleImpl<1, SKCenterLibEventWrapper, &__uuidof (_ISKCenterLibEvents) >() | |
, Bot (bot) | |
{ | |
} | |
STDMETHOD(OnTimer)(int timer) | |
{ | |
printf("%d\n", timer); | |
return 0; | |
} | |
}; | |
_ATL_FUNC_INFO SKCenterLibEventWrapper::CallBackOnTimer = { CC_STDCALL, VT_EMPTY, 1, {VT_I4} }; | |
class SKQuoteLibEventWrapper : public IDispEventSimpleImpl<1, SKQuoteLibEventWrapper, &__uuidof (_ISKQuoteLibEvents) > | |
{ | |
public: | |
// now you need to declare a sink map - a map of methods handling the events | |
BEGIN_SINK_MAP(SKQuoteLibEventWrapper) | |
SINK_ENTRY_INFO(1, __uuidof (_ISKQuoteLibEvents), 0x1, OnConnect, &CallBackOnConnect) | |
SINK_ENTRY_INFO(1, __uuidof (_ISKQuoteLibEvents), 0x2, OnNotifyQuote, &CallBackOnNotifyQuote) | |
END_SINK_MAP() | |
static _ATL_FUNC_INFO CallBackOnConnect; | |
static _ATL_FUNC_INFO CallBackOnNotifyQuote; | |
StockBot* Bot; | |
SKQuoteLibEventWrapper(StockBot* bot) | |
:IDispEventSimpleImpl<1, SKQuoteLibEventWrapper, &__uuidof (_ISKQuoteLibEvents) > () | |
, Bot(bot) | |
{ | |
} | |
STDMETHOD(OnConnect)(int nKind, int nCode) | |
{ | |
Job j(Job::Quote_Connect); | |
j.nValue1 = nKind; | |
j.nValue2 = nCode; | |
q.enqueue(j); | |
return 0; | |
} | |
STDMETHOD(OnNotifyQuote)(int sMarketNo, int sStockIdx) | |
{ | |
Job j(Job::GetStockByIndexResult); | |
j.nValue1 = sMarketNo; | |
j.nValue2 = sStockIdx; | |
q.enqueue(j); | |
return 0; | |
} | |
}; | |
_ATL_FUNC_INFO SKQuoteLibEventWrapper::CallBackOnConnect = { CC_STDCALL, VT_EMPTY, 2, {VT_I4, VT_I4} }; | |
_ATL_FUNC_INFO SKQuoteLibEventWrapper::CallBackOnNotifyQuote = { CC_STDCALL, VT_EMPTY, 2, {VT_I4, VT_I4} }; | |
class SKCOMWapper | |
{ | |
public: | |
ISKCenterLibPtr SKCenterLibPtr; | |
ISKQuoteLibPtr SKQuoteLibPtr; | |
SKCenterLibEventWrapper* SKCenterLibEventsWp; | |
SKQuoteLibEventWrapper* SKQuoteLibEventsWp; | |
StockBot* Bot; | |
SKCOMWapper(StockBot* bot): | |
Bot (bot) | |
{ | |
SKCenterLibPtr.CreateInstance(__uuidof(SKCenterLib)); | |
SKCenterLibEventsWp = new SKCenterLibEventWrapper(bot); | |
SKCenterLibEventsWp->DispEventAdvise(SKCenterLibPtr); | |
SKQuoteLibPtr.CreateInstance(__uuidof(SKQuoteLib)); | |
SKQuoteLibEventsWp = new SKQuoteLibEventWrapper(bot); | |
SKQuoteLibEventsWp->DispEventAdvise(SKQuoteLibPtr); | |
} | |
~SKCOMWapper() | |
{ | |
SKCenterLibEventsWp->DispEventUnadvise(SKCenterLibPtr); | |
delete SKCenterLibEventsWp; | |
SKCenterLibPtr->Release(); | |
SKCenterLibPtr = NULL; | |
SKQuoteLibEventsWp->DispEventUnadvise(SKQuoteLibPtr); | |
delete SKQuoteLibEventsWp; | |
SKQuoteLibPtr->Release(); | |
SKQuoteLibPtr = NULL; | |
} | |
}; | |
class StockBot | |
{ | |
public: | |
SKCOMWapper* SKCOM; | |
std::string StockNo; | |
std::map <BSTR, SKSTOCK*> Stock; | |
StockBot(std::string stockno) | |
{ | |
StockNo = stockno; | |
SKCOM = new SKCOMWapper(this); | |
} | |
void DoLogin(const char* account, const char* pwd) | |
{ | |
BSTR str_account = _bstr_t(account).Detach(); | |
BSTR str_pwd = _bstr_t(pwd).Detach(); | |
HRESULT ret = SKCOM->SKCenterLibPtr->SKCenterLib_Login(str_account, str_pwd); | |
::SysFreeString(str_account); | |
::SysFreeString(str_pwd); | |
Job j((int)Job::Quote_EnterMonitor); | |
q.enqueue(j); | |
} | |
void SKQuoteLibOnConnection(int nKind, int nCode) | |
{ | |
if (nKind == 3003 && nCode == 0) | |
{ | |
q.enqueue(Job(Job::GetStockByIndex)); | |
} | |
printf("OnConnection Kind:%d Code:%d\n", nKind, nCode); | |
} | |
void DoOnNotifyQuote(int sMarketNo, int sIndex) | |
{ | |
SKSTOCK* s = new SKSTOCK(); | |
SKCOM->SKQuoteLibPtr->SKQuoteLib_GetStockByIndex(sMarketNo, sIndex, s); | |
Job j(Job::OnNotifyQuote); | |
j.Stock = s; | |
q.enqueue(j); | |
} | |
void DoGetStockByIndex() | |
{ | |
BSTR str_stock = _bstr_t(StockNo.c_str ()).Detach(); | |
short no = -1; | |
long ret = SKCOM->SKQuoteLibPtr->SKQuoteLib_RequestStocks (&no, str_stock); | |
::SysFreeString(str_stock); | |
} | |
void DoEnterMonitor() | |
{ | |
SKCOM->SKQuoteLibPtr->SKQuoteLib_EnterMonitor(); | |
} | |
}; | |
void DoJob(StockBot& Bot, Job& j) | |
{ | |
switch (j.do_type) | |
{ | |
case Job::Quote_Connect: | |
Bot.SKQuoteLibOnConnection(j.nValue1, j.nValue2); | |
break; | |
case Job::Quote_EnterMonitor: | |
Bot.DoEnterMonitor(); | |
break; | |
case Job::GetStockByIndex: | |
Bot.DoGetStockByIndex(); | |
break; | |
case Job::GetStockByIndexResult: | |
Bot.DoOnNotifyQuote(j.nValue1, j.nValue2); | |
break; | |
case Job::OnNotifyQuote: | |
Bot.Stock[j.Stock->bstrStockNo] = j.Stock; | |
printf("OnNotifyQuote : %d %ld\n", j.Stock->sStockIdx, j.Stock->nClose); | |
break; | |
} | |
} | |
int main() | |
{ | |
CoInitializeEx(NULL, COINIT_MULTITHREADED); | |
HRESULT hr; | |
StockBot Bot("6111"); | |
Bot.DoLogin("帳號", "密碼"); | |
while (true) | |
{ | |
while (!q.empty()) { | |
Job j = q.dequeue(); | |
DoJob(Bot, j); | |
} | |
Sleep(100); | |
} | |
CoUninitialize(); | |
return 0; | |
} |
8 則留言:
請問一下 ISKCenterLibPtr 這是從哪來的?
謝謝
你好
#import "D:\\dll\\x64\\SKCOM.dll"
using namespace SKCOMLib;
在 import 後就可以使用 ISKCenterLibPtr
不好意思我問的不好,
我是指怎麼知道有ISKCenterLibPtr這東西可以用?
從群益的文件上沒看到,
謝謝
你好
我試一下重現的方式是
我使用 Visual Studio
先在程式 import dll 之後 編譯
在目錄下會產出 x64\Debug\skcom.tlh
然後再用 Visual Studio 或是其他文字編輯器 去開啟 tlh 這個檔案
就會看到 namespace 跟有什麼 class 或是 function 可以使用
然後依 ATL COM 的使用方式 後面加上 Ptr 就可呼叫使用了
(可以看一下 tlh 中使用 _COM_SMARTPTR_TYPEDEF)
以上
請教一下,單純用想用 C++ 或 C# 寫一個:股票委買(賣) 程式,
群益的API做的到嗎? 我想將寫好的程式當「下單」指令用,例如像:
buyStock.exe 2330 250 1 (--買入2330 以250元 1張)
當然可以, 群益API內部有提供買賣下單的函式,
只要登入後,再呼叫就可以了
請教一個很弱的問題,如果不太會用 C++ template,程式也能寫的出來嗎...
因為看完您的大作,發現對 C++ STL ATL 很不熟 orz
你好,如果覺得 C++ 不夠熟悉的話
可以改用 python 看看也是比較方便
可以參考 eason大大的網站
https://easontseng.blogspot.com/2017/08/api-in-python.html
張貼留言