Skip to main content
Glama
AlvisoOculus

OptionsAhoy: Stock Equity and Tax Optimizer

concentration_analyze

Read-onlyIdempotent

Measure single-stock concentration risk and quantify loss exposure at 30/50/70% drops. Compare after-tax three-year strategies: sell down, hold, or hedge with put/collar, including federal, state, and NIIT taxes.

Instructions

Use this when someone asks how risky a large single-stock position is, whether too much of their net worth is in one stock, whether they are over-concentrated in a single name, or how to reduce or diversify a concentrated position. Single-stock concentration risk analysis on an existing position. For standalone hedge pricing use protective_put_price; for the tax math on the option exercise or RSU vest that created the concentration, route to amt_iso_optimize / nso_calculate / rsu_sell_vs_hold first. Quantifies drawdown exposure at 30/50/70% downside, then compares three after-tax strategies over a three-year horizon (sell-down to target weight, hold, hedge with put or zero-cost collar), accounting for federal LTCG, state tax, the 3.8% Net Investment Income Tax (NIIT), and reinvestment opportunity cost. totalAssets (concentrated position + everything else) frames risk relative to the whole portfolio. Returns a top-level object with keys: concentration (position/totalAssets), riskBand (Low / Moderate / Concentrated / Highly concentrated / Extreme), isLongTermToday, longTermDate, daysUntilLongTerm, lossExposure ({drop, dollarLoss, newConcentration} for 30/50/70% drops), waitForLtInsight, schedule (yearly sales with per-year tax), hedging ({kind, protectionLevel, tenorYears, strike, putPrice, callStrike, callPrice, netPremium, sigma, riskFreeRate} - a 1-year 30%-OTM put by default, or the structure named by hedgeChoice), sectorContextLine, advisorBenchmarkLine. Example call: {positionValue: 400000, costBasis: 100000, acquisitionDate: "2022-01-01", sector: "tech_software", stateCode: "CA", filingStatus: "single", ordinaryIncome: 200000, totalAssets: 1200000, volatility: 0.45, ticker: "NVDA"}. Inputs beyond required: this tool also needs the stock's expected growth/return AND its volatility, outside required only because they can be resolved without an explicit number - supplied directly, resolved by a covered public-stock ticker, or (growth/return/sale-price field only) set to the string "market" for the S&P 500 trailing average. Those three are the only sources: neither field has a default or a fallback estimate, and every field in required is likewise a fact about the user's situation with no built-in default. A call that neither supplies nor resolves growth or volatility returns a required-field error naming the field; a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check, and it silently changes the result. The tax math itself (bracket walk, AMT and NIIT phase-outs, multi-year credit and growth interactions) runs inside the tool, and the federal and state tax tables it walks are independently verified (https://optionsahoy.com/verification). Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at https://optionsahoy.com/beta?src=mcp_multi.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sectorYesSector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided (a sector-typical implied volatility).
tickerNoOptional public-stock symbol (e.g. "NVDA", "AAPL"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a "required field" error naming that field: pass it explicitly, or (for the growth/return/sale-price field) pass the string "market" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve growth.
costBasisYesTotal cost basis of the position, USD (sum of strikes paid + ordinary-income inclusions on RSU vest / NSO exercise / disqualified ISO). Must come from the user.
stateCodeYesTwo-letter US state code. Drives state LTCG and ordinary brackets.
volatilityNoAnnualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, derived internally (the drag formula is horizon-dependent). This value must come from the user or from a `ticker` that resolves it as of the last market close; if neither supplies it, ask the user rather than estimating one; only as a last fallback does hedge pricing use a sector-typical implied volatility.
hedgeChoiceNoOptional hedge specification. When supplied, the `hedging` output block prices this exact structure (kind, protectionLevel, tenorYears, plus a short call for a collar) instead of the default 1-year 30%-OTM put; the sell-down-vs-hold schedule is unaffected. For full standalone hedge pricing (protective put / collar / put spread) use `protective_put_price`.
totalAssetsYesTotal investable portfolio in dollars (concentrated position + everything else). This value must come from the user, never inferred; if the user did not state it, ask.
filingStatusYesFederal filing status. Drives LTCG brackets and the NIIT MAGI threshold.
positionValueYesCurrent market value of the concentrated single-stock position, USD. Must come from the user.
ordinaryIncomeYesAnnual ordinary income before any sales, USD. Baseline for LTCG bracket determination. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
volatilityDragNoAlternative to `volatility`: the multiplicative price haircut already computed for the horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). This field is for a drag figure that already exists from a prior computation; the drag formula is horizon-dependent, so a figure derived for a different horizon does not carry over. Supplying `volatility` instead lets the tool derive it.
acquisitionDateYesEarliest acquisition date in the lot (YYYY-MM-DD). Drives the 1-year LTCG threshold and the long-term-vs-short-term tax routing.
expectedMarketReturnNoAnnual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for the 3-year horizon if omitted; the string "market" names that same default explicitly.
expectedPositionReturnNoAnnual expected return on the concentrated stock as a decimal (0.10 = 10%), or the string "market" to use the S&P 500 trailing average when the user has no view. Required unless `ticker` resolves it from trailing CAGR. This tool has no default for it: a value not stated by the user, not resolved by a covered `ticker`, and not the "market" sentinel is outside the input contract.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
hedgingYesModeled cost of a protective hedge covering the full position. Defaults to a 1-year 30%-OTM put; if a `hedgeChoice` is supplied, this block prices that structure (kind / protectionLevel / tenorYears, plus a short call for a collar).
riskBandYesQualitative concentration band for the position weight.
scheduleYesSell-down plans over 1, 2, and 3 years; empty when the position is already at or below the target weight.
longTermDateYesDate the position turns long-term (acquisitionDate + 1 year). ISO 8601 date-time string.
lossExposureYesDollar damage at 30/50/70% single-stock drawdowns.
concentrationYesPosition value / total assets, 0..1.
isLongTermTodayYesTrue when the position already qualifies for long-term capital gains treatment.
waitForLtInsightYesTax saved by waiting for long-term treatment before selling; null when already long-term or no sale is needed.
daysUntilLongTermYesDays until long-term treatment; 0 when already long-term.
sectorContextLineYesOne-line volatility/drawdown context for the chosen sector.
advisorBenchmarkLineYesOne-line comparison of the user weight vs the common advisor 10% single-name guideline.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changedv1.10.2
    • changedInput schema / properties / ticker / description
      Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve: pass that field explicitly, or (for the growth/return/sale-price field) pass the string \"market\" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve which field."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a \"required field\" error naming that field: pass it explicitly, or (for the growth/return/sale-price field) pass the string \"market\" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve growth."
    • changedInput schema / properties / volatility / description
      Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, derived internally (the drag formula is horizon-dependent). This value must come from the user or from a `ticker` that resolves it from the cached implied-vol table; if neither supplies it, ask the user rather than estimating one; only as a last fallback does hedge pricing use a sector-typical implied volatility."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, derived internally (the drag formula is horizon-dependent). This value must come from the user or from a `ticker` that resolves it as of the last market close; if neither supplies it, ask the user rather than estimating one; only as a last fallback does hedge pricing use a sector-typical implied volatility."
  2. Addedv1.10.1
  3. Removedv1.9.8
  4. Changed19 schema fields changedv1.9.7
    • changedInput schema / properties / hedgeChoice / description
      Previous value: -"Optional hedge specification. When provided, adds a hedged scenario to the sell-down-vs-hold comparison and computes the post-tax NFV of the hedged hold. Omit to compare only sell-down vs. hold."New value: +"Optional hedge specification. When supplied, the `hedging` output block prices this exact structure (kind, protectionLevel, tenorYears, plus a short call for a collar) instead of the default 1-year 30%-OTM put; the sell-down-vs-hold schedule is unaffected. For full standalone hedge pricing (protective put / collar / put spread) use `protective_put_price`."
    • changedInput schema / properties / hedgeChoice / properties / kind / description
      Previous value: -"Hedge instrument: 'put' (bare protective put — pay premium for downside protection) or 'collar' (put financed by a short call — caps upside in exchange for lower or zero net premium)."New value: +"Hedge instrument: 'put' (bare protective put, pay premium for downside protection) or 'collar' (put financed by a short call, caps upside in exchange for lower or zero net premium)."
    • changedInput schema / properties / sector / description
      Previous value: -"Sector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided. See lib/markets/sector-stats.ts for the per-sector annualVol table; this tool applies IV_OVER_RV_MULTIPLIER (1.20) to the realized vol to approximate implied vol."New value: +"Sector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided (a sector-typical implied volatility)."
    • addedInput schema / properties / stateCode / enum
      Added value: +[
      +  "AK",
      +  "AL",
      +  "AR",
      +  "AZ",
      +  "CA",
      +  "CO",
      +  "CT",
      +  "DC",
      +  "DE",
      +  "FL",
      +  "GA",
      +  "HI",
      +  "IA",
      +  "ID",
      +  "IL",
      +  "IN",
      +  "KS",
      +  "KY",
      +  "LA",
      +  "MA",
      +  "MD",
      +  "ME",
      +  "MI",
      +  "MN",
      +  "MO",
      +  "MS",
      +  "MT",
      +  "NC",
      +  "ND",
      +  "NE",
      +  "NH",
      +  "NJ",
      +  "NM",
      +  "NV",
      +  "NY",
      +  "OH",
      +  "OK",
      +  "OR",
      +  "PA",
      +  "RI",
      +  "SC",
      +  "SD",
      +  "TN",
      +  "TX",
      +  "UT",
      +  "VA",
      +  "VT",
      +  "WA",
      +  "WI",
      +  "WV",
      +  "WY"
      +]
    • removedInput schema / properties / stateCode / pattern
      Removed value: -"^[A-Z]{2}$"
    • changedInput schema / properties / ticker / description
      Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. About 90 large-cap symbols resolve a return; a slightly smaller set (~85) also resolves volatility. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it."
    • changedInput schema / properties / volatility / description
      Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them; only as a last fallback does hedge pricing use sector_stats.annualVol × 1.20."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself; the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them; only as a last fallback does hedge pricing fall back to a sector-typical implied volatility."
    • addedInput schema / properties / volatilityDrag
      Added value: +{
      +  "description": "Alternative to `volatility`: the multiplicative price haircut already computed for the horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). Most callers should pass `volatility` and let the tool compute the drag; only pass this if you already have a horizon drag figure. The model MUST NOT compute it itself.",
      +  "maximum": 0.99,
      +  "minimum": 0,
      +  "type": "number"
      +}
    • changedOutput schema / properties / hedging / description
      Previous value: -"Black-Scholes cost of a 1-year 30%-OTM protective put covering the full position."New value: +"Modeled cost of a protective hedge covering the full position. Defaults to a 1-year 30%-OTM put; if a `hedgeChoice` is supplied, this block prices that structure (kind / protectionLevel / tenorYears, plus a short call for a collar)."
    • addedOutput schema / properties / hedging / properties / callPrice
      Added value: +{
      +  "description": "Collar short-call premium in dollars received. Omitted for a put.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / hedging / properties / callStrike
      Added value: +{
      +  "description": "Collar short-call strike in dollars ((1 + upsideCapPct) x position value). Omitted for a put.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / hedging / properties / kind
      Added value: +{
      +  "description": "Structure priced: \"put\" (default) or \"collar\" when a hedgeChoice with kind:\"collar\" and upsideCapPct was supplied.",
      +  "enum": [
      +    "put",
      +    "collar"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / hedging / properties / netPremium
      Added value: +{
      +  "description": "Net premium paid in dollars: putPrice for a put, max(0, putPrice - callPrice) for a collar.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / hedging / properties / protectionLevel
      Added value: +{
      +  "description": "Floor as a fraction below spot (0.30 = a 30%-OTM put). Echoes hedgeChoice.protectionLevel, else 0.30.",
      +  "type": "number"
      +}
    • changedOutput schema / properties / hedging / properties / putPrice / description
      Previous value: -"Put premium in dollars for the 1-year tenor."New value: +"Gross long-put premium in dollars for the tenor."
    • changedOutput schema / properties / hedging / properties / sigma / description
      Previous value: -"Annualized volatility used in pricing (explicit/ticker implied vol, else sector realized vol x 1.20)."New value: +"Annualized volatility used in pricing (explicit or ticker-implied vol, else a sector-typical implied volatility)."
    • changedOutput schema / properties / hedging / properties / strike / description
      Previous value: -"Put strike in dollars (70% of position value)."New value: +"Long put strike in dollars ((1 - protectionLevel) x position value)."
    • addedOutput schema / properties / hedging / properties / tenorYears
      Added value: +{
      +  "description": "Hedge tenor in years. Echoes hedgeChoice.tenorYears, else 1.",
      +  "type": "number"
      +}
    • changedOutput schema / properties / hedging / required
      Previous value: -[
      -  "strike",
      -  "putPrice",
      -  "sigma",
      -  "riskFreeRate"
      -]New value: +[
      +  "kind",
      +  "protectionLevel",
      +  "tenorYears",
      +  "strike",
      +  "putPrice",
      +  "netPremium",
      +  "sigma",
      +  "riskFreeRate"
      +]
  5. Changed3 schema fields changedv1.9.2
    • changedInput schema / properties / ticker / description
      Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes the ticker's trailing CAGR for any unsupplied expected-return / sale-price field instead of requiring the caller to invent one. ~90 symbols covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."
    • changedInput schema / properties / volatility / description
      Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them; only when neither is supplied does hedge pricing fall back to sector_stats.annualVol × 1.20."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them; only as a last fallback does hedge pricing use sector_stats.annualVol × 1.20."
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "description": "Single-stock concentration analysis. All dollar amounts are USD.",
      +  "properties": {
      +    "advisorBenchmarkLine": {
      +      "description": "One-line comparison of the user weight vs the common advisor 10% single-name guideline.",
      +      "type": "string"
      +    },
      +    "concentration": {
      +      "description": "Position value / total assets, 0..1.",
      +      "type": "number"
      +    },
      +    "daysUntilLongTerm": {
      +      "description": "Days until long-term treatment; 0 when already long-term.",
      +      "type": "number"
      +    },
      +    "hedging": {
      +      "description": "Black-Scholes cost of a 1-year 30%-OTM protective put covering the full position.",
      +      "properties": {
      +        "putPrice": {
      +          "description": "Put premium in dollars for the 1-year tenor.",
      +          "type": "number"
      +        },
      +        "riskFreeRate": {
      +          "description": "Annualized risk-free rate used in pricing, as a decimal.",
      +          "type": "number"
      +        },
      +        "sigma": {
      +          "description": "Annualized volatility used in pricing (explicit/ticker implied vol, else sector realized vol x 1.20).",
      +          "type": "number"
      +        },
      +        "strike": {
      +          "description": "Put strike in dollars (70% of position value).",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "strike",
      +        "putPrice",
      +        "sigma",
      +        "riskFreeRate"
      +      ],
      +      "type": "object"
      +    },
      +    "isLongTermToday": {
      +      "description": "True when the position already qualifies for long-term capital gains treatment.",
      +      "type": "boolean"
      +    },
      +    "longTermDate": {
      +      "description": "Date the position turns long-term (acquisitionDate + 1 year). ISO 8601 date-time string.",
      +      "type": "string"
      +    },
      +    "lossExposure": {
      +      "description": "Dollar damage at 30/50/70% single-stock drawdowns.",
      +      "items": {
      +        "properties": {
      +          "dollarLoss": {
      +            "description": "Dollars lost at this drawdown.",
      +            "type": "number"
      +          },
      +          "drop": {
      +            "description": "Modeled drawdown as a fraction of position value (0.30, 0.50, 0.70).",
      +            "type": "number"
      +          },
      +          "newConcentration": {
      +            "description": "Portfolio concentration (0..1) after the drawdown.",
      +            "type": "number"
      +          }
      +        },
      +        "required": [
      +          "drop",
      +          "dollarLoss",
      +          "newConcentration"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "riskBand": {
      +      "description": "Qualitative concentration band for the position weight.",
      +      "enum": [
      +        "Low",
      +        "Moderate",
      +        "Concentrated",
      +        "Highly concentrated",
      +        "Extreme"
      +      ],
      +      "type": "string"
      +    },
      +    "schedule": {
      +      "description": "Sell-down plans over 1, 2, and 3 years; empty when the position is already at or below the target weight.",
      +      "items": {
      +        "properties": {
      +          "endOfHorizonWealth": {
      +            "description": "Total after-tax wealth in dollars at the end of the 3-year comparison horizon.",
      +            "type": "number"
      +          },
      +          "planKey": {
      +            "description": "Plan identifier.",
      +            "enum": [
      +              "lump_sum",
      +              "two_year",
      +              "three_year"
      +            ],
      +            "type": "string"
      +          },
      +          "planLabel": {
      +            "description": "Human-readable plan name, e.g. \"Sell over 2 years\".",
      +            "type": "string"
      +          },
      +          "savingsVsLumpSum": {
      +            "description": "Raw tax saved in dollars vs selling everything today; positive means this plan pays less tax.",
      +            "type": "number"
      +          },
      +          "taxBreakdown": {
      +            "description": "Plan-total tax slices, same-rate rows merged.",
      +            "items": {
      +              "description": "One tax slice: a dollar amount taxed at one rate.",
      +              "properties": {
      +                "amount": {
      +                  "description": "Dollars of gain in this slice.",
      +                  "type": "number"
      +                },
      +                "label": {
      +                  "description": "Tax line label, e.g. \"Federal LTCG\", \"NIIT\", \"California\".",
      +                  "type": "string"
      +                },
      +                "rate": {
      +                  "description": "Rate applied to this slice as a decimal (0.15 = 15%).",
      +                  "type": "number"
      +                },
      +                "tax": {
      +                  "description": "Tax in dollars: amount x rate.",
      +                  "type": "number"
      +                }
      +              },
      +              "required": [
      +                "label",
      +                "rate",
      +                "amount",
      +                "tax"
      +              ],
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "totalSale": {
      +            "description": "Total nominal sale dollars across the plan years.",
      +            "type": "number"
      +          },
      +          "totalTax": {
      +            "description": "Total tax in dollars across the plan years.",
      +            "type": "number"
      +          },
      +          "wealthByYear": {
      +            "description": "Total wealth in dollars at the end of each year, t = 0..3 (4 points). For charting.",
      +            "items": {
      +              "type": "number"
      +            },
      +            "type": "array"
      +          },
      +          "wealthVsLumpSum": {
      +            "description": "End-of-horizon wealth delta in dollars vs the sell-everything-today baseline; positive means this plan ends wealthier.",
      +            "type": "number"
      +          },
      +          "year1IsShortTerm": {
      +            "description": "True when the first sale year would be taxed at short-term rates.",
      +            "type": "boolean"
      +          },
      +          "yearlySales": {
      +            "description": "One entry per sale year: year (1-indexed), saleAmount, gainAmount, isLongTerm, federalTax, stateTax, totalTax in dollars, plus a per-slice breakdown.",
      +            "items": {
      +              "properties": {
      +                "breakdown": {
      +                  "description": "Per-rate tax slices for this sale.",
      +                  "items": {
      +                    "description": "One tax slice: a dollar amount taxed at one rate.",
      +                    "properties": {
      +                      "amount": {
      +                        "description": "Dollars of gain in this slice.",
      +                        "type": "number"
      +                      },
      +                      "label": {
      +                        "description": "Tax line label, e.g. \"Federal LTCG\", \"NIIT\", \"California\".",
      +                        "type": "string"
      +                      },
      +                      "rate": {
      +                        "description": "Rate applied to this slice as a decimal (0.15 = 15%).",
      +                        "type": "number"
      +                      },
      +                      "tax": {
      +                        "description": "Tax in dollars: amount x rate.",
      +                        "type": "number"
      +                      }
      +                    },
      +                    "required": [
      +                      "label",
      +                      "rate",
      +                      "amount",
      +                      "tax"
      +                    ],
      +                    "type": "object"
      +                  },
      +                  "type": "array"
      +                },
      +                "federalTax": {
      +                  "description": "Federal tax in dollars on this sale (including NIIT).",
      +                  "type": "number"
      +                },
      +                "gainAmount": {
      +                  "description": "Taxable gain in dollars within the sale.",
      +                  "type": "number"
      +                },
      +                "isLongTerm": {
      +                  "description": "True when this sale gets long-term capital gains treatment.",
      +                  "type": "boolean"
      +                },
      +                "saleAmount": {
      +                  "description": "Dollars sold this year.",
      +                  "type": "number"
      +                },
      +                "stateTax": {
      +                  "description": "State tax in dollars on this sale.",
      +                  "type": "number"
      +                },
      +                "totalTax": {
      +                  "description": "Total tax in dollars on this sale.",
      +                  "type": "number"
      +                },
      +                "year": {
      +                  "description": "Sale year, 1-indexed.",
      +                  "type": "number"
      +                }
      +              },
      +              "required": [
      +                "year",
      +                "saleAmount",
      +                "gainAmount",
      +                "isLongTerm",
      +                "federalTax",
      +                "stateTax",
      +                "totalTax",
      +                "breakdown"
      +              ],
      +              "type": "object"
      +            },
      +            "type": "array"
      +          }
      +        },
      +        "required": [
      +          "planKey",
      +          "planLabel",
      +          "yearlySales",
      +          "totalSale",
      +          "totalTax",
      +          "endOfHorizonWealth",
      +          "savingsVsLumpSum",
      +          "wealthVsLumpSum",
      +          "year1IsShortTerm",
      +          "taxBreakdown",
      +          "wealthByYear"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "sectorContextLine": {
      +      "description": "One-line volatility/drawdown context for the chosen sector.",
      +      "type": "string"
      +    },
      +    "waitForLtInsight": {
      +      "description": "Tax saved by waiting for long-term treatment before selling; null when already long-term or no sale is needed.",
      +      "properties": {
      +        "daysAway": {
      +          "description": "Days until that date.",
      +          "type": "number"
      +        },
      +        "delayedLumpSumTax": {
      +          "description": "Tax in dollars on the same sale executed after the long-term date.",
      +          "type": "number"
      +        },
      +        "immediateLumpSumTax": {
      +          "description": "Tax in dollars on the full sell-down executed today (short-term rates).",
      +          "type": "number"
      +        },
      +        "longTermDate": {
      +          "description": "Date the position turns long-term. ISO 8601 date-time string.",
      +          "type": "string"
      +        },
      +        "savings": {
      +          "description": "immediateLumpSumTax - delayedLumpSumTax in dollars (floored at 0).",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "longTermDate",
      +        "daysAway",
      +        "immediateLumpSumTax",
      +        "delayedLumpSumTax",
      +        "savings"
      +      ],
      +      "type": [
      +        "object",
      +        "null"
      +      ]
      +    }
      +  },
      +  "required": [
      +    "concentration",
      +    "riskBand",
      +    "isLongTermToday",
      +    "longTermDate",
      +    "daysUntilLongTerm",
      +    "lossExposure",
      +    "waitForLtInsight",
      +    "schedule",
      +    "hedging",
      +    "sectorContextLine",
      +    "advisorBenchmarkLine"
      +  ],
      +  "type": "object"
      +}
  6. Changed3 schema fields changedv1.3.4
    • changedInput schema / properties / volatility / description
      Previous value: -"Annualized implied volatility (sigma) of the stock. Optional. When omitted, falls back to sector_stats.annualVol × 1.20 (the IV-over-RV multiplier)."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them; only when neither is supplied does hedge pricing fall back to sector_stats.annualVol × 1.20."
    • removedInput schema / properties / volatilityDrag
      Removed value: -{
      -  "description": "Multiplicative haircut on the expected stock-price path at the 3-year analysis horizon (0..0.99), capturing the half-variance correction in compounded returns.",
      -  "maximum": 0.99,
      -  "minimum": 0,
      -  "type": "number"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "positionValue",
      -  "costBasis",
      -  "acquisitionDate",
      -  "sector",
      -  "stateCode",
      -  "filingStatus",
      -  "ordinaryIncome",
      -  "totalAssets",
      -  "volatilityDrag"
      -]New value: +[
      +  "positionValue",
      +  "costBasis",
      +  "acquisitionDate",
      +  "sector",
      +  "stateCode",
      +  "filingStatus",
      +  "ordinaryIncome",
      +  "totalAssets"
      +]
  7. Changed14 schema fields changedv1.2.2
    • addedInput schema / properties / acquisitionDate / description
      Added value: +"Earliest acquisition date in the lot (YYYY-MM-DD). Drives the 1-year LTCG threshold and the long-term-vs-short-term tax routing."
    • addedInput schema / properties / costBasis / description
      Added value: +"Total cost basis of the position, USD (sum of strikes paid + ordinary-income inclusions on RSU vest / NSO exercise / disqualified ISO)."
    • addedInput schema / properties / filingStatus / description
      Added value: +"Federal filing status. Drives LTCG brackets and the NIIT MAGI threshold."
    • addedInput schema / properties / hedgeChoice / description
      Added value: +"Optional hedge specification. When provided, adds a hedged scenario to the sell-down-vs-hold comparison and computes the post-tax NFV of the hedged hold. Omit to compare only sell-down vs. hold."
    • addedInput schema / properties / hedgeChoice / properties / kind / description
      Added value: +"Hedge instrument: 'put' (bare protective put — pay premium for downside protection) or 'collar' (put financed by a short call — caps upside in exchange for lower or zero net premium)."
    • addedInput schema / properties / hedgeChoice / properties / protectionLevel / description
      Added value: +"Put strike chosen as (1 − this fraction) × spot. 0.10 = 10% OTM put. Range 0.05..0.50."
    • addedInput schema / properties / hedgeChoice / properties / tenorYears / description
      Added value: +"Option tenor in years. 1 = 12-month; 0.25 = ~90-day."
    • addedInput schema / properties / hedgeChoice / properties / upsideCapPct / description
      Added value: +"For collars only: optional explicit upside cap as fraction above spot (e.g. 0.20 = 20% cap). Omit to let the tool solve for the cap that makes the collar zero-net-premium."
    • addedInput schema / properties / ordinaryIncome / description
      Added value: +"Annual W-2 ordinary income before any sales, USD. Baseline for LTCG bracket determination."
    • addedInput schema / properties / positionValue / description
      Added value: +"Current market value of the concentrated single-stock position, USD."
    • addedInput schema / properties / sector / description
      Added value: +"Sector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided. See lib/markets/sector-stats.ts for the per-sector annualVol table; this tool applies IV_OVER_RV_MULTIPLIER (1.20) to the realized vol to approximate implied vol."
    • addedInput schema / properties / stateCode / description
      Added value: +"Two-letter US state code. Drives state LTCG and ordinary brackets."
    • addedInput schema / properties / volatility / description
      Added value: +"Annualized implied volatility (sigma) of the stock. Optional. When omitted, falls back to sector_stats.annualVol × 1.20 (the IV-over-RV multiplier)."
    • addedInput schema / properties / volatilityDrag / description
      Added value: +"Multiplicative haircut on the expected stock-price path at the 3-year analysis horizon (0..0.99), capturing the half-variance correction in compounded returns."
  8. First observedv1.2.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial behavioral context beyond the readOnlyHint/idempotentHint annotations: it explains there is no default for growth/volatility, that a missing unresolved value returns a required-field error, that any syntactically valid number is accepted without provenance checks and silently changes the result, and that multi-tool results are independent single-position calculations. It also exposes the internal tax engine verification and the beta pointer. No contradiction with annotations—the description reinforces that this is a read-only analysis.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but deliberately organized: use cases first, then alternatives, then method/outputs, then the example, then the input-contract caveats, then verification. The length is justified by the tool's complexity, but some sentences could be tightened (e.g., the beta reference and verification link are secondary). It loses one point for being heavier than necessary while still front-loading the key information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 14 parameters, nested hedgeChoice, ticker resolution, no-default behavior, and a rich output schema, the description is complete: it covers use cases, method, output keys, example invocation, edge cases around missing values, and integration with sibling tools. An agent has everything needed to invoke the tool correctly and interpret its results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and each parameter description is thorough, so the baseline is 3. However, the tool description adds value beyond the schema by explaining the global input contract: the two parameters outside `required` (expected growth/return and volatility) and how they can be resolved via ticker, "market", or explicit value, plus the rule that a call failing to supply or resolve them errors out. The example call also helps tie parameters together. This is more than pure repetition, though the individual schema entries already carry much of the burden.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: "Single-stock concentration risk analysis on an existing position." It clearly describes the analysis method (drawdown exposure at 30/50/70%, three after-tax strategies) and distinguishes itself from siblings by naming protective_put_price, amt_iso_optimize, nso_calculate, and rsu_sell_vs_hold as the tools for adjacent concerns. An agent can tell exactly what this tool does and what it does not do.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use guidance is given: "Use this when someone asks how risky a large single-stock position is..." It also provides clear exclusions and routing: standalone hedge pricing goes to protective_put_price; tax math on option exercise/RSU vest goes to amt_iso_optimize / nso_calculate / rsu_sell_vs_hold. No ambiguity remains about when to choose this tool over alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.