If you want to share or sell your EA or Indicator then you should be thinking about how to protect your work and prevetn unauthorised distribution.

This article shows 4 techniques covering limited capability trials, time limited versions and ultimately issuing licence codes linked to a single account. These are simple techniques that you can use without the need for a licence server or any third party dll, of course they are limited for those same reasons.

For this article I will be using define statements to turn various protection methods on and off. You could use global variables but I find the define statements are more readable in the main code.

First I start with a simple EA. This EA does not take any trades but it will load and continue to run, without actually doing anything.

/*
Restricted EA
Copyright 2021 Orchard Forex
*/

#property copyright "Copyright 2021, Orchard Forex"
#property link "https://www.orchardforex.com"
#property version "1.00"

// this is important
#property strict

int OnInit() {

	return(INIT_SUCCEEDED);

}

void OnTick() {

}

I want to create my copy protection routines in a standard library. That makes it much easier to reuse in other experts or indicators. To do that I created the following outline.

/*
	LicenceCheck.mqh
	Copyright 2021, Orchard Forex
*/

#property copyright "Copyright 2013-2020, Orchard Forex"
#property link      "https://www.orchardforex.com"
#property version   "1.00"

// this is important
#property strict

bool	LicenceCheck() {

	bool	valid	=	true;
	
	// Insert code blocks below this line
	
	return(true);
	
}

Then just add the include statement to the EA

#include <Orchard/LicenceCheck/LicenceCheck.mqh>

and in the OnInit section call the licence check function and exit the EA or Indicator immediately if the check fails.

	if (!LicenceCheck()) return(INIT_FAILED);

So far there are no checks. The first check I want to add is a simple restriction on the symbols that can be used. You could replace this with timeframes or even implement both. To add a check on symbols insert this code inside the LicenceCheck function

#ifdef LIC_SYMBOLS
	valid	=	false;
	string	validSymbols[]	=	LIC_SYMBOLS;
	for (int i=ArraySize(validSymbols)-1; i>=0; i--) {
		if (Symbol()==validSymbols[i]) {
			valid	=	true;
			break;
		}
	}
	if (!valid) {
		Print("This is a limited trial version, it will not work with symbol " + Symbol());
		return(false);
	}
#endif

Going through the code, it will only be executed (or even compiled) if the macro LIC_SYMBOLS is defined. Then there is a simple loop through the symbols included in the macro to see if the current chart symbol is in the list. If not then the check fails. Now just add this definition into the EA, somewhere above the include statement for the licence check library

#define	LIC_SYMBOLS	{	"GBPUSD", "USDCAD"	}

#include <Orchard/LicenceCheck/LicenceCheck.mqh>

This line uses the { } syntax to define the macro as an array and the values in the macro are then used in the checks.

We can do the same with account types, to limit to demo accounts for example. Add this code to the Licence check function.

#ifdef LIC_TRADE_MODES
	valid	=	false;
	int	validModes[]	=	LIC_TRADE_MODES;
	long	accountTradeMode	=	AccountInfoInteger(ACCOUNT_TRADE_MODE);
	for (int i=ArraySize(validModes)-1; i>=0; i--) {
		if (accountTradeMode==validModes[i]) {
			valid	=	true;
			break;
		}
	}
	if (!valid) {
		Print("This is a limited trial version, it will not work on this type of account");
		return(false);
	}
#endif

It is basically the same as the symbol check but in this case we are comparing to the account type. Then add the macro definition to the EA like this

#define	LIC_TRADE_MODES		{	ACCOUNT_TRADE_MODE_CONTEST, ACCOUNT_TRADE_MODE_DEMO }

and everything works.

For something just a little more complicated, you may want to release a time limited version. In this simple example I’m not creating any hidden activation so the time limit isn’t based on when the EA is first used. It is based on a date specified in the EA. This is most likely useful where you are selling custom written code and want to make sure your client doesn’t just take the sample and not pay for your work.

To do this I use 2 macro definitions. The start date macro is optional and if you don’t specify it then the compile date will be used. Add these lines to your EA

#define	LIC_EXPIRES_DAYS	30
#define	LIC_EXPIRES_START	D'2021.03.01'

Then add this block to the licence check function

#ifdef LIC_EXPIRES_DAYS
	#ifndef LIC_EXPIRES_START
		#define LIC_EXPIRES_START	__DATETIME__
	#endif
	
	datetime	expiredDate	=	LIC_EXPIRES_START + (LIC_EXPIRES_DAYS*86400);
	PrintFormat("Time limited copy, licence to use expires at %s", TimeToString(expiredDate));
	if (TimeCurrent()>expiredDate) {
		Print("Licence to use has expired");
		return(false);
	}
#endif

The code will first define the start date as the compile date if you haven’t already set the start date. Then it calculates the trial end date, prints a trial message in the expert log and if the current date is after the end of the trial returns a failure.

The last situation uses encryption to set a registration key that is locked to a single account. In your EA you will need to define a private key, this can be anything but you should make it something hard to guess and use a different key in each EA. You also need to add an input for the end user to enter the licence code

#define	LIC_PRIVATE_KEY	"Orchard"
input	string	InpLicence	=	"enter licence code here";	//	Licence key

and modify the call to licence check from your EA to pass in the registration code

	if (!LicenceCheck(InpLicence)) return(INIT_FAILED);

and in the licence check function to receive the registration.

bool	LicenceCheck(string licence="") {

Add this function to the include file, to calculate the encrypted key that should be used with this account

string	KeyGen(string account, string privateKey) {

	uchar	accountChar[];
	StringToCharArray(account+privateKey, accountChar);
	
	uchar	keyChar[];
	StringToCharArray(privateKey, keyChar);
	
	uchar	resultChar[];
	CryptEncode(CRYPT_HASH_SHA256, accountChar, keyChar, resultChar);
	CryptEncode(CRYPT_BASE64, resultChar, resultChar, resultChar);
	
	string result	=	CharArrayToString(resultChar);
	return(result);

}

This code takes a supplied account number and key and converts both to char arrays before using CryptEncode to encrypt the account numer using the key and then converting the result to a human readable value using Base64.

With that function added insert this into the licence check function to get the current account number, call the KeyGen function to create the encrypted value and compare that with the registration key entered.

#ifdef LIC_PRIVATE_KEY
	long	account	=	AccountInfoInteger(ACCOUNT_LOGIN);
	string result	=	KeyGen(IntegerToString(account), LIC_PRIVATE_KEY);

	if (result!=licence) {
		Print("Invalid licence");
		return(false);
	}
	
#endif

That takes care of everything for the EA. You probably want to be able to generate licence codes for your clients and you can do that with this script. You just need the account number from the customer.

/*
	LicenceGenerator
	Copyright 2021, Orchard Forex
*/

#property copyright "Copyright 2013-2020, Orchard Forex"
#property link      "https://www.orchardforex.com"
#property version   "1.00"

// this is important
#property strict

#property script_show_inputs

#include <Orchard/LicenceCheck/LicenceCheck.mqh>

input	string	InpPrivateKey	=	"";
input	string	InpAccount		=	"";

void OnStart() {
   
   string	key	=	KeyGen(InpAccount, InpPrivateKey);
   Alert("The key is " + key);
   Print("The Key is " + key);
}

Leave a Reply