Analysis, not financial advice. Automated trading can lose money quickly. Test everything on a demo account first.
MetaTrader 4 can read data from the internet without any extra software. The built-in WebRequest() function lets an Expert Advisor or a script download a web page or a small text file, then act on what it finds. This guide shows how to connect MetaTrader 4 to a free signal feed, such as the hourly gold reading published on our signal feed page, and how to fix the error that stops most people on their first try: error 4060.
What WebRequest does, and where it works
WebRequest() sends an HTTP request (GET or POST) to an address and waits for the reply. It returns the HTTP status code, such as 200 for success, or -1 if the request failed before a reply arrived. When it returns -1, GetLastError() tells you why.
Three rules from the MQL4 documentation matter more than anything else:
- The address must be allowed in the terminal. MetaTrader 4 refuses to contact any website that is not on its allowed list.
- It only runs in Expert Advisors and scripts. It cannot be called from a custom indicator. Indicators run in a common thread shared by all indicators and charts on one symbol, so one indicator waiting on the network could freeze every chart of that symbol.
- It does not run in the Strategy Tester. A back test cannot download live data, so you need a separate plan for testing (see the frequently asked questions below).
The call is also synchronous: the Expert Advisor pauses until the reply arrives or the timeout expires. Keep requests small and infrequent, and never call WebRequest() on every tick.
Step 1: Allow the address in MetaTrader 4
- In MetaTrader 4, open Tools, then Options.
- Select the Expert Advisors tab.
- Tick Allow WebRequest for listed URL.
- Add the site's base address on a new line, for example
https://povertysux.co. You do not need to add the path to the file, and you do not need to add a port, because MetaTrader picks port 443 for addresses that start withhttpsand port 80 forhttp. - Click OK, then remove and re-attach the Expert Advisor so the setting takes effect.
While you are on that tab, make sure Allow automated trading is also enabled if you intend the Expert Advisor to place orders. Reading a signal does not require it.
Step 2: Understand the feed format
A good free feed is boring on purpose: one short line of text that a program can split without a parser library. Our signal.txt file contains one line of ten fields separated by the pipe character (|), with no spaces. The field names, in order, are:
signal|signal_code|spot|updated_utc|valid_until_utc|support|resistance|invalidation|event_risk|version
A real line from 6:18 PM Eastern Time on October 26, 2026 looked like this:
BUY|1|4015.65|2026-10-26T22:18:02Z|2026-10-26T23:28:02Z|4007.90|4018.70|4022.00|0|1
signalis one ofSTRONG_BUY,BUY,NEUTRAL,SELLorSTRONG_SELL.signal_codeis the same reading as a number: 2, 1, 0, -1 or -2.updated_utcis the time of the price sample the reading is based on, andvalid_until_utcis 70 minutes after it. Both are in Coordinated Universal Time (theZat the end).event_riskis 1 when a scheduled major economic release is within 120 minutes, otherwise 0.- New fields are only ever added at the end of the line, so an Expert Advisor should check for at least 10 fields rather than exactly 10.
The two fields that matter most are the numeric signal_code (easier to compare than text) and valid_until_utc, the time after which the reading should be treated as stale. A reading that is past its expiry time is not a signal at all; it is old information. Any Expert Advisor that reads a web feed should check the expiry before using the value.
Expiry is normal, not an error. Readings are published every hour from Sunday 6 PM to Friday 5 PM New York time, with no update during the daily 5 PM to 6 PM break. Because each reading expires 70 minutes after its own price sample, the file is legitimately expired for part of every evening around the break, and from about 5:25 PM on Friday until the first update after 6 PM on Sunday. Treat those times as "no signal".
The full field list, including the JSON and CSV versions, is on the signal feed documentation page.
Step 3: A minimal Expert Advisor that reads the feed
The example below polls the feed every five minutes using a timer, splits the line, checks the expiry time and shows the result in the chart's corner. It does not trade. Add your own order logic only after you have watched it run correctly on a demo account.
#property strict
input string SignalURL = "https://povertysux.co/signal.txt";
input int PollSeconds = 300; // five minutes; the feed updates hourly
input int TimeoutMs = 5000; // give up after five seconds
datetime g_lastPoll = 0;
string g_signal = "";
int g_code = 99; // 99 is this Expert Advisor's own "no reading" value; the feed never sends it
datetime g_validUntil = 0;
// Forget the stored reading so a failed download can never leave an old value in use.
void ClearReading()
{
g_signal = "";
g_code = 99;
g_validUntil = 0;
}
// Converts "2026-10-26T21:23:00Z" into an MQL4 datetime (UTC).
datetime IsoToTime(string iso)
{
string s = iso;
StringReplace(s, "-", ".");
StringReplace(s, "T", " ");
StringReplace(s, "Z", "");
return StringToTime(s);
}
bool FetchSignal()
{
ClearReading(); // a failure on any path below leaves "no reading"
char post[], result[];
string replyHeaders;
ResetLastError();
int status = WebRequest("GET", SignalURL, NULL, NULL, TimeoutMs,
post, 0, result, replyHeaders);
if(status == -1)
{
int err = GetLastError();
if(err == 4060)
Print("Error 4060: add the site address under Tools > Options > ",
"Expert Advisors, and run this as an Expert Advisor, not an indicator.");
else
Print("WebRequest failed, error ", err);
return false;
}
if(status != 200) { Print("Server answered with HTTP ", status); return false; }
string body = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
body = StringTrimRight(StringTrimLeft(body));
string f[];
if(StringSplit(body, '|', f) < 10) { Print("Unexpected reply: ", body); return false; }
for(int i = 0; i < ArraySize(f); i++) f[i] = StringTrimRight(StringTrimLeft(f[i]));
g_signal = f[0];
g_code = (int)StringToInteger(f[1]);
g_validUntil = IsoToTime(f[4]);
if(g_validUntil <= 0) { Print("Unreadable expiry time: ", f[4]); ClearReading(); return false; }
return true;
}
bool SignalIsFresh() { return g_validUntil > 0 && TimeGMT() < g_validUntil; }
int OnInit()
{
EventSetTimer(30);
g_lastPoll = TimeLocal();
FetchSignal();
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason) { EventKillTimer(); Comment(""); }
void OnTimer()
{
if(TimeLocal() - g_lastPoll >= PollSeconds)
{
g_lastPoll = TimeLocal();
FetchSignal();
}
if(!SignalIsFresh()) { Comment("Gold signal: missing or expired"); return; }
Comment("Gold signal: ", g_signal, " (code ", g_code, "), valid until ",
TimeToString(g_validUntil), " UTC"); // UTC = Coordinated Universal Time
}
A few design choices are worth copying even if you write your own version:
- Use a timer, not the tick handler. Gold can produce many ticks per second. A timer keeps network calls predictable.
- Treat failure as "no signal".
FetchSignal()clears the stored reading before every download, so if the request fails or the reply is malformed, the code shows "missing" straight away. A reading that downloaded correctly is used only until its own expiry time, never beyond it. - Compare times in Coordinated Universal Time.
TimeGMT()uses your computer's clock and time zone settings to work it out, so keep the computer's clock synchronised. The feed's times end inZ, meaning Coordinated Universal Time, andStringToTime()expects theyyyy.mm.dd hh:milayout, which is why the helper swaps the separators.
If you prefer not to write the parsing yourself, the free include file and example Expert Advisor linked from the signal feed page wrap the same steps in two functions.
How to fix error 4060
Error 4060 is named ERR_FUNCTION_NOT_CONFIRMED in MQL4, with the description "Function is not allowed for call". In practice it shows up in three situations. Check them in this order:
- The address is not on the allowed list, or the box is unticked. Go back to Step 1. Check the spelling, check
httpsagainsthttp, and remove any trailing spaces. The address you list must match the start of the address in your code. - The code is running inside an indicator. Indicators cannot call
WebRequest(). Move the network call into an Expert Advisor or a script. If you need the value inside an indicator, have the Expert Advisor store it in a global variable of the terminal (GlobalVariableSet()) and read it from the indicator. - The terminal has not picked up the new setting. Remove the Expert Advisor from the chart and attach it again. If that fails, restart MetaTrader 4.
Two related errors are easy to confuse with it. The MQL4 reference says WebRequest() cannot be executed in the Strategy Tester; the code you see there is typically error 4059 (ERR_FUNC_NOT_ALLOWED_IN_TESTING, "Function is not allowed in testing mode"). In MetaTrader 5 the indicator case usually reports error 4014 instead; in MetaTrader 4, 4014 means something else ("unknown command"). If you get an HTTP status such as 403 or 404 rather than -1, the terminal did connect: the problem is the address or the server, not the permission.
Other common problems
- The request times out. Increase the timeout to around 5,000 to 10,000 milliseconds, and check that a firewall or antivirus program is not blocking the terminal.
- The reply is empty or full of odd characters. Make sure you convert the reply with
CP_UTF8, and print the raw body to the Experts tab to see what actually arrived. - The value never changes, or shows as expired. Check the expiry time. Our feed is not updated during the daily 5 PM to 6 PM New York break or from Friday 5 PM to Sunday 6 PM, so readings legitimately expire then rather than being repeated. Your Expert Advisor should treat that as "no signal", not as an error.
- The terminal freezes briefly. Your polling interval is too short, or the timeout is too long. Polling once every few minutes is plenty for an hourly feed.
Testing without the Strategy Tester
Because WebRequest() does not run in the tester, separate the network part from the decision part. Our signal history page publishes signal-history.json, but it holds only the last 24 hourly readings, for the hourly timeframe only. If you want weeks of test data, have your Expert Advisor log each reading it downloads to a file, then write a test version that reads those saved values from the local file instead of the web. Then forward-test the live version on a demo account for at least several weeks before considering anything else.
A word on using any signal
A signal is a summary of indicators, not knowledge of the future. Read the technical summary to see how ours is built, size every position so that being wrong is affordable, and read the disclaimer before connecting any feed to real money.
Frequently asked questions
Can I call WebRequest from a custom indicator?
No. MetaTrader 4 only allows WebRequest() in Expert Advisors and scripts. Fetch the data in an Expert Advisor and pass it to the indicator through a terminal global variable or a file.
Do I have to list the full file address in the options?
No. Listing the site's base address, such as https://povertysux.co, covers the files under it. The protocol must match the one in your code.
Why does my Expert Advisor work live but fail in the Strategy Tester?
WebRequest() cannot run in the Strategy Tester, which typically shows up as error 4059. Test the logic with readings you have saved to a file instead.
How often should I poll a free feed?
Match the feed's update schedule. For an hourly signal, every five minutes is more than enough and keeps load on the server and your terminal low.
Is it safe to trade automatically on a free signal?
No signal is safe in the sense of guaranteeing a result. Use a demo account first, check the expiry on every reading, and decide in advance what your Expert Advisor does when the feed is missing.
Analysis, not financial advice. Poverty Sux does not manage money or recommend trades.
Not financial advice. This guide is general education about how technical tools and markets work. It does not consider your situation, and trading gold, especially with leverage, can lose more than you deposit. Read the disclaimer.