I Backtested the 0DTE Iron Condor Strategy on SPY and These are the Results
Exploring the Results of the Iron Condor Strategy Using Same-Day Expiration Options

We are always looking for innovative ideas and strategies that will yield positive returns consistently since they are driven by a desire to beat the market. Among the many possibilities accessible, the Iron Condor strategy is one of many approaches that we are interested in testing. Combining this strategy’s adaptability with the idea of trading 0DTE (zero days to expiry) options opens up some opportunities that have the potential to completely change the way we trade the market. This article will explore the application of the Iron Condor strategy with the ODTE SPY options to check its potential.
Let's start with the basics…
What is an Iron Condor?
The Iron Condor is an options trading strategy that is widely employed by traders seeking to generate consistent income in a relatively low-volatility market. This strategy is best suited for traders who anticipate that the price of the underlying asset, in this case, SPY, will remain within a certain price range over a defined period.
An Iron Condor involves the simultaneous sale of an out-of-the-money (OTM) put option and an out-of-the-money call option, followed by the purchase of another put and call option with a lower and higher strike price, respectively. This combination creates a four-legged position with defined risk and reward parameters.
The primary objectives of an Iron Condor are:
Generate Income: The primary aim of this strategy is to profit from the time decay of options by collecting the premiums from selling the OTM options. As long as the price of the underlying asset remains within a specified range, the trader can keep the premium received.
Limit Risk: The purchased options with lower and higher strike prices serve as a hedge, limiting potential losses in case the market moves significantly against the position.
Defined Profit Range: An Iron Condor offers a defined range within which the trader can achieve maximum profit. The width of this range is determined by the difference between the strike prices of the call and put options used in the strategy.
Non-Directional: Unlike some other strategies, the Iron Condor is considered a non-directional strategy. It can profit whether the underlying asset moves up, down, or remains relatively stable within the defined range.
What Does 0DTE Mean?
0DTE, or “zero days to expiration,” is a term that is frequently used in options trading. It refers to options that are set to expire at the end of the trading day, with no additional time remaining. This means that traders holding 0DTE options must make a decision before the trading day closes or risk the options expiring worthless.
What Are 0DTE Options?
0DTE options, also known as “same-day expiration” options, are a specific subset of options contracts. These contracts have a very short lifespan, expiring on the same day they are issued. While most standard options contracts have expiration dates that are days, weeks, or even months in the future, 0DTE options are unique in that they offer a quick, high-stakes trading opportunity.
0DTE options come in two main varieties:
0DTE Weekly Options: These options expire on the same day of the week as they are issued. For example, if an option is issued on a Friday, it will expire later that same day.
0DTE Daily Options: These options expire on the same trading day they are issued. For traders who are looking for short-term opportunities and quick decisions, daily options are particularly appealing.
How Do 0DTE Options Work?
0DTE options are attractive because of their quick turnover and large profits or losses that can occur in a single trading session. To understand how they work, it’s important to consider the following:
Intraday Trading: 0DTE options are exclusively intraday trading instruments. Traders must open and close their positions within the same trading day.
High Theta: The time decay of options, measured by theta, is extremely high for 0DTE options. This means the value of these options erodes rapidly as the trading day progresses.
Leveraged Gains and Losses: Due to their rapid time decay and leverage, 0DTE options can amplify both gains and losses. Small price movements in the underlying asset can lead to significant changes in the option value.
Time Sensitivity: Timing is critical when trading 0DTE options. Traders must closely monitor the market and make quick decisions based on their analysis of the underlying asset’s price movement.
Now that we have a good understanding of the Iron Condor strategy and 0DTE options, let’s explore how these two concepts can be combined to create a unique and dynamic trading approach.
Constructing a 0DTE Iron Condor
To create a 0DTE Iron Condor, traders follow a similar process to the traditional Iron Condor strategy, but with a few key differences:
Selection of Strike Prices: For a 0DTE Iron Condor, strike prices are typically chosen very close to the current market price of the underlying asset (in this case, SPY). The idea is to establish a narrow range within which you expect the asset’s price to remain.
Same-Day Expiration: All four options used in the Iron Condor have the same trading day expiration, making this a pure intraday trade.
Continuous Monitoring: Constant vigilance is crucial. Since the options expire at the end of the trading day, traders must be prepared to close their positions at any point if market conditions change.
You are not a Medium member yet? Why not take the next step and become a member for less than a Starbucks coffee per month? Don’t miss out — become a member now!
Also, if you enjoy reading my articles, please don’t forget to hit the follow button — Diego Degese
Risk and Reward of 0DTE Iron Condors
Trading a 0DTE Iron Condor combines the potential benefits of the Iron Condor strategy with the rapid pace of 0DTE options. Let’s break down the risk and reward aspects:
Risk:
Time Sensitivity: With 0DTE options, time decay (theta) accelerates, meaning that the options lose value rapidly. If the market doesn’t move as expected, the 0DTE Iron Condor could result in significant losses.
Quick Market Changes: The market can be highly unpredictable, and rapid changes in the underlying asset’s price can pose a risk. Traders must be vigilant and ready to react promptly.
Limited Adjustment Time: Unlike traditional Iron Condors, which can be adjusted over the course of several days or weeks, 0DTE Iron Condors leave very little room for adjustments. This can lead to limited risk management options.
Reward:
Rapid Profit Potential: 0DTE Iron Condors offer the potential for rapid profits if the market remains within the expected range. As time decay accelerates, the trader can capture the full premium on the options.
Defined Profit Range: Just like traditional Iron Condors, the 0DTE variant offers a defined range within which maximum profit can be achieved.
Intraday Trading: For traders who enjoy the excitement of intraday trading and quick decision-making, 0DTE Iron Condors can be particularly appealing.
When Do 0DTE Options Expire?
Understanding the expiration time of 0DTE options is crucial to effectively execute any trading strategy involving these contracts. 0DTE options expire on the same trading day they are issued, and their expiration time may vary depending on the exchange and market in which they are traded.
For instance, if you are trading on the CBOE (Chicago Board Options Exchange), 0DTE options typically expire at 4:00 PM (Eastern Time) on the trading day. It’s essential to be aware of the specific expiration time for the market you are trading in, as it can influence your trading decisions.
What Happens If the Option Isn’t Exercised Before It Expires?
As 0DTE options expire at the end of the trading day, they are typically European-style options. This means that they can only be exercised at expiration, unlike American-style options, which can be exercised at any time before expiration.
If you hold a 0DTE option and do not exercise it before it expires, the option will simply expire worthless, and you will lose the premium you paid to purchase it. This is a characteristic shared with all European-style options.
However, most traders in the 0DTE options market are primarily focused on trading the options themselves for their time value, rather than exercising them to acquire or dispose of the underlying asset. This approach aligns with the common goal of generating quick returns through rapid price movements in the options market.
If you enjoy reading my articles, please don’t forget to hit the follow button — Diego Degese
Backtesting the 0DTE Iron Condor on SPY
Now, let’s put the theory into practice. To check the 0DTE Iron Condor strategy on SPY, we will conduct a backtest using historical data from June 2021 to July 2023.
Importing Libraries and Suppressing Warnings
import warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
from tqdm import tqdmThe code begins by importing necessary Python libraries, such as warnings for suppressing warnings, numpy for numerical operations, pandas for data manipulation, andtqdm for progress tracking.
Set Constants
DEBUG = 1
OPTIONS_FILE = '../data/spy_dte_0_iron_condor.csv.gz'
FEES_PER_CONTRACT = 0.6
CONTRACTS = 10
HOUR_OPEN = 11
MINUTE_OPEN = 45
STRIKES = 1Here, various constants are set. It sets constants for debugging, the options data file location, fees per contract, the number of contracts to trade, the opening hour and minute for trading, and the number of strikes.
Helper Functions
def get_iron_condor_strikes(df, strikes):
put_sell = int(df['close_underlying'].values[0]) - strikes
call_sell = int(df['close_underlying'].values[0]) + strikes
put_buy = put_sell - 1
call_buy = call_sell + 1
return (put_buy, put_sell, call_sell, call_buy)
def get_iron_condor_cost(df, put_buy, put_sell, call_sell, call_buy):
# Open Iron Condor
df = df.sort_values('date', ascending=True)
stock_price = df[(df['strike'] == put_buy) & (df['kind'] == 'P')]['close_underlying'].values[0]
put_buy_price = df[(df['strike'] == put_buy) & (df['kind'] == 'P')]['close'].values[0]
put_sell_price = df[(df['strike'] == put_sell) & (df['kind'] == 'P')]['close'].values[0]
call_sell_price = df[(df['strike'] == call_sell) & (df['kind'] == 'C')]['close'].values[0]
call_buy_price = df[(df['strike'] == call_buy) & (df['kind'] == 'C')]['close'].values[0]
open_cost = -100 * put_buy_price
open_cost += 100 * put_sell_price
open_cost += 100 * call_sell_price
open_cost += -100 * call_buy_price
open_cost *= CONTRACTS
if DEBUG:
tqdm.write(f' OPEN ({stock_price}): {put_buy} @ {put_buy_price} - {put_sell} @ {put_sell_price} - {call_sell} @ {call_sell_price} - {call_buy} @ {call_buy_price}')
# Close Iron Condor
stock_price = df[(df['strike'] == put_buy) & (df['kind'] == 'P')]['close_underlying'].values[-1]
put_buy_price = df[(df['strike'] == put_buy) & (df['kind'] == 'P')]['close'].values[-1]
put_sell_price = df[(df['strike'] == put_sell) & (df['kind'] == 'P')]['close'].values[-1]
call_sell_price = df[(df['strike'] == call_sell) & (df['kind'] == 'C')]['close'].values[-1]
call_buy_price = df[(df['strike'] == call_buy) & (df['kind'] == 'C')]['close'].values[-1]
if DEBUG:
tqdm.write(f'CLOSE ({stock_price}): {put_buy} @ {put_buy_price} - {put_sell} @ {put_sell_price} - {call_sell} @ {call_sell_price} - {call_buy} @ {call_buy_price}')
close_cost = 100 * put_buy_price
close_cost += -100 * put_sell_price
close_cost += -100 * call_sell_price
close_cost += 100 * call_buy_price
close_cost *= CONTRACTS
if DEBUG:
tqdm.write(f'**************** Processed Date: {df["date"].dt.date.values[0]} - Result: {open_cost + close_cost:7.2f} ****************')
return open_cost + close_costThe code defines two helper functions used to calculate the strikes and costs for Iron Condor options:
get_iron_condor_strikes(df, strikes=STRIKES): This function calculates the strike prices for the Iron Condor based on the provided DataFrame. It returns the put and call strike prices for selling and buying.get_iron_condor_cost(df, put_buy, put_sell, call_sell, call_buy): This function calculates the cost of opening and closing an Iron Condor position based on the provided strike prices and DataFrame
Reading Options Data
df = pd.read_csv(OPTIONS_FILE, header=0)
df['date'] = pd.to_datetime(df['date'])The code reads historical options data from a CSV file and converts the ‘date’ column to datetime format.
Get All Expiration Dates
expirations = df['expire_date'].unique().tolist()It creates a list of unique expiration dates found in the dataset.
Calculate Profit and Loss by Day
pnl = []
df_cost = df[(df.date.dt.hour > HOUR_OPEN) | ((df.date.dt.hour == HOUR_OPEN) & (df.date.dt.minute >= MINUTE_OPEN))]
df_open = df_cost[(df_cost.date.dt.hour == HOUR_OPEN) & (df_cost.date.dt.minute == MINUTE_OPEN)]
for expiration in tqdm(expirations):
try:
(put_buy, put_sell, call_sell, call_buy) = get_iron_condor_strikes(df_open[df_open['expire_date'] == expiration], STRIKES))
cost = get_iron_condor_cost(df_cost[df_cost['expire_date'] == expiration], put_buy, put_sell, call_sell, call_buy)
pnl.append([expiration, cost])
except Exception as ex:
continueThis section processes the options data to calculate profit and loss for each day based on the specified Iron Condor strategy. It filters the data to consider only trading data after the specified opening time and extracts relevant data for the open Iron Condor positions.
It then iterates through the unique expiration dates and calculates the cost of opening and closing Iron Condor positions for each date. The results are appended to the pnl list.
Generate Results
# Calculate Net Result
df_result = pd.DataFrame(pnl, columns=['expire_date', 'gross_result'])
df_result['net_result'] = df_result['gross_result'] - CONTRACTS * FEES_PER_CONTRACT * 4 # Let them expire
df_result['wins'] = np.where(df_result['net_result'] > 0, 1, 0)
df_result['losses'] = np.where(df_result['net_result'] < 0, 1, 0)This part creates a DataFrame df_result from the calculated P&L data. It calculates the net result by subtracting the fees for letting options expire worthless. Additionally, it classifies each result as a win or a loss based on the net result.
Display Summary Results
# Show the parameters
print(f' PARAMETERS '.center(70, '*'))
print(f'* Hour Open: {HOUR_OPEN}')
print(f'* Minute Open: {MINUTE_OPEN}')
print(f'* Strikes From Price: {STRIKES}')
# Show the Total Result
print(f' SUMMARIZED RESULT '.center(70, '*'))
print(f'* Trading Days: {len(df_result)}')
print(f'* Gross PnL: $ {df_result["gross_result"].sum():.2f}')
print(f'* Net PnL: $ {df_result["net_result"].sum():.2f}')
print(f'* Win Rate: {100 * (df_result["wins"].sum() / (df_result["wins"].sum() + df_result["losses"].sum())):.2f} %')
# Show The Monthly Result
print(f' MONTHLY DETAIL RESULT '.center(70, '*'))
df_monthly = df_result[['expire_date', 'gross_result', 'net_result', 'wins', 'losses']]
df_monthly['year_month'] = df_monthly['expire_date'].str[0:7]
df_monthly = df_monthly.drop('expire_date', axis=1)
df_monthly = df_monthly.groupby(['year_month']).sum()
print(df_monthly)This part displays summary results including the parameters, total results, net P&L, and the win rate. It also shows monthly results, grouping the data by year and month and calculating cumulative results for each period.
Results
***************************** PARAMETERS *****************************
* Hour Open: 11
* Minute Open: 45
* Strikes From Price: 1
************************* SUMMARIZED RESULT **************************
* Trading Days: 360
* Gross PnL: $ 9970.00
* Net PnL: $ 1330.00
* Win Rate: 52.22 %
*********************** MONTHLY DETAIL RESULT ************************
gross_result net_result wins losses
year_month
2021-06 500.0 380.0 4 1
2021-07 690.0 378.0 8 5
2021-08 200.0 -112.0 9 4
2021-09 1410.0 1074.0 10 4
2021-10 -2630.0 -2942.0 4 9
2021-11 300.0 -12.0 8 5
2021-12 -30.0 -270.0 4 6
2022-01 1440.0 1224.0 5 4
2022-02 1660.0 1396.0 7 4
2022-03 -2050.0 -2386.0 6 8
2022-04 -2220.0 -2532.0 4 9
2022-05 680.0 368.0 6 7
2022-06 2980.0 2644.0 9 5
2022-07 1410.0 1098.0 7 6
2022-08 440.0 104.0 8 6
2022-09 -50.0 -362.0 6 7
2022-10 680.0 368.0 6 7
2022-11 1570.0 1186.0 8 8
2022-12 2360.0 1856.0 14 7
2023-01 -580.0 -1060.0 10 10
2023-02 1360.0 904.0 10 9
2023-03 270.0 -282.0 8 15
2023-04 -2730.0 -3186.0 7 12
2023-05 1000.0 472.0 12 10
2023-06 1310.0 1022.0 8 4The results of the Iron Condor strategy offer valuable insights into its performance over the specified trading period from June 2021 to July 2023. Here are the key conclusions based on the summarized results:
Parameters: The strategy was set to open positions at 11:45 AM and maintained a strike width of 1.
Summarized Result:
- The strategy was executed over 360 trading days, resulting in a gross profit of $9,970.00.
- After applying fees and costs associated with the strategy, the net profit was $1,330.00.
- The win rate for the strategy was approximately 52.22%, indicating that it resulted in a profitable outcome in slightly over half of the trading days.
Monthly Detail Result: The monthly breakdown of results shows variations in profitability over time.
- Months such as September 2021 and December 2022 displayed notable profitability, with net results of $1,074.00 and $1,856.00, respectively.
- In contrast, months like October 2021 and March 2023 experienced losses, with net results of -$2,942.00 and -$2,186.00, respectively.
- Overall, the strategy had both profitable and losing months, reflecting the inherent volatility and unpredictability of options trading.
Net Profit Consistency: While the strategy yielded an overall net profit, it’s important to note that the profitability was not consistent across all trading periods. Some months recorded substantial gains, while others resulted in losses. This suggests that the strategy’s performance is subject to market conditions and the accuracy of strike price selections.
Win Rate and Risk Management: The win rate of approximately 52.22% indicates a moderate level of success. Effective risk management, such as defined stop-loss levels, may help mitigate losses during less favorable market conditions.
Evaluation and Further Analysis: To make informed decisions about the strategy’s suitability, further analysis is needed, including assessing risk-adjusted returns, evaluating drawdowns, and considering the strategy’s performance against benchmark indices or alternative strategies.
Conclusion
In conclusion, the results of this 0DTE Iron Condor options trading strategy suggest its potential for profitability but also highlight the need for careful risk management and a deep understanding of market dynamics. Further research and analysis, including backtesting across various market conditions, can help refine and optimize this strategy for better performance in the future.
If you enjoy my work, please support me on Medium by becoming a member through my referral link, and consider giving it a clap as a small gesture of motivation. Thank you!
Download the full source code and the colab notebook of this article from here
X (Twitter): https://x.com/diegodegese LinkedIn: https://www.linkedin.com/in/ddegese Github: https://github.com/crapher
Disclaimer: Investing in the stock market involves risk and may not be suitable for all investors. The information provided in this article is for educational purposes only and should not be construed as investment advice or a recommendation to buy or sell any particular security. Always do your own research and consult with a licensed financial advisor before making any investment decisions. Past performance is not indicative of future results.
Subscribe to DDIntel Here.
Have a unique story to share? Submit to DDIntel here.
Join our creator ecosystem here.
DDIntel captures the more notable pieces from our main site and our popular DDI Medium publication. Check us out for more insightful work from our community.
DDI Official Telegram Channel: https://t.me/+tafUp6ecEys4YjQ1






