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

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

- `OnTesterInit`, `OnTesterPass`, `OnTesterDeinit`: Forge runs the sweep and keeps every pass. A strategy has no hook around the sweep in FORGE_API 1.
- `OnChartEvent`: A strategy has no chart events. The chart draws a run's plots and markers.
- `OnBookEvent`, `MarketBookAdd`, `MarketBookGet`: Forge runs have no order book depth.
- `CTrade.PositionCloseBy`, `PositionCloseBy`, `OrderCloseBy`: Forge closes each position at the market. Closing one position by an opposite one is not simulated.
- `CTrade.SetTypeFilling`, `ORDER_FILLING_FOK`, `ORDER_FILLING_IOC`: How orders fill is set for the run, not in the strategy.
- `CopyTicks`, `CopyTicksRange`: Strategies decide on closed bars. Ticks drive fills, not decisions.
- `IndicatorRelease`: Forge frees indicator arrays itself.
- `TesterWithdrawal`, `TesterDeposit`: A run's balance moves only by trades and their costs.
- `Every tick based on real ticks`, `1 minute OHLC`, `Open prices only`: How finely a run checks fills between bars is a run setting, not part of the strategy.
- `Alert`, `SendNotification`, `SendMail`, `PlaySound`, `WebRequest`: A backtest sends nothing. Log it with ctx.log.
- `DatabaseOpen`, `DatabaseExecute`, `the other SQLite functions`: A strategy has no database. A lookup table can ship as a declared asset.
- `OnnxCreate`, `OnnxRun`: Model files cannot be assets in FORGE_API 1.
- `CAppDialog`, `CCanvas`, `on-chart controls`: A strategy has no on-chart controls.

## Name by name

### The program's events

- `OnInit`: setup(self, ctx). Refuse bad settings with raise fg.InvalidParams(...).
- `OnDeinit`: on_end(self, ctx, reason)
- `OnTick`: compute(self, d) for signals over whole arrays; on_bar(self, ctx) for logic per bar
- `OnTimer`, `EventSetTimer`: @fg.every(...) on a method
- `OnTrade`: on_order_event(self, ctx, event)
- `OnTradeTransaction`: on_order_accepted, on_fill, on_order_cancelled, on_order_rejected, on_order_expired, then on_position_opened, on_position_changed, on_position_closed
- `#include`, `#import`: A 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

- `input`: param.Int, param.Float, param.Bool, param.Choice, param.Enum, param.Timeframe, param.Symbol, param.Session, param.Time, param.Price on the class
- `sinput`: param.Int(..., optimize=False) A setting a sweep never changes.
- `input group`: group="..." on each setting
- `.set files`: Load MT5 settings on the Tester's strategy card, or program.read_mql5_set(path) in a notebook
- `ENUM_MA_METHOD`, `ENUM_TIMEFRAMES`, `ENUM_APPLIED_PRICE`: param.MaMethod, param.Timeframe, param.Source

### Orders

- `CTrade.Buy`, `CTrade.Sell`, `OrderSend`, `OrderSendAsync`, `CTrade.PositionOpen`, `PositionOpen`, `MqlTradeRequest`: long_entry= and short_entry= in fg.Signals, or ctx.buy(size, sl=, tp=, tag=) and ctx.sell(...) in a hook
- `CTrade.BuyLimit`, `CTrade.BuyStop`, `CTrade.SellLimit`, `CTrade.SellStop`, `CTrade.OrderOpen`: ctx.order(fg.Order.limit(...)) or ctx.order(fg.Order.stop(...))
- `CTrade.PositionClose`, `PositionClose`, `OrderClose`: pos.close(), or ctx.close(tag=...)
- `CTrade.PositionClosePartial`: pos.close(portion=0.5)
- `CTrade.PositionModify`, `PositionModify`: pos.set_stop(price) and pos.set_target(price), or declared fg.Stop and fg.Target
- `CTrade.OrderModify`, `OrderModify`: ticket.modify(...) for a pending order; pos.set_stop and pos.set_target for an open trade MQL4's OrderModify changes both, so check which one the EA meant.
- `CTrade.OrderDelete`, `OrderDelete`: ticket.cancel(), or ctx.cancel_all(tag=...)
- `CTrade.SetExpertMagicNumber`, `magic numbers`: tag="..." on entries and orders
- `CTrade.SetDeviationInPoints`: The run's slippage setting
- `OrderCheck`, `OrderCalcMargin`, `OrderCalcProfit`: ctx.check(order), ctx.calc.margin(order), ctx.calc.profit(...)
- `MqlTradeResult.retcode`, `TRADE_RETCODE_*`: ticket.status and ticket.reject_reason, one of forge.types.RejectReason
- `ORDER_TIME_GTC`, `ORDER_TIME_DAY`, `ORDER_TIME_SPECIFIED`: tif="gtc", tif="day" or expire_at= on fg.Order.limit and fg.Order.stop
- `stops and targets in points`: fg.Stop.pips(...), fg.Target.pips(...)

### Positions

- `PositionsTotal`: len(ctx.positions())
- `PositionSelect`, `PositionSelectByTicket`, `PositionGetTicket`: ctx.positions(symbol=, tag=), each with pos.id
- `PositionGetDouble`: pos.avg_price, pos.stop, pos.target, pos.unrealized, pos.swap, pos.size POSITION_PRICE_OPEN, POSITION_SL, POSITION_TP, POSITION_PROFIT, POSITION_SWAP and POSITION_VOLUME.
- `PositionGetInteger`: pos.side, pos.open_time, pos.tag POSITION_TYPE, POSITION_TIME and POSITION_MAGIC.
- `PositionGetString`: pos.symbol, pos.entry_reason POSITION_SYMBOL and POSITION_COMMENT.
- `CPositionInfo`, `COrderInfo`, `CDealInfo`, `CAccountInfo`, `CSymbolInfo`: The objects above: pos, ticket, ctx.fills, ctx.account, ctx.symbol

### Pending orders

- `OrdersTotal`, `OrderSelect`, `OrderGetTicket`, `OrderGetDouble`, `OrderGetInteger`: ctx.orders(), each a ticket with price, stop_price, status and tag

### History

- `HistorySelect`, `HistoryDealsTotal`, `HistoryDealGetTicket`, `HistoryDealGetDouble`, `HistoryOrderGetInteger`: ctx.fills and ctx.trades: every fill and closed trade of the run so far

### The account

- `AccountInfoDouble`: ctx.account.balance, equity, margin_used, free_margin, margin_level ACCOUNT_BALANCE, ACCOUNT_EQUITY, ACCOUNT_MARGIN, ACCOUNT_MARGIN_FREE and ACCOUNT_MARGIN_LEVEL.
- `AccountInfoInteger`, `AccountInfoString`: ctx.account.leverage, ctx.account.mode, ctx.account.currency ACCOUNT_LEVERAGE, ACCOUNT_MARGIN_MODE and ACCOUNT_CURRENCY.

### The symbol

- `SymbolInfoDouble`, `_Point`, `_Digits`: ctx.symbol.point, tick_value, tick_size, lot_min, lot_step, lot_max, digits; d.spec in compute()
- `SymbolInfoTick`, `SYMBOL_BID`, `SYMBOL_ASK`: ctx.quote.bid and ctx.quote.ask in a hook; d.bid and d.ask series in compute()
- `SymbolInfoInteger`: ctx.quote.spread, ctx.symbol.stops_level SYMBOL_SPREAD and SYMBOL_TRADE_STOPS_LEVEL.
- `SymbolSelect`, `SymbolsTotal`, `SymbolName`: symbols = \["EURUSD", "GBPUSD"] on the class
- `SymbolInfoSessionTrade`, `SymbolInfoSessionQuote`: ctx.calendar.is_open() and fg.Filter.session(...)

### Time

- `TimeCurrent`, `TimeTradeServer`: ctx.broker_time In an EA under the tester this is simulated server time, and so is ctx.broker_time.
- `TimeGMT`: ctx.time (UTC)
- `TimeLocal`: ctx.local(zone) for a named zone A backtest has no local computer clock.
- `TimeToStruct`, `MqlDateTime`: Python datetime from ctx.broker_time or d.time_close

### Data

- `CopyRates`, `CopyClose`, `CopyOpen`, `CopyHigh`, `CopyLow`, `CopyTime`, `CopyTickVolume`, `CopySpread`, `iClose`, `iOpen`, `iHigh`, `iLow`, `iTime`, `iVolume`: d.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 hook
- `ArraySetAsSeries`: Nothing: Forge arrays always run oldest first
- `another symbol or timeframe in CopyClose`: d\["GBPUSD"].tf("H4").close Every other series is written in the file, so Forge loads it before the run.
- `iBars`, `Bars`, `iBarShift`: len(d.close), ctx.bar, numpy.searchsorted(d.time_close, t)
- `iHighest`, `iLowest`: fg.ta.highest and fg.ta.lowest over d.high and d.low
- `EMPTY_VALUE`: NaN

### Indicators

- `iMA and CopyBuffer`: fg.ta.ma(d.close, period, method=...) and the other fg.ta functions, called once in compute() Each MT5 built-in maps to one fg.ta function: the indicator table lists them with their outputs.
- `iCustom`: Put the calculation in a library file and call it in compute()

### The tester

- `OnTester`: score(self, stats) -> float It ranks a sweep. Every combination that ran still counts as a trial.
- `TesterStatistics`: stats\["net_profit"], stats.trade_count ... inside score(); ctx.stats while the run goes STAT_PROFIT, STAT_TRADES, STAT_PROFIT_FACTOR and the rest.
- `TesterStop`, `ExpertRemove`: ctx.stop_run(reason) The run keeps its results and still counts as a trial.
- `MQLInfoInteger(MQL_TESTER)`, `MQL_OPTIMIZATION`: ctx.mode Reading the mode never changes behaviour. A strategy that trades differently by mode is flagged on its result.
- `Modelling quality`, `History quality`: The run's price and fill facts on its run page MetaTrader'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.

### Randomness

- `MathRand`, `MathSrand`, `rand`, `srand`: ctx.rng, a NumPy generator the run seeds Never seed it by hand: the run's seed makes every run repeatable.

### Side effects

- `Print`, `PrintFormat`, `Comment`: ctx.log.info(...)
- `GlobalVariableSet`, `GlobalVariableGet`: An attribute set in setup(self, ctx) State lives on the strategy for one run.

### Chart and screen

- `ObjectCreate`, `ObjectSetInteger`, `ChartRedraw`: plots= in fg.Signals, or ctx.plot and ctx.mark in a hook

### Files and storage

- `FileOpen`, `FileRead*`, `FileWrite*`, `the other file functions`: self.read_asset(path) for a file listed in assets A strategy reads only the files it declares, and writes none.

### Maths and strings

- `MathAbs`, `MathMax`, `MathPow`, `ArraySort`, `ArrayMaximum`, `StringFind`, `StringSubstr`, `DoubleToString`, `TimeToString`, `the other maths, array, conversion and string built-ins`: Python 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 collections`: Python's list, dict, set, collections.deque, bisect, heapq and str
- `matrix`, `vector`, `their methods`: numpy arrays and numpy.linalg
