File size: 6,506 Bytes
26c5c2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env python3
"""
Fetch raw Yahoo Finance Options data and output the schema.
Shows raw API response structure for options chain data.
"""

import asyncio
import json
from datetime import datetime
from pathlib import Path

import httpx

YAHOO_HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Accept": "application/json",
}


def print_table(title: str, rows: list, col_widths: list = None):
    """Print ASCII table."""
    if not rows:
        return

    # Calculate column widths
    if col_widths is None:
        col_widths = []
        for col in range(len(rows[0])):
            width = max(len(str(row[col])) for row in rows)
            col_widths.append(width)

    # Print header
    print(f"\n{title}")

    # Top border
    line = "β”Œ" + "┬".join("─" * (w + 2) for w in col_widths) + "┐"
    print(line)

    # Header row
    header = rows[0]
    row_str = "β”‚" + "β”‚".join(f" {str(header[i]).ljust(col_widths[i])} " for i in range(len(header))) + "β”‚"
    print(row_str)

    # Separator
    line = "β”œ" + "β”Ό".join("─" * (w + 2) for w in col_widths) + "─"
    print(line)

    # Data rows
    for row in rows[1:]:
        row_str = "β”‚" + "β”‚".join(f" {str(row[i]).ljust(col_widths[i])} " for i in range(len(row))) + "β”‚"
        print(row_str)

    # Bottom border
    line = "β””" + "β”΄".join("─" * (w + 2) for w in col_widths) + "β”˜"
    print(line)


async def fetch_options(ticker: str) -> dict:
    """Fetch options chain from Yahoo Finance."""
    try:
        async with httpx.AsyncClient() as client:
            url = f"https://query1.finance.yahoo.com/v7/finance/options/{ticker}"
            response = await client.get(url, headers=YAHOO_HEADERS, timeout=15)
            return response.json()
    except Exception as e:
        return {"error": str(e)}


async def main():
    print("Yahoo Finance Options Data Schema")
    print("=" * 60)
    print()
    print("Endpoint: https://query1.finance.yahoo.com/v7/finance/options/{ticker}")
    print()

    print("Fetching AAPL options chain...")
    data = await fetch_options("AAPL")

    if "error" in data:
        print(f"ERROR: {data}")
        return

    option_chain = data.get("optionChain", {})
    result = option_chain.get("result", [{}])[0] if option_chain.get("result") else {}

    if not result:
        print("ERROR: No options data returned")
        return

    print()
    print("=" * 60)
    print()

    # Print raw API response structure
    print("Raw API Response Structure")
    print("-" * 40)

    # Underlying quote
    quote = result.get("quote", {})
    rows = [["field", "value"]]
    rows.append(["symbol", quote.get("symbol", "")])
    rows.append(["regularMarketPrice", quote.get("regularMarketPrice", "")])
    rows.append(["regularMarketTime", quote.get("regularMarketTime", "")])
    rows.append(["regularMarketChange", quote.get("regularMarketChange", "")])
    rows.append(["regularMarketChangePercent", quote.get("regularMarketChangePercent", "")])
    print_table("quote (Underlying)", rows)

    # Expiration dates
    expirations = result.get("expirationDates", [])
    rows = [["field", "description"]]
    rows.append(["expirationDates[]", "Unix timestamps of available expiration dates"])
    rows.append(["count", str(len(expirations))])
    if expirations:
        rows.append(["first", str(expirations[0])])
        rows.append(["last", str(expirations[-1])])
    print_table("Expiration Dates", rows)

    # Strikes
    strikes = result.get("strikes", [])
    rows = [["field", "description"]]
    rows.append(["strikes[]", "Available strike prices"])
    rows.append(["count", str(len(strikes))])
    if strikes:
        rows.append(["min", str(min(strikes))])
        rows.append(["max", str(max(strikes))])
    print_table("Strike Prices", rows)

    # Options data structure
    options = result.get("options", [{}])[0] if result.get("options") else {}
    calls = options.get("calls", [])
    puts = options.get("puts", [])

    rows = [["field", "description"]]
    rows.append(["expirationDate", "Expiration date (Unix timestamp)"])
    rows.append(["calls[]", f"Call options array (count: {len(calls)})"])
    rows.append(["puts[]", f"Put options array (count: {len(puts)})"])
    print_table("options[0] (First Expiration)", rows)

    # Call/Put contract fields
    if calls:
        sample_call = calls[0]
        rows = [["field", "value"]]
        rows.append(["contractSymbol", sample_call.get("contractSymbol", "")])
        rows.append(["strike", sample_call.get("strike", "")])
        rows.append(["currency", sample_call.get("currency", "")])
        rows.append(["lastPrice", sample_call.get("lastPrice", "")])
        rows.append(["change", sample_call.get("change", "")])
        rows.append(["percentChange", sample_call.get("percentChange", "")])
        rows.append(["volume", sample_call.get("volume", "")])
        rows.append(["openInterest", sample_call.get("openInterest", "")])
        rows.append(["bid", sample_call.get("bid", "")])
        rows.append(["ask", sample_call.get("ask", "")])
        rows.append(["impliedVolatility", sample_call.get("impliedVolatility", "")])
        rows.append(["inTheMoney", sample_call.get("inTheMoney", "")])
        rows.append(["expiration", sample_call.get("expiration", "")])
        rows.append(["lastTradeDate", sample_call.get("lastTradeDate", "")])
        print_table("calls[0] / puts[0] (Contract Fields)", rows)

    # ATM implied volatility example
    print()
    print()
    print("Implied Volatility Extraction")
    print("-" * 40)

    current_price = quote.get("regularMarketPrice", 0)
    if calls and current_price:
        atm_call = min(calls, key=lambda x: abs(x.get("strike", 0) - current_price))
        iv = atm_call.get("impliedVolatility", 0) * 100

        rows = [["field", "value"]]
        rows.append(["currentPrice", f"{current_price:.2f}"])
        rows.append(["atmStrike", atm_call.get("strike", "")])
        rows.append(["impliedVolatility (raw)", atm_call.get("impliedVolatility", "")])
        rows.append(["impliedVolatility (%)", f"{iv:.2f}%"])
        print_table("ATM Call Option", rows)

    # Save raw JSON
    output_path = Path(__file__).parent.parent / "docs" / "yahoo_options_raw.json"
    with open(output_path, 'w') as f:
        json.dump(data, f, indent=2, default=str)
    print(f"\nRaw JSON saved to: {output_path}")


if __name__ == "__main__":
    asyncio.run(main())