How I Achieved Almost 85% Win Rate Using 0DTE Options and the Credit Spread Strategy on QQQ đ€Ș
Using Put Credit Spread and Call Credit Spread Strategies with Same-Day Expiration Options
On my ongoing journey to outperform the market, Iâm constantly cooking up fresh strategies to boost profits while dialing down the risks. One such recipe is trading 0DTE options with Credit Spreads, in this case with QQQ. In this article, weâre going to explore how this strategy works, exploring both its perks and pitfalls. Plus, weâll run a backtest analysis with data spanning from January 2022 to January 2024 to really see how this method behaves.
What Is a Credit Spread?
Before entering into the specifics of trading 0DTE options, itâs essential to understand the concept of credit spreads. Credit spreads are versatile options trading strategies that involve simultaneously selling and buying options contracts on the same underlying asset but with different strike prices. The primary goal of a credit spread is to generate a net credit (receive a premium) when initiating the trade.
Call Credit Spread (Bearish)
A Call Credit Spread is executed when a trader sells an out-of-the-money (OTM) call option and simultaneously buys a further OTM call option with a higher strike price. This strategy is employed when the trader expects the underlying assetâs price to remain below the lower strike price at expiration.
Example 1: Letâs say you are trading QQQ, which is currently priced at $400. You decide to implement a Call Credit Spread with the following components:
- Sell a QQQ Call Option with a strike price of $410 with a premium of $10.
- Buy a QQQ Call Option with a strike price of $420 with a premium of $5.
In this scenario, you receive a net credit of $5 ($10 premium received â $5 premium paid). Your maximum profit is limited to this credit, and you profit if QQQ closes below $410 at expiration.
Example 2: If QQQ closes above $420 at expiration, your maximum loss is capped at $5 (the difference between the two strike prices minus the net credit received).
Put Credit Spread (Bullish)
A Put Credit Spread, on the other hand, involves selling an OTM put option and simultaneously buying a further OTM put option with a lower strike price. This strategy is used when we anticipate that the underlying assetâs price will remain above the higher strike price at expiration.
Example 1 Suppose you are trading QQQ at $400 and decide to implement a Put Credit Spread with these components:
- Sell QQQ Put Option with a strike price of $390 with a premium of $8.
- Buy QQQ Put Option with a strike price of $380 with a premium of $4.
In this instance, you receive a net credit of $4 ($8 premium received â $4 premium paid). Your maximum profit is capped at this credit, and you profit if QQQ closes above $390 at expiration.
Example 2 If QQQ closes below $380 at expiration, your maximum loss is limited to $6 (the difference between the two strike prices minus the net credit received).
Advantages and Disadvantages of Credit Spreads
Credit spreads offer several advantages and disadvantages that we should consider before incorporating them into our trading strategies.
Advantages:
Limited Risk: Credit spreads limit the potential losses to a predetermined amount, making them a risk-controlled strategy. Traders can define their maximum loss upfront, providing peace of mind.
Income Generation: These spreads generate a net credit when initiated, providing traders with an immediate cash inflow. This can be especially appealing in low-volatility market environments.
Probability of Profit: Credit spreads often have a higher probability of profit compared to other strategies. Traders can profit even if the underlying assetâs price moves modestly in the anticipated direction.
Versatility: Credit spreads can be tailored to various market conditions, including bullish, bearish, or neutral outlooks.
Disadvantages:
Limited Profit Potential: While credit spreads offer limited risk, they also cap potential profits. Traders can only profit up to the net credit received.
Margin Requirements: Trading options, including credit spreads, may require traders to maintain a margin account, potentially tying up more capital than other strategies.
Assignment Risk: There is a risk of early assignment, particularly for American-style options. If the short leg of the spread is assigned early, it can lead to unexpected losses.
Commissions and Fees: Traders should account for transaction costs, including commissions and fees, which can impact the profitability of credit spreads.
Now that we have a solid understanding of credit spreads, letâs shift our focus to 0DTE options and how they can be integrated into this strategy.
What Does 0DTE Mean?
0DTE stands for â0 Days to Expiration.â It refers to options contracts that are set to expire at the close of the trading session on the same day they are opened. These options have no time value remaining and are entirely comprised of intrinsic value. They are also commonly known as âsame-day expirationâ or âsame-day expiryâ options.
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.
If you enjoy reading my articles, please donât forget to hit the follow button â Diego Degese
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.
Trading Credit Spreads on QQQ with 0DTE Options:
Combining credit spreads with 0DTE options can be an interesting strategy for traders looking to benefit from short-term price movements in the QQQ ETF. This approach leverages the limited risk profile of credit spreads with the rapid decay of 0DTE options to potentially achieve attractive risk-reward ratios.
Hereâs how the backtested strategy works:
Identify Market Direction: Before initiating the trade, traders should have a clear view of the expected direction of the QQQ ETF. Are they bullish, bearish, or neutral? This directional bias will guide whether to set up a Call Credit Spread or a Put Credit Spread.
Select Strike Prices: Choose the strike prices for the credit spread based on the anticipated price movement of QQQ within the same trading day. For a bullish outlook, youâd select put strike prices below the current QQQ price, and for a bearish view, youâd choose call strike prices above the current QQQ price.
Open the Credit Spread: Execute the credit spread by simultaneously selling the out-of-the-money (OTM) option and buying the further OTM option. This generates a net credit, which is your potential profit.
Wait Until the Market Closes: Once the position is open, we will wait until the end of the day to check the result.
If you enjoy reading my articles, please donât forget to hit the follow button â Diego Degese
Backtesting the Strategy
To check the effectiveness of trading credit spreads on QQQ with 0DTE options, we will perform a backtest using historical data from June 2021 to July 2023. In this analysis, we focused on both Call and Put Credit Spreads, evaluating their performance under different market conditions.
Letâs break down the code step by step:
Step 1: Importing Libraries
import warnings
import numpy as np
import pandas as pd
from tqdm import tqdmIn this step, we will import the necessary Python libraries:
warnings: This library is used to suppress warning messages.numpy(abbreviated asnp): A library for numerical and mathematical operations.pandas(abbreviated aspd): A library for data manipulation and analysis.tqdm: A library for adding progress bars to loops and iterations.
Step 2: Defining Constants and Configuration
# Constants
DEBUG = 1
OPTIONS_FILE='../data/qqq_dte_0.csv.gz'
FEES_PER_CONTRACT = 0.6
CONTRACTS = 10
HOUR_OPEN = 10
MINUTE_OPEN = 30
STRIKES = 1
DIST_BETWEEN_STRIKES = 2
MAX_CHANGE_BEARISH = -0.35
MIN_CHANGE_BULLISH = 0.35
### Configuration ###
warnings.filterwarnings("ignore")
np.set_printoptions(suppress=True)
pd.options.mode.chained_assignment = NoneIn this step, we will define various constants and configuration settings:
DEBUG: A flag to control whether debug messages will be printed (1 for enabled, 0 for disabled).OPTIONS_FILE: The path to the CSV file containing options data.FEES_PER_CONTRACT: The fees associated with each options contract.CONTRACTS: The number of contracts used in the strategy.HOUR_OPENandMINUTE_OPEN: The hour and minute at which the trading session opens.STRIKESandDIST_BETWEEN_STRIKES: Parameters used to determine strike prices for options.MAX_CHANGE_BEARISHandMIN_CHANGE_BULLISH: Maximum and minimum percentage changes are used to determine whether to open bearish or bullish trades.
The code also sets up some configurations for handling warnings and formatting output.
Step 3: Defining Helper Functions
def get_vertical_option_kind(df, max_change_bearish, min_change_bullish):
changes = 100 * (df['close_underlying'].values / df['close_underlying'].values[0] - 1)
change = changes[-1]
min_change = min(changes)
max_change = max(changes)
if change < max_change_bearish and max_change < min_change_bullish:
return 'C' # Sell Call Vertical
elif change > min_change_bullish and min_change > max_change_bearish:
return 'P' # Sell Put Vertical
else:
return ''
def get_vertical_strikes(df, kind, strikes, dist_between_strikes):
if kind == 'P':
sell_strike = int(df['close_underlying'].values[0]) - strikes
buy_strike = sell_strike - dist_between_strikes
else:
sell_strike = int(df['close_underlying'].values[0]) + strikes
buy_strike = sell_strike + dist_between_strikes
return (buy_strike, sell_strike)
def get_vertical_result(df, kind, buy_strike, sell_strike):
# Open Vertical
df = df.sort_values('date', ascending=True)
stock_price = df['close_underlying'].values[0]
buy_price = df[(df['strike'] == buy_strike) & (df['kind'] == kind)]['close'].values[0]
sell_price = df[(df['strike'] == sell_strike) & (df['kind'] == kind)]['close'].values[0]
open_cost = -100 * buy_price
open_cost += 100 * sell_price
open_cost *= CONTRACTS
if DEBUG:
tqdm.write(f' OPEN {kind} ({stock_price:6.2f}): SELL {sell_strike} @ {sell_price:6.2f} - BUY {buy_strike} @ {buy_price:6.2f} - TOTAL: {(-buy_price + sell_price):6.2f}')
# Close Vertical (Prices are 0 if they are OTM/ATM or the difference between the strike and the underlying's close price if they are ITM)
stock_price = df['close_underlying'].values[-1]
sell_price = 0 if (kind == 'P' and buy_strike <= stock_price) or (kind == 'C' and buy_strike >= stock_price) else abs(buy_strike - stock_price)
buy_price = 0 if (kind == 'P' and sell_strike <= stock_price) or (kind == 'C' and sell_strike >= stock_price) else abs(sell_strike - stock_price)
if DEBUG:
tqdm.write(f'CLOSE {kind} ({stock_price:6.2f}): BUY {sell_strike} @ {buy_price:6.2f} - SELL {buy_strike} @ {sell_price:6.2f} - TOTAL: {(-buy_price + sell_price):6.2f}')
close_cost = -100 * buy_price
close_cost += 100 * sell_price
close_cost *= CONTRACTS
if DEBUG:
tqdm.write(f'**************** Processed Date: {df["date"].dt.date.values[0]} - Result: {open_cost + close_cost:8.2f} ****************')
return open_cost + close_costThen. we define some helper functions that perform various calculations and operations related to the trading strategy. These functions include:
get_vertical_option_kind: Determines whether to open a Call or Put vertical spread based on changes in the underlying asset's price.get_vertical_strikes: Calculates the strike prices for the vertical spread.get_vertical_result: Calculates the result after opening and closing the vertical spread.
These functions play a crucial role in executing and evaluating the trading strategy.
Step 4: Reading the Data
### Read File ###
df = pd.read_csv(OPTIONS_FILE, header=0)
df['date'] = pd.to_datetime(df['date'])In this step, the code reads the options data from the CSV file specified in OPTIONS_FILE into a pandas DataFrame (df). It also converts the 'date' column to datetime format for date manipulation.
Step 5: Getting Expiration Dates
### Get all the expiration dates ###
expirations = df['expire_date'].unique().tolist()This code extracts all unique expiration dates from the DataFrame and stores them in a list called expirations. These dates represent the various expiration dates of the options contracts in the dataset.
Step 6: Calculating Profit and Loss (P&L) by Day
pnl = []
df_trend = df[(df.date.dt.hour < HOUR_OPEN) | ((df.date.dt.hour == HOUR_OPEN) & (df.date.dt.minute < MINUTE_OPEN))]
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:
kind = get_vertical_option_kind(df_trend[df_trend['expire_date'] == expiration], MAX_CHANGE_BEARISH, MIN_CHANGE_BULLISH)
if kind == '':
if DEBUG:
tqdm.write(f'**************** Processed Date: {expiration} - Result: -- NA -- ****************')
continue
(buy_strike, sell_strike) = get_vertical_strikes(df_open[df_open['expire_date'] == expiration], kind, STRIKES, DIST_BETWEEN_STRIKES)
result = get_vertical_result(df_cost[df_cost['expire_date'] == expiration], kind, buy_strike, sell_strike)
pnl.append([expiration, result])
except Exception as ex:
continueIn this part, the code iterates through each expiration date stored in expirations. Within the loop, it does the following:
- Calls the
get_vertical_option_kindfunction to determine whether to open a Call or Put vertical spread based on market conditions. - Calculates the strike prices for the vertical spread using the
get_vertical_strikesfunction. - Calculates the cost of opening and closing the vertical spread using the
get_vertical_costfunction.
The calculated costs and other information are appended to the pnl list.
Step 7: Generating 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 * 2 # 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)In this step, the code creates a DataFrame df_result to store the calculated results. It calculates the net result by subtracting fees for opening and closing the contracts. It also categorizes each result as a win or loss based on whether the net result is positive or negative.
Step 8: Displaying 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}')
print(f'* Distance Between Strikes: {DIST_BETWEEN_STRIKES}')
print(f'* Max % Change To Open A Bearish Trade: {MAX_CHANGE_BEARISH}')
print(f'* Min % Change To Open A Bullish Trade: {MIN_CHANGE_BULLISH}')
# 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)Finally, the code prints out the summarized results of the trading strategy, including parameters, total results, and monthly detailed results. These results provide insights into the performance of the strategy over the specified time.
Results
***************************** PARAMETERS *****************************
* Hour Open: 10
* Minute Open: 30
* Strikes From Price: 1
* Distance Between Strikes: 2
* Max % Change To Open A Bearish Trade: -0.35
* Min % Change To Open A Bullish Trade: 0.35
************************* SUMMARIZED RESULT **************************
* Trading Days: 106
* Gross PnL: $ 68310.00
* Net PnL: $ 67038.00
* Win Rate: 84.91 %
*********************** MONTHLY DETAIL RESULT ************************
gross_result net_result wins losses
year_month
2022-01 4530.0 4482.0 4 0
2022-02 4790.0 4718.0 6 0
2022-03 5250.0 5190.0 5 0
2022-04 3680.0 3620.0 5 0
2022-05 2020.0 1912.0 5 4
2022-06 110.0 86.0 1 1
2022-07 5600.0 5528.0 6 0
2022-08 1290.0 1254.0 2 1
2022-09 3030.0 2958.0 4 2
2022-10 3190.0 3130.0 5 0
2022-11 660.0 588.0 4 2
2022-12 4030.0 3958.0 6 0
2023-01 2070.0 2046.0 2 0
2023-02 6000.0 5928.0 6 0
2023-03 4810.0 4726.0 5 2
2023-04 2270.0 2234.0 3 0
2023-05 440.0 416.0 2 0
2023-06 -2100.0 -2136.0 0 3
2023-07 4590.0 4554.0 3 0
2023-08 830.0 782.0 3 1
2023-09 1700.0 1676.0 2 0
2023-10 3970.0 3910.0 5 0
2023-11 1100.0 1088.0 1 0
2023-12 2860.0 2824.0 3 0
2024-01 1590.0 1566.0 2 0The credit spread strategy outlined here showcases a robust approach to options trading with specific parameters. Letâs break it down:
Overall Profitability: Over 106 trading days, the strategy yielded a Gross Profit and Loss of $68,310 and a Net PnL of $67,038. This indicates a healthy overall profitability, considering the Net PnL accounts for any costs or fees associated with the trades.
Win Rate: The strategy achieved an impressive win rate of 84.91%. This high percentage suggests a strong likelihood of successful trades using this approach.
Monthly Performance: Monthly results varied, with months like March and July 2022, showing high profits. However, June 2023 saw a negative result, indicating losses. The consistency in monthly profits, except for one month, points towards a generally stable performance of the strategy.
Risk Management: The defined parameters for opening bearish or bullish trades based on percentage changes appear to have contributed to the strategyâs success.
Conclusion
In summary, this credit spread strategy, with its specific set of rules and parameters, has shown significant profitability and a high win rate over the analyzed period. The monthly performance indicates a generally stable strategy with occasional downturns, highlighting the importance of continual monitoring and risk management in options trading.
See you soon and Happy TradingâŠ
If you enjoy my work, please support me on Medium by becoming a member through my referral link, and consider giving it a clap (or many?) 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.
A Message from InsiderFinance

Thanks for being a part of our community! Before you go:
- đ Clap for the story and follow the author đ
- đ° View more content in the InsiderFinance Wire
- đ Take our FREE Masterclass
- đ Discover Powerful Trading Tools





