Expert advisors, indicators and screeners
Robots and indicators that keep working after the backtest: lot size calculated correctly, broker limits respected, and a recovery path for the disconnects that stop amateur code.
- MQL4
- MQL5
- PINE V6
- CTRADER
We build MetaTrader expert advisors, TradingView indicators, and the systems real money runs on. Fixed price within 48 hours. Full source, always — no lock-in.
Losing two trades out of three is still a winning system, as long as each win is worth two losses. Drag the slider until it isn’t.
Hover the chart to read any single trade
A static pass over MQL4, MQL5 and Pine looking for the handful of mistakes that never show up in the Strategy Tester and always show up on a live account. It reads your code the way we would on the first pass of a paid audit.
Nothing is uploaded. The analysis runs entirely inside your browser.
The broker rejects any volume that is not a multiple of its step, so the order simply never opens. The Strategy Tester checks the step too, but only for the one symbol and broker you test on — so a backtest stays clean whenever the formula happens to land on that step, and the same code fails on a symbol or broker with a coarser one.
Fix: Read SYMBOL_VOLUME_STEP, floor the lot to a multiple of it, then clamp between SYMBOL_VOLUME_MIN and SYMBOL_VOLUME_MAX before sending.
OrderSend(Symbol(), OP_BUY, currentLot, Ask, 3, sl, tp, "buy", 0, 0, clrGreen);Every broker refuses stops placed closer to price than its minimum distance. On a quiet pair it works; the first time the spread widens, the order is rejected and the position runs unprotected.
Fix: Read SYMBOL_TRADE_STOPS_LEVEL and push the stop out to at least that distance, then check SYMBOL_TRADE_FREEZE_LEVEL before modifying an open position.
OrderSend(Symbol(), OP_BUY, currentLot, Ask, 3, sl, tp, "buy", 0, 0, clrGreen);This is not a bug, it is a choice — but it is the choice that produces a beautiful equity curve and then removes the account in one losing streak.
Fix: If it is deliberate, cap the number of steps and the maximum exposure, and test against the worst losing streak in the data rather than the average.
currentLot = currentLot * Multiplier;Indicator buffers can hold zero before they have enough bars, and a flat or single-tick bar has no range. Divide by that and, depending on a compiler setting, the program either stops with "zero divide" or carries on with infinity — and the first trades after a restart are placed on nonsense values.
Fix: Check the denominator is meaningfully above zero before dividing, and return early when the indicator is not ready.
double ratio = (fast - slow) / atr;The same input means ten times the distance depending on the broker. A 40-pip stop becomes a 4-pip stop, which the market takes out immediately.
Fix: Derive a pip from _Point and _Digits: multiply by 10 when _Digits is 3 or 5, and use that everywhere instead of raw Point.
double sl = Bid - StopLoss * Point;When AutoTrading is off, the account is read-only, or the terminal is still connecting, every send fails silently and the robot looks like it simply stopped working.
Fix: Guard the send with TERMINAL_TRADE_ALLOWED and MQL_TRADE_ALLOWED, and log the reason when it is refused.
OrderSend(Symbol(), OP_BUY, currentLot, Ask, 3, sl, tp, "buy", 0, 0, clrGreen);A rejected order is indistinguishable from a filled one. The robot carries on believing it is in a position that does not exist, and the next decision is built on a false state.
Fix: Capture the return value, check the retcode, and read GetLastError on failure. Decide explicitly whether to retry or stand down.
OrderSend(Symbol(), OP_BUY, currentLot, Ask, 3, sl, tp, "buy", 0, 0, clrGreen);The order is rejected for insufficient margin, and whatever your code does next assumes a position that does not exist. Checking funds before a trade is one of the checks MetaQuotes lists for EAs published in its Market.
Fix: Call OrderCalcMargin for the intended volume and compare it to AccountInfoDouble(ACCOUNT_MARGIN_FREE) before sending. Decide explicitly what to do when it does not fit — skipping the trade is a decision, silently failing is not.
OrderSend(Symbol(), OP_BUY, currentLot, Ask, 3, sl, tp, "buy", 0, 0, clrGreen);Two prices that look identical on screen differ in the last bits, so the branch never runs. It fails silently and intermittently, which is the hardest kind of bug to find months later.
Fix: Compare with a tolerance: MathAbs(a - b) < _Point / 2, or normalise both sides to the symbol's digits first.
if(AccountProfit() == 0.0)While OnTick is asleep the EA handles nothing else, and ticks that arrive meanwhile are not queued; any price read before the pause — and MQL4's Ask and Bid until RefreshRates — is stale when it wakes. In the Strategy Tester, Sleep costs no real time, so the backtest never shows what the pause costs live.
Fix: Use a timer event or a timestamp check instead of blocking, so the handler always returns quickly.
Sleep(500);Is your own EA or indicator doing the same?
Send it to us. We read it line by line and come back with a fixed price within 48 hours.
We are a software team, and the trading terminal is only where our work starts. What we are best at is software that handles money and time correctly: the robot that trades, the data it reads, the dashboard that shows what it did, and the app that wakes you when something is wrong. One team builds all of it, so nothing gets lost where the pieces meet.
Robots and indicators that keep working after the backtest: lot size calculated correctly, broker limits respected, and a recovery path for the disconnects that stop amateur code.
Collecting, cleaning and storing price data; logging every order; checking the records against the broker’s. The quiet layer that decides whether the number on your screen can be trusted.
Broker APIs, payment gateways, CRMs and messaging apps, wired together so a failed step can be run again safely and you can see what happened.
A page that shows what the system did today, a portal for your clients, an internal tool for your team. Fast on a weak connection, and correct in English, Persian and Arabic.
Android, iOS and desktop apps for the systems above: your portfolio in your pocket, and an alert that actually arrives. Built on the same core, so the rules cannot drift between platforms.
Where the system ends and real money begins. Everything above it is ours to get right.
What keeps a system standing is rarely the idea. It is whether somebody decided, in advance, what happens when the connection drops at 02:14.
Those decisions are the specification. It is written before the code, and the price is attached to it — so you know exactly what you are buying before we start.
The same indicator, the same candles, drawn two ways. One is what the chart shows you today. The other is what you could actually have known at the time. Flip the switch and watch the line move.
What the strategy tester counts, and what a vendor screenshots.
Signals that existed at the moment they appeared, on closed data only.
Pure invention. Every one of these is profit in the report and nothing in the account.
The higher-timeframe bar has not closed yet, but on history the chart already knows how it ends — so the average is drawn in a position nobody could have seen. Two lines of Pine cause this, and two lines fix it: a [1] offset on the requested expression, together with lookahead = barmerge.lookahead_on. That is the whole difference between a report you can trade and a report you cannot. How to test your own indicator →
This runs two hundred and forty Monte Carlo paths in your browser, live. Move the sliders. A 65% win rate at 0.5R loses money; a 40% win rate at 3R does not. Position sizing only decides how violently you get there.
Expectancy below zero means no position size saves it. That is the whole argument.
A deliberately simple model: fixed-fractional sizing, one R per trade, no correlation between trades, no slippage or commission. It is here to show the shape of the maths, not to price your strategy.
The same failure points the analyser looks for, written the way they should be: broker-constraint handling, a repaint guard that holds, and a retry that cannot double your position. This is the standard every delivery is held to.
Risk-based position sizing, normalised to every broker constraint.
//--- Risk-based lot size, normalised to every broker constraint.
//--- The three lines most EAs skip are exactly why they die on a
//--- live account and never in the tester.
double LotForRisk(const double riskPercent, const double stopPoints)
{
if(stopPoints <= 0.0) return(0.0);
const double equity = AccountInfoDouble(ACCOUNT_EQUITY);
const double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
const double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickValue <= 0.0 || tickSize <= 0.0) return(0.0);
const double lossPerLot = (stopPoints * _Point / tickSize) * tickValue;
if(lossPerLot <= 0.0) return(0.0);
double lots = (equity * riskPercent / 100.0) / lossPerLot;
//--- step / min / max. Without these you get "invalid volume"
//--- at 02:00 and the robot silently stops trading.
const double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
const double lo = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
const double hi = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
lots = MathFloor(lots / step) * step;
lots = MathMax(lo, MathMin(hi, lots));
return(NormalizeDouble(lots, (int)MathRound(-MathLog10(step))));
}A higher-timeframe filter that cannot repaint.
//@version=6
indicator("HTF trend filter", overlay = true)
// A higher-timeframe value is only safe to act on once that bar has
// closed. The [1] offset reads the last closed HTF bar; lookahead_on
// makes history and real time switch to it at the same moment. Drop
// the [1] and history reads the future; drop lookahead_on and history
// runs one HTF bar behind what the live chart showed.
htf = input.timeframe("240", "Higher timeframe")
length = input.int(50, "EMA length", minval = 2)
htfEma = request.security(syminfo.tickerid, htf,
ta.ema(close, length)[1],
lookahead = barmerge.lookahead_on)
trendUp = not na(htfEma) and close > htfEma
plot(htfEma, "HTF EMA", linewidth = 2,
color = trendUp ? color.new(#FFAA2B, 0) : color.new(#E8604C, 0))
alertcondition(ta.crossover(close, htfEma), "Cross up",
"Close crossed above the higher-timeframe EMA")Order dispatch that is safe to retry after a timeout.
/**
* Order dispatch that is safe to retry.
*
* A network timeout does not tell you whether the broker filled the
* order. Retrying blindly is how one intended position quietly
* becomes three. The client-side key makes the broker reject the
* duplicate instead of accepting it.
*/
export async function dispatch(order: Order, deps: Deps): Promise<Ack> {
const key = idempotencyKey(order); // stable across retries
const known = await deps.store.find(key);
if (known) return known; // already acknowledged
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
try {
const ack = await deps.broker.send({ ...order, clientOrderId: key });
await deps.store.record(key, ack);
return ack;
} catch (err) {
if (!isTransient(err)) throw err;
await sleep(backoff(attempt)); // 250ms, 500ms, 1s, 2s
}
}
throw new DispatchTimeout(key, MAX_ATTEMPTS);
}Every engagement is quoted as a fixed price against a written specification. If the spec does not change, the price does not change.
| Service | From | Delivery | Platforms | Get a quote for this |
|---|---|---|---|---|
| Expert Advisors for MetaTrader | 450 USD | 7–21 days | MetaTrader 4MetaTrader 5 | Detail → |
| TradingView indicators & screeners | 180 USD | 3–10 days | TradingViewPine v6 | Detail → |
| Strategy to code | 300 USD | 5–14 days | MetaTrader 4MetaTrader 5TradingView | Detail → |
| Web applications & client portals | 1200 USD | 2–6 weeks | Next.jsTypeScriptPostgreSQL | Detail → |
| Audit & repair | 120 USD | 2–5 days | MQL4MQL5PineTypeScript | Detail → |
Indicative starting prices. The quote you receive is fixed and itemised against the written specification.
All services in full →You know what you are paying for before we start typing. The order matters: every step exists to make the next one cheaper.
In plain language, a screenshot, or a voice note. You do not need to know what a pointer is. If the rules contradict each other, we tell you before we go further — that conversation is free.
Every rule, every edge case, every thing the system will deliberately not do. You approve it. This document is what the fixed price is attached to, and it is what protects both of us.
Built against the spec, then tested on real data — tick data for trading systems, production-shaped data for everything else. You see the test output, not a claim about it.
Source code, build instructions, parameter documentation, and a walkthrough call. Thirty days of fixes for anything that does not match the specification.
These are not marketing lines. Each one goes into the engagement agreement exactly as written here, and you get that agreement before you pay anything.
Quoted against the approved specification. If the spec does not change, the invoice does not change.
You receive the source code and the right to modify it. No licence server, no time bomb, no dependency on us.
Part on specification approval, the rest on delivery. You never pay in full for something you have not seen run.
Anything that does not match the specification is corrected at no cost for thirty days after handover.
NDA signed before you describe the strategy. Your rules are never reused, resold, or shown as an example.
If what you want cannot work, you hear it in the first reply — before an invoice exists, not after.
AlgoBot Pro is a small software team. The person who reads your message is the person who writes the code, and he trades himself — so the first questions you get are a trader’s questions: which broker, which session, what should happen when the connection drops. You do not have to take our word on quality: the analyser is open source, the code checker runs in your own browser, and every number on the Work page comes from a measurement.
Five questions is usually enough for a fixed quote. You do not need a specification, a diagram, or the right vocabulary — that part is our job.