@@ -1385,80 +1385,6 @@ async def compute_implied_vol(
13851385 return {"error" : str (e )}
13861386
13871387
1388- @mcp .tool ()
1389- async def fit_vol_surface (
1390- symbol : str ,
1391- spot_price : float ,
1392- quotes : dict [str , Any ],
1393- risk_free_rate : float = 0.05 ,
1394- beta : float = 1.0 ,
1395- ) -> dict [str , Any ]:
1396- """
1397- Fit SABR volatility surface to market IV quotes.
1398-
1399- SABR model captures volatility smile/skew patterns for more realistic
1400- option pricing and risk management.
1401-
1402- Args:
1403- symbol: Underlying symbol
1404- spot_price: Current underlying price
1405- quotes: Dictionary with 'strikes' and 'ivs' lists, plus 'dte'
1406- Example: {"strikes": [95, 100, 105], "ivs": [0.25, 0.22, 0.24], "dte": 30}
1407- risk_free_rate: Risk-free rate for forward calculation
1408- beta: SABR beta parameter (0=normal, 1=lognormal, 0.5=CIR)
1409-
1410- Returns:
1411- Dictionary with SABR parameters, fit quality, and interpolated smile
1412- """
1413- import pandas as pd
1414-
1415- from quantcore .options .adapters .pysabr_adapter import (
1416- fit_sabr_surface ,
1417- get_sabr_skew_metrics ,
1418- )
1419-
1420- try :
1421- # Validate inputs
1422- strikes = quotes .get ("strikes" , [])
1423- ivs = quotes .get ("ivs" , [])
1424- dte = quotes .get ("dte" , 30 )
1425-
1426- if len (strikes ) < 3 or len (strikes ) != len (ivs ):
1427- return {"error" : "Need at least 3 strike/IV pairs with matching lengths" }
1428-
1429- # Create DataFrame
1430- quotes_df = pd .DataFrame ({"strike" : strikes , "iv" : ivs })
1431-
1432- # Calculate forward price
1433- time_to_expiry = dte / 365.0
1434- forward = spot_price * np .exp (risk_free_rate * time_to_expiry )
1435-
1436- # Fit SABR
1437- result = fit_sabr_surface (
1438- quotes = quotes_df ,
1439- forward = forward ,
1440- time_to_expiry = time_to_expiry ,
1441- beta = beta ,
1442- )
1443-
1444- # Add skew metrics
1445- skew_metrics = get_sabr_skew_metrics (
1446- result ["params" ],
1447- forward = forward ,
1448- time_to_expiry = time_to_expiry ,
1449- )
1450-
1451- result ["symbol" ] = symbol
1452- result ["spot_price" ] = spot_price
1453- result ["forward_price" ] = forward
1454- result ["dte" ] = dte
1455- result ["skew_metrics" ] = skew_metrics
1456-
1457- return result
1458-
1459- except Exception as e :
1460- return {"error" : str (e )}
1461-
14621388
14631389@mcp .tool ()
14641390async def analyze_option_structure (
@@ -4329,198 +4255,6 @@ async def get_event_calendar(
43294255# =============================================================================
43304256
43314257
4332- @mcp .tool ()
4333- async def execute_paper_trade (
4334- symbol : str ,
4335- direction : str ,
4336- quantity : int ,
4337- structure_type : str = "single" ,
4338- confidence : float = 0.5 ,
4339- reason : str = "" ,
4340- ) -> dict [str , Any ]:
4341- """
4342- Execute a paper trade for testing strategies.
4343-
4344- Simulates realistic order execution with:
4345- - Slippage estimation
4346- - Fill simulation
4347- - Position tracking
4348-
4349- Note: This is internal simulation. For broker paper trading,
4350- use Alpaca MCP or eTrade sandbox.
4351-
4352- Args:
4353- symbol: Stock symbol
4354- direction: "long" or "short"
4355- quantity: Number of shares/contracts
4356- structure_type: Trade type ("single", "vertical", "iron_condor")
4357- confidence: Signal confidence (0-1)
4358- reason: Trade rationale
4359-
4360- Returns:
4361- Order confirmation with simulated fill
4362- """
4363- import uuid
4364- from datetime import datetime
4365-
4366- from quantcore .data .storage import DataStore
4367-
4368- store = DataStore ()
4369-
4370- try :
4371- # Get current price
4372- df = store .load_ohlcv (symbol , Timeframe .D1 )
4373-
4374- if df .empty :
4375- return {"error" : f"No data for { symbol } " }
4376-
4377- current_price = float (df ["close" ].iloc [- 1 ])
4378-
4379- # Simulate slippage (0.05% for liquid stocks)
4380- slippage_pct = 0.0005
4381- if direction .lower () == "long" :
4382- fill_price = current_price * (1 + slippage_pct )
4383- else :
4384- fill_price = current_price * (1 - slippage_pct )
4385-
4386- # Generate order
4387- order_id = str (uuid .uuid4 ())[:8 ]
4388- timestamp = datetime .now ()
4389-
4390- order = {
4391- "order_id" : order_id ,
4392- "timestamp" : timestamp .isoformat (),
4393- "symbol" : symbol ,
4394- "direction" : direction ,
4395- "quantity" : quantity ,
4396- "structure_type" : structure_type ,
4397- "confidence" : confidence ,
4398- "reason" : reason ,
4399- "execution" : {
4400- "status" : "FILLED" ,
4401- "fill_price" : round (fill_price , 2 ),
4402- "fill_timestamp" : timestamp .isoformat (),
4403- "slippage_bps" : round (slippage_pct * 10000 , 1 ),
4404- "notional_value" : round (fill_price * quantity , 2 ),
4405- },
4406- "market_context" : {
4407- "current_price" : current_price ,
4408- "bid_estimate" : round (current_price * 0.9999 , 2 ),
4409- "ask_estimate" : round (current_price * 1.0001 , 2 ),
4410- },
4411- }
4412-
4413- return {
4414- "success" : True ,
4415- "order" : order ,
4416- "message" : f"Paper trade executed: { direction } { quantity } { symbol } @ { fill_price :.2f} " ,
4417- }
4418-
4419- except Exception as e :
4420- return {"error" : str (e )}
4421- finally :
4422- store .close ()
4423-
4424-
4425- @mcp .tool ()
4426- async def get_order_book_snapshot (
4427- symbol : str ,
4428- levels : int = 5 ,
4429- ) -> dict [str , Any ]:
4430- """
4431- Get simulated order book snapshot.
4432-
4433- Returns bid/ask levels with depth for market microstructure analysis.
4434-
4435- Note: This is a simulated order book based on historical data.
4436- For live order book, use broker MCP.
4437-
4438- Args:
4439- symbol: Stock symbol
4440- levels: Number of price levels to show
4441-
4442- Returns:
4443- Order book with bid/ask levels, spread, and depth
4444- """
4445- from quantcore .data .storage import DataStore
4446-
4447- store = DataStore ()
4448-
4449- try :
4450- df = store .load_ohlcv (symbol , Timeframe .D1 )
4451-
4452- if df .empty :
4453- return {"error" : f"No data for { symbol } " }
4454-
4455- # Get current price and volatility
4456- current_price = float (df ["close" ].iloc [- 1 ])
4457- daily_range = float (df ["high" ].iloc [- 1 ] - df ["low" ].iloc [- 1 ])
4458- avg_volume = float (df ["volume" ].tail (20 ).mean ())
4459-
4460- # Estimate spread from high-low
4461- spread_estimate = daily_range / current_price * 0.1 # Simplified
4462- half_spread = max (spread_estimate / 2 , 0.0001 )
4463-
4464- # Generate synthetic order book
4465- bids = []
4466- asks = []
4467-
4468- for i in range (levels ):
4469- # Bid side (below mid)
4470- bid_price = current_price * (1 - half_spread - i * 0.0001 )
4471- bid_size = int (avg_volume / 100 * (levels - i ) / levels )
4472- bids .append (
4473- {
4474- "level" : i + 1 ,
4475- "price" : round (bid_price , 2 ),
4476- "size" : bid_size ,
4477- "orders" : max (1 , bid_size // 100 ),
4478- }
4479- )
4480-
4481- # Ask side (above mid)
4482- ask_price = current_price * (1 + half_spread + i * 0.0001 )
4483- ask_size = int (avg_volume / 100 * (levels - i ) / levels )
4484- asks .append (
4485- {
4486- "level" : i + 1 ,
4487- "price" : round (ask_price , 2 ),
4488- "size" : ask_size ,
4489- "orders" : max (1 , ask_size // 100 ),
4490- }
4491- )
4492-
4493- best_bid = bids [0 ]["price" ]
4494- best_ask = asks [0 ]["price" ]
4495- spread = best_ask - best_bid
4496- spread_bps = (spread / current_price ) * 10000
4497-
4498- return {
4499- "symbol" : symbol ,
4500- "timestamp" : str (df .index [- 1 ]),
4501- "best_bid" : best_bid ,
4502- "best_ask" : best_ask ,
4503- "mid_price" : round ((best_bid + best_ask ) / 2 , 2 ),
4504- "spread" : round (spread , 4 ),
4505- "spread_bps" : round (spread_bps , 2 ),
4506- "bids" : bids ,
4507- "asks" : asks ,
4508- "depth" : {
4509- "total_bid_size" : sum (b ["size" ] for b in bids ),
4510- "total_ask_size" : sum (a ["size" ] for a in asks ),
4511- "imbalance" : round (
4512- (sum (b ["size" ] for b in bids ) - sum (a ["size" ] for a in asks ))
4513- / (sum (b ["size" ] for b in bids ) + sum (a ["size" ] for a in asks )),
4514- 3 ,
4515- ),
4516- },
4517- "note" : "Simulated order book based on historical data. Use broker MCP for live data." ,
4518- }
4519-
4520- except Exception as e :
4521- return {"error" : str (e )}
4522- finally :
4523- store .close ()
45244258
45254259
45264260# =============================================================================
0 commit comments