关于炒股软件——金魔方炒股软件的Dll外挂开发
2015-01-19 14:40:04
金魔方平台是由飞狐交易师原创团队集多年研发经验,依靠和讯财经网强大资源,吸取国际专家思路而推出的十年巨作。目前新出的这个2.0版,这一版在数据存储方面作很大的改进,同等条件的沪深数据,飞狐和大交易师的存储空间要2.4G多,而现在的金魔方只要350M的存储空间,同一个指标选股飞狐大交易师要几十秒钟才有结果,金魔方只要几秒钟就有结果,并且没有数据接收的困扰。金魔方软件是一款具有划时代意义的量化投资平台软件。它强大易用的策略编写平台不但能编写公式指标,更能轻松自如地编写涉及资金管理、仓位控制的交易策略,充分表达您的投资思路和交易模型;它能在无限接近真实市场的模拟环境中全面细致地测评,从而帮助您研发、验证、挑选最可能在将来的真实市场中稳健盈利的交易策略;它的模拟交易系统能让你在私密环境中模拟测评你的策略;它的自动交易模块能让交易策略自动执行,从而助您克服人性弱点、突破生理极限,系统执行您的投资思想; 最后,它能使您非常便捷地任意组合不同策略,让它们互相配合、抵消风险,实现长期稳健盈利。它在量化投资的所有环节给予您强大帮助,使量化投资变得简单易行。从而使您能轻松运用量化投资,克服人性弱点、理性决策、健康投资。
目前大部分只知道如何使用金魔方,但是作为一名IT界的成员,我们更关心的是如何使用金魔方提供的金语言以及其提供的各种其它语言的接口来操作其金融数据,其提供的接口是开放的,数据是免费的,无须注册。那么下面我们将一起来看看其相关的数据结构以及接口情况。
以下是金魔方的主页面:
从上面的界面我们可以看到,中间是K线,左边是公式编辑器,它的公式是采用自带的金语言进行编写的,我们先来介绍一下它自带的金语言:
金语言参考手册
一. 指标。
用金语言所编写的程序称之为“指标”。
二. 金语言指标的类型
金语言能够生成五类指标。分别是:
1) 技术指标
2) 条件选股指标
3) 五彩K线指标
4) 自定义函数
5) 智能策略
技术指标:对证券数据进行计算、统计后得出的数据集合。通常需要显示在软件图表上。
条件选股:是一类特别的技术指标,其特征是只允许输出一个变量,而且这个变量必须是布尔值。
五彩K线指标:是一类特别的技术指标,它的用途很简单,就是用于控制K线的颜色。所以这类指标也只输出一个变量。这个变量存放的是K线的颜色。
自定义函数:用户把一些常用的计算提炼出来,以方便以后反复使用。
智能策略:能够直接作出交易动作的指标。
三. 金语言基础元素
3.1 数据是怎样计算的
序列
序列是很特别的一类数据。它跟数组很相像:可以通过下标访问。
序列跟数组的区别:
数组在声明时已经定义好元素数量,之后不会改变,而序列在声明时并不指明元素数量。它的数量由K线数量决定,随着行情进行,K线不断增加,序列也相应的自动增加,并且是一一对应。
序列的下标:金语言中,序列下标0代表最近的一根K线所对应的序列数据;下标1代表前一根K线所对应的数据。
单值
单值是除序列之外的所有数据类型。单值可以是数字,也可以是字符串,甚至可以是定长数组。
内置序列
金语言内置了常用的行情序列数据。包括:开、高、低、收、量、额、日期、时间、持仓等。
计算模式
金语言有两种计算模式。逐行计算和逐根计算。
逐行计算模式下,每次计算都对整个序列从头到尾重新计算其值。
逐根计算模式下,每次计算时只计算序列最新一根的值—-第一次载入图表时除外,此时会从头到尾对每一根K线进行逐根计算。
其中,智能策略和用户自定义函数只能运行在逐根模式下。
3.2 变量的声明
变量通过input(输入参数)、variable进行声明。
3.3 表达式
变量通过运算组成表达式。
3.4 输出
所谓输出,是指标运行时显示数据,输出数据。
金语言有多种输出方式。
1. 通过“:”操作符或Plot函数输出。
2. 通过画线函数输出。
3. 通过Explain、Print、Comment、声音函数、Email等函数输出。
3.5 控制语句
控制语句控制程序的执行。包括分支、循环等。
金语言有以下控制语言:
if、switch(case)、for、while、repeat
3.6 指标引用
技术指标能够通过refindi函数引用另一个指标的输出。
refindi只工作在逐行模式下。
3.7 数据引用
默认提供的高低收量额等数据对应于当前图表上的品种和周期。如果要使用非当前图表上的品种、周期的数据,可以通过refData函数取得。
3.8 自定义函数
自定义函数能够很方便的扩展金语言,既比使用DLL这样的手段简单,运行效率又比引用指标的方式高。
自定义函数只能工作在逐根模式下。
自定义函数通常这样使用:
1, 计算后返回一个值
2, 计算并填充引用参数。引用参数是自定义函数特殊的输入参数,能够在函数中计算,并把值带出函数。
自定义函数所声明的variable变量的值会保留,并能够在下一次计算时使用。
四. 金语言与金魔方
金语言在金魔方中编写、编译;
指标能显示在金魔方图表上或用于选股、预警。
智能策略指标编写之后,可以在金魔方中测评;
最后,智能策略可以通过金魔方实盘运行,并做出交易动作。
五. 金语言的扩展(用户DLL的接入)
金语言允许用户通过DLL增强金语言的能力。
金语言的DLL接口非常强大,相比之下,一般股软都预先限定DLL的输入参数,金语言则同时支持限定输入参数的方式,也支持用户描述的方式。
也就是说,用户只需在金语言中撰写函数描述,就能自由使用任意DLL函数包括,例如Windows Api中的函数。前提是这些函数用到的数据类型在金语言的支持之列——毕竟,金语言并未支持结构、指针等数据类型。
在介绍完金语言之后,我们正式进入金魔方平台的开发环节,首先我们点击程式交易工具栏进入功式编辑器,如图2所示:
公式编辑器中,开发人员可以直接使用它提供的金语言进行逻辑的编写,也可以采用dll外挂的方式来编写,上面图2 我就是采用dll的外挂形式来进行程序编写,dll外挂是指采用c或c++等高级语言编写函数,最后编译成dll的形式,然后用金语言调用,图2的公式中,FOXFUNC是指dll的命名,TRADE_FIRST是指dll中编写的函数名称,var1是指函数的返回值,这个返回值可以是一个数或是一串数字。drawline是指根据返回值的序列来连接各个k线的点,其中返回的序列下标是和每一条k线的编号相对应的。
上在描述了具体的dll调用过程,下面来看看,如何调用金魔方的API接口以及金魔方的基本数据结构。
/* /////////////////////////////////////////////////////////////////////////// 飞狐交易师“C语言接口”扩展程序调用接口规范V3.0 1.本规范适用于飞狐交易师V3.x公式系统. 2.扩展函数用于实现系统函数不能实现的特殊算法. 3.扩展函数用windows 32位动态连接库实现,建议使用Microsoft Visual C++编程. 4.调用时在公式编辑器中写"动态库名称@函数名称"(参数表)即可,例如下面函数可以写为"FOXFUNC@MYMACLOSE"(5) 5.动态连接库名称和函数名称可以自己定义. 6.使用时必须将动态库拷贝到飞狐交易师安装目录下的FmlDLL子目录下使用. */
1 //分析周期 2 //////////////////////////////////////////////////// 3 enum DATA_TYPE 4 { 5 TICK_DATA=2, //分笔成交 6 MIN1_DATA, //1分钟线 7 MIN5_DATA, //5分钟线 8 MIN15_DATA, //15分钟线 9 MIN30_DATA, //30分钟线 10 MIN60_DATA, //60分钟线 11 DAY_DATA, //日线 12 WEEK_DATA, //周线 13 MONTH_DATA, //月线 14 YEAR_DATA, //年线 15 MULTIDAY_DATA, //多日线 16 MULTIMIN_DATA, //多分钟线 17 MULTISEC_DATA //多秒线 18 }; 19 20 //////////////////////////////////////////////////// 21 //基本数据 22 //////////////////////////////////////////////////// 23 24 typedef struct tagSTKDATA 25 { 26 int m_time; //时间,UCT 27 //time_t m_time; 28 float m_fOpen; //开盘 29 float m_fHigh; //最高 30 float m_fLow; //最低 31 float m_fClose; //收盘 32 float m_fVolume; //成交量(手) 33 float m_fAmount; //成交额(元)/持仓(未平仓合约,仅期货有效) 34 WORD m_wAdvance; //上涨家数(仅大盘有效) 35 WORD m_wDecline; //下跌家数(仅大盘有效) 36 } STKDATA; 37 38 //////////////////////////////////////////////////// 39 //扩展数据,用于描述分笔成交数据的买卖盘 40 //////////////////////////////////////////////////// 41 42 typedef union tagSTKDATAEx 43 { 44 struct 45 { 46 float m_fBuyPrice[3]; //买1--买3价 47 float m_fBuyVol[3]; //买1--买3量 48 float m_fSellPrice[3]; //卖1--卖3价 49 float m_fSellVol[3]; //卖1--卖3量 50 DWORD m_dwToken; //成交方向 51 }; 52 float m_fDataEx[13]; 53 } STKDATAEx; 54 55 //////////////////////////////////////////////////// 56 //除权数据 57 //////////////////////////////////////////////////// 58 59 typedef struct tagSPLITDATA 60 { 61 //timetype m_time; //时间,UCT 62 time_t m_time; 63 float m_fHg; //红股 64 float m_fPg; //配股 65 float m_fPgj; //配股价 66 float m_fHl; //红利 67 } SPLITDATA; 68 69 70 //////////////////////////////////////////////////// 71 /*财务数据顺序(m_pfFinData内容) 72 73 序号 内容 74 75 0 总股本(万股), 76 1 国家股, 77 2 发起人法人股, 78 3 法人股, 79 4 B股, 80 5 H股, 81 6 流通A股, 82 7 职工股, 83 8 A2转配股, 84 9 总资产(千元), 85 10 流动资产, 86 11 固定资产, 87 12 无形资产, 88 13 长期投资, 89 14 流动负债, 90 15 长期负债, 91 16 资本公积金, 92 17 每股公积金, 93 18 股东权益, 94 19 主营收入, 95 20 主营利润, 96 21 其他利润, 97 22 营业利润, 98 23 投资收益, 99 24 补贴收入, 100 25 营业外收支, 101 26 上年损益调整, 102 27 利润总额, 103 28 税后利润, 104 29 净利润, 105 30 未分配利润, 106 31 每股未分配, 107 32 每股收益, 108 33 每股净资产, 109 34 调整每股净资, 110 35 股东权益比, 111 36 净资收益率 112 37 经营现金流入 113 38 经营现金流出 114 39 经营现金流量 115 40 投资现金流入 116 41 投资现金流出 117 42 投资现金流量 118 43 筹资现金流入 119 44 筹资现金流出 120 45 筹资现金流量 121 46 现金及等价物 122 47 应收帐款周转率 123 48 存货周转率 124 49 股东总数 125 50 发行价 126 51 速动比率 127 52 主营业务增长率 128 53 税后利润增长率 129 54 净资产增长率 130 55 总资产增长率 131 */ 132 133 //////////////////////////////////////////////////// 134 //调用参数项结构 135 //////////////////////////////////////////////////// 136 typedef struct tagCALCPARAM 137 { 138 union 139 { 140 const float m_fParam; //数值参数 141 const float* m_pfParam; //序列参数,指向一个浮点型数组 142 const char* m_szParam; //字符串参数 143 }; 144 //序列参数有效起始位置 145 const int m_nParamStart; 146 }CALCPARAM; 147 148 149 //////////////////////////////////////////////////// 150 //调用接口信息数据结构 151 //////////////////////////////////////////////////// 152 typedef struct tagCALCINFO 153 { 154 const DWORD m_dwSize; //结构大小 155 const DWORD m_dwVersion; //调用软件版本(V2.10 : 0x210) 156 const DWORD m_dwSerial; //调用软件序列号 157 //const char* m_strUserName; //软件用户名 158 const char* m_strStkLabel; //股票代码 159 const BOOL m_bIndex; //大盘 160 161 const int m_nNumData; //数据数量(pData,pDataEx,pResultBuf数据数量) 162 const STKDATA* m_pData; //常规数据,注意:当m_nNumData==0时可能为 NULL 163 const STKDATAEx* m_pDataEx; //扩展数据,分笔成交买卖盘,注意:可能为 NULL 164 165 const int m_nParam1Start; //参数1有效起始位置 166 const float* m_pfParam1; //调用参数1 167 const float* m_pfParam2; //调用参数2 168 const float* m_pfParam3; //调用参数3 169 const float* m_pfParam4; //调用参数4 170 171 float* m_pResultBuf; //结果缓冲区 172 const int m_dataType; //数据类型 173 const float* m_pfFinData; //财务数据 174 175 //以上与分析家兼容,所以沿用其结构和名称 176 177 //以下为飞狐交易师扩展 178 179 const DWORD m_dwReserved; // 保留 180 181 const int m_nNumParam; // 调用参数数量 182 const CALCPARAM* m_pCalcParam; // 调用参数数组 183 184 const DWORD m_dwReservedEx[4]; // 保留 185 186 char* m_strStkName; //股票名称 187 188 SPLITDATA* m_pSplitData; //除权数据 189 int m_nNumSplitData; //除权次数 190 191 } CALCINFO;
上面展示了金魔方的数据结构,数据结构主要是存取金融数据的,下面展示下我们自己定义的函数,这个函数也就是金魔方要调用的函数。
extern "C"__declspec(dllexport) int WINAPI TRADE_FIRST(CALCINFO *pData);
上面的结构体中CALCINFO *pData表示输入的数据,具体的输入输出如下:
/*函数输入 注1: (与分析家兼容) 1.函数调用参数由m_pfParam1--m_pfParam4带入,若为NULL则表示该参数无效. 2.当一个参数无效时,则其后的所有参数均无效. 如:m_pfParam2为NULL,则m_pfParam3,m_pfParam4一定为NULL. 3.参数1可以是常数参数或序列数参数,其余参数只能为常数参数. 4.若m_nParam1Start<0, 则参数1为常数参数,参数等于*m_pfParam1; 5.若m_nParam1Start>=0,则参数1为序列数参数,m_pfParam1指向一个浮点型数组, 数组大小为m_nNumData,数据有效范围为 m_nParam1Start 至 m_nNumData-1. 在时间上m_pData[x] 与 m_pfParam1[x]是一致的 注2: (飞狐交易师扩展) 1.该扩展结构使调用参数在技术上可以是无限数目的,且每个参数都可为数值或序列,由公式中实际的调用参数决定。 2.CALCPARAM结构用于带入参数信息和实际数据,m_pCalcParam数组大小为m_nNumParam,数据有效范围为 0 至 m_nNumParam-1. 3.按参数的顺序,m_pCalcParam[0]为第一个参数的数据,m_pCalcParam[1]为第二个参数的数据...,为了保持兼容,原m_nParam1Start、m_pfParam1等5个属性依然有赋值。 4.若 i位置的参数为数值,取用m_pCalcParam[i].m_fParam. 5.若 i位置的参数为序列,取用m_pCalcParam[i].m_pfParam,数组大小为m_nNumData,数据有效范围为m_pCalcParam[i].m_nParamStart 至 m_nNumData-1. 若m_pCalcParam[i].m_nParamStart<0, 则此数组中无有效数据。 6.由于可以调用多个序列,许多序列的计算可以先在公式中进行,然后作为调用的参数即可。 7.经此扩展,对分析家的DLL依然可以调用、兼容。 */ /////////////////////////////////////////////////////////////////////////////////// /* 函数输出 __declspec(dllexport) int xxxxxxxx(CALCINFO* pData); 1.函数名称需全部大写. 2.函数必须以上述形式声明,请用实际函数名称替代xxxxxxxx; 对于C++程序还需包括在 extern "C" { } 括号中. 3.函数计算结果用pData->m_pResultBuf带回. 4.函数返回-1表示错误或全部数据无效,否则返回第一个有效值位置,即: m_pResultBuf[返回值] -- m_pResultBuf[m_nNumData-1]间为有效值. 5.函数名称长度不能超过15字节,动态连接库文件名不能超过9字节(不包括扩展名),动态库名称不能叫SYSTEM,EXPLORER */
下面看一下我如何在dll主函数中调用相关的输入与最后的输出返回值
extern "C"__declspec(dllexport) int WINAPI TRADE_FIRST(CALCINFO *pData) { vector<struct info>value;//存放整合之合的数据点 value.reserve(50000); vector<struct up_down>up_down_value;//存放没有过滤前的顶,底点 up_down_value.reserve(50000); vector<struct up_down>filter_value;//存放最后的顶和底数据,连成笔 filter_value.reserve(50000); //vector<struct line_series>series;//存放整合之合的数据点,特征序列 //series.reserve(50000); //series.clear(); vector<struct line_series>big_line_series;//存放最后的数据 big_line_series.reserve(50000); big_line_series.clear(); if ( pData->m_pfParam1 && //参数1有效 pData->m_nParam1Start<0 && //参数1为常数 pData->m_pfParam2==NULL ) //仅有一个参数 { float fParam = *pData->m_pfParam1; int flag = (int)fParam; //参数1 while (pData->m_nNumData<4) { return -1; } if(flag>0) { if(pData->m_nNumData<100) return -1; comb_data(pData,value);//进行数据合并,value存储合并之后的数据 find_up_down(value,up_down_value);//找到顶底数据,存在up_down_value中 filter_up_down(up_down_value,filter_value,value); //过滤数据,找到笔 //feature_series(filter_value,series);//得到所有的向上的特征序列及向下的特征序列 //preidct(filter_value,big_line_series); for(int j=0;j<pData->m_nNumData;j++) { pData->m_pResultBuf[j]=0; } for(int i=0;i<filter_value.size();i++) { pData->m_pResultBuf[filter_value[i].data.node_no]=filter_value[i].data.flag;//返回序列,笔序列 } return 0; } } return -1; }
上面的pData为输入、输出数据结构体,在输入的时候,调用m_pdata数组就可以读入任意时间段的数据,最后数据返回的时候,再写入到m_pResult数组中,将结果带回。至于如何生成dll文件这里就不多讲,我采用的是vs2010,编译直接写入dll文件中。需要提醒的是,dll文件写入后要放入金魔方的FmlDll文件中。调试的时候也可以将debug调试器连接到金魔方.exe文件,这样就可以实时写入金魔方数据,最后结果也直接带入到金魔方中进行显示。