DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: cdk19821
今日帖子: 30
在线用户: 13
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/7 8:37:36
标题:
求教,关于大智慧调用的dll 浏览:17684
加入我的收藏
楼主: 大智慧用c写的,c++还是标准c就不清楚了。网上搜索的资料显示它可以在公式中调用用户自己编写的dll来进行数据处理。而且我也下载了网友提供的,vc6下编写的dll并且证明能够正常使用。但是我用delphi来编写dll,好像完全不能用。像下面这样简单的,也完全没结果。

求教一下,如果希望用delphi来编写dll,而让一个c编的现成的程序来调用,应该怎样做。


library MYDLL;

uses
  SysUtils,
  Classes;

{$R *.RES}

function TTT():integer;cdecl;// stdcall;  stdcall和cdecl都没效果
var
  tfile:TextFile;
begin
  AssignFile(tfile,'c:\a.txt');
  Rewrite(tfile);
  Writeln(tfile,'Work');
  CloseFile(tfile);
  result:=0;
end;

exports
  TTT index 1;

begin
end.
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2010/11/7 10:31:52
1楼: 你至少要知道,宿主程式會調用哪個函數。函數名是甚麼、參數是怎麼定義的。
----------------------------------------------
--
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/7 14:24:05
2楼: 按此在新窗口浏览图片 在大智慧里面,只需要在公式里面编写类似“MYDLL@TTT”这样的语句,就可以按照某结构传出数据,调用我mydll.dll里面的ttt函数,取得返回值。数据的结构有c的头文件,应该可以转成delphi的record,但是目前连数据都没有使用,只是想试着如果调用了我的dll,那就在我自己的dll里面写个文本来证明调用过,结果是没有反应。


网上搜了一下资料,好像是说c和delphi生成的dll的格式有一些不一样,但我是新手,具体原因还不了解,希望各位大侠能给个提示。
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2010/11/7 15:08:54
3楼: 你最好帖出完整的C语言的头文件出来。

DLL中,函数名是大小写敏感的。
到底是TTT还是ttt呢?


对于动态库来说,与开发平台无关的,至于WIN系统怎么去调用,只要你不是自己去模拟一次getprocaddress就不要去管格式如何

你要管的只是函数名,参数,调用约定等,这些信息才是你要关心的
----------------------------------------------
--
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/7 15:53:50
4楼: 头文件如下:

#ifndef __FXJFUNC_H_INCLUDE
#define __FXJFUNC_H_INCLUDE

/*
//////////
分析家扩展函数规范V3.10
1.本规范适用于分析家3.10标准版和专业版公式系统.
2.扩展函数用于实现系统函数不能实现的特殊算法.
3.扩展函数用windows 32位动态连接库实现,建议使用Microsoft Visual C++编程.
4.调用时在公式编辑器中写"动态库名称@函数名称"(参数表)即可,例如下面函数可以写为"FXJFUNC@MYCMALOSE"(5)
5.动态连接库名称和函数名称可以自己定义.
6.使用时可以将动态库拷贝到分析家目录下使用.
*/

#ifdef __cplusplus
extern "C"
{
#endif //__cplusplus

//////////
//分析周期
enum DATA_TYPE
{
  TICK_DATA=2,        //分笔成交
  MIN1_DATA,          //1分钟线
  MIN5_DATA,          //5分钟线          
  MIN15_DATA,          //15分钟线
  MIN30_DATA,          //30分钟线
  MIN60_DATA,          //60分钟线
  DAY_DATA,          //日线
  WEEK_DATA,          //周线
  MONTH_DATA,          //月线
  MULTI_DATA          //多日线
};

//////////
//基本数据

typedef struct tagSTKDATA  
{
  time_t  m_time;      //时间,UCT
  float  m_fOpen;    //开盘
  float  m_fHigh;    //最高
  float  m_fLow;      //最低
  float  m_fClose;    //收盘
  float  m_fVolume;    //成交量
  float  m_fAmount;    //成交额
  WORD  m_wAdvance;    //上涨家数(仅大盘有效)
  WORD  m_wDecline;    //下跌家数(仅大盘有效)
} STKDATA;


//////////
//扩展数据,用于描述分笔成交数据的买卖盘

typedef union tagSTKDATAEx
{
  struct
  {
    float m_fBuyPrice[3];    //买1--买3价
    float m_fBuyVol[3];      //买1--买3量
    float m_fSellPrice[3];    //卖1--卖3价  
    float m_fSellVol[3];    //卖1--卖3量
  };
  float m_fDataEx[12];      //保留
} STKDATAEx;

//////////
/*财务数据顺序(m_pfFinData内容)

  序号  内容

  0  总股本(万股),
  1  国家股,
  2  发起人法人股,
  3  法人股,
  4  B股,
  5  H股,
  6  流通A股,
  7  职工股,
  8  A2转配股,
  9  总资产(千元),
  10  流动资产,
  11  固定资产,
  12  无形资产,
  13  长期投资,
  14  流动负债,
  15  长期负债,
  16  资本公积金,
  17  每股公积金,
  18  股东权益,
  19  主营收入,
  20  主营利润,
  21  其他利润,
  22  营业利润,
  23  投资收益,
  24  补贴收入,
  25  营业外收支,
  26  上年损益调整,
  27  利润总额,
  28  税后利润,
  29  净利润,
  30  未分配利润,
  31  每股未分配,
  32  每股收益,
  33  每股净资产,
  34  调整每股净资,
  35  股东权益比,
  36  净资收益率
*/

//////////
//函数数据结构

typedef struct tagCALCINFO
{
  const DWORD      m_dwSize;        //结构大小
  const DWORD      m_dwVersion;      //调用软件版本(V2.10 : 0x210)
  const DWORD      m_dwSerial;        //调用软件序列号
  const char*      m_strStkLabel;      //股票代码
  const BOOL      m_bIndex;        //大盘

  const int      m_nNumData;        //数据数量(pData,pDataEx,pResultBuf数据数量)
  const STKDATA*    m_pData;        //常规数据,注意:当m_nNumData==0时可能为 NULL
  const STKDATAEx*  m_pDataEx;        //扩展数据,分笔成交买卖盘,注意:可能为 NULL

  const int      m_nParam1Start;      //参数1有效位置
  const float*    m_pfParam1;        //调用参数1  
  const float*    m_pfParam2;        //调用参数2
  const float*    m_pfParam3;        //调用参数3
  const float*    m_pfParam4;        //调用参数3

  float*        m_pResultBuf;      //结果缓冲区
  const DATA_TYPE    m_dataType;        //数据类型
  const float*    m_pfFinData;      //财务数据
} CALCINFO;

/* 
注: 
  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.
    在时间上m_pData[x] 与 m_pfParam1[x]是一致的
*/


//////////
/* 函数输出

__declspec(dllexport) int xxxxxxxx(CALCINFO* pData);  ---------- A
__declspec(dllexport) int xxxxxxxxVAR(CALCINDO* pData);  ---------- B

1.函数名称需全部大写.
2.函数必须以上述A,B两种形式之一声明,请用实际函数名称替代xxxxxxxx;
  对于C++程序还需包括在 extern "C" {   } 括号中.
3.上述形式A用于声明不带参数或全部参数为常数的函数;
  形式B用于声明参数1为序列数的函数;两种函数的区别在于后者以VAR结尾.
4.函数计算结果用pData->m_pResultBuf带回.
5.函数返回-1表示错误或全部数据无效,否则返回第一个有效值位置,即:
  m_pResultBuf[返回值] -- m_pResultBuf[m_nNumData-1]间为有效值.
6.函数名称长度不能超过15字节,动态连接库文件名不能超过9字节(不包括扩展名),动态库名称不能叫SYSTEM,EXPLORER
7.编译时请请选择1字节对齐

*/

//示例函数,使用时用实际名称替换
__declspec(dllexport) int WINAPI WRITE(CALCINFO* pData);
__declspec(dllexport) int WINAPI READ(CALCINFO* pData);


#ifdef __cplusplus
}
#endif //__cplusplus


#endif //__FXJFUNC_H_INCLUDE
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/7 15:55:43
5楼: 那个网友提供的可以使用的dll,他也给了c的源代码下载,具体如下

// FxjFunc.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "FxjFunc.h"
#include <string>
using namespace std;
#include <fstream>
#include<sstream> 


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

string DzhCode(string dm , const BOOL  bIndex)
{
  string mydm=dm;
  if(bIndex && mydm.substr(0,3)=="000")
  {
    mydm="1";mydm=mydm.append(dm);
    return mydm;
  }
  else
  {
    return mydm;
  }
}

string int2Code(int iCode)
{
  char strvar[20];
  itoa(iCode,strvar,10);//指标序号,第三个参数10表示十进制
  string mydm(strvar);
  if(iCode>1000000)//沪市指数1000001...
  {
    return mydm;
  }
  else
  {
    //char s[20];
    //sprintf(s,"00000%d",iCode);
    //return string(s);
    string zeros="";
    for(int j=strlen(strvar);j<6;j++) zeros.append("0");
    mydm=zeros.append(mydm);
    return mydm;
  }
}

string DzhDataType(DATA_TYPE datatype)
{
  switch(datatype)
  {
  case TICK_DATA:          //分笔成交
    return "TICK";
    break;
  case MIN1_DATA:          //1分钟线
    return "MIN1";
    break;
  case MIN5_DATA:          //5分钟线          
    return "MIN5";
    break;
  case MIN15_DATA:          //15分钟线
    return "MIN15";
    break;
  case MIN30_DATA:          //30分钟线
    return "MIN30";
    break;
  case MIN60_DATA:          //60分钟线
    return "MIN60";
    break;
  case DAY_DATA:          //日线
    return "DAY";
    break;
  case WEEK_DATA:          //周线
    return "WEEK";
    break;
  case MONTH_DATA:          //月线
    return "MONTH";
    break;
  case MULTI_DATA:          //多日线
    return "NDAYS";
    break;
  case 12:           
    return "YEAR";
    break;
  case 13:           
    return "QUARTER";
    break;
  case 14:           
    return "SEMIYEAR";
    break;
  case 15:           
    return "15";
    break;
  case 1:           
    return "1";
    break;
  default:
    return  "NA";
  }
  
}

string fileName(string dm , const BOOL  bIndex,int varid,DATA_TYPE datatype)
{
    char strvar[20];
    itoa(varid,strvar,10);//指标序号,第三个参数10表示十进制
    string filename ="FMLDATA\\";
    filename.append(DzhCode(dm,bIndex));
    filename.append(".");
    filename.append(strvar);
    filename.append(".");
    filename.append(DzhDataType(datatype));
    return filename;
}


__declspec(dllexport) int WINAPI WRITE(CALCINFO* pData)
{
  float f ;
  int nVar,nFirst,i;
  string code="";
  if(pData->m_pfParam1 && pData->m_pfParam2 && //参数1,2有效
    pData->m_nParam1Start>=0 &&          //参数1为序列数
    pData->m_pfParam3==NULL)      //有2个参数
  {
    nFirst = pData->m_nParam1Start;        //有效值
    code.append(pData->m_strStkLabel);
    f = *pData->m_pfParam2; nVar = (int)f;         
    if(nVar>0)
    {
      struct recordStruct{int dt;float value;} record;
      string filename=fileName(code,pData->m_bIndex,nVar,pData->m_dataType);
      ofstream fout(filename.c_str(),ios::binary);
      if(fout)
      {
        for(i=0;i<pData->m_nNumData;i++)
        {
          record.dt=pData->m_pData[i].m_time;
          record.value=pData->m_pfParam1[i];
          fout.write((char*)(&record),sizeof(record));
          pData->m_pResultBuf[i]=pData->m_pfParam1[i];
        }
      }
      fout.close();
      return 1;
    }
  }
  return -1;
}

__declspec(dllexport) int WINAPI READ(CALCINFO* pData)
{
  float f;
  int nCode,nVar,i;
  string code="";

  if(pData->m_pfParam1 &&  pData->m_pfParam2 &&        //参数有效
    pData->m_nParam1Start<0 &&      //参数为常数
    pData->m_pfParam3==NULL)      //2个参数
  {
    
    nCode=(int) *pData->m_pfParam1;  //参数1
    if(nCode==0)
      code.append(pData->m_strStkLabel);
    else
      code=int2Code(nCode);

    f = *pData->m_pfParam2;
    nVar = (int)f;          //参数1
    if(nVar>0)
    {

      struct recordStruct{int dt;float value;} record;
      string filename=fileName(code,pData->m_bIndex,nVar,pData->m_dataType);
      ifstream fin(filename.c_str(),ios::binary);
      if(fin)
      {
        record.dt=0;record.value=0;
        fin.read((char*)(&record),sizeof(record));
        for(i=0;i<pData->m_nNumData;i++)
        {
          int dzhrq =  pData->m_pData[i].m_time;
          while(!fin.eof() && dzhrq>record.dt )
          {
            fin.read((char*)(&record),sizeof(record));
          }
          if(!fin.eof() && dzhrq==record.dt)
          //if(!fin.eof())
          {
            pData->m_pResultBuf[i]=record.value;
          }
          if(fin.eof()) break;
        }
      }
      fin.close();

      return 1;
    }

  }
  return -1;
}
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/7 16:02:42
6楼: 在大智慧的公式里面,写入下面的语句,就可以调用那个FMLDATA.dll的WRITE函数输出了。
"FMLDATA@WRITE"(X,N)
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2010/11/8 15:56:17
7楼: 约定就是stdcall;

另外,你写的测试库函数带上参数:

function TTTT( lpValue: Pointer ): Integer; stdcall; export;
begin
  MessageBox( 0, 'TTTT', 'TTTT', 0 );
  Result:= 0;
end;

这样看看



还有就是,看看那个可以用的DLL的工程里def文件是怎么写的。看看有没有指定导出的函数名。

如果没有这个文件或者没有指定函数名的话
你DELPHI里的exports要这样写:


exports:
  TTTT name '_TTTT';
----------------------------------------------
--
作者:
男 unjiang (css) ★☆☆☆☆ -
盒子活跃会员
2010/11/8 18:17:34
8楼: 哈巴狗小窝中有一个通达信选股的插件,从c改写过来的,所有证券行情软件都差不多,可以参考一下。
----------------------------------------------
是爱好,就别苛求太多!
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/8 19:33:20
9楼: 谢谢leonna,用了下面这个就有反应了,应该是这个原因了
exports:
  TTTT name '_TTTT';




也谢谢unjiang 提示,搜到小窝和它的插件,正在研究
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/8 21:43:14
10楼: 按此在新窗口浏览图片 基本的问题了,再次求助。搜过资料,没头绪。

  BaseDataType= record //基本数据         
    m_time:Cardinal;//时间,UCT
    m_fOpen, m_fHigh, m_fLow, m_fClose, m_fVolume, m_fAmount:Single;
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;
。。。。。。
  DzhOutDataType=record  
省略。。。
    m_nNumData:Integer;//数据数量
    m_pData:^BaseDataType;

省略。。。
  end;
  DzhDataPointType=^DzhOutDataType;

DzhData:DzhDataPointType;

现在已经能够知道数据总数量,DzhData^.m_pData^.m_fOpen也得到了BaseDataType记录格式的第一组数据。
怎样循环得到后面各组的数据呢?其实就是怎样移动那个DzhData^.m_pData^
是类似这样来使用么?应该怎样改呢
  datap:^BaseDataType;
  for i:=0 to DzhData^.m_nNumData-1 do
  begin
    datap:=@DzhData^.m_pData+i*sizeof(BaseDataType);
    sometime:=datap^.m_time;
  end;
----------------------------------------------
-
作者:
男 gztomash ( ) ★☆☆☆☆ -
普通会员
2010/11/8 22:06:12
11楼: var
  datap:^BaseDataType;
...

datap := DzhData^.m_pData;
for i := 0 to DzhData^.m_nNumData - 1 do
begin
  sometime := datap^.m_time;
  Inc(datap); // Here it is.
end;
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/8 23:35:19
12楼: 按此在新窗口浏览图片 谢谢gztomash提示
  datap:=@DzhData^.m_pData^;可用
datap := DzhData^.m_pData;编译不通过,说 Incompatible types



按此在新窗口浏览图片 喜欢delphi,速度和功能接近c,但是比c容易
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/9 12:13:35
13楼: 按此在新窗口浏览图片 谢谢盒子论坛的各位热心人,现在自己的dll基本正常使用了。贴出自己的源代码,回馈盒子论坛


library MYDLL;

uses
  SysUtils, Windows;
type
  BaseDataType=record //基本数据
    m_time:Integer;//时间,UCT
    m_fOpen, m_fHigh, m_fLow, m_fClose, m_fVolume, m_fAmount:Single;
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;
  ExDataType=record //扩展数据,用于描述分笔成交数据的买卖盘
    m_fBuyPrice:array[1..5] of single;//买1--买5价
    m_fBuyVol:array[1..5] of single;//买1--买5量
    m_fSellPrice:array[1..5] of single;//卖1--卖5价
    m_fSellVol:array[1..5] of single;//卖1--卖5量
  end;
  DzhOutDataType=record //大智慧调用本dll时传出的数据格式
    m_dwSize:Cardinal;//结构大小
    m_dwVersion:Cardinal;//调用软件版本(V2.10 : 0x210)
    m_dwSerial:Cardinal;//调用软件序列号
    m_strStkLabel:PChar;//股票代码
    m_bIndex:Boolean;//是否大盘
    m_nNumData:Integer;//数据数量(pData,pDataEx,pResultBuf数据数量)
    m_pData:^BaseDataType;//常规数据,注意:当m_nNumData=0时可能为 NULL
    m_pDataEx:^ExDataType;//扩展数据,分笔成交买卖盘,注意:可能为 NULL
    m_nParam1Start:Cardinal;//参数1有效位置
    m_pfParam1:^Single;//调用参数1
    m_pfParam2:^Single;//调用参数2
    m_pfParam3:^Single;//调用参数3
    m_pfParam4:^Single;//调用参数3
    m_pResultBuf:^Single;//结果缓冲区
    m_dataType:Word;//分析周期
{          分析周期        值
 Main_data          分时图         1
 TICK_DATA,        //分笔成交          2
 MIN1_DATA,          //1分钟线         3
 MIN5_DATA,          //5分钟线         4
 MIN15_DATA,          //15分钟线      5
 MIN30_DATA,          //30分钟线      6
 MIN60_DATA,          //60分钟线      7
 DAY_DATA,          //日线          8
 WEEK_DATA,          //周线          9
 MONTH_DATA,          //月线          10
 MULTI_DATA          //多日线          11}
   m_pfFinData:^Single;//财务数据
  end;
  DzhDataPointType=^DzhOutDataType;


{$R *.RES}

function TTT(DzhData:DzhDataPointType):longint; stdcall;//   export;
var
  i:Integer;
  tfile :TextFile;
  tstr:string;
  MainDataP:^BaseDataType;
  ExtDataP:^ExDataType;
  MyResultP:^Single;
begin
  AssignFile(tfile, 'C:\DzhOut.txt');
  Rewrite(tfile);
  Writeln(tfile, '结构大小:'+inttostr(DzhData^.m_dwSize));
  Writeln(tfile, '调用软件版本:'+inttostr(DzhData^.m_dwVersion));
  Writeln(tfile, '调用软件序列号:'+inttostr(DzhData^.m_dwSerial));
  Writeln(tfile, '股票代码:'+DzhData^.m_strStkLabel);
  Writeln(tfile, '数据数量:'+inttostr(DzhData^.m_nNumData));
  Writeln(tfile, '分析周期:'+inttostr(DzhData^.m_dataType));
  if DzhData^.m_nNumData>0 then
  begin
    MainDataP:=@DzhData^.m_pData^;
    ExtDataP:=@DzhData^.m_pDataEx^;
    MyResultP:=@DzhData^.m_pResultBuf^;
    for i:=1 to DzhData^.m_nNumData do
    begin
      tstr:=DateTimeToStr(MainDataP^.m_time/86400+25569);
      tstr:=tstr+format(',%f,%f,%f,%f,%f,%f', [MainDataP^.m_fOpen, MainDataP^.m_fHigh, MainDataP^.m_fLow, MainDataP^.m_fClose, MainDataP^.m_fVolume, MainDataP^.m_fAmount]);
      MyResultP^:=33;// 自己计算的返回给大智慧的数据
      Inc(MainDataP);
      Inc(MyResultP);
      if DzhData^.m_dataType=2 then//分笔则输出5个委买委卖
      begin
        tstr:=tstr+format(',%f,%f,%f,%f,%f', [ExtDataP^.m_fBuyPrice[1], ExtDataP^.m_fBuyPrice[2], ExtDataP^.m_fBuyPrice[3], ExtDataP^.m_fBuyPrice[4], ExtDataP^.m_fBuyPrice[5]]);
        tstr:=tstr+format(',%f,%f,%f,%f,%f', [ExtDataP^.m_fBuyVol[1], ExtDataP^.m_fBuyVol[2], ExtDataP^.m_fBuyVol[3], ExtDataP^.m_fBuyVol[4], ExtDataP^.m_fBuyVol[5]]);
        tstr:=tstr+format(',%f,%f,%f,%f,%f', [ExtDataP^.m_fSellPrice[1], ExtDataP^.m_fSellPrice[2], ExtDataP^.m_fSellPrice[3], ExtDataP^.m_fSellPrice[4], ExtDataP^.m_fSellPrice[5]]);
        tstr:=tstr+format(',%f,%f,%f,%f,%f', [ExtDataP^.m_fSellVol[1], ExtDataP^.m_fSellVol[2], ExtDataP^.m_fSellVol[3], ExtDataP^.m_fSellVol[4], ExtDataP^.m_fSellVol[5]]);
        Inc(ExtDataP);
      end;
      Writeln(tfile, tstr);
    end;
  end;
  CloseFile(tfile);
  winexec(pchar('notepad.exe C:\DzhOut.txt'), 1);
  result:=1;
end;

exports
  TTT name'_TTT';

begin
end.
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2010/11/9 23:13:45
14楼: 问题解决了?
解决了就好。

另外说一下:如果是必须在extern "C"里定义的话,你这代码很不严谨。。
比如PCHAR,BOOLEAN等数据类型的翻译等
还有比如标识符的起名等。m_是C++里的习惯,在这里写说多别扭有多别扭。
还有,DELPHI中t开头一般是类名,直接用在变量上,也很难看。。


总之嘛。这代码看上去很难受,呵
----------------------------------------------
--
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2010/11/10 9:51:10
15楼: 按此在新窗口浏览图片 说的也是,本来是懒,直接用人家的头文件的东西。自己再用cnpack的f2改一次还是方便的,辛苦一次以后都顺眼。起名嘛,自己写自己用的东西是随便了一点,懒嘛按此在新窗口浏览图片

PCHAR,BOOLEAN,用哪一种类型会更好呢?
----------------------------------------------
-
作者:
男 lfj_hycs (电脑爱好者) ★☆☆☆☆ -
普通会员
2011/1/31 20:20:59
16楼: 朋友们好!我也想请教个问题,我的DLL公式写出来后在大智慧新一代中调用速度非常慢。后来发现原来是重复计算太多,鼠标动一下可能就有几千次调用DLL公式重新计算。不知道大智慧 DLL公式计算的触发条件是什么。DLL返回的是一个数组,按理说只需要计算一次就行了,请问各位朋友如何才能避免这样的重复计算。谢谢!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/4/18 17:01:13
17楼: 也许是因为鼠标激活了十字光标?按了esc键,取消了十字线以后移动鼠标仍然重复计算么?


我感觉一般是按上下箭头缩放k线或者左右移动k线的时候会重新计算。
可以试一下在自己dll函数里面加个showmessage,试试看神马时候会弹出提示窗,表明激活了计算
另外也应该精简优化自己的代码


我个人感觉用dll比用大智慧自己公式系统快实在太多,越是复杂的公式差别越大。
----------------------------------------------
-
作者:
男 yctb (尉迟太保) ▲▲▲▲▲ -
普通会员
2011/6/10 10:50:14
18楼: somewho你好,我也在学习大智慧的Dll公式编程,有问题请教您。
请问使用C接口,能够遍历板块个股数据,进行统计排序吗?
谢谢。
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/11 14:56:08
19楼: 我估计不能统计排序。

这个dll功能,貌似大智慧每次只是传出所指定的1个股票的数据,调用dll里面的函数得到计算结果就返回了。

如果想自动计算多个股票,就要进行多次调用,也许要通过新建一个大智慧的扩展数据,关联自己的指标,在这个指标里再调用自己的dll?也许在大盘列表里面新加一个表项,指定自己的指标来排序?我没有试过。
用dll的好处貌似是可以在k线图上面反应自己的计算结果。某种情况下,我觉得自己写个软件,直接读大智慧的数据来批量计算会快很多。dzh2\data\sh和dzh2\data\sz里面的DAY.DAT,EXTDAY.DAT,MIN.DAT,REPORT.DAT分别对应的上海深圳的日线,扩展数据,5分钟线和分笔的数据。
*.prp文件是历史分笔,好像是REPORT.DAT的分笔数据再加上当天列表数据。不过如果单单说分笔的内容,是可以使用和REPORT.DAT是一样的结构来读取的

dzh2\userdata\block里面的是版块文件
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/11 17:49:58
20楼: 分享一下两个测试。更多细节请详细研究c的头文件。
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/11 17:50:17
21楼: library Project1;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils, Windows;

type
  MainDataType=record //基本数据
    DateTime:Integer;//时间,UCT
    Open, High, Low, Close, Vol, Amount:Single;
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;
  ExDataType=record //扩展数据,用于描述分笔成交数据的买卖盘
    BuyPrice:array[1..5] of single;//买1--买5价
    BuyVol:array[1..5] of single;//买1--买5量
    SellPrice:array[1..5] of single;//卖1--卖5价
    SellVol:array[1..5] of single;//卖1--卖5量
  end;
  DzhOutDataType=record //大智慧调用本dll时传出的数据格式
    DataSize:Cardinal;//结构大小
    DZHVersion:Cardinal;//调用软件版本(V2.10 : 0x210)
    DZHSerial:Cardinal;//调用软件序列号
    StkNo:PChar;//股票代码
    IsIndex:Boolean;//是否大盘
    DataCount:Integer;//数据数量(pData,pDataEx,pResultBuf数据数量)
    pMainDataBuf:^MainDataType;//常规数据,注意:当m_nNumData=0时可能为 NULL
    pExtDataBuf:^ExDataType;//扩展数据,分笔成交买卖盘,注意:可能为 NULL
    m_nParam1Start:Cardinal;//参数1有效位置
    pParam1:^Single;//调用参数1
    pParam2:^Single;//调用参数2
    pParam3:^Single;//调用参数3
    pParam4:^Single;//调用参数3
    pResultBuf:^Single;//结果缓冲区
    DataType:Word;//分析周期
{          分析周期        值
 Main_data          分时图         1
 TICK_DATA,        //分笔成交          2
 MIN1_DATA,          //1分钟线         3
 MIN5_DATA,          //5分钟线         4
 MIN15_DATA,          //15分钟线      5
 MIN30_DATA,          //30分钟线      6
 MIN60_DATA,          //60分钟线      7
 DAY_DATA,          //日线          8
 WEEK_DATA,          //周线          9
 MONTH_DATA,          //月线          10
 MULTI_DATA          //多日线          11}
    pFinData:^Single;//财务数据
  end;
  DzhDataPointType=^DzhOutDataType;
{$R *.RES}

{编译的dll,输出改至大智慧的目录,比如C:\dzh2}

function test1(pDzhData:DzhDataPointType):Integer; stdcall;// 测试 返回值
var
  DataPo:Integer;
  Param1, Param2:Single;
  pMainData:^MainDataType;
  pMyResult:^Single;
begin
  if pDzhData.DataCount>0 then
  begin
    Param1:=pDzhData.pParam1^;//第一个传入参数
    Param2:=pDzhData.pParam2^;//第二个传入参数
    pMainData:=@pDzhData.pMainDataBuf^;
    pMyResult:=@pDzhData.pResultBuf^;
    if Param1=0 then //第一个参数为 0
    begin
      for DataPo:=1 to pDzhData.DataCount do
      begin
        pMyResult^:=pMainData.Close;// 计算结果为 收盘价
        Inc(pMainData);
        Inc(pMyResult);
      end
    end
    else if Param1=1 then //第一个参数为 1
    begin
      for DataPo:=1 to pDzhData^.DataCount do
      begin
        pMyResult^:=DataPo;// 计算结果为 一个自定义的顺序增加值
        Inc(pMyResult);
      end
    end;
    if (Param2>0) then Result:=8 else Result:=0;// 返回第一个有效值位置 ,第一个有效值位置是 0,不能超过(数据总量-1)
  end
  else
    Result:=-1;// 函数返回-1表示错误或全部数据无效
{以日线为例,大智慧指标的公式里面输入这个
"project1@test1"(0,0);
可以看见指标表示的就是从第一天开始的收盘价
如果改成这样
"project1@test1"(1,3);
日线上看其实就是上市天数了,Result:=8,实际上就是从第9个数据输出
}
end;

function test2(pDzhData:DzhDataPointType):Integer; stdcall;// 测试 浮点数
{这个测试用来说明大智慧、分析家的很多数据用浮点数来保存的,于是牵涉到浮点数的一个大问题,就是两个浮点数是不能用if a=b
这样的形式来判断否相等的。解决的办法一般是if abs(a-b)<=某个很小的数,那么就认为a等于b。
股票一般是小数2位代表分,可以用<=0.001。或者a和b都乘100然后round,这样就变成两个整数的比较,就可以用a=b这样的形式了
这个不是delphi的问题,是浮点数的问题。就算在大智慧的公式系统里面用大智慧自己的函数来比较也一样出这样的问题,
只能埋怨分析家当年选择用浮点数了
大智慧指标的公式里面输入下面这个,然后在分笔界面即可调用
"project1@test2";
}
var
  DataPo, i:Integer;
  pMainData:^MainDataType;
  pExtData:^ExDataType;
  outputfile :TextFile;
begin
  if (pDzhData.DataType=2)and(pDzhData.DataCount>0) then //分笔数据
  begin
    pMainData:=@pDzhData.pMainDataBuf^;
    pExtData:=@pDzhData.pExtDataBuf^;
    AssignFile(outputfile, 'c:\dzh_test.txt');//c盘根目录下面输出文本
    Rewrite(outputfile);
    for DataPo:=1 to pDzhData.DataCount do
    begin
      Writeln(outputfile, formatdatetime('yyyymmdd tt" : "', (pMainData.DateTime/86400)+25569));//时间
      for i:=5 downto 1 do Writeln(outputfile, format('卖%d : %g', [i, pExtData.SellPrice[i]]));//浮点数的真相
      Writeln(outputfile, '');
      Inc(pMainData);
      Inc(pExtData);
    end;
    CloseFile(outputfile);
    winexec(pchar('notepad.exe c:\dzh_test.txt'), SW_SHOWMINNOACTIVE);//最小化后台打开这个输出的文本。可以通过观察
    //任务栏上面何时多开了个notepad来了解大智慧什么时候调用dll计算。
    Result:=0;
  end
  else
    Result:=-1;
end;

exports
  test1 name'_test1',
  test2 name'_test2';

begin
end.
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/11 20:17:23
22楼: somewho,你好!
我打算用Delphi学做大智慧公式DLL,但一切都是从头开始,网上找到了很多相关资料,觉得你这个帖子最有帮助,但由于基础有限,范例还是看得不太明白.
请问你能不能给个实现大智慧简单自有函数比如ma()或ref()什么的DLL范例代码,以便对照学习是如何具体实现的.
谢谢!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/11 22:09:46
23楼: 哈哈,那好吧。我抽空慢慢的试着做一下有屎以来最方便入门的介绍吧

我现在还在用delphi5,个人感觉上好像如果只是数学运算,d5好像比d7快一点。但是如果有很多字符操作,那么加了fastmm和fastcode的d7确实比d5快的明显。fastmm和fastcode不支持d5。deplhi其他的版本我没有比较,因为我一直个人用,d5d7感觉合适自己。
我常用的辅助插件,一个是cnpack,他们网址http://www.cnpack.org/index.php?lang=zh-cn;另一个是delforex,用来格式化代码的,用在delphi2007以下版本。现在官网貌似已经没了。
下面的介绍就按我的d5了,所以可能和别的版本的delphi有点不一样。


在菜单“file”-“new...”,选“dll”,就可以新建一个空白dll源代码,像下面这样
----------
library Project1;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils,
  Classes;

{$R *.RES}

begin
end.

----------
在菜单“project”的“options”,“directories/conditionals”里面,“output directory”选择大智慧的目录,一般是C:\dzh2,这样编译的时候,生成的dll就在大智慧目录里面了。而大智慧调用dll的时候就是必须从自己的目录里面调用的。
把自己这个新project存盘,可以给project起名字,然后菜单“project”的build 项目名就可以编译生成用项目名命名的dll文件,如果设置了output directory为C:\dzh2,那么在C:\dzh2里面就可以看见那个dll了
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/11 23:25:19
24楼: 非常感谢热心的somewho朋友,非常期待你有"屎以来最方便的入门介绍"!
个人的经验,学以致用,边学边用是最好和最有效的学习方法.而万事开头难,入门过程是关乎学习成败的关键,希望"师傅"的介绍能将更多的同好者"引"领"进门"里来!
师傅大人在上,先受徒儿一拜了!呵呵...
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/12 15:14:28
25楼: 不用这么客气,交流一下就是了


下面的是一个计算ma的例子,可以对比着新建的空白dll的源代码,来了解在哪个地方插入那些代码。
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/12 15:17:43
26楼: library Project1;
{项目名字就用缺省的Project1,把项目输出路径改成了C:\dzh2,所以编译时产生C:\dzh2\Project1.dll这个文件。
在大智慧的指标公式里面加入下面这句
"Project1@testma"(5);
大智慧就会调用 C:\dzh2\Project1.dll里面的testma1函数,并传递参数5
}

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils;//Classes这个unit一般用不到。可以根据自己需要添加windows,math等其他unit

//下面添加类型声明,具体请参考delphi语法
type
  MainDataType=record //基本数据
    DateTime:Integer;//时间,UCT
    Open, High, Low, Close, Vol, Amount:Single;
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;
  ExDataType=record //扩展数据,用于描述分笔成交数据的买卖盘
    BuyPrice:array[1..5] of single;//买1--买5价
    BuyVol:array[1..5] of single;//买1--买5量
    SellPrice:array[1..5] of single;//卖1--卖5价
    SellVol:array[1..5] of single;//卖1--卖5量
  end;
  DzhOutDataType=record //大智慧调用本dll时传出的数据格式
    DataSize:Cardinal;//结构大小
    DZHVersion:Cardinal;//调用软件版本(V2.10 : 0x210)
    DZHSerial:Cardinal;//调用软件序列号
    StkNo:PChar;//股票代码
    IsIndex:Boolean;//是否大盘
    DataCount:Integer;//数据数量(pData,pDataEx,pResultBuf数据数量)
    pMainDataBuf:^MainDataType;//常规数据,注意:当m_nNumData=0时可能为 NULL
    pExtDataBuf:^ExDataType;//扩展数据,分笔成交买卖盘,注意:可能为 NULL
    m_nParam1Start:Cardinal;//参数1有效位置
    pParam1:^Single;//调用参数1
    pParam2:^Single;//调用参数2
    pParam3:^Single;//调用参数3
    pParam4:^Single;//调用参数3
    pResultBuf:^Single;//结果缓冲区
    DataType:Word;//分析周期
    pFinData:^Single;//财务数据
  end;
  DzhDataPointType=^DzhOutDataType;

{$R *.RES}

//下面开始的是自己的函数

function testma(pDzhData:DzhDataPointType):Integer; stdcall;// 自己做一个收盘价的ma函数
var
  DataPo, i, Param1:Integer;
  DataSum:Single;
  pMainData, pTempMainData:^MainDataType;
  pMyResult:^Single;
begin
  Param1:=Round(pDzhData.pParam1^);//第一个传入参数,就是求多少天均价的参数,这里转换为整形,后面的循环要用
  if (pDzhData.DataCount>=Param1)and(Param1>0) then //数据数量不少于参数 并且 参数>0 才计算,意思是假如要计算5日均线,
  //那么至少有5日的数据才计算
  begin
    pMainData:=@pDzhData.pMainDataBuf^;//指针指向大智慧传入的主数据缓冲区 的第一个数据位置
    Inc(pMainData, Param1-1);//指针向后移动Param1-1个位置,意思是假如要计算5日均线,那么从第5个数据开始计算
    pMyResult:=@pDzhData.pResultBuf^;//指针指向 结果缓冲区  的第一个数据位置
    Inc(pMyResult, Param1-1);//保存结果的指针也相应的向后移动Param1-1个位置,来对应主数据
    for DataPo:=Param1 to pDzhData.DataCount do //从第Param1个数据开始,计算全部数据的ma值
    begin
      DataSum:=pMainData.Close;// 合计值初始化为当前位置数据的收盘价
      pTempMainData:=pMainData;// 临时指针指向当前主数据位置
      for i:=2 to Param1 do
      begin
        Dec(pTempMainData);// 临时指针向前移动1,指向前一个主数据位置,
        DataSum:=DataSum+pTempMainData.Close;//把前一个周期的收盘价累加进来
      end;
      pMyResult^:=DataSum/Param1;// 把收盘均价的累加值除以Param1,得出Param1个周期均价,把计算结果赋给 结果缓冲区
      Inc(pMainData);//移动指针指向下一个主数据 位置
      Inc(pMyResult);//移动指针指向下一个结果位置
    end;
    Result:=Param1-1;// 函数返回有效数据从第Param1个数据开始
  end
  else
    Result:=-1;// 函数返回-1表示错误或全部数据无效
end;

//下面是dll函数输出说明
exports
  testma name'_testma';

//因为写的是dll,下面不用写内容了。
begin

end.
----------------------------------------------
-
作者:
男 eno (eno) ▲▲▲▲▲ -
普通会员
2011/6/12 17:05:42
27楼: 这个帖子太有针对性了,感谢楼主的无私奉献,希望楼主能进一步将大指慧的基础函都写出来,如hhv(),llv(),ref(),ema(),sma(),sum(),max()等等,一方面能对照学习编程,另一方面还能利用这些基础公式构建简单的dll公式,提高学习兴趣。如果这样,不但是楼主所说的有始以来最方便的入门介始,而且一定会成为有始以来最好的dll公式入门教材!
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/13 8:42:41
28楼: 非常感谢somewho朋友,待我消化后再来向你讨请教.
另外,如果要传价格(O,C,H,L)参数应该怎么做呢?
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/13 9:06:26
29楼: 也很赞同eno朋友的提议,因为这些基础函数的用法和算法大家都是很熟悉的,如果能知道在Delphi中是如何实现的,对增强对Delphi的理解将是大有裨益的,只不过这得辛苦somewho朋友了,实在有点不好意思!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/13 15:32:28
30楼: pMainData.Close就是收盘价了,相似的
pMainData.open就是开盘,等等。
在函数的var区域,已经声明了pMainData是一个指向MainDataType结构的指针变量。而MainDataType的数据结构已经在type里面声明了
  MainDataType=record //基本数据
    DateTime:Integer;//时间,UCT
    Open, High, Low, Close, Vol, Amount:Single;
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;

这个是delphi语法,多看看资料多练习一下就会熟悉了。



大智慧的其他函数我就不一一做了,有兴趣可以自己尝试做一下,就当是个练习。
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/13 17:25:14
31楼: 谢谢somewho,知道怎么做了.
但有一点没搞明白,MainDataType是我们在Delphi中自己定议的,它怎么会把大智慧的相关数据传过来了呢?
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/13 21:14:05
32楼: 呃~~~,这个是分析家大智慧提供的功能嘛,它提供了接口让用户可以通过分析家的公式系统来调用外部dll里面的函数,并且按照一定的格式把数据传给函数使用。
4楼的就是网上找来的c语言的头文件,我只是把用c语言定义的数据格式改成delphi的数据格式而已。
大概过程应该就是分析家先准备一块内存,然后按照约定的格式把数据填入这片内存。然后把指向这片内存的指针传给dll的函数。dll的函数得到指针以后,按照约定的格式就可以读取这片内存的数据,然后自己计算使用。
所以我的代码里面,定义的类型名变量名什么的都可以按自己的喜好重新修改,数据类型也可以用合适的来替代,只需要注意和c的头文件里面的定义保持一致,包括数据的先后次序一致和数据类型的兼容。

话说我也才注意,我的那个结构,时间DateTime用的integer不对哈,c头文件的是time_t,应该是长整形,对应delphi 的应该是Longword或者Cardinal,用到现在没出错,所以一直没发现这个错误呢
----------------------------------------------
-
作者:
男 eno (eno) ▲▲▲▲▲ -
普通会员
2011/6/14 6:51:14
33楼: 哎呀,我实在太菜了,但又想学,求楼主抽空多写一两个例子嘛,求你了!!!
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/14 10:58:53
34楼: 明白了,非常感谢!
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/14 11:14:52
35楼: 再请教:
大智慧的逐笔成交文件*.L2D(*为股票代码)数据格式是怎样的?能用Delphi读出来并写到数据库文件中去吗?
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/14 11:32:39
36楼: 找到了L2D的数据格式,但本人计算机水平太差,没办法消化,请高手编个读取到数据库的代码来,万谢!

起止地址 数据内容 数据含义 数据类型
00 - 03 F4 9B 13 FC 日线文件标志 int
04 - 07 10 02 00 00 未知 int
08 - 0B 00 43 DF 46 1970.01.01 00:00:00始的秒数 int
0C - 0F 5D 05 00 00 证券总数 int
10 - 13 ED 20 00 00 未知 int
14 - 17 ED 20 00 00 未知 int
18 - 21 30 30 30 30 30 31 00 00 证券代码 byte[10]
22 - 25 9C 12 00 00 日分笔记录数 int
26 - 57 00 00 BA 03...FF FF 记录块号 char[25]

00041000 记录页起始点,

每记录长: 0x34 = 52
每页记录数: 0xEC = 236
每页长: 每记录长0x34 * 每页记录数0xEC = 0x2FF0 = 12272

地 址 数据内容 数据含义 数据类型
41000 - 41003 35 FA DF 46 1970.01.01 00:00:00 始的秒数 int
41004 - 41007 00 00 18 41 最新价 float
41008 - 4100B 00 80 B4 43 累计成交量 float
4100C - 4100F 80 46 A7 48 累计成交金额 float

41010 - 41011 51 9C 累计成交笔数 char
41012 - 41013 00 00 未知 char
41014 10 累计成交笔数的溢出标志(00|10) byte
41015 80 买入,卖出标识(80|E0买入,C0|A0卖出) byte

41016 - 41017 23 01 委买量1 char
41018 - 41019 8E 5B 委买量2 char
4101A - 4101B 80 27 委买量3 char
4101C - 4101D 8E 5B 委买量4 char
4101E - 4101F B8 40 委买量5 char

41020 - 41021 23 01 委卖量1 char
41022 - 41023 8E 5B 委卖量2 char
41024 - 41025 80 27 委卖量3 char
41026 - 41027 8E 5B 委卖量4 char
41028 - 41029 B8 40 委卖量5 char

4102A 16 委买价1 与成交价的差 byte
4102B 9A 委买价2 与成交价的差 byte
4102C 80 委买价3 与成交价的差 byte
4102D 40 委卖价4 与成交价的差 byte
4102E 30 委卖价5 与成交价的差 byte

4102F 57 委卖价1 与成交价的差 byte
41030 68 委卖价2 与成交价的差 byte
41031 69 委卖价3 与成交价的差 byte
41032 7A 委卖价4 与成交价的差 byte
41033 81 委卖价5 与成交价的差 byte

注意:
1、如有疑问请与我联系,愿与您共同挖掘股票成交数据。
2、数据类型为JAVA数据类型定义,实际读数据时应该将数据十六进制取反,如:41000 - 41003的数据为 35 FA DF 46,实际应该读成:0x46DFFA35 = 1189083701, 这个1189083701值就是距1970.01.01 00:00:00 始的实际秒数。其他int,float,char都是这样取反的。
3、委买卖与成交价的差,是小数点后的整数差,如果买卖标识为80或C0,成交价小数点后是两位(股票),如果买卖标识为E0或A0,成交价小数点后是三位(权证等)
4、累计成交笔数如果溢出,则41014位上的值不为00,而是10或者20,30等,成交笔数计算方法应为:41014位上的值取第一位,10取前面的1,20取前面的2。。。 再与41010 - 41011位上的值合并成一个16进制串,本例中,实际值应该为0x19C51。
5、记录块号计算方法:char[0] = 0x00 第一个记录块起始地址为:41000h + 0x0000 * 0x2FF0 = 41000h,char[1] = 0x03BA 则第二个记录块起始地址为:41000h + 0x03BA * 0x2FF0 = 0x00077F78

5、记录块号计算方法:char[0] = 0x00 第一个记录块起始地址为:41000h + 0x0000 * 0xEC = 41000h,char[1] = 0x03BA 则第二个记录块起始地址为:41000h + 03BA * 0xEC = 0x00077F78
上面的页长应该是0x2FF0,而不是0xEC,希望有缘的朋友看到后能自己改正过来
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/14 13:06:38
37楼: 再来一个例子,把大智慧传给dll的数据输出到文本。
自己写大智慧的dll基本就是这样了,剩下的是自己学习熟悉delphi语法了


library Project1;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils, Windows;

type
  MainDataType=record //基本数据
    DateTime:Cardinal;//时间,UCT
    Open, High, Low, Close, Vol, Amount:Single;
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;
  ExDataType=record //扩展数据,用于描述分笔成交数据的买卖盘
    BuyPrice:array[1..5] of single;//买1--买5价
    BuyVol:array[1..5] of single;//买1--买5量
    SellPrice:array[1..5] of single;//卖1--卖5价
    SellVol:array[1..5] of single;//卖1--卖5量
  end;
  DzhOutDataType=record //大智慧调用本dll时传出的数据格式
    DataSize:Cardinal;//结构大小
    DZHVersion:Cardinal;//调用软件版本(V2.10 : 0x210)
    DZHSerial:Cardinal;//调用软件序列号
    StkNo:PChar;//股票代码
    IsIndex:Boolean;//是否大盘
    DataCount:Integer;//数据数量
    pMainDataBuf:^MainDataType;//常规数据,注意:当m_nNumData=0时可能为 NULL
    pExtDataBuf:^ExDataType;//扩展数据,分笔成交买卖盘,注意:可能为 NULL
    m_nParam1Start:Cardinal;//参数1有效位置
    pParam1:^Single;//调用参数1
    pParam2:^Single;//调用参数2
    pParam3:^Single;//调用参数3
    pParam4:^Single;//调用参数3
    pResultBuf:^Single;//结果缓冲区
    DataType:Word;//分析周期
    pFinData:^Single;//财务数据
  end;
  DzhDataPointType=^DzhOutDataType;

{$R *.RES}

function TestOutput(pDzhData:DzhDataPointType):Integer; stdcall;// 一个把数据输出成文本的函数
//大智慧公式里面用"project1@TestOutput";即可输出
var
  DataPo, i:Integer;
  pMainData:^MainDataType;
  pExtData:^ExDataType;
  OutPutString, FilenameString:string;
  OutPutFile:TextFile;
begin
  if pDzhData.DataCount>0 then //数据数量 >0 才输出
  begin
    FilenameString:='c:\'+pDzhData.StkNo+'.txt';//文本输出到 c 盘根目录,用股票代码做为文件名
    OutPutString:='股票代码:'+pDzhData.StkNo+#13#10;
    pMainData:=@pDzhData.pMainDataBuf^;//指针指向 大智慧传入的主数据缓冲区 的第一个数据位置
    pExtData:=@pDzhData.pExtDataBuf^;//指针指向 大智慧传入的扩展数据缓冲区  的第一个数据位置
    for DataPo:=1 to pDzhData.DataCount do //从第 1 个数据开始
    begin
      OutPutString:=OutPutString+formatdatetime('"时间:"yyyy"年"mm"月"dd"日" tt'#13#10, (pMainData.DateTime/86400)+25569);
      OutPutString:=OutPutString+'开盘价:'+FloatToStr(pMainData.Open)+#13#10;
      OutPutString:=OutPutString+'最高价:'+FloatToStr(pMainData.High)+#13#10;
      OutPutString:=OutPutString+'最低价:'+FloatToStr(pMainData.Low)+#13#10;
      OutPutString:=OutPutString+'收盘价:'+FloatToStr(pMainData.Close)+#13#10;
      OutPutString:=OutPutString+'成交量:'+FloatToStr(pMainData.Vol)+#13#10;
      OutPutString:=OutPutString+'成交额:'+FloatToStr(pMainData.Amount)+#13#10;
      if pDzhData.DataType=2 then //如果是 分笔数据,就输出5个委买卖数据
      begin
        for i:=5 downto 1 do OutPutString:=OutPutString+Format('卖%d%8.2f%10.0f'#13#10, [i, pExtData.SellPrice[i], pExtData.SellVol[i]]);
        OutPutString:=OutPutString+#13#10;
        for i:=1 to 5 do OutPutString:=OutPutString+Format('买%d%8.2f%10.0f'#13#10, [i, pExtData.BuyPrice[i], pExtData.BuyVol[i]]);
      end;
      OutPutString:=OutPutString+#13#10;
      Inc(pMainData);//移动指针指向下一个主数据 位置
      Inc(pExtData);//移动指针指向下一个扩展数据 位置
    end;
    AssignFile(OutPutFile, FilenameString);
    Rewrite(OutPutFile);
    Writeln(OutPutFile, OutPutString);
    CloseFile(OutPutFile);
    winexec(pchar('notepad.exe '+FilenameString), SW_SHOWMINNOACTIVE);
    Result:=0;
  end
  else
    Result:=-1;
end;

exports
  TestOutput name'_TestOutput';

begin

end.
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/14 14:09:50
38楼: to 36楼,不知道你这个格式是不是真的L2D的数据格式,我看着像是分笔数据REPORT.DAT的格式。我贴个以前的代码,可能还有不完善的地方,你喜欢就研究一下吧。窗体里面就一个button控件。



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Math;

type
  stockinfo=record
    stockno:array[1..10] of char;
    datacou, nouse:Smallint;
    blockpo:array[1..25] of Smallint;
  end;
  reporddatatype=record
    datatime:Cardinal;
    newp, allvol, allamount:single;
    allbishu, errlabel1:word;
    errlabel2, buysell:Shortint;
    buy_vol, sell_vol:array[1..5] of word;
    buy_delta, sell_delta:array[1..5] of Shortint;
  end;
  exchangedatatype=record
    gooddata:boolean;
    datatime, errlabel:Cardinal;
    vol, allamount:single;
    newp, allbishu:word;
    buysell:Shortint;
    buy_p, sell_p:array[1..5] of word;
    buy_vol, sell_vol:array[1..5] of Cardinal;
  end;

  TForm1=class(TForm)
    Button1:TButton;
    procedure Button1Click(Sender:TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1:TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender:TObject);
var
  iFileHandle, stkcount, stkpo, i, j, k, tpo:integer;
  stkinfodata:stockinfo;
  stkno, tstr:string;
  tpower:array[1..5, 1..4] of Cardinal;
  tempdata:array[1..236] of reporddatatype;
  alldata:array[1..5000] of exchangedatatype;
  datalist:tstringlist;
begin
  iFileHandle:=FileOpen('C:\dzh2\data\sh\REPORT.DAT', fmShareDenyNone);//读上海市场文件
  if iFileHandle=-1 then
  begin
    showmessage('文件打开错误');
    exit;
  end;
  FileSeek(iFileHandle, 8, 0);
  FileRead(iFileHandle, i, 4);
  for i:=1 to 5 do
    for j:=1 to 4 do tpower[i, j]:=trunc(power(2, i*4-j));// 用来排除错误的
  FileRead(iFileHandle, stkcount, 4);
  for stkpo:=0 to stkcount-1 do
  begin
    FileSeek(iFileHandle, stkpo*64+24, 0);
    FileRead(iFileHandle, stkinfodata, 64);
    stkno:=copy(stkinfodata.stockno, 1, 6);
    if stkno='600000' then //指定的股票代码,可以自己改。因为读的是上海数据,所以应用上海代码。如果是000001则读的是上海大盘
    begin
      tpo:=0;
      for i:=1 to 25 do
        if stkinfodata.blockpo[i]<0 then
          break
        else
        begin
          FileSeek(iFileHandle, stkinfodata.blockpo[i]*12272+266240, 0);
          FileRead(iFileHandle, tempdata, 12272);
          for j:=1 to 236 do
          begin
          inc(tpo);
          if tpo>stkinfodata.datacou then break;
          alldata[tpo].datatime:=tempdata[j].datatime;
          alldata[tpo].newp:=round(tempdata[j].newp*100);
          alldata[tpo].vol:=tempdata[j].allvol;
          alldata[tpo].allamount:=tempdata[j].allamount;
          alldata[tpo].allbishu:=tempdata[j].allbishu;
          alldata[tpo].errlabel:=tempdata[j].errlabel1+tempdata[j].errlabel2*65536;
          alldata[tpo].buysell:=tempdata[j].buysell;
          for k:=1 to 5 do
          begin
          if tempdata[j].sell_delta[k]=-128 then //如果是涨跌停板,相对值是个负数
          alldata[tpo].sell_p[k]:=0
          else
          begin
          if (alldata[tpo].errlabel and tpower[k, 1])=tpower[k, 1] then
          begin//这个是一个很特殊的情况,某个标志位被置1以后,委托价是最新价加4倍差价
          alldata[tpo].sell_p[k]:=alldata[tpo].newp+tempdata[j].sell_delta[k]*4;
          alldata[tpo].errlabel:=alldata[tpo].errlabel and not(tpower[k, 1]);
          end
          else //一般情况下,委托价格是最新价加上差值
          alldata[tpo].sell_p[k]:=alldata[tpo].newp+tempdata[j].sell_delta[k];
          end;
          if tempdata[j].buy_delta[k]=-128 then
          alldata[tpo].buy_p[k]:=0
          else
          begin
          if (alldata[tpo].errlabel and tpower[k, 2])=tpower[k, 2] then
          begin
          alldata[tpo].buy_p[k]:=alldata[tpo].newp+tempdata[j].buy_delta[k]*4;
          alldata[tpo].errlabel:=alldata[tpo].errlabel and not(tpower[k, 2]);
          end
          else
          alldata[tpo].buy_p[k]:=alldata[tpo].newp+tempdata[j].buy_delta[k];
          end;
          if (alldata[tpo].errlabel and tpower[k, 3])=tpower[k, 3] then
          begin
          alldata[tpo].sell_vol[k]:=tempdata[j].sell_vol[k]*32;
          alldata[tpo].errlabel:=alldata[tpo].errlabel and not(tpower[k, 3]);
          end
          else
          alldata[tpo].sell_vol[k]:=tempdata[j].sell_vol[k];
          if (alldata[tpo].errlabel and tpower[k, 4])=tpower[k, 4] then
          begin
          alldata[tpo].buy_vol[k]:=tempdata[j].buy_vol[k]*32;
          alldata[tpo].errlabel:=alldata[tpo].errlabel and not(tpower[k, 4]);
          end
          else
          alldata[tpo].buy_vol[k]:=tempdata[j].buy_vol[k];
          end;
          if (alldata[tpo].errlabel=0) then
          alldata[tpo].gooddata:=true
          else
          alldata[tpo].gooddata:=false;//仍然有未知错误
          if tpo=stkinfodata.datacou then break;
          end;
        end;
      datalist:=TStringList.Create;
      datalist.Append('股票代码:'+stkno);
      for i:=1 to stkinfodata.datacou do
      begin
        tstr:=FormatDateTime('dddddd tt', alldata[i].datatime/86400+25569)+','+floattostr(alldata[i].newp/100);//时间,最新价
        if i=1 then //成交量成交额,对于分笔来说是从第一笔开始的累加值,所以要这次值减上次值才能得出这笔的实际量、额
          tstr:=tstr+format(',%f,%f', [alldata[i].vol, alldata[i].allamount])
        else
          tstr:=tstr+format(',%f,%f', [(alldata[i].vol-alldata[i-1].vol), (alldata[i].allamount-alldata[i-1].allamount)]);
        tstr:=tstr+format(',%d,%d,%d,%d', [alldata[i].allbishu, alldata[i].errlabel, 0, alldata[i].buysell]);
        for j:=1 to 5 do tstr:=tstr+format(',%f', [alldata[i].buy_p[j]/100]);//5个委买价格
        for j:=1 to 5 do tstr:=tstr+format(',%d', [alldata[i].buy_vol[j]]);//5个委买数量
        for j:=1 to 5 do tstr:=tstr+format(',%f', [alldata[i].sell_p[j]/100]);//5个委卖价格
        for j:=1 to 5 do tstr:=tstr+format(',%d', [alldata[i].sell_vol[j]]);//5个委卖数量
        datalist.Append(tstr);
      end;
      datalist.SaveToFile('C:\ireaddata.csv');
      Break;
    end;
  end;
  FileClose(iFileHandle);
  Application.Terminate;
end;

end.
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/14 14:25:12
39楼: 非常感谢!
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/6/25 10:51:27
40楼: 谢谢lz的帖子。现在我想用lz的c++的code将导出的binary file改为txt file,用txt的想法是想用matlab计算其中数据,再用dll读计算后的数据回大智慧。dll的write to txt简单,搞定ok.但是read txt to大智慧无法实现,没有数据,不知道那里出问题,请达人帮忙看下。谢谢。code(仅列出dll部分),使用了vector,编译通过,但就是没有在大智慧得到数据。以下:
__declspec(dllexport) int WINAPI WRITE(CALCINFO* pData)
{
  float f ;
  int nVar,nFirst,i;
  string code="";
  if(pData->m_pfParam1 && pData->m_pfParam2 && //参数1,2有效
    pData->m_nParam1Start>=0 &&          //参数1为序列数
    pData->m_pfParam3==NULL)      //有2个参数
  {
    nFirst = pData->m_nParam1Start;        //有效值
    code.append(pData->m_strStkLabel);
    f = *pData->m_pfParam2; nVar = (int)f;         
    if(nVar>0)
    {
      
      string filename=fileName(code,pData->m_bIndex,nVar,pData->m_dataType);
      ofstream out(filename.c_str());
      if(out)
      {
          for(i=0;i<pData->m_nNumData;i++)
        {
          struct recordStruct{int dt;float value;} record;
          record.dt=pData->m_pData[i].m_time;
          record.value=pData->m_pfParam1[i];
          pData->m_pResultBuf[i]=pData->m_pfParam1[i];
          out<<record.dt<<" "<<record.value<<endl;
        }
      }
      return 1;
    }
  }
  return -1;
}

struct recordStruct{int dt;float value;} record;
__declspec(dllexport) int WINAPI READ(CALCINFO* pData)
{
  float f;
  int nCode,nVar;
  int n=0;
  string temp;
  
  string code="";

  if(pData->m_pfParam1 &&  pData->m_pfParam2 &&        //参数有效
    pData->m_nParam1Start<0 &&      //参数为常数
    pData->m_pfParam3==NULL)      //2个参数
  {
    
    nCode=(int) *pData->m_pfParam1;  //参数1
    if(nCode==0)
      code.append(pData->m_strStkLabel);
    else
      code=int2Code(nCode);

    f = *pData->m_pfParam2;
    nVar = (int)f;          //参数1
    if(nVar>0)
    {

      
      string filename=fileName(code,pData->m_bIndex,nVar,pData->m_dataType);
    ifstream fin(filename.c_str()); 
    if(fin)
      {
      record.dt=0;record.value=0;
      vector<recordStruct> v;
      int i=0;
    while (fin)
    {
       fin>>record.dt;
       fin>>record.value;
       v.push_back(record);
       i++;
    }
      for(i=0;i<pData->m_nNumData;i++)
        {
        
      int dzhrq =  pData->m_pData[i].m_time;
      
          
          if(!fin.eof() && dzhrq==record.dt)
          //if(!fin.eof())
          {
          pData->m_pResultBuf[i]=record.value;
          }
          if(fin.eof()) break;
        }


      return 1;
    }

  }
  return -1;
}
}
----------------------------------------------
-
作者:
男 whonyu (whonyu) ▲▲▲▲▲ -
普通会员
2011/6/25 11:39:55
41楼: somewho楼主,我的qq769485830,我也在新一代开发中遇到一些疑问待解,看到我的留言加我qq好友好吗,谢谢你啦
----------------------------------------------
-
作者:
男 whonyu (whonyu) ▲▲▲▲▲ -
普通会员
2011/6/25 11:41:56
42楼: 如有其它高手精通大智慧嵌入自编公式  用户权限控制的也可以加我好友,我的qq769485830,谢谢!解决问题必谢
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/6/25 19:42:14
43楼: 好了,自己搞定,谢谢lz...
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/25 21:30:36
44楼: to  newstar20088:

搞定就好。建议一下
如果用c的话,时间变量(比如那个record.dt)还是用回time_t这个数据类型好点,不要用integer了。我之前的帖子里面用了integer是我的错,但是没办法修改旧帖子了。

另外,头文件里面说:
[quote]5.函数返回-1表示错误或全部数据无效,否则返回第一个有效值位置,即:    m_pResultBuf[返回值] -- m_pResultBuf[m_nNumData-1]间为有效值.[/quote]
所以正常返回值应该是0,代表第一个数据是有效数据。可以尝试返回100或其他数,那么大智慧里面应该没有画前99个数据的线的。



to whonyu:谢谢你,不过我没有用qq的。如果有什么问题可以在这里提出来,如果我会我就尽量回答
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/6/27 13:40:06
45楼: lz关于把大智慧传给dll的数据输出到文本的例子,在大智慧中使用时,好像大智慧非常慢,是不是dll在重复计算?如何解决,谢谢。
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/27 14:23:28
46楼: 16楼也问过同样问题。可以在自己的代码里面,输出文本的语句后面添加这个

winexec(pchar('notepad.exe '+输出文本路径全名), SW_SHOWMINNOACTIVE);


意思是后台打开文本(这个是delphi的,c可能有点不一样)。这样就可以知道什么时候会激活重新计算了。


如果输出的内容很多,当然是非常慢了,因为要读写硬盘嘛,然后重复计算多少次就要重复读写多少次。
好像也没什么解决办法,不输出文件呗,或者也许可以放在收盘公式,平时看盘不用或者用另一个不输入输出文件的函数。或者不用dll,直接读大智慧的report.dat这个分笔数据文件。38楼就是一个直接读report.dat的代码,以前做的,也许有点错误。
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/28 16:29:13
47楼: somewho,你好!
我试着将你26楼的ma函数改为跟大智慧内部函数ma(X,N)一样带2个参数的形式,
但不知道X在DLL中应该用什么数据类型,怎样来判断它是C,O,L,还是H?
Var
Param1:Single;
......
Begin
Param1:=pDzhData.pParam1^;

后面用IF FloatToStr(Param1)='C'判断X是否为收盘价为什么不起作用?
(我现在只能用数字1,2,3,4来代替C,O,L,H来作为X参数!)
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/28 17:14:09
48楼: 大智慧传来的4个参数都是single浮点数(当然,也可以在自己代码里面转为整型),而不是字符串。FloatToStr(Param1)的结果是把一个浮点数转成字符串,比方说Param1=1.234,那么FloatToStr(Param1)的结果是字符串‘1.234’,当然不会等于‘C’。

如果在大智慧公式里面用"mydll@ma"(c,N)这样的代码,那么大智慧应该是把收盘价当参数传给了自己的dll。意思就是这一个股票收盘价是5.5元,那么"mydll@ma"(c,N)就相当于"mydll@ma"(5.5,N);然后翻到另一个收盘价13.2的股票,那么"mydll@ma"(c,N)就相当于"mydll@ma"(13.2,N)。


如果要完成类似大智慧公式ma(o+c,n)这样的计算,也只能用"mydll@ma"(1,N)这样,然后自己代码里面
if Param1=1 then
xxx:=pMainData.open+pMainData.Close
大概这样的方式来完成。如果一个股票开盘价5,收盘价5.5,那么"mydll@ma"(o+c,N)相当于"mydll@ma"(10.5,N)


那个ma的代码只是个简单例子,用来熟悉一下大智慧的dll功能而已。如果是一般的计算还是建议用回大智慧本身的公式,容易很多。dll的优势在于能完成大智慧自身公式很难完成的复杂计算
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/6/28 17:44:26
49楼: 谢谢somewho!
大智慧只能传single?不能传其他类型的参数?
呵呵,我是想通过模拟实现大智慧内部函数来学习Delphi语法和练习做DLL,实际运用肯定首选考虑自带函数了,至于自己用DLL来完成复杂计算,我现在的水平差的太远了,还得慢慢学习,多向你请教啊.
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/6/30 9:36:03
50楼: 请问lz的13楼的代码在导出txt文档后,有时会在openint即Amount的数据前产生符号负号,即‘-’,不是全部Amount前都有,也不是全部股票都会有,比如000009-中国宝安,就有这种情况,为什么?
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/30 12:17:51
51楼: to  newstar20088 

先给个具体错误吧,比方说宝安哪天的什么线的数据有错?另外也应该先检查一下大智慧里面本来的数据有没有问题。
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/6/30 13:17:01
52楼: 我想输出这样的txt格式:
19910625,4.30,5.00,4.30,4.30,4096,1972000
19910626,4.60,4.80,4.50,4.60,4027,1861000
19910627,4.70,4.80,4.55,4.70,5110,3380000
。。。。。。。。。。
所以在你的code基础上做了些修改,可能是我操作的问题,现在ok了。以下是我的出错误的code,还没明白为什么会有负号出现:
library MYDLL;

uses
  SysUtils,
  Windows;

type
  BaseDataType=record //基本数据
    m_time:Integer;//时间,UCT
    m_fOpen, m_fHigh, m_fLow, m_fClose, m_fVolume, m_fAmount:Single;
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;
  ExDataType=record //扩展数据,用于描述分笔成交数据的买卖盘
    m_fBuyPrice:array[1..5] of single;//买1--买5价
    m_fBuyVol:array[1..5] of single;//买1--买5量
    m_fSellPrice:array[1..5] of single;//卖1--卖5价
    m_fSellVol:array[1..5] of single;//卖1--卖5量
  end;
  DzhOutDataType=record //大智慧调用本dll时传出的数据格式
    m_dwSize:Cardinal;//结构大小
    m_dwVersion:Cardinal;//调用软件版本(V2.10 : 0x210)
    m_dwSerial:Cardinal;//调用软件序列号
    m_strStkLabel:PChar;//股票代码
    m_bIndex:Boolean;//是否大盘
    m_nNumData:Integer;//数据数量(pData,pDataEx,pResultBuf数据数量)
    m_pData:^BaseDataType;//常规数据,注意:当m_nNumData=0时可能为 NULL
    m_pDataEx:^ExDataType;//扩展数据,分笔成交买卖盘,注意:可能为 NULL
    m_nParam1Start:Cardinal;//参数1有效位置
    m_pfParam1:^Single;//调用参数1
    m_pfParam2:^Single;//调用参数2
    m_pfParam3:^Single;//调用参数3
    m_pfParam4:^Single;//调用参数3
    m_pResultBuf:^Single;//结果缓冲区
    m_dataType:Word;//分析周期
{          分析周期        值
 Main_data          分时图         1
 TICK_DATA,        //分笔成交          2
 MIN1_DATA,          //1分钟线         3
 MIN5_DATA,          //5分钟线         4
 MIN15_DATA,          //15分钟线      5
 MIN30_DATA,          //30分钟线      6
 MIN60_DATA,          //60分钟线      7
 DAY_DATA,          //日线          8
 WEEK_DATA,          //周线          9
 MONTH_DATA,          //月线          10
 MULTI_DATA          //多日线          11}
   m_pfFinData:^Single;//财务数据
  end;
  DzhDataPointType=^DzhOutDataType;


{$R *.RES}

function TTT(DzhData:DzhDataPointType):longint; stdcall;//   export;
var
  i,v,o:Integer;
  tfile :TextFile;
  tstr,FilenameString:string;
  MainDataP:^BaseDataType;
  ExtDataP:^ExDataType;
  MyResultP:^Single;
begin
  FilenameString:='c:\DzhOut\'+DzhData.m_strStkLabel+'.txt';//文本输出到 c 盘根目录,用股票代码做为文件名
  AssignFile(tfile, FilenameString);
  Rewrite(tfile);
  if DzhData^.m_nNumData>0 then
  begin
    MainDataP:=@DzhData^.m_pData^;
    ExtDataP:=@DzhData^.m_pDataEx^;
    MyResultP:=@DzhData^.m_pResultBuf^;
    for i:=1 to DzhData^.m_nNumData do
    begin
      v := Round(MainDataP^.m_fVolume);
      o := Round(MainDataP^.m_fAmount);
      tstr:=formatDateTime('yyyymmdd',MainDataP^.m_time/86400+25569);
      tstr:=tstr+format(',%f,%f,%f,%f,%d,%d', [MainDataP^.m_fOpen, MainDataP^.m_fHigh, MainDataP^.m_fLow, MainDataP^.m_fClose, v, o]);
      MyResultP^:=33;// 自己计算的返回给大智慧的数据
      Inc(MainDataP);
      Inc(MyResultP);
     
      Writeln(tfile, tstr);
    end;
  end;
  CloseFile(tfile);
  
  result:=1;
end;

exports
  TTT name'_TTT';

begin
end.
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/6/30 15:20:19
53楼: 没看见有问题,现在还有发现错误数据么?
大智慧看k线图的时候,缩放k线或者在k线上移动十字光标都会引发重新计算,如果自己dll里面有输出代码,重复计算时就会重复输出,反复读写硬盘。输出越多读写越多速度越慢。



另外最前面的帖子,当时我也是在练习中,所以会有部分错误。比如result,如果正常计算而且希望第一个返回数据有效,那么应该result:=0
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/7/1 16:08:58
54楼: 现在ok了。多谢。
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/1 16:58:01
55楼: 终于学会了通过把可变数组指向大智慧传来数据,来使用大智慧的数据。

现在操作大智慧传来数据就像用数组一样随意,返回结果也同样随意,不再需要被指针操作困扰了

请更多的关注下面这个例子,比以前改变了不少。


library TestDll;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils, Windows;
type
  MainDataType=record //基本数据
    DataDTime:Cardinal;//时间
    Open, High, Low, Close, Vol, Amount:Single;//开盘价,最高价,最低价,收盘价,成交量,成交额
    m_wAdvance, m_wDecline:WORD;//上涨家数(仅大盘有效) 下跌家数(仅大盘有效)
  end;
  ExDataType=record //扩展数据,用于描述分笔成交数据的买卖盘
    BuyPrice, BuyVol, SellPrice, SellVol:array[1..5] of single;//买1--买5价  买1--买5量  卖1--卖5价  卖1--卖5量
  end;
  DzhOutDataType=record //大智慧调用本dll时传出的数据格式
    DataSize:Cardinal;//结构大小
    DZHVersion:Cardinal;//调用软件版本(V2.10 : 0x210)
    DZHSerial:Cardinal;//调用软件序列号
    StkNo:PChar;//股票代码
    IsIndex:Boolean;//是否大盘
    DataCount:Integer;//数据数量(pData,pDataEx,pResultBuf数据数量)
    P_MainDataBuf:^MainDataType;//常规数据,注意:当m_nNumData=0时可能为 NULL
    P_ExtDataBuf:^ExDataType;//扩展数据,分笔成交买卖盘,注意:可能为 NULL
    m_nParam1Start:Cardinal;//参数1有效位置
    m_pfParam1:^Single;//调用参数1
    m_pfParam2:^Single;//调用参数2
    m_pfParam3:^Single;//调用参数3
    m_pfParam4:^Single;//调用参数4
    P_ResultBuf:^Single;//结果缓冲区
    DataType:Word;//分析周期
    m_pfFinData:^Single;//财务数据
  end;

{$R *.RES}

function TestFunc(var DzhData:DzhOutDataType):Integer; stdcall;
var
  DataCount, i, j:Integer;
  ArrMainData:array of MainDataType;//主数据数组  可变数组
  ArrExtData:array of ExDataType;//扩展数据数组  可变数组
  ArrResult:array of Single;//自己的计算结果数组   可变数组
  OutputString, FilenameString:string;
  OutputFile:TextFile;
begin
  if DzhData.DataCount>0 then
  begin
    DataCount:=DzhData.DataCount;// 数据数量
    SetLength(ArrMainData, DataCount);//设置  可变数组 主数据数组 的大小 和总数据量一样
    SetLength(ArrExtData, DataCount);//设置  可变数组 扩展数据数组 的大小 和总数据量一样
    SetLength(ArrResult, DataCount);//设置  可变数组 自己的计算结果数组 的大小 和总数据量一样
    ArrMainData:=@DzhData.P_MainDataBuf^;// 大智慧传入数据块的地址 赋给可变数组
    ArrExtData:=@DzhData.P_ExtDataBuf^;//同上
    ArrResult:=@DzhData.P_ResultBuf^;//同上
    OutPutString:='股票代码:'+DzhData.StkNo+#13#10;
    for i:=0 to DataCount-1 do //可变数组的下标从0开始  上限就是DataCount-1了,注意不要越界,否则大智慧会出错退出
    begin
      OutPutString:=OutPutString+FormatDateTime('"时间:"yyyy"年"mm"月"dd"日" hh:nn:ss'#13#10, ArrMainData[i].DataDTime/86400+25569);
      OutPutString:=OutPutString+'开盘价:'+FloatToStr(ArrMainData[i].open)+#13#10;
      OutPutString:=OutPutString+'最高价:'+FloatToStr(ArrMainData[i].High)+#13#10;
      OutPutString:=OutPutString+'最低价:'+FloatToStr(ArrMainData[i].Low)+#13#10;
      OutPutString:=OutPutString+'收盘价:'+FloatToStr(ArrMainData[i].Close)+#13#10;
      OutPutString:=OutPutString+'成交量:'+FloatToStr(ArrMainData[i].Vol)+#13#10;
      OutPutString:=OutPutString+'成交额:'+FloatToStr(ArrMainData[i].Amount)+#13#10;
      if DzhData.DataType=2 then //如果是 分笔数据,就输出5个委买卖数据
      begin
        for j:=5 downto 1 do OutPutString:=OutPutString+Format('卖%d  %g  %g'#13#10, [j, ArrExtData[i].SellPrice[j], ArrExtData[i].SellVol[j]]);
        OutPutString:=OutPutString+'----------'#13#10;
        for j:=1 to 5 do OutPutString:=OutPutString+Format('买%d  %g  %g'#13#10, [j, ArrExtData[i].BuyPrice[j], ArrExtData[i].BuyVol[j]]);
      end;
      OutPutString:=OutPutString+#13#10;
      ArrResult[i]:=i+1;// 计算返回给大智慧的结果 在这里实际就是周期数
    end;
    FilenameString:='c:\dzhoutput.txt';
    AssignFile(OutputFile, FilenameString);
    Rewrite(OutputFile);
    Writeln(OutPutFile, OutPutString);
    CloseFile(OutputFile);
    winexec(pchar('notepad.exe '+FilenameString), SW_SHOWMINNOACTIVE);//后台打开
    Result:=0;//向大智慧返回 有效数据起始位置
  end
  else
    Result:=-1;//向大智慧返回 此次调用无效
end;

exports
  TestFunc NAME'_TestFunc';

begin
end.
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/1 22:00:40
56楼: 按此在新窗口浏览图片 突然想起来,神马可变数组,那货叫做动态数组,哈,真没文化
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/1 23:23:31
57楼: 谢谢,学习了!
另外,动态数组是不是可以直接在前面定义而不是在函数里?这样后面的函数都可以调用了啊.
再有,请教一下上面这例子在实际运用中有什么作用呢?如果要在外部使用这些数据,那直接读取相关数据文件不是更方便些?至少可以自动转出全部个股的数据,而这样需要手动翻页才能得到一只个股的数据啊.
我想倒是可以这个方法存盘逐笔数据,可接口中没有关于逐笔数的相关定义啊!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/2 11:31:13
58楼: 动态数组定义还是参考delphi语法吧

55楼例子是表示可以像用数组一样使用大智慧传来的数据,实际使用中比我之前用指针的代码更能随意运用数据,因为用数组下标比调整指针指向更方便。

自己读数据文件更快而且可以批量处理,但是除权要自己做。dll可以在k线图里面直观看到自己的计算结果。两种方法各有优缺点。

这个接口其实是分析家的接口,大智慧买了分析家以后让这个接口继续有效,但是没有公开说明是否升级了新接口,我也找不到。逐笔数据好像是要盘中收的,盘后没得补,而且数据也同样不一定准确。如果大智慧不提供或者不公布相应接口,我也没办法
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/2 20:55:37
59楼: 谢谢!

Level-2的逐笔成交数据是缓存在dzh2\data\sh\TEMP目录下,文件名以证券代码为名,后辍是.L2D,如果重启大智慧,L2D文件会全部被删除,而且临时文件只有在浏览过该证券的逐笔成交后才会生成,如果有办法在重启大智慧之前将临时文件数据转存为另外的格式保存起来就好了!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/2 22:26:42
60楼: 这个就不清楚了,我还在用的免费版。我也不是很确信level2的数据有重要帮助,最近也可能没时间研究它。


如果level2数据不能快速下载,就算能成功靠自动翻页得到全部股票数据,按沪深2000股票计算,假设网络不错,2秒能成功翻一个股票,也要1小时才能完成,估计相当费事。而且还怕大智慧缺德的去加密数据。
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/2 23:30:39
61楼: 我本来也一直是用免费版的,深市有一段时间有免费的L2行情,就试用了一阵,因此习惯了10档行情,就选了个最低端的专业版.

个人感觉L2中的逐笔成交是最有用的,因为它最接近成交实际,能较为真实地反映出买卖双方的力度信息,用来做盯盘指标很具战意义,但由于现在不能实现逐笔成交自动预警,会错过很好的机会,当然,这只是对短线而已.

不过你倒是提醒了我一件事:如果取得数据太费时,对于盘中来讲也就失去了意义,而对于中长线行情有没有参考价值,由于不能保存历史数据也就无从考证,这也许可以用分笔成交来试试.

谢谢somewho!
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/3 12:05:04
62楼: 请教somewho:

DLL最前面的type申明中, 财务数据为 m_pfFinData:^Single;,这里是不是有点问题啊?这样怎么能取得到相关的财务数据呢?财务数据应分为很多项的啊,比如总股本,流通股,净利润等?
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/3 14:36:24
63楼: 那个只是从c的头文件转过来而已,估计是个single的数组。不知道多大,c的头文件里面说是36个元素,估计现在已经变化了。就好像那个c头文件里面还是3个委买卖位置,现在其实是5个了嘛。
我没有用它,所以一直没研究。可以自己试一下,看看到底现在那个数组有多大,每个元素保存的是什么数据。
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/3 17:22:11
64楼: 经过试验,55楼代码好像下面这3句都去掉也能正常,就是不事先设定动态数组的大小也能正常使用数据。好处是在大智慧里面速度快了。
有个小问题是编译虽然通过,但是有提示。而且要特别注意使用数组不要越界


    SetLength(ArrMainData, DataCount);//设置  可变数组 主数据数组 的大小 和总数据量一样
    SetLength(ArrExtData, DataCount);//设置  可变数组 扩展数据数组 的大小 和总数据量一样
    SetLength(ArrResult, DataCount);//设置  可变数组 自己的计算结果数组 的大小 和总数据量一样
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/4 8:42:48
65楼: 谢谢!

按你提示试了数组,没能取出值来按此在新窗口浏览图片
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/4 12:06:00
66楼: 55楼的代码,函数声明就已经变化了

function TestFunc(var DzhData:DzhOutDataType):Integer; stdcall;


如果完全复制55楼代码,还是不能正常操作数据么?
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/4 13:04:11
67楼: 哦,不是,55楼代码能正常运行.

我是说把财务数据当一个数组来操作,没能取出相应的值来.
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/4 16:08:00
68楼: 我随便试了下好像是没有数据。不过现在我也不想研究这个了。如果实在需要可以在大智慧里面把财务数据当参数传来用吧
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/4 18:52:56
69楼: 好的,谢谢!
----------------------------------------------
-
作者:
男 flashzheng (五蕴空) ▲▲▲▲▲ -
普通会员
2011/7/14 16:30:53
70楼: 请问一下somewho ,我搜了一些资料,发现网上都没有介绍股票涨幅的数据获取(或计算)方式, 不知道哪个dat文件里带有涨幅数据,如何提取? 如果没有dat带这都数据,这个指标是如何计算出来的? 十分感谢!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/15 10:30:16
71楼: to flashzheng:这个自己计算的,(本周期价/上周期价-1)×100就是百分比涨幅
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/15 22:54:12
72楼: somewhow你好!

请抽空帮研究一下怎样取出相关财务数据好吗?(m_pfFinData:^Single;//财务数据)

我只有你这一个老师,没其他人可请教了.

谢谢!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/17 14:02:47
73楼: to baojcs:最近没什么时间弄这个。之前也尝试过,好像没有读到什么数据。也许大智慧已经修改了某些数据结构,正如3个委托现在其实已经变成5个委托;也许这个网上搜来的头文件本来就有错。等过段时间我有空了再尝试一下吧。
----------------------------------------------
-
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2011/7/17 15:14:59
74楼: 强人,干脆好事做到底,把你的demo代码也贴出来吧;

相信对大家很有用的。
----------------------------------------------
青云论坛
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/17 20:22:40
75楼: 谢谢somewho!

不好意思,再三地打扰你了,实在是因为我刚开始学也只懂点delphi,而现在网在懂delphi同时又了解大智慧的人太少了(用C写DLL的多些,但C对我这个非专业者来说难学了点儿)。

等你有空的时候研究一下吧,再次感谢!
----------------------------------------------
-
作者:
男 flashzheng (五蕴空) ▲▲▲▲▲ -
普通会员
2011/7/18 10:49:59
76楼: somewhow你好!

to flashzheng:这个自己计算的,(本周期价/上周期价-1)×100就是百分比涨幅



这个对于前后两天未除权或分红的股票是适用的,但一旦遇到分红送股就不准确了,不知道DAT文件里是否有复权的数据,以及数据的存储块和数据结构如何?

如有这方面的资料还请分享下,谢谢!
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/7/20 1:33:00
77楼: 关于:提供Amibroker国内行情插件安装程序(http://blog.sina.com.cn/smarttradingsystem),实时数据是抓取sina网页的,但作者对其作品要求注册码认证,比较麻烦,所以想自己写。amibroker的datafeed plugin的dll的开发数据包在amibroker的网站上可以下载(http://www.amibroker.com/download.html).c++语言写的。其中QT的例子就可以用来写dataplugin.现正读其代码,有点晕。以下是plugin的cpp文件,贴出来看看大家有没有什么好的思路....有附上amibroker的开发包。

//////////
// Plugin.cpp
// Standard implementation file for AmiBroker data plug-ins
//
//////////
// Copyright (c) 2001-2009 AmiBroker.com. All rights reserved. 
//
// Users and possessors of this source code are hereby granted a nonexclusive, 
// royalty-free copyright license to use this code in individual and commercial software.
//
// AMIBROKER.COM MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE CODE FOR ANY PURPOSE. 
// IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. 
// AMIBROKER.COM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, 
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
// IN NO EVENT SHALL AMIBROKER.COM BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, OR 
// CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.
// 
// Any use of this source code must include the above notice, 
// in the user documentation and internal comments to the code.
//////////

#include "stdafx.h"
#include "Plugin.h"
#include "resource.h"
#include "ConfigDlg.h"


// These are the only two lines you need to change
#define PLUGIN_NAME "QuoteTracker data Plug-in"
#define VENDOR_NAME "Amibroker.com"
#define PLUGIN_VERSION 20001
#define PLUGIN_ID PIDCODE( 'Q', 'T', 'R', 'K')

// IMPORTANT: Define plugin type !!!
#define THIS_PLUGIN_TYPE PLUGIN_TYPE_DATA

//////////
// Data section
//////////
struct PluginInfo oPluginInfo =
{
    sizeof( struct PluginInfo ),
    THIS_PLUGIN_TYPE,    
    PLUGIN_VERSION,
    PLUGIN_ID,
    PLUGIN_NAME,
    VENDOR_NAME,
    13012679,
    387000
};

//////////
// Basic plug-in interface functions exported by DLL
//////////

PLUGINAPI int GetPluginInfo( struct PluginInfo *pInfo ) 

  *pInfo = oPluginInfo;

  return TRUE;
}


PLUGINAPI int Init(void) 

  AFX_MANAGE_STATE( AfxGetStaticModuleState() );

  return 1;
}   

PLUGINAPI int Release(void) 

  AFX_MANAGE_STATE( AfxGetStaticModuleState() );

  return 1;     // default implementation does nothing



//////////
// Configure function
// is called when user clicks "Configure" button
// in the File->Database Settings dialog
//
// It should show up configuration dialog for the plugin
//////////
PLUGINAPI int Configure( LPCTSTR pszPath, struct InfoSite *pSite )
{
  AFX_MANAGE_STATE( AfxGetStaticModuleState() );

  CConfigDlg oDlg;
  oDlg.m_pSite = pSite;

  if( oDlg.DoModal() == IDOK )
  {
  }

  return 1;
}

////////// 
// GetExtraData function is used
// to retrieve other data (fundamentals for example)
//
// In case of QuoteTracker plugin - it is not used
//////////
PLUGINAPI AmiVar GetExtraData( LPCTSTR pszTicker, LPCTSTR pszName, int nArraySize, int nPeriodicity, void* (*pfAlloc)(unsigned int nSize) )
{
  AmiVar var;

  var.type = VAR_NONE;
  var.val = 0;

  return var;
}

PLUGINAPI int SetTimeBase( int nTimeBase )
{
  return ( nTimeBase >= 60 && nTimeBase <= ( 24 * 60 * 60 ) )  ? 1 : 0;
}


//////////
//////////
// Start of data source specific code
//////////
//////////


//////////
// Constants
//////////
#define RETRY_COUNT 8
#define AGENT_NAME PLUGIN_NAME
enum
{
  STATUS_WAIT,
  STATUS_CONNECTED,
  STATUS_DISCONNECTED,
  STATUS_SHUTDOWN
};

//////////
// Globals
//////////

typedef CArray< struct Quotation, struct Quotation > CQuoteArray;

HWND g_hAmiBrokerWnd = NULL;

int    g_nPortNumber = 16239;
int    g_nRefreshInterval = 1;
BOOL  g_bAutoAddSymbols = TRUE;
int    g_nSymbolLimit = 100;
int    g_bOptimizedIntraday = TRUE;
int    g_nTimeShift = 0;
CString g_oServer = "127.0.0.1";

int    g_nStatus = STATUS_WAIT;
int    g_nRetryCount = RETRY_COUNT;

static struct RecentInfo *g_aInfos = NULL;
static int    RecentInfoSize = 0;



//////////
// GetSymbolLimit function is called by AmiBroker
// to find out the maximum number of real-time streaming symbols
// that can be displayed in the real-time quote window.
//////////
PLUGINAPI int GetSymbolLimit( void )
{
  return g_nSymbolLimit;  
}


//////////
// GetStatus function is called periodically
// (in on-idle processing) to retrieve the status of the plugin
// Returned status information (see PluginStatus structure definition)
// contains numeric status code  as well as short and long
// text descriptions of status.
//
// The highest nibble (4-bit part) of nStatus code
// represents type of status:
// 0 - OK, 1 - WARNING, 2 - MINOR ERROR, 3 - SEVERE ERROR
// that translate to color of status area:
// 0 - green, 1 - yellow, 2 - red, 3 - violet 

PLUGINAPI int GetStatus( struct PluginStatus *status )
{
  switch( g_nStatus )
  {
  case STATUS_WAIT:
    status->nStatusCode = 0x10000000;
    strcpy( status->szShortMessage, "WAIT" );
    strcpy( status->szLongMessage, "Waiting for connection" );
    status->clrStatusColor = RGB( 255, 255, 0 );
    break;
  case STATUS_CONNECTED:
    status->nStatusCode = 0x00000000;
    strcpy( status->szShortMessage, "OK" );
    strcpy( status->szLongMessage, "Connected OK" );
    status->clrStatusColor = RGB( 0, 255, 0 );
    break;
  case STATUS_DISCONNECTED:
    status->nStatusCode = 0x20000000;
    strcpy( status->szShortMessage, "ERR" );
    strcpy( status->szLongMessage, "Disconnected.\n\nPlease check if QuoteTracker is running.\nAmiBroker will try to reconnect in 15 seconds." );
    status->clrStatusColor = RGB( 255, 0, 0 );
    break;
  case STATUS_SHUTDOWN:
    status->nStatusCode = 0x30000000;
    strcpy( status->szShortMessage, "DOWN" );
    strcpy( status->szLongMessage, "Connection is shut down.\nWill not retry until you re-connect manually." );
    status->clrStatusColor = RGB( 192, 0, 192 );
    break;
  default:
    strcpy( status->szShortMessage, "Unkn" );
    strcpy( status->szLongMessage, "Unknown status" );
    status->clrStatusColor = RGB( 255, 255, 255 );
    break;
  }

  return 1;
}

// Quote Tracker reports all times in US eastern time
// this is not a problem for intraday because
// AmiBroker supports time shifting 
// File->Database Settings->Intraday Settings
// but the problem exists for EOD
// quotes when QT reports' previous day for Australia for example

int GetTimeOffset( void )
{
  // set offset initially to 5 hours (difference between GMT and EST)
  int nOffset = 5;

  TIME_ZONE_INFORMATION tzinfo;
  DWORD dw = GetTimeZoneInformation(&tzinfo);
  if (dw == 0xFFFFFFFF)
  {
    return -1;
  }

  if (dw == TIME_ZONE_ID_DAYLIGHT)
    nOffset -= ((tzinfo.Bias + tzinfo.DaylightBias))/60;
  else if (dw == TIME_ZONE_ID_STANDARD)
    nOffset -= ((tzinfo.Bias + tzinfo.StandardBias))/60;
  else
    nOffset -= (tzinfo.Bias)/60;

  return nOffset;
}

//////////
// IsQuoteTrackerRunning
// is a helper function that detects if QuoteTracker
// window is open
//////////
BOOL IsQuoteTrackerRunning( void )
{
  HANDLE hMutex = CreateMutex( NULL, FALSE, "QuoteTrkr" );

  BOOL bOK =  GetLastError() == ERROR_ALREADY_EXISTS;

  if( hMutex )
  {
    CloseHandle( hMutex );
  }


  return bOK;
}



//////////
// GrowRecentInfoIfNecessary function
// checks the size of RecentInfo table and
// grows the table if needed
//////////

void GrowRecentInfoIfNecessary( int iSymbol )
{
  if( g_aInfos == NULL || iSymbol >= RecentInfoSize )
  {
    RecentInfoSize += 200;
    g_aInfos = (struct RecentInfo *)realloc( g_aInfos, sizeof( struct RecentInfo ) * RecentInfoSize );

    memset( g_aInfos + RecentInfoSize - 200, 0, sizeof( struct RecentInfo ) * 200 );
  }
}

//////////
// FindRecentInfo function
// searches for RecentInfo of requested ticker 
// and returns the pointer to the array element
// holding data for given ticker 
//
// Note that this function performs linear scan
// that is not optimal in performance terms, but
// since we don't track more than a few hundred symbols
// with QuoteTracker this does not affect the performance
// that much considering the fact that it is called only
// a few times per second.
struct RecentInfo *FindRecentInfo( LPCTSTR pszTicker )
{
  struct RecentInfo *ri = NULL;

  for( int iSymbol = 0; g_aInfos && iSymbol < RecentInfoSize && g_aInfos[ iSymbol ].Name && g_aInfos[ iSymbol ].Name[ 0 ]; iSymbol++ )
  {
    if( ! stricmp( g_aInfos[ iSymbol ].Name, pszTicker ) )
    {
      ri = &g_aInfos[ iSymbol ];
      break;
    }
  }

  return ri;
}


//////////
// AddToQTPortfolio function
// adds specified ticker to the QuoteTracker current portfolio
//////////

BOOL AddToQTPortfolio( LPCTSTR pszTicker )
{
  BOOL bOK = TRUE;

  try
  {
    CString oURL;
    oURL.Format( "http://%s:%d/req?AddStocksToPort(CURRENT,%s)", g_oServer, g_nPortNumber, pszTicker );

    CInternetSession oSession( AGENT_NAME, 1, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_DONT_CACHE );
    CStdioFile *poFile = oSession.OpenURL( oURL,1, INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE );

    CString oLine;
    if( poFile && poFile->ReadString( oLine ) && oLine.Left( 2 ) == "OK" ) bOK = TRUE;


    poFile->Close();

    delete poFile;

    oSession.Close();
  }
  catch( CInternetException *e )
  {
    e->Delete();

    g_nStatus = STATUS_DISCONNECTED;
  }

  return bOK;
}


//////////
// GetAvailableSymbols function
// retrieves the list of symbols present and active
// in all QuoteTracker portfolios
// This function is called when user clicks
// "Retrieve Symbols" button in Configure dialog
//////////
CString GetAvailableSymbols( void )
{
  BOOL bOK = TRUE;

  CString oResult;

  try
  {
    CString oURL;
    oURL.Format( "http://%s:%d/req?EnumSymbols(ACTIVE)", g_oServer, g_nPortNumber );

    CInternetSession oSession( AGENT_NAME, 1, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_DONT_CACHE );
    CStdioFile *poFile = oSession.OpenURL( oURL,1, INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE );

    CString oLine;
    if( poFile && poFile->ReadString( oLine ) && oLine.Left( 2 ) == "OK" ) 
    {
      bOK = TRUE;

      poFile->ReadString( oResult );
    }

    poFile->Close();

    delete poFile;

    oSession.Close();
  }
  catch( CInternetException *e )
  {
    e->Delete();

    g_nStatus = STATUS_DISCONNECTED;
  }

  return oResult;
}

//////////
// FindOrAddRecentInfo
// searches RecentInfo table for record corresponding
// to requested ticker and if it is not found
// it adds new record at the end of the table
//////////
struct RecentInfo *FindOrAddRecentInfo( LPCTSTR pszTicker )
{
  struct RecentInfo *ri = NULL;

  if( g_aInfos == NULL ) return NULL;

  for( int iSymbol = 0; g_aInfos && iSymbol < RecentInfoSize && g_aInfos[ iSymbol ].Name && g_aInfos[ iSymbol ].Name[ 0 ]; iSymbol++ )
  {
    if( ! stricmp( g_aInfos[ iSymbol ].Name, pszTicker ) )
    {
      ri = &g_aInfos[ iSymbol ];
      return ri;  // already exists - do not need to add anything
    }
  }

  if( iSymbol < g_nSymbolLimit )
  {
    if( AddToQTPortfolio( pszTicker ) )
    {
      GrowRecentInfoIfNecessary( iSymbol );
      ri = &g_aInfos[ iSymbol ];
      strcpy( ri->Name, pszTicker );
      ri->nStatus = 0;
    }
  }

  return ri;
}

//////////
// Forward declaration of Timer callback function
//////////
VOID CALLBACK OnTimerProc( HWND, UINT, UINT_PTR, DWORD );


//////////
// SetupRetry function
// is a helper function that controls
// retries when connect attempt fails.
// After g_nRetryCount attempts it switches to SHUTDOWN state
//////////

void SetupRetry( void )
{
  if( --g_nRetryCount )
  {
    SetTimer( g_hAmiBrokerWnd, 198, 15000, (TIMERPROC)OnTimerProc );
    g_nStatus = STATUS_DISCONNECTED;
  }
  else
  {
    g_nStatus = STATUS_SHUTDOWN;
  }
}

//////////
// safe_atoi and safe_atof
// are helper function that work the same as
// atoi and atof but do not produce
// crash when NULL string pointer is passed
// as parameter
//////////

int  safe_atoi( const char *string )
{
  if( string == NULL ) return 0;

  return atoi( string );
}

double safe_atof( const char *string )
{
  if( string == NULL ) return 0.0f;

  return atof( string );
}

//////////
// Timer Callback procedure
// It is called periodically to retrieve
// current quotes from QuoteTracker.
// It just issues request getLastQuote(ACTIVE)
// to QT HTTP server, reads the response
// and updates corresponding fields of recent info table
//////////
 
VOID CALLBACK OnTimerProc(
  HWND hwnd,         // handle to window
  UINT uMsg,         // WM_TIMER message
  UINT_PTR idEvent,  // timer identifier
  DWORD dwTime       // current system time
  )
{
  struct RecentInfo *ri;

  if( idEvent == 199 || idEvent == 198 )
  {
    if( ! IsQuoteTrackerRunning() )
    {
      KillTimer( g_hAmiBrokerWnd, idEvent );
      SetupRetry();
      return;
    }
    
    try
    {
      CInternetSession oSession( AGENT_NAME, 1, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_DONT_CACHE );
      
      CString oURL;

      oURL.Format("http://%s:%d/req?getLastQuote(ACTIVE)", g_oServer, g_nPortNumber );

      CStdioFile *poFile = oSession.OpenURL( oURL,1, INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE );

      if( poFile )
      {
        CString oLine;

        int iSymbol = 0;

        if( poFile->ReadString( oLine ) )
        {
          if( oLine == "OK" )
          {
            while( poFile->ReadString( oLine ) )
            {
              TRACE( oLine + "\n" );
              
              if( oLine.GetLength() < 20 ) continue;//AfxMessageBox("test");

              GrowRecentInfoIfNecessary( iSymbol );

              ri = &g_aInfos[ iSymbol ];
                  
              char *line = oLine.LockBuffer();


              ri->nStructSize = sizeof( struct RecentInfo );
              strcpy( ri->Name, strtok( line, "," ) );

              int month = safe_atoi( strtok( NULL, "/-." ) );
              int day = safe_atoi( strtok( NULL, "/-." ) );
              int year = safe_atoi( strtok( NULL, "," ) );

              int hour = safe_atoi( strtok( NULL, ":." ) );
              int minute = safe_atoi( strtok( NULL, ":." ) );
              int second = safe_atoi( strtok( NULL, "," ) );

              float fOldLast, fOldBid, fOldAsk;

              fOldLast = ri->fLast;
              fOldBid = ri->fBid;
              fOldAsk = ri->fAsk;

              //->nTimeUpdate = ri->nTimeUpdate & ~0x07 + time(NULL) & 0x7;


              ri->fLast = (float) safe_atof( strtok( NULL, "," ) );
              ri->fBid = (float) safe_atof( strtok( NULL, "," ) );
              ri->fAsk = (float) safe_atof( strtok( NULL, "," ) );
              ri->fChange = (float) safe_atof( strtok( NULL, "," ) );

              // tick
              strtok( NULL, "," );

              double dTemp = safe_atof( strtok( NULL, "," ) ); 
              ri->iTotalVol = (int) dTemp;
              ri->fTotalVol = (float) dTemp;
               ri->fHigh = (float) safe_atof( strtok( NULL, "," ) );
              ri->fLow = (float) safe_atof( strtok( NULL, "," ) );
              ri->iBidSize = safe_atoi( strtok( NULL, "," ) );
              ri->iAskSize = safe_atoi( strtok( NULL, "," ) );

              dTemp = safe_atof( strtok( NULL, "," ) ); 
              ri->iTradeVol = (int) dTemp;
              ri->fTradeVol = (float) dTemp;

              strtok( NULL, "," );
              strtok( NULL, "," );
              ri->fOpen = (float) safe_atof( strtok( NULL, "," ) );
              ri->f52WeekLow = (float) safe_atof( strtok( NULL, "," ) );
              ri->f52WeekHigh = (float) safe_atof( strtok( NULL, "," ) );


              ri->nDateChange = 10000 * year + 100 * month + day; 
              ri->nTimeChange = 10000 * hour + 100 * minute + second;

              if( ri->fLast != fOldLast || ri->fAsk != fOldAsk || ri->fBid != fOldBid )
              {
                ri->nDateUpdate = ri->nDateChange;
                ri->nTimeUpdate = ri->nTimeChange;
              }

              ri->nBitmap = RI_LAST | ( ri->fOpen ? RI_OPEN : 0 ) | ( ri->fHigh ? RI_HIGHLOW : 0 ) | RI_TRADEVOL | RI_52WEEK | 
                RI_TOTALVOL | RI_PREVCHANGE | RI_BID | RI_ASK | RI_DATEUPDATE | RI_DATECHANGE;  

              ri->nStatus = RI_STATUS_UPDATE | RI_STATUS_BIDASK | RI_STATUS_TRADE | RI_STATUS_BARSREADY;


              oLine.UnlockBuffer();

              iSymbol++;
            }
          }
        }

        ::SendMessage( g_hAmiBrokerWnd, WM_USER_STREAMING_UPDATE, 0, 0 );

        poFile->Close();
        delete poFile;
    
      
        g_nStatus = STATUS_CONNECTED;
        g_nRetryCount = RETRY_COUNT;
      }

      oSession.Close();
    }
    catch( CInternetException *e )
    {
      e->Delete();
      KillTimer( g_hAmiBrokerWnd, idEvent );
      SetupRetry();
      return;
    } 

  }

  if( idEvent == 198 )
  {
    KillTimer( g_hAmiBrokerWnd, 198 );
    SetTimer( g_hAmiBrokerWnd, 199, g_nRefreshInterval * 1000, (TIMERPROC)OnTimerProc );
  }
}

//////////
// GetIntradayBars() function
// retrieves Time/Sales records starting from nStartTime from QuoteTracker HTTP server
// and compresses them into N-minute records of 
// requested periodicity (interval).
//////////
BOOL GetIntradayBars( LPCTSTR pszTicker, int nPeriodicity, int nStartTime, CQuoteArray *pCurQuotes )
{
  BOOL bResult = FALSE;
  
  try
  {
    CInternetSession oSession( AGENT_NAME, 1, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_DONT_CACHE );
    
    CString oURL;

    oURL.Format("http://%s:%d/req?GetTimeSales(%s,%d,0)", g_oServer, g_nPortNumber, pszTicker, nStartTime );

    CStdioFile *poFile = oSession.OpenURL( oURL,1, INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE );

    if( poFile )
    {
      CString oLine;

      int iSymbol = 0;


      if( poFile->ReadString( oLine ) )
      {
        if( oLine == "OK" )
        {
          union AmiDate lastdate;
          union AmiDate curdate;

          lastdate.Date = curdate.Date = 0;

          float fCumVolume = 0;
          float fPrevCumVolume = 0;

          while( poFile->ReadString( oLine ) )
          {
            TRACE( oLine + "\n" );

            char *line = oLine.LockBuffer();

            curdate.PackDate.Month = safe_atoi( strtok( line, "/-." ) );
            curdate.PackDate.Day = safe_atoi( strtok( NULL, "/-." ) );
            curdate.PackDate.Year = safe_atoi( strtok( NULL, "," ) );

            curdate.PackDate.Hour = atoi( strtok( NULL, ":." ) );
            curdate.PackDate.Minute = atoi( strtok( NULL, ":." ) );
            curdate.PackDate.Second = 0;  // 

            if( nPeriodicity > 60 && nPeriodicity < 3600 )
            {
              curdate.PackDate.Minute = (nPeriodicity/60) * ( curdate.PackDate.Minute / (nPeriodicity/60) );
            }

            if( nPeriodicity == 3600 ) curdate.PackDate.Minute = 0;
            
            strtok( NULL, "," );    // skip seconds - we are using only minutes

            float fLast = (float) atof( strtok( NULL, "," ) );

            strtok( NULL, "," );    // skip Ask
            strtok( NULL, "," );    // skip Bid

            float fVolume = (float) atof( strtok( NULL, "," ) );

            fCumVolume += fVolume;

            fVolume = max( 0, fCumVolume - fPrevCumVolume );

            if( lastdate.Date < curdate.Date )
            {
              lastdate.Date = curdate.Date;

              struct Quotation NewQT;
              NewQT.DateTime = curdate;
              NewQT.Open = NewQT.High = NewQT.Low = NewQT.Price = fLast;
              NewQT.Volume = fVolume;
              NewQT.OpenInterest = NewQT.AuxData1 = NewQT.AuxData2 = 0;

              pCurQuotes->Add( NewQT );

            }
            else
            {
              struct Quotation *qt = & pCurQuotes->GetData()[ pCurQuotes->GetUpperBound() ];

              qt->Price = fLast;
              qt->Volume += fVolume;
              if( qt->High < fLast ) qt->High = fLast;
              if( qt->Low > fLast ) qt->Low = fLast;
            }
              
            oLine.UnlockBuffer();

            fPrevCumVolume = fCumVolume;

          }

          bResult = TRUE;

        }
      }

      poFile->Close();
      delete poFile;

    }

    oSession.Close();
  }
  catch( CInternetException *e )
  {
    e->Delete();
    g_nStatus = STATUS_DISCONNECTED;
  } 

  return bResult;
}

//////////
// BlendQuoteArrays is general-purpose function
// (not data-source dependent) that merges
// quotes that are already present in AmIBroker's database
// with new quotes retrieved from the data source
//////////

int BlendQuoteArrays( struct Quotation *pQuotes, int nPeriodicity, int nLastValid, int nSize, CQuoteArray *pCurQuotes )
{
  int iQty = pCurQuotes->GetSize();

  DATE_TIME_INT nFirstDate = iQty == 0 ? (DATE_TIME_INT)-1 : pCurQuotes->GetAt( 0 ).DateTime.Date;

  for( int iStart = nLastValid; iStart >= 0; iStart-- )
  {
    // find last which is not greater than the one we
    // have from real time
    if( pQuotes[ iStart ].DateTime.Date < nFirstDate ) break;
  }

  iStart++; // start with next

  int iSrc = 0; // by default we start with the first bar we have

  // if we have more RT data than requested
  // we fill entire array
  // and start with the first we can
  //
  // <----------> nSize
  // <----------> iQty
  //       ^ we have to start here iStart
  //
  if( iQty > nSize )
  {
    iStart = 0; 
    iSrc = iQty - nSize;
  }
  else
  if( iQty + iStart > nSize )
  {
    // Now handle the situation like this:
    // <----------> nSize
    //    <----------> iQty
    // <------>
    //       iStart

    memmove( pQuotes, pQuotes + iQty + iStart - nSize, sizeof( Quotation ) * ( nSize - iQty ) );
  
    iStart = nSize - iQty;
    iSrc = 0;
  }

  int iNumQuotes = min( nSize - iStart, iQty - iSrc );  

  if( iNumQuotes > 0 )
  {
    memcpy( pQuotes + iStart, pCurQuotes->GetData() + iSrc, iNumQuotes * sizeof( Quotation ) );
  }
  else
  {
    iNumQuotes = 0;
  }

  return iStart + iNumQuotes;

}



//////////
// This function adjusts date and time
// according to time zone shift - this is needed
// because QuoteTracker returns all times in EST time zone.
int GetAdjustedDate( int nDate, int nTime )
{
  int nYear = (nDate / 10000);
  int nMonth = (nDate / 100 ) % 100;
  int nDay = nDate % 100;

  int nHour = (nTime / 10000);
  int nMinute = (nTime / 100 ) % 100;
  int nSecond = nTime % 100;

  CTime oTime( nYear, nMonth, nDay, nHour, nMinute, nSecond );

  oTime += CTimeSpan( 0, g_nTimeShift, 0, 0 );

  return 10000 * oTime.GetYear() + 100 * oTime.GetMonth() + oTime.GetDay();

}


// LEGACY format support
// convert back and forth between old and new format
//
// WARNING: it is highly inefficient and should be avoided
// So this is left just for maintaning compatibility,
// not for performance

PLUGINAPI int GetQuotes( LPCTSTR pszTicker, int nPeriodicity, int nLastValid, int nSize, struct QuotationFormat4 *pQuotes  )
{
  AFX_MANAGE_STATE( AfxGetStaticModuleState() );

  Quotation *pQuote5 = (struct Quotation *) malloc( nSize * sizeof( Quotation ) );

  QuotationFormat4 *src = pQuotes; 
  Quotation *dst = pQuote5;

  int i;

  for( i = 0; i <= nLastValid; i++, src++, dst++ )
  {
    ConvertFormat4Quote( src, dst );
  }

  int nQty = GetQuotesEx( pszTicker, nPeriodicity, nLastValid, nSize, pQuote5, NULL );

  dst = pQuote5;
  src = pQuotes;

  for( i = 0; i < nQty; i++, dst++, src++ )
  {
    ConvertFormat5Quote( dst, src );
  }

  free( pQuote5 );

  return nQty;
}

//////////
// GetQuotesEx is a most important function
// for every data plugin
// it is called by AmiBroker everytime AmiBroker
// needs new data for given symbol.
//
// Internally AmiBroker caches response obtained
// from GetQuotes function but you may force it to 
// get new data by sending appropriate message to AmiBroker
// main window.
//
//
// When AmiBroker calls GetQuotes function it allocates
// array of quotations of size equal to default number of bars as set in
// File->Database Settings,
// and fills the array with quotes that are already present in the
// database.
// Filled area covers array elements from zero to nLastValid
//
// In your DLL you can update the array with more recent quotes.
// Depending on the data source you can either fill entire array
// from the scratch (Metastock, TC2000, QP2 plugins do that)
// or just add/update a few recent bars and leave the remaining bars
// untouched (eSignal, myTrack, QuoteTracker plugins do that)

PLUGINAPI int GetQuotesEx( LPCTSTR pszTicker, int nPeriodicity, int nLastValid, int nSize, struct Quotation *pQuotes, GQEContext *pContext  )
{
  AFX_MANAGE_STATE( AfxGetStaticModuleState() );

  if( g_nStatus >= STATUS_DISCONNECTED ) return nLastValid + 1;

  struct RecentInfo *ri;
  
  if( g_bAutoAddSymbols )
    ri = FindOrAddRecentInfo( pszTicker );
  else
    ri = FindRecentInfo( pszTicker );

  if( nPeriodicity == 24 * 60 * 60 ) // daily
  {
    if( ri && nSize > 0 && ri->nStatus && ri->fLast )
    {
      BOOL bTooOld = FALSE;

      int nCurDate = GetAdjustedDate( ri->nDateUpdate, ri->nTimeUpdate );

      if( nLastValid >= 0 )
      {
        int nLastDate = ( pQuotes[ nLastValid ].DateTime.PackDate.Year ) * 10000  +
                pQuotes[ nLastValid ].DateTime.PackDate.Month * 100 +
                pQuotes[ nLastValid ].DateTime.PackDate.Day;


        if( nCurDate < nLastDate ) bTooOld = TRUE; 
        if( nCurDate > nLastDate ) nLastValid++;
      }
      else
      {
        nLastValid = 0;
      }
      
      if( nSize > 0 && nLastValid == nSize ) 
      {
        memmove( pQuotes, pQuotes + 1, ( nSize - 1 ) * sizeof (struct Quotation ) );
        nLastValid--;
      }

      if( ! bTooOld )
      {
        struct Quotation *qt = &pQuotes[ nLastValid ];

        qt->DateTime.Date = DAILY_MASK;
        qt->DateTime.PackDate.Year = (nCurDate / 10000) ;
        qt->DateTime.PackDate.Month = (nCurDate / 100 ) % 100;
        qt->DateTime.PackDate.Day = nCurDate % 100;

        qt->Open = ri->fOpen ? ri->fOpen : ri->fLast;
        qt->Price = ri->fLast;
        qt->High = ri->fHigh ? ri->fHigh : ri->fLast;
        qt->Low = ri->fLow ? ri->fLow : ri->fLast;
        qt->Volume = ri->fTotalVol;
        qt->OpenInterest = 0;
        qt->AuxData1 = 0;
        qt->AuxData2 = 0;
      }
    }

    return nLastValid + 1;
  }
  else
  {
    static AmiDate oToday;
  
    CQuoteArray oCurQuotes;

    if( oToday.Date == 0 )
    {
      //
      // get last trading day date
      //
      SYSTEMTIME oTime;
      GetSystemTime( &oTime );

      if( oTime.wDayOfWeek == 0 && oTime.wDay > 2 ) oTime.wDay -=2; /* sunday */
      if( oTime.wDayOfWeek == 6 && oTime.wDay > 1 ) oTime.wDay -=1; /* saturday */

      oToday.PackDate.Year = oTime.wYear;
      oToday.PackDate.Month = oTime.wMonth;
      oToday.PackDate.Day = oTime.wDay;
    }
      
    int nStartTime = 0;

    static int iAccessCounter = 0;

    // 
    // This strange looking code is provided because QuoteTracker
    // generates bar for non trading hours that can fool the checking routine
    // So every 16-th refresh we are checking 100 bars back (instead of 4)
    // if we have data for today or yesterday
    // 
    int iCheckBack = ( (iAccessCounter++) & 0xF ) ? 4 : 100;

    if( g_bOptimizedIntraday && nLastValid > iCheckBack )
    {
      AmiDate oBarDate = pQuotes[ nLastValid - iCheckBack ].DateTime;

      if( ( ( oBarDate.Date & DAILY_MASK ) != DAILY_MASK ) && /* not EOD quote */
        ( oBarDate.Date & ~DAILY_MASK ) == oToday.Date /* intraday for current day */ )
      {
        nStartTime = 10000 * oBarDate.PackDate.Hour + 100 * oBarDate.PackDate.Minute;
      }
    }      

    GetIntradayBars( pszTicker, nPeriodicity, nStartTime, &oCurQuotes );
  
    return BlendQuoteArrays( pQuotes, nPeriodicity, nLastValid, nSize, &oCurQuotes );
  }
}


//////////
// Notify function implementation
//
// Notify() is called by AmiBroker
// in the various circumstances including:
// 1. database is loaded/unloaded
// 2. right mouse button is clicked in the 
//    plugin status area of AmiBroker's status bar.
//
// The implementation may provide special handling
// of such events.
//////////

PLUGINAPI int Notify(struct PluginNotification *pn) 

  AFX_MANAGE_STATE( AfxGetStaticModuleState() );

  if( g_hAmiBrokerWnd == NULL && ( pn->nReason & REASON_DATABASE_LOADED ) )
  {
    g_hAmiBrokerWnd = pn->hMainWnd;

    g_nTimeShift = GetTimeOffset();

    g_nTimeShift = AfxGetApp()->GetProfileInt( "QuoteTracker", "TimeShift", g_nTimeShift );
    TRACE("TIME OFFSET %d\n", g_nTimeShift );

    g_oServer = AfxGetApp()->GetProfileString( "QuoteTracker", "Server", "127.0.0.1" );
    g_nPortNumber = AfxGetApp()->GetProfileInt( "QuoteTracker", "Port", 16239 );
    g_nRefreshInterval = AfxGetApp()->GetProfileInt( "QuoteTracker", "RefreshInterval", 5 );
    g_bAutoAddSymbols = AfxGetApp()->GetProfileInt( "QuoteTracker", "AutoAddSymbols", 1 );
    g_nSymbolLimit = AfxGetApp()->GetProfileInt( "QuoteTracker", "SymbolLimit", 100 );
    g_bOptimizedIntraday = AfxGetApp()->GetProfileInt( "QuoteTracker", "OptimizedIntraday", 1 );

    g_nStatus = STATUS_WAIT;

    SetTimer( g_hAmiBrokerWnd, 198, 1000, (TIMERPROC)OnTimerProc );

  }

  if( pn->nReason & REASON_DATABASE_UNLOADED ) 
  {
    KillTimer( g_hAmiBrokerWnd, 198 );
    KillTimer( g_hAmiBrokerWnd, 199 );
    g_hAmiBrokerWnd = NULL;

    free( g_aInfos );
    g_aInfos = NULL;
  }


  if( pn->nReason & REASON_STATUS_RMBCLICK ) 
  {
    CPoint pt;

    GetCursorPos( &pt );

    CMenu oMenu; 
    CMenu *poPopup = NULL;

    oMenu.LoadMenu( IDR_STATUS_MENU );

    poPopup = oMenu.GetSubMenu( 0 );

    if( g_nStatus == STATUS_CONNECTED )
    {
      poPopup->EnableMenuItem(ID_STATUS_RECONNECT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED ); 
    }

    if( g_nStatus == STATUS_SHUTDOWN )
    {
      poPopup->EnableMenuItem(ID_STATUS_SHUTDOWN, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED ); 
    }

    //
    // Show popup window
    // 
    // Note: specyfing   TPM_NONOTIFY is ESSENTIAL - otherwise it crashes !!!
    // because of the fact that cool menus look for accellerators and this fails
    // when resources are switched to DLL ones.
    //

    int nResponse =  poPopup->TrackPopupMenu( TPM_NONOTIFY | TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, CWnd::FromHandle( pn->hMainWnd ) );

    switch( nResponse )
    {
    case ID_STATUS_RECONNECT:
      KillTimer( g_hAmiBrokerWnd, 198 );
      KillTimer( g_hAmiBrokerWnd, 199 );
      SetTimer( g_hAmiBrokerWnd, 198, 1000, (TIMERPROC)OnTimerProc );
      break;
    case ID_STATUS_SHUTDOWN:
      KillTimer( g_hAmiBrokerWnd, 198 );
      KillTimer( g_hAmiBrokerWnd, 199 );
      g_nStatus = STATUS_SHUTDOWN;
      break;
    }
  
  }

  return 1;
}   

//////////
// GetRecentInfo function is called by
// real-time quote window to retrieve the most
// recent quote and other data
// see the definition of RecentInfo structure
// in the Plugin.h file
//////////

PLUGINAPI struct RecentInfo * GetRecentInfo( LPCTSTR pszTicker )
{
  return FindRecentInfo( pszTicker );
}
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/20 16:41:53
78楼: to qingyun:55楼的就是最新的代码了,把这3行去掉也可以正常使用,而且会快一点
   SetLength(ArrMainData, DataCount);
    SetLength(ArrExtData, DataCount);
    SetLength(ArrResult, DataCount);

主要数据都已经可以自由操纵了,怎样去实现自己的目的就要靠自己了
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/20 16:55:35
79楼: to  baojcs:再试了一下,读的几十个都是0,我也不知道具体原因,不知道是不是大智慧已经不传出财务数据了。如果看见网上有些什么说法,请告诉我,我也再试试
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/20 19:44:14
80楼: 谢谢somewho,费心了!

我试着用C来取值,按数组对待,结果也是0!

我也打电话问过大智慧的技术支持,回答是搞不清楚,无语!

还有什么方法能在DLL中获取到个股的财务数据(目前最需要得到流通股数量)?

如果按你68楼说"在大智慧里面把财务数据当参数传来用",但参数不能传变量,这办法行不通啊!
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/21 8:32:53
81楼: to baojcs:大智慧公式里面,"mydll@testfun"(x1,x2,x3,x4);就可以传4个参数给自己的函数了
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/21 8:46:30
82楼: to flashzheng:大智慧传给dll的数据是和k线图里面一样的。k线图设置成什么样的除权,传给dll的数据就是什么样的除权数据。

以前除权数据是保存在STKINFO.DAT文件里面。现在的大智慧已经有STKINFO60.DAT,STKINFO70.DAT,我没有去研究具体数据结构。

日线数据DAY.DAT,5分钟数据MIN.DAT保存的都是未经除权处理的数据。38楼的代码是读分笔的,但是其实也差不多。主要区别是:日线5分钟线如下
  DzhStockInfoType=record
    StockNo:array[1..10] of char;
    DataCou, Offset:Smallint;
    BlockPo:array[1..25] of word;//Smallint;
  end;
  DzhDayDataType=record
    DataTime:Cardinal;
    Open, High, Low, Close, Vol, Amount:single;
    upcou, downcou:Smallint;
  end;
读取具体数据时,日线5分钟线都一样如下
          FileSeek(iFileHandle, stkinfodata.BlockPo[i]*8192+266240, 0);
          FileRead(iFileHandle, tempdata, 8192);

分笔文件是
          FileSeek(iFileHandle, stkinfodata.blockpo[i]*12272+266240, 0);
          FileRead(iFileHandle, tempdata, 12272);

不过这些都是以前的代码,应该有不完善的地方,请自行测试修改。而且日线5分钟线不需要像分笔那样进行委托错误处理
需要说明的是:日线Offset基本为0,无用。但是5分钟线Offset经常不为0,需要对读取出来的数据进行偏移。
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/21 14:07:39
83楼: 回somewho第81楼:

我的意思是参数只能传常数而不能传变量,而每一只个股的财务数据是不一样的,这样不能将个股的相关财务数据以变量的形式自动传给DLL,那这DLL只能针对一个股票计算而不能通用了.
----------------------------------------------
-
作者:
男 somewho (somewho) ▲▲▲▲▲ -
普通会员
2011/7/22 9:11:26
84楼: 补充一下82楼的代码,那个FileRead(iFileHandle, tempdata, 8192);里面的8192其实就是tempdata数组的大小,日线5分钟线的tempdata数组和分笔的tempdata的维数是不一样的,我记不清多少了,好像是256



to baojcs的83楼,不是很理解你的问题。
dll的缺陷就是只能针对单个股票,不能进行横向比较
但是在大智慧的公式里面,诸如FINANCE2(16)这样的,得到的就是对于此个股的财务数据。用"mydll@testfun"(FINANCE2(16));这样就可以传了
----------------------------------------------
-
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/7/22 18:19:42
85楼: 非常感谢somewho,问题解决了!

原来我自已犯了一个低级错误,本来接口第一个参数是可以传序列参数的,我却在DLL中限定了只能传常量,所以试过用"mydll@testfun"(FINANCE2(16))这样来传财务数据未果!

再次感谢somewho朋友!
----------------------------------------------
-
作者:
男 newstar20088 (parkcn) ▲▲▲▲▲ -
普通会员
2011/8/1 18:22:59
86楼: python:请帮助完善代码功能,想加入定时功能,最重要是如何设定向新浪查询的方式,不至于被封ip.

该代码是向新浪股票接口-http://hq.sinajs.cn/list=sh600066查询获得股票实时数据(虽然有延迟几秒,但可以接受),据网上资料:只能一次最多查150支股票左右,每秒要向新浪发起多次查询, 不知道会不会封IP?目的1.加入定时功能,做到每5秒查询整个股票市场的数据(几千只股票).2.如何设定向新浪查询的方式,不至于被封ip。
另想用c++做data plugin供外软amibroker用,谈谈思路,看高手有什么建议。
1.根据amibroke的开发包里QT的data plugin代码修改一下指向嵌入数据库sqlite。
2.QT的数据源可以使用通达信,大智慧的历史数据,实时数据可用新浪接口,该数据可以用嵌入数据库sqlite导入,然后用QT的data plugin取出供amibroke使用就ok了。
python查询新浪股票数据代码一下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import MySQLdb
import string
import time
import random
import urllib2

print "数据库连接版本:" + MySQLdb.__version__ + ",正在读取网络数据,稍待..."

def get_stock_day(url = 'sh000001'):
    
    url = "http://hq.sinajs.cn/list="; + url
    s = urllib2.urlopen(url).read() #获取上证指数的当日行情数据
    #格式如下:var hq_str_sh000001="上证指数,2751.973,2751.527,2759.575,……,2010-12-30,15:03:04";
    #截取指定字符之后到最后三个字符之前的内容(我想是因为包含换行符才是3)
    #注意:这里获取的字串的编码为GB2312,数据类型是字符串str,可以在这里将其按照其对应的编码,即GB2312转换成Unicode,也可以在之后入库前转换
    #print type(s)
    #s = unicode(s,"GB2312")
    return s

def stock_save(stock_code = 'sh000001'):
    connection = MySQLdb.connect(user="root",passwd="parkcn",host="localhost",db="stock",charset='utf8')
    cursor = connection.cursor()
    cursor.execute("set NAMES utf8")
    cursorin = connection.cursor()
    if (cursor):
        print "数据库连接成功..."
    else:
        print "数据库连接失败..."
        sys.exit()
        
    #默认股票代码为上证指数:sh000001
    #stock_code = 'sz399001'
    print "正在试图获取" + stock_code + "的收盘数据..."
    stock_day = get_stock_day(stock_code)
    
    print "数据获取成功:" + stock_day
    ss = stock_day.replace('"','').split('=')[1].split(',')
    rday =ss[30]
    
    #写入操作
    sql= "DELETE FROM ohlc where date=CURRENT_DATE()"
    if cursor.execute(sql):
        print "该数据已删除!"
    else:
        sql = "INSERT INTO ohlc (ticker,date,open,high,low,close,  \
          amount,volume,yesclose) values \
          (%s,%s,%s,%s,%s,%s,%s,%s,%s)"
        param = (stock_code,rday,ss[1],ss[4],ss[5],ss[3],string.atof(ss[9])/10000,string.atof(ss[8])/100,ss[2])
        cursorin.execute(sql,param)
        connection.commit()  
        print "数据写入成功!" 
        cursor.close
        connection.close
        return None

   

stock_all = ['sh000001','sz399001','sz399005','sz399006','sz399106']
for stock_code_one in stock_all:
    
    stock_save(stock_code_one)
    delaytime = random.randint(10,30)
    print "delay:" + str(delaytime)

    time.sleep(delaytime/1000)
----------------------------------------------
-
作者:
男 foxred (foxred) ★☆☆☆☆ -
普通会员
2011/10/19 19:36:46
87楼: delphi对变量、参数等的大水写不敏感,而VC++对大小字是很敏感的,不知道会不会是这个原因。
----------------------------------------------
delphi
作者:
男 baojcs (baojcs) ▲▲▲▲▲ -
普通会员
2011/11/28 15:47:41
88楼: 请教一下somewho老师:

M:"tiankong@V5Y60"(DYNAINFO(37) * DYNAINFO(14),43304),COLORFF99FF ; 
主力动向:"tiankong@V5Y61"(DYNAINFO(66) / DYNAINFO(67),43304),STICK3D,COLOR0000FF ; 

计算"主力动向"时是用到了前面的"M"变量的,但tiankong.dll中能直接使用"M"变量吗?怎么传过去的呢?
----------------------------------------------
-
作者:
男 liujinwu ( ) ★☆☆☆☆ -
普通会员
2013/7/7 9:37:56
89楼: somewho老师:

StkNo:PChar;//股票代码
你的程序直接在DELPHI里头调试,这个元素获取出来,始终是乱码啊,用INSPECTOR级别调试进去看,也是一样乱码,不知啥原因,其他数据正常。


麻烦告知一下你的 联系方式,有问题想找你请教。
我是 420370494@qq.com
----------------------------------------------
-
作者:
男 bjlg (蓝天) ★☆☆☆☆ -
盒子活跃会员
2013/7/7 17:05:07
90楼: L2D数据是加密的吧
----------------------------------------------
http://delphi.icm.edu.pl/ftp/http://delphi-z.ru
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行199.2188毫秒 RSS