options-strategy-advisor
Options strategy analysis for Indian F&O markets (NSE). Use when user requests options strategy recommendations, P/L analysis, Greeks calculation, risk management, or F&O strategy planning for Nifty, Bank Nifty, or stock options.
Install
npx skills add https://github.com/ajeeshworkspace/indian-trading-skills/tree/master/skills/options-strategy-advisor
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install ajeeshworkspace-indian-trading-skills@llmmart
git clone https://github.com/ajeeshworkspace/indian-trading-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole ajeeshworkspace/indian-trading-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Options Strategy Advisor — Indian F&O Markets (NSE)
Overview
This skill provides comprehensive options strategy analysis tailored to the Indian Futures & Options market on the National Stock Exchange (NSE). It covers strategy selection, live data retrieval, margin estimation, profit/loss simulation, Greeks analysis, and risk management — all adapted for the specific characteristics of Indian F&O trading.
Indian F&O Market Characteristics
Exercise Style
- European-style exercise only. Options on NSE can only be exercised at expiry, not before. This simplifies pricing (Black-Scholes applies directly without early-exercise adjustments) and means time value is always fully captured by the seller until expiry.
Expiry Schedule
| Underlying | Expiry Day | Expiry Type |
|---|---|---|
| NIFTY | Thursday | Weekly + Monthly |
| BANK NIFTY | Wednesday | Weekly + Monthly |
| FINNIFTY | Tuesday | Weekly + Monthly |
| SENSEX (BSE) | Friday | Weekly + Monthly |
| Stock Options | Last Thursday | Monthly only |
- Monthly expiry is the last Thursday of the month (or preceding trading day if Thursday is a holiday).
- Weekly expiries are available only for index options, not individual stocks.
Lot Sizes
Lot sizes are periodically revised by the exchanges. Always verify current lot
sizes using the Groww MCP tool fno_mcx_contracts_search_tool before calculating
margin or position size. Recent reference values:
- NIFTY: 75 (recently changed — confirm via MCP)
- BANK NIFTY: 15 (recently changed — confirm via MCP)
- FINNIFTY: 25
- Stock options: Varies by stock (check contract specifications)
Margin Requirements
SEBI mandates the following margin components for F&O:
- SPAN Margin — Risk-based margin calculated by the exchange clearing corporation.
- Exposure Margin — Additional margin over SPAN for market-wide risk.
- Peak Margin — Intraday margin snapshots; brokers must collect at least the peak margin observed during the day.
Use calculate_fno_margin to get exact margin for any trade before placing it.
Transaction Costs
- STT (Securities Transaction Tax): Levied on the sell side of options at 0.0625% of the intrinsic value on exercise (for ITM options at expiry). For futures, STT is 0.0125% on sell side.
- Brokerage: Varies by broker (Groww charges per-order flat fees).
- Exchange charges, GST, SEBI turnover fee, stamp duty also apply.
F&O Ban Mechanism
When the market-wide position limit (MWPL) for a stock's F&O contracts exceeds 95%, SEBI places the stock under an F&O ban. During the ban:
- No new positions can be initiated.
- Only squaring off (closing) of existing positions is allowed.
- The ban is lifted when MWPL drops below 80%.
Broker MCP Tool Integration
This skill uses broker MCP tools for live market data and execution support. Use whichever broker is connected (Groww or Zerodha Kite). Always prefer live data over assumptions.
Groww MCP Tools (if connected)
| Tool | Purpose |
|---|---|
get_ltp (segment=FNO, query_type=fno) |
Live option/futures prices and OI |
get_quotes_and_depth (segment=FNO) |
Bid/ask spreads and market depth |
fno_mcx_contracts_search_tool |
Search F&O contracts, lot sizes, expiries |
fetch_historical_candle_data (segment=FNO) |
Historical option price data |
fetch_curated_fno |
F&O gainers, losers, most traded |
get_open_interest_analysis |
OI structure, PCR, support/resistance |
get_greeks_for_fno_contract |
Live Greeks for specific contracts |
get_greeks_for_fno_symbol |
Greeks for all contracts of an underlying |
get_atm_straddle_chart |
ATM straddle premium analysis |
get_payoff_chart_steps |
Payoff diagram generation instructions |
calculate_fno_margin |
Margin requirement calculation |
get_available_margin_details |
User's available margin |
resolve_market_time_and_calendar |
Market hours and trading calendar |
Zerodha Kite MCP Tools (if connected)
| Tool | Purpose |
|---|---|
get_ltp |
Last traded price for F&O instruments |
get_quotes |
Real-time quotes with bid/ask depth |
get_ohlc |
OHLC data for options/futures contracts |
get_historical_data |
Historical candle data for F&O |
search_instruments |
Search for F&O contracts by name/expiry |
get_margins |
Account margins and available funds |
get_positions |
Current F&O positions |
get_orders / get_order_history |
Order status and execution details |
place_order / modify_order / cancel_order |
Order management |
place_gtt_order / get_gtts |
GTT order management |
Tool Equivalence Map
| Action | Groww MCP | Zerodha Kite MCP |
|---|---|---|
| Live price | get_ltp |
get_ltp |
| Market depth | get_quotes_and_depth |
get_quotes |
| Historical data | fetch_historical_candle_data |
get_historical_data |
| Search contracts | fno_mcx_contracts_search_tool |
search_instruments |
| Margin check | calculate_fno_margin / get_available_margin_details |
get_margins |
| Positions | get_my_trading_positions_today |
get_positions |
| Place orders | place_fno_order |
place_order |
Supported Strategies
Income Strategies
Covered Call — Long underlying futures + Short OTM Call
- Objective: Generate income on existing long position.
- Best when: Mildly bullish, want to earn premium.
- Indian note: Use futures as underlying (no direct stock delivery for covered calls in F&O segment).
Cash-Secured Put — Short OTM Put (with margin set aside)
- Objective: Earn premium while waiting to buy at a lower price.
- Best when: Bullish on underlying, willing to take delivery equivalent.
- Indian note: Physical settlement applies for stock options (ITM at expiry).
Protection Strategies
Protective Put — Long underlying + Long Put
- Objective: Insure existing long position against downside.
- Best when: Want to cap losses while maintaining upside.
Collar — Long underlying + Long Put + Short Call
- Objective: Cap both upside and downside. Zero-cost collar if premiums offset.
- Best when: Want protection without paying net premium.
Directional Strategies
Bull Call Spread — Long lower-strike Call + Short higher-strike Call
- Objective: Limited-risk bullish bet.
- Best when: Moderately bullish, want defined risk.
Bear Put Spread — Long higher-strike Put + Short lower-strike Put
- Objective: Limited-risk bearish bet.
- Best when: Moderately bearish, want defined risk.
Bull Put Spread — Short higher-strike Put + Long lower-strike Put
- Objective: Credit spread, profit if price stays above short strike.
- Best when: Mildly bullish, want to collect premium.
Bear Call Spread — Short lower-strike Call + Long higher-strike Call
- Objective: Credit spread, profit if price stays below short strike.
- Best when: Mildly bearish, want to collect premium.
Volatility Strategies
Long Straddle — Long ATM Call + Long ATM Put
- Objective: Profit from large move in either direction.
- Best when: Expecting high volatility (e.g., pre-budget, RBI policy, earnings).
- Indian note: Popular before Union Budget day, election results, RBI MPC.
Short Straddle — Short ATM Call + Short ATM Put
- Objective: Profit from time decay when expecting range-bound movement.
- Best when: Low implied volatility expected, range-bound market.
- Indian note: Very popular on weekly expiry day for Nifty/Bank Nifty.
Long Strangle — Long OTM Call + Long OTM Put
- Objective: Cheaper alternative to straddle for volatility plays.
- Best when: Expecting very large move, want lower cost than straddle.
Short Strangle — Short OTM Call + Short OTM Put
- Objective: Wider profit zone than short straddle, less premium received.
- Best when: Expecting range-bound, comfortable with wider risk.
Range-Bound Strategies
Iron Condor — Bull Put Spread + Bear Call Spread
- Objective: Defined-risk range-bound strategy.
- Best when: Expecting low volatility, want defined max loss.
- Indian note: Very popular for weekly Nifty expiry plays.
Iron Butterfly — Short ATM Call + Short ATM Put + Long OTM Call + Long OTM Put
- Objective: Defined-risk version of short straddle.
- Best when: Expecting pin at a specific strike (max pain).
Advanced Strategies
Calendar Spread (Time Spread) — Short near-expiry option + Long far-expiry option (same strike)
- Objective: Profit from differential time decay.
- Best when: Expecting current expiry to decay faster, longer-term view intact.
- Indian note: Useful between weekly and monthly expiry cycles.
Diagonal Spread — Calendar spread with different strikes.
- Objective: Directional bias + time decay benefit.
- Best when: Have a directional view and want to finance via near-expiry sale.
Ratio Spread — Buy N options at one strike, sell M options at another (N != M).
- Objective: Reduce cost of directional trade; accept risk on extreme moves.
- Best when: Strong view on direction but want reduced cost.
- Caution: Naked leg creates unlimited risk on one side.
Workflow
Follow this sequence when advising on an options strategy:
Step 1: Gather Input
Collect the following from the user:
- Underlying: Which index or stock? (NIFTY, BANKNIFTY, FINNIFTY, or a specific stock)
- Market View: Bullish, bearish, neutral, volatile, or range-bound?
- Strategy Preference: Specific strategy or let the advisor recommend?
- Expiry: Weekly or monthly? Specific date?
- Risk Tolerance: Maximum loss acceptable? Capital available?
- Objective: Income generation, hedging, speculation, or volatility play?
Step 2: Fetch Live Data via Groww MCP
Resolve market time and calendar:
resolve_market_time_and_calendar() → confirm market is open, get trading days to expirySearch for contracts:
fno_mcx_contracts_search_tool(search_term="NIFTY 25 MAR") → get exact trading symbolsGet live prices:
get_ltp(search_queries=["nifty 24000 CE mar", "nifty 24000 PE mar"], segment="FNO", query_type="fno")Get Greeks:
get_greeks_for_fno_contract(search_queries=["nifty 24000 mar CE"], expiry="2026-03-26")Analyze Open Interest:
get_open_interest_analysis(symbol="NIFTY", view="all")Check ATM straddle premium (for volatility assessment):
get_atm_straddle_chart(symbol="NIFTY")
Step 3: Calculate Margin Requirement
For each leg of the strategy that involves selling (writing) options:
calculate_fno_margin(
trading_symbol="NIFTY25MAR24000CE",
num_lots=1,
transaction_type="SELL",
product="NRML"
)
Also check user's available margin:
get_available_margin_details()
Step 4: Simulate P/L Across Price Range
Use the scripts/black_scholes.py script or manual calculation:
- Define a price range (e.g., underlying +/- 5% from current price).
- For each price point, calculate P/L for each leg.
- Sum up P/L across all legs.
- Identify breakeven points, max profit, max loss.
Key calculations:
- Breakeven = Strike +/- Net Premium (for single-leg strategies)
- Max Profit = Net Premium Received (for credit strategies) or Strike Width - Net Debit (for debit spreads)
- Max Loss = Net Premium Paid (for debit strategies) or Strike Width - Net Credit (for credit spreads)
Step 5: Generate ASCII P/L Diagram
Create a visual payoff diagram showing:
- X-axis: Underlying price at expiry
- Y-axis: Profit/Loss per lot
- Breakeven point(s) marked
- Max profit and max loss zones labeled
Also use get_payoff_chart_steps() for Groww's built-in payoff chart generation.
Step 6: Provide Risk Management Guidance
Include in every recommendation:
- Position sizing: How many lots based on capital and risk tolerance.
- Stop-loss levels: When to exit (e.g., if loss exceeds 2x premium received).
- Adjustment triggers: When and how to adjust the strategy.
- Expiry management: Roll, close, or let expire — guidance based on ITM/OTM status.
- STT warning: Remind about STT on ITM options at expiry (can erode profits significantly).
- Margin monitoring: Warn about peak margin requirements and potential margin calls.
Step 7: Save Report
Present the complete analysis as a structured report:
=== OPTIONS STRATEGY REPORT ===
Date: [current date]
Underlying: [symbol] @ [current price]
Strategy: [strategy name]
Expiry: [expiry date] ([days to expiry] days)
--- LEGS ---
Leg 1: [BUY/SELL] [qty] [CALL/PUT] @ Strike [strike] for [premium]
Leg 2: [BUY/SELL] [qty] [CALL/PUT] @ Strike [strike] for [premium]
--- KEY METRICS ---
Net Premium: [debit/credit] [amount] per lot
Max Profit: [amount] per lot (at [price])
Max Loss: [amount] per lot (at [price])
Breakeven: [price(s)]
Risk-Reward Ratio: [ratio]
Probability of Profit: [estimate based on delta]
--- GREEKS (NET POSITION) ---
Delta: [value] | Gamma: [value] | Theta: [value] | Vega: [value]
--- MARGIN REQUIREMENT ---
Total Margin: [amount]
Available Margin: [amount]
Margin Utilization: [percentage]
--- P/L DIAGRAM ---
[ASCII payoff chart]
--- RISK MANAGEMENT ---
- Stop Loss: [criteria]
- Adjustment Plan: [when and how]
- Expiry Action: [recommendation]
- STT Impact: [if applicable]
Strategy Selection Guide
Use this decision tree to recommend strategies based on user's market view:
Bullish View
- Strong bullish: Long Call or Bull Call Spread
- Mildly bullish: Bull Put Spread (credit) or Covered Call
- Bullish + high IV: Bull Put Spread (sell expensive puts)
- Bullish + low IV: Long Call or Bull Call Spread (buy cheap options)
Bearish View
- Strong bearish: Long Put or Bear Put Spread
- Mildly bearish: Bear Call Spread (credit)
- Bearish + high IV: Bear Call Spread (sell expensive calls)
- Bearish + low IV: Long Put or Bear Put Spread
Neutral / Range-Bound View
- Tight range expected: Short Straddle or Iron Butterfly
- Wider range expected: Short Strangle or Iron Condor
- Neutral + want defined risk: Iron Condor or Iron Butterfly
Volatile View (Expecting Big Move)
- Direction unknown, big move expected: Long Straddle
- Direction unknown, very big move expected: Long Strangle (cheaper)
- Pre-event (budget, RBI, earnings): Long Straddle or Long Strangle
Time Decay Play
- Near-term decay focus: Calendar Spread
- Directional + decay: Diagonal Spread
Important Indian Market Considerations
India VIX
- India VIX measures the market's expectation of 30-day volatility.
- VIX > 20: High volatility environment — favor long volatility strategies.
- VIX < 15: Low volatility environment — favor short volatility strategies.
- VIX between 15-20: Normal range — use directional or range-bound strategies.
- VIX typically spikes before elections, budgets, RBI policy, and global crises.
Weekly Expiry Trading
- Thursday (Nifty): Most liquid expiry. Short straddle/strangle sellers dominate. Theta decay is highest on the expiry day.
- Wednesday (Bank Nifty): High gamma risk. Moves can be sharp near expiry.
- Premium sellers should be cautious of gamma risk on expiry day — a small move in the underlying can cause large P/L swings.
Physical Settlement (Stock Options)
- Stock options that expire ITM are physically settled — actual delivery of shares.
- This requires full delivery margin (value of shares). Plan exits before expiry to avoid unexpected margin requirements.
- Index options are cash-settled — no delivery concerns.
Max Pain
- Max Pain is the strike price at which the maximum number of options (calls + puts) expire worthless, causing minimum payout by option writers.
- Indian markets tend to gravitate toward max pain on expiry day, especially for Nifty weekly expiry.
- Use OI analysis to identify max pain and position accordingly.
OI-Based Analysis
- High Call OI at a strike: Acts as resistance. Call writers are betting the price won't cross this level.
- High Put OI at a strike: Acts as support. Put writers are betting the price won't fall below this level.
- PCR (Put-Call Ratio):
- PCR > 1.2: Bullish signal (more puts written, indicating support)
- PCR < 0.8: Bearish signal (more calls written, indicating resistance)
- PCR between 0.8-1.2: Neutral
Error Handling
- If broker MCP tools (Groww or Zerodha) return errors, inform the user and suggest checking market hours or contract availability. Try the alternative broker's equivalent tool if available.
- If a contract search yields no results, try alternative search terms or check if the expiry has passed.
- If margin data is unavailable, provide theoretical estimates with a disclaimer.
- Always validate that the market is open before fetching live data — use
resolve_market_time_and_calendar(). - If a stock is under F&O ban, alert the user immediately and suggest alternative underlyings.
Files (indian-trading-skills)
-
references
-
indian_fno_guide.md 32.1 KB
# Indian F&O Market Reference Guide A comprehensive reference for options strategy advisors working with NSE Futures & Options. This document covers market structure, regulations, costs, and practical trading considerations specific to the Indian derivatives market. --- ## Table of Contents 1. [NSE F&O Market Structure](#1-nse-fo-market-structure) 2. [SEBI Margin Framework](#2-sebi-margin-framework) 3. [STT and Transaction Charges](#3-stt-and-transaction-charges) 4. [European vs American Exercise](#4-european-vs-american-exercise) 5. [Weekly vs Monthly Expiry Strategies](#5-weekly-vs-monthly-expiry-strategies) 6. [OI-Based Support and Resistance](#6-oi-based-support-and-resistance) 7. [Max Pain Theory for Indian Expiry](#7-max-pain-theory-for-indian-expiry) 8. [India VIX and Option Premiums](#8-india-vix-and-option-premiums) 9. [F&O Ban Mechanism](#9-fo-ban-mechanism) 10. [Common Strategies for Indian Traders](#10-common-strategies-for-indian-traders) 11. [Position Sizing for Indian F&O](#11-position-sizing-for-indian-fo) 12. [Rollover Strategies Near Expiry](#12-rollover-strategies-near-expiry) --- ## 1. NSE F&O Market Structure ### Contract Specifications #### Index Options | Parameter | NIFTY 50 | BANK NIFTY | FINNIFTY | MIDCAP NIFTY | |-----------------|-----------------|-----------------|-----------------|-----------------| | Lot Size | 75 (verify) | 15 (verify) | 25 | 50 | | Tick Size | 0.05 | 0.05 | 0.05 | 0.05 | | Strike Interval | 50 | 100 | 50 | 25 | | Expiry | Weekly (Thu) | Weekly (Wed) | Weekly (Tue) | Monthly (Mon) | | Settlement | Cash | Cash | Cash | Cash | | Exercise | European | European | European | European | Note: Lot sizes are revised periodically by the exchange. Always verify the current lot size using `fno_mcx_contracts_search_tool` before trading. #### Stock Options | Parameter | Value | |-----------------|------------------------------------------------| | Lot Size | Varies by stock (set by exchange) | | Tick Size | 0.05 | | Strike Interval | Varies by stock price level | | Expiry | Monthly only (last Thursday of the month) | | Settlement | Physical delivery (shares change hands) | | Exercise | European | #### Futures Contracts | Parameter | Index Futures | Stock Futures | |-----------------|-----------------|-----------------| | Contract Months | Near, Next, Far | Near, Next, Far | | Lot Size | Same as options | Same as options | | Settlement | Cash (daily MTM)| Physical (at expiry) | | Last Trading Day| Last Thursday of expiry month | ### Trading Hours | Session | Time (IST) | |-----------------|------------------| | Pre-Open | 09:00 - 09:08 | | Normal Trading | 09:15 - 15:30 | | Post-Close | 15:40 - 16:00 | ### Expiry Schedule - **NIFTY:** Every Thursday (weekly) + last Thursday of month (monthly) - **BANK NIFTY:** Every Wednesday (weekly) + last Wednesday of month (monthly) - **FINNIFTY:** Every Tuesday (weekly) - **SENSEX (BSE):** Every Friday (weekly) - **Stock Options:** Last Thursday of month only - **Stock Futures:** Last Thursday of month If the scheduled expiry day is a trading holiday, the expiry moves to the preceding trading day. ### Available Strikes The exchange lists strikes around the current spot price in predefined intervals. As the underlying moves, new strikes are added. Strikes well away from the current price (deep OTM or deep ITM) may have very low liquidity. --- ## 2. SEBI Margin Framework SEBI's margin framework for F&O trading consists of multiple components designed to cover different types of risk. ### Margin Components #### 1. SPAN Margin (Initial Margin) SPAN (Standard Portfolio Analysis of Risk) calculates the worst-case loss of a portfolio over a one-day time horizon. It considers: - Price scanning range (based on volatility) - Up and down movements in the underlying - Changes in volatility - Time decay - Correlation between instruments (for portfolio margining) SPAN margin is recalculated multiple times during the trading day. #### 2. Exposure Margin An additional margin collected over and above SPAN to cover risks not captured by SPAN: - **Index derivatives:** 3% of the notional value of the contract - **Stock derivatives:** Higher of 5% or 1.5 standard deviations of the logarithmic returns of the stock over the last 6 months #### 3. Premium Margin (for Option Buyers) Option buyers pay the full premium upfront. This is not a margin in the traditional sense but the cost of the option position. #### 4. Assignment Margin Collected from option sellers who are assigned. Covers the settlement obligation. ### Peak Margin Requirement Since September 2021, SEBI mandates **peak margin** collection: - Brokers must collect margins based on the peak (highest) margin required during the trading day, not just the end-of-day margin. - The clearing corporation takes at least 4 random snapshots during the day. - If a client's margin falls below the peak requirement, a penalty is levied. - This means intraday positions also require full margin. ### Margin for Multi-Leg Strategies For hedged positions (spreads, iron condors, etc.), SPAN recognizes the hedge benefit and reduces the margin requirement compared to naked positions. However, the margin benefit is only available if both legs are in the same expiry. For example: - **Naked short NIFTY call:** Requires full SPAN + exposure margin (often Rs. 1,00,000+ per lot). - **Bear Call Spread (short call + long call):** Margin is significantly lower because the long call limits the maximum loss. Use `calculate_fno_margin` to get the exact margin for any combination. ### Margin Penalty If the client's margin falls short of the required margin: | Shortfall | Penalty | |--------------------------|------------------------------| | Up to 10% of required | 0.5% per day of shortfall | | More than 10% | 1.0% per day of shortfall | Penalties are levied per day and compounded for repeat offenses within a month. --- ## 3. STT and Transaction Charges ### Securities Transaction Tax (STT) STT is a tax levied by the government on securities transactions. | Transaction | STT Rate | Levied On | |---------------------------|-----------------------------|------------------| | Futures (sell) | 0.0125% | Sell turnover | | Options (sell) | 0.0625% | Sell premium | | Options (exercise at expiry, ITM) | 0.0625% | Intrinsic value | **Critical STT Trap for Options:** When an option expires in-the-money (ITM), STT is levied on the entire intrinsic value (settlement value), not just the premium. This can result in STT exceeding the actual profit. Example: - You sell a NIFTY 24000 CE at Rs. 5 (premium received: 5 x 75 = Rs. 375). - NIFTY expires at 24010 (option is ITM by Rs. 10). - Intrinsic value on exercise: 10 x 75 = Rs. 750. - STT on exercise: 0.0625% of Rs. 750 = Rs. 0.47 per share = Rs. 35.16. - But if NIFTY expires at 24200, intrinsic value = 200 x 75 = Rs. 15,000. - STT = 0.0625% of Rs. 15,000 = Rs. 9.38. For deep ITM options at expiry, this tax can be substantial. Always square off ITM positions before expiry to avoid the exercise-based STT. ### Other Transaction Charges | Charge | Rate (approximate) | |---------------------------|-----------------------------| | Exchange Transaction Fee | ~0.05% (NSE) | | SEBI Turnover Fee | Rs. 10 per crore | | GST | 18% on brokerage + fees | | Stamp Duty | 0.003% (buy side) for options| ### Total Cost Per Trade (Approximate) For a typical NIFTY option trade (1 lot, premium Rs. 100): - Brokerage: Rs. 20 (flat per order, typical discount broker) - STT: Rs. 4.69 (on sell) - Exchange charges: Rs. 3.97 - GST: Rs. 4.31 - SEBI fee: Rs. 0.08 - Stamp duty: Rs. 0.23 - **Total: approximately Rs. 33 per side** --- ## 4. European vs American Exercise ### All NSE Options Are European Since 2010, all options on NSE (both index and stock) are European-style. This means: - **Cannot exercise before expiry.** The option can only be exercised (or is auto-exercised) at expiry. - **No early exercise risk for sellers.** Sellers do not need to worry about being assigned before expiry. - **Time value is always retained.** Unlike American options where early exercise can strip time value, European options always trade with full time value intact. ### Implications for Pricing - **Black-Scholes model applies directly.** No need for binomial trees or American option adjustments. The closed-form Black-Scholes formula is exact for European options. - **Put-Call Parity holds exactly:** ``` Call - Put = S * exp(-q*T) - K * exp(-r*T) ``` where S = spot, K = strike, r = risk-free rate, q = dividend yield, T = time. - **No early exercise premium.** American options sometimes carry a premium (especially deep ITM puts) because of the early exercise feature. European options have no such premium. ### Implications for Strategies - **Covered call sellers:** No risk of early assignment. The short call will only be exercised at expiry. - **Calendar spreads:** Work cleanly because both legs follow European rules. - **Deep ITM options:** May trade at a slight discount to intrinsic value (unlike American options which always trade at or above intrinsic). This is because you cannot exercise early to capture the intrinsic value. --- ## 5. Weekly vs Monthly Expiry Strategies ### Weekly Expiry Characteristics | Feature | Weekly | Monthly | |----------------------|---------------------------|----------------------------| | Time to Expiry | 0-7 days | 0-30+ days | | Theta Decay | Very rapid (accelerating) | Gradual (more predictable) | | Gamma Risk | Extremely high near expiry| Moderate | | Premium | Lower (less time value) | Higher | | Liquidity | High for index options | High for all | | Available For | Index options only | All stocks and indices | ### Weekly Expiry Strategies #### 1. Short Straddle / Strangle on Expiry Day The most popular strategy among Indian traders for weekly expiry: - Sell ATM straddle or OTM strangle on the expiry morning (or 1-2 days before). - Benefit from accelerated theta decay in the final hours. - Risk: Gamma is extremely high, meaning a small move in the underlying causes a large change in delta and hence P&L. **Typical approach:** - Sell straddle at 9:15 AM on expiry day. - Set stop-loss at 1.5x to 2x of premium received. - Close position by 2:30 PM or let expire. - Monitor continuously for sharp moves. #### 2. Iron Condor for Range Days - Deploy iron condor 1-2 days before expiry. - Choose strikes based on expected range (use OI data for support/resistance). - Benefit from rapid decay with defined risk. #### 3. Directional Plays with Weekly Options - Buy weekly OTM options when expecting a large move (event-based). - Very cheap premium but high risk of total loss. - Best used for asymmetric bets (small risk, large potential reward). ### Monthly Expiry Strategies #### 1. Positional Strategies - Deploy strategies 15-25 days before monthly expiry. - Allow time for the thesis to play out. - More stable Greeks compared to weekly options. #### 2. Earnings-Based Plays - Stock options are monthly only, so earnings plays use monthly expiry. - Long straddle/strangle before earnings announcement. - Short straddle/strangle after earnings if IV crush is expected. #### 3. Rollover Period Strategies - In the last week before monthly expiry, rollover activity creates opportunities. - Open interest shifts from current month to next month. - Basis (difference between futures and spot) widens or narrows, creating spread trading opportunities. --- ## 6. OI-Based Support and Resistance ### Interpreting Open Interest Open Interest (OI) represents the total number of outstanding derivative contracts that have not been settled. #### Call OI = Resistance When a large amount of call OI builds up at a particular strike, it suggests that: - Many traders have sold (written) calls at that strike. - These writers will actively defend their position by selling the underlying near that level. - This creates overhead resistance for the underlying. Example: If NIFTY 24500 CE has the highest call OI, 24500 is likely to act as resistance. #### Put OI = Support When a large amount of put OI builds up at a particular strike: - Many traders have sold (written) puts at that strike. - These writers will defend their position by buying the underlying near that level. - This creates support for the underlying. Example: If NIFTY 23500 PE has the highest put OI, 23500 is likely to act as support. ### Put-Call Ratio (PCR) PCR = Total Put OI / Total Call OI | PCR Range | Interpretation | |---------------|-------------------------------------------------------| | PCR > 1.3 | Very bullish (excessive put writing = strong support) | | 1.1 - 1.3 | Moderately bullish | | 0.9 - 1.1 | Neutral | | 0.7 - 0.9 | Moderately bearish | | PCR < 0.7 | Very bearish (excessive call writing = strong ceiling) | **Contrarian interpretation:** Extreme PCR values can also signal reversals: - Very high PCR (> 1.5): Market may be over-hedged, potential for short covering rally. - Very low PCR (< 0.5): Market may be complacent, potential for downside. ### Change in OI Analysis More informative than absolute OI is the change in OI during the day: | Price Move | OI Change | Interpretation | |------------|-----------|----------------------| | Up | Increase | Long buildup (bullish)| | Up | Decrease | Short covering (less bullish) | | Down | Increase | Short buildup (bearish)| | Down | Decrease | Long unwinding (less bearish)| Use `get_open_interest_analysis` with `view='change_in_oi'` to get intraday OI change data. ### Practical OI Analysis Workflow 1. Call `get_open_interest_analysis(symbol="NIFTY", view="all")` to get full picture. 2. Identify the strikes with highest call OI (resistance) and put OI (support). 3. Check PCR for directional bias. 4. Use `view='change_in_oi'` to see if OI is building or unwinding during the day. 5. Combine with price action and VIX for strategy selection. --- ## 7. Max Pain Theory for Indian Expiry ### What is Max Pain? Max Pain is the strike price at which the maximum number of options contracts (calls + puts combined) expire worthless. At this point, option buyers lose the most money and option sellers (writers) pay out the least. ### Calculation For each strike price: 1. Calculate the total value of all call options that would be ITM if the underlying settled at that strike. 2. Calculate the total value of all put options that would be ITM if the underlying settled at that strike. 3. Add call pain + put pain = total pain at that strike. 4. The strike with the minimum total pain is the Max Pain strike. ### Max Pain in Indian Markets Max Pain is particularly relevant for Indian weekly expiry because: 1. **Large option writing community:** India has a massive number of retail option sellers. These sellers collectively have an interest in the underlying settling near max pain. 2. **Weekly expiry concentration:** A disproportionate amount of OI is concentrated in weekly options, making max pain more influential. 3. **Hedging activity by institutional writers:** When the underlying moves away from max pain, institutional option sellers hedge by buying/selling the underlying or futures, which pushes the price back toward max pain. 4. **Empirical observation:** NIFTY weekly expiry often settles within 50-100 points of the max pain strike, especially on low-volatility days. ### Trading with Max Pain - **Before expiry (2-3 days):** If the underlying is far from max pain, expect a gravitational pull toward max pain. - **On expiry day:** If the underlying is near max pain, deploy range-bound strategies (iron condor, short strangle). - **Deviation from max pain:** Strong trend days or event-driven moves can override max pain. Do not rely on max pain alone. ### Limitations - Max pain shifts throughout the week as OI changes. - On high-volatility days, max pain is less reliable. - It is a tendency, not a certainty. Use it as one data point among many. --- ## 8. India VIX and Option Premiums ### What is India VIX? India VIX (Volatility Index) measures the market's expectation of volatility over the next 30 calendar days. It is computed from NIFTY option prices using a model-free approach similar to the CBOE VIX methodology. - **India VIX is expressed as an annualized percentage.** - India VIX of 15 means the market expects NIFTY to move approximately 15% / sqrt(12) = ~4.3% over the next month. - For a daily expected move: India VIX / sqrt(252). ### VIX Levels and Interpretation | VIX Level | Market Regime | Strategy Implications | |-----------|------------------|-------------------------------------------| | < 12 | Very low vol | Sell options cautiously; premiums are thin | | 12 - 15 | Low vol | Favor selling strategies (iron condor) | | 15 - 20 | Normal | Balanced approach; both buying and selling | | 20 - 25 | Elevated | Premiums are rich; selling is attractive | | 25 - 35 | High vol | Selling is lucrative but risky; wide stops | | > 35 | Crisis/Panic | Extreme caution; only defined-risk trades | ### VIX-Premium Relationship - **VIX rising:** Option premiums increase across all strikes. Sellers receive more premium but face higher risk. Buyers pay more but have higher probability of a profitable move. - **VIX falling (IV crush):** Option premiums shrink. This is painful for option buyers and beneficial for sellers. ### Common VIX Patterns in India 1. **Pre-budget spike:** VIX typically rises 5-10 points in the 2-3 days before the Union Budget and crashes 3-5 points immediately after. 2. **Pre-election volatility:** VIX can spike to 25-35 before general election results and crash dramatically after results. 3. **RBI MPC meetings:** Moderate VIX increase before policy decisions, quick normalization after. 4. **Global events:** VIX correlates with global volatility (US VIX, geopolitical events). ### Using VIX for Strategy Selection - **VIX > 20 and you are selling options:** Collect premium but use defined-risk strategies (iron condor, spreads) rather than naked selling. - **VIX < 13 and you are buying options:** Premiums are cheap; good for directional bets or straddles before expected events. - **VIX dropping sharply:** If you have long option positions, close them quickly to avoid IV crush losses. - **VIX at extremes:** Mean reversion is likely. VIX above 30 tends to revert; VIX below 10 tends to rise. --- ## 9. F&O Ban Mechanism ### What is F&O Ban? When the aggregate open interest in all F&O contracts of a particular stock exceeds 95% of the Market-Wide Position Limit (MWPL), the stock enters an F&O ban period. ### MWPL Calculation MWPL = 20% of the free-float market capitalization of the stock (expressed in number of shares and then converted to the nearest lot size). ### Ban Rules 1. **Entry into ban:** OI > 95% of MWPL. The exchange announces the ban, effective from the next trading day. 2. **During ban:** - No new F&O positions can be created (no fresh buying or selling). - Only squaring off (closing) of existing positions is allowed. - Any order that would increase OI is rejected by the exchange. 3. **Exit from ban:** OI drops below 80% of MWPL. The ban is lifted from the next trading day. 4. **Penalty for violating ban:** If a trader increases positions during the ban period, a penalty of 1% of the value of the increased position per day is levied (minimum Rs. 5,000 to maximum Rs. 1,00,000). ### Stocks Commonly Under F&O Ban Stocks with lower free-float capitalization or those experiencing speculative activity are more prone to F&O bans. Examples include mid-cap stocks with active F&O trading. ### Impact on Strategies - **Before ban:** If OI is approaching 90% MWPL, be cautious about entering new positions. Existing positions can become illiquid. - **During ban:** You can only exit. This may lead to wider bid-ask spreads and difficulty closing at fair prices. - **Approaching ban exit:** As OI decreases toward 80%, traders start to position for the ban lift, which can create trading opportunities. ### Monitoring Always check if a stock is near the ban threshold before deploying strategies. The exchanges publish daily MWPL utilization reports. Groww MCP tools can help identify stocks in or near ban. --- ## 10. Common Strategies for Indian Traders ### Strategy 1: Nifty Short Straddle on Thursday Expiry **Setup:** - Sell NIFTY ATM CE + ATM PE on Thursday morning (expiry day). - Or deploy 1-2 days before expiry for more premium. **Rationale:** - Theta decay is fastest on expiry day. - NIFTY tends to settle near max pain on non-event days. **Risk Management:** - Stop-loss at 1.5x-2x premium received on either side. - Adjust by adding a hedge leg (convert to strangle by shifting the losing side). - Maximum loss should not exceed 3% of capital. **Capital Required:** SPAN + Exposure margin for 2 naked option legs (typically Rs. 1,50,000 - Rs. 2,00,000 per lot for the straddle). ### Strategy 2: Bank Nifty Iron Condor on Wednesday Expiry **Setup:** - Sell OTM Call + Sell OTM Put. - Buy further OTM Call + Buy further OTM Put (wings for protection). - Deploy 1-2 days before Wednesday expiry. **Rationale:** - Bank Nifty is more volatile than Nifty, so premiums are richer. - Iron condor provides defined risk unlike naked straddles. **Risk Management:** - Max loss is limited to (strike width - net credit) x lot size. - Exit if underlying breaches the short strikes. **Capital Required:** Reduced margin due to hedged position (typically Rs. 50,000 - Rs. 80,000 per lot). ### Strategy 3: Event-Day Long Straddle (Budget/RBI/Elections) **Setup:** - Buy ATM Straddle 2-3 days before the event. - Choose monthly expiry to avoid extreme theta decay. **Rationale:** - VIX spikes before events, but the actual move can exceed the premium paid. - Elections and budgets can cause 2-5% moves in Nifty. **Risk Management:** - Maximum loss is the total premium paid. - Set a target of 1.5x-2x premium paid for exit. - Close immediately after the event (within the first hour) to capture the move before IV crush sets in. **Capital Required:** Premium paid upfront (no margin since buyer). ### Strategy 4: Covered Call with Futures **Setup:** - Buy 1 lot of NIFTY futures. - Sell 1 lot of OTM NIFTY call option (same expiry). **Rationale:** - Generate income on the long futures position. - OTM call sold provides a buffer against small declines. **Risk Management:** - Downside risk is the futures P/L minus the call premium received. - Roll the short call to the next expiry if not exercised. **Capital Required:** Futures margin + reduced margin for the covered call (hedge benefit). ### Strategy 5: Calendar Spread (Weekly-to-Monthly) **Setup:** - Sell current week ATM option (weekly expiry). - Buy next month ATM option (monthly expiry). **Rationale:** - Near-week option decays faster than the far-month option. - Net theta is positive (earning from time decay difference). **Risk Management:** - If the underlying moves sharply, both legs move similarly and net P/L is small. - Close the short leg at expiry and either close the long leg or sell a new short leg against it. **Capital Required:** The net debit of the spread plus any margin requirement for the short leg. --- ## 11. Position Sizing for Indian F&O ### Per-Lot Risk Approach The safest approach to position sizing is to calculate risk per lot and size positions based on a fixed percentage of capital at risk. #### Step-by-Step Process 1. **Determine total trading capital** allocated to F&O. 2. **Set maximum risk per trade:** 1-2% of total capital for beginners, 3-5% for experienced traders. 3. **Calculate risk per lot** for the chosen strategy: - For defined-risk strategies (spreads, iron condors): Max loss = (strike width - net credit) x lot size. - For undefined-risk strategies (naked options): Use the stop-loss amount as the risk per lot. 4. **Number of lots = Maximum risk per trade / Risk per lot.** #### Example - Trading capital: Rs. 10,00,000 - Maximum risk per trade: 2% = Rs. 20,000 - Strategy: Nifty Iron Condor - Short put: 23800, Long put: 23500 (width = 300) - Short call: 24200, Long call: 24500 (width = 300) - Net credit: Rs. 80 per share - Max loss per lot: (300 - 80) x 75 = Rs. 16,500 - Number of lots: 20,000 / 16,500 = 1.21 -> trade 1 lot. ### Capital Allocation Guidelines | Capital Range | Recommended Approach | |-----------------------|------------------------------------------------| | < Rs. 2,00,000 | Only buy options (no writing). 1-2 lots max. | | Rs. 2-5,00,000 | Defined-risk strategies (spreads). 1-3 lots. | | Rs. 5-15,00,000 | Mixed strategies. Allocate 60% to defined-risk.| | Rs. 15-50,00,000 | Full strategy repertoire. Use portfolio margin.| | > Rs. 50,00,000 | Multi-strategy portfolios. Diversify across underlyings.| ### Diversification Rules - Do not allocate more than 30% of capital to a single underlying. - Do not allocate more than 50% of capital to a single strategy type. - Maintain at least 20% of capital as cash buffer for margin calls and adjustments. - For undefined-risk positions, the margin requirement itself limits position size. ### Scaling Positions - Start with 1 lot. Add lots only after the position moves in your favor. - Never average down on a losing options position (especially for buyers). - For sellers, consider adding lots at different strikes (laddering) rather than doubling at the same strike. --- ## 12. Rollover Strategies Near Expiry ### What is Rollover? Rollover is the process of closing a position in the expiring contract and simultaneously opening the same (or similar) position in the next expiry contract. This allows traders to maintain their market exposure without physical settlement or contract expiry. ### When to Roll | Scenario | Action | |---------------------------------|-------------------------------------------| | Profitable position, view intact | Roll to next expiry to extend duration | | Losing position, view still valid| Roll and adjust (different strike/strategy)| | At-the-money near expiry | Roll to avoid gamma risk and STT on exercise| | Deep OTM with no value | Let expire worthless (no need to roll) | | Deep ITM near expiry (stocks) | Roll to avoid physical settlement | ### Rollover Mechanics #### For Index Options (Cash Settled) 1. Close the current expiry position. 2. Open the equivalent position in the next expiry. 3. The difference in premium between the two expiries is the rollover cost (debit) or credit. #### For Stock Options (Physically Settled) 1. Must close before expiry to avoid physical delivery and associated margins. 2. Stock ITM options at expiry trigger physical settlement, requiring full delivery margin (entire stock value x lot size). 3. Close at least 2-3 days before expiry to avoid margin issues. ### Rollover Cost Analysis The rollover cost depends on: - **Time value of the new contract:** More time = more expensive. - **Changes in implied volatility:** If IV has risen, the new contract is more expensive; if IV has fallen, it may be cheaper. - **Interest rate component:** Far-month contracts embed higher carry cost. - **Dividends:** Expected dividends reduce call premiums and increase put premiums in the far-month contract. ### Roll Strategies #### 1. Straight Roll (Same Strike, Next Expiry) - Close current month position. - Open same strike, same type in next month. - Simplest but may not be optimal if the underlying has moved significantly. #### 2. Roll and Adjust (Different Strike) - Close current position. - Open at a different strike in next month that better reflects the current market conditions and your updated view. - Example: If you sold NIFTY 24500 CE and NIFTY has risen to 24400, roll to selling NIFTY 25000 CE in the next month. #### 3. Roll Up / Roll Down - **Roll Up (for calls):** Close lower-strike call, open higher-strike call. Used when the underlying has moved up and you want to capture more upside in a covered call strategy. - **Roll Down (for puts):** Close higher-strike put, open lower-strike put. Used when the underlying has fallen and you want to adjust your support level. #### 4. Roll and Widen (for Spreads) - Close the current spread. - Open a wider spread in the next month. - Captures more premium but increases maximum risk. ### Rollover Timing - **Ideal rollover window:** 3-5 days before expiry for monthly contracts; 1-2 days for weekly contracts. - **Liquidity consideration:** Rollover too close to expiry results in wide bid-ask spreads in the expiring contract. Rollover too early means you miss out on theta decay in the current contract. - **Cost consideration:** The rollover cost (net debit/credit) should be factored into the overall strategy P/L. ### Rollover Ratio (Market Indicator) The rollover ratio for Nifty and Bank Nifty futures is a widely watched market indicator: - **High rollover (> 75%):** Strong conviction; traders are carrying positions forward. Bullish if long rollover, bearish if short rollover. - **Low rollover (< 60%):** Lack of conviction. Traders are squaring off rather than rolling forward. Can signal a trend change. - **Rollover with basis:** If the basis (futures premium over spot) increases during rollover, it signals bullish sentiment. If the basis decreases (or becomes negative/backwardation), it signals bearish sentiment. --- ## Appendix: Quick Reference Formulas ### Black-Scholes (European Options) ``` d1 = [ln(S/K) + (r - q + sigma^2/2) * T] / (sigma * sqrt(T)) d2 = d1 - sigma * sqrt(T) Call = S * exp(-q*T) * N(d1) - K * exp(-r*T) * N(d2) Put = K * exp(-r*T) * N(-d2) - S * exp(-q*T) * N(-d1) ``` Where: - S = Spot price - K = Strike price - T = Time to expiry (in years) - r = Risk-free rate (India 91-day T-bill, ~6.5-7%) - q = Dividend yield - sigma = Annualized volatility - N() = Cumulative standard normal distribution ### Greeks | Greek | Call | Put | |-------|-----------------------------------|------------------------------------| | Delta | exp(-qT) * N(d1) | exp(-qT) * [N(d1) - 1] | | Gamma | exp(-qT) * n(d1) / (S*sigma*sqrt(T)) | Same as Call | | Theta | -(S*exp(-qT)*n(d1)*sigma)/(2*sqrt(T)) - r*K*exp(-rT)*N(d2) + q*S*exp(-qT)*N(d1) | -(S*exp(-qT)*n(d1)*sigma)/(2*sqrt(T)) + r*K*exp(-rT)*N(-d2) - q*S*exp(-qT)*N(-d1) | | Vega | S * exp(-qT) * n(d1) * sqrt(T) | Same as Call | | Rho | K * T * exp(-rT) * N(d2) | -K * T * exp(-rT) * N(-d2) | Where n() = standard normal PDF. ### Put-Call Parity (European) ``` Call - Put = S * exp(-q*T) - K * exp(-r*T) ``` ### Expected Daily Move ``` Expected move = Spot * (India VIX / 100) / sqrt(252) ``` ### Position Greeks (Multi-Leg) ``` Net Delta = Sum of (Direction_i * Delta_i * Lots_i * LotSize_i) Net Gamma = Sum of (Direction_i * Gamma_i * Lots_i * LotSize_i) Net Theta = Sum of (Direction_i * Theta_i * Lots_i * LotSize_i) Net Vega = Sum of (Direction_i * Vega_i * Lots_i * LotSize_i) ``` Where Direction = +1 for long, -1 for short.
-
-
scripts
-
black_scholes.py 50.7 KB
#!/usr/bin/env python3 """ Black-Scholes Option Pricing Engine for Indian F&O Markets (NSE) Features: - European options pricing (NSE options are European-style) - Full Greeks: Delta, Gamma, Theta, Vega, Rho - Historical volatility calculation via yfinance - Multi-leg strategy P/L simulation - ASCII payoff diagram generation - CLI interface for standalone use Default risk-free rate: India 91-day T-bill rate (~6.5-7%) """ import argparse import math import sys from dataclasses import dataclass, field from enum import Enum from typing import List, Optional, Tuple # --------------------------------------------------------------------------- # Constants # --------------------------------------------------------------------------- INDIA_RISK_FREE_RATE = 0.07 # ~7% (91-day T-bill rate approximation) TRADING_DAYS_PER_YEAR = 252 # NSE trading days per year # Common NSE F&O lot sizes (reference values — verify via MCP before trading) DEFAULT_LOT_SIZES = { "NIFTY": 75, "BANKNIFTY": 15, "FINNIFTY": 25, "MIDCPNIFTY": 50, } # --------------------------------------------------------------------------- # Enums # --------------------------------------------------------------------------- class OptionType(Enum): CALL = "CALL" PUT = "PUT" class PositionType(Enum): LONG = "LONG" SHORT = "SHORT" # --------------------------------------------------------------------------- # Math helpers — cumulative normal distribution # --------------------------------------------------------------------------- def _norm_pdf(x: float) -> float: """Standard normal probability density function.""" return math.exp(-0.5 * x * x) / math.sqrt(2.0 * math.pi) def _norm_cdf(x: float) -> float: """ Cumulative distribution function for the standard normal distribution. Uses the Abramowitz and Stegun rational approximation (max error ~7.5e-8). """ if x >= 0: return _norm_cdf_positive(x) else: return 1.0 - _norm_cdf_positive(-x) def _norm_cdf_positive(x: float) -> float: """CDF for x >= 0 using Abramowitz & Stegun approximation 26.2.17.""" b0 = 0.2316419 b1 = 0.319381530 b2 = -0.356563782 b3 = 1.781477937 b4 = -1.821255978 b5 = 1.330274429 t = 1.0 / (1.0 + b0 * x) t2 = t * t t3 = t2 * t t4 = t3 * t t5 = t4 * t return 1.0 - _norm_pdf(x) * (b1 * t + b2 * t2 + b3 * t3 + b4 * t4 + b5 * t5) # --------------------------------------------------------------------------- # Data classes # --------------------------------------------------------------------------- @dataclass class OptionLeg: """Represents a single option leg in a strategy.""" option_type: OptionType position_type: PositionType strike: float premium: float quantity: int = 1 # Number of lots lot_size: int = 1 # Shares per lot @property def direction(self) -> int: """Returns +1 for long, -1 for short.""" return 1 if self.position_type == PositionType.LONG else -1 def payoff_at_expiry(self, spot: float) -> float: """Calculate payoff per unit at expiry for the given spot price.""" if self.option_type == OptionType.CALL: intrinsic = max(0.0, spot - self.strike) else: intrinsic = max(0.0, self.strike - spot) payoff_per_unit = self.direction * (intrinsic - self.premium) return payoff_per_unit * self.quantity * self.lot_size def cost(self) -> float: """Total cost (debit) or credit of this leg.""" return -self.direction * self.premium * self.quantity * self.lot_size @dataclass class Greeks: """Container for option Greeks.""" delta: float = 0.0 gamma: float = 0.0 theta: float = 0.0 # Per calendar day vega: float = 0.0 # Per 1% change in volatility rho: float = 0.0 # Per 1% change in interest rate @dataclass class OptionResult: """Result of option pricing calculation.""" price: float greeks: Greeks d1: float d2: float @dataclass class PLPoint: """A single point on the P/L curve.""" spot: float pl: float @dataclass class StrategyAnalysis: """Complete analysis of an options strategy.""" strategy_name: str legs: List[OptionLeg] underlying_price: float net_premium: float # Positive = net debit, Negative = net credit max_profit: float max_loss: float breakeven_points: List[float] risk_reward_ratio: float pl_curve: List[PLPoint] net_greeks: Optional[Greeks] = None # --------------------------------------------------------------------------- # OptionPricer — Black-Scholes engine # --------------------------------------------------------------------------- class OptionPricer: """ Black-Scholes option pricing engine for European options. Designed for Indian NSE F&O market where all options are European-style. Parameters ---------- spot : float Current price of the underlying asset. strike : float Strike price of the option. time_to_expiry : float Time to expiry in years. For example, 7 days = 7/365. volatility : float Annualized volatility as a decimal (e.g., 0.15 for 15%). risk_free_rate : float Annualized risk-free rate (default: India 91-day T-bill rate). dividend_yield : float Continuous dividend yield as a decimal (default: 0 for index options). """ def __init__( self, spot: float, strike: float, time_to_expiry: float, volatility: float, risk_free_rate: float = INDIA_RISK_FREE_RATE, dividend_yield: float = 0.0, ): self.S = spot self.K = strike self.T = max(time_to_expiry, 1e-10) # Avoid division by zero self.sigma = volatility self.r = risk_free_rate self.q = dividend_yield # Pre-calculate d1 and d2 self._d1, self._d2 = self._calculate_d1_d2() def _calculate_d1_d2(self) -> Tuple[float, float]: """Calculate the d1 and d2 parameters for Black-Scholes.""" sqrt_T = math.sqrt(self.T) d1 = ( math.log(self.S / self.K) + (self.r - self.q + 0.5 * self.sigma ** 2) * self.T ) / (self.sigma * sqrt_T) d2 = d1 - self.sigma * sqrt_T return d1, d2 # --- Pricing --- def call_price(self) -> float: """Calculate the Black-Scholes price of a European call option.""" return ( self.S * math.exp(-self.q * self.T) * _norm_cdf(self._d1) - self.K * math.exp(-self.r * self.T) * _norm_cdf(self._d2) ) def put_price(self) -> float: """Calculate the Black-Scholes price of a European put option.""" return ( self.K * math.exp(-self.r * self.T) * _norm_cdf(-self._d2) - self.S * math.exp(-self.q * self.T) * _norm_cdf(-self._d1) ) def price(self, option_type: OptionType) -> float: """Calculate option price based on type.""" if option_type == OptionType.CALL: return self.call_price() else: return self.put_price() # --- Greeks --- def delta(self, option_type: OptionType) -> float: """ Delta: Rate of change of option price with respect to underlying price. Call delta: [0, 1], Put delta: [-1, 0] """ eq_discount = math.exp(-self.q * self.T) if option_type == OptionType.CALL: return eq_discount * _norm_cdf(self._d1) else: return eq_discount * (_norm_cdf(self._d1) - 1.0) def gamma(self) -> float: """ Gamma: Rate of change of delta with respect to underlying price. Same for both calls and puts. """ eq_discount = math.exp(-self.q * self.T) return ( eq_discount * _norm_pdf(self._d1) / (self.S * self.sigma * math.sqrt(self.T)) ) def theta(self, option_type: OptionType) -> float: """ Theta: Rate of change of option price with respect to time. Returned as per-calendar-day decay (divide annual by 365). Negative for long positions (time decay hurts buyers). """ sqrt_T = math.sqrt(self.T) eq_discount = math.exp(-self.q * self.T) # Common term term1 = -(self.S * eq_discount * _norm_pdf(self._d1) * self.sigma) / (2.0 * sqrt_T) if option_type == OptionType.CALL: term2 = -self.r * self.K * math.exp(-self.r * self.T) * _norm_cdf(self._d2) term3 = self.q * self.S * eq_discount * _norm_cdf(self._d1) annual_theta = term1 + term2 + term3 else: term2 = self.r * self.K * math.exp(-self.r * self.T) * _norm_cdf(-self._d2) term3 = -self.q * self.S * eq_discount * _norm_cdf(-self._d1) annual_theta = term1 + term2 + term3 return annual_theta / 365.0 # Per calendar day def vega(self) -> float: """ Vega: Rate of change of option price with respect to volatility. Returned per 1% change in volatility (divide by 100). Same for both calls and puts. """ eq_discount = math.exp(-self.q * self.T) raw_vega = self.S * eq_discount * _norm_pdf(self._d1) * math.sqrt(self.T) return raw_vega / 100.0 # Per 1% vol change def rho(self, option_type: OptionType) -> float: """ Rho: Rate of change of option price with respect to interest rate. Returned per 1% change in interest rate (divide by 100). """ if option_type == OptionType.CALL: raw_rho = ( self.K * self.T * math.exp(-self.r * self.T) * _norm_cdf(self._d2) ) else: raw_rho = ( -self.K * self.T * math.exp(-self.r * self.T) * _norm_cdf(-self._d2) ) return raw_rho / 100.0 # Per 1% rate change def all_greeks(self, option_type: OptionType) -> Greeks: """Calculate all Greeks for the given option type.""" return Greeks( delta=self.delta(option_type), gamma=self.gamma(), theta=self.theta(option_type), vega=self.vega(), rho=self.rho(option_type), ) def full_result(self, option_type: OptionType) -> OptionResult: """Return complete pricing result with price, Greeks, and intermediates.""" return OptionResult( price=self.price(option_type), greeks=self.all_greeks(option_type), d1=self._d1, d2=self._d2, ) # --- Implied Volatility --- @staticmethod def implied_volatility( market_price: float, spot: float, strike: float, time_to_expiry: float, option_type: OptionType, risk_free_rate: float = INDIA_RISK_FREE_RATE, dividend_yield: float = 0.0, tolerance: float = 1e-6, max_iterations: int = 100, ) -> float: """ Calculate implied volatility using the Newton-Raphson method. Parameters ---------- market_price : float The observed market price of the option. spot, strike, time_to_expiry : float Option parameters. option_type : OptionType CALL or PUT. risk_free_rate, dividend_yield : float Market parameters. tolerance : float Convergence tolerance. max_iterations : int Maximum iterations for Newton-Raphson. Returns ------- float Implied volatility as a decimal. Raises ------ ValueError If the method does not converge. """ # Initial guess using Brenner-Subrahmanyam approximation sigma = math.sqrt(2.0 * math.pi / time_to_expiry) * (market_price / spot) sigma = max(sigma, 0.01) # Floor at 1% for i in range(max_iterations): pricer = OptionPricer( spot, strike, time_to_expiry, sigma, risk_free_rate, dividend_yield ) price = pricer.price(option_type) vega_raw = pricer.vega() * 100.0 # Convert back to raw vega if abs(vega_raw) < 1e-12: # Vega too small — bisection fallback break diff = price - market_price if abs(diff) < tolerance: return sigma sigma = sigma - diff / vega_raw sigma = max(sigma, 0.001) # Prevent negative volatility # Bisection fallback if Newton-Raphson didn't converge return OptionPricer._iv_bisection( market_price, spot, strike, time_to_expiry, option_type, risk_free_rate, dividend_yield, tolerance, max_iterations ) @staticmethod def _iv_bisection( market_price: float, spot: float, strike: float, time_to_expiry: float, option_type: OptionType, risk_free_rate: float, dividend_yield: float, tolerance: float, max_iterations: int, ) -> float: """Bisection method fallback for implied volatility.""" low = 0.001 high = 5.0 # 500% volatility upper bound for _ in range(max_iterations): mid = (low + high) / 2.0 pricer = OptionPricer( spot, strike, time_to_expiry, mid, risk_free_rate, dividend_yield ) price = pricer.price(option_type) diff = price - market_price if abs(diff) < tolerance: return mid if diff > 0: high = mid else: low = mid raise ValueError( f"Implied volatility did not converge after {max_iterations} iterations. " f"Market price: {market_price}, Spot: {spot}, Strike: {strike}, " f"T: {time_to_expiry}, Type: {option_type.value}" ) # --------------------------------------------------------------------------- # Historical Volatility Calculator # --------------------------------------------------------------------------- def calculate_historical_volatility( ticker: str, days: int = 30, annualize: bool = True, ) -> float: """ Calculate historical volatility from daily returns using yfinance data. Parameters ---------- ticker : str Yahoo Finance ticker symbol. For Indian stocks use ".NS" suffix (e.g., "RELIANCE.NS", "^NSEI" for Nifty, "^NSEBANK" for Bank Nifty). days : int Number of trading days to look back (default: 30). annualize : bool If True, annualize the volatility (multiply by sqrt(252)). Returns ------- float Historical volatility as a decimal. """ try: import yfinance as yf except ImportError: print( "ERROR: yfinance is not installed. Install it with: pip install yfinance", file=sys.stderr, ) print( "Returning default volatility of 15% as fallback.", file=sys.stderr, ) return 0.15 # Map common Indian index names to Yahoo Finance tickers ticker_map = { "NIFTY": "^NSEI", "NIFTY50": "^NSEI", "BANKNIFTY": "^NSEBANK", "NIFTYBANK": "^NSEBANK", "FINNIFTY": "NIFTY_FIN_SERVICE.NS", "INDIAVIX": "^INDIAVIX", "SENSEX": "^BSESN", } yf_ticker = ticker_map.get(ticker.upper(), ticker) # Add .NS suffix for Indian stocks if not already present if ( not yf_ticker.startswith("^") and not yf_ticker.endswith(".NS") and not yf_ticker.endswith(".BO") ): yf_ticker = yf_ticker + ".NS" # Fetch extra days to account for weekends and holidays fetch_days = int(days * 1.6) + 10 try: data = yf.download( yf_ticker, period=f"{fetch_days}d", progress=False, auto_adjust=True, ) if data.empty or len(data) < 5: print( f"WARNING: Insufficient data for {yf_ticker}. " f"Returning default volatility of 15%.", file=sys.stderr, ) return 0.15 # Use closing prices closes = data["Close"].dropna().values.flatten() if len(closes) < 2: return 0.15 # Take the most recent 'days' trading days closes = closes[-(days + 1):] # Calculate log returns log_returns = [] for i in range(1, len(closes)): if closes[i - 1] > 0 and closes[i] > 0: log_returns.append(math.log(closes[i] / closes[i - 1])) if len(log_returns) < 2: return 0.15 # Standard deviation of log returns mean_return = sum(log_returns) / len(log_returns) variance = sum((r - mean_return) ** 2 for r in log_returns) / (len(log_returns) - 1) daily_vol = math.sqrt(variance) if annualize: return daily_vol * math.sqrt(TRADING_DAYS_PER_YEAR) else: return daily_vol except Exception as e: print( f"WARNING: Could not fetch data for {yf_ticker}: {e}. " f"Returning default volatility of 15%.", file=sys.stderr, ) return 0.15 # --------------------------------------------------------------------------- # P/L Simulation # --------------------------------------------------------------------------- def simulate_pl( strategy_legs: List[OptionLeg], price_range: Tuple[float, float], num_points: int = 50, ) -> List[PLPoint]: """ Simulate profit/loss at expiry across a range of underlying prices. Parameters ---------- strategy_legs : list of OptionLeg The legs of the options strategy. price_range : tuple of (float, float) (min_price, max_price) for simulation. num_points : int Number of price points to simulate. Returns ------- list of PLPoint P/L at each price point. """ min_price, max_price = price_range step = (max_price - min_price) / max(num_points - 1, 1) pl_curve = [] for i in range(num_points): spot = min_price + i * step total_pl = sum(leg.payoff_at_expiry(spot) for leg in strategy_legs) pl_curve.append(PLPoint(spot=round(spot, 2), pl=round(total_pl, 2))) return pl_curve def find_breakeven_points( strategy_legs: List[OptionLeg], price_range: Tuple[float, float], num_points: int = 1000, ) -> List[float]: """ Find breakeven points where P/L crosses zero. Uses linear interpolation between simulated points. """ pl_curve = simulate_pl(strategy_legs, price_range, num_points) breakevens = [] for i in range(1, len(pl_curve)): prev = pl_curve[i - 1] curr = pl_curve[i] # Check for sign change if prev.pl * curr.pl < 0: # Linear interpolation if abs(curr.pl - prev.pl) > 1e-10: ratio = abs(prev.pl) / abs(curr.pl - prev.pl) be_price = prev.spot + ratio * (curr.spot - prev.spot) breakevens.append(round(be_price, 2)) elif abs(curr.pl) < 0.01: breakevens.append(curr.spot) return breakevens def analyze_strategy( strategy_name: str, legs: List[OptionLeg], underlying_price: float, price_range: Optional[Tuple[float, float]] = None, num_points: int = 50, net_greeks: Optional[Greeks] = None, ) -> StrategyAnalysis: """ Perform complete analysis of an options strategy. Parameters ---------- strategy_name : str Name of the strategy (e.g., "Iron Condor"). legs : list of OptionLeg All legs of the strategy. underlying_price : float Current price of the underlying. price_range : tuple of (float, float), optional Price range for simulation. Defaults to +/- 10% of underlying. num_points : int Number of simulation points. net_greeks : Greeks, optional Pre-calculated net Greeks for the position. Returns ------- StrategyAnalysis Complete strategy analysis. """ if price_range is None: margin = underlying_price * 0.10 price_range = (underlying_price - margin, underlying_price + margin) # Net premium net_premium = sum( leg.direction * leg.premium * leg.quantity * leg.lot_size for leg in legs ) # P/L curve pl_curve = simulate_pl(legs, price_range, num_points) # Max profit and max loss max_profit = max(point.pl for point in pl_curve) max_loss = min(point.pl for point in pl_curve) # Breakeven points breakevens = find_breakeven_points(legs, price_range, num_points=1000) # Risk-reward ratio if abs(max_loss) > 1e-10: risk_reward = abs(max_profit / max_loss) else: risk_reward = float("inf") return StrategyAnalysis( strategy_name=strategy_name, legs=legs, underlying_price=underlying_price, net_premium=net_premium, max_profit=max_profit, max_loss=max_loss, breakeven_points=breakevens, risk_reward_ratio=round(risk_reward, 2), pl_curve=pl_curve, net_greeks=net_greeks, ) # --------------------------------------------------------------------------- # ASCII P/L Diagram Generator # --------------------------------------------------------------------------- def generate_ascii_pl_diagram( pl_data: List[PLPoint], width: int = 60, height: int = 20, title: str = "P/L at Expiry", ) -> str: """ Generate an ASCII art P/L diagram. Parameters ---------- pl_data : list of PLPoint P/L data points to plot. width : int Width of the chart in characters. height : int Height of the chart in characters. title : str Title to display above the chart. Returns ------- str ASCII art P/L diagram as a multi-line string. """ if not pl_data: return "No data to plot." spots = [p.spot for p in pl_data] pls = [p.pl for p in pl_data] min_spot = min(spots) max_spot = max(spots) min_pl = min(pls) max_pl = max(pls) # Add padding to P/L range pl_range = max_pl - min_pl if pl_range < 1e-10: pl_range = 1.0 min_pl -= 0.5 max_pl += 0.5 spot_range = max_spot - min_spot if spot_range < 1e-10: spot_range = 1.0 # Determine zero line position if min_pl <= 0 <= max_pl: zero_row = height - 1 - int((0 - min_pl) / pl_range * (height - 1)) else: zero_row = -1 # Zero line is outside the chart # Build the grid grid = [[" " for _ in range(width)] for _ in range(height)] # Draw zero line if 0 <= zero_row < height: for c in range(width): grid[zero_row][c] = "-" # Plot the P/L curve for point in pl_data: col = int((point.spot - min_spot) / spot_range * (width - 1)) row = height - 1 - int((point.pl - min_pl) / pl_range * (height - 1)) col = max(0, min(col, width - 1)) row = max(0, min(row, height - 1)) if point.pl > 0: grid[row][col] = "+" elif point.pl < 0: grid[row][col] = "o" else: grid[row][col] = "*" # Build output lines = [] lines.append("") lines.append(f" {title}") lines.append(f" {'=' * (width + 12)}") # Y-axis labels for r in range(height): pl_value = max_pl - (r / (height - 1)) * pl_range label = f"{pl_value:>10.0f}" row_str = "".join(grid[r]) if r == zero_row: lines.append(f"{label} |{row_str}| <- Zero") else: lines.append(f"{label} |{row_str}|") # X-axis lines.append(f"{'':>10} +{'=' * width}+") # X-axis labels x_label_positions = [0, width // 4, width // 2, 3 * width // 4, width - 1] x_labels = [] for pos in x_label_positions: spot_value = min_spot + (pos / (width - 1)) * spot_range x_labels.append(f"{spot_value:.0f}") # Distribute labels across the x-axis x_label_line = " " * 11 positions_used = 0 for i, pos in enumerate(x_label_positions): label = x_labels[i] actual_pos = 11 + pos padding = actual_pos - len(x_label_line) if padding > 0: x_label_line += " " * padding x_label_line += label lines.append(x_label_line) # Legend lines.append("") lines.append(f" Legend: '+' = Profit | 'o' = Loss | '*' = Breakeven | '-' = Zero line") lines.append(f" X-axis: Underlying price at expiry") lines.append(f" Y-axis: Profit / Loss") return "\n".join(lines) # --------------------------------------------------------------------------- # Strategy Report Generator # --------------------------------------------------------------------------- def generate_strategy_report(analysis: StrategyAnalysis) -> str: """Generate a formatted text report of the strategy analysis.""" lines = [] lines.append("") lines.append("=" * 70) lines.append(" OPTIONS STRATEGY REPORT") lines.append("=" * 70) lines.append("") lines.append(f" Strategy : {analysis.strategy_name}") lines.append(f" Underlying : {analysis.underlying_price:.2f}") lines.append("") # Legs lines.append("-" * 70) lines.append(" STRATEGY LEGS") lines.append("-" * 70) for i, leg in enumerate(analysis.legs, 1): direction_str = "BUY" if leg.position_type == PositionType.LONG else "SELL" lines.append( f" Leg {i}: {direction_str:4s} {leg.quantity} x " f"{leg.option_type.value:4s} @ Strike {leg.strike:.2f} " f"for {leg.premium:.2f} " f"(Lot size: {leg.lot_size})" ) lines.append("") # Key Metrics lines.append("-" * 70) lines.append(" KEY METRICS") lines.append("-" * 70) premium_type = "DEBIT" if analysis.net_premium > 0 else "CREDIT" lines.append( f" Net Premium : {premium_type} {abs(analysis.net_premium):.2f}" ) lines.append(f" Max Profit : {analysis.max_profit:.2f}") lines.append(f" Max Loss : {analysis.max_loss:.2f}") if analysis.breakeven_points: be_str = ", ".join(f"{be:.2f}" for be in analysis.breakeven_points) lines.append(f" Breakeven(s) : {be_str}") else: lines.append(f" Breakeven(s) : None found in range") lines.append(f" Risk-Reward : 1:{analysis.risk_reward_ratio:.2f}") lines.append("") # Net Greeks if analysis.net_greeks: lines.append("-" * 70) lines.append(" NET POSITION GREEKS") lines.append("-" * 70) g = analysis.net_greeks lines.append(f" Delta : {g.delta:>10.4f}") lines.append(f" Gamma : {g.gamma:>10.6f}") lines.append(f" Theta : {g.theta:>10.4f} (per day)") lines.append(f" Vega : {g.vega:>10.4f} (per 1% IV change)") lines.append(f" Rho : {g.rho:>10.4f} (per 1% rate change)") lines.append("") # P/L Diagram if analysis.pl_curve: diagram = generate_ascii_pl_diagram( analysis.pl_curve, title=f"P/L Diagram: {analysis.strategy_name}" ) lines.append("-" * 70) lines.append(" PAYOFF DIAGRAM") lines.append("-" * 70) lines.append(diagram) lines.append("") # Risk Management lines.append("-" * 70) lines.append(" RISK MANAGEMENT NOTES") lines.append("-" * 70) lines.append(" - Set stop-loss if unrealized loss exceeds 2x premium received") lines.append(" - Monitor margin requirements daily (SEBI peak margin rules)") lines.append(" - Watch for F&O ban risk if stock OI approaches 95% MWPL") lines.append(" - Close ITM options before expiry to avoid STT on exercise") lines.append(" - For stock options: physical settlement requires delivery margin") lines.append(" - Review position 2-3 days before expiry for rollover decision") lines.append("") lines.append("=" * 70) return "\n".join(lines) # --------------------------------------------------------------------------- # Pre-built Strategy Constructors # --------------------------------------------------------------------------- def build_bull_call_spread( lower_strike: float, upper_strike: float, lower_premium: float, upper_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Bull Call Spread: Buy lower strike call, sell upper strike call.""" return [ OptionLeg(OptionType.CALL, PositionType.LONG, lower_strike, lower_premium, lots, lot_size), OptionLeg(OptionType.CALL, PositionType.SHORT, upper_strike, upper_premium, lots, lot_size), ] def build_bear_put_spread( upper_strike: float, lower_strike: float, upper_premium: float, lower_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Bear Put Spread: Buy higher strike put, sell lower strike put.""" return [ OptionLeg(OptionType.PUT, PositionType.LONG, upper_strike, upper_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.SHORT, lower_strike, lower_premium, lots, lot_size), ] def build_bull_put_spread( upper_strike: float, lower_strike: float, upper_premium: float, lower_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Bull Put Spread: Sell higher strike put, buy lower strike put.""" return [ OptionLeg(OptionType.PUT, PositionType.SHORT, upper_strike, upper_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.LONG, lower_strike, lower_premium, lots, lot_size), ] def build_bear_call_spread( lower_strike: float, upper_strike: float, lower_premium: float, upper_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Bear Call Spread: Sell lower strike call, buy upper strike call.""" return [ OptionLeg(OptionType.CALL, PositionType.SHORT, lower_strike, lower_premium, lots, lot_size), OptionLeg(OptionType.CALL, PositionType.LONG, upper_strike, upper_premium, lots, lot_size), ] def build_long_straddle( strike: float, call_premium: float, put_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Long Straddle: Buy ATM call and ATM put at same strike.""" return [ OptionLeg(OptionType.CALL, PositionType.LONG, strike, call_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.LONG, strike, put_premium, lots, lot_size), ] def build_short_straddle( strike: float, call_premium: float, put_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Short Straddle: Sell ATM call and ATM put at same strike.""" return [ OptionLeg(OptionType.CALL, PositionType.SHORT, strike, call_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.SHORT, strike, put_premium, lots, lot_size), ] def build_long_strangle( call_strike: float, put_strike: float, call_premium: float, put_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Long Strangle: Buy OTM call and OTM put.""" return [ OptionLeg(OptionType.CALL, PositionType.LONG, call_strike, call_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.LONG, put_strike, put_premium, lots, lot_size), ] def build_short_strangle( call_strike: float, put_strike: float, call_premium: float, put_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """Build a Short Strangle: Sell OTM call and OTM put.""" return [ OptionLeg(OptionType.CALL, PositionType.SHORT, call_strike, call_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.SHORT, put_strike, put_premium, lots, lot_size), ] def build_iron_condor( put_buy_strike: float, put_sell_strike: float, call_sell_strike: float, call_buy_strike: float, put_buy_premium: float, put_sell_premium: float, call_sell_premium: float, call_buy_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """ Build an Iron Condor: Buy OTM Put (lowest) + Sell Put (lower-mid) + Sell Call (upper-mid) + Buy OTM Call (highest). """ return [ OptionLeg(OptionType.PUT, PositionType.LONG, put_buy_strike, put_buy_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.SHORT, put_sell_strike, put_sell_premium, lots, lot_size), OptionLeg(OptionType.CALL, PositionType.SHORT, call_sell_strike, call_sell_premium, lots, lot_size), OptionLeg(OptionType.CALL, PositionType.LONG, call_buy_strike, call_buy_premium, lots, lot_size), ] def build_iron_butterfly( put_buy_strike: float, atm_strike: float, call_buy_strike: float, put_buy_premium: float, atm_put_premium: float, atm_call_premium: float, call_buy_premium: float, lots: int = 1, lot_size: int = 75, ) -> List[OptionLeg]: """ Build an Iron Butterfly: Buy OTM Put + Sell ATM Put + Sell ATM Call + Buy OTM Call. """ return [ OptionLeg(OptionType.PUT, PositionType.LONG, put_buy_strike, put_buy_premium, lots, lot_size), OptionLeg(OptionType.PUT, PositionType.SHORT, atm_strike, atm_put_premium, lots, lot_size), OptionLeg(OptionType.CALL, PositionType.SHORT, atm_strike, atm_call_premium, lots, lot_size), OptionLeg(OptionType.CALL, PositionType.LONG, call_buy_strike, call_buy_premium, lots, lot_size), ] # --------------------------------------------------------------------------- # Command-Line Interface # --------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser( description="Black-Scholes Option Pricing Engine for Indian F&O Markets (NSE)", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Price a NIFTY call option python black_scholes.py price --spot 24000 --strike 24500 --expiry-days 7 --vol 0.13 --type CALL # Calculate all Greeks python black_scholes.py greeks --spot 24000 --strike 24500 --expiry-days 7 --vol 0.13 --type CALL # Calculate implied volatility from market price python black_scholes.py iv --spot 24000 --strike 24500 --expiry-days 7 --market-price 120 --type CALL # Calculate historical volatility for NIFTY python black_scholes.py hvol --ticker NIFTY --days 30 # Analyze a Bull Call Spread python black_scholes.py strategy --type bull-call-spread \\ --spot 24000 --lower-strike 24000 --upper-strike 24500 \\ --lower-premium 250 --upper-premium 80 --lot-size 75 # Analyze a Short Straddle python black_scholes.py strategy --type short-straddle \\ --spot 24000 --atm-strike 24000 \\ --call-premium 200 --put-premium 180 --lot-size 75 # Analyze an Iron Condor python black_scholes.py strategy --type iron-condor \\ --spot 24000 --lot-size 75 \\ --put-buy-strike 23500 --put-sell-strike 23800 \\ --call-sell-strike 24200 --call-buy-strike 24500 \\ --put-buy-premium 30 --put-sell-premium 70 \\ --call-sell-premium 65 --call-buy-premium 25 """, ) subparsers = parser.add_subparsers(dest="command", help="Command to run") # --- Price command --- price_parser = subparsers.add_parser("price", help="Calculate option price") price_parser.add_argument("--spot", type=float, required=True, help="Underlying spot price") price_parser.add_argument("--strike", type=float, required=True, help="Strike price") price_parser.add_argument("--expiry-days", type=float, required=True, help="Days to expiry") price_parser.add_argument("--vol", type=float, required=True, help="Annualized volatility (decimal, e.g., 0.15)") price_parser.add_argument("--type", choices=["CALL", "PUT"], required=True, help="Option type") price_parser.add_argument("--rate", type=float, default=INDIA_RISK_FREE_RATE, help=f"Risk-free rate (default: {INDIA_RISK_FREE_RATE})") price_parser.add_argument("--div-yield", type=float, default=0.0, help="Dividend yield (default: 0)") # --- Greeks command --- greeks_parser = subparsers.add_parser("greeks", help="Calculate all Greeks") greeks_parser.add_argument("--spot", type=float, required=True, help="Underlying spot price") greeks_parser.add_argument("--strike", type=float, required=True, help="Strike price") greeks_parser.add_argument("--expiry-days", type=float, required=True, help="Days to expiry") greeks_parser.add_argument("--vol", type=float, required=True, help="Annualized volatility (decimal)") greeks_parser.add_argument("--type", choices=["CALL", "PUT"], required=True, help="Option type") greeks_parser.add_argument("--rate", type=float, default=INDIA_RISK_FREE_RATE, help=f"Risk-free rate (default: {INDIA_RISK_FREE_RATE})") greeks_parser.add_argument("--div-yield", type=float, default=0.0, help="Dividend yield (default: 0)") # --- Implied Volatility command --- iv_parser = subparsers.add_parser("iv", help="Calculate implied volatility") iv_parser.add_argument("--spot", type=float, required=True, help="Underlying spot price") iv_parser.add_argument("--strike", type=float, required=True, help="Strike price") iv_parser.add_argument("--expiry-days", type=float, required=True, help="Days to expiry") iv_parser.add_argument("--market-price", type=float, required=True, help="Observed market price of the option") iv_parser.add_argument("--type", choices=["CALL", "PUT"], required=True, help="Option type") iv_parser.add_argument("--rate", type=float, default=INDIA_RISK_FREE_RATE, help=f"Risk-free rate (default: {INDIA_RISK_FREE_RATE})") iv_parser.add_argument("--div-yield", type=float, default=0.0, help="Dividend yield (default: 0)") # --- Historical Volatility command --- hvol_parser = subparsers.add_parser("hvol", help="Calculate historical volatility") hvol_parser.add_argument("--ticker", type=str, required=True, help="Ticker symbol (e.g., NIFTY, RELIANCE, TCS)") hvol_parser.add_argument("--days", type=int, default=30, help="Lookback period in trading days (default: 30)") # --- Strategy command --- strat_parser = subparsers.add_parser("strategy", help="Analyze an options strategy") strat_parser.add_argument( "--type", choices=[ "bull-call-spread", "bear-put-spread", "bull-put-spread", "bear-call-spread", "long-straddle", "short-straddle", "long-strangle", "short-strangle", "iron-condor", "iron-butterfly", ], required=True, help="Strategy type", ) strat_parser.add_argument("--spot", type=float, required=True, help="Underlying spot price") strat_parser.add_argument("--lot-size", type=int, default=75, help="Lot size (default: 75 for NIFTY)") strat_parser.add_argument("--lots", type=int, default=1, help="Number of lots (default: 1)") # Strike arguments for various strategies strat_parser.add_argument("--lower-strike", type=float, help="Lower strike price") strat_parser.add_argument("--upper-strike", type=float, help="Upper strike price") strat_parser.add_argument("--atm-strike", type=float, help="ATM strike price") strat_parser.add_argument("--call-strike", type=float, help="Call strike (for strangles)") strat_parser.add_argument("--put-strike", type=float, help="Put strike (for strangles)") strat_parser.add_argument("--put-buy-strike", type=float, help="Put buy strike (for iron condor/butterfly)") strat_parser.add_argument("--put-sell-strike", type=float, help="Put sell strike (for iron condor)") strat_parser.add_argument("--call-sell-strike", type=float, help="Call sell strike (for iron condor)") strat_parser.add_argument("--call-buy-strike", type=float, help="Call buy strike (for iron condor/butterfly)") # Premium arguments strat_parser.add_argument("--lower-premium", type=float, help="Lower strike premium") strat_parser.add_argument("--upper-premium", type=float, help="Upper strike premium") strat_parser.add_argument("--call-premium", type=float, help="Call premium") strat_parser.add_argument("--put-premium", type=float, help="Put premium") strat_parser.add_argument("--put-buy-premium", type=float, help="Put buy premium (iron condor/butterfly)") strat_parser.add_argument("--put-sell-premium", type=float, help="Put sell premium (iron condor)") strat_parser.add_argument("--call-sell-premium", type=float, help="Call sell premium (iron condor)") strat_parser.add_argument("--call-buy-premium", type=float, help="Call buy premium (iron condor/butterfly)") strat_parser.add_argument("--atm-call-premium", type=float, help="ATM call premium (iron butterfly)") strat_parser.add_argument("--atm-put-premium", type=float, help="ATM put premium (iron butterfly)") args = parser.parse_args() if not args.command: parser.print_help() sys.exit(1) # --- Execute commands --- if args.command == "price": opt_type = OptionType.CALL if args.type == "CALL" else OptionType.PUT pricer = OptionPricer( spot=args.spot, strike=args.strike, time_to_expiry=args.expiry_days / 365.0, volatility=args.vol, risk_free_rate=args.rate, dividend_yield=args.div_yield, ) price = pricer.price(opt_type) print(f"\n{'=' * 50}") print(f" Black-Scholes Option Price") print(f"{'=' * 50}") print(f" Underlying : {args.spot:.2f}") print(f" Strike : {args.strike:.2f}") print(f" Days to Exp : {args.expiry_days:.0f}") print(f" Volatility : {args.vol:.2%}") print(f" Risk-Free : {args.rate:.2%}") print(f" Type : {args.type}") print(f" ---------------------") print(f" PRICE : {price:.2f}") print(f"{'=' * 50}\n") elif args.command == "greeks": opt_type = OptionType.CALL if args.type == "CALL" else OptionType.PUT pricer = OptionPricer( spot=args.spot, strike=args.strike, time_to_expiry=args.expiry_days / 365.0, volatility=args.vol, risk_free_rate=args.rate, dividend_yield=args.div_yield, ) result = pricer.full_result(opt_type) print(f"\n{'=' * 50}") print(f" Black-Scholes Greeks") print(f"{'=' * 50}") print(f" Underlying : {args.spot:.2f}") print(f" Strike : {args.strike:.2f}") print(f" Days to Exp : {args.expiry_days:.0f}") print(f" Volatility : {args.vol:.2%}") print(f" Risk-Free : {args.rate:.2%}") print(f" Type : {args.type}") print(f" ---------------------") print(f" Price : {result.price:.4f}") print(f" Delta : {result.greeks.delta:.6f}") print(f" Gamma : {result.greeks.gamma:.6f}") print(f" Theta : {result.greeks.theta:.4f} (per day)") print(f" Vega : {result.greeks.vega:.4f} (per 1% vol)") print(f" Rho : {result.greeks.rho:.4f} (per 1% rate)") print(f" d1 : {result.d1:.6f}") print(f" d2 : {result.d2:.6f}") print(f"{'=' * 50}\n") elif args.command == "iv": opt_type = OptionType.CALL if args.type == "CALL" else OptionType.PUT try: iv = OptionPricer.implied_volatility( market_price=args.market_price, spot=args.spot, strike=args.strike, time_to_expiry=args.expiry_days / 365.0, option_type=opt_type, risk_free_rate=args.rate, dividend_yield=args.div_yield, ) print(f"\n{'=' * 50}") print(f" Implied Volatility") print(f"{'=' * 50}") print(f" Underlying : {args.spot:.2f}") print(f" Strike : {args.strike:.2f}") print(f" Days to Exp : {args.expiry_days:.0f}") print(f" Market Price : {args.market_price:.2f}") print(f" Type : {args.type}") print(f" ---------------------") print(f" IMPLIED VOL : {iv:.4f} ({iv:.2%})") print(f"{'=' * 50}\n") except ValueError as e: print(f"\nERROR: {e}\n", file=sys.stderr) sys.exit(1) elif args.command == "hvol": hvol = calculate_historical_volatility(args.ticker, args.days) print(f"\n{'=' * 50}") print(f" Historical Volatility") print(f"{'=' * 50}") print(f" Ticker : {args.ticker}") print(f" Lookback : {args.days} trading days") print(f" ---------------------") print(f" HIST VOL : {hvol:.4f} ({hvol:.2%})") print(f"{'=' * 50}\n") elif args.command == "strategy": legs = _build_strategy_from_args(args) if legs is None: sys.exit(1) strategy_name = args.type.replace("-", " ").title() analysis = analyze_strategy( strategy_name=strategy_name, legs=legs, underlying_price=args.spot, ) report = generate_strategy_report(analysis) print(report) def _build_strategy_from_args(args) -> Optional[List[OptionLeg]]: """Build strategy legs from CLI arguments.""" lot_size = args.lot_size lots = args.lots if args.type == "bull-call-spread": if not all([args.lower_strike, args.upper_strike, args.lower_premium, args.upper_premium]): print("ERROR: Bull Call Spread requires --lower-strike, --upper-strike, --lower-premium, --upper-premium", file=sys.stderr) return None return build_bull_call_spread( args.lower_strike, args.upper_strike, args.lower_premium, args.upper_premium, lots, lot_size, ) elif args.type == "bear-put-spread": if not all([args.upper_strike, args.lower_strike, args.upper_premium, args.lower_premium]): print("ERROR: Bear Put Spread requires --upper-strike, --lower-strike, --upper-premium, --lower-premium", file=sys.stderr) return None return build_bear_put_spread( args.upper_strike, args.lower_strike, args.upper_premium, args.lower_premium, lots, lot_size, ) elif args.type == "bull-put-spread": if not all([args.upper_strike, args.lower_strike, args.upper_premium, args.lower_premium]): print("ERROR: Bull Put Spread requires --upper-strike, --lower-strike, --upper-premium, --lower-premium", file=sys.stderr) return None return build_bull_put_spread( args.upper_strike, args.lower_strike, args.upper_premium, args.lower_premium, lots, lot_size, ) elif args.type == "bear-call-spread": if not all([args.lower_strike, args.upper_strike, args.lower_premium, args.upper_premium]): print("ERROR: Bear Call Spread requires --lower-strike, --upper-strike, --lower-premium, --upper-premium", file=sys.stderr) return None return build_bear_call_spread( args.lower_strike, args.upper_strike, args.lower_premium, args.upper_premium, lots, lot_size, ) elif args.type == "long-straddle": if not all([args.atm_strike, args.call_premium, args.put_premium]): print("ERROR: Long Straddle requires --atm-strike, --call-premium, --put-premium", file=sys.stderr) return None return build_long_straddle( args.atm_strike, args.call_premium, args.put_premium, lots, lot_size, ) elif args.type == "short-straddle": if not all([args.atm_strike, args.call_premium, args.put_premium]): print("ERROR: Short Straddle requires --atm-strike, --call-premium, --put-premium", file=sys.stderr) return None return build_short_straddle( args.atm_strike, args.call_premium, args.put_premium, lots, lot_size, ) elif args.type == "long-strangle": if not all([args.call_strike, args.put_strike, args.call_premium, args.put_premium]): print("ERROR: Long Strangle requires --call-strike, --put-strike, --call-premium, --put-premium", file=sys.stderr) return None return build_long_strangle( args.call_strike, args.put_strike, args.call_premium, args.put_premium, lots, lot_size, ) elif args.type == "short-strangle": if not all([args.call_strike, args.put_strike, args.call_premium, args.put_premium]): print("ERROR: Short Strangle requires --call-strike, --put-strike, --call-premium, --put-premium", file=sys.stderr) return None return build_short_strangle( args.call_strike, args.put_strike, args.call_premium, args.put_premium, lots, lot_size, ) elif args.type == "iron-condor": required = [ args.put_buy_strike, args.put_sell_strike, args.call_sell_strike, args.call_buy_strike, args.put_buy_premium, args.put_sell_premium, args.call_sell_premium, args.call_buy_premium, ] if not all(v is not None for v in required): print( "ERROR: Iron Condor requires --put-buy-strike, --put-sell-strike, " "--call-sell-strike, --call-buy-strike, --put-buy-premium, " "--put-sell-premium, --call-sell-premium, --call-buy-premium", file=sys.stderr, ) return None return build_iron_condor( args.put_buy_strike, args.put_sell_strike, args.call_sell_strike, args.call_buy_strike, args.put_buy_premium, args.put_sell_premium, args.call_sell_premium, args.call_buy_premium, lots, lot_size, ) elif args.type == "iron-butterfly": required = [ args.put_buy_strike, args.atm_strike, args.call_buy_strike, args.put_buy_premium, args.atm_put_premium, args.atm_call_premium, args.call_buy_premium, ] if not all(v is not None for v in required): print( "ERROR: Iron Butterfly requires --put-buy-strike, --atm-strike, " "--call-buy-strike, --put-buy-premium, --atm-put-premium, " "--atm-call-premium, --call-buy-premium", file=sys.stderr, ) return None return build_iron_butterfly( args.put_buy_strike, args.atm_strike, args.call_buy_strike, args.put_buy_premium, args.atm_put_premium, args.atm_call_premium, args.call_buy_premium, lots, lot_size, ) print(f"ERROR: Unknown strategy type: {args.type}", file=sys.stderr) return None # --------------------------------------------------------------------------- # Entry point # --------------------------------------------------------------------------- if __name__ == "__main__": main()
-
-
SKILL.md 17.3 KB
--- name: options-strategy-advisor description: > Options strategy analysis for Indian F&O markets (NSE). Use when user requests options strategy recommendations, P/L analysis, Greeks calculation, risk management, or F&O strategy planning for Nifty, Bank Nifty, or stock options. --- # Options Strategy Advisor — Indian F&O Markets (NSE) ## Overview This skill provides comprehensive options strategy analysis tailored to the Indian Futures & Options market on the National Stock Exchange (NSE). It covers strategy selection, live data retrieval, margin estimation, profit/loss simulation, Greeks analysis, and risk management — all adapted for the specific characteristics of Indian F&O trading. --- ## Indian F&O Market Characteristics ### Exercise Style - **European-style exercise only.** Options on NSE can only be exercised at expiry, not before. This simplifies pricing (Black-Scholes applies directly without early-exercise adjustments) and means time value is always fully captured by the seller until expiry. ### Expiry Schedule | Underlying | Expiry Day | Expiry Type | |--------------|-------------|----------------------| | NIFTY | Thursday | Weekly + Monthly | | BANK NIFTY | Wednesday | Weekly + Monthly | | FINNIFTY | Tuesday | Weekly + Monthly | | SENSEX (BSE) | Friday | Weekly + Monthly | | Stock Options | Last Thursday | Monthly only | - Monthly expiry is the last Thursday of the month (or preceding trading day if Thursday is a holiday). - Weekly expiries are available only for index options, not individual stocks. ### Lot Sizes Lot sizes are periodically revised by the exchanges. Always verify current lot sizes using the Groww MCP tool `fno_mcx_contracts_search_tool` before calculating margin or position size. Recent reference values: - NIFTY: 75 (recently changed — confirm via MCP) - BANK NIFTY: 15 (recently changed — confirm via MCP) - FINNIFTY: 25 - Stock options: Varies by stock (check contract specifications) ### Margin Requirements SEBI mandates the following margin components for F&O: 1. **SPAN Margin** — Risk-based margin calculated by the exchange clearing corporation. 2. **Exposure Margin** — Additional margin over SPAN for market-wide risk. 3. **Peak Margin** — Intraday margin snapshots; brokers must collect at least the peak margin observed during the day. Use `calculate_fno_margin` to get exact margin for any trade before placing it. ### Transaction Costs - **STT (Securities Transaction Tax):** Levied on the sell side of options at 0.0625% of the intrinsic value on exercise (for ITM options at expiry). For futures, STT is 0.0125% on sell side. - **Brokerage:** Varies by broker (Groww charges per-order flat fees). - **Exchange charges, GST, SEBI turnover fee, stamp duty** also apply. ### F&O Ban Mechanism When the market-wide position limit (MWPL) for a stock's F&O contracts exceeds 95%, SEBI places the stock under an F&O ban. During the ban: - No new positions can be initiated. - Only squaring off (closing) of existing positions is allowed. - The ban is lifted when MWPL drops below 80%. --- ## Broker MCP Tool Integration This skill uses broker MCP tools for live market data and execution support. Use whichever broker is connected (Groww or Zerodha Kite). Always prefer live data over assumptions. ### Groww MCP Tools (if connected) | Tool | Purpose | |------|---------| | `get_ltp` (segment=FNO, query_type=fno) | Live option/futures prices and OI | | `get_quotes_and_depth` (segment=FNO) | Bid/ask spreads and market depth | | `fno_mcx_contracts_search_tool` | Search F&O contracts, lot sizes, expiries | | `fetch_historical_candle_data` (segment=FNO) | Historical option price data | | `fetch_curated_fno` | F&O gainers, losers, most traded | | `get_open_interest_analysis` | OI structure, PCR, support/resistance | | `get_greeks_for_fno_contract` | Live Greeks for specific contracts | | `get_greeks_for_fno_symbol` | Greeks for all contracts of an underlying | | `get_atm_straddle_chart` | ATM straddle premium analysis | | `get_payoff_chart_steps` | Payoff diagram generation instructions | | `calculate_fno_margin` | Margin requirement calculation | | `get_available_margin_details` | User's available margin | | `resolve_market_time_and_calendar` | Market hours and trading calendar | ### Zerodha Kite MCP Tools (if connected) | Tool | Purpose | |------|---------| | `get_ltp` | Last traded price for F&O instruments | | `get_quotes` | Real-time quotes with bid/ask depth | | `get_ohlc` | OHLC data for options/futures contracts | | `get_historical_data` | Historical candle data for F&O | | `search_instruments` | Search for F&O contracts by name/expiry | | `get_margins` | Account margins and available funds | | `get_positions` | Current F&O positions | | `get_orders` / `get_order_history` | Order status and execution details | | `place_order` / `modify_order` / `cancel_order` | Order management | | `place_gtt_order` / `get_gtts` | GTT order management | ### Tool Equivalence Map | Action | Groww MCP | Zerodha Kite MCP | |--------|-----------|------------------| | Live price | `get_ltp` | `get_ltp` | | Market depth | `get_quotes_and_depth` | `get_quotes` | | Historical data | `fetch_historical_candle_data` | `get_historical_data` | | Search contracts | `fno_mcx_contracts_search_tool` | `search_instruments` | | Margin check | `calculate_fno_margin` / `get_available_margin_details` | `get_margins` | | Positions | `get_my_trading_positions_today` | `get_positions` | | Place orders | `place_fno_order` | `place_order` | --- ## Supported Strategies ### Income Strategies 1. **Covered Call** — Long underlying futures + Short OTM Call - Objective: Generate income on existing long position. - Best when: Mildly bullish, want to earn premium. - Indian note: Use futures as underlying (no direct stock delivery for covered calls in F&O segment). 2. **Cash-Secured Put** — Short OTM Put (with margin set aside) - Objective: Earn premium while waiting to buy at a lower price. - Best when: Bullish on underlying, willing to take delivery equivalent. - Indian note: Physical settlement applies for stock options (ITM at expiry). ### Protection Strategies 3. **Protective Put** — Long underlying + Long Put - Objective: Insure existing long position against downside. - Best when: Want to cap losses while maintaining upside. 4. **Collar** — Long underlying + Long Put + Short Call - Objective: Cap both upside and downside. Zero-cost collar if premiums offset. - Best when: Want protection without paying net premium. ### Directional Strategies 5. **Bull Call Spread** — Long lower-strike Call + Short higher-strike Call - Objective: Limited-risk bullish bet. - Best when: Moderately bullish, want defined risk. 6. **Bear Put Spread** — Long higher-strike Put + Short lower-strike Put - Objective: Limited-risk bearish bet. - Best when: Moderately bearish, want defined risk. 7. **Bull Put Spread** — Short higher-strike Put + Long lower-strike Put - Objective: Credit spread, profit if price stays above short strike. - Best when: Mildly bullish, want to collect premium. 8. **Bear Call Spread** — Short lower-strike Call + Long higher-strike Call - Objective: Credit spread, profit if price stays below short strike. - Best when: Mildly bearish, want to collect premium. ### Volatility Strategies 9. **Long Straddle** — Long ATM Call + Long ATM Put - Objective: Profit from large move in either direction. - Best when: Expecting high volatility (e.g., pre-budget, RBI policy, earnings). - Indian note: Popular before Union Budget day, election results, RBI MPC. 10. **Short Straddle** — Short ATM Call + Short ATM Put - Objective: Profit from time decay when expecting range-bound movement. - Best when: Low implied volatility expected, range-bound market. - Indian note: Very popular on weekly expiry day for Nifty/Bank Nifty. 11. **Long Strangle** — Long OTM Call + Long OTM Put - Objective: Cheaper alternative to straddle for volatility plays. - Best when: Expecting very large move, want lower cost than straddle. 12. **Short Strangle** — Short OTM Call + Short OTM Put - Objective: Wider profit zone than short straddle, less premium received. - Best when: Expecting range-bound, comfortable with wider risk. ### Range-Bound Strategies 13. **Iron Condor** — Bull Put Spread + Bear Call Spread - Objective: Defined-risk range-bound strategy. - Best when: Expecting low volatility, want defined max loss. - Indian note: Very popular for weekly Nifty expiry plays. 14. **Iron Butterfly** — Short ATM Call + Short ATM Put + Long OTM Call + Long OTM Put - Objective: Defined-risk version of short straddle. - Best when: Expecting pin at a specific strike (max pain). ### Advanced Strategies 15. **Calendar Spread (Time Spread)** — Short near-expiry option + Long far-expiry option (same strike) - Objective: Profit from differential time decay. - Best when: Expecting current expiry to decay faster, longer-term view intact. - Indian note: Useful between weekly and monthly expiry cycles. 16. **Diagonal Spread** — Calendar spread with different strikes. - Objective: Directional bias + time decay benefit. - Best when: Have a directional view and want to finance via near-expiry sale. 17. **Ratio Spread** — Buy N options at one strike, sell M options at another (N != M). - Objective: Reduce cost of directional trade; accept risk on extreme moves. - Best when: Strong view on direction but want reduced cost. - Caution: Naked leg creates unlimited risk on one side. --- ## Workflow Follow this sequence when advising on an options strategy: ### Step 1: Gather Input Collect the following from the user: - **Underlying:** Which index or stock? (NIFTY, BANKNIFTY, FINNIFTY, or a specific stock) - **Market View:** Bullish, bearish, neutral, volatile, or range-bound? - **Strategy Preference:** Specific strategy or let the advisor recommend? - **Expiry:** Weekly or monthly? Specific date? - **Risk Tolerance:** Maximum loss acceptable? Capital available? - **Objective:** Income generation, hedging, speculation, or volatility play? ### Step 2: Fetch Live Data via Groww MCP 1. **Resolve market time and calendar:** ``` resolve_market_time_and_calendar() → confirm market is open, get trading days to expiry ``` 2. **Search for contracts:** ``` fno_mcx_contracts_search_tool(search_term="NIFTY 25 MAR") → get exact trading symbols ``` 3. **Get live prices:** ``` get_ltp(search_queries=["nifty 24000 CE mar", "nifty 24000 PE mar"], segment="FNO", query_type="fno") ``` 4. **Get Greeks:** ``` get_greeks_for_fno_contract(search_queries=["nifty 24000 mar CE"], expiry="2026-03-26") ``` 5. **Analyze Open Interest:** ``` get_open_interest_analysis(symbol="NIFTY", view="all") ``` 6. **Check ATM straddle premium (for volatility assessment):** ``` get_atm_straddle_chart(symbol="NIFTY") ``` ### Step 3: Calculate Margin Requirement For each leg of the strategy that involves selling (writing) options: ``` calculate_fno_margin( trading_symbol="NIFTY25MAR24000CE", num_lots=1, transaction_type="SELL", product="NRML" ) ``` Also check user's available margin: ``` get_available_margin_details() ``` ### Step 4: Simulate P/L Across Price Range Use the `scripts/black_scholes.py` script or manual calculation: - Define a price range (e.g., underlying +/- 5% from current price). - For each price point, calculate P/L for each leg. - Sum up P/L across all legs. - Identify breakeven points, max profit, max loss. Key calculations: - **Breakeven** = Strike +/- Net Premium (for single-leg strategies) - **Max Profit** = Net Premium Received (for credit strategies) or Strike Width - Net Debit (for debit spreads) - **Max Loss** = Net Premium Paid (for debit strategies) or Strike Width - Net Credit (for credit spreads) ### Step 5: Generate ASCII P/L Diagram Create a visual payoff diagram showing: - X-axis: Underlying price at expiry - Y-axis: Profit/Loss per lot - Breakeven point(s) marked - Max profit and max loss zones labeled Also use `get_payoff_chart_steps()` for Groww's built-in payoff chart generation. ### Step 6: Provide Risk Management Guidance Include in every recommendation: - **Position sizing:** How many lots based on capital and risk tolerance. - **Stop-loss levels:** When to exit (e.g., if loss exceeds 2x premium received). - **Adjustment triggers:** When and how to adjust the strategy. - **Expiry management:** Roll, close, or let expire — guidance based on ITM/OTM status. - **STT warning:** Remind about STT on ITM options at expiry (can erode profits significantly). - **Margin monitoring:** Warn about peak margin requirements and potential margin calls. ### Step 7: Save Report Present the complete analysis as a structured report: ``` === OPTIONS STRATEGY REPORT === Date: [current date] Underlying: [symbol] @ [current price] Strategy: [strategy name] Expiry: [expiry date] ([days to expiry] days) --- LEGS --- Leg 1: [BUY/SELL] [qty] [CALL/PUT] @ Strike [strike] for [premium] Leg 2: [BUY/SELL] [qty] [CALL/PUT] @ Strike [strike] for [premium] --- KEY METRICS --- Net Premium: [debit/credit] [amount] per lot Max Profit: [amount] per lot (at [price]) Max Loss: [amount] per lot (at [price]) Breakeven: [price(s)] Risk-Reward Ratio: [ratio] Probability of Profit: [estimate based on delta] --- GREEKS (NET POSITION) --- Delta: [value] | Gamma: [value] | Theta: [value] | Vega: [value] --- MARGIN REQUIREMENT --- Total Margin: [amount] Available Margin: [amount] Margin Utilization: [percentage] --- P/L DIAGRAM --- [ASCII payoff chart] --- RISK MANAGEMENT --- - Stop Loss: [criteria] - Adjustment Plan: [when and how] - Expiry Action: [recommendation] - STT Impact: [if applicable] ``` --- ## Strategy Selection Guide Use this decision tree to recommend strategies based on user's market view: ### Bullish View - **Strong bullish:** Long Call or Bull Call Spread - **Mildly bullish:** Bull Put Spread (credit) or Covered Call - **Bullish + high IV:** Bull Put Spread (sell expensive puts) - **Bullish + low IV:** Long Call or Bull Call Spread (buy cheap options) ### Bearish View - **Strong bearish:** Long Put or Bear Put Spread - **Mildly bearish:** Bear Call Spread (credit) - **Bearish + high IV:** Bear Call Spread (sell expensive calls) - **Bearish + low IV:** Long Put or Bear Put Spread ### Neutral / Range-Bound View - **Tight range expected:** Short Straddle or Iron Butterfly - **Wider range expected:** Short Strangle or Iron Condor - **Neutral + want defined risk:** Iron Condor or Iron Butterfly ### Volatile View (Expecting Big Move) - **Direction unknown, big move expected:** Long Straddle - **Direction unknown, very big move expected:** Long Strangle (cheaper) - **Pre-event (budget, RBI, earnings):** Long Straddle or Long Strangle ### Time Decay Play - **Near-term decay focus:** Calendar Spread - **Directional + decay:** Diagonal Spread --- ## Important Indian Market Considerations ### India VIX - India VIX measures the market's expectation of 30-day volatility. - VIX > 20: High volatility environment — favor long volatility strategies. - VIX < 15: Low volatility environment — favor short volatility strategies. - VIX between 15-20: Normal range — use directional or range-bound strategies. - VIX typically spikes before elections, budgets, RBI policy, and global crises. ### Weekly Expiry Trading - **Thursday (Nifty):** Most liquid expiry. Short straddle/strangle sellers dominate. Theta decay is highest on the expiry day. - **Wednesday (Bank Nifty):** High gamma risk. Moves can be sharp near expiry. - Premium sellers should be cautious of gamma risk on expiry day — a small move in the underlying can cause large P/L swings. ### Physical Settlement (Stock Options) - Stock options that expire ITM are physically settled — actual delivery of shares. - This requires full delivery margin (value of shares). Plan exits before expiry to avoid unexpected margin requirements. - Index options are cash-settled — no delivery concerns. ### Max Pain - Max Pain is the strike price at which the maximum number of options (calls + puts) expire worthless, causing minimum payout by option writers. - Indian markets tend to gravitate toward max pain on expiry day, especially for Nifty weekly expiry. - Use OI analysis to identify max pain and position accordingly. ### OI-Based Analysis - **High Call OI at a strike:** Acts as resistance. Call writers are betting the price won't cross this level. - **High Put OI at a strike:** Acts as support. Put writers are betting the price won't fall below this level. - **PCR (Put-Call Ratio):** - PCR > 1.2: Bullish signal (more puts written, indicating support) - PCR < 0.8: Bearish signal (more calls written, indicating resistance) - PCR between 0.8-1.2: Neutral --- ## Error Handling - If broker MCP tools (Groww or Zerodha) return errors, inform the user and suggest checking market hours or contract availability. Try the alternative broker's equivalent tool if available. - If a contract search yields no results, try alternative search terms or check if the expiry has passed. - If margin data is unavailable, provide theoretical estimates with a disclaimer. - Always validate that the market is open before fetching live data — use `resolve_market_time_and_calendar()`. - If a stock is under F&O ban, alert the user immediately and suggest alternative underlyings.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.