AUREUS / BUILD 212 PUBLIC IMPLEMENTATION EVIDENCE SOURCES - infrastructure/execution/mt5_executor.py SHA-256 4aa9ff9435bb3fc4d02e9aca3765173f2398544646c1dc1eaef0e53246691c98 - infrastructure/execution/broker_execution_gate.py SHA-256 d3829131406716d3a8ec0833a9c9af058cfcd49255be4fee2538fb061060e731 - infrastructure/mt5/gateway.py SHA-256 bb5f501a3ab3bc979f5ca6d217a6eeea0da126e64d8c93d323777a5fc59cd0cc PUBLICATION: selected execution and broker-boundary excerpts BOUNDARY: strategy logic, account identifiers, credentials and private runtime paths are not published. WHAT THIS PROVES - Broker permission checks are separated from the executor. - Account-bound multi-account mode re-checks the active MT5 login/server immediately before mutation. - mt5.order_send() is implemented and a fill requires TRADE_RETCODE_DONE. - The MT5 gateway reads account, open-position and deal-history state. - The captured runtime window published separately remained fail-closed; this source excerpt is not presented as proof of a completed fill. MT5 EXECUTOR EXCERPT 0021: class MT5Executor(Executor): 0022: """ 0023: Executes approved trades through MetaTrader 5. 0024: 0025: This class owns only the final order_send responsibility. 0026: 0027: Broker permission checks are delegated to BrokerExecutionGate so execution 0028: safety can evolve independently from the executor. 0029: 0030: In account-bound multi-account mode, this executor performs a final 0031: login/server binding check immediately before order_send. This prevents a 0032: trade from being sent if the process is no longer attached to the expected 0033: MT5 account after the broker execution gate approved the request. 0076: if decision.request is None: 0077: return self._rejected( 0078: candidate=candidate, 0079: reason="Broker execution gate approved without an order request.", 0080: ) 0081: 0082: binding_rejection_reason = self._order_send_binding_rejection_reason() 0083: 0084: if binding_rejection_reason is not None: 0085: return self._rejected( 0086: candidate=candidate, 0087: reason=binding_rejection_reason, 0088: ) 0089: 0090: result = mt5.order_send(decision.request) 0091: 0092: if result is None: 0093: return self._rejected( 0094: candidate=candidate, 0095: reason=f"MT5 order_send failed: {mt5.last_error()}", 0096: ) 0097: 0098: if result.retcode != mt5.TRADE_RETCODE_DONE: 0099: return self._rejected( 0100: candidate=candidate, 0101: reason=( 0102: f"MT5 order rejected | " 0103: f"retcode={result.retcode} | " 0104: f"comment={result.comment}" 0105: ), 0106: ) 0107: 0108: return ExecutionReport( 0109: status="FILLED", 0110: symbol=candidate.symbol, 0111: direction=candidate.direction, 0112: order_type=candidate.order_type, 0113: entry_price=float(result.price), 0114: stop_loss=float(candidate.stop_loss), 0115: take_profit=float(candidate.take_profit), 0116: volume=self.volume, 0117: ticket=str(result.order), 0118: executed_at=datetime.utcnow(), 0119: reason="MT5 demo execution successful.", 0120: ) BROKER EXECUTION GATE EXCERPT 0316: ) -> BrokerExecutionDecision: 0317: """ 0318: Conservative XAUUSD market availability gate. 0319: 0320: This intentionally blocks known unsafe windows before order_send: 0321: - Saturday closure 0322: - conservative Sunday pre-open window 0323: - conservative Friday close window 0324: - daily metals rollover / broker maintenance break 0325: 0326: Later, this should move to broker-specific schedule config. 0327: """ 0646: order_type = None 0647: price = float(candidate.entry) 0648: 0649: return { 0650: "action": mt5.TRADE_ACTION_DEAL, 0651: "symbol": candidate.symbol, 0652: "volume": volume, 0653: "type": order_type, 0654: "price": price, 0655: "sl": float(candidate.stop_loss), 0656: "tp": float(candidate.take_profit), 0657: "deviation": deviation, 0658: "magic": magic_number, 0659: "comment": "AUREUS", 0660: "type_time": mt5.ORDER_TIME_GTC, 0661: "type_filling": mt5.ORDER_FILLING_IOC, MT5 GATEWAY EXCERPT 0022: """ 0023: Read-side MT5 gateway. 0024: 0025: In legacy single-account mode, this gateway keeps the previous behavior and 0026: calls mt5.initialize() without an explicit path. 0027: 0028: In account-bound multi-account mode, it initializes MT5 using the terminal 0029: path supplied by AccountRuntimeSettings. This ensures each isolated Python 0030: process connects to its assigned MT5 terminal/account instead of relying on 0031: MetaTrader5's global/default terminal resolution. 0065: def get_account_snapshot(self) -> AccountSnapshot | None: 0066: if not self.connect(): 0067: return None 0068: 0069: account = mt5.account_info() 0070: 0071: if account is None: 0072: print("[MT5 Gateway] Account info unavailable") 0073: return None 0074: 0075: return AccountSnapshot( 0076: login=int(account.login), 0077: server=str(account.server), 0078: company=str(account.company), 0079: name=str(account.name), 0177: numeric_position_id = int(position_id) 0178: except (TypeError, ValueError): 0179: return [] 0180: 0181: deals = self._history_deals_get_by_position(numeric_position_id) 0182: 0183: if len(deals) == 0: 0184: deals = self._history_deals_get_recent( 0185: lookback_days=lookback_days, 0186: ) 0187: deals = [ 0188: deal 0189: for deal in deals 0190: if deal.position_id == str(numeric_position_id)