I'd like to hear opinion from other coders about which way of organizing their complex EA code works best for them. I'm assuming if you have an EA which totals 1000+ lines of code, you don't normally put all the code in the main EA mq4 but rather have bunch of header files you include and/or libraries you import.
I don't use dynamically linked ex4 libraries much because: a) they don't support default parameter values, b) the more libraries used in the EA the harder to track all the dependencies and the list of files you need to deploy with your EA, c) a separate copy of the same ex4 library is loaded for each calling code which makes it difficult to store common data in library's global variables.
I currently have my code spread across bunch of mqh files which contain functions and their implementations so can have function parameters with default values, code is linked statically in EA's ex4. On the other hand, any change in a shared mqh and I need to recompile each EA which uses that mqh. Another annoying thing is that in common mqh files which are shared by more than one EA there are functions which are used in one EA but not in the other and so the compiler keeps spitting the warnings: "Function blah is not referenced and will be removed from exp-file". I can live with those warnings but I keep wondering if there's a better/more efficient way of organizing the code or any features of mql4 and compiler which I have missed. Thanks.
may be some pictures tell more than 1000 words.
- don't use multiple libraries with cross dependencies. cross calling works but errorhandling will be cumbersome. example: eaA calls libB, libB calls libC, later eaA directly calls libC. now there are 2 instances of libC in memory with different state of local and global scopes. they don't know nothing about each other.
- put as most as you can in one lib but be aware that MQL limits the size of libs to 500 arrays.
- write wrappers around the MQL init(), start() and deinit() functions, the MetaQuotes implementations are to limited for production-ready code. my wrappers are in include\core\...
- write a framework for absolutely failsafe error handling. it belongs in the wrapper files. make sure you don't ever miss a single unnoticed error. inline this is impossible, it needs to be solved somewhere global. make sure the tester stops if an error occures.
- rebuild state after ea shutdown -> ea reload
- rebuild state after ea recompile -> ea reload
- make sure you can remote control the terminal from code
- configuration, configuration, configuration... make everything configurable by external config files
- learn how to create and update synthetic instruments in real time. you need them for analysing tester results. the tester report is useless. the statement "it doesn't work" is a myth.
it's impossible to post all my code but you should see what it needs and get the direction. for someone new to coding it's probably not possible
last advice, and i know this will annoy some mods here but it's my experience: the jobs section here is no help.
I use DLL's (no ex4 libraries) and MQH files. Each library and each MQH perform their functions and there is no cross talking. They are all called from the main program file, kind of treated as isloated objects. The main program requests information that the dll or mqh provides and with that something else is called. Each MQH or DLL performs duties from money management, data collection, logging, signal processing, database handling... As in the previous post, error checking is extremely important, I have a utilities MQH which takes care of things like this. I also have a MQH just for configuration where all the key-value pairs are specified.
Thus, I have a rather short main program which calls many long MQH files. I also get warnings when I compile for functions not used, but that doesnt bother me either.
IMHO, using a good logging library together with "PauseTest()" is the best way to debug and trace your code. Without that, it would be impossible to trace many MQH files doing different tasks during EA execution.
with this a typical EA looks like this
stderror.mqh and stdlib.mq4 are compatible to the original MetaQuotes versions
And regularly backup your code outside your machine using Export :) (I use TortoiseSVN)
And for editor, I use notepadd++ with Custom syntax highlighting, word completion and code folding as main features I use.
Thanks guys for all your posts so far. I use Git (Cygwin version) as VCS which works great for me as it doesn't require a database and can be archived by simply copying the .git directory elsewhere.
@paulpanke your code structure is much more advanced than mine and I'll give it a deeper review. I have an error handling module, wrote my own logger and other general stuff and I keep adding new general on as-needed basis. My original question in this thread was more about mqh vs. ex4 vs. dll, i.e. about physical code organization in files, not about EA logic itself. However, I still find the posts with different EA logic wrappers very useful so keep them coming. Thanks.
@sokramm presumably you don't write DLLs in mql4 but rather in C/C++, right? you keep mql4 stuff in mqh and general stuff (logging, database access, etc.) in DLL?
Start with writing everything in one mql file, then if you encounter parts/functions you need again and again, put only those in a MQL lib. Do not create many libs, try to put as many as you can in one until you reach the MQL limit. Switch to DLL's if you need more advanced coding features (object orientation, sockets, networking, ipc) *OR* if you need speed. You don't need speed for working out logic but you need speed for testing, once your logic is ready to run. Only implement those features in a DLL that really slow down your code, not all and everything. This is typical development practice no matter what language we are talking about. Only improve the slow and only the slow parts.
Make sure to pass errors from inside the DLL back to the MQL error handler. Do the same if you make use of iCustom(). If an error happens inside the called indicator, pass it back to the calling code. You can achieve this by creating a memory block for MQL communication (hint: ArrayResize() allocates memory) and passing a pointer to this block as an int parameter to the iCustom() indicator (hint: use the sample library delivered with MT4 for getting pointer values). now you not only have access to indicator buffers, but you communicate back and forth between ea and indicator. make sure your EA stops if the indicator signals an error.
you don't need to stop at every error if you handle that error by yourself. just like exceptions you "eat" if you know what you are doing.
regarding the "unreferenced function" warning i do this in my global start() function (it's in "include\core\...", not in the EA itself):
and later this. it's a simple and very effective solution. this are typical functions i don't want to put in a library because they are called often and i like to avoid the slow down caused by the library call.