Hello! Welcome back to our course on building a Python-based trading system.
In our last lesson, we implemented the profit-taking logic for our credit spread strategies. Our system can now identify when to close a position to lock in a predefined percentage of the maximum profit. This is a crucial step, but it only covers the scenario where a trade goes in our favor.
Today, we address the equally important, and arguably more critical, aspect of position management: handling losses. The learning outcome for this lesson is to implement stop-loss logic based on the position reaching a predefined loss threshold. A disciplined, automated approach to cutting losses is a non-negotiable feature of any robust trading system.
1. Understanding Stop-Losses for Credit Spreads
For a credit spread, you receive a premium (a credit) when you open the position. Your goal is for the value of this spread to decrease, ideally to zero, so you can keep the entire credit. A trade moves against you when the spread becomes more valuable—that is, the cost to buy it back increases.
A stop-loss, therefore, is an order to close the position if the cost to buy it back rises to a certain predefined level, thereby capping your maximum loss on the trade.
A common way to define this level is to set it as a multiple of the initial credit received. For example, a trader might decide to exit a position if the cost to close it becomes two or three times the premium they initially collected.
Let's watch a segment of a video where this exact concept is discussed with a practical example.
Managing Credit Spread Profits & Losses with Planned Exit Orders | Getting Started with Options
In this video from Trader Talks, the presenter plans the exit for a bull put spread, specifically discussing how to set a stop-loss threshold based on the initial credit.
Watch the clip from 22:22 to 28:08. Pay close attention to how the stop-loss is defined. After collecting a $1.05 credit, the plan is to exit if the cost to close the spread rises to $3.15 (3x the credit), capping the realized loss at $2.10.
As the video demonstrates, the core logic is:
- Exit Condition:
Cost_to_Close≥Stop_Loss_Price Stop_Loss_Price:Initial_Credit*Stop_Loss_Multiplier
Here, the Stop_Loss_Multiplier is a key parameter you'll configure in your strategy. A multiplier of 2.0, for instance, means you're willing to risk an amount equal to your initial credit (since Loss = Cost_to_Close - Initial_Credit = 2 * Initial_Credit - Initial_Credit = Initial_Credit). A multiplier of 3.0 means you're risking twice your initial credit.
2. Linking the Stop-Loss Price to Market Events
While using a multiple of the credit is a straightforward rule, it's also useful to understand what a specific stop-loss price signifies in terms of market behavior. For a spread with a $1 width, a price of $0.50 is a critical point. It indicates that the underlying asset's price is trading exactly in the middle of your short and long strikes. This significantly increases the risk of the short leg being assigned.
Let's watch another short clip that explains this connection.
How I Use Stop Losses For Credit Spreads | Real Trade Example
This video from Austin Bouley explains why a spread price of $0.50 (for a $1-wide spread) is a meaningful level for a stop-loss, connecting it directly to assignment risk.
Watch from 03:47 to 05:37. The key takeaway is that exiting when the spread price hits $0.50 is a way to get out of the trade just as it enters the money, helping to avoid assignment risk.
This gives us a deeper rationale for setting stop-losses. It's not just an arbitrary number, but can be tied to a specific, undesirable change in the position's risk profile. For our implementation, we will stick with the Stop_Loss_Multiplier approach as it's more general and can be applied to spreads of any width, but it's important to understand this underlying context.
3. Implementing the Stop-Loss Logic
We will now update the manage_position method we created in the last lesson. It already handles profit-taking; we'll add a check for our stop-loss condition. The logic will first check for a profit-taking exit, and if that condition is not met, it will then check for a stop-loss exit.
First, let's add the stop_loss_multiplier to our strategy's parameters.
Here is the updated BullPutSpread class in framework/strategies.py:
# In framework/strategies.py
# ... other imports from previous lessons ...
from framework.interfaces import Strategy
class BullPutSpread(Strategy):
def __init__(self, symbol: str, params: dict):
super().__init__(name=f"BullPutSpread_{symbol}")
self.symbol = symbol
# Entry parameters
self.min_dte = params.get('min_dte', 21)
self.max_dte = params.get('max_dte', 60)
self.oi_threshold = params.get('oi_threshold', 50)
self.target_delta = params.get('target_delta', -0.30)
self.delta_tolerance = params.get('delta_tolerance', 0.05)
self.spread_width = params.get('spread_width', 5)
self.min_credit = params.get('min_credit', 0.25)
# Position Management Parameters
self.profit_target_pct = params.get('profit_target_pct', 0.50)
# --- NEW Stop-Loss Parameter ---
self.stop_loss_multiplier = params.get('stop_loss_multiplier', 2.0) # e.g., exit if loss is 1x the credit received
def find_trade_opportunity(self, signal, data_handler, analytics_engine):
# This method remains unchanged.
pass
# ... Other helper methods like _find_spread and _validate_spread remain unchanged ...
def manage_position(self, position: dict, data_handler) -> str:
"""
Checks if the position should be closed based on profit target or stop-loss.
Returns an action string: 'HOLD', 'CLOSE_PROFIT', or 'CLOSE_STOPLOSS'.
"""
# 1. Extract necessary info from the position object
short_leg_symbol = position['short_leg']['symbol']
long_leg_symbol = position['long_leg']['symbol']
initial_credit = position['initial_credit']
# 2. Get current market prices (quotes) for both legs
short_leg_quote = data_handler.get_latest_quote(short_leg_symbol)
long_leg_quote = data_handler.get_latest_quote(long_leg_symbol)
if not short_leg_quote or not long_leg_quote:
print(f"Could not retrieve current quotes for {self.symbol} position. Holding.")
return 'HOLD'
# 3. Calculate the current cost to close the spread
cost_to_close = short_leg_quote['ask_price'] - long_leg_quote['bid_price']
# 4. Check for Profit Target (from previous lesson)
target_cost_to_close = initial_credit * (1 - self.profit_target_pct)
if cost_to_close <= target_cost_to_close:
current_pnl = initial_credit - cost_to_close
profit_pct = (current_pnl / initial_credit) * 100
print(
f"PROFIT TARGET HIT for {self.symbol}. "
f"Current P&L: ${current_pnl:.2f} ({profit_pct:.1f}% of max profit). "
f"Closing position."
)
return 'CLOSE_PROFIT'
# 5. --- NEW: Check for Stop-Loss ---
stop_loss_price = initial_credit * self.stop_loss_multiplier
if cost_to_close >= stop_loss_price:
current_loss = cost_to_close - initial_credit
print(
f"STOP-LOSS TRIGGERED for {self.symbol}. "
f"Cost to close (${cost_to_close:.2f}) exceeded stop price (${stop_loss_price:.2f}). "
f"Current Loss: ${current_loss:.2f}. Closing position."
)
return 'CLOSE_STOPLOSS'
# 6. If neither condition is met, hold the position
return 'HOLD'
This implementation defines a "hard stop-loss," a fixed threshold for exiting. The Python article Bull Call Spread: Defined Risk for Bullish Trends (resource LINK) describes this concept for debit spreads. Our logic is the direct equivalent for credit spreads.
Test your understanding!
You have a BullPutSpread position with the following details:
initial_creditreceived was $0.80 per share.- Your
profit_target_pctis set to 0.50 (50%). - Your
stop_loss_multiplieris set to 2.5. - The current market quote for the short put is: Bid $2.10, Ask $2.15.
- The current market quote for the long put is: Bid $0.20, Ask $0.25.
Based on the logic in our updated manage_position method, what action should be taken? Show your calculations for both profit-taking and stop-loss checks.
Show answer
-
Calculate the Cost to Close:
- To close, you buy back the short put (at its ask price) and sell the long put (at its bid price).
cost_to_close= (Short Put Ask) - (Long Put Bid)cost_to_close= $2.15 - $0.20 = $1.95
-
Check for Profit Target:
target_cost_to_close=initial_credit* (1 -profit_target_pct)target_cost_to_close= $0.80 * (1 - 0.50) = $0.40- Condition:
cost_to_close($1.95) ≤target_cost_to_close($0.40)? This is FALSE. The profit target is not met.
-
Check for Stop-Loss:
stop_loss_price=initial_credit*stop_loss_multiplierstop_loss_price= $0.80 * 2.5 = $2.00- Condition:
cost_to_close($1.95) ≥stop_loss_price($2.00)? This is FALSE. The stop-loss has not been triggered.
Conclusion: Since neither the profit target nor the stop-loss condition has been met, the method should return 'HOLD'. The position is currently at an unrealized loss of $1.15 ($1.95 - $0.80), but it hasn't reached the $1.20 loss that would trigger the stop ($2.00 - $0.80).
A Brief Look at Other Stop-Loss Methods
The price-based stop-loss we implemented is simple and effective. It's worth knowing that other methods exist, which we can integrate into our framework later. The Alpaca Markets article Backtesting 0DTE Options with Python (resource LINK) demonstrates two alternatives in its trade_0DTE_options_historical function:
- Delta-Based Stop-Loss: Exits if the position's total delta changes by a certain amount (e.g., doubles from its initial value). This is a way to manage directional risk.
- Assignment Risk Stop-Loss: Exits if the underlying asset's price breaches a level uncomfortably close to the short strike, signaling high assignment risk.
We will revisit delta-based management in our next lesson, but for now, our price-based stop-loss provides a solid foundation for risk control.
Conclusion
Fantastic progress today. You have now equipped your strategy classes with the two fundamental pillars of position management: taking profits and cutting losses. By implementing a clear, automated stop-loss rule, you are ensuring your system operates with discipline and protects your capital from significant drawdowns.
Key Takeaways:
- A stop-loss for a credit spread is triggered when the cost to close the position increases to a predefined threshold.
- This threshold is commonly defined as a multiple of the initial credit (e.g., 2x or 3x), which is controlled by a
stop_loss_multiplierparameter. - We have extended the
manage_positionmethod to return'CLOSE_STOPLOSS'when this threshold is breached. - Your strategies now have a complete, albeit basic, exit logic for both winning and losing trades.
Preview of the Next Lesson:
Our current exit logic is binary: take profit or cut the loss. But what about the grey area in between? Sometimes, a trade moves against you but hasn't yet hit the stop-loss. In these situations, we might want to adjust the position rather than close it. In the next lesson, we will explore this advanced concept: developing adjustment logic to manage the position's delta, such as rolling the spread up, down, or out.