摘要:
1.窗口的初始化
2.窗口切换的处理
3.读取本地数据
4.发送请求数据
5.接收推送行情数据
6.画图
第一部分 视图的结构和数据初始化
SListView.cpp同时实现了两种功能,一个是推送行情,一个是请求行情。
int CSListView::OnCreate(LPCREATESTRUCT lpCreateStruct) 方法中,
1.初始化GridCtrl
2.准备底部标签
3.注册了窗口句柄,用于接收主动推送行情的行情数据
4.设置了一个定时器,每隔3秒主动向行情服务器请求一次行情数据。
int CSListView::OnCreate(LPCREATESTRUCT lpCreateStruct) 方法中的
{
// Create GridCtrl 初始化GridCtrl
CRect rect;
GetClientRect(rect);
if (!m_Grid.Create(rect, this, IDC_SLISTVIEW_GRID, WS_CHILD|WS_BORDER|WS_TABSTOP|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL))
{
TRACE("CSListView::OnCreate(...), Create m_Grid Error.\n");
return -1;
}
// fill it up with stuff
m_Grid.SetEditable(FALSE);
m_Grid.SetListMode(TRUE);
m_Grid.SetHeaderSort(FALSE);
m_Grid.SetSingleRowSelection(FALSE);
m_Grid.EnableDragAndDrop(TRUE);
m_Grid.SetGridLines(GVL_NONE);
m_Grid.EnableTitleTips(TRUE);
m_Grid.SetRowResize(TRUE);
m_Grid.SetColumnResize(TRUE);
.....
//底部标签
CTSKTabManagerItem* pItem;
CString strName;
strName.LoadString(IDS_SLISTVIEW_CLASS);
pItem = new CTSKTabManagerItem();
pItem->SetCaption(strName);
AddItem(SL_WORKTAB_CLASS, pItem);
strName.LoadString(IDS_SLISTVIEW_A);
pItem = new CTSKTabManagerItem();
pItem->SetCaption(strName);
AddItem(SL_WORKTAB_A, pItem);
//strName.LoadString(IDS_SLISTVIEW_MSMALL);
strName = _T("中小板");
pItem = new CTSKTabManagerItem();
pItem->SetCaption("中小板");
AddItem(SL_WORKTAB_MSMALL, pItem);
.....
主动推送行情,注册窗口句柄,在 \StkNet\Src\StkReceiver.cpp中实现
AfxGetStkReceiver().AddRcvDataWnd(GetSafeHwnd());
//行情列表刷新速度
SetTimer(SLV_TIMER_REFRESH, 20000, NULL);
}
推送行情
G:\stock\TskingVS2019\src\Client\StkUI\View\SListView.cpp
定义了一个接收数据的消息
BEGIN_MESSAGE_MAP(CSListView, CView)
ON_MESSAGE(WM_APP_STKRECEIVER_DATA, OnStkReceiverData)
END_MESSAGE_MAP()
消息处理
LRESULT CSListView::OnStkReceiverData(WPARAM wParam, LPARAM lParam)
{
PCOMMPACKET pCommPacket = (PCOMMPACKET)lParam;
switch(wParam)
{
case CStock::dataReport:
if (pCommPacket && CStock::dataReport == pCommPacket->m_dwDataType
&& pCommPacket->m_dwCount > 0)
{
CStockContainer & container = AfxGetSListStockContainer();
// 如果当前股票列表显示的日期不是今天的,返回,不做刷新
DWORD dwDate = -1;;
AfxGetSListStockContainer().GetCurrentType(NULL,NULL,&dwDate);
if (-1 != dwDate && dwDate != CSPTime(pCommPacket->m_pReport[0].m_time).ToStockTimeDay())
return 0L;
container.Lock();
// 刷新数据
BOOL bNeedReSort = FALSE;
for (DWORD i=0; i<pCommPacket->m_dwCount; i++)
{
int id = 0;
if (container.GetStockInfo(pCommPacket->m_pReport[i].m_szCode, NULL, &id))
{
CStockInfo & info = container.ElementAt(id);
UpdateStockInfoByREPORT(info, &(pCommPacket->m_pReport[i]));
StockInfoChanged(id, info);
bNeedReSort = TRUE;
}
}
// 统计平均值刷新
if (bNeedReSort)
{
container.SetAverage();
//SetAverageItem(m_Grid, container, TRUE);
}
// 重新排序
if (-1 != m_nColSort && bNeedReSort)
{
LPARAM lParam = m_Grid.GetItemData(0, m_nColSort);
if (lParam >= 0)
{
CStockContainer::m_pSortContainer = &container;
container.m_nSortVariantID = lParam;
container.m_bSortAscend = m_bSortAscend;
m_Grid.SortItems(ItemCompareFunc, 0, TRUE);
}
m_Grid.Invalidate();
}
container.UnLock();
}
break;
}
return 0L;
}
WM_APP_STKRECEIVER_DATA 由以下几个地方发出:
1.通视驱动发出,
G:\stock\TskingVS2019\src\Client\StkNet\Src\StkReceiver.cpp
// 通视处理数据
LRESULT CStkReceiver::OnStkReceiverTsdata(
UINT wFileType, // 文件类型, 参见 StockDrv.H
LONG lPara)
{
int i = 0;
PBYTE pFileBuf = NULL;
PBYTE pBuffx = NULL;
PGZLXBINDATA pGZLX = NULL;
RCV_DATA * pHeader = (RCV_DATA *) lPara;
// 检查参数
ASSERT(pHeader && pHeader->m_pData);
if (!pHeader || !pHeader->m_pData)
return -1L;
// ASSERT(pHeader->m_nPacketNum > 0 || pHeader->m_bDISK);
if (pHeader->m_nPacketNum <= 0 && !pHeader->m_bDISK)
return -1L;
// 对于处理较慢的数据类型,建议将数据备份,另创建一线程处理
switch(wFileType)
{
case RCV_REPORT:
{
// 检查参数
//ASSERT(pHeader->m_nPacketNum > 0 && !pHeader->m_bDISK);
if (pHeader->m_nPacketNum <= 0 || pHeader->m_bDISK)
return -1L;
// 分配备份数据存储空间
COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataReport, pHeader->m_nPacketNum);
if (NULL == pCommPacket)
return -1L;
// 修改AfxGetStockContainer()
int nBufSize = pHeader->m_pReport[0].m_cbSize;
PBYTE pBaseBuf = (PBYTE)&pHeader->m_pReport[0];
for(i=0; i<pHeader->m_nPacketNum; i++)
{
PRCV_REPORT_STRUCTEx pReport = (PRCV_REPORT_STRUCTEx)(pBaseBuf + nBufSize*i);
if (convert_TSREPORT_to_REPORT(pReport, &(pCommPacket->m_pReport[i])))
{
/* 以下代码转移到新建线程中执行 */
// AfxGetStkReceiver().OnReceiveReport(&(pCommPacket->m_pReport[i]));
}
}
{
CSingleLock lock(&g_mutexCommPacket,TRUE);
g_aptrCommPacket.Add(pCommPacket);
}
/* 以下代码转移到新建线程中执行
// 保存至本地文件
AfxGetDB().StoreReport(pCommPacket->m_pReport, pCommPacket->m_dwCount, FALSE);
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataReport, (LPARAM)pCommPacket);
// 释放备份数据存储空间
FreeCommPacket(pCommPacket);
*/
}
break;
case RCV_FILEDATA:
switch(pHeader->m_wDataType)
{
case FILE_HISTORY_EX: // 补日线数据
{
RCV_HISTORY_STRUCTEx * pDay = pHeader->m_pDay;
int nPacketNum = pHeader->m_nPacketNum;
CKData kdata(CKData::ktypeDay);
CString sCode;
DWORD dwMarket = CStock::marketUnknown;
// 读取文件
if (pHeader->m_bDISK)
{
pDay = NULL;
CFile file;
if (file.Open(pHeader->m_File.m_szFileName,CFile::modeRead))
{
ASSERT(file.GetLength() == pHeader->m_File.m_dwLen);
nPacketNum = (int)(file.GetLength()/sizeof(RCV_HISTORY_STRUCTEx));
if (nPacketNum > 0)
pDay = new RCV_HISTORY_STRUCTEx[nPacketNum];
if (pDay)
file.Read(pDay, sizeof(RCV_HISTORY_STRUCTEx)*nPacketNum);
file.Close();
}
}
// 分配备份数据存储空间
COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataK, nPacketNum);
if (NULL == pCommPacket)
return -1L;
// 处理数据
DWORD dwCount = 0;
for(i=0; i<nPacketNum && pDay; i++)
{
if (pDay[i].m_head.m_dwHeadTag == EKE_HEAD_TAG)
{
if (kdata.GetSize() > 0)
{
kdata.AutoSetKType();
UpdateStockContainerByKData(AfxGetStockContainer(), sCode, kdata);
AfxGetDB().InstallKData(kdata);
kdata.RemoveAll();
}
sCode = pDay[i].m_head.m_szLabel;
dwMarket = TSMarket_to_Market(pDay[i].m_head.m_wMarket);
continue;
}
convert_TSHISTORY_to_KDATA(dwMarket, sCode, &(pDay[i]), &(pCommPacket->m_pKdata[dwCount]));
kdata.Add(pCommPacket->m_pKdata[dwCount]);
dwCount ++;
}
pCommPacket->m_dwCount = dwCount;
if (pHeader->m_bDISK && pDay)
delete [] pDay;
if (kdata.GetSize() > 0)
{
kdata.AutoSetKType();
UpdateStockContainerByKData(AfxGetStockContainer(), sCode, kdata);
AfxGetDB().InstallKData(kdata);
}
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataK, (LPARAM)pCommPacket);
// 释放备份数据存储空间
FreeCommPacket(pCommPacket);
}
break;
case FILE_MINUTE_EX: // 补分钟线数据
case 83: // 补分笔数据
{
RCV_MINUTE_STRUCTEx * pMin = pHeader->m_pMinute;
ASSERT(pMin->m_head.m_dwHeadTag == EKE_HEAD_TAG);
CMinute minute;
BOOL bAutoSaveKDataMin = AfxGetProfile().GetAutoSaveKDataMin(); // 作为分钟K线保存
CString sCode;
DWORD dwMarket = CStock::marketUnknown;
// 分配备份数据存储空间
COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataMinute, pHeader->m_nPacketNum);
if (NULL == pCommPacket)
return -1L;
DWORD dwCount = 0;
for(i=0; i<pHeader->m_nPacketNum; i++)
{
if (pMin[i].m_head.m_dwHeadTag == EKE_HEAD_TAG)
{
if (minute.GetSize() > 0)
{
CKData kdMin(CKData::ktypeMin5);
if (minute.ToKData(kdMin))
AfxGetDB().InstallKData(kdMin);
minute.RemoveAll();
}
sCode = pMin[i].m_head.m_szLabel;
dwMarket = TSMarket_to_Market(pMin[i].m_head.m_wMarket);
continue;
}
convert_TSMINUTE_to_MINUTE(dwMarket, sCode, &(pMin[i]), &(pCommPacket->m_pMinute[dwCount]));
if (bAutoSaveKDataMin)
minute.Add(pCommPacket->m_pMinute[dwCount]);
dwCount ++;
}
pCommPacket->m_dwCount = dwCount;
if (minute.GetSize() > 0)
{
CKData kdMin(CKData::ktypeMin5);
if (minute.ToKData(kdMin))
AfxGetDB().InstallKData(kdMin);
}
// 保存至本地文件
AfxGetDB().StoreMinute(pCommPacket->m_pMinute, pCommPacket->m_dwCount);
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataMinute, (LPARAM)pCommPacket);
// 释放备份数据存储空间
FreeCommPacket(pCommPacket);
}
break;
case FILE_POWER_EX: // 补充除权数据
{
RCV_POWER_STRUCTEx * pPower = pHeader->m_pPower;
int nPacketNum = pHeader->m_nPacketNum;
CDRData drdata;
CString sCode;
DWORD dwMarket = CStock::marketUnknown;
if (pHeader->m_bDISK)
{
pPower = NULL;
CFile file;
if (file.Open(pHeader->m_File.m_szFileName,CFile::modeRead))
{
ASSERT(file.GetLength() == pHeader->m_File.m_dwLen);
nPacketNum = (int)(file.GetLength()/sizeof(RCV_POWER_STRUCTEx));
if (nPacketNum > 0)
pPower = new RCV_POWER_STRUCTEx[nPacketNum];
if (pPower)
file.Read(pPower, sizeof(RCV_POWER_STRUCTEx)*nPacketNum);
file.Close();
}
}
// 分配备份数据存储空间
COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataDR, nPacketNum);
if (NULL == pCommPacket)
return -1L;
DWORD dwCount = 0;
for(i=0; i<nPacketNum && pPower; i++)
{
if (pPower[i].m_head.m_dwHeadTag == EKE_HEAD_TAG)
{
if (drdata.GetSize() > 0)
AfxGetDB().InstallDRData(drdata);
drdata.RemoveAll();
sCode = pPower[i].m_head.m_szLabel;
dwMarket = TSMarket_to_Market(pPower[i].m_head.m_wMarket);
continue;
}
convert_TSPOWER_to_DRDATA(dwMarket, sCode, &(pPower[i]), &(pCommPacket->m_pDrdata[dwCount]));
drdata.Add(pCommPacket->m_pDrdata[dwCount]);
dwCount ++;
}
if (pHeader->m_bDISK && pPower)
delete [] pPower;
if (drdata.GetSize() > 0)
AfxGetDB().InstallDRData(drdata);
pCommPacket->m_dwCount = dwCount;
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataDR, (LPARAM)pCommPacket);
// 释放备份数据存储空间
FreeCommPacket(pCommPacket);
}
break;
case FILE_MULTISORT_EX: // 综合排名
{
RCV_MULTISORT_STRUCTEx * pMultisort = pHeader->m_pMultisort;
// 分配备份数据存储空间
COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataMultisort, pHeader->m_nPacketNum);
if (NULL == pCommPacket)
return -1L;
for(i=0; i<pHeader->m_nPacketNum; i++)
{
convert_TSMULTISORT_to_MULTISORT(&(pMultisort[i]), &(pCommPacket->m_pMultisort[i]));
}
pCommPacket->m_dwCount = pHeader->m_nPacketNum;
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataMultisort, (LPARAM)pCommPacket);
// 释放备份数据存储空间
FreeCommPacket(pCommPacket);
}
break;
case FILE_BASE_EX: // 钱龙兼容基本资料文件,m_szFileName仅包含文件名
{
if (pHeader->m_bDISK)
{
AfxGetDB().InstallBaseText(pHeader->m_File.m_szFileName, AfxGetFileTitle(pHeader->m_File.m_szFileName));
}
else
{
AfxGetDB().InstallBaseText(LPCTSTR(pHeader->m_pData), pHeader->m_File.m_dwLen, AfxGetFileTitle(pHeader->m_File.m_szFileName));
}
// 分配备份数据存储空间
COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataCode, 1);
if (NULL == pCommPacket)
return -1L;
CString sTemp = AfxGetFileTitleNoExt(pHeader->m_File.m_szFileName);
strncpy(pCommPacket->m_pStockcode[0].m_szCode, sTemp, min(sizeof(pCommPacket->m_pStockcode[0].m_szCode)-1,sTemp.GetLength()));
pCommPacket->m_pStockcode[0].m_dwMarket = CStock::marketUnknown;
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataBasetext, (LPARAM)pCommPacket);
// 释放备份数据存储空间
FreeCommPacket(pCommPacket);
}
break;
case FILE_NEWS_EX: // 新闻类,其类型由m_szFileName中子目录名来定
if (pHeader->m_bDISK)
{
AfxGetDB().InstallNewsText(pHeader->m_File.m_szFileName, AfxGetFileTitle(pHeader->m_File.m_szFileName));
}
else
{
AfxGetDB().InstallNewsText(LPCTSTR(pHeader->m_pData), pHeader->m_File.m_dwLen, AfxGetFileTitle(pHeader->m_File.m_szFileName));
}
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataNews, NULL);
break;
case FILE_HTML_EX: // HTML文件,m_szFileName为URL
AfxGetProfile().AddHTMLURLToExplorer(pHeader->m_File.m_szFileName);
break;
case FILE_SOFTWARE_EX:
TRACE("%s\n",pHeader->m_File.m_szFileName);
TRACE("%d\n",pHeader->m_File.m_dwLen);
pBuffx = (PBYTE)pHeader->m_pData;
break;
case FILE_SHAZQDATA_EX: //h上海国债净价交易
break;
case FILE_TYPE_RES:
TRACE("MSG: CStkReceiver::OnStkDataOK, Replaced data \n");
break;
default:
TRACE("Msg: CStkReceiver::OnStkReceiverData,Unkonw data type\n");
}
break;
}
return 0L;
}
2.void CStkReceiver::NotifyWndRcvData(WPARAM wParam, LPARAM lParam)发出
G:\stock\TskingVS2019\src\Client\StkNet\Src\StkReceiver.cpp
void CStkReceiver::NotifyWndRcvData(WPARAM wParam, LPARAM lParam)
{
for(int i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA , wParam, lParam);
}
前者是通视驱动发送过来的消息处理后再发送给各窗口,后者是请求行情服务器得到数据后发送给各窗口。
step 1:各视图向CStkReceiver注册自己的窗口句柄
step 2:各视图定义自己的消息及处理函数。ON_MESSAGE(WM_APP_STKRECEIVER_DATA, OnStkReceiverData)
step 3:CStkReceiver向通视注册,用OnStkReceiverTsdata()函数处理通视驱动发送过来的数据,处理后转发各个已注册窗口。
gSTOCKDLL.Stock_Init(m_hWnd,WM_APP_STKRECEIVER_TSDATA,RCV_WORK_SENDMSG)
行情服务器也注册了
LRESULT CStkReceiver::OnStkReceiverConnectserver(WPARAM wParam, LPARAM lParam)
{
if (gNetTSDLL.LoadDriver("NetTS.dll")
&& gNetTSDLL.Stock_Init(m_hWnd,WM_APP_STKRECEIVER_TSDATA,RCV_WORK_SENDMSG) > 0)
return TRUE;
return FALSE;
}
G:\stock\TskingVS2019\src\Client\NetTS\TSWnd.cpp
当队列有数据时,主动发送消息
// 分发数据线程,行情服务器主动推送线程 2019/06/08 by freeman
UINT TSDispatchThreadMain(LPVOID pParam)
{
while(TRUE)
{
UINT nMsgType = 0;
PRCV_DATA pRCV_DATA = NULL;
if (CTSCache::GetInstance().PopPacket(nMsgType, pRCV_DATA))
{
ASSERT(pRCV_DATA && pRCV_DATA->m_pData);
if (pRCV_DATA && pRCV_DATA->m_pData)
CTSWnd::GetInstance().SendMessage(nMsgType, (LPARAM)pRCV_DATA);
请求行情
调用链条:ListView.cpp里面的定时器发起SendRequestQuote(FALSE)->调用StkNet项目里面的
AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);
->调用StkNet项目里面的gNetTSDLL.RequestStockData(nDataTypeTS, pStocks, nSize, nKType, nDataCount);
->痛过gNetTSDLL全局变量调用NetTS项目里面的函数
int WINAPI RequestStockData( int nDataType, TW_STOCK * pStocks, int nSize, int nKType, int nDataCount )
->调用NetTS项目里面的CTWSocket::GetInstance().RequestStockData( nDataType, pStocks, nSize, nKType, nDataCount );
在这个函数里面,构造发送Askbuffer的二进制字节流发送给行情服务器。
具体实现过程如下:
step 1:SListView.cpp的定时器,每隔3秒定时向行情服务器发起一次请求行情数据
void CSListView::OnTimer(UINT nIDEvent)
{
if (SLV_TIMER_REFRESH == nIDEvent)
{
SendRequestQuote(FALSE);
}
CView::OnTimer(nIDEvent);
}
step 2:执行SendRequestQuote(FALSE);发起请求行情
SendRequestQuote(FALSE),当bForced=FALSE时,会判断是否是在交易时间内,如是则发起请求,否则不发起请求。
请求
用AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);发起向行情服务器的主动请求
//发球请求行情
void CSListView::SendRequestQuote(BOOL bForced)
{
if (!AfxGetStkReceiver().NetEngineIsWorking())
return;
static int nLastStart = -1;
static int nLastCount = -1;
BOOL bQuoteTipSended = FALSE;
//判断目前是否在交易时间内
BOOL bInTrade = CSPTime::InTradeTime(CSPTime::GetCurrentTime().GetTime(), 900);
CView* pView = AfxGetStaticDoc()->GetActiveView();
//如果是强制刷新. 则发起请求沪、深指数数据2019/06/06 by freeman
if (bForced)
{
CStockContainer stocks;
CStockInfo info;
if (AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAIN, &info))
stocks.Add(info);
if (AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAINSZN, &info))
stocks.Add(info);
if (stocks.GetSize() > 0)
AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);//发起请求行情:请求沪、深指数
}
//如果是强制刷新或者股票列表窗口处于激活状态,则发起请求当前窗口股票列表的数据
if (bForced || pView == this)
{
CCellRange cr = m_Grid.GetVisibleNonFixedCellRange();//非固定
int nStart = cr.GetMinRow() - 1;
int nCount = cr.GetMaxRow() - cr.GetMinRow() + 1;
CStockContainer& container = AfxGetSListStockContainer();
container.Lock();
if (nStart >= 0 && nCount > 0 && container.GetSize() > 0 && (bInTrade || bForced || nLastStart != nStart || nLastCount != nCount))
{
nLastStart = nStart;
nLastCount = nCount;
//从AfxGetSListStockContainer()中找出当前窗口的股票列表,并发送请求
AfxGetStkReceiver().RequestStockData(CStock::dataReport, container.GetData()+nStart, min(nCount,container.GetSize()-nStart), 0, 0);//发起请求行情:请求当前窗口的股票列表
bQuoteTipSended = (0 == nStart && nCount >= 9);
}
container.UnLock();
}
//如果目前是在交易时间内。2019/06/06 by freeman
if (bInTrade)
{
// 预警系统所需股票行情刷新
CAlarmCondContainer& conds = AfxGetProfile().GetAlarmCondContainer();
CStockContainer stocks;
stocks.SetSize(0, conds.GetSize() + 5);
for (int i = 0; i < conds.GetSize(); i++)
{
ALARMCOND cond = conds.GetAt(i);
CStockInfo info;
if (AfxGetStockContainer().GetStockInfo(cond.m_szCode, &info))
stocks.Add(info);
}
CStockInfo infoMain, infoMainSzn;
if (AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAIN, &infoMain)
&& AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAINSZN, &infoMainSzn))
{
stocks.Add(infoMain);
stocks.Add(infoMainSzn);
}
// 行情QuoteTip 需要行情数据
if (!bQuoteTipSended)
{
CStockContainer & container = AfxGetSListStockContainer();
container.Lock();
for (int i=0; i<9 && i<container.GetSize(); i++)
stocks.Add(container.ElementAt(i));
container.UnLock();
}
if (stocks.GetSize() > 0)
AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);
}
}
第二部分 切换到股票列表视图窗口
窗口激活时调用
void CSListView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
TRACE(_T("CSListView::OnActivateView %d\n"), bActivate);
if (bActivate && m_bShouldUpdate)
{
OnUpdate(NULL, UPDATE_HINT_SLISTVIEW, NULL);//更新数据
m_bShouldUpdate = FALSE;
}
CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}
//数据更新 2019/06/08 by freeman
void CSListView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
TRACE(_T("CSListView::OnUpdate %d %d %d\n"), pSender, lHint, pHint);
if (UPDATE_HINT_SLISTVIEW != lHint)
return;
if (IsWindowVisible())
SetFocus();
int nColumnCount = m_Grid.GetColumnCount();
if (nColumnCount <= 0)
return;
if (GetParentFrame()->GetSafeHwnd() != AfxGetMainFrame()->GetActiveFrame()->GetSafeHwnd())
{
m_bShouldUpdate = TRUE;
return;
}
m_bShouldUpdate = FALSE;
CStockContainer & container = AfxGetSListStockContainer();
container.Lock();
m_Grid.DeleteNonFixedRows();
// Progress
CMainFrame * pMainFrame = AfxGetMainFrame();
if (pMainFrame)
{
pMainFrame->ShowProgressBar();
pMainFrame->SetProgress(0);
pMainFrame->SetMessageText(IDS_MAINFRAME_WAITING);
}
CUIntArray anParams;
anParams.SetSize(0, nColumnCount);
for (int nCol=0; nCol < nColumnCount; nCol ++)
{
LPARAM lParam = m_Grid.GetItemData(0, nCol);//获取第0行的数据
anParams.Add(lParam);
}
CRect rectClient;
GetClientRect(&rectClient);
int nPageCount = 1 + rectClient.Height() / abs(m_Grid.GetFixedRowHeight()) + 1;
//股票容器
for (int i=0; i<container.GetSize(); i++)
{
CStockInfo & info = container.GetStockInfoByID(i);
//插入一行,第0列为股票名称 2019/06/08 by freeman
int nRow = m_Grid.InsertRow(info.GetStockName());
m_Grid.SetItemData(nRow, 0, i);
//显示除股票名称外的其他列信息
for (int nCol=0; nCol<anParams.GetSize(); nCol++)
{
m_Grid.SetItemFormat(nRow, nCol, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
m_Grid.SetItemText(nRow, nCol, AfxGetVariantDispString(anParams[nCol], info, &container));
m_Grid.SetItemBkColour(nRow, nCol, AfxGetProfile().GetColor(CColorClass::clrSListBK));
m_Grid.SetItemFgColour(nRow, nCol, AfxGetVariantColor(anParams[nCol], info));
if (anParams[nCol] == SLH_DATE)
m_Grid.SetColumnWidth(nCol, 80);
}
if (i == nPageCount+5)
{
m_Grid.Invalidate();
MSG msg;
while (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
AfxGetApp()->PumpMessage();
}
if (pMainFrame)
pMainFrame->SetProgress(100*i/container.GetSize());
}
// 统计列表的平均值
//SetAverageItem(m_Grid, container, FALSE);
// Sort If Needed
if (-1 != m_nColSort)
{
LPARAM lParam = m_Grid.GetItemData(0, m_nColSort);
if (lParam >= 0)
{
CStockContainer::m_pSortContainer = &container;
container.m_nSortVariantID = lParam;
container.m_bSortAscend = m_bSortAscend;
m_Grid.SortItems(ItemCompareFunc, 0, TRUE);
}
}
if (pMainFrame)
{
pMainFrame->SetProgress(100);
pMainFrame->HideProgressBar();
pMainFrame->SetMessageText(IDS_MAINFRAME_FINISHED);
pMainFrame->SetMessageText(IDS_HELPTIP_SLIST);
}
if (m_bFirstUpdate)
{
m_Grid.AutoSizeColumns();
}
else
{
m_Grid.SetColumnWidth(0, 80);
m_Grid.SetColumnWidth(1, 80);
}
m_bFirstUpdate = FALSE;
m_Grid.Invalidate();
SendRequestQuote(TRUE);
container.UnLock();
}
//股票数据发生变化 2019/06/08 by freeman
void CSListView::StockInfoChanged(LONG infoid, CStockInfo & info)
{
CStockContainer & container = AfxGetSListStockContainer();
container.Lock();
// update grid value
for (int nRow=m_Grid.GetRowCount()-1; nRow >= 0; nRow--)
{
LPARAM id = m_Grid.GetItemData(nRow,0);
if (id == infoid)
{
for (int nCol=0; nCol<m_Grid.GetColumnCount(); nCol++)
{
LPARAM lParam = m_Grid.GetItemData(0,nCol);
m_Grid.SetItemText(nRow, nCol, AfxGetVariantDispString(lParam, info, &container));
m_Grid.SetItemFgColour(nRow, nCol, AfxGetVariantColor(lParam, info));
}
m_Grid.RedrawRow(nRow);//重画一行 2019/06/08 by freeman
break;
}
}
container.UnLock();
}
第三部分 股票列表显示
股票列表指标项目定义
G:\stock\TskingVS2019\src\Client\StkLib\Include\Express.h
#if !defined( __STKLIB_EXPRESS_H__ )
#define __STKLIB_EXPRESS_H__
/***
定义股票列表项的唯一标识.SListView 股票列表项定义 2019/06/08 by freeman
*/
// 股票
#define SLH_NONE 0
#define SLH_MIN 1
#define SLH_CODE 1 // 股票代码
#define SLH_NAME 2 // 股票名称
// 技术数据
#define SLH_DATE 3 // 日期
#define SLH_LASTCLOSE 4 // 昨收
#define SLH_OPEN 5 // 今开
#define SLH_CLOSE 6 // 收盘价
#define SLH_HIGH 7 // 最高价
#define SLH_LOW 8 // 最低价
#define SLH_AVERAGE 9 // 均价
#define SLH_DIFF 10 // 涨跌
#define SLH_DIFFPERCENT 11 // 涨幅%
#define SLH_SCOPE 12 // 震幅%
#define SLH_VOLUME 13 // 成交量
G:\stock\TskingVS2019\src\Client\StkLib\Src\Profile.cpp
//设置股票列表缺省列的函数
void CStProfile::SetSListColumnsDefault( )
{
// All Order
m_anSListColumnsOrder.RemoveAll();
#ifdef CLKLAN_ENGLISH_US
for( UINT i=SLH_MIN; i<=SLH_MAX; i++ )
{
m_anSListColumnsOrder.Add( i );
}
#else
m_anSListColumnsOrder.Add( SLH_NAME );
m_anSListColumnsOrder.Add( SLH_CODE );
for( UINT i=SLH_MIN; i<=SLH_MAX; i++ )
{
if( SLH_NAME != i && SLH_CODE != i )
m_anSListColumnsOrder.Add( i );
}
#endif
for( int k=0; k<m_aSListColumnsUser.GetSize(); k++ )
{
m_anSListColumnsOrder.Add( m_aSListColumnsUser.ElementAt(k).m_nID );
}
// Show
m_anSListColumnsShow.RemoveAll( );
m_anSListColumnsShow.Copy( m_anSListColumnsOrder );
}
CSPDWordArray & CStProfile::GetSListColumnsShow( )
{
if( m_anSListColumnsShow.GetSize() == 0 )
SetSListColumnsDefault( );
// Remove some dulplicate
for( int i=m_anSListColumnsShow.GetSize()-1; i>=0; i-- )
{
for( int k=0; k<i; k++ )
{
if( m_anSListColumnsShow[i] == m_anSListColumnsShow[k] )
{
m_anSListColumnsShow.RemoveAt( i );
SP_ASSERT( FALSE );
break;
}
}
}
return m_anSListColumnsShow;
}
CSPDWordArray & CStProfile::GetSListColumnsOrder( )
{
int nRealCount = SLH_MAX-SLH_MIN+1 + m_aSListColumnsUser.GetSize();
if( m_anSListColumnsOrder.GetSize() != nRealCount )
SetSListColumnsDefault( );
// Remove some dulplicate
for( int i=m_anSListColumnsOrder.GetSize()-1; i>=0; i-- )
{
for( int k=0; k<i; k++ )
{
if( m_anSListColumnsOrder[i] == m_anSListColumnsOrder[k] )
{
m_anSListColumnsOrder.RemoveAt( i );
SP_ASSERT( FALSE );
break;
}
}
}
return m_anSListColumnsOrder;
}
CIndexContainer &CStProfile::GetSListColumnsUser( )
{
return m_aSListColumnsUser;
}
窗口标题更新
ON_MESSAGE(WM_USER_GETVIEWTITLE, OnGetViewTitle)
G:\stock\TskingVS2019\src\Client\StkUI\StaticDoc.h
#define WM_USER_GETVIEWTITLE (WM_USER + 1)
发出消息
G:\stock\TskingVS2019\src\Client\StkUI\ChildFrm.cpp
void CChildFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle);
if ((GetStyle() & FWS_ADDTOTITLE) == 0)
return; // leave child window alone!
CView * pView = GetActiveView( );
if (bAddToTitle)
{
TCHAR szText[256+_MAX_PATH];
memset( szText, 0, sizeof(szText) );
if (pView == NULL)
lstrcpyn(szText, m_strTitle, 256+MAX_PATH);
else
pView->SendMessage( WM_USER_GETVIEWTITLE, 255+MAX_PATH, long(szText) );
// set title if changed, but don't remove completely
AfxSetWindowText(m_hWnd, szText);
}
}
G:\stock\TskingVS2019\src\Client\StkLib\Include\Express.h
/***
定义股票列表项的唯一标识.SListView 股票列表项定义 2019/06/08 by freeman
*/
// 股票
#define SLH_NONE 0
#define SLH_MIN 1
#define SLH_NO 1 // 股票序号,extend by freeman
#define SLH_CODE 2 // 股票代码
#define SLH_NAME 3 // 股票名称
// 技术数据
#define SLH_DATE 4 // 日期
#define SLH_LASTCLOSE 5 // 昨收
#define SLH_OPEN 6 // 今开
#define SLH_CLOSE 7 // 收盘价
#define SLH_HIGH 8 // 最高价
#define SLH_LOW 9 // 最低价
#define SLH_AVERAGE 10 // 均价
#define SLH_DIFF 11 // 涨跌
#define SLH_DIFFPERCENT 12 // 涨幅%
#define SLH_SCOPE 13 // 震幅%
#define SLH_VOLUME 14 // 成交量
#define SLH_AMOUNT 15 // 成交额
#define SLH_VOLUP 16 // 外盘
#define SLH_VOLDOWN 17 // 内盘
#define SLH_DIFFPERCENT_MIN5 18 // 五分钟涨幅%
#define SLH_SELLBUYRATIO 19 // 委比%
#define SLH_SELLBUYDIFF 20 // 委量差
#define SLH_BUYPRICE3 21 // 买价三
#define SLH_BUYPRICE2 22 // 买价二
#define SLH_BUYPRICE1 23 // 买价一
#define SLH_SELLPRICE1 24 // 卖价一
#define SLH_SELLPRICE2 25 // 卖价二
#define SLH_SELLPRICE3 26 // 卖价三
#define SLH_BUYVOLUME3 27 // 买量三
#define SLH_BUYVOLUME2 28 // 买量二
#define SLH_BUYVOLUME1 29 // 买量一
#define SLH_SELLVOLUME1 30 // 卖量一
#define SLH_SELLVOLUME2 31 // 卖量二
#define SLH_SELLVOLUME3 32 // 卖量三
// 主要基本指标
#define SLH_REPORTTYPE 33 // 报表类型
#define SLH_PE 34 // 市盈率
#define SLH_PNETASSET 35 // 市净率
#define SLH_PMAININCOME 36 // 市销率
#define SLH_RATIO_PCASH 37 // 价格净现金比
#define SLH_RATIO_CURRENCY 38 // 流通率
#define SLH_RATIO_CHANGEHAND 39 // 换手率
#define SLH_RATIO_VOLUME 40 // 量比
#define SLH_RS 41 // 相对强度
#define SLH_MARKETVALUE 42 // 总市值
#define SLH_MARKETVALUEA 43 // A股市值
#define SLH_MARKETVALUEB 44 // B股市值
// ★偿债能力
#define SLH_RATIO_LIQUIDITY 45 // 流动比率
#define SLH_RATIO_QUICK 46 // 速动比率
#define SLH_VELOCITY_RECEIVABLES 47 // 应收帐款周率
// ★经营能力
#define SLH_VELOCITY_MERCHANDISE 48 // 存货周转率
#define SLH_MAIN_INCOME 49 // 主营业务收入
#define SLH_CASH_PS 50 // 每股净现金流量
// ★盈利能力
#define SLH_PROFIT_MARGIN 51 // 主营业务利润率
#define SLH_NETASSET_YIELD 52 // 净资产收益率
// ★资本结构
#define SLH_DATE_BEGIN 53 // 上市日期
#define SLH_SHARE_COUNT_TOTAL 54 // 总股本
#define SLH_SHARE_COUNT_A 55 // 流通A股
#define SLH_SHARE_COUNT_B 56 // 流通B股
#define SLH_SHARE_COUNT_H 57 // 流通H股
#define SLH_SHARE_COUNT_NATIONAL 58 // 国有股
#define SLH_SHARE_COUNT_CORP 59 // 法人股
#define SLH_PROFIT_PSUD 60 // 每股未分配利润
#define SLH_ASSET 61 // 总资产
#define SLH_RATIO_HOLDERRIGHT 62 // 股东权益比率
#define SLH_RATIO_LONGDEBT 63 // 长期负债率
#define SLH_RATIO_DEBT 64 // 资产负债率
// ★投资收益能力
#define SLH_NETASSET_PS 65 // 每股净资产
#define SLH_NETASSET_PS_REGULATE 66 // 调整每股净资产
#define SLH_EPS 67 // 每股收益
#define SLH_EPS_DEDUCT 68 // 扣除后每股收益
#define SLH_NET_PROFIT 69 // 净利润
#define SLH_MAIN_PROFIT 70 // 主营业务利润
#define SLH_TOTAL_PROFIT 71 // 利润总额
#define SLH_PROFIT_INC 72 // 主营利润增长率
#define SLH_INCOME_INC 73 // 主营收入增长率
#define SLH_ASSET_INC 74 // 总资产增长率
#define SLH_ANNGAINS_AVERAGE 75 // 年平均收益率
#define SLH_ANNGAINS_STDDEV 76 // 收益标准差
#define SLH_BETA 77 // β值
#define SLH_SHARP 78 // 夏普风险指数
#define SLH_TRADE 79 // 行业
#define SLH_PROVINCE 80 // 省份
#define SLH_MAX 80
#define SLH_INVALID (UINT(-1))
#define SLH_USERDEFINE_BEGIN 10000
#define SLH_WIDTH_MIN 60
#define SLMAX_SLHTEXTLEN 256