{
  "openapi": "3.1.1",
  "info": {
    "title": "RavingDesk API",
    "version": "1.0.0",
    "summary": "Deterministic tools for people and AI agents.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://ravingdesk.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/tools": {
      "get": {
        "operationId": "listTools",
        "summary": "Catalog of all tools",
        "responses": {
          "200": {
            "description": "Tool list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/{id}": {
      "get": {
        "operationId": "getTool",
        "summary": "Definition of one tool",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "ravingdesk-paper-portfolio",
                "ravingdesk-position-size",
                "ravingdesk-crypto-round-trip",
                "ravingdesk-eth-gas",
                "currency-converter",
                "historical-fx-chart",
                "gas-fee-intelligence-explorer",
                "break-even-explorer",
                "percentage-ratio-calculator"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ravingdesk-paper-portfolio/calculate": {
      "post": {
        "operationId": "calculateRavingdeskPaperPortfolio",
        "summary": "Turn paper fills and your own marks into positions, P&L, exposure and a price-shock check.",
        "description": "Replays a list of paper fills (buys and sells, long and short) with weighted-average cost, then values the open positions at the marks you supply: cash, equity, realized and unrealized profit or loss, long/short/gross/net exposure, concentration and the equity after a uniform price shock. Totals are withheld when a position has no mark. It is the ledger engine of the RavingDesk paper-trading workspace.\n\nMethodology:\n- Fills are replayed in the order given. Adding to a position updates the weighted-average entry; reducing it realizes (exit − average entry) × quantity for longs and the reverse for shorts, minus the proportional entry fee and the exit fee.\n- A fill larger than the open position closes it and opens the remainder in the other direction at the fill price.\n- Cash = starting cash − buys + sells − fees (short sales add cash; negative cash is allowed, no margin checks). Equity = cash + quantity × mark of every open position.\n- Exposure: long = sum of long market values, short = sum of absolute short market values, gross = long + short, net = long − short; concentration is the largest position’s share of gross. Ratios to equity are only shown when equity is positive.\n- If any open position lacks a mark, equity, unrealized P&L, exposure and the shock are withheld instead of being calculated from partial data.\n- The price shock multiplies every mark by (1 + shock %), floored at zero, and recalculates equity.\n\nPaper-trading calculation for education and planning only. It is not investment, tax or legal advice, uses only the numbers you enter and never places an order.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ravingdesk_paper_portfolio",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RavingdeskPaperPortfolioInput"
              },
              "examples": {
                "example1": {
                  "summary": "Fictional long and short with a partial close, −10 % shock",
                  "value": {
                    "startingCash": 100000,
                    "trades": [
                      {
                        "symbol": "EXAMPLE-A",
                        "side": "buy",
                        "quantity": 100,
                        "price": 50,
                        "fee": 2
                      },
                      {
                        "symbol": "EXAMPLE-A",
                        "side": "sell",
                        "quantity": 40,
                        "price": 56,
                        "fee": 2
                      },
                      {
                        "symbol": "EXAMPLE-B",
                        "side": "sell",
                        "quantity": 30,
                        "price": 120,
                        "fee": 3
                      }
                    ],
                    "marks": [
                      {
                        "symbol": "EXAMPLE-A",
                        "price": 54
                      },
                      {
                        "symbol": "EXAMPLE-B",
                        "price": 112
                      }
                    ],
                    "shockPercent": -10
                  }
                },
                "example2": {
                  "summary": "Reversal from long to short in one fill",
                  "value": {
                    "startingCash": 10000,
                    "trades": [
                      {
                        "symbol": "DEMO",
                        "side": "buy",
                        "quantity": 10,
                        "price": 100,
                        "fee": 1
                      },
                      {
                        "symbol": "DEMO",
                        "side": "sell",
                        "quantity": 25,
                        "price": 110,
                        "fee": 2
                      }
                    ],
                    "marks": [
                      {
                        "symbol": "DEMO",
                        "price": 105
                      }
                    ],
                    "shockPercent": 5
                  }
                },
                "example3": {
                  "summary": "Missing mark: totals withheld",
                  "value": {
                    "startingCash": 50000,
                    "trades": [
                      {
                        "symbol": "EXAMPLE-C",
                        "side": "buy",
                        "quantity": 20,
                        "price": 75,
                        "fee": 1
                      },
                      {
                        "symbol": "EXAMPLE-D",
                        "side": "buy",
                        "quantity": 5,
                        "price": 300,
                        "fee": 1
                      }
                    ],
                    "marks": [
                      {
                        "symbol": "EXAMPLE-C",
                        "price": 80
                      }
                    ],
                    "shockPercent": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RavingdeskPaperPortfolioResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ravingdesk-position-size/calculate": {
      "post": {
        "operationId": "calculateRavingdeskPositionSize",
        "summary": "Size a long or short paper trade from your risk budget, stop, target and round-trip fee.",
        "description": "Calculates how many units fit a fixed risk budget (account × risk %) between entry and stop, including an estimated round-trip fee, plus the loss at the stop, the profit at the target, reward/risk and a price-move map. It is the risk planner of the RavingDesk paper-trading workspace; it does not place orders.\n\nMethodology:\n- Risk budget = account × risk % ÷ 100. The estimated round-trip fee is subtracted first, so quantity = (risk budget − fee) ÷ |entry − stop|.\n- Loss at the stop = quantity × |entry − stop| + fee (equals the risk budget); profit at the target = quantity × |target − entry| − fee; reward/risk = profit ÷ loss.\n- For a long the stop must be below and the target above the entry; for a short it is the other way round.\n- The move map shows the gross profit or loss of the planned quantity at −10 %, −5 %, 0, +5 % and +10 % from the entry, without fees.\n- Quantities are fractional; round down to whole units or lot sizes yourself. Stops are not guaranteed fills: gaps and slippage can make the real loss larger.\n\nPaper-trading calculation for education and planning only. It is not investment, tax or legal advice, uses only the numbers you enter and never places an order.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ravingdesk_position_size",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RavingdeskPositionSizeInput"
              },
              "examples": {
                "example1": {
                  "summary": "Long: 10,000 account, 1 % risk, entry 100, stop 95, target 110, fee 4",
                  "value": {
                    "side": "long",
                    "account": 10000,
                    "riskPercent": 1,
                    "entry": 100,
                    "stop": 95,
                    "target": 110,
                    "fee": 4
                  }
                },
                "example2": {
                  "summary": "Short: 25,000 account, 0.5 % risk, entry 50, stop 52.5, target 45",
                  "value": {
                    "side": "short",
                    "account": 25000,
                    "riskPercent": 0.5,
                    "entry": 50,
                    "stop": 52.5,
                    "target": 45,
                    "fee": 2
                  }
                },
                "example3": {
                  "summary": "Tight stop: 5,000 account, 2 % risk, entry 20, stop 19.8, target 21",
                  "value": {
                    "side": "long",
                    "account": 5000,
                    "riskPercent": 2,
                    "entry": 20,
                    "stop": 19.8,
                    "target": 21,
                    "fee": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RavingdeskPositionSizeResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ravingdesk-crypto-round-trip/calculate": {
      "post": {
        "operationId": "calculateRavingdeskCryptoRoundTrip",
        "summary": "What a crypto spot buy-and-sell really costs: spread, slippage, fees, withdrawal and break-even.",
        "description": "Estimates a hypothetical spot round trip from the mid prices and cost assumptions you enter: buy fill (ask plus slippage), sell fill (bid minus slippage), trading fees on both sides and a network/withdrawal fee in base units, with net profit or loss, return on cost and the exit mid price needed to break even. Nothing is fetched and no order is sent. It is the crypto spot lab of the RavingDesk workspace.\n\nMethodology:\n- Buy fill = entry mid × (1 + spread ÷ 2) × (1 + slippage); sell fill = exit mid × (1 − spread ÷ 2) × (1 − slippage). Spread and slippage are in basis points (1 bp = 0.01 %).\n- Entry cost = quantity × buy fill × (1 + entry fee %); the network fee is deducted in base units, then the remainder is sold: net proceeds = remaining quantity × sell fill × (1 − exit fee %).\n- Break-even exit mid = entry cost ÷ (remaining quantity × (1 − spread ÷ 2) × (1 − slippage) × (1 − exit fee %)). The cost of the round trip is the loss if the mid price does not move.\n- Assumes a full fill at one price per side, fees charged in the quote asset, no market impact beyond the slippage you enter, no taxes and no fees in a third asset.\n\nPaper-trading calculation for education and planning only. It is not investment, tax or legal advice, uses only the numbers you enter and never places an order. Crypto prices and fees change quickly; use the rates of your own venue.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ravingdesk_crypto_round_trip",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RavingdeskCryptoRoundTripInput"
              },
              "examples": {
                "example1": {
                  "summary": "Fictional BTC round trip: 1 BTC, 60,000 → 62,000, 10 bps spread, 0.1 % fees",
                  "value": {
                    "base": "BTC",
                    "quote": "USDT",
                    "quantity": 1,
                    "entryMid": 60000,
                    "exitMid": 62000,
                    "spreadBps": 10,
                    "slippageBps": 5,
                    "entryFeePercent": 0.1,
                    "exitFeePercent": 0.1,
                    "networkBase": 0
                  }
                },
                "example2": {
                  "summary": "Small ETH trade with a withdrawal fee in between",
                  "value": {
                    "base": "ETH",
                    "quote": "EUR",
                    "quantity": 0.5,
                    "entryMid": 3000,
                    "exitMid": 3050,
                    "spreadBps": 20,
                    "slippageBps": 10,
                    "entryFeePercent": 0.26,
                    "exitFeePercent": 0.26,
                    "networkBase": 0.002
                  }
                },
                "example3": {
                  "summary": "Flat price: what the round trip costs if nothing moves",
                  "value": {
                    "base": "SOL",
                    "quote": "USD",
                    "quantity": 20,
                    "entryMid": 150,
                    "exitMid": 150,
                    "spreadBps": 30,
                    "slippageBps": 15,
                    "entryFeePercent": 0.4,
                    "exitFeePercent": 0.4,
                    "networkBase": 0.01
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RavingdeskCryptoRoundTripResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ravingdesk-eth-gas/calculate": {
      "post": {
        "operationId": "calculateRavingdeskEthGas",
        "summary": "Estimate an Ethereum transaction fee from gas used, base fee, max fee and priority tip (EIP-1559).",
        "description": "Applies the EIP-1559 fee rules to the numbers you enter: effective gas price (base fee plus a priority tip capped by the max fee), the estimated execution fee for the gas used and the maximum fee you authorise (gas limit × max fee), optionally converted at your own ETH price. It flags a max fee below the base fee. No network data is fetched; it is the gas estimator of the RavingDesk workspace.\n\nMethodology:\n- EIP-1559: the effective gas price is min(max fee, base fee + max priority fee). A transaction whose max fee is below the current base fee cannot be included until the base fee falls.\n- Estimated execution fee = gas used × effective gas price; the base-fee part (gas used × base fee) is burned and the rest is the validator tip.\n- Maximum fee = gas limit × max fee: the most the transaction can cost; unused gas is not charged.\n- 1 ETH = 10⁹ gwei. Quote amounts use the ETH price you enter. Layer-2 data fees, blob fees and the transferred value are not included.\n\nEstimate from your own numbers; base fees change every block and inclusion is not guaranteed. Not financial advice; no transaction is created or sent.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ravingdesk_eth_gas",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RavingdeskEthGasInput"
              },
              "examples": {
                "example1": {
                  "summary": "Plain ETH transfer: 21,000 gas, base 30, max 50, tip 2 gwei, ETH at 3,000",
                  "value": {
                    "gasUsed": 21000,
                    "gasLimit": 30000,
                    "baseFeeGwei": 30,
                    "maxFeeGwei": 50,
                    "maxPriorityFeeGwei": 2,
                    "ethQuoteRate": 3000
                  }
                },
                "example2": {
                  "summary": "Token swap: 150,000 gas, limit 200,000, base 12 gwei",
                  "value": {
                    "gasUsed": 150000,
                    "gasLimit": 200000,
                    "baseFeeGwei": 12,
                    "maxFeeGwei": 20,
                    "maxPriorityFeeGwei": 1.5
                  }
                },
                "example3": {
                  "summary": "Max fee below the base fee: not includable",
                  "value": {
                    "gasUsed": 21000,
                    "gasLimit": 21000,
                    "baseFeeGwei": 40,
                    "maxFeeGwei": 35,
                    "maxPriorityFeeGwei": 2,
                    "ethQuoteRate": 3000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RavingdeskEthGasResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/currency-converter/calculate": {
      "post": {
        "operationId": "calculateCurrencyConverter",
        "summary": "Convert an amount into several currencies at ECB euro reference rates, with rate date and source.",
        "description": "Converts an amount between the 30 currencies of the European Central Bank’s daily euro foreign exchange reference rates – into several target currencies at once, at the latest rates or those of a past date. Cross rates are calculated via the euro; every result shows the rate, the inverse rate, the reference date and the source.\n\nMethodology:\n- Rates come from the ECB’s daily reference-rate file (eurofxref-daily.xml) or, for past dates, from the ECB Data Portal (dataset EXR, daily series D.<currency>.EUR.SP00.A). They are cached per day; the daily file is re-checked at most hourly until the day’s rates (around 16:00 CET) are available.\n- All ECB rates are quoted as units of currency per 1 euro, so a cross rate is rate(to) ÷ rate(from); converting X from A to B = X × rate(B) ÷ rate(A). Rates are shown with 6 significant digits; amounts are rounded half away from zero to the currency’s minor units (0 for JPY, KRW and ISK).\n- For a date without a publication (weekends, TARGET holidays) the last earlier publication is used and named.\n- Reference rates are published for information only. Card schemes, banks and exchange offices use their own rates and add fees, so the amount you actually pay or receive will differ.\n\nECB reference rates are not transaction rates. For a real payment, check your provider’s rate and fees.",
        "tags": [
          "money"
        ],
        "x-mcp-tool": "currency_converter",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CurrencyConverterInput"
              },
              "examples": {
                "example1": {
                  "summary": "100 EUR into USD, GBP, CHF and JPY (latest)",
                  "value": {
                    "amount": 100,
                    "from": "EUR",
                    "to": "USD, GBP, CHF, JPY"
                  }
                },
                "example2": {
                  "summary": "250 USD into EUR and GBP on 2024-01-02",
                  "value": {
                    "amount": 250,
                    "from": "USD",
                    "to": "EUR, GBP",
                    "date": "2024-01-02"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrencyConverterResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/historical-fx-chart/calculate": {
      "post": {
        "operationId": "calculateHistoricalFxChart",
        "summary": "Chart exchange rates over time from ECB data: min, max, average, change, volatility and CSV export.",
        "description": "Pick a base currency, one or more quote currencies and a date range (back to 1999). The tool loads the ECB’s daily euro reference rates, builds the cross rates, draws a line chart (indexed to 100 to compare several pairs) and reports start, end, minimum, maximum, average, change and annualised volatility, with a CSV download.\n\nMethodology:\n- Daily reference rates (units per euro) are loaded from the ECB Data Portal SDMX API as CSV (series D.<currency>.EUR.SP00.A, only the requested currencies and dates) and cached; ranges ending more than a week ago are cached permanently, recent ranges per day.\n- Cross rate base/quote on each publication day = rate(quote) ÷ rate(base); days where either leg is missing are skipped. EUR legs are 1.\n- Statistics: first and last observation, minimum and maximum with dates, arithmetic mean, change = last ÷ first − 1, annualised volatility = standard deviation of daily log returns × √252.\n- Indexed view: every pair divided by its first value × 100, so pairs with very different levels (e.g. JPY and GBP) can be compared.\n\nReference rates for information; they are not the rates at which you could have traded.",
        "tags": [
          "money"
        ],
        "x-mcp-tool": "historical_fx_chart",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HistoricalFxChartInput"
              },
              "examples": {
                "example1": {
                  "summary": "EUR/USD and EUR/GBP over 2024 (indexed)",
                  "value": {
                    "base": "EUR",
                    "quotes": "USD, GBP",
                    "start": "2024-01-01",
                    "end": "2024-12-31"
                  }
                },
                "example2": {
                  "summary": "USD/JPY in 2023",
                  "value": {
                    "base": "USD",
                    "quotes": "JPY",
                    "start": "2023-01-01",
                    "end": "2023-12-31",
                    "png": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoricalFxChartResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/gas-fee-intelligence-explorer/calculate": {
      "post": {
        "operationId": "calculateGasFeeIntelligenceExplorer",
        "summary": "Live gas on 7 EVM chains: base fee, tips, trend and the USD cost of transfers and swaps.",
        "description": "Pulls eth_feeHistory (last 100 blocks, 10th/50th/90th percentile tips) and eth_gasPrice from Ethereum, Base, Arbitrum, OP Mainnet, Polygon, Avalanche and BNB Chain, prices the native tokens via DefiLlama and shows what a native transfer, ERC-20 transfer and swap cost on each chain — ranked cheapest first, with OP Stack L1 data fees as an upper bound.\n\nMethodology:\n- eth_feeHistory(blockCount, \"latest\", [10, 50, 90]) per chain: base fee per block (the last entry is the next block's base fee) and the 10th/50th/90th percentile priority fee of each block; the tool takes the median of each percentile across blocks.\n- Price tiers = next base fee + median p10 (slow) / p50 (standard) / p90 (fast) tip. Chains without a base fee use eth_gasPrice. Trend compares the average base fee of the newest fifth of blocks with the oldest fifth (±15 %).\n- Operation costs use typical gas amounts — native transfer 21,000 (exact), ERC-20 transfer ≈ 65,000, DEX swap ≈ 150,000 (estimates; real usage varies by contract and state) — times the standard tier.\n- USD: native token prices from the DefiLlama coins API (coingecko:ethereum, polygon-ecosystem-token, avalanche-2, binancecoin).\n- L2 data fees: on Base and OP Mainnet the OP Stack GasPriceOracle (0x4200…000F) getL1FeeUpperBound(size) is added as an upper bound using approximate transaction sizes (50 / 120 / 350 bytes unsigned). Arbitrum's L1 component is not included (it is folded into gas used at execution time and needs a per-transaction estimate).",
        "tags": [
          "web3"
        ],
        "x-mcp-tool": "gas_fee_intelligence_explorer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GasFeeIntelligenceExplorerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Ethereum vs Base vs Polygon from pasted data (offline)",
                  "value": {
                    "mode": "pasted",
                    "pasted": "{\"prices\":{\"ETH\":2400,\"POL\":0.25,\"BNB\":600},\"chains\":{\"ethereum\":{\"gasPrice\":\"0x77359400\",\"feeHistory\":{\"oldestBlock\":\"0x1406f40\",\"baseFeePerGas\":[\"0x6fc23ac00\",\"0x77359400\",\"0x6fc23ac00\",\"0x684ee1800\",\"0x6fc23ac00\",\"0x74a3df240\"],\"gasUsedRatio\":[0.62,0.41,0.38,0.55,0.6],\"reward\":[[\"0x3b9aca0\",\"0x5f5e100\",\"0x77359400\"],[\"0x2faf080\",\"0x5f5e100\",\"0x3b9aca00\"],[\"0x0\",\"0x3b9aca0\",\"0x77359400\"],[\"0x2faf080\",\"0x5f5e100\",\"0x3b9aca00\"],[\"0x3b9aca0\",\"0x77359400\",\"0xb2d05e00\"]]}},\"base\":{\"gasPrice\":\"0x1312d00\",\"feeHistory\":{\"oldestBlock\":\"0x16e3600\",\"baseFeePerGas\":[\"0xf4240\",\"0xf4240\",\"0x10c8e0\",\"0xf4240\",\"0xe4e1c\",\"0xf4240\"],\"gasUsedRatio\":[0.31,0.29,0.35,0.3,0.28],\"reward\":[[\"0x0\",\"0xf4240\",\"0x989680\"],[\"0x0\",\"0xf4240\",\"0x989680\"],[\"0x0\",\"0xf4240\",\"0x1312d00\"],[\"0x0\",\"0xf4240\",\"0x989680\"],[\"0x0\",\"0x7a120\",\"0x989680\"]]},\"l1FeeUpperBoundWei\":{\"eth-transfer\":\"0x9184e72a00\",\"erc20-transfer\":\"0xdf8475800\",\"swap\":\"0x2540be400\"}},\"polygon\":{\"gasPrice\":\"0x6fc23ac000\",\"feeHistory\":{\"oldestBlock\":\"0x4c4b400\",\"baseFeePerGas\":[\"0x5d21dba000\",\"0x5d21dba000\",\"0x5d21dba000\",\"0x5d21dba000\",\"0x5d21dba000\",\"0x5d21dba000\"],\"gasUsedRatio\":[0.5,0.48,0.52,0.51,0.5],\"reward\":[[\"0x6fc23ac00\",\"0x6fc23ac00\",\"0x1176592e00\"],[\"0x6fc23ac00\",\"0x6fc23ac00\",\"0x1176592e00\"],[\"0x6fc23ac00\",\"0x6fc23ac00\",\"0x1176592e00\"],[\"0x6fc23ac00\",\"0x6fc23ac00\",\"0x1176592e00\"],[\"0x6fc23ac00\",\"0x6fc23ac00\",\"0x1176592e00\"]]}}}}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GasFeeIntelligenceExplorerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/break-even-explorer/calculate": {
      "post": {
        "operationId": "calculateBreakEvenExplorer",
        "summary": "When does an investment pay for itself? Payback, NPV and IRR for solar, EVs, heat pumps…",
        "description": "Enter an upfront investment, subsidies and the yearly savings it brings, with optional price escalation, performance degradation and maintenance. The explorer returns simple and discounted payback (fractional years), NPV, IRR and a year-by-year cumulative cash-flow table.\n\nMethodology:\n- Net investment (year 0 outflow) = upfront investment − subsidies.\n- Savings in year t = year-1 savings × (1 + escalation)^(t−1) × (1 − degradation)^(t−1), unless overridden for that year.\n- Net cash flow in year t = savings − maintenance (maintenance is constant in nominal terms).\n- Simple payback = the year in which cumulative cash flow turns non-negative, interpolated linearly within that year (savings assumed to accrue evenly).\n- Discounted payback uses cash flows discounted by (1 + r)^t; NPV = −net investment + Σ net cash flow ÷ (1 + r)^t.\n- IRR = the rate at which NPV = 0, found by bisection between −99% and 1000% (null if NPV does not change sign in that range).\n- Presets only supply defaults for escalation, degradation and horizon when those fields are left empty; the values used are always listed.",
        "tags": [
          "money"
        ],
        "x-mcp-tool": "break_even_explorer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BreakEvenExplorerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Rooftop solar with a grant",
                  "value": {
                    "preset": "solar",
                    "currency": "EUR",
                    "upfrontInvestment": 12000,
                    "subsidies": 1500,
                    "annualSavings": 1100,
                    "maintenancePerYear": 100,
                    "discountRatePercent": 3
                  }
                },
                "example2": {
                  "summary": "Insulation project, flat savings, 15 years",
                  "value": {
                    "preset": "custom",
                    "currency": "EUR",
                    "upfrontInvestment": 6000,
                    "subsidies": 0,
                    "annualSavings": 700,
                    "savingsEscalationPercent": 0,
                    "degradationPercent": 0,
                    "maintenancePerYear": 0,
                    "discountRatePercent": 4,
                    "horizonYears": 15
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BreakEvenExplorerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/percentage-ratio-calculator/calculate": {
      "post": {
        "operationId": "calculatePercentageRatioCalculator",
        "summary": "X% of Y, % change, reverse %, percentage points, ratios and aspect ratios, with formulas.",
        "description": "Answers every everyday percentage question (X% of Y, X is what percent of Y, percentage change, increase or decrease by a percentage, reverse percentage, percentage points versus percent) and every ratio task (simplify, scale, split an amount, aspect ratios). Each answer shows the formula and the steps.\n\nMethodology:\n- X% of Y = X ÷ 100 × Y. X as a percent of Y = X ÷ Y × 100.\n- Percentage change from X to Y = (Y − X) ÷ |X| × 100; positive is an increase.\n- Increase Y by X% = Y × (1 + X/100); decrease Y by X% = Y × (1 − X/100).\n- Reverse percentage: if Y is the value after a change of X%, the original is Y ÷ (1 + X/100) (use a negative X for a decrease, e.g. −20 for 20% off).\n- Percentage points measure the plain difference between two percentages (3% − 2% = 1 percentage point); percent measures the relative change ((3 − 2) ÷ 2 = 50%).\n- Ratios are simplified exactly: decimal terms are first multiplied by a power of 10 to make whole numbers, then divided by their greatest common divisor.\n- Splitting an amount by a ratio uses the largest-remainder method at the chosen decimal places, so the parts always add up to exactly the amount.\n- Aspect ratios: width:height simplified by the GCD, the decimal width ÷ height, and the nearest well-known ratio within 1%. Resizing keeps the ratio: new height = new width × height ÷ width.",
        "tags": [
          "calculators"
        ],
        "x-mcp-tool": "percentage_ratio_calculator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PercentageRatioCalculatorInput"
              },
              "examples": {
                "example1": {
                  "summary": "What is 15% of 80?",
                  "value": {
                    "mode": "percent-of",
                    "x": 15,
                    "y": 80
                  }
                },
                "example2": {
                  "summary": "Price rose from 40 to 50: percentage change",
                  "value": {
                    "mode": "change",
                    "x": 40,
                    "y": 50
                  }
                },
                "example3": {
                  "summary": "Price after 19% VAT is 119: price before tax",
                  "value": {
                    "mode": "reverse",
                    "x": 19,
                    "y": 119
                  }
                },
                "example4": {
                  "summary": "Interest rate from 2% to 3%: points vs percent",
                  "value": {
                    "mode": "points",
                    "x": 2,
                    "y": 3
                  }
                },
                "example5": {
                  "summary": "Split 1,000 by 2:3:5",
                  "value": {
                    "mode": "ratio-split",
                    "ratio": "2:3:5",
                    "amount": 1000
                  }
                },
                "example6": {
                  "summary": "1920 × 1080 aspect ratio, resize to 1280 wide",
                  "value": {
                    "mode": "aspect",
                    "x": 1920,
                    "y": 1080,
                    "newWidth": 1280
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PercentageRatioCalculatorResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workflows": {
      "get": {
        "operationId": "listWorkflows",
        "summary": "Multi-tool workflows",
        "responses": {
          "200": {
            "description": "Workflow list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "RavingdeskPaperPortfolioInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "startingCash": {
            "title": "Starting cash",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000,
            "default": 100000
          },
          "trades": {
            "title": "Paper fills (in the order they happened)",
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "symbol": {
                  "title": "Symbol",
                  "type": "string",
                  "maxLength": 30
                },
                "side": {
                  "title": "Side",
                  "type": "string",
                  "enum": [
                    "buy",
                    "sell"
                  ]
                },
                "quantity": {
                  "title": "Quantity",
                  "type": "number",
                  "minimum": 1e-8,
                  "maximum": 1000000000
                },
                "price": {
                  "title": "Fill price",
                  "type": "number",
                  "minimum": 1e-8,
                  "maximum": 1000000000
                },
                "fee": {
                  "title": "Fee",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 1000000000
                }
              },
              "required": [
                "symbol",
                "side",
                "quantity",
                "price"
              ]
            }
          },
          "marks": {
            "title": "Marks (your own current prices)",
            "description": "One price per open symbol. Without a mark, equity and exposure totals are not calculated.",
            "type": "array",
            "maxItems": 500,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "symbol": {
                  "title": "Symbol",
                  "type": "string",
                  "maxLength": 30
                },
                "price": {
                  "title": "Mark price",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 1000000000
                }
              },
              "required": [
                "symbol",
                "price"
              ]
            }
          },
          "shockPercent": {
            "title": "Uniform price shock",
            "description": "Applied to every mark at once (sensitivity check, not a forecast).",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": -10,
            "x-unit": "%"
          }
        },
        "required": [
          "startingCash"
        ]
      },
      "RavingdeskPaperPortfolioResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "cash",
              "equity",
              "totalPnl",
              "realizedPnl",
              "unrealizedPnl",
              "missingMarks",
              "positions",
              "exposure",
              "shock"
            ],
            "properties": {
              "cash": {
                "type": "number"
              },
              "equity": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "totalPnl": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "realizedPnl": {
                "type": "number"
              },
              "unrealizedPnl": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "missingMarks": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "positions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "symbol",
                    "side",
                    "quantity",
                    "averageEntry",
                    "mark",
                    "marketValue",
                    "unrealizedPnl"
                  ],
                  "properties": {
                    "symbol": {
                      "type": "string"
                    },
                    "side": {
                      "type": "string",
                      "enum": [
                        "long",
                        "short"
                      ]
                    },
                    "quantity": {
                      "type": "number"
                    },
                    "averageEntry": {
                      "type": "number"
                    },
                    "mark": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "marketValue": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "unrealizedPnl": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  }
                }
              },
              "exposure": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "required": [
                  "longNotional",
                  "shortNotional",
                  "grossNotional",
                  "netNotional",
                  "grossEquityPercent",
                  "netEquityPercent",
                  "largestSharePercent"
                ],
                "properties": {
                  "longNotional": {
                    "type": "number"
                  },
                  "shortNotional": {
                    "type": "number"
                  },
                  "grossNotional": {
                    "type": "number"
                  },
                  "netNotional": {
                    "type": "number"
                  },
                  "grossEquityPercent": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "netEquityPercent": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "largestSharePercent": {
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                }
              },
              "shock": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "required": [
                  "percent",
                  "equity",
                  "change"
                ],
                "properties": {
                  "percent": {
                    "type": "number"
                  },
                  "equity": {
                    "type": "number"
                  },
                  "change": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "RavingdeskPositionSizeInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "side": {
            "title": "Direction",
            "type": "string",
            "enum": [
              "long",
              "short"
            ],
            "default": "long"
          },
          "account": {
            "title": "Account size",
            "description": "Paper account value in your account currency.",
            "type": "number",
            "minimum": 0.01,
            "maximum": 1000000000,
            "default": 10000
          },
          "riskPercent": {
            "title": "Risk per trade",
            "type": "number",
            "minimum": 0.0001,
            "maximum": 100,
            "default": 1,
            "x-unit": "%"
          },
          "entry": {
            "title": "Entry price",
            "type": "number",
            "minimum": 1e-8,
            "maximum": 1000000000,
            "default": 100
          },
          "stop": {
            "title": "Stop price",
            "description": "Below entry for a long, above entry for a short.",
            "type": "number",
            "minimum": 1e-8,
            "maximum": 1000000000,
            "default": 95
          },
          "target": {
            "title": "Target price",
            "description": "Above entry for a long, below entry for a short.",
            "type": "number",
            "minimum": 1e-8,
            "maximum": 1000000000,
            "default": 110
          },
          "fee": {
            "title": "Estimated round-trip fee",
            "description": "Entry plus exit fee in account currency; it is taken out of the risk budget.",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000,
            "default": 0
          }
        },
        "required": [
          "account",
          "riskPercent",
          "entry",
          "stop",
          "target"
        ]
      },
      "RavingdeskPositionSizeResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "side",
              "quantity",
              "riskBudget",
              "riskPerUnit",
              "potentialLoss",
              "potentialProfit",
              "rewardRisk",
              "notional",
              "notionalPercentOfAccount",
              "scenarios"
            ],
            "properties": {
              "side": {
                "type": "string",
                "enum": [
                  "long",
                  "short"
                ]
              },
              "quantity": {
                "type": "number",
                "description": "Fractional units that fit the risk budget after the fee."
              },
              "riskBudget": {
                "type": "number"
              },
              "riskPerUnit": {
                "type": "number"
              },
              "potentialLoss": {
                "type": "number",
                "description": "Loss if filled exactly at the stop, fee included."
              },
              "potentialProfit": {
                "type": "number",
                "description": "Profit if filled exactly at the target, fee included."
              },
              "rewardRisk": {
                "type": "number"
              },
              "notional": {
                "type": "number"
              },
              "notionalPercentOfAccount": {
                "type": "number"
              },
              "scenarios": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "move",
                    "price",
                    "grossPnl"
                  ],
                  "properties": {
                    "move": {
                      "type": "number"
                    },
                    "price": {
                      "type": "number"
                    },
                    "grossPnl": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "RavingdeskCryptoRoundTripInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "base": {
            "title": "Base asset",
            "type": "string",
            "maxLength": 12,
            "default": "BTC"
          },
          "quote": {
            "title": "Quote asset",
            "type": "string",
            "maxLength": 12,
            "default": "USDT"
          },
          "quantity": {
            "title": "Base quantity bought",
            "type": "number",
            "minimum": 1e-8,
            "maximum": 1000000000000,
            "default": 1
          },
          "entryMid": {
            "title": "Entry mid price",
            "type": "number",
            "minimum": 1e-8,
            "maximum": 1000000000000,
            "default": 60000
          },
          "exitMid": {
            "title": "Exit mid price",
            "type": "number",
            "minimum": 1e-8,
            "maximum": 1000000000000,
            "default": 62000
          },
          "spreadBps": {
            "title": "Full bid–ask spread",
            "type": "number",
            "minimum": 0,
            "maximum": 9999,
            "default": 10,
            "x-unit": "bps"
          },
          "slippageBps": {
            "title": "Slippage per side",
            "type": "number",
            "minimum": 0,
            "maximum": 9999,
            "default": 5,
            "x-unit": "bps"
          },
          "entryFeePercent": {
            "title": "Entry trading fee",
            "type": "number",
            "minimum": 0,
            "maximum": 99.99,
            "default": 0.1,
            "x-unit": "%"
          },
          "exitFeePercent": {
            "title": "Exit trading fee",
            "type": "number",
            "minimum": 0,
            "maximum": 99.99,
            "default": 0.1,
            "x-unit": "%"
          },
          "networkBase": {
            "title": "Network / withdrawal fee",
            "description": "In base units, deducted once between buying and selling (e.g. moving coins to another exchange).",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000000,
            "default": 0
          }
        },
        "required": [
          "quantity",
          "entryMid",
          "exitMid"
        ]
      },
      "RavingdeskCryptoRoundTripResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "buyPrice",
              "sellPrice",
              "acquired",
              "entryFee",
              "exitFee",
              "totalCost",
              "netProceeds",
              "breakEvenMid",
              "breakEvenMovePercent",
              "pnl",
              "returnPercent",
              "costOfRoundTrip"
            ],
            "properties": {
              "buyPrice": {
                "type": "number"
              },
              "sellPrice": {
                "type": "number"
              },
              "acquired": {
                "type": "number"
              },
              "entryNotional": {
                "type": "number"
              },
              "entryFee": {
                "type": "number"
              },
              "exitNotional": {
                "type": "number"
              },
              "exitFee": {
                "type": "number"
              },
              "totalCost": {
                "type": "number"
              },
              "netProceeds": {
                "type": "number"
              },
              "breakEvenMid": {
                "type": "number"
              },
              "breakEvenMovePercent": {
                "type": "number"
              },
              "pnl": {
                "type": "number"
              },
              "returnPercent": {
                "type": "number"
              },
              "costOfRoundTrip": {
                "type": "number"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "RavingdeskEthGasInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "gasUsed": {
            "title": "Gas used",
            "description": "21,000 for a plain ETH transfer; contract calls use more.",
            "type": "integer",
            "minimum": 1,
            "maximum": 1000000000,
            "default": 21000
          },
          "gasLimit": {
            "title": "Gas limit",
            "type": "integer",
            "minimum": 1,
            "maximum": 1000000000,
            "default": 30000
          },
          "baseFeeGwei": {
            "title": "Base fee",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000,
            "default": 30,
            "x-unit": "gwei"
          },
          "maxFeeGwei": {
            "title": "Max fee per gas",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000,
            "default": 50,
            "x-unit": "gwei"
          },
          "maxPriorityFeeGwei": {
            "title": "Max priority fee (tip)",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000,
            "default": 2,
            "x-unit": "gwei"
          },
          "ethQuoteRate": {
            "title": "Your ETH price (optional)",
            "description": "Quote-currency units per 1 ETH, e.g. 3000. Leave empty for ETH only.",
            "type": "number",
            "minimum": 1e-8,
            "maximum": 1000000000000
          }
        },
        "required": [
          "gasUsed",
          "gasLimit",
          "baseFeeGwei",
          "maxFeeGwei",
          "maxPriorityFeeGwei"
        ]
      },
      "RavingdeskEthGasResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "marketable",
              "effectiveGasPriceGwei",
              "estimatedFeeEth",
              "maximumFeeEth",
              "estimatedFeeQuote",
              "maximumFeeQuote",
              "burnedEth",
              "tipEth"
            ],
            "properties": {
              "marketable": {
                "type": "boolean"
              },
              "effectiveGasPriceGwei": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "estimatedFeeEth": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "maximumFeeEth": {
                "type": "number"
              },
              "estimatedFeeQuote": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "maximumFeeQuote": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "burnedEth": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "tipEth": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "CurrencyConverterInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000000000,
            "default": 100
          },
          "from": {
            "title": "From currency",
            "description": "ISO code, e.g. EUR, USD, GBP, CHF, JPY.",
            "type": "string",
            "maxLength": 3,
            "default": "EUR"
          },
          "to": {
            "title": "To currencies",
            "description": "One or more ISO codes, comma-separated, or \"all\".",
            "type": "string",
            "maxLength": 200,
            "default": "USD, GBP, CHF, JPY"
          },
          "date": {
            "title": "Rate date",
            "description": "Optional: use the reference rates of this date (the last publication on or before it). Empty: latest rates.",
            "type": "string",
            "format": "date"
          }
        },
        "required": [
          "amount",
          "from",
          "to"
        ]
      },
      "CurrencyConverterResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "amount",
              "from",
              "rates",
              "conversions"
            ],
            "properties": {
              "amount": {
                "type": "number"
              },
              "from": {
                "type": "string"
              },
              "rates": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "date",
                  "source",
                  "url",
                  "fetchedAt",
                  "stale"
                ],
                "properties": {
                  "date": {
                    "type": "string",
                    "description": "Reference date of the rates."
                  },
                  "requestedDate": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "source": {
                    "type": "string",
                    "enum": [
                      "ecb-daily",
                      "ecb-sdmx",
                      "manual"
                    ]
                  },
                  "url": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "fetchedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "stale": {
                    "type": "boolean"
                  }
                }
              },
              "conversions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "currency",
                    "name",
                    "rate",
                    "inverseRate",
                    "amount",
                    "rounded"
                  ],
                  "properties": {
                    "currency": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "rate": {
                      "type": "number",
                      "description": "Units of the target currency per 1 unit of the source currency."
                    },
                    "inverseRate": {
                      "type": "number"
                    },
                    "amount": {
                      "type": "number"
                    },
                    "rounded": {
                      "type": "number",
                      "description": "Rounded to the currency’s minor units."
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "HistoricalFxChartInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "base": {
            "title": "Base currency",
            "description": "The rate shows how many units of the quote currency one unit of the base buys.",
            "type": "string",
            "maxLength": 3,
            "default": "EUR"
          },
          "quotes": {
            "title": "Quote currencies",
            "description": "Up to 6 ISO codes, comma-separated.",
            "type": "string",
            "maxLength": 100,
            "default": "USD, GBP"
          },
          "start": {
            "title": "From",
            "description": "Default: one year before the end date. Earliest 1999-01-04.",
            "type": "string",
            "format": "date"
          },
          "end": {
            "title": "To",
            "description": "Default: today.",
            "type": "string",
            "format": "date"
          },
          "normalise": {
            "title": "Index to 100 at the start",
            "type": "string",
            "enum": [
              "auto",
              "yes",
              "no"
            ],
            "default": "auto"
          },
          "png": {
            "title": "Also create a PNG image",
            "type": "boolean",
            "default": true
          }
        },
        "required": [
          "base",
          "quotes"
        ]
      },
      "HistoricalFxChartResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "base",
              "quotes",
              "start",
              "end",
              "normalised",
              "pairs",
              "svg",
              "png",
              "dataUrl"
            ],
            "properties": {
              "base": {
                "type": "string"
              },
              "quotes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "start": {
                "type": "string"
              },
              "end": {
                "type": "string"
              },
              "normalised": {
                "type": "boolean"
              },
              "pairs": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "pair",
                    "base",
                    "quote",
                    "observations",
                    "start",
                    "end",
                    "min",
                    "max",
                    "average",
                    "changePercent",
                    "annualisedVolatilityPercent"
                  ],
                  "properties": {
                    "pair": {
                      "type": "string"
                    },
                    "base": {
                      "type": "string"
                    },
                    "quote": {
                      "type": "string"
                    },
                    "observations": {
                      "type": "integer"
                    },
                    "start": {
                      "type": "object"
                    },
                    "end": {
                      "type": "object"
                    },
                    "min": {
                      "type": "object"
                    },
                    "max": {
                      "type": "object"
                    },
                    "average": {
                      "type": "number"
                    },
                    "changePercent": {
                      "type": "number"
                    },
                    "annualisedVolatilityPercent": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  }
                }
              },
              "svg": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "png": {
                "anyOf": [
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "artifactId",
                      "filename",
                      "mime",
                      "bytes",
                      "sha256",
                      "path"
                    ],
                    "properties": {
                      "artifactId": {
                        "type": "string"
                      },
                      "filename": {
                        "type": "string"
                      },
                      "mime": {
                        "type": "string"
                      },
                      "bytes": {
                        "type": "integer"
                      },
                      "sha256": {
                        "type": "string"
                      },
                      "path": {
                        "type": "string",
                        "description": "Download path on the same site (valid for about 2 hours)."
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "dataUrl": {
                "type": "string",
                "description": "ECB Data Portal request used."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "GasFeeIntelligenceExplorerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "mode": {
            "title": "Data source",
            "type": "string",
            "enum": [
              "live",
              "pasted"
            ],
            "default": "live"
          },
          "chains": {
            "title": "Chains",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "ethereum",
                "base",
                "arbitrum",
                "optimism",
                "polygon",
                "avalanche",
                "bsc"
              ]
            },
            "default": [
              "ethereum",
              "base",
              "arbitrum",
              "optimism",
              "polygon",
              "avalanche",
              "bsc"
            ]
          },
          "blocks": {
            "title": "Blocks of fee history",
            "type": "integer",
            "minimum": 10,
            "maximum": 100,
            "default": 100
          },
          "pasted": {
            "title": "Pasted JSON",
            "description": "{\"prices\":{\"ETH\":2400},\"chains\":{\"ethereum\":{\"feeHistory\":{…eth_feeHistory result…},\"gasPrice\":\"0x…\",\"l1FeeUpperBoundWei\":{\"erc20-transfer\":\"0x…\"}}}}",
            "type": "string",
            "maxLength": 1000000
          }
        },
        "required": []
      },
      "GasFeeIntelligenceExplorerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "mode",
              "chains",
              "ranking",
              "operations"
            ],
            "properties": {
              "mode": {
                "type": "string"
              },
              "chains": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "chain",
                    "name",
                    "native",
                    "priceUsd",
                    "oldestBlock",
                    "newestBlock",
                    "blocks",
                    "baseFeeGwei",
                    "priorityGwei",
                    "gasPriceGwei",
                    "avgGasUsedRatio",
                    "trend",
                    "tiersGwei",
                    "operations",
                    "l1DataFee",
                    "error"
                  ],
                  "properties": {
                    "chain": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "native": {
                      "type": "string"
                    },
                    "priceUsd": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "oldestBlock": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "newestBlock": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "blocks": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "baseFeeGwei": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": false,
                      "required": [
                        "next",
                        "min",
                        "avg",
                        "max"
                      ],
                      "properties": {
                        "next": {
                          "type": "number"
                        },
                        "min": {
                          "type": "number"
                        },
                        "avg": {
                          "type": "number"
                        },
                        "max": {
                          "type": "number"
                        }
                      }
                    },
                    "priorityGwei": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": false,
                      "required": [
                        "p10",
                        "p50",
                        "p90"
                      ],
                      "properties": {
                        "p10": {
                          "type": "number"
                        },
                        "p50": {
                          "type": "number"
                        },
                        "p90": {
                          "type": "number"
                        }
                      }
                    },
                    "gasPriceGwei": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "avgGasUsedRatio": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "trend": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "tiersGwei": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": false,
                      "required": [
                        "slow",
                        "standard",
                        "fast"
                      ],
                      "properties": {
                        "slow": {
                          "type": "number"
                        },
                        "standard": {
                          "type": "number"
                        },
                        "fast": {
                          "type": "number"
                        }
                      }
                    },
                    "operations": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "operation",
                          "gas",
                          "executionNative",
                          "l1DataFeeNative",
                          "totalNative",
                          "totalUsd"
                        ],
                        "properties": {
                          "operation": {
                            "type": "string"
                          },
                          "gas": {
                            "type": "integer"
                          },
                          "executionNative": {
                            "type": "number"
                          },
                          "l1DataFeeNative": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "totalNative": {
                            "type": "number"
                          },
                          "totalUsd": {
                            "type": [
                              "number",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "l1DataFee": {
                      "type": "string",
                      "enum": [
                        "included-upper-bound",
                        "not-included",
                        "not-applicable"
                      ]
                    },
                    "error": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              },
              "ranking": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "rank",
                    "chain",
                    "erc20TransferUsd",
                    "erc20TransferNative"
                  ],
                  "properties": {
                    "rank": {
                      "type": "integer"
                    },
                    "chain": {
                      "type": "string"
                    },
                    "erc20TransferUsd": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "erc20TransferNative": {
                      "type": "number"
                    }
                  }
                }
              },
              "operations": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "label",
                    "gas"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "gas": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "BreakEvenExplorerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "preset": {
            "title": "Preset",
            "description": "Presets only fill in escalation, degradation and horizon when you leave those empty; the values are listed under assumptions.",
            "type": "string",
            "enum": [
              "custom",
              "solar",
              "ev",
              "heat-pump"
            ],
            "default": "custom"
          },
          "currency": {
            "title": "Currency",
            "description": "Shown in results only; no conversion is performed.",
            "type": "string",
            "maxLength": 8,
            "default": "EUR"
          },
          "upfrontInvestment": {
            "title": "Upfront investment",
            "type": "number",
            "minimum": 0,
            "default": 10000
          },
          "subsidies": {
            "title": "Subsidies / grants / tax credits",
            "description": "Received at the start; reduces the net investment.",
            "type": "number",
            "minimum": 0,
            "default": 0
          },
          "annualSavings": {
            "title": "Savings in year 1",
            "type": "number",
            "default": 1200,
            "x-unit": "per year"
          },
          "savingsEscalationPercent": {
            "title": "Savings escalation (e.g. energy price inflation)",
            "description": "Empty = preset default.",
            "type": "number",
            "minimum": -50,
            "maximum": 50,
            "x-unit": "%/year"
          },
          "degradationPercent": {
            "title": "Performance degradation",
            "description": "Empty = preset default.",
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "x-unit": "%/year"
          },
          "maintenancePerYear": {
            "title": "Maintenance per year",
            "type": "number",
            "minimum": 0,
            "default": 0
          },
          "discountRatePercent": {
            "title": "Discount rate",
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "default": 3,
            "x-unit": "%/year"
          },
          "horizonYears": {
            "title": "Horizon",
            "description": "Empty = preset default.",
            "type": "integer",
            "minimum": 1,
            "maximum": 60,
            "x-unit": "years"
          },
          "savingsByYear": {
            "title": "Override savings for specific years (optional)",
            "description": "Savings entered here replace the escalated/degraded value for that year (before maintenance).",
            "type": "array",
            "maxItems": 60,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "year": {
                  "title": "Year",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "savings": {
                  "title": "Savings",
                  "type": "number"
                }
              },
              "required": [
                "year",
                "savings"
              ]
            }
          }
        },
        "required": [
          "upfrontInvestment",
          "annualSavings"
        ]
      },
      "BreakEvenExplorerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "preset",
              "currency",
              "netInvestment",
              "horizonYears",
              "escalationPercent",
              "degradationPercent",
              "discountRatePercent",
              "simplePaybackYears",
              "discountedPaybackYears",
              "npv",
              "irrPercent",
              "totalNetSavings",
              "roiPercent",
              "years"
            ],
            "properties": {
              "preset": {
                "type": "string",
                "enum": [
                  "custom",
                  "solar",
                  "ev",
                  "heat-pump"
                ]
              },
              "currency": {
                "type": "string"
              },
              "netInvestment": {
                "type": "number",
                "description": "Upfront investment − subsidies."
              },
              "horizonYears": {
                "type": "integer"
              },
              "escalationPercent": {
                "type": "number"
              },
              "degradationPercent": {
                "type": "number"
              },
              "discountRatePercent": {
                "type": "number"
              },
              "simplePaybackYears": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Fractional years until cumulative nominal cash flow reaches 0; null if not within the horizon."
              },
              "discountedPaybackYears": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "npv": {
                "type": "number"
              },
              "irrPercent": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Internal rate of return in %; null if none exists in [−99%, 1000%]."
              },
              "totalNetSavings": {
                "type": "number",
                "description": "Sum of yearly net cash flows (savings − maintenance) over the horizon."
              },
              "roiPercent": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "(total net savings − net investment) ÷ net investment; null when net investment is 0."
              },
              "years": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "year",
                    "savings",
                    "maintenance",
                    "netCashFlow",
                    "cumulative",
                    "discountedCashFlow",
                    "discountedCumulative"
                  ],
                  "properties": {
                    "year": {
                      "type": "integer"
                    },
                    "savings": {
                      "type": "number"
                    },
                    "maintenance": {
                      "type": "number"
                    },
                    "netCashFlow": {
                      "type": "number"
                    },
                    "cumulative": {
                      "type": "number"
                    },
                    "discountedCashFlow": {
                      "type": "number"
                    },
                    "discountedCumulative": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "PercentageRatioCalculatorInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "mode": {
            "title": "Question",
            "type": "string",
            "enum": [
              "percent-of",
              "what-percent",
              "change",
              "increase",
              "decrease",
              "reverse",
              "points",
              "ratio-simplify",
              "ratio-scale",
              "ratio-split",
              "aspect"
            ],
            "default": "percent-of"
          },
          "x": {
            "title": "Value X",
            "description": "Percentage modes: the percentage or the start value (see the question). Aspect ratio: width.",
            "type": "number"
          },
          "y": {
            "title": "Value Y",
            "description": "Percentage modes: the base, end or final value. Aspect ratio: height.",
            "type": "number"
          },
          "ratio": {
            "title": "Ratio",
            "description": "For the ratio modes: 2 to 10 positive terms separated by \":\" (e.g. 16:9, 2:3:5, 1.5:2).",
            "type": "string",
            "maxLength": 120
          },
          "amount": {
            "title": "Amount",
            "description": "Split: the total to share out. Scale: the value the chosen term should become.",
            "type": "number"
          },
          "scaleTerm": {
            "title": "Term to scale",
            "description": "Scale mode: which ratio term (1 = first) should equal the amount.",
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "default": 1
          },
          "newWidth": {
            "title": "New width",
            "description": "Aspect ratio: optional; the matching height is calculated.",
            "type": "number",
            "minimum": 0
          },
          "newHeight": {
            "title": "New height",
            "description": "Aspect ratio: optional; the matching width is calculated.",
            "type": "number",
            "minimum": 0
          },
          "decimals": {
            "title": "Decimal places",
            "description": "Rounding of the displayed answer and of split amounts (2 = cents).",
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "default": 2
          }
        },
        "required": []
      },
      "PercentageRatioCalculatorResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "mode",
              "question",
              "answer",
              "answerText",
              "formula",
              "steps",
              "ratio"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "percent-of",
                  "what-percent",
                  "change",
                  "increase",
                  "decrease",
                  "reverse",
                  "points",
                  "ratio-simplify",
                  "ratio-scale",
                  "ratio-split",
                  "aspect"
                ]
              },
              "question": {
                "type": "string"
              },
              "answer": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Main numeric answer, rounded to the chosen decimals (null for ratio-only answers)."
              },
              "answerText": {
                "type": "string"
              },
              "formula": {
                "type": "string"
              },
              "steps": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "ratio": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "properties": {
                  "terms": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  },
                  "simplified": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  },
                  "unitForm": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    },
                    "description": "Ratio divided by its first term (1 : n)."
                  },
                  "parts": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    },
                    "description": "Split amounts (sum exactly to the amount) or scaled terms."
                  },
                  "decimal": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Aspect ratio as a decimal (width ÷ height)."
                  },
                  "nearestNamed": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "newWidth": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "newHeight": {
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}