Creating Metatrader Dlls with Lazarus / Free Pascal @ Forex Factory
Total Page:16
File Type:pdf, Size:1020Kb
5/4/2014 Creating Metatrader DLLs with Lazarus / Free Pascal @ Forex Factory Login 6:21am Google Powered Home Forums Trades Calendar News Market Brokers Platform Tech / 7 Subscribe Reply to Thread Options Creating Metatrader DLLs with Lazarus / Free Pascal Search This Thread Page 1 2 3 4 5 First Unread Last Post Go to Post# Go to Page# First Post: Feb 6, 2010 5:54pm | Edited Dec 15, 2010 3:06am Quote Post 1 Bookmark Thread 7bit Printable Version In this thread i will try to collect all the small bits of information that are not so obvious but needed to create DLLs that play together with Metatader, a collection of minimal and self contained code snippets that illustrate how things are done. The intended audience are people already familiar with programming, this is no Pascal tutorial, i will focus only on all the little things that need to be known for Metatrader specific development projects. I will use Lazarus, the free IDE containing the the Free Pascal compiler FPC, an excellent and very advanced (Object- )Pascal Compiler. I chose Lazarus/FPC because it is by far the easiest and most efficient tool for this job that is available today, and it is free! Make sure you are downloading the 32 bit version, even if you are running Windows 64 bit on x64 hardware, Metatrader is a 32 bit application and can only use 32 bit DLLs, the 64 bit version of Lazarus/FPC would by default produce 64 bit binaries which can not be used for our purpose. To create a new DLL project (if you started Lazarus for the first time) click on Project -> new project -> Library. Give it a meaningful name and save it into an empty folder. The project will initially consist of only two files. An .lpi file containing all compiler settings and and .lpr file containing the source code, these two files are all you need to backup, pass on to your colleagues or manage in a source code repository. Additional units (if you decide to split up bigger projects) are saved as individual .pas files. If you come from a C background you will find it notable that there is no need for make, .def or whatever-files, everything that compiler and linker need to know is written in the pascal source itself, you simply throw the main unit at the compiler and it will figure out everything by itself: http://www.at.freepascal.org/advantage.html I will expand this first Posting over time with examples of increasing complexity, whenever I have new examples. 1. Minimal example with strings I will start this thread with a minimal example of a working dll that will show you how to pass strings to your DLL and how to return strings to Metatrader. Since we are using modern Object-Pascal instead of ancient and clumsy C or C++ it is really easy and intuitive: This is all that is needed on the Pascal side: Inserted Code [b]library[/b] minimal; [color=DeepSkyBlue]{$mode objfpc}{$H+}[/color] FXCM Specs [b]uses[/b] sysutils; Similar Threads Free trial- MYFX - [color=SeaGreen]// strings from and to Metatrader will always be passed METATRADER plug in - // as PChar which is a pointer to a nullterminated C-string.[/color] revolutionising trade [b]function[/b] foo(x: double; y: [color=Blue]PChar[/color]): [color=Blue]PChar[/color]; [b management - ][color=Red]stdcall[/color][/b]; metatrader [b]var[/b] 32 replies s :[color=Black]ansistring[/color]; [color=SeaGreen]// reference counted and memory manag Free EA Creating ed strings.[/color] Software 2 replies [b]begin[/b] IPC with Lazarus and [color=SeaGreen] // our PChar will be copied into an ansistring automatically, MT4 // no need to worry about the ugly details of memory allocation.[/color] 0 replies s := 'Hello ' + FloatToStr(x) + ' ' + y + '!'; A Free Indicator for Metatrader Mt4 [color=SeaGreen] // cast it back into a pointer. Metatrader will copy the 10 replies // string from the pointer into it's own memory.[/color] result := [color=Blue]PChar[/color](s); [b]end;[/b] [b]exports[/b] foo; [b] begin end.[/b] and this is the corresponding mql4: Inserted Code http://www.forexfactory.com/showthread.php?t=219576 1/9 5/4/2014 Creating Metatrader DLLs with Lazarus / Free Pascal @ Forex Factory #import "minimal.dll" [color=SeaGreen]// declare the imported function exactly as it is exported by the dll[/c olor] [color=Blue]string[/color] foo(double x, [color=Blue]string[/color] y); #import int init(){ [color=Blue]string[/color] s = foo(42.3, "Worlds"); Print(s); [color=SeaGreen]// will print "Hello 42.3 Worlds!"[/color] } int start(){ } The very first and most important thing you need to know about DLLs used for MT4 is marked with red color: Metatrader assumes the 'stdcall' calling convention which means MT4 will push the arguments onto the stack from right to left and our function is responsible for cleaning the stack before returning. FPC's default calling convention would be 'register', also known as 'Borland fastcall', which would use processor registers for the first three 32 Bit arguments. Using the wrong calling convention would immediately crash Metatrader with a segfault due to a messed up stack. Thus we must decorate all exported functions with the keyword stdcall. Another option for achieving the same goal would be the compiler directive {$CALLING STDCALL} at the top of each unit containing exported functions. 2. Passing doubles and integers by reference By reference means in Metatrader simply putting an ampersand & behind the type identifier in the function declaration and the function can then write values to these arguments. This is a convenient way to return more than one value from a Home funcFtoiornu.m Usnfortunately wiTthra imdepsorted functionsC athleisn sdeaer ms only possibNlee wsith arrays. For soMmaer wkeetird reason we caBnronkoetrs just pass scalar values by reference to a DLL, MT4 would just push a bunch of zeros onto the stack which is quite useless. We need to do this with the help of arrays. If we pass an array by reference MT4 will pass a Pointer to the first array element which is easily handled in Pascal either by a typed pointer to an array that can be dereferenced or even more elegantly by declaring the arguments with the keyword var which is technically the same, only nicer: Inserted Code [b]library[/b] testlib; [color=DeepSkyBlue]{$mode objfpc}{$H+}[/color] [b]type[/b] TDPair = array[0..1] of double; TIPair = array[0..1] of LongInt;[color=SeaGreen] // 32 bit int[/color] [color=SeaGreen]// function parameters declared as var will accept pointers.[/color] [b]procedure[/b] VarsByReference([color=Red][b]var[/b][/color] a: TDPair; [color=Red][b]var [/b][/color] b: TIPair); stdcall; [b]begin[/b] [color=SeaGreen]// now let's make some changes to the variables[/color] a[0] += a[1]; a[1] -= a[0]; b[0] += b[1]; b[1] -= b[0]; [b]end;[/b] [b]exports[/b] VarsByReference; [b]begin[/b] [b]end.[/b] and in mql4: Inserted Code Platform Tech / Creating Metatrader DLLs with Lazarus / Free Pascal Page 1 2 3 4 5 http://www.forexfactory.com/showthread.php?t=219576 2/9 5/4/2014 Creating Metatrader DLLs with Lazarus / Free Pascal @ Forex Factory #import "testlib.dll" [color=SeaGreen] /** * the function takes 2 arrays which will be * passed by reference. On the DLL side this * means we will receive Pointers to the * the memory locations of the arrays */[/color] void VarsByReference(double[b][color=Red]&[/color][/b] a[], int[b][color=Red]&[/color] [/b] b[]); #import int init(){ double foo[2];[color=SeaGreen] // define pair of doubles[/color] int bar[2]; [color=SeaGreen]// define pair of integers[/color] foo[0] = 1.23; foo[1] = 4.56; bar[0] = 42; bar[1] = 23; VarsByReference(foo, bar); [color=SeaGreen]// print the changed values[/color] Print(foo[0]); Print(foo[1]); Print(bar[0]); Print(bar[1]); } int start(){ } 3. Giving the DLL access to all bars in the chart mql4 has two interesting functions: ArrayCopyRates() and ArrayCopySeries(). These functions, when applied to an empty array, do something interesting with it. Despite the name "copy" they don't actually copy anything, instead they replace the array entirely with something different and very interesting: Once you have applied one of these functions to an array variable, this variable will from now on behave exactly like one of the built-in arrays Time[], Open[], High[], Low[], Close[], Volume[]. It will automatically update when new ticks come in and if you now pass such an array by-reference to your DLL your DLL will receive a pointer to a memory location containing always up to date quotes. We will use ArrayCopyRates() since this will give us the pointer to a 2-dimensional array, containing time and OHLCV for every bar in the chart. There is only one noteworthy thing: in mql these series arrays always start with 0 being the current bar, in reality when accessing the memory directly in our DLL, it starts with 0 being the oldest bar and we need to pass an additional parameter to tell the DLL how many bars there are in total so it can figure out which one is the current one. (The array has no upper bound but values beyond the current index (Bars-1) are undefined and meaningless.) The following example will calculate a simple moving average over the last 14 close prices to illustrate this concept, the mql4 EA will call this function and plot an arrow into the chart at the calculated price: Inserted Code [b]library[/b] testlib; [color=MediumTurquoise]{$mode objfpc} {$H+}[/color] [b]type[/b] [color=SeaGreen]{ MT4 stores the data of each candle in such a structure.