Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 71

Executing Your Trading Strategy: Generating Entry & Exit Points

Recommended resources

Hello! Welcome back to your journey into algorithmic trading.

In our last lesson, you built your first vectorized backtester. You learned how to take a simple trading rule (price crossing a moving average), generate a position state over time, and plot an equity curve. This gave you a high-level view of a strategy's performance. However, an equity curve doesn't tell the whole story. It doesn't tell you your win rate, your average profit per trade, or how many trades the strategy took.

This lesson directly addresses that gap, fulfilling the learning outcome: Generate a series of trades (entry/exit points) from your strategy logic. We will move beyond the continuous "in-or-out" position state and learn how to create a discrete list of every single trade the strategy would have taken. This is the foundational data you'll need for almost all meaningful performance analysis, bringing you one step closer to your goal of building and evaluating automated trading bots.

From Signals to a Trade List: The Core Logic

The fundamental task is to pair up entry signals with their corresponding exit signals. Imagine you're watching a chart. You see your setup, you enter a trade. You are now "in a position." You ignore all new entry signals until you see a reason to exit. Once you exit, you are "flat" again, ready to look for the next entry.

We can simulate this logic in Python by looping through our price data and using a "state" variable, like a flag called is_in_trade, to keep track of whether we currently have an open position.

Here's the process:

  1. Initialize: Start with no open trades (is_in_trade = False). Create an empty list to store our completed trades.
  2. Iterate: Go through the price data, candle by candle (row by row).
  3. Check for Entry: On each candle, ask: "Am I currently flat, AND is there a buy signal?" If yes, record the entry price and date, then set is_in_trade = True.
  4. Check for Exit: On each candle, ask: "Am I currently in a trade, AND is there a sell signal?" If yes, record the exit price and date, combine it with the entry information to form a complete "trade" object, add it to our list of trades, and finally set is_in_trade = False.
  5. Finalize: Once the loop is finished, convert the list of completed trades into a pandas DataFrame for easy analysis.

Implementing a State-Based Backtester

Let's walk through a clean, practical implementation of this logic. The following article provides an excellent, straightforward example using an EMA crossover strategy.

First, we need to generate our signals. The article demonstrates how to create buy_signal and sell_signal columns, where a 1 marks the crossover event.

Backtesting Trading Strategies using Python Pandas

Let's review how to generate entry and exit signals for a crossover strategy. This is the input for our trade-generation logic.

Read the section 'Trading strategy'. Focus on the code block that creates the 'buy_signal' and 'sell_signal' columns. Notice how it uses a boolean comparison (df["ema12"] > df["ema26"]) and then checks for a change from the previous row (.ne(df["ema12gtema26"].shift())) to identify the exact moment of the cross.

Now that we have our signals, we can implement the iterative logic we just discussed to generate a list of trades.

Backtesting Trading Strategies using Python Pandas

This next section of the article implements the state-based iteration perfectly. It will show you how to loop through the DataFrame and use a flag to simulate opening and closing trades.

Read the section 'Backtesting the strategy'. Study the for loop carefully. Pay close attention to how the is_order_open variable (our state flag) is used to control the logic. Notice when it's set to 1 (entry) and 0 (exit), and how a dictionary named order is used to collect the details before being appended to the orders list.

After running this loop, the orders list contains a series of dictionaries, with each dictionary representing one closed trade. The final, crucial line df_orders = pd.DataFrame(orders) converts this list into the clean, tabular format that is the goal of this lesson. You now have a structured log of every trade, ready for detailed analysis.

Test your understanding!

In the code from the article, what prevents the script from opening a new buy position if a second buy_signal appears while a trade is already open?

Show answer

The entry logic is contained within the conditional if row["buy_signal"] and is_order_open == 0:. If a trade is already open, is_order_open will be 1, so the second part of the condition (is_order_open == 0) will be false. This prevents a new trade from being initiated until the current one is closed and the flag is reset to 0.


A More Dynamic Approach: Finding Exits with a While Loop

The previous method works great when you have a pre-defined sell_signal column. But what about more dynamic ICT scenarios? For example, your exit might not be a simple indicator cross, but rather price hitting a stop-loss or a take-profit level that is calculated at the moment of entry.

To handle this, we can use a slightly different structure: an outer loop to find entries and a nested inner loop to find the exits for each specific trade.

Let's look at a video that demonstrates this powerful technique. The strategy itself is different, but the programming pattern is what's important.

Backtesting TradePros 76% Winning Rate Trading Strategy in Python [EMA/Stoch RSI/ATR]

This video from Algovibes, 'Backtesting TradePros...', demonstrates how to find dynamic exits. The presenter finds a buy signal, locks in the trade-specific stop-loss and take-profit levels, and then uses a while loop to check future candles until one of those levels is hit.

Watch from 12:32 to 18:25. Don't worry about the specifics of the indicators. Focus on the structure: The outer for loop identifies a buy signal (if df.loc[i, 'buy_signal']). Inside, the stop_loss and target_profit for that specific trade are stored in variables. The inner while in_position: loop starts, checking each subsequent candle's price against the stored SL/TP levels. When an exit condition is met, the outcome is recorded, and in_position is set to False to break the inner loop and allow the outer loop to search for the next trade.

This nested loop pattern is incredibly useful. It closely mimics the real-world process of managing an open trade and is a key technique for backtesting strategies with dynamic targets or stops, which are common in ICT methods. While it's slightly more complex than the single-loop approach, it offers much greater flexibility.


Conclusion

Today you've made a significant leap forward. You've moved from a high-level equity curve to generating a detailed, actionable list of individual trades. This log is the raw material for deep strategy analysis and is a universal requirement for any serious backtesting effort.

Key Takeaways:

  • Generating a series of trades involves pairing entry signals with their corresponding exit signals.
  • A simple and effective method is to iterate through your data using a state flag (e.g., is_in_trade) to ensure you only enter when you are flat and only exit when you are in a position.
  • The output is typically a list of dictionaries or a pandas DataFrame where each row represents one complete trade, containing details like entry/exit prices and times.
  • For dynamic exit conditions (like a stop-loss calculated at entry), a nested loop structure provides the flexibility to check future price action against trade-specific levels.

Preview of the Next Lesson:
Now that we can generate a clean list of every trade a strategy takes, what can we do with it? In the next lesson, you will learn how to calculate and interpret key performance metrics: win rate, profit factor, and drawdown. You'll use the trade list you learned to create today to quantify your strategy's effectiveness in precise, objective terms.