Trade management for algorithmic trading bot

As an Amazon Associate I earn from qualifying purchases.

5 Min Read

Successful traders focus on how much they are going to (or willing) to lose rather than how much profit they will make. Limiting your risk is vital to the success of your algorithmic trading bot. Today, I will be covering trade management for your algorithmic trading bot.

Here is a quick reminder of what topic I have covered so far in creating an algorithmic trading bot:

  1. Open a trade using the MT5 API with Python
  2. Close a trade with MT5 using Python
  3. Creating an algotrader/trading bot with Python – Part 1
  4. Creating an algotrader/trading bot with Python – Part 2
  5. Creating an algotrader/trading bot with Python – Part 3
  6. Creating a strategy for your algorithmic trading bot – Part 1
  7. Creating a strategy for your algorithmic trading bot – Part 2
  8. Dynamically calculate lot size for your algorithmic trading bot
  9. Send messages from Python to Slack
  10. Send an email from Python

Getting the closed trades history

Let’s start by implementing a new method from the MT5 API that will allow you to retrieve all previously closed trades and the profit. This is important as we will be using this to calculate the lost trades for the current day. You will be using a method called history_deals_get from the MT5 API which takes the following arguments: date_from, date_to and an option parameter group which we will not be using today.

Create a new method in trader.py and name it get_order_history with arguments date_from and date_to:

def get_order_history(date_from, date_to):

Now, make a call to the MT5 API history_deals_get with the date_from and date_to arguments and assign it to a variable named res:

def get_order_history(date_from, date_to):
    res = mt5.history_deals_get(date_from, date_to)

Since res can be None or empty you need to account for this. If res is not empty then convert the result to data frame and set the time field to a datetime type. Otherwise, return an empty data frame.

def get_order_history(date_from, date_to):
    res = mt5.history_deals_get(date_from, date_to)
    
    if(res is not None and res != ()):
        df = pd.DataFrame(list(res),columns=res[0]._asdict().keys())
        df['time'] = pd.to_datetime(df['time'], unit='s')
        return df
    
    return pd.DataFrame()

Calculating lost trades per day

To manage our trades you will be checking to see how many trades you have lost in the current day. Start by creating a new method and name it calc_daily_lost_trades with no arguments:

def calc_daily_lost_trades():

Calculate the current date and time and adjust this for your broker time. Remember, your broker time is not necessarily the same as your local time. Since my broker is in Athens, I will convert the time to this particular timezone. A full last of pytz time zones can be found here.

now = datetime.now().astimezone(pytz.timezone('Europe/Athens'))

Since we are calculating daily losses we need to define midnight as the start of the trading day:

midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)

Call the method get_order_history created above with midnight as the first argument and now as the second and assign it to a variable named res. This will give us all the closed trades for the current day from midnight:

def calc_daily_lost_trades():
    now = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
    midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)
    res = get_order_history(midnight, now)

If res is empty then we know that no trades have been closed. In this case we can simply return 0.

def calc_daily_lost_trades():
    now = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
    midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)
    res = get_order_history(midnight, now)

    if(res.empty):
        return 0

Otherwise, let’s iterate over the rows in res and find out what the profit/loss was for each trade. Start by defining a variable named lost_trade_count for lost trades and a loop to iterate over the rows:

def calc_daily_lost_trades():
    now = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
    midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)
    res = get_order_history(midnight, now)

    if(res.empty):
        return 0
    else:
        lost_trade_count = 0
        for i, row in res.iterrows():

Find the profit field in the res data frame and add an if condition to say that if profit is less than zero (i.e. a losing trade) increase the lost_trade_count by 1:

def calc_daily_lost_trades():
    now = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
    midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)
    res = get_order_history(midnight, now)

    if(res.empty):
        return 0
    else:
        lost_trade_count = 0
        for i, row in res.iterrows():
            profit = float(row['profit'])
            if(profit < 0):
                lost_trade_count = lost_trade_count + 1

Finally, return lost_trade_count

def calc_daily_lost_trades():
    now = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
    now = datetime(now.year, now.month, now.day, hour=now.hour, minute=now.minute)
    midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)
    res = get_order_history(midnight, now)

    if(res.empty):
        return 0
    else:
        lost_trade_count = 0
        for i, row in res.iterrows():
            profit = float(row['profit'])
            if(profit < 0):
                lost_trade_count = lost_trade_count + 1
        return lost_trade_count

Limiting your trading exposure

Now that we have a way to calculate daily losses, let’s see how we can use this to our advantage and only open trades if we have not exceeded the maximum allowed daily losses. Start by navigating to the check_trades method and find the code below:

        for index, last in last_row.iterrows():
            #Exit strategy
            if(last['close'] < last['EMA'] and last['close'] > last['SMA']):
                close_positon_by_symbol(pair)

            #Entry strategy
            if(last['close'] > last['EMA'] and last['close'] < last['SMA']):
                lot_size = calc_position_size(pair, strategy)
                open_position(pair, "BUY", lot_size, float (strategy['takeProfit']), float(strategy['stopLoss']))

Add a check above # Entry strategy to check the current amount of lost trades against the amount of losing trades you are willing to have in one day. (This value will come from your strategy.json file.

Call the calc_daily_lost_trades() method and assign the result to a variable named lost_trade_count:

        for index, last in last_row.iterrows():
            #Exit strategy
            if(last['close'] < last['EMA'] and last['close'] > last['SMA']):
                close_positon_by_symbol(pair)

            lost_trade_count = calc_daily_lost_trades()

Add a check to see if lost_trade_count is greater than your max losses – if it is, then you will want to add a continue statement as you will only want to close trades from this point on. If the lost_trade_count is not bigger than the maximum amount of allowable losses then continue to open trades:

        for index, last in last_row.iterrows():
            #Exit strategy
            if(last['close'] < last['EMA'] and last['close'] > last['SMA']):
                close_positon_by_symbol(pair)

            lost_trade_count = calc_daily_lost_trades()

            if(lost_trade_count > strategy['maxLosses']):
                print("Daily losses have been exceeded. Not executing any more trades today")
                continue
            
            #Entry strategy
            if(last['close'] > last['EMA'] and last['close'] < last['SMA']):
                lot_size = calc_position_size(pair, strategy)
                open_position(pair, "BUY", lot_size, float (strategy['takeProfit']), float(strategy['stopLoss']))

Adding max losses to your strategy

You should now have a way of calculating daily losses and limiting your trading exposure. Now you need to define strategy['maxLosses']. This can be done in strategy.json

Your strategy will look like this. (if not I recommend going back to the articles where I talk about creating a strategy):

{
  "account_currency" : "USD",
	"strategy_name": "myStrategy",
	"pairs": [
		"EURUSD",
		"USDCAD",
		"GBPUSD"
    ],
    "risk" : 2,
    "takeProfit": 700.0,
    "stopLoss": 300.0,
	  "movingAverages": {
        "SMA": {
            "val": 10,
            "aboveBelow": "below"
        },
        "EMA": {
            "val": 50,
            "aboveBelow": "above"
        }
    }
}

Under risk, add a new key named “maxLosses” and assign a value of 3. This means that if your trader loses more than 3 trades it will not open any more trades for that day.

{
  "account_currency" : "USD",
	"strategy_name": "myStrategy",
	"pairs": [
		"EURUSD",
		"USDCAD",
		"GBPUSD"
    ],
    "risk" : 2,
    "maxLosses" : 3,
...

Testing the code

To test this I will open 3 trades manually and close them with losses. I should see a message saying “Daily losses have been exceeded. Not executing any more trades today” if everything is working as expected.

Trading bot started with strategy: strategy
Running trader at 2021-02-05 08:30:00.688101
Connected: Connecting to MT5 Client
Daily losses have been exceeded. Not executing any more trades today
Daily losses have been exceeded. Not executing any more trades today
Daily losses have been exceeded. Not executing any more trades today

As you can see above, since I have hit my daily losses no more trades will be opened for today.

If you are interested in learning more about algo trading and trading systems, I highly recommend reading this book. I have taken some of my own trading ideas and strategies from this book. It also provided me a great insight into effective back testing. Check it out here.

That’s all for now! Check back on Monday to see how you can monitor your P/L throughout the day! As always, if you have any questions or comments please feel free to post them below. Additionally, if you run into any issues please let me know.

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to my newsletter to keep up to date with my latest posts

Holler Box