MT4 Manager API .NET Wrapper

This library makes it possible to develop applications in any .NET-compatible language using MetaQuotes' MetaTrader 4 Manager API.

This library makes it possible to develop applications in any .NET-compatible language using MetaQuotes' MetaTrader 4 Manager API.

It is built in C#, can be used on the Any CPU platform, and automatically chooses the right library (mtmanapi.dll or mtmanapi64.dll) to link dynamically at run-time.

The Manager API provides three kinds of connection to an MT4 server:

  • Normal mode — request/response scheme, the way the MetaTrader Administrator works.
  • Pumping mode — real-time data from the server, the way the MetaTrader Manager works.
  • Dealing mode — processing client requests, the Dealer feature of the MetaTrader Manager.

Quick startup guide

All the examples provided here are written in C# in Visual Studio 2022 (or JetBrains Rider / VS Code with the C# Dev Kit). You are free to choose any .NET language and IDE version.

Check that your server has a current .NET runtime installed. We recommend installing .NET 10 (LTS) on the server. The wrapper also runs on .NET Framework 4.6.2+ and modern .NET. You can download the latest runtime from the .NET download page.

In Visual Studio you can add http://nuget.cplugin.com/feeds/default as a package source (see Microsoft's guide) and add a CPlugin.PlatformWrapper.MetaTrader4 reference to your project.

To download the package manually from our NuGet, navigate to http://nuget.cplugin.com/feeds/default/CPlugin.PlatformWrapper.MetaTrader4/versions and add it as a reference to the project.

Working examples: https://github.com/CPlugin/PlatformWrapper.Examples

Next, create an account in MT4 in a manager group (using the Manager or Administrator terminal) and add it to the managers list (using the Administrator terminal). The API can only use a manager account. You will authenticate with the manager account's login (an integer) and its password. Depending on the task you intend to perform, the account will need the relevant permissions granted.

Ready to go?

For convenience, add the namespace usage globally to your source file:

using CPlugin.PlatformWrapper.MetaTrader4;

After that you can instantiate an object of the required connection type — Manager, for example. The constructor takes four positional arguments: the server address, the manager account login (an integer), the account password, and a logger delegate:

var mgr = new Manager("127.0.0.1:443", 1, "<password>", (ctx, type, message, exception) =>
{
    if (exception != null)
        Log.Error(exception, exception.Message);
    else
        Log.Information($"[{type}] {message}");
});

Let's try connecting to the MT4 server using this class:

var rc = mgr.Connect();
if (rc != ResultCode.Ok)
{
    Console.WriteLine($"Connection error [{rc}] - {mgr.ErrorDescription(rc)}");
    return;
}

Assuming the connection is up, from now on we are able to work with all features of this MT4 server using the mgr object.

Demo version limitation

This library works for 30 days with no limitations. After that a valid license is needed.

The files provided here are secured — this is necessary to protect quality, to offer a guarantee, and to provide after-sale technical support. All reverse-engineering attempts are prohibited and will void all current and future business relations with us.

Working examples

Source code posted here might be obsolete.

Up-to-date working code examples can be found in our GitHub repository at https://github.com/CPlugin/PlatformWrapper.Examples. You can also subscribe to all updates to stay informed.

Important to know

IDisposable

Each Manager class inherits the IDisposable interface to safely release resources allocated by the native library.

Exceptions

It will throw exceptions.

Getting messages from the wrapper to know what is happening

You can catch messages written by the Manager classes by filling the Logger field. The logger delegate takes four arguments — (ctx, type, message, exception), where type is a Logging.EnMessageType. For example:

var mgr = new ManagerPumpEx("mt4.broker.com:443", 1, "<password>", null);

// Console logging
mgr.Logger = (ctx, type, message, exception) =>
{
    if (exception != null)
        Log.Error(exception, exception.Message);
    else
        Log.Information($"[{type}] {message}");
};

// OR

// Logging using NLog, the extension mentioned can be found at the bottom of this page
mgr.Logger = (ctx, type, message, exception) => Log.ProceedMT4WrapperMessages(type, message, exception);

Thread safe

All Manager classes are thread safe, so you can call their methods from a multi-threaded environment. They use locking internally.

Normal mode connection

This mode provides a basic connection. It offers all the features you can use through the MT4 Administrator terminal.

When you need to maintain connections, you can call the KeepAlive method — it creates a separate thread and pings the MT4 server every three seconds.

using (var man = new Manager("127.0.0.1:443", 1, "<password>", (ctx, type, message, exception) =>
{
    if (exception != null)
        Log.Error(exception, exception.Message);
    else
        Log.Information($"[{type}] {message}");
}))
{
    Console.WriteLine("{0}", man.FullVersion);

    // get all users from MT4 server
    var allUsers = man.UsersRequest();

    // create new user
    var ur = new UserRecord()
    {
        Group = "Classic",
        Name = "Name Surname",
        Leverage = 100 // for 1:100
    };
    var result = man.UserRecordNew(ref ur);

    // check result
    Debug.Assert(result == ResultCode.Ok);
    Debug.Assert(ur.Login != 0);

} // dispose Manager class when you don't need it anymore

Pumping mode connection

This mode is needed when you want to get updates from the MT4 server in real time, the way the MT4 Manager terminal does. To subscribe to events, you have a list of event objects to subscribe to. For example:

// create instance — logger is the 4th positional argument
var mgr = new ManagerPumpEx("127.0.0.1:443", 1, "<password>", (ctx, type, message, exception) =>
{
    if (exception != null)
        Log.Error(exception, exception.Message);
    else
        Log.Information($"[{type}] {message}");
})
{
    // object initializer is only for extra properties, e.g. pumping flags
    PumpingFlags = EnPumpingFlags.PUMP_MODE_SYMBOLS | EnPumpingFlags.PUMP_MODE_TRADES
};

// connect to MT4
var result = mgr.Connect();

mgr.OnStart = sender => {
    // get symbols list from local cache
    var sga = sender.SymbolsGetAll();

    foreach (var conSymbol in sga)
    {
        // subscribe to each symbol update
        result = sender.SymbolAdd(conSymbol.Key);
    }
};

// symbols updated
mgr.OnSymbols = sender => {
    Console.WriteLine("OnSymbols");
};

// symbol settings updated
mgr.OnSymbolsEx = (sender, type, cs) => {
    Console.WriteLine("{0} - {1}", type, cs);
};

// catch ping event
mgr.OnPing = sender => {
    Console.WriteLine("Ping");
};

// catch quotes
mgr.OnBidAsk = sender => {
    // get last quotes came from server since last update
    var ticks = mgr.SymbolInfoUpdated();
};

while (true)
{
    if (Console.KeyAvailable)
    {
        var kki = Console.ReadKey(true);
        if (kki.Key == ConsoleKey.Q)
        {
            Console.WriteLine("User pressed 'Q' key");
            mgr.Disconnect();
            break;
        }
    }
}

Useful helpers:

public static class NLogHelpers
{
    public static void ProceedMT4WrapperMessages(this Logger logger, Logging.EnMessageType type, string message, Exception ex)
    {
        switch (type)
        {
            case Logging.EnMessageType.Debug:
                logger.Debug(message);
                break;
            case Logging.EnMessageType.Error:
                logger.Error(message);
                break;
            case Logging.EnMessageType.Info:
                logger.Info(message);
                break;
            case Logging.EnMessageType.Verbose:
                logger.Trace(message);
                break;
            case Logging.EnMessageType.Warning:
                logger.Warn(message);
                break;
            case Logging.EnMessageType.Exception:
                if (ex != null)
                    logger.Error(ex);
                else
                    logger.Error(message);
                break;
            default:
                logger.Debug(message);
                break;
        }
    }
}