Skip to content
NEXORA Money & AI

How to Build a License Key and Expiry System for a Trading Tool

October 4, 2026 · OYIN ADESOLA · 5 min read · Updated October 12, 2026
Cover graphic for the article: How to Build a License Key and Expiry System for a Trading Tool

Last checked: 12 October 2026. This guide draws on the MQL5 reference for CryptEncode, AccountInfoInteger, TimeCurrent and WebRequest. The system described is a basic speed bump, not strong security. Compile and test on a demo account before shipping anything to customers.

If you sell an MT5 tool on your own website, you need a way to say “this copy belongs to this customer and works until this date”. That is a licence key and expiry system. This guide builds a simple offline version that needs no server, explains its limits and describes when to move to online verification.

What the key must express

  • Which account the licence is for (the trading account number).
  • When it expires.
  • That the key was issued by you and not invented by someone else.

The third point is done with a checksum computed from the other values and a secret that only you know. If someone edits the account number or date in the key, the checksum no longer matches and the check fails.

Key format

A readable format keeps support simple: LOGIN-YYYYMMDD-CHECK, for example 12345678-20261231-A1B2C3D4. The check part is the first eight hexadecimal characters of a SHA-256 hash of the login, the expiry and your secret string.

Summary graphic listing the main sections of: How to Build a License Key and Expiry System for a Trading Tool

Generating keys

You create keys on your side, with a small MQL5 script that runs the same hash. This script prints a key for a given login and expiry:

#property script_show_inputs

input long   InpLogin  = 12345678;
input string InpExpiry = "20261231";      // YYYYMMDD
const  string SECRET   = "change-this-long-random-secret";

string Sha256Hex(const string text)
  {
   uchar data[], key[], hash[];
   // copy exactly StringLen characters so no trailing zero byte is hashed
   StringToCharArray(text, data, 0, StringLen(text), CP_UTF8);
   if(CryptEncode(CRYPT_HASH_SHA256, data, key, hash) <= 0) return("");
   string hex = "";
   for(int i = 0; i < ArraySize(hash); i++)
      hex += StringFormat("%02X", hash[i]);
   return(hex);
  }

string MakeKey(const long login, const string expiry)
  {
   string h = Sha256Hex(SECRET + "|" + (string)login + "|" + expiry);
   return((string)login + "-" + expiry + "-" + StringSubstr(h, 0, 8));
  }

void OnStart()
  {
   Print("Licence key: ", MakeKey(InpLogin, InpExpiry));
  }

Keep this script private. Anyone who holds the secret can make valid keys.

Checking keys inside the product

The product receives the key as an input. It splits the key, recomputes the check, compares account and date, and refuses to run if anything fails.

input string InpLicenceKey = "";    // paste the key you received

// Sha256Hex() and SECRET are the same as in the key generator.

bool CheckLicence(string &message)
  {
   string parts[];
   if(StringSplit(InpLicenceKey, '-', parts) != 3)
     { message = "Licence key format is wrong."; return(false); }

   long   login  = (long)parts[0];
   string expiry = parts[1];
   string check  = parts[2];

   string expected = StringSubstr(Sha256Hex(SECRET + "|" + parts[0] + "|" + expiry), 0, 8);
   if(check != expected)
     { message = "Licence key is not valid."; return(false); }

   if(login != AccountInfoInteger(ACCOUNT_LOGIN))
     { message = "This licence is for another account."; return(false); }

   string e = StringSubstr(expiry,0,4) + "." + StringSubstr(expiry,4,2) + "." + StringSubstr(expiry,6,2);
   datetime end = StringToTime(e) + 86400;      // valid through the whole expiry day
   if(TimeCurrent() >= end)
     { message = "Licence expired on " + e + "."; return(false); }

   message = "Licensed until " + e;
   return(true);
  }

Call CheckLicence from OnInit or at the start of OnCalculate or OnTick, display the message on the chart and stop the real work when it returns false. The hash function CryptEncode is part of the MQL5 standard library and does not need a DLL, so this scheme is compatible with the rule against DLL imports where that applies.

Honest limitations

  • The secret lives inside the file. Anyone with enough skill might extract it from a compiled program and generate their own keys. This protects against casual sharing, not against a determined attacker.
  • The key is transferable on the same account. Anyone who runs the product on that account can use it. That is normal for account-locked licences.
  • TimeCurrent depends on the broker server. It is better than the local clock, but at weekends it reflects the last quote, so a licence may appear to expire a little late.
  • No revocation. You cannot cancel a key before its expiry because the product never asks you.

When to move to online verification

If you sell subscriptions, refunds or high-priced tools, you want to be able to switch a licence off. That needs a server. The MQL5 reference says WebRequest works in Expert Advisors and scripts, not in indicators, and the destination address must be added to the allowed URL list in the terminal’s options. A design that works for indicators is a small companion EA that checks the licence and shares the result through a global variable or a file. An online check also needs a plan for outages: if your server is down, should paying customers be locked out? A common choice is a grace period of a day or two.

Customer-friendly practices

  1. Tell buyers exactly what the licence covers before they pay.
  2. Show the licence status on the chart so support is easy.
  3. Provide a way to move the licence to a new account, with a limit to prevent abuse.
  4. Send renewal reminders by email before the expiry.
  5. Never send keys through channels that other people can read.

Testing the system

  1. Generate a key for your demo account and a future date. The product should run.
  2. Edit one digit of the key. It should fail.
  3. Use a key for a different account. It should say so.
  4. Generate a key with yesterday’s date. It should report expiry.
  5. Run it in the Strategy Tester and decide whether the tester should be allowed, as in the trial article.

What we could not confirm

  • That CryptEncode returns identical results on every build. Test your generator and checker with the same build.
  • That the code compiles without warnings. The generator and the checker must hash exactly the same bytes, so test them against each other and print the hash length while testing.

Frequently asked questions

Is this secure enough for a 500 dollar EA? Probably not by itself. Use online verification and per-customer builds for higher-value products.

Do I need a licence system on the MQL5 Market? No. The Market’s activations and encryption handle it.

Key takeaways

An account-locked, checksummed key with an expiry is a reasonable first licence system, but it is a speed bump. Keep the generator private, display clear messages and move to server verification when the stakes rise.

Sources checked

  • MQL5 Reference, CryptEncode, AccountInfoInteger, TimeCurrent and WebRequest, mql5.com
  • MQL5 Book, “Data exchange with a web server via HTTP/HTTPS”, mql5.com

Educational content only — not financial advice. Income and savings results vary and are never guaranteed. How we write

Enjoyed this story?

Get the best stories delivered weekly.

Leave a Reply

Your email address will not be published. Required fields are marked *

Share with