Multi-Tier IB

This is a server plugin for MetaTrader 4 that calculates and pays commissions in real time. It can also calculate a rebate to pay back to the client when an agent decides to share part of the bonus fee. It is ready to work with the WebAPI through the MetaQuotes Manager API.

This is a server plugin for MetaTrader 4 that calculates and pays commissions in real time. It can also calculate a rebate to pay back to the client when an agent decides to share part of the bonus fee. It is ready to work with the WebAPI through the MetaQuotes Manager API.

Installation

Deploy the plugin by placing the .dll and .lic files under the /plugins/ folder of your MT4 server installation directory.

Settings

In general, the settings file should follow this format.

CPlugin.IBCommissions.ini

guid=<guid>
shares=<shares>
clients count=<number of clients>
client<client index>=<client login> <agent login>
commissions count=<number of commissions>
commission<commission index>=<group> <symbol> <amount> <numerator> <denominator>
rebates count=<number of rebates>
rebate<rebate index>=<rebate client login> <rebate percent>
verbose=<verbose>

Common settings

  • guid — a unique GUID that is generated automatically the first time you run the server with the plugin. You can change it afterwards if needed. It is used for correlation when you call the WebAPI, so this exact plugin instance picks up the request.
  • verbose — logging verbosity: 0 for errors and warnings, 1 for 0 plus common info and settings, 2 for debugging.
  • shares — a list of numbers separated by spaces. Each number is the percentage shared with the next level up within the multi-level IB tree. The default is 20. The first level is always 100%. The last number is used as the default for any deeper levels.

For example, suppose shares=20 10 and we have the tree client > SubIB_Level3 > SubIB_Level2 > SubIB_Level1 > MasterIB, and we want to pay $5:

  • SubIB_Level3 claims $5 and must share 20% of $5 with SubIB_Level2. Therefore he gets $5 - 20% of $5 = $5 - $1 = $4.
  • SubIB_Level2 claims $1 and must share 10% of $1 with SubIB_Level1. Therefore he gets $1 - 10% of $1 = $1 - $0.1 = $0.9.
  • SubIB_Level1 claims $0.1 and must share 10% of $0.1 with MasterIB. Therefore he gets $0.1 - 10% of $0.1 = $0.1 - $0.01 = $0.09.
  • MasterIB gets $0.01.

The sum is always $5.

Clients configuration

  • clients count — the number of records below that define the client-to-agent links.
  • client<index> — the index must be between 1 and <number of clients> with no gaps.
  • client login — the account that trades.
  • agent login — the IB who receives the commissions.

Commissions rules

  • commissions count — the number of records below that define commission rules.
  • commission<index> — the index must be between 1 and <number of commissions> with no gaps.
  • group — the client's group name mask, for instance demo, demoforex or demo.
  • symbol — the symbol name mask or symbol security group mask, for instance EURUSD, EUR* or Metals.
  • amount — the amount paid, interpreted according to <numerator> and <denominator>.
  • numerator — what is paid: 1 for money (USD by default), 2 for pips.
  • denominator — what it is paid for: 1 for each closed lot, 2 for each closed deal.

When the plugin calculates a commission, it pays <numerator> as the amount per <denominator> as the volume traded, just as MT4 does.

You can therefore choose one of these algorithms:

  • $/lot
  • $/deal
  • pips/lot
  • pips/deal

Example:

commission1=demo EUR* 5 1 1
  • All groups starting with demo.
  • All symbols containing EUR in their names.
  • We pay $5 per each closed lot.

Rebates for clients

  • rebates count — the number of records below that define rebates.
  • rebate<index> — the index must be between 1 and <number of rebates> with no gaps.
  • rebate client login — the login of the client who receives a piece of the bonus that his IB got.
  • rebate percent — the percentage. The plugin deducts the rebate from the master payment to the IB.

For example, a client trades. An IB is going to receive $1.00 of commission and decides to pay 10% back to the client. Therefore the IB gets $0.9 and the client gets $0.1 — $1.00 in total.

Real-life settings file example with MT4 log

Suppose we have this scheme:

  • 100 is the MasterIB.
  • 101 is a SubIB of 100.
  • 102 is a SubIB of 101.
  • 1000 is a client of 102.
  • 102 decides to pay back 10% of his bonus to his client.

The CPlugin.IBCommissions.ini file:

guid=da7ededa-28dd-4b4f-9ed9-db219710422e
verbose=2
shares=20
clients count=3
client1=1000 102
client2=102 101
client3=101 100
commissions count=1
commission1=demoforex * 5 1 1
rebates count=1
rebate1=1000 10

Let us walk through what happens after 1000 closes one lot.

For each lot closed by the client the broker pays $5, so the MasterIB, the SubIBs and the client share that $5 between them. With a single-level tree the MasterIB would get the whole amount. Using the sharing parameter, we transfer 20% of $5 from 102 to 101, and 101 in turn transfers 20% of his bonus to 100. In addition, 102 decided to pay back 10% of his bonus to the client. Therefore we take 10% of $4, which is $0.4, and pay it to the client; after that 102 keeps only $3.6. The $1 sent to 101 must be shared between 101 and 100, so we transfer 20% of $1 to 100.

The MT4 log confirms it:

0 16:13:41 222.222.0.1 '1': request from '1000' (buy 1.00 EURUSD at 1.11751 sl: 0.00000 tp: 0.00000)
0 16:13:41 222.222.0.1 '1': confirm '1000' buy 1.00 EURUSD at 1.11751 sl: 0.00000 tp: 0.00000 (1.11707 / 1.11725)
1 16:13:41 127.0.0.1 '1000': order #503723575, buy 1.00 EURUSD at 1.11751

... after a while ...

1 16:13:43 127.0.0.1 '1000': close instant order #503723575 buy 1.00 EURUSD at 1.11712 sl: 0.00000 tp: 0.00000 (1.11712 / 1.11730)
0 16:13:43 222.222.0.1 '1': request from '1000' (close #503723575 buy 1.00 EURUSD at 1.11712)
0 16:13:43 222.222.0.1 '1': confirm '1000' close #503723575 buy 1.00 EURUSD at 1.11712 (1.11710 / 1.11729)
0 16:14:17 222.222.0.1 '1': dealer disconnected
2 16:14:46 222.222.0.1 '1': login (4.00 482, manager, 0)
0 16:14:46 CPlugin.IBCommissions commission #503723576 3.60 USD credited to '102'
0 16:14:46 CPlugin.IBCommissions rebate #503723577 0.40 USD credited to '1000'
0 16:14:46 CPlugin.IBCommissions commission #503723578 0.80 USD credited to '101'
0 16:14:46 CPlugin.IBCommissions commission #503723579 0.20 USD credited to '100'

WebAPI integration

The plugin hooks MT4ServerAPI::ExternalCommand to answer WebAPI requests. To work with it, send any of the requests described below; it sends back a response or nothing, depending on the request.

C++ enums and DTOs

#pragma pack(push, 1)
struct RequestCommonHeader
{
    GUID guid;
    int cmdCode;
};

enum CommandCodes
{
    BASE,
    Rebates_GetAll,
    Rebates_Clear,
    Rebates_Get,
    Rebates_Set,
    Commissions_GetAll,
    Commissions_Clear,
    Commissions_Get,
    Commissions_Set,
    Links_GetAll,
    Links_Clear,
    Links_Get,
    Links_Set,
    Links_GetIB,
};

struct RebateDTO
{
    int client;
    double percent;
};

struct CommissionDTO
{
    char group;
    char symbol;
    double value;
    int numerator;
    int denominator;
};

struct LinkDTO
{
    int client;
    int ib;
};

#pragma pack(pop)

Common structures

#pragma pack(push, 1)
struct RequestCommonHeader
{
    GUID guid;
    int commandCode;
};

struct ResponseVoid
{
};

template<typename T>
struct Response
{
    const BYTE ff; // it is always 0xff
    T data;
};

template<typename T>
struct ResponseArray
{
    const BYTE ff; // it is always 0xff
    int total;
    T data[];
};
#pragma pack(pop)

Structures to pass into ExternalCommand for each request type

#pragma pack(push, 1)
struct Request_Base : RequestCommonHeader { };
// response: ResponseVoid

struct Request_Rebates_GetAll : RequestCommonHeader { };
// response: ResponseArray<RebateDTO>

struct Request_Rebates_Clear : RequestCommonHeader { };
// response: ResponseVoid

struct Request_Rebates_Get : RequestCommonHeader { int login; };
// response: RebateDTO

struct Request_Rebates_Set : RequestCommonHeader { RebateDTO rebate; };
// response: ResponseVoid

struct Request_Commissions_GetAll : RequestCommonHeader { };
// response: ResponseArray<CommissionDTO>

struct Request_Commissions_Clear : RequestCommonHeader { };
// response: ResponseVoid

struct Request_Commissions_Get : RequestCommonHeader { int idx; };
// response: CommissionDTO

struct Request_Commissions_Set : RequestCommonHeader { int idx; CommissionDTO data; };
// response: ResponseVoid

struct Request_Links_GetAll : RequestCommonHeader { };
// response: ResponseArray<LinkDTO>

struct Request_Links_Clear : RequestCommonHeader { };
// response: ResponseVoid

struct Request_Links_Get : RequestCommonHeader { int login; };
// response: LinkDTO

struct Request_Links_Set : RequestCommonHeader { LinkDTO link; };
// response: ResponseVoid

struct Request_Links_GetIB : RequestCommonHeader { int login; };
// response: ResponseArray<int>
#pragma pack(pop)