Skip to main content
Glama
AlvisoOculus

OptionsAhoy: Stock Equity and Tax Optimizer

rsu_sell_vs_hold

Read-onlyIdempotent

Compare selling RSUs at vest versus holding to long-term capital gains, factoring in federal, state, FICA, and withholding shortfalls to guide your decision.

Instructions

Use this when someone asks whether to sell RSUs at vest or hold them, whether to sell their vested shares or diversify out of company stock, or about the tax and withholding on a restricted stock unit (RSU) vest. After-tax RSU vest analysis: sell-at-vest vs hold-to-long-term-capital-gains (LTCG) over holdYears. Covers federal ordinary tax, state tax, FICA (Social Security + Medicare + Additional Medicare), and the shortfall between mandatory 22% supplemental withholding and the user's marginal bracket. Use for RSUs at vest; for ISO/AMT use amt_iso_optimize, for NSO use nso_calculate. Deterministic and offline; tax tables compiled in. Returns vest, hold, sellNowInvest, holdMinusSell, and bracketJump; see outputSchema for the full shape. Example call: {shares: 1000, currentPrice: 100, ordinaryIncome: 200000, filingStatus: "single", stateCode: "CA", stillEmployed: true, holdYears: 2, volatility: 0.3, ticker: "MSFT"}. 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
sharesYesRestricted Stock Unit (RSU) shares vesting in this tranche. Must come from the user.
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.
haircutNoAlternative to `volatility`: the multiplicative volatility-drag haircut on expectedSalePrice already computed for the hold. Supply this OR `volatility` (if both are given, haircut wins). This field is for a haircut figure that already exists from a prior computation; the haircut formula is horizon-dependent, so a figure derived for a different horizon does not carry over. Supplying `volatility` instead lets the tool derive it.
holdYearsYesYears to hold after vest (0.25..5). Below 1 year triggers the short-term capital gains cliff (ordinary rates on appreciation). The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
stateCodeYesTwo-letter US state code.
volatilityNoAnnualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool derives the horizon-cumulative drag internally, and the correct 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.
currentPriceYesFair market value per share at vest, USD. Also the cost basis on retained shares. Must come from the user.
filingStatusYesFederal filing status.
stillEmployedYesTrue if still employed at vest. Drives FICA applicability and whether the 22% supplemental withholding rule applies.
ordinaryIncomeYesAnnual ordinary income before this vest, USD. Baseline for the bracket walk on the vest amount. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
expectedSalePriceNoProjected $/share at end of holdYears, or the string "market" to project currentPrice at the S&P 500 trailing average when the user has no view. Required unless `ticker` resolves it from currentPrice × (1 + trailing CAGR)^holdYears. 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.
expectedMarketReturnNoAnnual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted; the string "market" names that same default explicitly.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
holdYesKeep the after-tax shares for holdYears, then sell.
vestYesTax bill at vest on the full vest value (taxed as ordinary W-2 income).
bracketJumpYesMarginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket.
holdMinusSellYeshold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the vested shares; negative favors selling at vest and reinvesting.
sellNowInvestYesCounterfactual: sell every share at vest and reinvest the net cash at expectedMarketReturn for holdYears.

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 derives the horizon-cumulative drag internally, and the correct 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."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 derives the horizon-cumulative drag internally, and the correct 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."
  2. Addedv1.10.1
  3. Removedv1.9.8
  4. Changed4 schema fields changedv1.9.7
    • 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 computes the horizon-cumulative drag 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."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag 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."
  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 computes the horizon-cumulative drag 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."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag 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."
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "description": "RSU sell-at-vest vs hold result. All dollar amounts are USD.",
      +  "properties": {
      +    "bracketJump": {
      +      "description": "Marginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket.",
      +      "properties": {
      +        "fromRate": {
      +          "description": "Marginal federal rate before the event, as a decimal (0.24 = 24%).",
      +          "type": "number"
      +        },
      +        "thresholdAtJump": {
      +          "description": "Taxable-income threshold in dollars where the bracket changes.",
      +          "type": "number"
      +        },
      +        "toRate": {
      +          "description": "Marginal federal rate after the event, as a decimal.",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "fromRate",
      +        "toRate",
      +        "thresholdAtJump"
      +      ],
      +      "type": [
      +        "object",
      +        "null"
      +      ]
      +    },
      +    "hold": {
      +      "description": "Keep the after-tax shares for holdYears, then sell.",
      +      "properties": {
      +        "capGainFederal": {
      +          "description": "Federal capital gains tax in dollars on the gain: LTCG (including NIIT) when isLongTerm, else the marginal ordinary rate.",
      +          "type": "number"
      +        },
      +        "capGainState": {
      +          "description": "State capital gains tax in dollars on the gain.",
      +          "type": "number"
      +        },
      +        "capGainTotal": {
      +          "description": "Total capital gains tax in dollars at sale.",
      +          "type": "number"
      +        },
      +        "costBasis": {
      +          "description": "Cost basis per share in dollars (FMV at vest).",
      +          "type": "number"
      +        },
      +        "effectiveSalePrice": {
      +          "description": "Projected sale price per share in dollars at end of holdYears, after the volatility haircut.",
      +          "type": "number"
      +        },
      +        "expectedGain": {
      +          "description": "Expected capital gain in dollars on the retained shares: (effectiveSalePrice - costBasis) x sharesRetained.",
      +          "type": "number"
      +        },
      +        "isLongTerm": {
      +          "description": "True when holdYears >= 1, so appreciation gets long-term capital gains treatment.",
      +          "type": "boolean"
      +        },
      +        "netAtYearN": {
      +          "description": "Net after-tax value of holding in dollars at end of holdYears: sale proceeds - capGainTotal.",
      +          "type": "number"
      +        },
      +        "sharesRetained": {
      +          "description": "Shares kept after the sell-to-cover dollar-equivalent of the vest tax.",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "costBasis",
      +        "effectiveSalePrice",
      +        "sharesRetained",
      +        "expectedGain",
      +        "capGainFederal",
      +        "capGainState",
      +        "capGainTotal",
      +        "isLongTerm",
      +        "netAtYearN"
      +      ],
      +      "type": "object"
      +    },
      +    "holdMinusSell": {
      +      "description": "hold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the vested shares; negative favors selling at vest and reinvesting.",
      +      "type": "number"
      +    },
      +    "sellNowInvest": {
      +      "description": "Counterfactual: sell every share at vest and reinvest the net cash at expectedMarketReturn for holdYears.",
      +      "properties": {
      +        "capGainFederal": {
      +          "description": "Federal capital gains tax in dollars on the market gain: LTCG (including NIIT) when isLongTerm, else the marginal ordinary rate.",
      +          "type": "number"
      +        },
      +        "capGainState": {
      +          "description": "State capital gains tax in dollars on the market gain.",
      +          "type": "number"
      +        },
      +        "capGainTotal": {
      +          "description": "Total capital gains tax in dollars on the market gain.",
      +          "type": "number"
      +        },
      +        "isLongTerm": {
      +          "description": "True when holdYears >= 1.",
      +          "type": "boolean"
      +        },
      +        "marketGain": {
      +          "description": "Market growth in dollars on the reinvested cash over holdYears.",
      +          "type": "number"
      +        },
      +        "netAtYearN": {
      +          "description": "Net after-tax value of sell-at-vest-and-invest in dollars at end of holdYears.",
      +          "type": "number"
      +        },
      +        "netCashAtY0": {
      +          "description": "Net cash in dollars at vest available to reinvest (equals vest.netCashAtVest).",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "netCashAtY0",
      +        "marketGain",
      +        "capGainFederal",
      +        "capGainState",
      +        "capGainTotal",
      +        "isLongTerm",
      +        "netAtYearN"
      +      ],
      +      "type": "object"
      +    },
      +    "vest": {
      +      "description": "Tax bill at vest on the full vest value (taxed as ordinary W-2 income).",
      +      "properties": {
      +        "additionalMedicare": {
      +          "description": "Additional Medicare (0.9%) tax in dollars.",
      +          "type": "number"
      +        },
      +        "federal": {
      +          "description": "True federal ordinary income tax on the vest value in dollars (marginal bracket, not the withholding).",
      +          "type": "number"
      +        },
      +        "federalWithheldAtVest": {
      +          "description": "Mandatory federal supplemental withholding in dollars (22% on the first $1M of supplemental wages, 37% above). When less than vest.federal, the difference is owed at tax time.",
      +          "type": "number"
      +        },
      +        "medicare": {
      +          "description": "Medicare tax in dollars.",
      +          "type": "number"
      +        },
      +        "netCashAtVest": {
      +          "description": "vestValue - total: net cash in dollars if every share is sold at vest.",
      +          "type": "number"
      +        },
      +        "socialSecurity": {
      +          "description": "Social Security tax in dollars (0 when not employed or already past the wage base).",
      +          "type": "number"
      +        },
      +        "state": {
      +          "description": "State income tax on the vest value in dollars.",
      +          "type": "number"
      +        },
      +        "total": {
      +          "description": "Total tax at vest in dollars.",
      +          "type": "number"
      +        },
      +        "vestValue": {
      +          "description": "shares x currentPrice in dollars, taxed as ordinary income at vest.",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "vestValue",
      +        "federal",
      +        "state",
      +        "socialSecurity",
      +        "medicare",
      +        "additionalMedicare",
      +        "total",
      +        "netCashAtVest",
      +        "federalWithheldAtVest"
      +      ],
      +      "type": "object"
      +    }
      +  },
      +  "required": [
      +    "vest",
      +    "bracketJump",
      +    "hold",
      +    "sellNowInvest",
      +    "holdMinusSell"
      +  ],
      +  "type": "object"
      +}
  6. Changed3 schema fields changedv1.3.4
    • removedInput schema / properties / haircut
      Removed value: -{
      -  "description": "Multiplicative haircut on expectedSalePrice (0..1) capturing volatility drag at the hold horizon.",
      -  "maximum": 1,
      -  "minimum": 0,
      -  "type": "number"
      -}
    • addedInput schema / properties / volatility
      Added value: +{
      +  "description": "Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag 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.",
      +  "minimum": 0,
      +  "type": "number"
      +}
    • changedInput schema / required
      Previous value: -[
      -  "shares",
      -  "currentPrice",
      -  "ordinaryIncome",
      -  "filingStatus",
      -  "stateCode",
      -  "stillEmployed",
      -  "holdYears",
      -  "haircut"
      -]New value: +[
      +  "shares",
      +  "currentPrice",
      +  "ordinaryIncome",
      +  "filingStatus",
      +  "stateCode",
      +  "stillEmployed",
      +  "holdYears"
      +]
  7. Changed8 schema fields changedv1.2.2
    • addedInput schema / properties / currentPrice / description
      Added value: +"Fair market value per share at vest, USD. Also the cost basis on retained shares."
    • addedInput schema / properties / filingStatus / description
      Added value: +"Federal filing status."
    • addedInput schema / properties / haircut / description
      Added value: +"Multiplicative haircut on expectedSalePrice (0..1) capturing volatility drag at the hold horizon."
    • addedInput schema / properties / holdYears / description
      Added value: +"Years to hold after vest (0.25..5). Below 1 year triggers the short-term capital gains cliff (ordinary rates on appreciation)."
    • addedInput schema / properties / ordinaryIncome / description
      Added value: +"Annual W-2 ordinary income before this vest, USD. Baseline for the bracket walk on the vest amount."
    • addedInput schema / properties / shares / description
      Added value: +"Restricted Stock Unit (RSU) shares vesting in this tranche."
    • addedInput schema / properties / stateCode / description
      Added value: +"Two-letter US state code."
    • addedInput schema / properties / stillEmployed / description
      Added value: +"True if still employed at vest. Drives FICA applicability and whether the 22% supplemental withholding rule applies."
  8. First observedv1.2.0

TDQS

A4.9/5.0
Behavior5/5

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

Annotations already indicate readOnly, idempotent, and non-destructive. The description goes well beyond this by disclosing deterministic/offline behavior, compiled and independently verified tax tables, lack of defaults or fallback estimates, required-field error behavior naming missing parameters, and the independence of multi-tool results. This gives an agent a rich behavioral model.

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 and somewhat repetitive around the no-default/provenance caveats, but the verbosity is justified by 12 parameters and complex resolution rules. It is front-loaded with use cases and the decisive scope statement, and most sentences carry distinct operational meaning. Minor redundancy costs one point.

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?

Given the complexity, the output schema, and the annotations, the description covers everything an agent needs: when to use, what it returns, how to handle missing/unresolvable inputs, error behavior, sibling routing, and verification sources. No critical gap is apparent for safe and correct invocation.

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

Parameters5/5

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

Schema coverage is 100%, but the description adds substantial meaning: no field has a default, growth/volatility must be supplied or resolved by ticker/market, the 'market' sentinel convention, provenance expectations ('never invent a number'), the haircut-vs-volatility distinction, and holdYears horizon-dependent cliffs. This far exceeds the baseline for a fully documented schema.

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 opens with explicit use cases: 'whether to sell RSUs at vest or hold them,' 'sell vested shares or diversify,' and 'tax and withholding on RSU vest.' It names the exact resource and verbs, and distinguishes itself by naming sibling tools for ISO/AMT and NSO. An agent can immediately tell 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?

Usage conditions are explicit: 'Use for RSUs at vest.' It directly names alternatives ('for ISO/AMT use amt_iso_optimize, for NSO use nso_calculate') and describes when this tool is not appropriate. It also states that it is deterministic and offline, setting expectations for when it can be invoked.

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