Why do we want Google Apps Script?
To trade commerce knowledge between separate MetaTrader terminals, a easy relay server is required. Google Apps Script acts as a free middleman that transfers commerce occasions from the Grasp account to the Slave account. It ensures dependable supply of occasions even after web interruptions or terminal restarts and doesn’t require a VPS or devoted server.
Methods to create and deploy Google Apps Script
-
Go to https://script.google.com and Click on Begin scripting

-
Click on New undertaking

-
Delete the default code and paste the script supplied under the instruction steps

-
Press Ctrl + S to avoid wasting the undertaking (the highest menu will turn out to be lively)

-
Click on Deploy within the top-right nook and choose New deployment

-
Within the opened window, click on Choose sort (⚙️) and select Internet app

-
In the Description discipline, enter Model 1 (any textual content is ok). Set Who has entry to Anybody and depart Execute as unchanged

-
Click on Deploy – your Apps Script URL will likely be generated. Copy and paste this URL into the EA enter settings

const API_KEY = 'I_AM_API_KEY'; const MAX_PRUNE = 200; const CONSUMER_TTL_MS = 6 * 60 * 60 * 1000; const MAX_CONSUMERS_PER_CHANNEL = 50; perform doPost(e) { const lock = LockService.getScriptLock(); let locked = false; strive 'single'; const retailer = PropertiesService.getScriptProperties(); let uncooked = e.postData.contents catch (err) seen(cc) = Quantity(retailer.getProperty(_seenKey(channel, cc)) lastly } perform doGet(e) { const lock = LockService.getScriptLock(); let locked = false; strive { lock.waitLock(10000); locked = true; if (!e || !e.parameter) return _resp( '0'); if (!seen) proceed; if (now - seen <= CONSUMER_TTL_MS) lively.push(c); ); const key = (e.parameter.key || '').toString(); if (key !== API_KEY) return _resp( a < min) min = a; ); const channel = (e.parameter.channel || 'default').toString(); const shopper = (e.parameter.shopper || '').toString(); const c = shopper || 'single'; const restrict = Math.max(1, Math.min(100, Quantity(e.parameter.restrict || 20))); const retailer = PropertiesService.getScriptProperties(); _touchConsumerFast(retailer, channel, c); const minId = Quantity(retailer.getProperty(_minKey(channel)) || '0'); const seq = Quantity(retailer.getProperty(_seqKey(channel)) || '0'); const ackKey = _ackKey(channel, c); let ack = Quantity(retailer.getProperty(ackKey) || '0'); if (minId > 0) const mode = (e.parameter.mode || '').toString(); if (mode === 'well being' || mode === 'debug') { const customers = _listActiveConsumersFast(retailer, channel); const minAck = _minAckFast(retailer, channel, customers); const out = ; if (mode === 'debug') '0'); retailer.setProperty(ackKey, String(Math.max(0, seq))); return; return _resp(out); } const occasions = (); let missing_id = 0; for (let id = ack + 1; id <= seq && occasions.size < restrict; id++) { const evStr = retailer.getProperty(_evKey(channel, id)); if (!evStr) const a = Quantity(retailer.getProperty(_ackKey(channel, c)) strive arr = JSON.parse(retailer.getProperty(listKey) catch (parseErr) } if (missing_id && minId > 0 && missing_id < minId) { const newAck = Math.max(0, minId - 1); retailer.setProperty(ackKey, String(newAck)); const events2 = (); let missing2 = 0; for (let id = newAck + 1; id <= seq && events2.size < restrict; id++) { const evStr = retailer.getProperty(_evKey(channel, id)); if (!evStr) const a = Quantity(retailer.getProperty(_ackKey(channel, c)) strive { events2.push(JSON.parse(evStr)); } catch (_) { missing2 = id; break; } } if (!missing2) { return _resp({ okay:true, ack: newAck, seq: seq, occasions: events2 }); } return _resp({ okay:false, error:'gap_detected', ack: newAck, seq: seq, missing_id: missing2 }); } if (missing_id) { return _resp({ okay:false, error:'gap_detected', ack: ack, seq: seq, missing_id: missing_id }); } return _resp({ okay:true, ack: ack, seq: seq, occasions: occasions }); } catch (err) { return _resp({ okay:false, error:'exception', message:String(err), stack:(err && err.stack) ? String(err.stack) : '' }); } lastly { if (locked) { strive { lock.releaseLock(); } catch(_) {} } } } perform _nextSeq(retailer, channel) perform _touchConsumerFast(retailer, channel, shopper) { const now = Date.now(); retailer.setProperty(_seenKey(channel, shopper), String(now)); const listKey = _consumersKey(channel); let arr = (); strive catch(_) { arr = (); } if (arr.indexOf(shopper) < 0) { arr.push(shopper); if (arr.size > MAX_CONSUMERS_PER_CHANNEL) arr = arr.slice(arr.size - MAX_CONSUMERS_PER_CHANNEL); retailer.setProperty(listKey, JSON.stringify(arr)); } const ackKey = _ackKey(channel, shopper); const ackStr = retailer.getProperty(ackKey); if (ackStr === null || ackStr === undefined || ackStr === '') const minId = Quantity(retailer.getProperty(_minKey(channel)) || '0'); const ack = Quantity(ackStr || '0'); if (minId > 0) { const floorAck = Math.max(0, minId - 1); if (ack < floorAck) retailer.setProperty(ackKey, String(floorAck)); } } perform _listActiveConsumersFast(retailer, channel) { const now = Date.now(); const listKey = _consumersKey(channel); let arr = (); strive catch(_) { arr = (); } const lively = (); for (const c of arr) if (lively.size === 0) lively.push('single'); return lively; } perform _minAckFast(retailer, channel, customers) { let min = null; for (const c of customers) a < min) min = a; return min === null ? 0 : min; } perform _pruneByMinAckFast(retailer, channel) { const customers = _listActiveConsumersFast(retailer, channel); const minAck = _minAckFast(retailer, channel, customers); if (minAck <= 0) return; _pruneAckedUpTo(retailer, channel, minAck); } perform _pruneAckedUpTo(retailer, channel, ackId) { const minKey = _minKey(channel); let minId = Quantity(retailer.getProperty(minKey) || '0'); if (!minId) return; let eliminated = 0; whereas (minId && minId <= ackId && eliminated < MAX_PRUNE) { retailer.deleteProperty(_evKey(channel, minId)); minId++; eliminated++; } const seq = Quantity(retailer.getProperty(_seqKey(channel)) || '0'); if (minId > seq) { retailer.deleteProperty(minKey); } else { retailer.setProperty(minKey, String(minId)); } } perform _seqKey(channel) { return channel + '__seq'; } perform _minKey(channel) { return channel + '__min'; } perform _ackKey(channel, shopper) { return channel + '__ack__' + shopper; } perform _evKey(channel, id) { return channel + '__ev__' + id; } perform _seenKey(channel, shopper) { return channel + '__seen__' + shopper; } perform _consumersKey(channel) { return channel + '__consumers'; } perform _resp(obj) { return ContentService .createTextOutput(JSON.stringify(obj)) .setMimeType(ContentService.MimeType.JSON); }