mql4-lib
repository·master·Indexed 20 days ago
https://github.com/dingmaotu/mql4-libAn object-oriented foundation library for MQL4 and MQL5 developers that provides reusable components and a Java-like coding style. It includes modules for language enhancements (Lang), collections (LinkedList, Vector, HashSet, HashMap), serialization (RESP protocol), trading abstractions, chart tools, and utility functions for file management and OpenCL support.
What's inside mql4-lib
- mql4-lib is a foundation library designed to bring a professional, object-oriented coding style (similar to Java) to MQL4 and MQL5 programming. It aims to replace the limited standard MQL libraries with reusable, elegant, and component-based designs. While originally targeting MQL4, most components are compatible with MQL5, making it a cross-version library for both MT4 and MT5 (x86/x64).
Core components of mql4-lib
masterThe library is organized into several specialized directories that extend MQL capabilities:
Lang: Modules that enhance the MQL language (e.g., base classes for Scripts, Indicators, and EAs).Collection: Useful collection types.Format: Implementations of serialization formats.Charts: Various chart types and common chart tools.Trade: Abstractions for trading operations.History: Abstractions for historical data.Utils: Various utility functions.UI: Chart objects and UI controls (in progress).OpenCL: OpenCL support for MT4 (in progress).
How Runtime Controlled Indicators work
masterIn
mql4-lib, an Indicator can exist in two modes:- Runtime Controlled (Standalone): The indicator is controlled by the MetaTrader terminal. In this mode,
isRuntimeControlled()returnstrue. You must configure visual styles (e.g.,SetIndexStyle,IndicatorShortName) inside the constructor. - Driven (Programmatic): The indicator is driven by your own code (e.g., an EA or Script) using a
HistoryDataderived class likeRenkoorTimeSeriesData. In this mode,isRuntimeControlled()returnsfalse, and you are responsible for resizing buffers (e.g., usingArrayResize) within themain()method.
Using an Indicator in an EA with a Renko Driver
To use an indicator programmatically, instantiate its parameter class, create the indicator instance, and subscribe to the driver's
OnUpdateevent.//--- OnInit DeMarkerParam *param=new DeMarkerParam; param.setAvgPeriod(14); deMarker=new DeMarker(param); //--- Subscribe to Renko driver renko = new Renko(_Symbol,300); renko.OnUpdate+=deMarker; //--- OnTick renko.update(Close[0]); //--- Access value double value = deMarker[0]; //--- OnDeinit delete renko;- Runtime Controlled (Standalone): The indicator is controlled by the MetaTrader terminal. In this mode,
Parse RESP data with RespMsgParser and RespStreamParser
masterThe library provides two types of parsers for the RESP protocol:
RespMsgParser: Designed for message-oriented contexts where the entire buffer is received at once. It includes acheck()method to validate if a buffer contains a validRespValuewithout fully creating the object.RespStreamParser: Designed for stream-oriented contexts where data may arrive partially. It allows for resuming parsing as more input is provided (inspired byhiredisReplyReader).
Both parsers provide a
getError()method to retrieve error codes (defined inMql/Format/RespParseError.mqh) if theparse()method returnsNULL.Calculate profit and target prices using symmetric order semantics
masterThe library provides two primary operations for price calculation that allow you to express order logic symmetrically, regardless of whether the order is a BUY or a SELL. This approach reduces code complexity by focusing on the direction of profit rather than checking order types manually.
Profit Calculation:
p(s, e)Calculates the profit as the absolute price difference from a start price
sto an end pricee.- To calculate loss, use
-p(s, e)orp(e, s). Using-p(s, e)is preferred for clarity.
Target Price Calculation:
pp(p, pr)Calculates the target price if you start from price
pand want to achieve a profit ofpr.- To target a specific loss, use
pp(p, -pr). - Convenience Overload: The
prparameter can also accept a value in points directly.
Expressing Breakeven
You can express the breakeven condition symmetrically for both BUY and SELL orders using:
order.p(order.getOpenPrice(), order.getStopLoss()) >= 0.- For a BUY order, this evaluates to
openPrice <= stopLoss. - For a SELL order, this evaluates to
openPrice >= stopLoss. - Conceptually: "If we move from the open price to the stop loss price, we still profit."
// Example of symmetric breakeven check if(o.p(o.getOpenPrice(), o.getStopLoss()) >= 0) { // Order is at or past breakeven }- To calculate loss, use
Use Collections (Lists, Vectors, and Sets) in MQL
masterThe library provides sophisticated collection types using MQL class templates.
List Types
LinkedList<T>: A linked list implementation. Faster for frequent insertions and deletions.Vector<T>: An array-based implementation (similar to Java'sArrayList). Faster for random access.
Set Types
ArraySet<T>: Array-based set implementation.HashSet<T>: Hash-based set implementation.
Iteration
To iterate through collections, use the
Iter<T>RAII class or the provided macros. The macros handle resource cleanup automatically.foreach(Type, collection): Uses an internal iterator variable namedit.foreachv(Type, var, collection): Declares a specific variablevarfor the element.foreachorder(pool): Specialized macro for iterating through an order pool.
LinkedList<Order*> orderList; // linked list based implementation, faster insert/remove LinkedList<int> intList; // supports primary types Vector<Order*> orderVector; // array based implementation, faster random access Vector<int> intVector; // Using Iter RAII class for(Iter<Order*> it(list); !it.end(); it.next()) { Order* o = it.current(); Print(o.toString()); } // Using foreach macro foreach(Order*, list) { Order* o = it.current(); Print(o.toString()); } // Using foreachv macro foreachv(Order*, o, list) { Print(o.toString()); }Work with history files using Utils/HistoryFile
masterMetaTrader uses special history files to back chart displays. The library provides theUtils/HistoryFileclass to wrap operations on these files, which have a fixed structure. This is particularly useful for implementing custom chart types as offline charts (e.g.,PriceBreakChartorRenkoChart).Implement symmetric order semantics with the Order class
masterThe
Orderclass provides "symmetric order semantics" to simplify logic that must handle both BUY and SELL orders. Instead of writing separate blocks for each type, you can use semantic methods that behave correctly regardless of the order direction.Formatting and Conversion
f(p): Formats pricepto a string based on symbol digits.n(p): Normalizes pricepto a double based on symbol digits.ap(p): Gets the absolute price difference of point valuep(integer).
Price Levels
s(): Gets the correct price to start an order (Ask for BUY, Bid for SELL).e(): Gets the correct price to end an order (Bid for BUY, Ask for SELL).
Install mql4-lib in MetaTrader
masterTo install the library, copy the contents into your MetaTrader Data Folder's
Includedirectory. It is recommended to useMql(Pascal Case) as the root directory name to ensure compatibility across both MT4 and MT5.Installation Paths:
- For MT4:
<MetaTrader Data>\MQL4\Include\Mql\<mql4-lib content> - For MT5:
<MetaTrader Data>\MQL5\Include\Mql\<mql4-lib content>
Note: It is recommended to use the latest version of MetaTrader 4 or 5, as older versions may lack required features.
- For MT4:
Send custom app events from C/C++ to MQL4
masterYou can bypass MetaTrader's limitations by sending custom events from an external C/C++ application using the
PostMessageorPostThreadMessageWin32 API. This mechanism uses a customWM_KEYDOWNmessage to triggerOnChartEventin MQL.To ensure the MQL program (specifically those deriving from
EventApp) processes the message via itsonAppEventhandler, you must encode theeventandparamusing theEncodeKeydownMessagealgorithm.Limitations:
- The parameter is limited to a 32-bit integer.
- This solution may not work in 64-bit MetaTrader 5.
- It is considered a temporary workaround; the official way to handle asynchronous events is via
ChartEventCustom(which is difficult to implement in C++ due to anti-debugging measures).
#include <Windows.h> #include <stdint.h> #include <limits.h> static const int WORD_BIT = sizeof(int16_t)*CHAR_BIT; void EncodeKeydownMessage(const WORD event,const DWORD param,WPARAM &wparam,LPARAM &lparam) { DWORD t=(DWORD)event; t<<= WORD_BIT; t |= 0x80000000; DWORD highPart= param & 0xFFFF0000; DWORD lowPart = param & 0x0000FFFF; wparam = (WPARAM)(t|(highPart>>WORD_BIT)); lparam = (LPARAM)lowPart; } BOOL MqlSendAppMessage(HWND hwnd, WORD event, DWORD param) { WPARAM wparam; LPARAM lparam; EncodeKeydownMessage(event, param, wparam, lparam); return PostMessageW(hwnd,WM_KEYDOWN,wparam, lparam); }Create an Expert Advisor (EA) with input parameters
masterTo create an Expert Advisor with inputs, follow these steps:
- Define a parameter class inheriting from
AppParam. - Use the
ObjectAttrmacro to declare fields. This automatically generates getter/setter methods following Java Beans conventions. - Define your EA class inheriting from
ExpertAdvisor, passing the parameter class to its constructor. - Use
BEGIN_INPUTandEND_INPUTblocks to define the inputs for the MetaTrader terminal. - Use
DECLARE_EAto register the EA. Set the second parameter totrueto indicate it uses input parameters.
Note: Use the constructor for initialization (instead of
OnInit) and the destructor for deinitialization (instead ofOnDeinit). Usefail(message, returnCode)within the constructor if initialization fails.#include <Mql/Lang/ExpertAdvisor.mqh> class MyEaParam: public AppParam { ObjectAttr(string,eaName,EaName); ObjectAttr(double,baseLot,BaseLot); public: }; class MyEa: public ExpertAdvisor { private: MyEaParam *m_param; public: MyEa(MyEaParam *param) :m_param(param) { } ~MyEa() { } void main() {Print("Hello from " + m_param.getEaName());} }; BEGIN_INPUT(MyEaParam) INPUT(string,EaName,"My EA"); INPUT(double,BaseLot,0.1); END_INPUT DECLARE_EA(MyEa,true)- Define a parameter class inheriting from
Iterate through orders using OrderPool and foreachorder
masterInstead of manually looping through
OrdersTotal()and usingOrderSelect(), you can use theOrderPoolclass and theforeachordermacro to encapsulate filtering logic.To use this, derive a class from
HistoryPool(orOrderPool) and implement thematches()method to define your filtering criteria.#include <Mql/Trade/OrderPool.mqh> // Define a pool that only matches profitable orders class ProfitHistoryPool: public HistoryPool { public: bool matches() const { return Order::Profit() > 0; } }; void OnStart() { ProfitHistoryPool profitPool; // Use the foreachorder macro to iterate and filter automatically foreachorder(profitPool) { Order o; Print(o.toString()); } }