Structures P&L API - At-Expiry Payoff & Breakevens - FlashAlpha Lab API
Lab API Structures P&L

Structures P&L API

At-expiry profit-and-loss curve, breakevens, and max profit/loss for an arbitrary multi-leg option structure.

Endpoint

POST /v1/structures/pnl
Auth required (X-Api-Key) Rate Limited: Yes Basic+

Request Body Fields

Name In Required Default Description
legsbodyyes - One or more legs. Must be non-empty.
legs[].actionbodyyesbuybuy (alias long) or sell (alias short).
legs[].typebodyyescallcall (alias c) or put (alias p).
legs[].strikebodyyes - Strike price. Must be > 0.
legs[].premiumbodyyes - Per-contract premium paid/received. Must be >= 0.
legs[].quantitybodyno1Number of contracts. Must be > 0.
minUnderlyingbodynoderivedLower bound of the underlying-price curve. If omitted (or not strictly below maxUnderlying), the range is derived from the leg strikes ±30%.
maxUnderlyingbodynoderivedUpper bound of the curve. See minUnderlying.
pointsbodyno81Number of equally-spaced curve sample points (endpoints inclusive). Clamped to a minimum of 2.

Request Body

{
  "legs": [
    { "action": "buy",  "type": "call", "strike": 100, "premium": 3.20, "quantity": 1 },
    { "action": "sell", "type": "call", "strike": 110, "premium": 1.10, "quantity": 1 }
  ],
  "minUnderlying": 80,
  "maxUnderlying": 130,
  "points": 81
}
curl -X POST "https://lab.flashalpha.com/v1/structures/pnl" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "legs": [
          { "action": "buy",  "type": "call", "strike": 100, "premium": 3.20, "quantity": 1 },
          { "action": "sell", "type": "call", "strike": 110, "premium": 1.10, "quantity": 1 }
        ],
        "minUnderlying": 80,
        "maxUnderlying": 130,
        "points": 81
      }'
import requests

resp = requests.post(
    "https://lab.flashalpha.com/v1/structures/pnl",
    headers={"X-Api-Key": "YOUR_API_KEY"},
    json={
        "legs": [
            {"action": "buy",  "type": "call", "strike": 100, "premium": 3.20, "quantity": 1},
            {"action": "sell", "type": "call", "strike": 110, "premium": 1.10, "quantity": 1},
        ],
        "minUnderlying": 80,
        "maxUnderlying": 130,
        "points": 81,
    },
)
data = resp.json()
print(f"Breakevens: {data['breakevens']}")
const resp = await fetch(
  "https://lab.flashalpha.com/v1/structures/pnl",
  {
    method: "POST",
    headers: {
      "X-Api-Key": "YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      legs: [
        { action: "buy",  type: "call", strike: 100, premium: 3.20, quantity: 1 },
        { action: "sell", type: "call", strike: 110, premium: 1.10, quantity: 1 }
      ],
      minUnderlying: 80,
      maxUnderlying: 130,
      points: 81
    })
  }
);
const data = await resp.json();
console.log("Breakevens:", data.breakevens);

This is a pure-math POST endpoint: the result is computed analytically from the legs you supply, with no market-data lookup, symbol resolution, or live IV. There is no live Try-It widget for it - run the cURL example above to try it.

Response

{
  "legs": [
    { "action": "buy",  "type": "call", "strike": 100, "premium": 3.2, "quantity": 1 },
    { "action": "sell", "type": "call", "strike": 110, "premium": 1.1, "quantity": 1 }
  ],
  "pnl_curve": [
    { "underlying": 80,    "pnl": -2.1 },
    { "underlying": 80.625, "pnl": -2.1 },
    { "underlying": 100,   "pnl": -2.1 },
    { "underlying": 110,   "pnl": 7.9 },
    { "underlying": 130,   "pnl": 7.9 }
  ],
  "breakevens": [102.1],
  "max_profit": 7.9,
  "max_loss": -2.1
}

Key Response Fields

Field Type Description
legsarrayEchoes the request body verbatim
pnl_curvearraySampled payoff points, each { underlying, pnl }
breakevensarrayUnderlying prices where P&L crosses zero (may be empty)
max_profitnumber/nullBounded maximum profit, or null when unbounded on that side
max_lossnumber/nullBounded maximum loss, or null when unbounded on that side

Errors

Status Error When
400empty_legslegs is missing or empty.
400invalid_actionleg[i].action is not buy/sell (or long/short).
400invalid_typeleg[i].type is not call/put (or c/p).
400invalid_strikeleg[i].strike <= 0.
400invalid_premiumleg[i].premium < 0.
400invalid_quantityleg[i].quantity <= 0.
403tier_restrictedFree plan. Requires Basic or higher.

About

This endpoint is part of the Structures family of pure-math multi-leg utilities. Every result is a deterministic function of the legs you supply: there is no market-data lookup, no symbol resolution, and no live IV. You pass the legs and premiums, and the response is computed analytically.

The at-expiry payoff is piecewise-linear in the underlying, so breakevens and the bounded max/min are solved exactly from the kinks at the strikes. max_profit and max_loss are null on any side that is unbounded - for example, a naked long call has unbounded max_profit, and a naked short call has unbounded max_loss.

Common Use Cases

  • Render an at-expiry payoff diagram by plotting the pnl_curve points for any custom spread, condor, or butterfly
  • Surface exact breakevens before placing a multi-leg order so you know where the trade turns profitable
  • Read max_profit and max_loss to confirm a structure is defined-risk and size the position to your risk budget
  • Detect unbounded exposure when max_profit or max_loss comes back null, flagging naked legs before you trade them
  • Compute reward-to-risk from max_profit over max_loss to rank candidate structures side by side without any market data
  • Set profit-target and stop levels off the breakevens and curve kinks for an exit plan that matches the payoff
  • Validate a structure's shape across the supplied minUnderlying/maxUnderlying range before committing capital

Related reading

Complementary endpoints

  • Structure Greeks - add aggregate delta/gamma/theta/vega to the same legs
  • Option Quote - pull real premiums and IV to feed the legs
  • VRP - gauge whether the structure is rich or cheap to put on

Ready to build?

Get your free API key and start pulling live options data in 30 seconds.