Convert an MQL5 EA to Python by hand: MQL5 to Forge

Forge API 1Updated 14 Oct 2026

There is no automatic converter, and Oathra does not convert MQL4 or MQL5 code. This page sets what you write in MQL5 beside what you write in Forge's Python, job by job, and says what has no Forge form.

What you write in MQL5, and what you write in Forge instead. 65 names, grouped by what they are for.

What has no Forge form

You writeWhy
OnTesterInit, OnTesterPass, OnTesterDeinitForge runs the sweep and keeps every pass. A strategy has no hook around the sweep in FORGE_API 1.
OnChartEventA strategy has no chart events. The chart draws a run's plots and markers.
OnBookEvent, MarketBookAdd, MarketBookGetForge runs have no order book depth.
CTrade.PositionCloseBy, PositionCloseBy, OrderCloseByForge closes each position at the market. Closing one position by an opposite one is not simulated.
CTrade.SetTypeFilling, ORDER_FILLING_FOK, ORDER_FILLING_IOCHow orders fill is set for the run, not in the strategy.
CopyTicks, CopyTicksRangeStrategies decide on closed bars. Ticks drive fills, not decisions.
IndicatorReleaseForge frees indicator arrays itself.
TesterWithdrawal, TesterDepositA run's balance moves only by trades and their costs.
Every tick based on real ticks, 1 minute OHLC, Open prices onlyHow finely a run checks fills between bars is a run setting, not part of the strategy.
Alert, SendNotification, SendMail, PlaySound, WebRequestA backtest sends nothing. Log it with ctx.log.
DatabaseOpen, DatabaseExecute, the other SQLite functionsA strategy has no database. A lookup table can ship as a declared asset.
OnnxCreate, OnnxRunModel files cannot be assets in FORGE_API 1.
CAppDialog, CCanvas, on-chart controlsA strategy has no on-chart controls.

Name by name

The program's events

You writeIn ForgeNoteTrap
OnInitsetup(self, ctx). Refuse bad settings with raise fg.InvalidParams(...).fg.InvalidParams
OnDeiniton_end(self, ctx, reason)
OnTickcompute(self, d) for signals over whole arrays; on_bar(self, ctx) for logic per barTrap
OnTimer, EventSetTimer@fg.every(...) on a methodfg.every
OnTradeon_order_event(self, ctx, event)
OnTradeTransactionon_order_accepted, on_fill, on_order_cancelled, on_order_rejected, on_order_expired, then on_position_opened, on_position_changed, on_position_closed
#include, #importA Python import of your own library file, which lists its names in __all__A #import of a DLL has no equivalent: Forge runs Python only.

Settings

You writeIn ForgeNote
inputparam.Int, param.Float, param.Bool, param.Choice, param.Enum, param.Timeframe, param.Symbol, param.Session, param.Time, param.Price on the classfg.param.Intfg.param.Floatfg.param.Boolfg.param.Choice
sinputparam.Int(..., optimize=False)fg.param.IntA setting a sweep never changes.
input groupgroup="..." on each settingfg.param.Int
.set filesLoad MT5 settings on the Tester's strategy card, or program.read_mql5_set(path) in a notebook
ENUM_MA_METHOD, ENUM_TIMEFRAMES, ENUM_APPLIED_PRICEparam.MaMethod, param.Timeframe, param.Sourcefg.param.MaMethodfg.param.Timeframefg.param.Source

Orders

You writeIn ForgeNoteTrap
CTrade.Buy, CTrade.Sell, OrderSend, OrderSendAsync, CTrade.PositionOpen, PositionOpen, MqlTradeRequestlong_entry= and short_entry= in fg.Signals, or ctx.buy(size, sl=, tp=, tag=) and ctx.sell(...) in a hookfg.Signals
CTrade.BuyLimit, CTrade.BuyStop, CTrade.SellLimit, CTrade.SellStop, CTrade.OrderOpenctx.order(fg.Order.limit(...)) or ctx.order(fg.Order.stop(...))fg.Order
CTrade.PositionClose, PositionClose, OrderClosepos.close(), or ctx.close(tag=...)
CTrade.PositionClosePartialpos.close(portion=0.5)
CTrade.PositionModify, PositionModifypos.set_stop(price) and pos.set_target(price), or declared fg.Stop and fg.Targetfg.Stopfg.Target
CTrade.OrderModify, OrderModifyticket.modify(...) for a pending order; pos.set_stop and pos.set_target for an open tradeMQL4's OrderModify changes both, so check which one the EA meant.
CTrade.OrderDelete, OrderDeleteticket.cancel(), or ctx.cancel_all(tag=...)
CTrade.SetExpertMagicNumber, magic numberstag="..." on entries and ordersfg.SignalsTrap
CTrade.SetDeviationInPointsThe run's slippage setting
OrderCheck, OrderCalcMargin, OrderCalcProfitctx.check(order), ctx.calc.margin(order), ctx.calc.profit(...)
MqlTradeResult.retcode, TRADE_RETCODE_*ticket.status and ticket.reject_reason, one of forge.types.RejectReasonfg.types.RejectReason
ORDER_TIME_GTC, ORDER_TIME_DAY, ORDER_TIME_SPECIFIEDtif="gtc", tif="day" or expire_at= on fg.Order.limit and fg.Order.stopfg.Order
stops and targets in pointsfg.Stop.pips(...), fg.Target.pips(...)fg.Stopfg.TargetTrap

Positions

You writeIn ForgeNote
PositionsTotallen(ctx.positions())
PositionSelect, PositionSelectByTicket, PositionGetTicketctx.positions(symbol=, tag=), each with pos.id
PositionGetDoublepos.avg_price, pos.stop, pos.target, pos.unrealized, pos.swap, pos.sizePOSITION_PRICE_OPEN, POSITION_SL, POSITION_TP, POSITION_PROFIT, POSITION_SWAP and POSITION_VOLUME.
PositionGetIntegerpos.side, pos.open_time, pos.tagPOSITION_TYPE, POSITION_TIME and POSITION_MAGIC.
PositionGetStringpos.symbol, pos.entry_reasonPOSITION_SYMBOL and POSITION_COMMENT.
CPositionInfo, COrderInfo, CDealInfo, CAccountInfo, CSymbolInfoThe objects above: pos, ticket, ctx.fills, ctx.account, ctx.symbol

Pending orders

You writeIn Forge
OrdersTotal, OrderSelect, OrderGetTicket, OrderGetDouble, OrderGetIntegerctx.orders(), each a ticket with price, stop_price, status and tag

History

You writeIn Forge
HistorySelect, HistoryDealsTotal, HistoryDealGetTicket, HistoryDealGetDouble, HistoryOrderGetIntegerctx.fills and ctx.trades: every fill and closed trade of the run so far

The account

You writeIn ForgeNoteTrap
AccountInfoDoublectx.account.balance, equity, margin_used, free_margin, margin_levelACCOUNT_BALANCE, ACCOUNT_EQUITY, ACCOUNT_MARGIN, ACCOUNT_MARGIN_FREE and ACCOUNT_MARGIN_LEVEL.
AccountInfoInteger, AccountInfoStringctx.account.leverage, ctx.account.mode, ctx.account.currencyACCOUNT_LEVERAGE, ACCOUNT_MARGIN_MODE and ACCOUNT_CURRENCY.Trap

The symbol

You writeIn ForgeNoteTrap
SymbolInfoDouble, _Point, _Digitsctx.symbol.point, tick_value, tick_size, lot_min, lot_step, lot_max, digits; d.spec in compute()Trap
SymbolInfoTick, SYMBOL_BID, SYMBOL_ASKctx.quote.bid and ctx.quote.ask in a hook; d.bid and d.ask series in compute()
SymbolInfoIntegerctx.quote.spread, ctx.symbol.stops_levelSYMBOL_SPREAD and SYMBOL_TRADE_STOPS_LEVEL.
SymbolSelect, SymbolsTotal, SymbolNamesymbols = ["EURUSD", "GBPUSD"] on the class
SymbolInfoSessionTrade, SymbolInfoSessionQuotectx.calendar.is_open() and fg.Filter.session(...)fg.Filter

Time

You writeIn ForgeNoteTrap
TimeCurrent, TimeTradeServerctx.broker_timeIn an EA under the tester this is simulated server time, and so is ctx.broker_time.Trap
TimeGMTctx.time (UTC)
TimeLocalctx.local(zone) for a named zoneA backtest has no local computer clock.
TimeToStruct, MqlDateTimePython datetime from ctx.broker_time or d.time_close

Data

You writeIn ForgeNoteTrap
CopyRates, CopyClose, CopyOpen, CopyHigh, CopyLow, CopyTime, CopyTickVolume, CopySpread, iClose, iOpen, iHigh, iLow, iTime, iVolumed.close, d.open, d.high, d.low, d.time_close, d.volume, d.spread in compute(); ctx.bars.close.ago(n) or ctx.history("close", n) in a hookTrap
ArraySetAsSeriesNothing: Forge arrays always run oldest firstTrap
another symbol or timeframe in CopyClosed["GBPUSD"].tf("H4").closeEvery other series is written in the file, so Forge loads it before the run.
iBars, Bars, iBarShiftlen(d.close), ctx.bar, numpy.searchsorted(d.time_close, t)
iHighest, iLowestfg.ta.highest and fg.ta.lowest over d.high and d.lowfg.ta.highestfg.ta.lowest
EMPTY_VALUENaNTrap

Indicators

You writeIn ForgeNote
iMA and CopyBufferfg.ta.ma(d.close, period, method=...) and the other fg.ta functions, called once in compute()fg.ta.maEach MT5 built-in maps to one fg.ta function: the indicator table lists them with their outputs.
iCustomPut the calculation in a library file and call it in compute()

The tester

You writeIn ForgeNoteTrap
OnTesterscore(self, stats) -> floatIt ranks a sweep. Every combination that ran still counts as a trial.
TesterStatisticsstats["net_profit"], stats.trade_count ... inside score(); ctx.stats while the run goesSTAT_PROFIT, STAT_TRADES, STAT_PROFIT_FACTOR and the rest.
TesterStop, ExpertRemovectx.stop_run(reason)The run keeps its results and still counts as a trial.
MQLInfoInteger(MQL_TESTER), MQL_OPTIMIZATIONctx.modeReading the mode never changes behaviour. A strategy that trades differently by mode is flagged on its result.
Modelling quality, History qualityThe run's price and fill facts on its run pageMetaTrader's figures count how complete the price history is, not whether fills are right. Forge's data quality score grades a dataset's files and is not a modelling quality either.Trap

Randomness

You writeIn ForgeNote
MathRand, MathSrand, rand, srandctx.rng, a NumPy generator the run seedsNever seed it by hand: the run's seed makes every run repeatable.

Side effects

You writeIn ForgeNote
Print, PrintFormat, Commentctx.log.info(...)
GlobalVariableSet, GlobalVariableGetAn attribute set in setup(self, ctx)State lives on the strategy for one run.

Chart and screen

You writeIn Forge
ObjectCreate, ObjectSetInteger, ChartRedrawplots= in fg.Signals, or ctx.plot and ctx.mark in a hookfg.Plot

Files and storage

You writeIn ForgeNote
FileOpen, FileRead*, FileWrite*, the other file functionsself.read_asset(path) for a file listed in assetsA strategy reads only the files it declares, and writes none.

Maths and strings

You writeIn Forge
MathAbs, MathMax, MathPow, ArraySort, ArrayMaximum, StringFind, StringSubstr, DoubleToString, TimeToString, the other maths, array, conversion and string built-insPython and numpy: abs, math, numpy reductions, slicing, sorted, bisect, f-strings, str methods and datetime
CArrayObj, CArrayDouble, CList, CTree, CHashMap, CString, the other Standard Library collectionsPython's list, dict, set, collections.deque, bisect, heapq and str
matrix, vector, their methodsnumpy arrays and numpy.linalg