Build a Remote Forex Risk Management System in Python
Introduction
In a world where one wrong move in the foreign exchange market can reset your account faster than you say "stop loss", the art of risk management is becoming not just a useful skill, but a real lifeline for a trader. Do you remember that fascinating story about a trader who lost all his savings due to a sudden surge in the exchange rate of Swiss franc in 2015? Or the one who fell asleep with an open position and woke up with a margin call?
Today I present you a real shield for your trading capital — a remote Python risk management system that does not sleep, does not get distracted and never gives in to emotions. It's like having a personal bodyguard for your trading account who watches every move around the clock and is ready to step in instantly at the first sign of danger.
Imagine: you go about your business, and your trusty digital assistant monitors drawdowns, monitors daily and weekly loss limits, and can even automatically close all positions if the market goes against you. No nervous breakdowns, sleepless nights, or missed opportunities for fear of losing control.
Our remote risk manager is not just a tool, it is your insurance against financial chaos in the unpredictable world of Forex trading. Are you ready to turn your trading from a risky gamble into a controlled process? Then buckle up — we're going on a journey through the world of smart risk management, where technology meets financial security.
Why can't a trader survive without risk management today?
In modern trading, risk management has ceased to be an option - it has become a prerequisite for survival in financial markets. Let's find out why.
The era of extreme volatility
In recent years, financial markets have been subjected to unprecedented shocks: a pandemic, geopolitical conflicts, drastic changes in the monetary policy of central banks, and global economic turmoil. Each of these events created conditions of extreme volatility, when one wrong move could zero a trading account in a matter of minutes.
Just think back to January 2015, when the Swiss National Bank abruptly lifted the franc's peg to the Euro. In a few minutes, there was an unprecedented 30% surge in the exchange rate, ruining thousands of traders and even some brokerage companies. Who survived that day? Those who had a reliable risk management system.
The psychological factor ruins the majority
It has long been proven that trader’s main enemy is not the market, but his own psychology. Fear, greed, hope, revenge on the market — these emotions destroy even the most sophisticated trading strategies. An automated risk management system protects you not only from the market, but also from yourself.
When the market moves against you, the psychological pressure can be unbearable. Many traders are tempted to "average" a losing position or remove a stop loss in the hope of a reversal. Such decisions, made at a time of emotional stress, usually lead to disastrous consequences.
Technological progress is a double—edged sword
Modern trading platforms provide unprecedented access to markets. With one click, you can open a position that will control tens or even hundreds of thousands of dollars. This power requires appropriate responsibility.
Technical failures, Internet connection errors, incorrect clicks, typos when entering volume — in the high-speed world of algorithmic trading, even a small mistake can lead to huge losses. The automatic risk management system acts as a safety net protecting against such disasters.
Institutional players have an advantage
Banks, hedge funds, and other institutional market players invest millions in risk management systems. They use sophisticated algorithms, hundreds of analysts, and powerful computers to minimize their risks and maximize profits.
A retail trader who trades without a risk management system is like an amateur who goes out to play against a professional team. Your only protection is strict discipline and automated risk control systems.
Automation as a necessity
In a world where markets operate around the clock and decisions are made in milliseconds, human attention is not enough. You can't keep track of all your positions all the time, especially when you're sleeping or busy with other things.
An automated risk management system, similar to the one presented in this article, becomes your vigilant guardian, who monitors compliance with your trading rules 24/7, never gets tired, does not get distracted and does not give in to emotions.
In modern high-frequency trading, this is not a luxury, but a necessary condition for survival.
The mathematics of risk management: Why conservative trading wins in the long run
There is a fundamental asymmetry in trading that is often underestimated by beginners and ignored by emotional traders. This asymmetry is mathematical in nature and explains why a conservative approach to risk management almost always outperforms aggressive strategies in the long run. That is why we have created an automated risk management system in Python, presented in this article.
The ruthless mathematics of losses and recovery
Consider a simple mathematical model. Let's say you have a trading account of $10,000. Here's what happens in different loss scenarios:
| Loss (%) | Account balance | Required profit for recovery (%) |
|---|---|---|
| 10% | $9,000 | 11.1% |
| 20% | $8,000 | 25% |
| 30% | $7,000 | 42.9% |
| 40% | $6,000 | 66.7% |
| 50% | $5,000 | 100% |
| 60% | $4,000 | 150% |
| 75% | $2,500 | 300% |
| 90% | $1,000 | 900% |
| 95% | $500 | 1900% |
Key observation: the required recovery percentage increases exponentially as losses increase. This is not a linear relationship, but a hyperbolic one, which is expressed by the formula:
Required recovery percentage = (100% / (100% loss percentage)) - 100%
This mathematical reality is ruthless and unforgiving. It works regardless of your experience, knowledge of the market or trading strategy. Our Python risk management service was created precisely with an understanding of this mathematical pattern.
How does our Python risk management system solve this problem?
The service we have developed automatically controls several key risk parameters. It monitors the maximum drawdown — our system monitors both the drawdown on balance and equity, and can automatically close positions when a set threshold is reached (for example, 10% of the maximum). The system also controls daily and weekly loss limits — you can set the maximum allowable loss for a day or week, both as a percentage and in absolute terms, and exceeding these limits automatically blocks trading. In addition, margin control is provided — the system constantly monitors the margin level and warns about approaching dangerous values.
In the system code, these parameters can be seen in the following snippets:
# Defining risk limits daily_loss_limit = self.risk_settings.get('daily_loss_limit', 5 if limit_mode == 'percent' else 100) weekly_loss_limit = self.risk_settings.get('weekly_loss_limit', 10 if limit_mode == 'percent' else 300) max_drawdown_limit = self.risk_settings.get('max_drawdown', 10 if limit_mode == 'percent' else 100) # Checking for exceeding limits daily_balance_limit_exceeded = abs(daily_balance_loss) > (account_info.balance * daily_balance_limit / 100 if limit_mode == 'percent' else daily_balance_limit) weekly_balance_limit_exceeded = abs(weekly_balance_loss) > (account_info.balance * weekly_balance_limit / 100 if limit_mode == 'percent' else weekly_balance_limit) # Control of exceeding the maximum drawdown if drawdown_mode == 'balance': max_drawdown_exceeded = balance_drawdown > max_drawdown_limit elif drawdown_mode == 'equity': max_drawdown_exceeded = equity_drawdown > max_drawdown_limit else: # both max_drawdown_exceeded = balance_drawdown > max_drawdown_limit or equity_drawdown > max_drawdown_limit
Expected return based on probability
Suppose, you have a trading system with a 60% success rate and a 1:1 profit-to-loss ratio. The mathematical expectation of such a system is positive, but what happens with different position sizes?
Calculate the expected capital growth after 100 transactions at different risk levels per transaction:
| Risk per trade (%) | Average capital growth (%) | Standard deviation (%) | Probability of a negative result |
|---|---|---|---|
| 1% | 20% | 10% | 2.3% |
| 2% | 44% | 21% | 1.8% |
| 5% | 152% | 65% | 1% |
| 10% | 463% | 225% | 2% |
| 20% | 2125% | 1250% | 4.5% |
At first glance, it may seem that more risk = more reward. However, this is an illusion, because the standard deviation (volatility of results) increases proportionally to the risk size. In addition, with risks above 20%, the probability of complete ruin increases significantly. It is also important to keep in mind that the psychological stress of large drawdowns often leads to disruption of the trading system. This is probably the first thing to think about in order to come to the conclusion that any trader will reduce the account to zero at high risk. In nine years on the market, I have not seen a single successful "deposit acceleration".
Automation saves you from emotional mistakes
One of the main advantages of our Python risk management system is the exclusion of the human factor. The presented code contains the function of automatically closing positions in case of violation of risk parameters:
def close_all_positions(self): positions = mt5.positions_get() if positions is None: self.error_signal.emit(f"Error in retrieving positions to close: {mt5.last_error()}") return if not positions: self.error_signal.emit(“No open positions to close") return for position in positions: try: symbol = position.symbol volume = position.volume position_id = position.ticket position_type = position.type tick = mt5.symbol_info_tick(symbol) if not tick: self.error_signal.emit(f”Failed to retrieve data on symbol {symbol}") continue close_price = tick.bid if position_type == mt5.ORDER_TYPE_BUY else tick.ask close_type = mt5.ORDER_TYPE_SELL if position_type == mt5.ORDER_TYPE_BUY else mt5.ORDER_TYPE_BUY close_request = { "action": mt5.TRADE_ACTION_DEAL, "symbol": symbol, "volume": volume, "type": close_type, "position": position_id, "price": close_price, "deviation": 100, "comment": "Close by Risk Manager", } result = mt5.order_send(close_request) if not result or result.retcode != mt5.TRADE_RETCODE_DONE: self.error_signal.emit(f"Position closing error {position_id}: {result.retcode if result else mt5.last_error()}") except Exception as e: self.error_signal.emit(f”Error at closing position: {str(e)}")
The system does not succumb to fear, greed or hope. It strictly follows the set rules, which is critically important in times of market turbulence.
Continuous monitoring to have restful sleep
Our Python-based solution works 24/7, even when you are asleep or busy with other things. Every 18 seconds, the system checks all risk parameters and takes the necessary measures to protect your capital:
while self.running: try: account_info = mt5.account_info() # ... risk analysis ... limits_exceeded = daily_balance_limit_exceeded or weekly_balance_limit_exceeded or daily_equity_limit_exceeded or weekly_equity_limit_exceeded or max_drawdown_exceeded with self.block_lock: if limits_exceeded and self.risk_settings.get('auto_close_positions', True): self.blocked_trading = True if positions: self.close_all_positions() self.error_signal.emit(f"All positions closed due to exceeding limits") except Exception as e: self.error_signal.emit(f"Error in monitoring cycle: {str(e)}") time.sleep(18) # Update every 18 s
Leveraging our risk management system
The Python service code presented in the article provides full protection for your trading account through monitoring daily and weekly losses, monitoring maximum drawdown, automatically closing positions when limits are exceeded, and visually displaying all risk parameters through a user-friendly GUI.
It is intuitively simple to use — you upload an account, risk limits for a day, a week, specify whether to control the risk by balance/equity or both, and start using it.

With our system, you can be sure that the mathematics of risk management is working for you, not against you. It will help you avoid catastrophic losses and ensure long-term survival in the highly competitive environment of financial markets.
Practical implementation of the risk management system
Introduction of a remote risk manager into your trading practice takes place in a few simple steps. First, install Python and necessary libraries on your computer or remote server. The main dependencies include PyQt5 for the GUI, MetaTrader 5 for communication with the trading platform, as well as pandas and numpy for data analysis.
After installing all the components, you can start the system and start setting up parameters for your trading account. The intuitive interface allows you to quickly enter the data for connecting to MetaTrader 5: server, login and password. The system supports monitoring of multiple accounts simultaneously, which is especially convenient for those who manage multiple strategies or client funds.
Special attention should be paid to setting risk management parameters. You can set daily and weekly loss limits, both as a percentage of the balance and in absolute values. For example, many professional traders use the rule "do not lose more than 2% per day and 5% per week." This approach ensures that even a series of unsuccessful trades will not lead to critical losses.
The system also allows you to set the maximum allowable drawdown. This is an extremely important parameter, as historical data show that getting out of a drawdown becomes exponentially more difficult as its depth increases. By setting, for example, a maximum drawdown of 10-15%, you protect yourself from a long and painful process of capital recovery.
The user-friendly graphical interface of the system provides visualization of key metrics in real time. You can monitor your account status, open positions, margin levels, and potential risks on informative charts and tables. This allows you to instantly assess the situation and make informed decisions.
One of the unique features of our system is the weekly reports feature. The system automatically generates detailed HTML reports with charts and tables illustrating the dynamics of your account, the effectiveness of risk management and key trading points. These reports become an invaluable tool for analyzing and improving your strategy.
The architecture of the system is based on the principle of multithreading, which guarantees a quick response to changing market conditions. The main thread monitors account and risks, while individual threads are responsible for updating the interface and saving data. This approach ensures reliable operation even in high market volatility.
The built-in auto-save function ensures that even in the event of a program failure or server reboot, all settings and the status of trade blocking will be saved. This prevents unintentional opening of positions after system restart.
By introducing this system into your trading practice, you get not merely a program, but a reliable digital assistant which never gets tired, does not give in to emotions and strictly follows the established rules. This is especially valuable in times of market stress, when human psychology is prone to making irrational decisions.
Well, now let's move on to architecture development.
Architecture of a remote risk manager
The remote risk manager we developed has a multi-tier architecture that ensures reliability, scalability, and usability. Let's look at the main components of the system and how they interact with each other.
The main system components:
- The AccountMonitor monitoring module is the core of our system, implemented as a separate thread that provides continuous monitoring of the trading account status through the MetaTrader 5 API.
- The RiskManagerGUI graphical interface is a convenient shell for interacting with the system, allowing you to configure risk management parameters and monitor the current status of accounts.
- The SQLite database is a repository of historical account status data, which is used to calculate maximum balance and equity values, as well as to generate reports.
- The reporting module is a component responsible for creating weekly HTML reports with charts and tables for analyzing trading results.
Working with MetaTrader 5 API
The central element of our system is integration with MetaTrader 5 via the official Python API. This integration allows us to receive data on the account status, open positions and trade history, as well as perform trading operations if necessary.
# Initialize MetaTrader 5 with explicit path indication if not mt5.initialize(path=self.terminal_path): self.error_signal.emit(f"Error initializing MetaTrader 5 on path {self.terminal_path}: {mt5.last_error()}") return authorized = mt5.login( login=int(self.account_info['login']), password=self.account_info['password'], server=self.account_info['server'] )
Please, note that we explicitly specify the path to MetaTrader 5 terminal during initialization. This is especially important when deploying the system to a remote server, where the file location may differ from the standard one.
After successful authorization, the system begins continuous monitoring of the account, requesting information every 18 seconds:
while self.running: try: account_info = mt5.account_info() if not account_info: self.error_signal.emit(“Failed to receive account information") time.sleep(18) continue positions = mt5.positions_get() # Further data analysis... except Exception as e: self.error_signal.emit(f"Error in monitoring cycle: {str(e)}") time.sleep(18) # Update every 18 s
Multithreaded architecture
To ensure responsiveness of the user interface even during intensive data processing, we use a multithreaded architecture. The monitoring module runs in a separate thread that communicates with the main GUI thread through the PyQt5 signal and slot system:
class AccountMonitor(QThread): update_signal = pyqtSignal(dict) error_signal = pyqtSignal(str) # ... def run(self): # Monitoring and analysis code # ... self.update_signal.emit(update_data)
This approach ensures that even with delays in receiving data from MetaTrader 5, user’s API interface remains responsive.
Risk analysis algorithms
The heart of our system is risk analysis algorithms that continuously evaluate the current account status and make decisions based on user-defined parameters.
One of the key algorithms is the calculation of balance drawdown and equity:
cursor.execute('SELECT MAX(balance) FROM account_history WHERE account_id = ?', (self.account_info['login'],)) result = cursor.fetchone() max_balance = result[0] if result and result[0] is not None else initial_balance balance_drawdown = 0 equity_drawdown = 0 drawdown_mode = self.risk_settings.get('drawdown_mode', 'balance') limit_mode = self.risk_settings.get('limit_mode', 'percent') if max_balance > 0: if drawdown_mode in ['balance', 'both']: balance_drawdown = (max_balance - account_info.balance) / max_balance * 100 if limit_mode == 'percent' else max_balance - account_info.balance
The system stores information about the maximum achieved balance and equity values, which allows you to accurately calculate the current drawdown. Depending on user's settings, the drawdown can be calculated both as a percentage and in absolute values.
Another important algorithm is the analysis of daily and weekly losses:
from_date = dt.datetime.now() - dt.timedelta(days=7) to_date = dt.datetime.now() deals = mt5.history_deals_get(from_date, to_date) or [] daily_balance_loss = weekly_balance_loss = daily_equity_loss = weekly_equity_loss = 0 if deals: deals_df = pd.DataFrame(list(deals), columns=deals[0]._asdict().keys()) today = dt.datetime.now().date() today_deals = deals_df[pd.to_datetime(deals_df['time']).dt.date == today] week_start = (dt.datetime.now() - dt.timedelta(days=dt.datetime.now().weekday())).date() week_deals = deals_df[pd.to_datetime(deals_df['time']).dt.date >= week_start] daily_balance_loss = today_deals[today_deals['profit'] < 0]['profit'].sum() if not today_deals.empty else 0 weekly_balance_loss = week_deals[week_deals['profit'] < 0]['profit'].sum() if not week_deals.empty else 0
The system analyzes the history of trades for the current day and week, calculating total losses and comparing them with the established limits.
Automatic position closing mechanism
One of the most important functions of our system is the automatic closing of positions when the risk limits are exceeded. This is a critical protection mechanism that triggers even in trader’s absence:
limits_exceeded = daily_balance_limit_exceeded or weekly_balance_limit_exceeded or daily_equity_limit_exceeded or weekly_equity_limit_exceeded or max_drawdown_exceeded with self.block_lock: if limits_exceeded and self.risk_settings.get('auto_close_positions', True): self.blocked_trading = True if positions: self.close_all_positions() self.error_signal.emit(f"All positions closed due to exceeding limits: DBL={daily_balance_limit_exceeded}, WBL={weekly_balance_limit_exceeded}, DEL={daily_equity_limit_exceeded}, WEL={weekly_equity_limit_exceeded}, MDD={max_drawdown_exceeded}")
If any of the configured limits are exceeded, the system can automatically close all open positions and block further trading. This protects the trader from catastrophic losses in the event of strong market movements or a series of unsuccessful trades.
The process of closing positions is implemented subject to all the features of MetaTrader 5 API:
def close_all_positions(self): positions = mt5.positions_get() if positions is None: self.error_signal.emit(f"Error in retrieving positions to close: {mt5.last_error()}") return if not positions: self.error_signal.emit(“No open positions to close") return for position in positions: try: symbol = position.symbol volume = position.volume position_id = position.ticket position_type = position.type tick = mt5.symbol_info_tick(symbol) if not tick: self.error_signal.emit(f”Failed to retrieve data on symbol {symbol}") continue close_price = tick.bid if position_type == mt5.ORDER_TYPE_BUY else tick.ask close_type = mt5.ORDER_TYPE_SELL if position_type == mt5.ORDER_TYPE_BUY else mt5.ORDER_TYPE_BUY close_request = { "action": mt5.TRADE_ACTION_DEAL, "symbol": symbol, "volume": volume, "type": close_type, "position": position_id, "price": close_price, "deviation": 100, "comment": "Close by Risk Manager", } result = mt5.order_send(close_request) # Result processing... except Exception as e: self.error_signal.emit(f”Error at closing position: {str(e)}")
The system sequentially processes each open position, determines position direction (buy or sell), receives current market prices and generates a closing request with corresponding parameters.
Persistence of data and settings
To ensure reliable operation, even in the event of a restart, the system leverages an SQLite database to store all the necessary information:
def save_to_db(self, data): try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS accounts ( account_id INTEGER PRIMARY KEY, server TEXT, login TEXT, password TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )''') cursor.execute('''CREATE TABLE IF NOT EXISTS account_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, account_id INTEGER, balance REAL, equity REAL, balance_drawdown REAL, equity_drawdown REAL, margin REAL, free_margin REAL, margin_level REAL, daily_balance_loss REAL, weekly_balance_loss REAL, daily_equity_loss REAL, weekly_equity_loss REAL, positions_count INTEGER, blocked_trading INTEGER, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )''') # Saving data to database... except sqlite3.Error as e: self.error_signal.emit(f”Database error: {str(e)}") finally: conn.close()
The database stores information about connected accounts, their settings, and account status history. This allows you not only to restore the system state after restart, but also provides data for calculating drawdowns and generating analytical reports.
Visualization and reporting
An important aspect of our system is clear data visualization and report generation. We use the pyqtgraph library to build real-time charts:
equity_plot = getattr(self, f"equity_plot_{account_id}") equity_data = getattr(self, f"equity_data_{account_id}") equity_times = getattr(self, f"equity_times_{account_id}") equity_data.append(data['equity']) equity_times.append(dt.datetime.now().timestamp()) if len(equity_data) > 100: equity_data.pop(0) equity_times.pop(0) equity_plot.clear() equity_plot.plot(equity_times, equity_data, pen=pg.mkPen(color=(0, 150, 0), width=2))
For weekly reports, the system generates HTML pages with matplotlib charts and data tables:
def generate_weekly_report(self, data): try: conn = sqlite3.connect(self.db_path) df = pd.read_sql_query('SELECT timestamp, balance, equity, balance_drawdown, equity_drawdown FROM account_history WHERE account_id = ? AND timestamp >= ?', conn, params=(data['account_id'], dt.datetime.now() - dt.timedelta(days=7))) if df.empty: self.error_signal.emit(f"Нет данных для отчета по счету {data['account_id']}") return table_html = df.to_html(index=False, classes='table table-striped', border=0) fig, ax = plt.subplots(figsize=(10, 6)) ax.plot(pd.to_datetime(df['timestamp']), df['balance_drawdown'], label='Balance Drawdown (%)', color='blue') ax.plot(pd.to_datetime(df['timestamp']), df['equity_drawdown'], label='Equity Drawdown (%)', color='red') ax.set_title(f"Weekly Drawdown Report for Account {data['account_id']}") ax.set_xlabel("Date") ax.set_ylabel("Drawdown (%)") ax.legend() ax.grid(True) # Generating an HTML report... except Exception as e: self.error_signal.emit(f"Error generating report: {str(e)}") finally: conn.close()
These reports are becoming an invaluable tool for analyzing trading results and the effectiveness of risk management.
Deploying the system on a remote server
One of the main advantages of our risk manager is the ability to deploy it on a remote server, which ensures continuous operation 24/7, regardless of the condition of your main computer. This is especially important for professional traders who cannot afford the risk of a monitoring system shutdown due to a power failure or internet connection problems (or when it is simply inconvenient for you to keep the main machine on all the time).
Further development of the system
Our risk management system provides a solid foundation for risk management in trading, but there are many opportunities for its further expansion:
- Integration with multiple MetaTrader 5/MetaTrader 4 terminals is for traders working with different brokers and platforms.
- Advanced analytics — additional performance metrics such as the Sharpe ratio, the maximum series of losing trades, the win/loss ratio, etc.
- Machine learning is the forecasting of potential risks based on historical data and the current state of the market.
- Alerts via Telegram/Discord/Email — instant notifications of critical events to your phone or computer.
- Advanced position closing strategies — for example, partial closing of positions instead of full closing when approaching risk limits.
Conclusion
A remote Risk Manager in Python is a powerful tool to protect your trading capital from excessive risks. Thanks to the automation of risk control, you can focus on finding trading opportunities, being confident that the system will protect you from catastrophic losses.
In today's highly competitive environment of financial markets, it is not those who make the most profit in the short term who survive, but those who are able to effectively manage risks and preserve capital in the long term. Our system helps you become a part of this successful group of traders.
Remember: the main objective of risk management is not only to protect you from major losses, but also to provide psychological comfort that will allow you to follow your chosen trading strategy even during periods of market stress. As practice shows, it is psychological discipline that often distinguishes successful traders from losers.
Implement the described system into your trading practice, adapt it to your individual needs and observe how your approach to the market changes when you know that your capital is reliably protected.
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/17410
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Features of Custom Indicators Creation
Developing a multi-currency Expert Advisor (Part 24): Adding a new strategy (II)
Features of Experts Advisors
Python-MetaTrader 5 Strategy Tester (Part 03): MT5-Like Trading Operations — Handling and Managing
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use