如何使用C++编程编写自己的通达信选股插件

0 评论
/
34 阅读
/
10303 字
23 2025-01

通达信插件选股

通达信插件选股功能可能大家都有听说过,有很多牛X的炒股选手,将自己的选股经验,做成程序,以插件的方式集成在炒股软件中使用. 这样子每次选股,复盘,就不用那么麻烦,而且更加有数据依据,不会因为自己的错误判断导致买错股票.

运行截图

选择插件选股功能:

运行截图

插件选股界面:

运行截图

那么插件选股的插件是怎么开发的呢?

在通达信基础的炒股软件中,插件选股的插件是一个 .dll的文件,放到 股票软件文件夹内/Plugin/插件.dll 下面的, 调用的时候,在软件界面上找到插件选股,调用出来,进行选股操作.

插件选股,采用C++的方式进行编程,这种编程比用公式效率更高,同时也增加了安全性,自己的选股方法也不会被泄露. 适合做成软件对外出售,而不用担心自己的选股方法会被泄露.

下面是插件开发的几个关键文件

开发工具

采用Visual Studio 2022 社区版 开发.

测试软件:

  • 通达信 PC客户端
  • 招商证券 PC客户端

插件入口.h头文件

/**************************************************/
/*		PLUGIN动态连接库导出头文件				  */
/**************************************************/

#ifndef PLUGIN_H_
#define PLUGIN_H_

#ifdef PLUGIN_EXPORTS
#define PLUGIN_API extern "C"  __declspec(dllexport)
#else
#define PLUGIN_API extern "C" __declspec(dllimport)
#endif

#include "OutStruct.h"

#pragma pack(push,1)

typedef struct tag_PluginPara	//参数信息的结构定义
{
	char  acParaName[14];		//参数的中文名称
	int   nMin;					//参数最小取值范围
	int   nMax;					//参数最大取值范围
	int   nDefault;				//系统推荐的缺省值
	int   nValue;				//用户定义的值
}PLUGINPARAM;

typedef struct tag_PlugInfo
{
	char  Name[50];				//名称与版本
	char  Dy[30];				//产地
	char  Author[30];			//设计人
	char  Descript[100];		//选股描述
	char  Period[30];			//适应周期
	char  OtherInfo[300];
	short ParamNum;				//0<=参数个数<=4
	PLUGINPARAM ParamInfo[4];	//参数信息,见上
}PLUGIN,*LPPLUGIN;

//回调函数,取数据接口
typedef long(CALLBACK * PDATAIOFUNC)(char * Code,short nSetCode,short DataType,void * pData,short nDataNum,NTime,NTime,BYTE nTQ,unsigned long);
//注册回调函数
PLUGIN_API void  RegisterDataInterface(PDATAIOFUNC pfn);
//得到版权信息
PLUGIN_API void	 GetCopyRightInfo(LPPLUGIN info);
//按最近数据计算(nDataNum为ASK_ALL表示所有数据)
PLUGIN_API BOOL	 InputInfoThenCalc1(char * Code,short nSetCode,int Value[4],short DataType,short nDataNum,BYTE nTQ,unsigned long unused);
//选取区段计算
PLUGIN_API BOOL	 InputInfoThenCalc2(char * Code,short nSetCode,int Value[4],short DataType,NTime time1,NTime time2,BYTE nTQ,unsigned long unused); 


#pragma pack(pop)
#endif


插件实现CPP

#include "stdafx.h"
#include "stdio.h"
#include "DataProcessing.h"
#include "StockSelector.h"
#include "StockAnalysis.h"
#include "Common.h"
#define PLUGIN_EXPORTS
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}

PDATAIOFUNC	 g_pFuncCallBack;
//数据文件夹
char dataFolderPath[MAX_PATH] = { 0 };
//获取回调函数
void RegisterDataInterface(PDATAIOFUNC pfn)
{
	g_pFuncCallBack = pfn;
}
/*

在股票交易策略中,箱体突破(Breakout)是一种常用的技术分析方法,
用来识别股票价格突破某一特定价格范围(箱体)的可能性。
在C++中实现这一策略,你可以通过以下几个步骤来完成:

确定箱体的边界:首先,你需要确定箱体的上界和下界。
这通常基于过去一段时间内的最高价和最低价。

监控价格:持续监控股票的当前价格。

检测突破:当股票价格突破箱体的上界或下界时,认为发生了突破。

执行交易:根据突破的方向(向上或向下),执行相应的买入或卖出操作。

下面是一个简单的C++示例,展示如何实现这一逻辑:

*/
//注册插件信息
void GetCopyRightInfo(LPPLUGIN info)
{
	//填写基本信息
	strcpy(info->Name,"筛选箱体突破股");
	strcpy(info->Dy,"地域");
	strcpy(info->Author,"软件开发大郭(GrabByte.com)");//插件作者
	strcpy(info->Period,"箱体突破");
	strcpy(info->Descript,"辅助复盘的箱体突破插件");//选股对象描述
	strcpy(info->OtherInfo,"筛选箱体突破停股");

	//调用前,清空日志
	LogFileClear();
	// 获取当前可执行文件所在的文件夹路径
	GetExecutableFolder(dataFolderPath, sizeof(dataFolderPath));

	//填写参数信息
	info->ParamNum = 2;
	strcpy(info->ParamInfo[0].acParaName,"无涨停日");
	info->ParamInfo[0].nMin= 1;
	info->ParamInfo[0].nMax= 200;
	info->ParamInfo[0].nDefault= 20;
	strcpy(info->ParamInfo[1].acParaName,"股价小于");
	info->ParamInfo[1].nMin=1;
	info->ParamInfo[1].nMax=1000;
	info->ParamInfo[1].nDefault=21;
}


//1.	Code为股票代码,如申请上证指数数据则赋值为999999
//2.	nSetCode为市场分类,0为深市,1为沪市
//3.	DataType为申请数据类型,缺省为日K线历史数据,如申请行情数据则赋值为REPORT_DAT2,其他相关类型参见OutStruct.h
//4.	pData为申请数据缓冲区,若为NULL且nDataNum为 - 1则函数返回历史数据个数
//5.	nDataNum为申请数据个数,若为 - 1且pData为NULL则函数返回历史数据个数
//6.	2个Ntime为申请数据的时间范围,缺省为全部本地历史数据
//7.	nTQ是否为精确除权

//选股条件判断函数 默认调用此函数,使用全部本地历史数据
BOOL InputInfoThenCalc1(char* Code, short nSetCode, int Value[4], short DataType, short nDataNum, BYTE nTQ, unsigned long unused) //按最近数据计算
{
    //处理全部历史数据
    NTime tmpTime = { 0 };
    return processData(Code, nSetCode, Value, DataType, nDataNum, tmpTime, tmpTime, nTQ, unused);
}
//指定日期段的时候,调用此函数进行选股
BOOL InputInfoThenCalc2(char* Code, short nSetCode, int Value[4], short DataType, NTime time1, NTime time2, BYTE nTQ, unsigned long unused)  //选取区段
{
    //处理时间段的历史数据
    //数据个数
    long nDataNum = g_pFuncCallBack(Code, nSetCode, DataType, NULL, -1, time1, time2, nTQ, 0);
    //数据处理
    return processData(Code, nSetCode, Value, DataType, nDataNum, time1, time2, nTQ, unused);
}



DataProcessing.h

#pragma once
// DataProcessing.h
#ifndef DATA_PROCESSING_H
#define DATA_PROCESSING_H
#include "Common.h"
BOOL processData(char* Code, short nSetCode, int Value[4], short DataType, short nDataNum, NTime time1, NTime time2, BYTE nTQ, unsigned long unused);
LPHISDAT allocateHisDat(short nDataNum);
long fetchHistoricalData(char* Code, short nSetCode, short DataType, LPHISDAT pHisDat, short nDataNum, NTime time1, NTime time2, BYTE nTQ);
#endif


DataProcessing.cpp

// DataProcessing.cpp
#include "DataProcessing.h"
#include "Common.h"
#include <cstdio>
#include <cstring>
#include <memory>

#include <iostream>
#include <vector>
#include <algorithm>


/*******************************************************/
extern PDATAIOFUNC	 g_pFuncCallBack;
LPHISDAT allocateHisDat(short nDataNum) {
    return new HISDAT[nDataNum];
}

long fetchHistoricalData(char* Code, short nSetCode, short DataType, LPHISDAT pHisDat, short nDataNum, NTime time1, NTime time2, BYTE nTQ) {
    return g_pFuncCallBack(Code, nSetCode, DataType, pHisDat, nDataNum, time1, time2, nTQ, 0);
}
// 计算成交量变化率
std::vector<float> calculateVolumeChangeRate(const HISDAT* pHisDat, long readnum) {
    std::vector<float> volumeChangeRate(readnum);
    for (long i = 1; i < readnum; ++i) {
        float prevVolume = pHisDat[i - 1].fVolume;
        volumeChangeRate[i] = (prevVolume != 0) ? ((pHisDat[i].fVolume - prevVolume) / prevVolume) * 100.0f : 0.0f;
    }
    return volumeChangeRate;
}

// 检测最近N日的下跌趋势
bool hasSignificantDowntrend(const std::vector<float>& prices, int nDays) {
    int downDays = 0;
    int requiredDownDays = static_cast<int>(nDays * 0.80); // 80%的天数都是下跌趋势

    for (int i = prices.size() - nDays; i < prices.size(); ++i) {
        if (prices[i] < prices[i - 1]) {
            downDays++;
        }
    }
    return downDays >= requiredDownDays;
}

// 检测突然放量
bool isSuddenVolumeIncrease(const std::vector<float>& volumeChangeRate, float threshold) {
    return volumeChangeRate.back() > threshold;
}

// 计算枢轴点和支撑位、压力位
void calculatePivotPoints(float high, float low, float close, float& pivot, float& support1, float& support2, float& resistance1, float& resistance2) {
    pivot = (high + low + close) / 3.0f;
    support1 = (2 * pivot) - high;
    support2 = pivot - (high - low);
    resistance1 = (2 * pivot) - low;
    resistance2 = pivot + (high - low);
}

// 主力潜伏检测函数
bool detectMainForcePresence(const HISDAT* pHisDat, long readnum, const std::vector<float>& prices, int nDays) {
    // 检查成交量变化率
    std::vector<float> volumeChangeRate = calculateVolumeChangeRate(pHisDat, readnum);
    float volumeThreshold = 100.0f; // 设置一个阈值,例如100%

    // 检测最近N日的下跌趋势
    if (readnum >= nDays && !hasSignificantDowntrend(prices, nDays)) {
        return false;
    }

    // 检测突然放量
    return isSuddenVolumeIncrease(volumeChangeRate, volumeThreshold);
}


BOOL processData(char* Code, short nSetCode, int Value[4], short DataType, short nDataNum, NTime time1, NTime time2, BYTE nTQ, unsigned long unused) {
    BOOL nRet = FALSE;
    std::unique_ptr<HISDAT[]> pHisDat(new HISDAT[nDataNum]);
    std::unique_ptr<STOCKINFO> pStockInfo(new STOCKINFO());
    static char logMessage[1024] = { 0 };

    // 获取股票基本信息
    long readnum1 = g_pFuncCallBack(Code, nSetCode, STKINFO_DAT, pStockInfo.get(), 1, time1, time2, 0, 0);
    if (readnum1 <= 0 || !pStockInfo) {
        return FALSE;
    }

    // 排除名称中包含 "ST" 的股票
    if (strstr(pStockInfo->Name, "ST") != nullptr) {
        return FALSE;
    }

    // 获取历史数据
    long readnum = g_pFuncCallBack(Code, nSetCode, DataType, pHisDat.get(), nDataNum, time1, time2, nTQ, 0);
    if (readnum <= 0 || pHisDat[readnum - 1].Close == 0) {
        return FALSE;
    }

    // 获取收盘价数据
    std::vector<float> prices(readnum);
    for (long i = 0; i < readnum; ++i) {
        prices[i] = pHisDat[i].Close;
    }

    // 使用 Value[0] 作为最近N日的交易日
    int nDays = Value[0];

    // 检测主力潜伏
    if (detectMainForcePresence(pHisDat.get(), readnum, prices, nDays)) {
        sprintf(logMessage, "[processData] 检测到主力潜伏 股票代码: %s 股票名称: %s", Code, pStockInfo->Name);
        LogMessage(logMessage);
        nRet = TRUE;
    }

    // 计算最近一天的高点、低点和收盘价
    float high = pHisDat[readnum - 1].High;
    float low = pHisDat[readnum - 1].Low;
    float close = pHisDat[readnum - 1].Close;

    // 计算枢轴点和支撑位、压力位
    float pivot, support1, support2, resistance1, resistance2;
    calculatePivotPoints(high, low, close, pivot, support1, support2, resistance1, resistance2);

    // 记录支撑位和压力位,并包括股票代码和股票名称
    sprintf(logMessage, "股票代码: %s 股票名称: %s 支撑位1: %.2f, 支撑位2: %.2f, 压力位1: %.2f, 压力位2: %.2f",
        Code, pStockInfo->Name, support1, support2, resistance1, resistance2);
    LogMessage(logMessage);

    return nRet;
}
    暂无数据