* * *

Author Topic: Using Dynamic Data Exchange (DDE) for MetaTrader 4 (MT4) [Example DDE Lazarus]  (Read 794 times)

DavidTh30

  • New member
  • *
  • Posts: 6
Connect Dynamic Data Exchange (DDE) to MetaTrader 4 (MT4)

Code: Pascal  [Select]
  1. Type
  2.   CurrencyConnect = record
  3.     Life:Boolean;
  4.     Res: UINT;
  5.     g_idInst: DWORD;
  6.     MainTopic:PChar;
  7.     TopicMT4:HSZ;
  8.     ErrorMessageString:String;
  9.     Topic1:PChar;  //QUOTE  //Time   //TIMESEC
  10.     TopicQUOTE:HSZ;
  11.     hCnvTopicQUOTE: DWORD;
  12.     Topic2:PChar;  //BID
  13.     TopicBID:HSZ;
  14.     hCnvTopicBID: DWORD;
  15.     Topic3:PChar;  //ASK
  16.     TopicASK:HSZ;
  17.     hCnvTopicASK: DWORD;
  18.     Topic4:PChar;  //HIGH
  19.     TopicHIGH:HSZ;
  20.     hCnvTopicHIGH: DWORD;
  21.     Topic5:PChar;  //LOW
  22.     TopicLOW:HSZ;
  23.     hCnvTopicLOW: DWORD;
  24.   end;
  25.  
  26. type
  27.   CurrencyPair = record
  28.     Life:Boolean;  //Connect=True, Error/Disconnect=False
  29.     Item:PChar;  //EURUSD //GBPUSD //USDJPY //AUDNZD  
  30.     HSZPair:HSZ;
  31.     ErrorMessageString:String;
  32.     hDataQUOTE: HDDEDATA;
  33.     hDataBID: HDDEDATA;
  34.     hDataASK: HDDEDATA;
  35.     hDataHIGH: HDDEDATA;
  36.     hDataLOW: HDDEDATA;
  37.     Tem:PDWORD;
  38.     Result_:pbyte;
  39.   end;
  40.  
  41. Function GetDdeError(g_id: DWORD):String;
  42. Procedure MainLoop();
  43. Function CurrencyGethData(P_:CurrencyPair; M_:CurrencyConnect):String;
  44.  
  45. var
  46.   Form1: TForm1;
  47.   MainCurrencyConnect:CurrencyConnect;
  48.   CurrencyEURUSD:CurrencyPair;
  49.   StartLoop, Life:Boolean;
  50.   DDECallbackMessage:String;
  51.   WM_MY_MESSAGE: UINT;
  52.  
  53. Function TForm1.GetDdeError(g_id: DWORD):String;
  54. var
  55.   DDEError: Word;
  56. begin
  57.   GetDdeError:='';
  58.   DDEError := DdeGetLastError(g_id);
  59.   case DDEError of
  60.     DMLERR_ADVACKTIMEOUT: Result := 'Timeout on sync advise request';
  61.     DMLERR_BUSY: Result := 'Server is busy';
  62.     DMLERR_DATAACKTIMEOUT: Result := 'Timeout on sync data request';
  63.     DMLERR_DLL_NOT_INITIALIZED: Result := 'DDEML not initialised';
  64.     DMLERR_DLL_USAGE: Result := 'Invalid request';
  65.     DMLERR_EXECACKTIMEOUT: Result := 'Timeout on sync exec request';
  66.     DMLERR_INVALIDPARAMETER: Result := 'Invalid parameter in request';
  67.     DMLERR_LOW_MEMORY: Result := 'Server ran out of buffer memory';
  68.     DMLERR_MEMORY_ERROR: Result := 'Memory allocation error';
  69.     DMLERR_NO_CONV_ESTABLISHED: Result := 'No conversation established';
  70.     DMLERR_NOTPROCESSED: Result := 'Request not processed by server';
  71.     DMLERR_POKEACKTIMEOUT: Result := 'Timeout on sync poke request';
  72.     DMLERR_POSTMSG_FAILED: Result := 'PostMessage failed';
  73.     DMLERR_REENTRANCY: Result := 'Sync request already in progress';
  74.     DMLERR_SERVER_DIED: Result := 'Server died';
  75.     DMLERR_SYS_ERROR: Result := 'DDEML Internal error';
  76.     DMLERR_UNADVACKTIMEOUT: Result := 'Timeout on unadvise request';
  77.     DMLERR_UNFOUND_QUEUE_ID: Result := 'Invalid transaction ID';
  78.   end;
  79. end;
  80.  
  81. Function DDECallback(CallType, Fmt: UINT; Conv: HConv; hsz1, hsz2: HSZ;
  82.     Data: HDDEData; Data1, Data2: DWORD): HDDEData; stdcall;
  83. begin
  84.   Result := 0;
  85.   case CallType of
  86.     xtyp_Register:
  87.       begin
  88.         DDECallbackMessage:='xtyp_Register';
  89.         Result:=-1;
  90.       end;
  91.     xtyp_Unregister:
  92.       begin
  93.         DDECallbackMessage:='xtyp_Unregister';
  94.         Result:=-1;
  95.       end;
  96.     xtyp_xAct_Complete:
  97.       begin
  98.         DDECallbackMessage:='xtyp_xAct_Complete';
  99.       end;
  100.     xtyp_Request, Xtyp_AdvData:
  101.       begin
  102.         DDECallbackMessage:='Xtyp_AdvData';
  103.       end;
  104.     xtyp_Disconnect:
  105.       begin
  106.         DDECallbackMessage:='xtyp_Disconnect';
  107.         Result:=-1;
  108.       end;
  109.     XTYP_ERROR:
  110.       begin
  111.         DDECallbackMessage:='XTYP_ERROR';
  112.         Result:=-1;
  113.       end;
  114.     XTYP_EXECUTE:
  115.       begin
  116.         DDECallbackMessage:='XTYP_EXECUTE';
  117.       end;
  118.     XTYPF_NOBLOCK:
  119.       begin
  120.         DDECallbackMessage:='XTYPF_NOBLOCK';
  121.         Result:=-1;
  122.       end;
  123.     XTYP_CONNECT, XTYP_CONNECT_CONFIRM:
  124.       begin
  125.         DDECallbackMessage:='XTYP_CONNECT, XTYP_CONNECT_CONFIRM';
  126.       end;
  127.     XTYP_ADVREQ:
  128.       begin
  129.         //ShowMessage('XTYP_ADVREQ');
  130.         DDECallbackMessage:='XTYP_ADVREQ';
  131.       end;
  132.     xtyp_Poke:
  133.       begin
  134.         DDECallbackMessage:='xtyp_Poke';
  135.       end;
  136.     xtyp_AdvStart:
  137.       begin
  138.         DDECallbackMessage:='xtyp_AdvStart';
  139.       end;
  140.     xtyp_AdvStop:
  141.       begin
  142.         DDECallbackMessage:='xtyp_AdvStop';
  143.       end;
  144.     DMLERR_SERVER_DIED:
  145.       begin
  146.         DDECallbackMessage:='DMLERR_SERVER_DIED';
  147.       end;
  148.     DMLERR_SYS_ERROR:
  149.       begin
  150.         DDECallbackMessage:='DMLERR_SYS_ERROR';
  151.       end;
  152.     DMLERR_BUSY:
  153.       begin
  154.         DDECallbackMessage:='DMLERR_BUSY';
  155.       end;
  156.     DMLERR_DATAACKTIMEOUT:
  157.       begin
  158.         DDECallbackMessage:='DMLERR_DATAACKTIMEOUT';
  159.       end;
  160.     DMLERR_ADVACKTIMEOUT:
  161.       begin
  162.         DDECallbackMessage:='DMLERR_ADVACKTIMEOUT';
  163.       end;
  164.   end;
  165. end;
  166.  
  167. procedure TForm1.FreeDataHandle(P_:CurrencyPair);
  168. begin
  169.     DdeFreeDataHandle(P_.hDataQUOTE);
  170.     DdeFreeDataHandle(P_.hDataBID);
  171.     DdeFreeDataHandle(P_.hDataASK);
  172.     DdeFreeDataHandle(P_.hDataHIGH);
  173.     DdeFreeDataHandle(P_.hDataLOW);
  174. end;  
  175.  
  176. Function TForm1.CurrencyGethData(P_:CurrencyPair; M_:CurrencyConnect):String;
  177. var
  178.   Buf: String[255];
  179.   Tem:String;
  180. begin
  181.     Tem:=P_.Item+chr(13);
  182.     Application.ProcessMessages;
  183.  
  184.     P_.hDataQUOTE := DdeClientTransaction(nil, 0, M_.hCnvTopicQUOTE, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  185.     if P_.hDataQUOTE<>0 then P_.Result_:=DdeAccessData(P_.hDataQUOTE,P_.Tem);
  186.     //DdeGetData(P_.hDataQUOTE, @Buf, SizeOf(Buf), 0);    //DdeGetData(hData, @Buf, 255, 0);
  187.     //tem2 := Buf;
  188.     //strcopy(Result, PChar(P_.Result_));
  189.     if P_.hDataQUOTE=0 then
  190.     Tem:=Tem+'QUOTE: N/A' else Tem:=Tem+'QUOTE:'+PChar(P_.Result_)+chr(13);//Tem:='QUOTE:'+PChar(P_.Result_)+chr(13);     //Tem:='QUOTE:'+tem2+chr(13);
  191.     Application.ProcessMessages;
  192.  
  193.     P_.hDataBID := DdeClientTransaction(nil, 0, M_.hCnvTopicBID, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  194.     if P_.hDataBID<>0 then P_.Result_:=DdeAccessData(P_.hDataBID,P_.Tem);
  195.     if P_.hDataBID=0 then
  196.     Tem:=Tem+'BID: N/A' else Tem:=Tem+'BID:'+PChar(P_.Result_)+chr(13);
  197.     Application.ProcessMessages;
  198.  
  199.     P_.hDataASK := DdeClientTransaction(nil, 0, M_.hCnvTopicASK, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  200.     if P_.hDataASK<>0 then P_.Result_:=DdeAccessData(P_.hDataASK,P_.Tem);
  201.     if P_.hDataASK=0 then
  202.     Tem:=Tem+'ASK: N/A' else Tem:=Tem+'ASK:'+PChar(P_.Result_)+chr(13);
  203.     Application.ProcessMessages;
  204.  
  205.     P_.hDataHIGH := DdeClientTransaction(nil, 0, M_.hCnvTopicHIGH, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  206.     if P_.hDataHIGH<>0 then P_.Result_:=DdeAccessData(P_.hDataHIGH,P_.Tem);
  207.     if P_.hDataHIGH=0 then
  208.     Tem:=Tem+'HIGH: N/A' else Tem:=Tem+'HIGH:'+PChar(P_.Result_)+chr(13);
  209.     Application.ProcessMessages;
  210.  
  211.     P_.hDataLOW := DdeClientTransaction(nil, 0, M_.hCnvTopicLOW, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  212.     if P_.hDataLOW<>0 then P_.Result_:=DdeAccessData(P_.hDataLOW,P_.Tem);
  213.     if P_.hDataLOW=0 then
  214.     Tem:=Tem+'LOW: N/A' else Tem:=Tem+'LOW:'+PChar(P_.Result_);
  215.     Result:=Tem;
  216.     Application.ProcessMessages;
  217.  
  218.     FreeDataHandle(CurrencyEURUSD);
  219.  
  220.     Application.ProcessMessages;
  221. end;
  222.  
  223. procedure TForm1.MainLoop();
  224. begin
  225.   if StartLoop = Life then exit;
  226.   Life:=StartLoop;
  227.   while StartLoop do
  228.   begin
  229.     EURUSD.Caption := CurrencyGethData(CurrencyEURUSD,MainCurrencyConnect);
  230.  
  231.     if DDECallbackMessage<>'' then Label2.Caption:='Status: '+DDECallbackMessage;
  232.     Application.ProcessMessages;
  233.   end;
  234. end;
  235.  
  236. procedure TForm1.FormCreate(Sender: TObject);
  237. begin
  238.   StartLoop:=False;
  239.   Life:=False;
  240.  
  241.   MainCurrencyConnect.g_idInst:=0;
  242.   MainCurrencyConnect.ErrorMessageString:='';
  243.   MainCurrencyConnect.MainTopic:='MT4';
  244.   MainCurrencyConnect.Topic1:='QUOTE'; //QUOTE TIME TIMESEC
  245.   MainCurrencyConnect.Topic2:='BID';
  246.   MainCurrencyConnect.Topic3:='ASK';
  247.   MainCurrencyConnect.Topic4:='HIGH';
  248.   MainCurrencyConnect.Topic5:='LOW';
  249.  
  250.   CurrencyEURUSD.Item:='EURUSD';
  251.   CurrencyEURUSD.ErrorMessageString:='';
  252.  
  253.   MainCurrencyConnect.Res:=DdeInitialize(@MainCurrencyConnect.g_idInst, @DdeCallback, APPCLASS_STANDARD, 0);
  254.  
  255. MainCurrencyConnect.hCnvTopicQUOTE := DdeConnect(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicMT4, MainCurrencyConnect.TopicQUOTE, nil);
  256.     MainCurrencyConnect.hCnvTopicBID := DdeConnect(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicMT4, MainCurrencyConnect.TopicBID, nil);
  257.     MainCurrencyConnect.hCnvTopicASK := DdeConnect(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicMT4, MainCurrencyConnect.TopicASK, nil);
  258.     MainCurrencyConnect.hCnvTopicHIGH := DdeConnect(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicMT4, MainCurrencyConnect.TopicHIGH, nil);
  259.     MainCurrencyConnect.hCnvTopicLOW := DdeConnect(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicMT4, MainCurrencyConnect.TopicLOW, nil);
  260.      
  261. if MainCurrencyConnect.hCnvTopicQUOTE <> 0 then
  262.     begin
  263.       DdeClientTransaction(nil, 0, MainCurrencyConnect.hCnvTopicQUOTE, CurrencyEURUSD.HSZPair, CF_TEXT, XTYP_ADVSTART, 50, nil);
  264.     end;  
  265. end;
  266.  
  267. Function TForm1.CurrencyGethData(P_:CurrencyPair; M_:CurrencyConnect):String;
  268. var
  269.   Buf: String[255];
  270.   Tem:String;
  271. begin
  272.     Tem:=P_.Item+chr(13);
  273.     Application.ProcessMessages;
  274.  
  275.     P_.hDataQUOTE := DdeClientTransaction(nil, 0, M_.hCnvTopicQUOTE, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  276.     if P_.hDataQUOTE<>0 then P_.Result_:=DdeAccessData(P_.hDataQUOTE,P_.Tem);
  277.     //DdeGetData(P_.hDataQUOTE, @Buf, SizeOf(Buf), 0);    //DdeGetData(hData, @Buf, 255, 0);
  278.     //tem2 := Buf;
  279.     //strcopy(Result, PChar(P_.Result_));
  280.     if P_.hDataQUOTE=0 then
  281.     Tem:=Tem+'QUOTE: N/A' else Tem:=Tem+'QUOTE:'+PChar(P_.Result_)+chr(13);//Tem:='QUOTE:'+PChar(P_.Result_)+chr(13);     //Tem:='QUOTE:'+tem2+chr(13);
  282.     Application.ProcessMessages;
  283.  
  284.     P_.hDataBID := DdeClientTransaction(nil, 0, M_.hCnvTopicBID, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  285.     if P_.hDataBID<>0 then P_.Result_:=DdeAccessData(P_.hDataBID,P_.Tem);
  286.     if P_.hDataBID=0 then
  287.     Tem:=Tem+'BID: N/A' else Tem:=Tem+'BID:'+PChar(P_.Result_)+chr(13);
  288.     Application.ProcessMessages;
  289.  
  290.     P_.hDataASK := DdeClientTransaction(nil, 0, M_.hCnvTopicASK, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  291.     if P_.hDataASK<>0 then P_.Result_:=DdeAccessData(P_.hDataASK,P_.Tem);
  292.     if P_.hDataASK=0 then
  293.     Tem:=Tem+'ASK: N/A' else Tem:=Tem+'ASK:'+PChar(P_.Result_)+chr(13);
  294.     Application.ProcessMessages;
  295.  
  296.     P_.hDataHIGH := DdeClientTransaction(nil, 0, M_.hCnvTopicHIGH, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  297.     if P_.hDataHIGH<>0 then P_.Result_:=DdeAccessData(P_.hDataHIGH,P_.Tem);
  298.     if P_.hDataHIGH=0 then
  299.     Tem:=Tem+'HIGH: N/A' else Tem:=Tem+'HIGH:'+PChar(P_.Result_)+chr(13);
  300.     Application.ProcessMessages;
  301.  
  302.     P_.hDataLOW := DdeClientTransaction(nil, 0, M_.hCnvTopicLOW, P_.HSZPair, CF_TEXT, XTYP_REQUEST, 5000, nil);
  303.     if P_.hDataLOW<>0 then P_.Result_:=DdeAccessData(P_.hDataLOW,P_.Tem);
  304.     if P_.hDataLOW=0 then
  305.     Tem:=Tem+'LOW: N/A' else Tem:=Tem+'LOW:'+PChar(P_.Result_);
  306.     Result:=Tem;
  307.     Application.ProcessMessages;
  308.  
  309.     FreeDataHandle(P_);
  310.  
  311.     Application.ProcessMessages;
  312. end;  
  313.  
  314. procedure TForm1.MainLoop();
  315. var
  316.   Tem:String;
  317. begin
  318.   if StartLoop = Life then exit;
  319.   Life:=StartLoop;
  320.   while StartLoop do
  321.   begin
  322.     EURUSD.Caption := CurrencyGethData(CurrencyEURUSD,MainCurrencyConnect);
  323.  
  324.     if DDECallbackMessage<>'' then Label2.Caption:='Status: '+DDECallbackMessage;
  325.     Application.ProcessMessages;
  326.   end;
  327. end;  
  328.  
  329. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  330. begin
  331.   StartLoop:=False;
  332.   DdeFreeStringHandle(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicQUOTE);
  333.   DdeFreeStringHandle(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicBID);
  334.   DdeFreeStringHandle(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicASK);
  335.   DdeFreeStringHandle(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicHIGH);
  336.   DdeFreeStringHandle(MainCurrencyConnect.g_idInst, MainCurrencyConnect.TopicLOW);
  337.   FreeDataHandle(CurrencyEURUSD);
  338. end;

 :) :) :) :) :) :)

DDE4.zip is the client of DDE to connect the MT4.
The code still have a problem with risk data.
Somebody know how to create own DDE server using Pascal?
« Last Edit: December 26, 2017, 03:30:18 pm by DavidTh30 »

Thaddy

  • Hero Member
  • *****
  • Posts: 5798
You basically need to use lowlevel windows API calls. see msdn https://msdn.microsoft.com/en-us/library/windows/desktop/ms648775(v=vs.85).aspx
See this discussion: http://free-pascal-lazarus.989080.n3.nabble.com/Lazarus-Windows-DDE-td4034195.html
Note that if you have a  Delphi version (pref any of D6 to D2007) with sourcecode you can use their DDE units (the lowlevel classes, no components) without many problems and only on 32 bit windows.

It is easy to translate those examples if you have a little knowledge of C(++) but real programmers do have that, don't you?  :D :D :P.
recommends {$macro on}{$define Silly:=ObjFpc}

jamie

  • Hero Member
  • *****
  • Posts: 654
So I take if by your comment 'only on 32 bit windows" that the DDE is not supported for 64 bit OS ?

Thaddy

  • Hero Member
  • *****
  • Posts: 5798
It is legacy. But with the winapi it should still work on 64 bit too. E.g.I got a Delphi 2 unit from torry's and all I needed to do was change the integer parameters to PtrUnit.
I am not giving a link or my code, because that code (on torry.net) is a DDE bug fix too and a possible copyright infringement. You will find it...
« Last Edit: December 30, 2017, 10:53:58 pm by Thaddy »
recommends {$macro on}{$define Silly:=ObjFpc}

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus