策略为王启动源码分析-股票行情接收对数据的处理实现原理

Published

股票行情接收由src\StkNet\Src\StkReceiver.cpp来实现

1.推送行情
static CSTKDRVContainer gSTOCKDLL;

CSTKDRVContainer在src\StkNet\Src\STKDRVContainer.cpp实现,主要功能:

1.加载第三方驱动(如通视的dll,银江的dll)

2.向stkui主程序相关的目标窗口句柄发送行情数据包()


2.点播行情
static CNetSTKDRV gNetTSDLL;

当点击“连接行情服务器时”,

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;
}

2.分时图对行情数据的处理

2.1 RealTime.cpp分时图行情对接收到的数据的处理

2.2 分时图处理行情数据

对收到的行情数据,只对成交明细数据dataReport和分时线数据进行处理。

G:\stock\TskingVS2019\src\Client\StkUI\View\RePlayRealTime.cpp

//接收数据的消息处理函数
LRESULT CRePlayRealTime::OnStkReceiverData( WPARAM wParam, LPARAM lParam )
{
PCOMMPACKET	pCommPacket	= (PCOMMPACKET)lParam;

	switch( wParam )
	{
	case CStock::dataReport:

        case CStock::dataMinute:
        }
}

2.2.1 对分笔数据的处理

      接收数据的消息处理函数,分时图对行情数据的处理,对收到的行情数据,只对成交明细数据dataReport和分时线数据进行处理。

     只更新了股票StockInfo中的卖一等和驲K线数据。插入了分笔数据,插入了分时数据。对其他分钟线和日K线数据未做处理。

//接收数据的消息处理函数,分时图对行情数据的处理,对收到的行情数据,只对成交明细数据dataReport和分时线数据进行处理。
LRESULT CRealTime::OnStkReceiverData( WPARAM wParam, LPARAM lParam )
{
	PCOMMPACKET	pCommPacket	= (PCOMMPACKET)lParam;

	switch( wParam )
	{
	case CStock::dataReport:
		if( pCommPacket && CStock::dataReport == pCommPacket->m_dwDataType )
		{
			BOOL	bInv = FALSE;
			for(DWORD i=0; i<pCommPacket->m_dwCount; i++)
			{
				//判断数据是否是当前股票分笔数据 by freeman
				if( m_CurStock.GetStockInfo().IsEqualTo( pCommPacket->m_pReport[i].m_dwMarket, pCommPacket->m_pReport[i].m_szCode ) )
				{
					//更新了股票StockInfo中的卖一等和K线数据。
					UpdateStockInfoByREPORT( m_CurStock.GetStockInfo(), &(pCommPacket->m_pReport[i]) );
					//插入分笔数据
					if( -1 != m_CurStock.GetReport().InsertReportSort( pCommPacket->m_pReport[i] ) )
					{
						MINUTE	minute;
						if( convert_REPORT_to_MINUTE( &(pCommPacket->m_pReport[i]), &minute ) )
							//插入分时数据
							m_CurStock.GetMinute().InsertMinuteSort( minute );
						m_nCurrentStartPos	= -1;
						bInv = TRUE;
					}
				}
			}
			if( bInv && m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()) )
				m_pParent->Invalidate();
		}
		break;

 

3.K线图数据处理

K线图收到数据后,交给CStockGraph::OnStkReceiverData(WPARAM wParam, LPARAM lParam)处理

G:\stock\TskingVS2019\src\Client\StkUI\View\GraphView.cpp

LRESULT CGraphView::OnStkReceiverData(WPARAM wParam, LPARAM lParam)
{
	return m_graph.OnStkReceiverData(wParam,lParam);
}

 

G:\stock\TskingVS2019\src\Client\StkUI\View\StockGraph.cpp

//周期图数据消息接收处理,只对分笔数据、分时数据、K线数据进行处理 by freeman
LRESULT CStockGraph::OnStkReceiverData(WPARAM wParam, LPARAM lParam)
{
	PCOMMPACKET	pCommPacket = (PCOMMPACKET)lParam;

	switch (wParam)
	{
	case CStock::dataReport:
		if (pCommPacket && CStock::dataReport == pCommPacket->m_dwDataType)
		{
			DWORD i;
			for (i = 0; i < pCommPacket->m_dwCount; i++)
			{
				if (m_CurStock.GetStockInfo().IsEqualTo(pCommPacket->m_pReport[i].m_dwMarket, pCommPacket->m_pReport[i].m_szCode))
					break;
			}
			if (i != pCommPacket->m_dwCount)
			{
				// 加入最新一天的K线数据
				PrepareStockData(TRUE);
				if (m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()))
					m_pParent->Invalidate();
			}
		}
		break;
	case CStock::dataK:
		if (pCommPacket && CStock::dataK == pCommPacket->m_dwDataType)
		{
			DWORD i;
			for (i = 0; i < pCommPacket->m_dwCount; i++)
			{
				if (m_CurStock.GetStockInfo().IsEqualTo(pCommPacket->m_pKdata[i].m_dwMarket, pCommPacket->m_pKdata[i].m_szCode))
					break;
			}
			if (i != pCommPacket->m_dwCount)
			{
				// 加入最新一天的K线数据
				PrepareStockData(TRUE);
				if (m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()))
					m_pParent->Invalidate();
			}
		}
		break;
	case CStock::dataMinute:
		// WILLCHECK
		if (!CKData::IsDayOrMin(m_nCurKType) && pCommPacket && CStock::dataMinute == pCommPacket->m_dwDataType)
		{
			int	nOldSize = m_CurStock.GetMinute().GetSize();
			DWORD i;
			for (i = 0; i < pCommPacket->m_dwCount; i++)
			{
				if (m_CurStock.GetStockInfo().IsEqualTo(pCommPacket->m_pMinute[i].m_dwMarket, pCommPacket->m_pMinute[i].m_szCode))
					break;
			}
			if (i != pCommPacket->m_dwCount)
			{
				// 加入最新的分钟K线数据
				PrepareStockData(TRUE);
				if (m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()))
					m_pParent->Invalidate();
			}
		}
		break;
	}

	return 0L;
}

 

4.SlistView对接收数据的处理

G:\stock\TskingVS2019\src\Client\StkUI\View\SListView.cpp

//前面定义了ON_MESSAGE(WM_APP_STKRECEIVER_DATA, OnStkReceiverData)的消息,处理接收到的消息 2019/06/08 by freeman
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;//股票在容器中的id号
				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);//更新股票容器数据,id为股票在容器中的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();//Invalidate只是放一个WM_PAINT消息在队列里。
			}
			container.UnLock();
		}
		break;
	}
	return 0L;
}

 

合并当日K线数据

G:\stock\TskingVS2019\src\Client\StkLib\Src\Packets.cpp

//实时更新CStockContainer日线数据 
BOOL UpdateStockContainerByKData( CStockContainer &container, LPCTSTR lpszCode, CKData & kdata )
{
	container.Lock();

	int	id = 0;
	if( CKData::ktypeDay == kdata.GetKType()
		&& container.GetStockInfo( lpszCode, NULL, &id ) )
	{
		CStockInfo	& info	=	container.ElementAt(id);
		if( kdata.GetSize() > (int)AfxGetProfile().GetCacheDays() )
		{
			// 只使用AfxGetProfile().GetCacheDays()天的数据
			CKData	temp( kdata.GetKType() );
			for( int i=kdata.GetSize()-AfxGetProfile().GetCacheDays(); i<kdata.GetSize(); i++ )
				temp.Add( kdata.ElementAt(i) );
			//合并当日K线数据 by freeman
			info.m_kdata.MergeKData( &temp );
		}
		else
			//合并当日K线数据 by freeman
			info.m_kdata.MergeKData( &kdata );

		
		// 指数涨跌家数
		int nSize = info.m_kdata.GetSize();
		if( nSize > 0 )
		{
			info.m_dwAdvance	=	info.m_kdata.ElementAt(nSize-1).m_dwAdvance;
			info.m_dwDecline	=	info.m_kdata.ElementAt(nSize-1).m_dwDecline;
		}
	}
	container.UnLock();
	return TRUE;
}

 

G:\stock\TskingVS2019\src\Client\StkLib\Src\AfxCore.cpp

 

BOOL AfxReloadStock( CStock & stock )
{
	CStockInfo info = stock.GetStockInfo();

	// Reload AfxGetStockMain()
	AfxGetStockContainer().GetStockInfo( info.GetStockCode(), &info );

	stock.Clear( );
	stock.SetStockInfo( &info );
	stock.SetDatabase( &AfxGetDB() );
	stock.PrepareData( CStock::dataK, CKData::ktypeDay );
	
	// Merge New 合并StockInfo的日K线数据
	stock.GetKDataDay().MergeKData( &(info.m_kdata) );
	
	stock.PrepareData( CStock::dataK, CKData::ktypeWeek );
	stock.PrepareData( CStock::dataK, CKData::ktypeMonth );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin1);//extend 1分钟K线 by freeman 
	stock.PrepareData( CStock::dataK, CKData::ktypeMin5 );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin15 );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin30 );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin60 );
	return TRUE;
}

 

BOOL AfxPrepareStockData( CStDatabase * pDatabase, CStock &stock, int nKType, int nKFormat, int nMaindataType, BOOL bFullFill, BOOL bReload )
{
	CStockInfo	info;
	if( !AfxGetStockContainer().GetStockInfo( stock.GetStockCode(), &info ) )
		info	=	stock.GetStockInfo( );

	stock.SetDatabase( pDatabase );
	stock.PrepareData( CStock::dataK, CKData::ktypeDay, bReload );
	//stock.PrepareData(CStock::dataK, CKData::ktypeMin1, bReload);//extend 1分钟K线 by freeman 
	stock.PrepareData( CStock::dataK, CKData::ktypeMin5, bReload );
	stock.PrepareData( CStock::dataDR, CKData::ktypeDay, bReload );
	if( bReload || stock.GetKDataDay().GetCurFormat() == CKData::formatOriginal )
		//对当日日K线数据进行合并
		stock.GetKDataDay().MergeKData( &(info.m_kdata) );

G:\stock\TskingVS2019\src\Client\StkLib\Src\Stock.cpp

//合并K先数据
	int CStock::MergeKData(CStock & stock, int period)
	{
		CKData* pkdata = NULL;
		CKData* pkdata2 = NULL;
		switch (period)
		{
		case CKData::ktypeMonth:
			pkdata = &(GetKDataMonth());
			pkdata2 = &(stock.GetKDataMonth());
			break;
		case CKData::ktypeWeek:
			pkdata = &(GetKDataWeek());
			pkdata2 = &(stock.GetKDataWeek());
			break;
		case CKData::ktypeDay:
			pkdata = &(GetKDataDay());
			pkdata2 = &(stock.GetKDataDay());
			break;
		case CKData::ktypeMin60:
			pkdata = &(GetKDataMin60());
			pkdata2 = &(stock.GetKDataMin60());
			break;
		case CKData::ktypeMin30:
			pkdata = &(GetKDataMin30());
			pkdata2 = &(stock.GetKDataMin30());
			break;
		case CKData::ktypeMin15:
			pkdata = &(GetKDataMin15());
			pkdata2 = &(stock.GetKDataMin15());
			break;
		case CKData::ktypeMin5:
			pkdata = &(GetKDataMin5());
			pkdata2 = &(stock.GetKDataMin5());
			break;
		case CKData::ktypeMin1://extend 1分钟K线 by freeman
			pkdata = &(GetKDataMin1());
			pkdata2 = &(stock.GetKDataMin1());
			break;
		default:
			SP_ASSERT(FALSE);
			return 0;
		}

		return pkdata->MergeKData(pkdata2);
	}

 

合并K线数据

G:\tmp\stock\TskingVS2019\src\Client\StkLib\Src\AfxCore.cpp

BOOL AfxReloadStock( CStock & stock )
{
	CStockInfo info = stock.GetStockInfo();

	// Reload AfxGetStockMain()
	AfxGetStockContainer().GetStockInfo( info.GetStockCode(), &info );

	stock.Clear( );
	stock.SetStockInfo( &info );
	stock.SetDatabase( &AfxGetDB() );
	stock.PrepareData( CStock::dataK, CKData::ktypeDay );
	
	// Merge New 合并StockInfo的日K线数据
	stock.GetKDataDay().MergeKData( &(info.m_kdata) );
	
	stock.PrepareData( CStock::dataK, CKData::ktypeWeek );
	stock.PrepareData( CStock::dataK, CKData::ktypeMonth );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin1);//extend 1分钟K线 by freeman 加载1分钟数据是否需要性能优化?
	stock.PrepareData( CStock::dataK, CKData::ktypeMin5 );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin15 );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin30 );
	stock.PrepareData( CStock::dataK, CKData::ktypeMin60 );
	return TRUE;
}

 

G:\tmp\stock\TskingVS2019\src\Client\StkLib\Src\Packets.cpp

//实时合并CStockContainer的stockinfo日K线缓存数据数据 
BOOL UpdateStockContainerByKData( CStockContainer &container, LPCTSTR lpszCode, CKData & kdata )
{
	container.Lock();

	int	id = 0;
	if( CKData::ktypeDay == kdata.GetKType()
		&& container.GetStockInfo( lpszCode, NULL, &id ) )
	{
		CStockInfo	& info	=	container.ElementAt(id);
		if( kdata.GetSize() > (int)AfxGetProfile().GetCacheDays() )
		{
			// 只使用AfxGetProfile().GetCacheDays()天的数据
			CKData	temp( kdata.GetKType() );
			for( int i=kdata.GetSize()-AfxGetProfile().GetCacheDays(); i<kdata.GetSize(); i++ )
				temp.Add( kdata.ElementAt(i) );
			//合并当日K线数据 by freeman
			info.m_kdata.MergeKData( &temp );
		}
		else
			//合并当日K线数据 by freeman
			info.m_kdata.MergeKData( &kdata );

		
		// 指数涨跌家数
		int nSize = info.m_kdata.GetSize();
		if( nSize > 0 )
		{
			info.m_dwAdvance	=	info.m_kdata.ElementAt(nSize-1).m_dwAdvance;
			info.m_dwDecline	=	info.m_kdata.ElementAt(nSize-1).m_dwDecline;
		}
	}
	container.UnLock();
	return TRUE;
}

G:\tmp\stock\TskingVS2019\src\Client\StkLib\Src\SelfDB.cpp

//从乾隆文件中加载K线,合并5分钟K线数据 2019 / 06 / 08 by freeman
int CSelfDB::LoadKData(CStock* pstock, int nKType)
{
	// WILLCHECK
	int ret = CQianlong::LoadKData(pstock, nKType);

	if (CKData::ktypeMin5 == nKType && LoadMinute(pstock) > 0)
	{
		CKData	& kdataMin5 = pstock->GetKDataMin5();
		CMinute & minute = pstock->GetMinute();
		CKData kdataNew;
		//分时线转K线
		if (minute.ToKData(kdataNew) && CKData::ktypeMin5 == kdataNew.GetKType())
		{
			//合并5分钟K先数据 
			kdataMin5.MergeKData(&kdataNew);
		}

		return kdataMin5.GetSize();
	}
	return ret;
}

G:\stock\TskingVS2019\src\Client\StkLib\Src\Database.cpp

//加载K线数据时,依次枚举调用各个驱动获取K线数据
int CStDatabase::LoadKData( CStock *pstock, int period )
{
	if( !pstock || !IsOK())	return 0;

	int nSize = m_pStore->LoadKData( pstock, period );

	for( int i=0; i<m_aptrAssistant.GetSize(); i++ )
	{
		IStStore	* pStore	=	(IStStore *)m_aptrAssistant.ElementAt(i);
		CStock	stock;
		stock.SetStockInfo( &(pstock->GetStockInfo()) );
		if( pStore->LoadKData( &stock, period ) > 0 )
		{
			//从不同的数据提供接口中合并K线数据 ,这里需要性能优化 by freeman
			nSize	=	pstock->MergeKData( stock, period );
		}
	}
	return nSize;
}

 

 接收到的分笔数据处理

			// 刷新数据
			BOOL	bNeedReSort = FALSE;
			for (DWORD i = 0; i < pCommPacket->m_dwCount; i++)
			{
				int	id = 0;//股票在容器中的id号
				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);//更新股票容器数据,id为股票在容器中的id号,info 为新的股票信息
					bNeedReSort = TRUE;
				}
			}