策略为王源代码分析-GraphView.cpp-K线图右键弹窗迭加图(DJ)

Published

 

 

ON_COMMAND(ID_VIEW_HIDETHISTECH, OnViewHidethistech)  //日K线图隐藏技术指标的事件
	ON_UPDATE_COMMAND_UI(ID_VIEW_HIDETHISTECH, OnUpdateViewHidethistech)//日K线图隐藏技术指标的事件
	ON_COMMAND(ID_VIEW_THISTECHPARAM, OnViewThistechparam)//日K线图显示某项技术指标的事件
	ON_UPDATE_COMMAND_UI(ID_VIEW_THISTECHPARAM, OnUpdateViewThistechparam)//日K线图显示某项技术指标的事件
	//}}AFX_MSG_MAP

 

第一部分 弹出菜单框

在主程序中,会根据当前视图的类型确定弹出框所包含的菜单项

1.弹出菜单消息

G:\stock\TskingVS2019\src\Client\StkUI\MainFrm.cpp

BEGIN_MESSAGE_MAP(CMainFrame, CTskMainFrame)
	//{{AFX_MSG_MAP(CMainFrame)
	ON_WM_CREATE()
	ON_WM_TIMER()
	ON_WM_SIZE()
	ON_WM_CLOSE()
	ON_WM_INITMENUPOPUP()  //鼠标右键弹出框菜单

WM_INITMENU 对应File Edit View Help四个
WM_INITMENUPOPUP消息对应Open Save Print, Exit子菜单项

WM_INITMENU是窗口主菜单被鼠标点击或用Alt键激活时产生的,WPARAM是窗口主菜单的HMENU。
WM_INITMENUPOPUP是要弹出某个子菜单的时候产生的,WPARAM是窗口要弹出的子菜单的HMENU,LPARAM的低WORD表示要弹出的子菜单在其父菜单中的索引值,从0开始,LPARAM的高WORD如果是1,表示当前操作的是窗口的SYSMENU,如果是0则不是SYSMENU。

这两个消息与子菜单中的菜单项无关。

G:\stock\TskingVS2019\src\Client\StkUI\MainFrm.h

afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu);

参数
hmenuPopup
wParam值.下拉菜单或子菜单的句柄


uPos
lParam低次序字的值.指定一个打开的下拉菜单或子菜单在菜单项中基于0相关联的位置

 

fSystemMenu
lParam高次序字的值.指定是否下拉菜单是窗体菜单(同样大家知道的系统菜单或控制菜单),如果菜单是窗体菜单,

这个参数是TRUE,否则它是FALSE;

	InitMenuPopup(pPopupMenu);
	}

	CTskMainFrame::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}

第二部分 弹出框菜单的消息响应

弹出框菜单的响应分为两种,对没有子菜单项的,用ON_COMMAND()消息来响应;对有子菜单项的,用ON_COMMAND_RANGE()消息来响应。

响应鼠标右键弹出菜单点击子菜单项消息响应事件

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

菜单ID值是连续的,用ON_COMMAND_RANGE来映射消息处理函数,可以在一个函数中处理一个范围内的所有消息。

BEGIN_MESSAGE_MAP(CGraphView, CView)
	//{{AFX_MSG_MAP(CGraphView)
	ON_WM_CREATE()
ON_COMMAND(ID_VIEW_HIDETHISTECH, OnViewHidethistech)  //日K线图弹出框“隐藏该指标(&H)”的事件
	ON_UPDATE_COMMAND_UI(ID_VIEW_HIDETHISTECH, OnUpdateViewHidethistech)//日K线图弹出框“隐藏该指标(&H)”的事件
	ON_COMMAND(ID_VIEW_THISTECHPARAM, OnViewThistechparam)//日K线图弹出框“调整该指标参数(&D)...”的事件
	ON_UPDATE_COMMAND_UI(ID_VIEW_THISTECHPARAM, OnUpdateViewThistechparam)//日日K线图弹出框“调整该指标参数(&D)...”的事件

ON_COMMAND_RANGE(ID_VIEW_TECH_START, ID_VIEW_TECH_END, OnViewTechRange) //技术指标发生变化
	ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_TECH_START, ID_VIEW_TECH_END, OnUpdateViewTechRange)//技术指标发生变化

 

实现函数

//功能:调用AfxGetProfile().ToggleGraphViewTech(nID)更新m_anGraphTechsShow;数组,实际上是添加或删除要显示或隐藏的指标id。然后重新画图。

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

void CGraphView::OnViewTechRange(UINT nCmdID)
{
	if (nCmdID < ID_VIEW_TECH_START || nCmdID > ID_VIEW_TECH_END)
	{
		ASSERT(FALSE);
		return;
	}

	UINT	nID	=	nCmdID - ID_VIEW_TECH_START;
	if (nID > STT_MAX)
		nID = nID-STT_MAX-1+STT_USER_MIN;
	AfxGetProfile().ToggleGraphViewTech(nID);

	CRect	rectClient;
	GetClientRect(&rectClient);
	m_graph.ResetClient(rectClient);
	Invalidate();
}

 

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

//功能:根据子菜单项ID更新m_anGraphTechsShow;数组,实际上是添加或删除要显示或隐藏的指标id
void CStProfile::ToggleGraphViewTech( UINT nTech )
{
	SP_ASSERT( (nTech >= STT_MIN && nTech <= STT_MAX) || nTech >= STT_USER_MIN );

	if( nTech >= STT_KLINE_MIN && nTech <= STT_KLINE_MAX )
	{
		BOOL	bHas	=	FALSE;
		CSPDWordArray	& anKLine	=	GetGraphTechsKLine( );
		for( int k=anKLine.GetSize()-1; k>=0; k-- )
		{
			if( anKLine[k] == nTech )
			{
				anKLine.RemoveAt(k);
				bHas	=	TRUE;
			}
		}
		if( !bHas )
			anKLine.InsertAt( 0, nTech );
	}
	else
	{
		BOOL	bHas	=	FALSE;
		CSPDWordArray	& anShow	=	GetGraphTechsShow( );
		for( int k=anShow.GetSize()-1; k>=0; k-- )
		{
			if( anShow[k] == nTech )
			{
				anShow.RemoveAt(k);
				bHas	=	TRUE;
			}
		}
		if( !bHas )
			anShow.Add( nTech );
	}
}

 

叠加图指标类

StkLib\Src\TechKLine.cpp

/////////////////////////////////////////////////////////////////////
//	CDJ
// K线叠加图 指标实现类 by freeman
CStock		CDJ::m_stockSha;
CStock		CDJ::m_stockSzn;
CSPString	CDJ::m_strCodeOrg;
CDJ::CDJ( )
{
	SetDefaultParameters( );
}

CDJ::CDJ( CKData * pKData )
	: CTechnique( pKData )
{
	SetDefaultParameters( );
}

CDJ::~CDJ()
{
	Clear( );
}

void CDJ::SetDefaultParameters( )
{
	m_strCodeSha	=	STKLIB_CODE_MAIN;
	m_strCodeSzn	=	STKLIB_CODE_MAINSZN;
}

void CDJ::AttachParameters( CDJ & src )
{
	m_strCodeSha	=	src.m_strCodeSha;
	m_strCodeSzn	=	src.m_strCodeSzn;
}

BOOL CDJ::IsValidParameters( )
{
	return ( m_strCodeSha.GetLength() > 0 && m_strCodeSzn.GetLength() > 0 );
}

void CDJ::Clear( )
{
	CTechnique::Clear( );
}

/***
	K线叠加图,准备叠加K线的数据 叠加图 数据准备函数 by freeman
*/
BOOL CDJ::PrepareStockData(CStDatabase * pDatabase, const char * szCodeOrg,
						   int nCurKType, int nCurKFormat, int nCurMaindataType,
						   DWORD dwAutoResumeDRBegin, int nAutoResumeDRLimit )
{
	SP_ASSERT( pDatabase );

	// bReload and kdayMain
	BOOL	bReload	=	(NULL!=szCodeOrg && 0!=strncmp(szCodeOrg,m_strCodeOrg,m_strCodeOrg.GetLength()) );
	m_strCodeOrg	=	szCodeOrg;

	// m_stockSha
	m_stockSha.SetStockCode( CStock::marketSHSE, m_strCodeSha );
	AfxPrepareStockData( pDatabase, m_stockSha, nCurKType, nCurKFormat, nCurMaindataType, FALSE, bReload );

	// m_stockSzn
	m_stockSzn.SetStockCode( CStock::marketSZSE, m_strCodeSzn );
	AfxPrepareStockData( pDatabase, m_stockSzn, nCurKType, nCurKFormat, nCurMaindataType, FALSE, bReload );
	
	return TRUE;
}

 

第三部分 画叠加图

技术指标窗口的绘图方法是和K线窗口的绘图方法是相同的.

 

1.视图 CGraphView 中的OnDraw函数调用m_graph.Redraw函数来绘制相关的图表

OnPaint是WM_PAINT消息的消息处理函数,在OnPaint中调用OnDraw。
OnPaint()是CWnd的类成员,负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,没有响应消息的功能.当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows发送WM_PAINT消息。该视图的OnPaint 处理函数通过创建CPaintDC类的DC对象来响应该消息并调用视图的OnDraw成员函数.OnPaint最后也要调用OnDraw,因此一般在OnDraw函数中进行绘制。

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

/////////////////////////////////////////////////////////////////////////////
// CGraphView drawing

void CGraphView::OnDraw(CDC* pDC)
{
	CStaticDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	CRect rectClient;
	GetClientRect(&rectClient);

	CMemDC memDC(pDC);
	m_graph.Redraw(&memDC, rectClient); //画图

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

void CStockGraph::Redraw(CDC* pDC, CRect rectAll)
{
	ASSERT(m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()));
	if (m_rectAll != rectAll)
		ResetClient(rectAll);
	DrawStock(pDC);
}

2.Redraw函数调用DrawStock来绘制图表

在DrawStock()函数中,

1.DrawKLine(pDC); 负责画日K线和叠加类的指标。

1.DrawTechLine(pDC); 负责画窗口底部的技术指标线。

//画股票K线 by freeman
void CStockGraph::DrawStock(CDC* pDC)
{
	ASSERT(m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()));
	if (!m_pParent || !::IsWindow(m_pParent->GetSafeHwnd()))
		return;

	CClientDC	dc(m_pParent);
	if (NULL == pDC)
		pDC = &dc;

	if (!m_CurStock.GetStockInfo().IsValidStock())
		return;

	CKData& kdata = m_CurStock.GetKData(m_nCurKType);
	if (kdata.GetSize() == 0)
	{
		DrawNoDataInfo(pDC); //股票K线没有数据时,显示获取更多数据请点击菜单“数据->历史数据下载...”。"
	}
	else if (PrepareDrawKLine()) // Reset m_nIndexStart, m_nIndexEnd, m_nIndexCurrent
	{
		DrawDateAxis(pDC); //画K线底部的日期提示框里的文字
		DrawTechLine(pDC); //画窗口底部的技术指标线
		DrawKLine(pDC); //画K线图
		DrawKData(); //画周期K线的左上角信息
	}

	DrawReportRegion(pDC); //画日K线右下角窗口:参数、成本、火焰山、活跃
}
2.1在函数DrawStock中调用DrawTechLine来进行实际技术指标的绘制工作
//画窗口底部的技术指标线 by freeman
void CStockGraph::DrawTechLine(CDC* pDC)
{
	CSPDWordArray& anShow = AfxGetProfile().GetGraphTechsShow();
	for (int k = 0; k < m_arcTech.GetSize(); k++)
	{
		CRect	rect = m_arcTech.ElementAt(k);
		if (k < anShow.GetSize())
		{
			DrawTechLine(pDC, anShow[k], rect, FALSE);
			DrawTechLine(pDC, anShow[k], rect, TRUE);
		}
	}
}
/画窗口底部的技术指标线 by freeman
void CStockGraph::DrawTechLine(CDC* pDC, UINT nTech, CRect rect, BOOL bDrawTitle)
{
	DECLARE_COLOR_DEFINATION

	if (!bDrawTitle)
	{
		pDC->FillSolidRect(&rect, clrBK);
		pDC->FillSolidRect(CRect(m_rectAll.left, rect.top, rect.left - 1, rect.bottom), clrBK);
		pDC->Draw3dRect(&rect, clrBorder, clrBorder);
	}

	switch (nTech)
	{
	case STT_MACD:		DrawTechMACD(pDC, rect, bDrawTitle);		break;
	case STT_MIKE:		DrawTechMIKE(pDC, rect, bDrawTitle);		break;
	case STT_PSY:		DrawTechPSY(pDC, rect, bDrawTitle);		break;
   .

        .

        .
        default:
	{
		UINT nTechUserCount = CTechUser::GetTechUserCount();
		if (nTech >= STT_USER_MIN && nTech <= STT_USER_MIN + nTechUserCount - 1)
			DrawTechUser(nTech, pDC, rect, bDrawTitle);
			
		else
			ASSERT(FALSE);
	}
     }

 

2.2 画日K线和画叠加类的指标。
/画周期K线图 by freeman
void CStockGraph::DrawKLine(CDC* pDC)
{

	// KLine Attribute Rect
	CRect	rectKLineAttrib = m_rectKLineCenter;
	rectKLineAttrib.top -= m_nHeightSubtitle;

	// Draw STT_DJ and STT_CW
	CSPDWordArray& anTech = AfxGetProfile().GetGraphTechsKLine();
	for (int k = 0; k < anTech.GetSize(); k++)
		if (STT_DJ == anTech[k] || STT_CW == anTech[k]) //画K线时 同时画叠加图
			DrawKLineAttribute(pDC, anTech[k], rectKLineAttrib, FALSE, dMin, dMax);

	DrawTechDJ2(pDC,  rectKLineAttrib, FALSE, dMin, dMax);

	// Draw K Line 循环画K线
	for (int k = m_nIndexStart; k <= m_nIndexEnd; k++)
	{
		DrawOneKLine(pDC, k, k, &kdata, dMin, dMax, FALSE);
	}

	// Draw Tech KLine except STT_DJ and STT_CW
	for (int k = 0; k < anTech.GetSize(); k++)    //画除叠加图之外的其他叠加技术指标	
		if (STT_DJ != anTech[k] && STT_CW != anTech[k])
			DrawKLineAttribute(pDC, anTech[k], rectKLineAttrib, FALSE, dMin, dMax);

 

 

5. 然后根据实际的技术指标类型,调用不同的绘图函数来完成不同的技术指标的绘制

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

//画周期K线叠加技术指标 如boll线、叠加图等 by freeman
void CStockGraph::DrawKLineAttribute(CDC* pDC, UINT nTech, CRect rect, BOOL bDrawTitle, double dMin, double dMax)
{
	switch (nTech)
	{
	case STT_MA:		DrawTechMA(pDC, rect, bDrawTitle, dMin, dMax);		break;
	case STT_BBI:		DrawTechBBI(pDC, rect, bDrawTitle, dMin, dMax);		break;
	case STT_BOLL:		DrawTechBOLL(pDC, rect, bDrawTitle, dMin, dMax);		break;
	case STT_PV:		DrawTechPV(pDC, rect, bDrawTitle, dMin, dMax);		break;
	case STT_SAR:		DrawTechSAR(pDC, rect, bDrawTitle, dMin, dMax);		break;
	case STT_DJ:		DrawTechDJ(pDC, rect, bDrawTitle, dMin, dMax);		break; //画叠加图
	case STT_CW:		DrawTechCW(pDC, rect, bDrawTitle, dMin, dMax);		break;

	default:;
		ASSERT(FALSE);
	}
}

 

3.1 光标移动时间处理

void CStockGraph::OnIndexCurrentChanged()
{
	if (m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()))
	{
		CClientDC	dc(m_pParent);

		CRect	rectKLineAttrib = m_rectKLineCenter;
		rectKLineAttrib.top -= m_nHeightSubtitle;
		CSPDWordArray& anTech = AfxGetProfile().GetGraphTechsKLine();
		if (anTech.GetSize() > 0)
			DrawKLineAttribute (&dc, anTech[0], rectKLineAttrib, TRUE, 0, 0);

		CSPDWordArray& anShow = AfxGetProfile().GetGraphTechsShow();
		for (int k = 0; k < m_arcTech.GetSize(); k++)
		{
			CRect	rect = m_arcTech.ElementAt(k);
			if (k < anShow.GetSize() && anShow[k] == STT_MIKE)
			{
				DrawTechLine(&dc, anShow[k], rect, FALSE);
				DrawTechLine(&dc, anShow[k], rect, TRUE);
			}
			else if (k < anShow.GetSize())
			{
				DrawTechLine(&dc, anShow[k], rect, TRUE);
			}
		}

		DrawReportRegion(&dc);
		DrawKData();
	}
}

 

3.在函数DrawStock中调用DrawTechLine来进行实际技术指标的绘制工作

4. 函数

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


   DrawKLineAttribute(CDC* pDC, UINT nTech, CRect rect, BOOL bDrawTitle, double dMin, double dMax)

  画周期K线叠加技术指标 如boll线、叠加图等 

   DrawTechLine(CDC * pDC, UINT nTech, CRect rect, BOOL bDrawTitle )

  来完成实际的绘制工作,也就是给定绘图设备pDC,技术指标类型nTech,特定的窗口位置rect,以及标题bDrawTitle等参数.

 

 

   

 

 

6. 函数DrawTechMACD( CDC * pDC, CRect rect, BOOL bDrawTitle, int nTech, CMACD * pmacd )

   完成实际的技术指标的绘制工作

 

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

//功能:画叠加图函数 CStockGraph::DrawTechDJ()
void CStockGraph::DrawTechDJ( CDC * pDC, CRect rect, BOOL bDrawTitle, double dMin, double dMax )
{
	CHECK_NODATATODRAW
	DECLARE_COLOR_DEFINATION

	CKData	& kdata	=	m_CurStock.GetKData(m_nCurKType);
	m_techparam.dj.AttachParameters( AfxGetProfile().GetTechParameters().dj );

	// Draw Sub Title
	if( bDrawTitle )
	{
		CString	strTitle	=	AfxGetSTTShortName(STT_DJ);
		strTitle	+=	"    ";
		if( m_CurStock.GetStockInfo().IsShangHai() )
			strTitle	+=	m_techparam.dj.m_strCodeSha;
		else if( m_CurStock.GetStockInfo().IsShenZhen() )
			strTitle	+=	m_techparam.dj.m_strCodeSzn;
		DrawTechTitle( pDC, rect.left+5, rect.top+1, strTitle, TA_LEFT | TA_TOP, 14, clrBK, clrTitle );
		return;
	}

	// Prepare Data
	if( !m_techparam.dj.IsValidParameters() )
		m_techparam.dj.SetDefaultParameters( );
	if( !m_techparam.dj.PrepareStockData(	&AfxGetDB(), m_CurStock.GetStockCode(),
							m_nCurKType, m_nCurKFormat, m_nCurMaindataType,
							AfxGetProfile().GetAutoResumeDRBegin(), AfxGetProfile().GetAutoResumeDRLimit() ) )
		return;
	CKData	* pKData = NULL;
	if( m_CurStock.GetStockInfo().IsShangHai() )
		pKData	=	& (	CDJ::m_stockSha.GetKData(m_nCurKType) );
	else if( m_CurStock.GetStockInfo().IsShenZhen() )
		pKData	=	& (	CDJ::m_stockSzn.GetKData(m_nCurKType) );
	else
		return;

	// GetMinMaxInfo
	double	dMinDJ = 0, dMaxDJ = 0;
	CKLine	kline( pKData );
	int	nStartDJ	=	pKData->GetIndexByDate( kdata.ElementAt(m_nIndexStart).m_date );
	int	nEndDJ		=	pKData->GetIndexByDate( kdata.ElementAt(m_nIndexEnd).m_date );
	if( -1 == nStartDJ )	nStartDJ	=	0;
	if( -1 == nEndDJ )		nEndDJ		=	pKData->GetSize()-1;
	if( nStartDJ < 0 || nEndDJ < 0 || !kline.GetMinMaxInfo( nStartDJ, nEndDJ, &dMinDJ, &dMaxDJ ) )
		return;

	// Draw
	for( int nIndex=m_nIndexStart; nIndex<=m_nIndexEnd; nIndex++ )
	{
		DWORD	date	=	kdata.ElementAt(nIndex).m_date;
		int	nIndexDJ	=	pKData->GetIndexByDate( date );
		if( nIndexDJ >= 0 && nIndexDJ < pKData->GetSize() )
		{
			DrawOneKLine( pDC, nIndex, nIndexDJ, pKData, dMinDJ, dMaxDJ, TRUE );
		}
	}
}