Spaces:
Sleeping
Sleeping
chore: Remove dead Tradier integration
Browse filesfetch_tradier_implied_volatility was never called and no Tradier key
has ever been provisioned; volatility is served by Yahoo + FRED
(+ Alpha Vantage when keyed). Removes the fetcher, config, and
.env.example entry.
- .env.example +0 -2
- mcp-servers/volatility-basket/server.py +0 -125
- mcp_client.py +1 -1
.env.example
CHANGED
|
@@ -18,8 +18,6 @@ FRED_VIX_API_KEY=your_fred_api_key
|
|
| 18 |
# Alpha Vantage (implied volatility; free tier is 25 req/day)
|
| 19 |
ALPHA_VANTAGE_API_KEY=your_alphavantage_api_key
|
| 20 |
|
| 21 |
-
# Tradier (options data for volatility basket)
|
| 22 |
-
TRADIER_API_KEY=your_tradier_api_key
|
| 23 |
|
| 24 |
# BLS - Bureau of Labor Statistics (macro basket)
|
| 25 |
BLS_API_KEY=your_bls_api_key
|
|
|
|
| 18 |
# Alpha Vantage (implied volatility; free tier is 25 req/day)
|
| 19 |
ALPHA_VANTAGE_API_KEY=your_alphavantage_api_key
|
| 20 |
|
|
|
|
|
|
|
| 21 |
|
| 22 |
# BLS - Bureau of Labor Statistics (macro basket)
|
| 23 |
BLS_API_KEY=your_bls_api_key
|
mcp-servers/volatility-basket/server.py
CHANGED
|
@@ -52,14 +52,10 @@ server = Server("volatility-basket")
|
|
| 52 |
# API Keys (optional - enables authoritative sources)
|
| 53 |
FRED_API_KEY = os.getenv("FRED_API_KEY") or os.getenv("FRED_VIX_API_KEY") # Get free key: https://fred.stlouisfed.org/docs/api/api_key.html
|
| 54 |
ALPHA_VANTAGE_KEY = os.getenv("ALPHA_VANTAGE_API_KEY") # Get free key: https://www.alphavantage.co/support/#api-key
|
| 55 |
-
TRADIER_API_KEY = os.getenv("TRADIER_API_KEY") # Get free key: https://developer.tradier.com/
|
| 56 |
|
| 57 |
# Alpha Vantage API configuration (Secondary for Beta, Historical Volatility)
|
| 58 |
ALPHA_VANTAGE_BASE_URL = "https://www.alphavantage.co/query"
|
| 59 |
|
| 60 |
-
# Tradier API configuration (Primary for Implied Volatility)
|
| 61 |
-
TRADIER_BASE_URL = "https://api.tradier.com/v1"
|
| 62 |
-
|
| 63 |
# Yahoo Finance requires browser-like headers
|
| 64 |
YAHOO_HEADERS = {
|
| 65 |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
@@ -734,127 +730,6 @@ async def fetch_alpha_vantage_historical_volatility(ticker: str, period_days: in
|
|
| 734 |
return None
|
| 735 |
|
| 736 |
|
| 737 |
-
# ============================================================
|
| 738 |
-
# TRADIER FETCHERS (Primary/Secondary for Implied Volatility)
|
| 739 |
-
# ============================================================
|
| 740 |
-
|
| 741 |
-
async def fetch_tradier_implied_volatility(ticker: str) -> Optional[dict]:
|
| 742 |
-
"""
|
| 743 |
-
Fetch implied volatility from Tradier options chain.
|
| 744 |
-
Provides stock-specific IV from ATM options.
|
| 745 |
-
|
| 746 |
-
API: https://developer.tradier.com/
|
| 747 |
-
Requires free account creation.
|
| 748 |
-
"""
|
| 749 |
-
if not TRADIER_API_KEY:
|
| 750 |
-
return None
|
| 751 |
-
|
| 752 |
-
try:
|
| 753 |
-
async with httpx.AsyncClient() as client:
|
| 754 |
-
headers = {
|
| 755 |
-
"Authorization": f"Bearer {TRADIER_API_KEY}",
|
| 756 |
-
"Accept": "application/json"
|
| 757 |
-
}
|
| 758 |
-
|
| 759 |
-
# First get current quote for ATM strike
|
| 760 |
-
quote_url = f"{TRADIER_BASE_URL}/markets/quotes"
|
| 761 |
-
quote_params = {"symbols": ticker.upper()}
|
| 762 |
-
quote_resp = await client.get(quote_url, params=quote_params, headers=headers, timeout=10)
|
| 763 |
-
quote_data = quote_resp.json()
|
| 764 |
-
|
| 765 |
-
quotes = quote_data.get("quotes", {}).get("quote", {})
|
| 766 |
-
if isinstance(quotes, list):
|
| 767 |
-
quotes = quotes[0] if quotes else {}
|
| 768 |
-
|
| 769 |
-
current_price = quotes.get("last", 0) or quotes.get("close", 0)
|
| 770 |
-
if not current_price:
|
| 771 |
-
return None
|
| 772 |
-
|
| 773 |
-
# Get options expirations
|
| 774 |
-
exp_url = f"{TRADIER_BASE_URL}/markets/options/expirations"
|
| 775 |
-
exp_params = {"symbol": ticker.upper()}
|
| 776 |
-
exp_resp = await client.get(exp_url, params=exp_params, headers=headers, timeout=10)
|
| 777 |
-
exp_data = exp_resp.json()
|
| 778 |
-
|
| 779 |
-
expirations = exp_data.get("expirations", {}).get("date", [])
|
| 780 |
-
if not expirations:
|
| 781 |
-
return None
|
| 782 |
-
|
| 783 |
-
# Use nearest expiration
|
| 784 |
-
nearest_exp = expirations[0] if isinstance(expirations, list) else expirations
|
| 785 |
-
|
| 786 |
-
# Get options chain
|
| 787 |
-
chain_url = f"{TRADIER_BASE_URL}/markets/options/chains"
|
| 788 |
-
chain_params = {
|
| 789 |
-
"symbol": ticker.upper(),
|
| 790 |
-
"expiration": nearest_exp,
|
| 791 |
-
"greeks": "true"
|
| 792 |
-
}
|
| 793 |
-
chain_resp = await client.get(chain_url, params=chain_params, headers=headers, timeout=10)
|
| 794 |
-
chain_data = chain_resp.json()
|
| 795 |
-
|
| 796 |
-
options = chain_data.get("options", {}).get("option", [])
|
| 797 |
-
if not options:
|
| 798 |
-
return None
|
| 799 |
-
|
| 800 |
-
# Filter calls and find ATM
|
| 801 |
-
calls = [o for o in options if o.get("option_type") == "call"]
|
| 802 |
-
if not calls:
|
| 803 |
-
return None
|
| 804 |
-
|
| 805 |
-
# Find ATM call (closest to current price)
|
| 806 |
-
atm_call = min(calls, key=lambda x: abs(x.get("strike", 0) - current_price))
|
| 807 |
-
|
| 808 |
-
# Get IV from greeks
|
| 809 |
-
greeks = atm_call.get("greeks", {})
|
| 810 |
-
iv = greeks.get("mid_iv", 0) or greeks.get("ask_iv", 0) or greeks.get("bid_iv", 0)
|
| 811 |
-
|
| 812 |
-
if not iv:
|
| 813 |
-
# Fallback to smv_vol if available
|
| 814 |
-
iv = greeks.get("smv_vol", 0)
|
| 815 |
-
|
| 816 |
-
if not iv:
|
| 817 |
-
return None
|
| 818 |
-
|
| 819 |
-
iv_pct = iv * 100 # Convert to percentage
|
| 820 |
-
|
| 821 |
-
# Interpretation
|
| 822 |
-
if iv_pct < 25:
|
| 823 |
-
interpretation = "Low IV - Market expects limited price movement"
|
| 824 |
-
swot_impact = "OPPORTUNITY"
|
| 825 |
-
elif iv_pct < 40:
|
| 826 |
-
interpretation = "Moderate IV - Normal expected movement"
|
| 827 |
-
swot_impact = "NEUTRAL"
|
| 828 |
-
elif iv_pct < 60:
|
| 829 |
-
interpretation = "High IV - Market expects significant movement"
|
| 830 |
-
swot_impact = "THREAT"
|
| 831 |
-
else:
|
| 832 |
-
interpretation = "Very high IV - Extreme movement expected (earnings, event)"
|
| 833 |
-
swot_impact = "THREAT"
|
| 834 |
-
|
| 835 |
-
# Use quote trade_date if available, else today
|
| 836 |
-
trade_date = quote.get("trade_date", datetime.now().strftime("%Y-%m-%d"))
|
| 837 |
-
if isinstance(trade_date, str) and "T" in trade_date:
|
| 838 |
-
trade_date = trade_date.split("T")[0]
|
| 839 |
-
|
| 840 |
-
return {
|
| 841 |
-
"metric": "Implied Volatility",
|
| 842 |
-
"ticker": ticker.upper(),
|
| 843 |
-
"value": round(iv_pct, 2),
|
| 844 |
-
"unit": "%",
|
| 845 |
-
"strike": atm_call.get("strike"),
|
| 846 |
-
"expiration": nearest_exp,
|
| 847 |
-
"interpretation": interpretation,
|
| 848 |
-
"swot_category": swot_impact,
|
| 849 |
-
"source": "Tradier",
|
| 850 |
-
"as_of": trade_date
|
| 851 |
-
}
|
| 852 |
-
|
| 853 |
-
except Exception as e:
|
| 854 |
-
logger.error(f"Tradier IV fetch error for {ticker}: {e}")
|
| 855 |
-
return None
|
| 856 |
-
|
| 857 |
-
|
| 858 |
# ============================================================
|
| 859 |
# MULTI-SOURCE AGGREGATOR
|
| 860 |
# ============================================================
|
|
|
|
| 52 |
# API Keys (optional - enables authoritative sources)
|
| 53 |
FRED_API_KEY = os.getenv("FRED_API_KEY") or os.getenv("FRED_VIX_API_KEY") # Get free key: https://fred.stlouisfed.org/docs/api/api_key.html
|
| 54 |
ALPHA_VANTAGE_KEY = os.getenv("ALPHA_VANTAGE_API_KEY") # Get free key: https://www.alphavantage.co/support/#api-key
|
|
|
|
| 55 |
|
| 56 |
# Alpha Vantage API configuration (Secondary for Beta, Historical Volatility)
|
| 57 |
ALPHA_VANTAGE_BASE_URL = "https://www.alphavantage.co/query"
|
| 58 |
|
|
|
|
|
|
|
|
|
|
| 59 |
# Yahoo Finance requires browser-like headers
|
| 60 |
YAHOO_HEADERS = {
|
| 61 |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
|
|
| 730 |
return None
|
| 731 |
|
| 732 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 733 |
# ============================================================
|
| 734 |
# MULTI-SOURCE AGGREGATOR
|
| 735 |
# ============================================================
|
mcp_client.py
CHANGED
|
@@ -356,7 +356,7 @@ async def call_volatility_mcp(ticker: str) -> dict:
|
|
| 356 |
|
| 357 |
|
| 358 |
async def call_volatility_all_sources_mcp(ticker: str) -> dict:
|
| 359 |
-
"""Fetch volatility from ALL sources (Yahoo + Alpha Vantage
|
| 360 |
return await call_mcp_server(
|
| 361 |
"volatility-basket",
|
| 362 |
"get_all_sources_volatility",
|
|
|
|
| 356 |
|
| 357 |
|
| 358 |
async def call_volatility_all_sources_mcp(ticker: str) -> dict:
|
| 359 |
+
"""Fetch volatility from ALL sources (Yahoo + Alpha Vantage)."""
|
| 360 |
return await call_mcp_server(
|
| 361 |
"volatility-basket",
|
| 362 |
"get_all_sources_volatility",
|