{
  "openapi": "3.1.0",
  "info": {
    "title": "Stock Averager API",
    "version": "1.0.0",
    "summary": "Stateless investment and options calculations.",
    "description": "The calculations behind the Stock Averager calculators, available as JSON.\n\n**When to use it.** You have the numbers — a position, a contribution schedule, an option's parameters — and you need the arithmetic done exactly: a new average cost, a break-even including fees, a future value, a CAGR, or Black-Scholes price and Greeks.\n\n**When not to use it.** There is no market data here: no quotes, no ticker lookup, no historical prices. Every price and volatility is an input you supply. Nothing is stored, and no output is advice.\n\n**Calling it.** No authentication and no API key. Every operation is a POST with a JSON body. Errors are RFC 9457 problem details carrying a stable `code`, a `hint`, and per-field `errors`.\n\n**Fair use.** There is no enforced rate limit today. Please stay under roughly 60 requests a minute; if that changes it will be announced at https://www.stockaverager.com/docs.",
    "contact": {
      "name": "Stock Averager",
      "email": "stockaverager@gmail.com",
      "url": "https://www.stockaverager.com/contact"
    },
    "license": {
      "name": "Free to use, no warranty",
      "url": "https://www.stockaverager.com/terms"
    },
    "termsOfService": "https://www.stockaverager.com/terms"
  },
  "servers": [
    {
      "url": "https://www.stockaverager.com",
      "description": "Production"
    }
  ],
  "security": [],
  "externalDocs": {
    "description": "API documentation",
    "url": "https://www.stockaverager.com/docs"
  },
  "tags": [
    {
      "name": "Position",
      "description": "Arithmetic on a holding you already have: averaging, cost basis, break-even."
    },
    {
      "name": "Projection",
      "description": "What contributions or withdrawals compound to under an assumed return."
    },
    {
      "name": "Return",
      "description": "Turning a start and end value into a comparable annualised rate."
    },
    {
      "name": "Options",
      "description": "Black-Scholes pricing and sensitivities for a single contract."
    },
    {
      "name": "Meta",
      "description": "Discovery endpoints."
    }
  ],
  "paths": {
    "/api/v1/average-down": {
      "post": {
        "operationId": "calculateAverageDown",
        "summary": "Shares needed to reach a target average price",
        "description": "Given an existing position and the current market price, returns how many additional shares to buy to bring the average cost down to a target, and the resulting position. The target must sit strictly between the current price and the original price — no share count reaches an average below the price you are buying at.\n\nInteractive equivalent: https://www.stockaverager.com/tools/stock-averager",
        "tags": [
          "Position"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "shares": {
                    "description": "Shares currently held.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      100
                    ]
                  },
                  "originalPrice": {
                    "description": "Average price already paid per share.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      80
                    ]
                  },
                  "currentPrice": {
                    "description": "Current market price per share.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      50
                    ]
                  },
                  "targetAveragePrice": {
                    "description": "Average price per share you want to reach.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      65
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "shares",
                  "originalPrice",
                  "currentPrice",
                  "targetAveragePrice"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "shares": 100,
                    "originalPrice": 80,
                    "currentPrice": 50,
                    "targetAveragePrice": 65
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateAverageDown"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "shares": {
                          "description": "Shares currently held.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            100
                          ]
                        },
                        "originalPrice": {
                          "description": "Average price already paid per share.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            80
                          ]
                        },
                        "currentPrice": {
                          "description": "Current market price per share.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            50
                          ]
                        },
                        "targetAveragePrice": {
                          "description": "Average price per share you want to reach.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            65
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "shares",
                        "originalPrice",
                        "currentPrice",
                        "targetAveragePrice"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "additionalShares": {
                          "description": "Shares to buy at currentPrice to reach the target average.",
                          "type": "number",
                          "examples": [
                            150
                          ]
                        },
                        "additionalInvestment": {
                          "description": "Cash required for those shares.",
                          "type": "number",
                          "examples": [
                            7500
                          ]
                        },
                        "newTotalShares": {
                          "description": "Shares held afterwards.",
                          "type": "number",
                          "examples": [
                            250
                          ]
                        },
                        "newTotalInvestment": {
                          "description": "Total cash invested afterwards.",
                          "type": "number",
                          "examples": [
                            15500
                          ]
                        },
                        "newAveragePrice": {
                          "description": "Resulting average cost per share.",
                          "type": "number",
                          "examples": [
                            62
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "additionalShares",
                        "additionalInvestment",
                        "newTotalShares",
                        "newTotalInvestment",
                        "newAveragePrice"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cost-basis": {
      "post": {
        "operationId": "calculateCostBasis",
        "summary": "Average cost basis across purchase lots",
        "description": "Average-cost basis for a position built from several purchases, including per-lot fees. Use this before computing a gain, a break-even or a tax liability on a position bought in tranches.\n\nInteractive equivalent: https://www.stockaverager.com/tools/stock-averager/cost-basis-calculator",
        "tags": [
          "Position"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lots": {
                    "description": "One entry per purchase lot, in any order.",
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "shares": {
                          "description": "Shares bought in this lot.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            100
                          ]
                        },
                        "price": {
                          "description": "Price paid per share in this lot.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            50
                          ]
                        },
                        "fees": {
                          "description": "Brokerage and charges for this lot.",
                          "type": "number",
                          "minimum": 0,
                          "default": 0,
                          "examples": [
                            0
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "shares",
                        "price"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "additionalProperties": false,
                "required": [
                  "lots"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "lots": [
                      {
                        "shares": 100,
                        "price": 50,
                        "fees": 0
                      },
                      {
                        "shares": 100,
                        "price": 50,
                        "fees": 0
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateCostBasis"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "lots": {
                          "description": "One entry per purchase lot, in any order.",
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "shares": {
                                "description": "Shares bought in this lot.",
                                "type": "number",
                                "exclusiveMinimum": 0,
                                "examples": [
                                  100
                                ]
                              },
                              "price": {
                                "description": "Price paid per share in this lot.",
                                "type": "number",
                                "exclusiveMinimum": 0,
                                "examples": [
                                  50
                                ]
                              },
                              "fees": {
                                "description": "Brokerage and charges for this lot.",
                                "type": "number",
                                "minimum": 0,
                                "default": 0,
                                "examples": [
                                  0
                                ]
                              }
                            },
                            "additionalProperties": false,
                            "required": [
                              "shares",
                              "price"
                            ]
                          },
                          "minItems": 1,
                          "maxItems": 500
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "lots"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "totalShares": {
                          "description": "Shares held across every lot.",
                          "type": "number",
                          "examples": [
                            200
                          ]
                        },
                        "totalInvested": {
                          "description": "Cash invested across every lot, fees included.",
                          "type": "number",
                          "examples": [
                            8000
                          ]
                        },
                        "averageCostPerShare": {
                          "description": "Cost basis per share.",
                          "type": "number",
                          "examples": [
                            40
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "totalShares",
                        "totalInvested",
                        "averageCostPerShare"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sip": {
      "post": {
        "operationId": "calculateSip",
        "summary": "Future value of a monthly investment plan",
        "description": "Compounds a fixed monthly contribution at an assumed annual return. The return is an assumption you supply, not a forecast — no market data is involved.\n\nInteractive equivalent: https://www.stockaverager.com/tools/sip-calculator",
        "tags": [
          "Projection"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "monthlyInvestment": {
                    "description": "Amount invested every month.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      500
                    ]
                  },
                  "expectedAnnualReturn": {
                    "description": "Assumed annual return, as a percentage.",
                    "type": "number",
                    "maximum": 50,
                    "exclusiveMinimum": 0,
                    "examples": [
                      12
                    ]
                  },
                  "years": {
                    "description": "Contribution period in years.",
                    "type": "number",
                    "maximum": 50,
                    "exclusiveMinimum": 0,
                    "examples": [
                      10
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "monthlyInvestment",
                  "expectedAnnualReturn",
                  "years"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "monthlyInvestment": 500,
                    "expectedAnnualReturn": 12,
                    "years": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateSip"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "monthlyInvestment": {
                          "description": "Amount invested every month.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            500
                          ]
                        },
                        "expectedAnnualReturn": {
                          "description": "Assumed annual return, as a percentage.",
                          "type": "number",
                          "maximum": 50,
                          "exclusiveMinimum": 0,
                          "examples": [
                            12
                          ]
                        },
                        "years": {
                          "description": "Contribution period in years.",
                          "type": "number",
                          "maximum": 50,
                          "exclusiveMinimum": 0,
                          "examples": [
                            10
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "monthlyInvestment",
                        "expectedAnnualReturn",
                        "years"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "futureValue": {
                          "description": "Projected value at the end of the period.",
                          "type": "number",
                          "examples": [
                            116170.05
                          ]
                        },
                        "totalInvestment": {
                          "description": "Sum of every contribution.",
                          "type": "number",
                          "examples": [
                            60000
                          ]
                        },
                        "totalReturns": {
                          "description": "futureValue minus totalInvestment.",
                          "type": "number",
                          "examples": [
                            56170.05
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "futureValue",
                        "totalInvestment",
                        "totalReturns"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/lumpsum": {
      "post": {
        "operationId": "calculateLumpsum",
        "summary": "Future value of a one-time investment",
        "description": "Compounds a single deposit at an assumed annual return and returns the year-by-year path. Use calculateSip instead when money goes in on a schedule.\n\nInteractive equivalent: https://www.stockaverager.com/tools/lumpsum-calculator",
        "tags": [
          "Projection"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "description": "Amount invested once, up front.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      10000
                    ]
                  },
                  "expectedAnnualReturn": {
                    "description": "Assumed annual return, as a percentage.",
                    "type": "number",
                    "maximum": 50,
                    "exclusiveMinimum": 0,
                    "examples": [
                      9
                    ]
                  },
                  "years": {
                    "description": "Holding period in whole years.",
                    "type": "integer",
                    "maximum": 50,
                    "exclusiveMinimum": 0,
                    "examples": [
                      15
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "amount",
                  "expectedAnnualReturn",
                  "years"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "amount": 10000,
                    "expectedAnnualReturn": 9,
                    "years": 15
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateLumpsum"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "amount": {
                          "description": "Amount invested once, up front.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            10000
                          ]
                        },
                        "expectedAnnualReturn": {
                          "description": "Assumed annual return, as a percentage.",
                          "type": "number",
                          "maximum": 50,
                          "exclusiveMinimum": 0,
                          "examples": [
                            9
                          ]
                        },
                        "years": {
                          "description": "Holding period in whole years.",
                          "type": "integer",
                          "maximum": 50,
                          "exclusiveMinimum": 0,
                          "examples": [
                            15
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "amount",
                        "expectedAnnualReturn",
                        "years"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "futureValue": {
                          "description": "Projected value at the end of the period.",
                          "type": "number",
                          "examples": [
                            36424.63
                          ]
                        },
                        "totalReturns": {
                          "description": "futureValue minus the amount invested.",
                          "type": "number",
                          "examples": [
                            26424.63
                          ]
                        },
                        "wealthGainPercent": {
                          "description": "Total return as a percentage of the amount invested.",
                          "type": "number",
                          "examples": [
                            264.25
                          ]
                        },
                        "yearlyBreakdown": {
                          "description": "Value and cumulative return at the end of each year, in order.",
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "year": {
                                "description": "Year number, starting at 1.",
                                "type": "integer",
                                "examples": [
                                  1
                                ]
                              },
                              "value": {
                                "description": "Projected value at the end of this year.",
                                "type": "number",
                                "examples": [
                                  10900
                                ]
                              },
                              "returns": {
                                "description": "Cumulative return at the end of this year.",
                                "type": "number",
                                "examples": [
                                  900
                                ]
                              }
                            },
                            "additionalProperties": false,
                            "required": [
                              "year",
                              "value",
                              "returns"
                            ]
                          }
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "futureValue",
                        "totalReturns",
                        "wealthGainPercent",
                        "yearlyBreakdown"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/swp": {
      "post": {
        "operationId": "calculateSwp",
        "summary": "How long a corpus survives fixed withdrawals",
        "description": "Draws a fixed amount every month while the remaining balance keeps compounding, and reports when the corpus runs out. Capped at 600 months (50 years); if the withdrawal is smaller than the growth, `depleted` is false and the corpus survives the cap.\n\nInteractive equivalent: https://www.stockaverager.com/tools/swp-calculator",
        "tags": [
          "Projection"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "initialInvestment": {
                    "description": "Starting corpus.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      500000
                    ]
                  },
                  "monthlyWithdrawal": {
                    "description": "Amount withdrawn every month.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      3000
                    ]
                  },
                  "expectedAnnualReturn": {
                    "description": "Assumed annual return on the remaining balance, as a percentage.",
                    "type": "number",
                    "maximum": 50,
                    "exclusiveMinimum": 0,
                    "examples": [
                      8
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "initialInvestment",
                  "monthlyWithdrawal",
                  "expectedAnnualReturn"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "initialInvestment": 500000,
                    "monthlyWithdrawal": 3000,
                    "expectedAnnualReturn": 8
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateSwp"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "initialInvestment": {
                          "description": "Starting corpus.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            500000
                          ]
                        },
                        "monthlyWithdrawal": {
                          "description": "Amount withdrawn every month.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            3000
                          ]
                        },
                        "expectedAnnualReturn": {
                          "description": "Assumed annual return on the remaining balance, as a percentage.",
                          "type": "number",
                          "maximum": 50,
                          "exclusiveMinimum": 0,
                          "examples": [
                            8
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "initialInvestment",
                        "monthlyWithdrawal",
                        "expectedAnnualReturn"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "withdrawalMonths": {
                          "description": "Months of withdrawals before the corpus is exhausted, or 600 if it survives the cap.",
                          "type": "number",
                          "examples": [
                            600
                          ]
                        },
                        "totalWithdrawals": {
                          "description": "Total cash withdrawn over that period.",
                          "type": "number",
                          "examples": [
                            1800000
                          ]
                        },
                        "remainingBalance": {
                          "description": "Balance left at the end.",
                          "type": "number",
                          "examples": [
                            1263775.7
                          ]
                        },
                        "totalInterestEarned": {
                          "description": "Growth earned across the period.",
                          "type": "number",
                          "examples": [
                            2563775.7
                          ]
                        },
                        "depleted": {
                          "description": "True when the corpus ran out before the 600-month cap.",
                          "type": "boolean",
                          "examples": [
                            false
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "withdrawalMonths",
                        "totalWithdrawals",
                        "remainingBalance",
                        "totalInterestEarned",
                        "depleted"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cagr": {
      "post": {
        "operationId": "calculateCagr",
        "summary": "Compound annual growth rate",
        "description": "The annualised rate implied by a start value, an end value and a holding period. Use it to compare investments held for different lengths of time. For uneven cash flows in and out, CAGR is the wrong measure — IRR is.\n\nInteractive equivalent: https://www.stockaverager.com/tools/cagr-calculator",
        "tags": [
          "Return"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "initialValue": {
                    "description": "Value at the start of the period.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      12000
                    ]
                  },
                  "finalValue": {
                    "description": "Value at the end of the period.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      31000
                    ]
                  },
                  "years": {
                    "description": "Holding period in years.",
                    "type": "number",
                    "maximum": 100,
                    "exclusiveMinimum": 0,
                    "examples": [
                      7
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "initialValue",
                  "finalValue",
                  "years"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "initialValue": 12000,
                    "finalValue": 31000,
                    "years": 7
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateCagr"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "initialValue": {
                          "description": "Value at the start of the period.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            12000
                          ]
                        },
                        "finalValue": {
                          "description": "Value at the end of the period.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            31000
                          ]
                        },
                        "years": {
                          "description": "Holding period in years.",
                          "type": "number",
                          "maximum": 100,
                          "exclusiveMinimum": 0,
                          "examples": [
                            7
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "initialValue",
                        "finalValue",
                        "years"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "cagrPercent": {
                          "description": "Compound annual growth rate, as a percentage.",
                          "type": "number",
                          "examples": [
                            14.51
                          ]
                        },
                        "absoluteReturn": {
                          "description": "finalValue minus initialValue.",
                          "type": "number",
                          "examples": [
                            19000
                          ]
                        },
                        "totalGrowthPercent": {
                          "description": "Total return over the whole period, as a percentage.",
                          "type": "number",
                          "examples": [
                            158.33
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "cagrPercent",
                        "absoluteReturn",
                        "totalGrowthPercent"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/break-even": {
      "post": {
        "operationId": "calculateBreakEven",
        "summary": "Break-even price including fees",
        "description": "The selling price at which a position covers its cost once buy-side and sell-side charges are included. On small positions the fee impact is often larger than it looks.\n\nInteractive equivalent: https://www.stockaverager.com/tools/break-even-calculator",
        "tags": [
          "Position"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "shares": {
                    "description": "Shares held.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      100
                    ]
                  },
                  "purchasePrice": {
                    "description": "Price paid per share.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      50
                    ]
                  },
                  "buyFees": {
                    "description": "Total charges on the purchase.",
                    "type": "number",
                    "minimum": 0,
                    "default": 0,
                    "examples": [
                      10
                    ]
                  },
                  "sellFees": {
                    "description": "Total charges expected on the sale.",
                    "type": "number",
                    "minimum": 0,
                    "default": 0,
                    "examples": [
                      10
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "shares",
                  "purchasePrice"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "shares": 100,
                    "purchasePrice": 50,
                    "buyFees": 10,
                    "sellFees": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateBreakEven"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "shares": {
                          "description": "Shares held.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            100
                          ]
                        },
                        "purchasePrice": {
                          "description": "Price paid per share.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            50
                          ]
                        },
                        "buyFees": {
                          "description": "Total charges on the purchase.",
                          "type": "number",
                          "minimum": 0,
                          "default": 0,
                          "examples": [
                            10
                          ]
                        },
                        "sellFees": {
                          "description": "Total charges expected on the sale.",
                          "type": "number",
                          "minimum": 0,
                          "default": 0,
                          "examples": [
                            10
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "shares",
                        "purchasePrice"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "breakEvenPrice": {
                          "description": "Price per share that covers cost and all charges.",
                          "type": "number",
                          "examples": [
                            50.2
                          ]
                        },
                        "totalCost": {
                          "description": "Cash spent acquiring the position, buy fees included.",
                          "type": "number",
                          "examples": [
                            5010
                          ]
                        },
                        "totalFees": {
                          "description": "Buy plus sell charges.",
                          "type": "number",
                          "examples": [
                            20
                          ]
                        },
                        "feeImpact": {
                          "description": "How far fees push the break-even above the purchase price.",
                          "type": "number",
                          "examples": [
                            0.2
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "breakEvenPrice",
                        "totalCost",
                        "totalFees",
                        "feeImpact"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/options/greeks": {
      "post": {
        "operationId": "calculateOptionGreeks",
        "summary": "Black-Scholes price and Greeks for one option",
        "description": "Prices a single European option with Black-Scholes and returns its sensitivities. Theta is per calendar day and Vega is per one percentage point of implied volatility, matching how desks quote them. Requires a volatility input — there is no market data behind this, so supply the implied volatility you want to model.\n\nInteractive equivalent: https://www.stockaverager.com/tools/options-greeks-calculator",
        "tags": [
          "Options"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "spotPrice": {
                    "description": "Current price of the underlying.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      147
                    ]
                  },
                  "strikePrice": {
                    "description": "Strike price of the option.",
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "examples": [
                      150
                    ]
                  },
                  "daysToExpiry": {
                    "description": "Calendar days until expiry.",
                    "type": "number",
                    "maximum": 3650,
                    "exclusiveMinimum": 0,
                    "examples": [
                      30
                    ]
                  },
                  "volatilityPercent": {
                    "description": "Implied volatility, as an annualised percentage.",
                    "type": "number",
                    "maximum": 500,
                    "exclusiveMinimum": 0,
                    "examples": [
                      28
                    ]
                  },
                  "riskFreeRatePercent": {
                    "description": "Annual risk-free rate, as a percentage.",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "examples": [
                      4
                    ]
                  },
                  "optionType": {
                    "description": "Which side to price.",
                    "type": "string",
                    "enum": [
                      "call",
                      "put"
                    ],
                    "examples": [
                      "call"
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "spotPrice",
                  "strikePrice",
                  "daysToExpiry",
                  "volatilityPercent",
                  "riskFreeRatePercent",
                  "optionType"
                ]
              },
              "examples": {
                "default": {
                  "summary": "A worked example",
                  "value": {
                    "spotPrice": 147,
                    "strikePrice": 150,
                    "daysToExpiry": 30,
                    "volatilityPercent": 28,
                    "riskFreeRatePercent": 4,
                    "optionType": "call"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operationId": {
                      "type": "string",
                      "description": "Echo of the operation that ran.",
                      "examples": [
                        "calculateOptionGreeks"
                      ]
                    },
                    "inputs": {
                      "type": "object",
                      "properties": {
                        "spotPrice": {
                          "description": "Current price of the underlying.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            147
                          ]
                        },
                        "strikePrice": {
                          "description": "Strike price of the option.",
                          "type": "number",
                          "exclusiveMinimum": 0,
                          "examples": [
                            150
                          ]
                        },
                        "daysToExpiry": {
                          "description": "Calendar days until expiry.",
                          "type": "number",
                          "maximum": 3650,
                          "exclusiveMinimum": 0,
                          "examples": [
                            30
                          ]
                        },
                        "volatilityPercent": {
                          "description": "Implied volatility, as an annualised percentage.",
                          "type": "number",
                          "maximum": 500,
                          "exclusiveMinimum": 0,
                          "examples": [
                            28
                          ]
                        },
                        "riskFreeRatePercent": {
                          "description": "Annual risk-free rate, as a percentage.",
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "examples": [
                            4
                          ]
                        },
                        "optionType": {
                          "description": "Which side to price.",
                          "type": "string",
                          "enum": [
                            "call",
                            "put"
                          ],
                          "examples": [
                            "call"
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "spotPrice",
                        "strikePrice",
                        "daysToExpiry",
                        "volatilityPercent",
                        "riskFreeRatePercent",
                        "optionType"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "properties": {
                        "optionPrice": {
                          "description": "Theoretical premium per share.",
                          "type": "number",
                          "examples": [
                            3.42
                          ]
                        },
                        "delta": {
                          "description": "Change in premium per 1 unit move in the underlying.",
                          "type": "number",
                          "examples": [
                            0.42
                          ]
                        },
                        "gamma": {
                          "description": "Change in delta per 1 unit move in the underlying.",
                          "type": "number",
                          "examples": [
                            0.0345
                          ]
                        },
                        "theta": {
                          "description": "Premium lost per calendar day, all else equal.",
                          "type": "number",
                          "examples": [
                            -0.08
                          ]
                        },
                        "vega": {
                          "description": "Premium change per 1 percentage point of implied volatility.",
                          "type": "number",
                          "examples": [
                            0.16
                          ]
                        },
                        "intrinsicValue": {
                          "description": "Value if exercised now.",
                          "type": "number",
                          "examples": [
                            0
                          ]
                        },
                        "timeValue": {
                          "description": "optionPrice minus intrinsicValue.",
                          "type": "number",
                          "examples": [
                            3.42
                          ]
                        }
                      },
                      "additionalProperties": false,
                      "required": [
                        "optionPrice",
                        "delta",
                        "gamma",
                        "theta",
                        "vega",
                        "intrinsicValue",
                        "timeValue"
                      ]
                    },
                    "meta": {
                      "type": "object",
                      "description": "Provenance for the numbers above.",
                      "properties": {
                        "calculator": {
                          "type": "string",
                          "format": "uri",
                          "description": "The equivalent on-page calculator."
                        },
                        "disclaimer": {
                          "type": "string",
                          "description": "Scope of the result."
                        }
                      }
                    }
                  },
                  "required": [
                    "operationId",
                    "inputs",
                    "result"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method — these operations are POST only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "A field failed validation (code validation_failed), or the fields are individually valid but cannot be solved together (code calculation_impossible).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The calculation failed unexpectedly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1": {
      "get": {
        "operationId": "listOperations",
        "summary": "List every published operation",
        "description": "A self-describing index of the API: one entry per operation with its URL, summary and a worked example request. Useful for discovering the surface without parsing this document.",
        "tags": [
          "Meta"
        ],
        "responses": {
          "200": {
            "description": "The operation index.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "documentation": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "operations": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "operationId": {
                            "type": "string"
                          },
                          "method": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "summary": {
                            "type": "string"
                          },
                          "tag": {
                            "type": "string"
                          },
                          "calculator": {
                            "type": "string",
                            "format": "uri"
                          },
                          "exampleRequest": {
                            "type": "object"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "405": {
            "description": "The index is GET only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. Every error response uses this shape.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "URI identifying the problem type; resolves to its documentation."
          },
          "title": {
            "type": "string",
            "description": "Short, stable summary of the problem type."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code, repeated for clients that only read the body."
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this specific request."
          },
          "code": {
            "type": "string",
            "description": "Stable machine token to branch on, e.g. validation_failed.",
            "examples": [
              "validation_failed"
            ]
          },
          "hint": {
            "type": "string",
            "description": "What to change to make the request succeed."
          },
          "errors": {
            "type": "array",
            "description": "Per-field failures. Present on validation errors.",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "description": "Dotted path to the offending field."
                },
                "code": {
                  "type": "string",
                  "description": "Why it failed: required, type, range, enum or unknown_field."
                },
                "message": {
                  "type": "string",
                  "description": "Human-readable description of the failure."
                },
                "hint": {
                  "type": "string",
                  "description": "How to fix this field."
                }
              },
              "required": [
                "field",
                "code",
                "message",
                "hint"
              ]
            }
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code"
        ]
      }
    }
  }
}