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.
----------------------------------------------
-
现在已经能够知道数据总数量,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;
----------------------------------------------
-
datap := DzhData^.m_pData; for i := 0 to DzhData^.m_nNumData - 1 do begin sometime := datap^.m_time; Inc(datap); // Here it is. end;
----------------------------------------------
-
{ 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. }
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.
----------------------------------------------
-
{ 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. }
{ 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. }
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;
{ 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. }
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;
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;
如果要完成类似大智慧公式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)
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.
----------------------------------------------
-
{ 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. }
to baojcs:最近没什么时间弄这个。之前也尝试过,好像没有读到什么数据。也许大智慧已经修改了某些数据结构,正如3个委托现在其实已经变成5个委托;也许这个网上搜来的头文件本来就有错。等过段时间我有空了再尝试一下吧。
----------------------------------------------
-
////////// // 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. //////////
// 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
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;
////////// // 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;
////////// // 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 //////////
////////// // 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;
////////// // 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;
////////// // 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 } }
////////// // 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 //////////
////////// // 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 //////////
////////// // 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;
////////// // 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;
////////// // 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();
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
////////// // 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;
// 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() );
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() );
// // 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;
////////// // 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() );
// // 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. //
////////// // 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 //////////