Table of Contents
1.主界面和弹出框的菜单定义
G:\stock\Tskingfromgoogle\src\StkUI\StkUI.rc
界面所有菜单项和对应的事件在此文件定义。
IDR_MAINFRAME MENU
BEGIN
POPUP "系统(&S)"
BEGIN
MENUITEM "连接Internet服务器(&N)...", ID_SYS_CONNECTSERVER
MENUITEM "断开Internet服务器(&D)", ID_SYS_DISCONNECTSERVER
2.画图的函数
画图的函数都在StkUI\View\StockGraph.cpp和SktUI\View\DrawTech.cpp中
void CStockGraph::DrawTechBIAS( CDC * pDC, CRect rect, BOOL bDrawTitle )
{
CKData & kdata = m_CurStock.GetKData(m_nCurKType);
m_techparam.bias.AttachParameters( AfxGetProfile().GetTechParameters().bias );
double dMin = 0, dMax = 0;
if( !m_techparam.bias.GetMinMaxInfo( m_nIndexStart, m_nIndexEnd, &dMin, &dMax )
|| !DrawAxis( pDC, rect, m_nHeightSubtitle, 5, dMin, dMax, FALSE, TRUE, 2 ) )
return;
DrawTechUtil1( pDC, rect, bDrawTitle, dMin, dMax, STT_BIAS, &(m_techparam.bias), &kdata,
AfxGetProfile().GetColor(CColorClass::clrLine1), drawtechtype_line );
}
本地数据目录为:
G:\stock\Tskingfromgoogle\src\Bin
股票行情列表主窗口
G:\stock\Tskingfromgoogle\src\StkUI\View\SListView.cpp
定义了底部tab标签,然后定义GridCtrl类型的变量用于显示每只股票的列
protected:
CGridCtrl m_Grid;
通过G:\stock\Tskingfromgoogle\src\StkUI\GridCtrl.h显示每只股票的列
STKLIB中的类及其继承关系
StkLib的几个全局静态变量
工程StkLib的几个全局静态变量:
g_stockcontainer:AfxGetStockContainer()可以得到该变量的引用,该
变量记录所有股票的行情信息和基本信息,CStockInfo的数组。
g_domaincontainer:AfxGetDomainContainer()可以得到该变量的引用,
该变量记录所有板块的信息。
g_groupcontainer:AfxGetGroupContainer()可以得到该变量的引用,该
变量记录所有分组的信息。
g_stdatabase:AfxGetDB()可以得到该变量的引用,该变量实现了本软件的
数据文件接口。
g_stprofile:AfxGetProfile()可以得到该变量的引用,该变量记录当前软
件的一些设置。
STKLIB中主要类和继承关系:
这个工程是实现股票数据结构、技术指标计算的动态连接库,代码与平台无关。
1. 股票数据结构相关的类
class CKData
::K线数据数组类
KDATA是K线数据结构
CKDATA是将封装了股票数据的相关的操作
class CDRData
::除权除息数组类
DRDATA除权数据结构
CDRDATA封装了除权数据的操作
class CBaseData
::财务资料数组类
BASEDATA财务信息数据结构
CBaseData封装了财务数据的操作
以上为股票的静态数据结构
以下为股票的动态数据结构
class CReport
::行情信息数据数组类
REPORT行情刷新数据结构
CReport封装了行情信息数据的操作
class CMinute
::行情分时数据数组类
MINUTE行情分时数据结构
CMinute封装了行情分时数据的操作
class COutline
::行情额外信息数据数组类
OUTLINE行情额外数据结构
COutline封装了行情额外信息数据操作
////////////////////////////////////
class CStockInfo
::单个股票信息类
class CStock
::单个股票的数据,包括股票信息,K线数据,基本资料,除权除息数据等
*********以上类是在stock.h中定义的,主要是股票数据结构的定义***********
**********以下类是股票信息数据CStockInfo的数组类,在container.h中定义*********
class CStockInfoArray
::CStockInfo数阻类
typedef CSPArray<CStockInfo, CStockInfo &> CStockInfoArray;
class CStockContainer: public CStockInfoArray
::股票信息数组类
有一个股票信息数组的全局对象AfxGetStockContainer()保存所有股票的信息
class CDomain: public CSPStringArray
::板块类,包含板块名称、板块中的股票代码
class CDomainArray
::板块数组类
typedef CSPArray<CDomain, CDomain &> CDomainArray;
class CDomainContainer : public CDomainArray
::板块数组类
2.股票数据库相关的类
class IStStore
::读取K线数据的通用接口,各种格式的读取K线数据类必须从此类继承
class CStDatabase : public Object
::本地数据类,提供对磁盘数据的操作接口,包括股票信息,各种周期K线数据,基本资料,权息资料的读取和安装。
class CQianlong : public IStStore
::钱龙数据格式的读取类
class CClkFile
::文件操作
class CSelfDB : public CQianlong
::自有格式数据的读取,除了除权除息数据、行情数据外,其余与钱龙格式数据相同。
3.技术指标类
class CTechnique
::技术指标基类
其他的技术指标类是从基类继承的
4.股票列表视图的列变量定义,以及自定义列的表达式计算
class CExpress
::表达式,可以包含变量
class CIndex
::新建指标,包含含有变量的表达式,在股票列表上显示为一列
class CIndexContainer
::新建指标数组
5.策略定义
class CRateParam
::交易税费设置类
class CSPTimeContainer
::CSPTimeContainer : public CSPTimeArray
typedef CSPArray<CSPTime, CSPTime &> CSPTimeArray;
class COpParam
::策略模拟操作条件类
class CStockOwnContainer
::拥有股票数组
class COpRecordContainer
::操作记录数组
class CAssetSerialContainer
::资产数组
class CTechStock
::一只待操作的股票,包含股票的信息,K线数据,长周期K线数据,所需技术指标,所需长周期技术指标
class CTechStockContainer
::CTechStock数组
class CStrategy
::策略
6.软件的用户配置
class CTechParameters
::技术指标参数,包含所有技术指标的参数设定
class CColorRefContainer
::CSPArray<COLORREF, COLORREF> CColorRefArray;
class STKLIB_API CColorRefContainer : public CColorRefArray
class CColorClass
::界面配色方案
class CColorClassContainer
::配色方案数组
class CStProfile
::系统设置
STKNET中的类及其继承关系
STKNET--这个工程是实现网络数据自动下载功能的动态连接库:
1.数据包类和数据库类
class CPackage
::数据包类,表示一个数据包
class CPackages
::数据包数组
class CDownloadServer
::产品服务器信息类,表示一个产品服务器
class CServers
::产品服务器数组
class CNetDatabase : public CStDatabase
::网络数据类,提供对磁盘数据的操作接口,包括股票信息,各种周期K线数据,基本资料,权息资料的 读取和安装。
class CStDatabase
::本地数据类,提供对磁盘数据的操作接口,包括股票信息,各种周期K线数据,基本资料,权息资料的读取和安装。
view、StkReveiver、stockdrv、NetTs.DLL三者之间的关系梳理
1.StkReveiver类负责接收处理通视驱动的消息和行情服务器的消息,然后向视图发送消息
定义了收到上述两者发来的WM_APP_STKRECEIVER_TSDATA消息的处理函数OnStkReceiverTsdata()
G:\stock\TskingVS2019\src\Client\StkNet\Src\StkReceiver.cpp
BEGIN_MESSAGE_MAP(CStkReceiver, CWnd)
//{{AFX_MSG_MAP(CStkReceiver)
ON_WM_CREATE()
ON_WM_DESTROY()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_APP_STKRECEIVER_CONNECTSERVER,OnStkReceiverConnectserver)
ON_MESSAGE(WM_APP_STKRECEIVER_DISCONNECTSERVER,OnStkReceiverDisconnectserver)
ON_MESSAGE(WM_APP_STKRECEIVER_INIT,OnStkReceiverInit)
ON_MESSAGE(WM_APP_STKRECEIVER_SETUP,OnStkReceiverSetup)
ON_MESSAGE(WM_APP_STKRECEIVER_QUIT,OnStkReceiverQuit)
ON_MESSAGE(WM_APP_STKRECEIVER_TSDATA,OnStkReceiverTsdata) //收到消息的处理函数
END_MESSAGE_MAP()
消息的处理函数OnStkReceiverTsdata()
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;
}
通知各窗口处理
// 通知各股票信息显示窗口,刷新行情数据
for(i=0; i<m_awndRcvData.GetSize(); i++)
::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataReport, (LPARAM)pCommPacket);
消息来源
1.由NetTS.DLL发出
BOOL CStkReceiver::NetEngineBeginWorking(LPCTSTR lpszAddress, UINT nPort, LPCTSTR lpszUser, LPCTSTR lpszPasswd)
{
if (gNetTSDLL.LoadDriver("NetTS.dll")
&& gNetTSDLL.Stock_Init_Nodialog(m_hWnd,WM_APP_STKRECEIVER_TSDATA,RCV_WORK_SENDMSG,
lpszAddress,nPort,lpszUser,lpszPasswd) > 0)
return TRUE;
return FALSE;
}
点击连接行情服务器时
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;
}
2.由通视发出
/通视驱动初始化
void CSelectEngineDlg::OnSetupreceiver()
{
int nSel = m_listEngine.GetCurSel();
if( LB_ERR == nSel )
{
AfxMessageBox( AfxModuleLoadString(IDS_SELECTENGINE_ERRSELECT), MB_OK );
return;
}
int nData = (int)m_listEngine.GetItemData(nSel);
if( nData >= 0 && nData<m_astrDriver.GetSize() )
{
m_drv.Stock_Quit(GetSafeHwnd());
m_drv.UnloadDriver();
if( m_drv.LoadDriver( m_astrDriver[nData] )
&& m_drv.Stock_Init(GetSafeHwnd(),WM_APP_STKRECEIVER_TSDATA,RCV_WORK_SENDMSG) )
{
m_drv.SetupReceiver(TRUE);
}
else
AfxMessageBox( AfxModuleLoadString(IDS_SELECTENGINE_ERRLOAD), MB_OK );
}
m_btnSetup.EnableWindow( FALSE );
}