Build a Position Sizing Tool with an API: Kelly Criterion for Options | FlashAlpha

Build a Position Sizing Tool with an API: Kelly Criterion for Options

How to build an options position sizing tool using the FlashAlpha API. Compute Kelly criterion optimal fractions for options trades using real-world probability measures. Combine with BSM Greeks and IV solver for a complete risk management toolkit. Growth tier endpoint with examples in Python, JavaScript, C#, Go, Java, and cURL.

T
Tomasz Dobrowolski Quant Engineer
Mar 29, 2026
42 min read
KellyCriterion PositionSizing API RiskManagement Python DeveloperGuide Options

If you're searching for a Kelly criterion API, an options position sizing tool, or trying to build a position sizing calculator that handles the mathematical complexity of options, this is the guide. One endpoint takes your trade parameters and returns the optimal Kelly fraction, expected edge, win probability, expected payoff, and bankroll risk metrics — all computed using proper log-normal distribution integration.


What Is the Kelly Criterion?

The Kelly criterion determines the fraction of your bankroll that maximizes long-run growth. For a simple bet with probability p of winning and payoff ratio b, the Kelly fraction is:

f* = (bp - q) / b     where q = 1 - p

For a coin flip that pays 2:1, Kelly says bet 25% of your bankroll. Simple enough.

Options break this formula. Here's why:

  1. Non-binary payoffs. A call option doesn't just win or lose. It can expire worthless, finish slightly in-the-money (partial loss after premium), or finish deep in-the-money. The payoff is a continuous distribution, not a coin flip.
  2. Probability measures from BSM. The probability of finishing in-the-money isn't a guess — it's N(d2) from the Black-Scholes-Merton model, which depends on spot, strike, time to expiry, volatility, and the risk-free rate. And that's the risk-neutral probability. The real-world probability requires adjusting for the drift parameter mu.
  3. Expected payoff integration. The expected payoff of an option involves integrating the payoff function over the log-normal distribution of the underlying. This is not arithmetic you do on the back of a napkin.
  4. Premium as cost basis. Your maximum loss is the premium paid, but your expected loss depends on the full probability-weighted payoff distribution relative to that premium.

Computing Kelly for options correctly means solving the expected log-wealth maximization problem under the physical (real-world) probability measure, using the BSM framework for the underlying dynamics. That's what the API does.

The API Approach: One Call with Your Trade Parameters

The FlashAlpha Kelly endpoint (GET /v1/pricing/kelly) takes the parameters that define your trade and returns position sizing metrics. Here's what goes in and what comes out:

ParameterRequiredDescription
spotYesCurrent underlying price
strikeYesOption strike price
dteYesDays to expiration
sigmaYesImplied volatility (annualized, decimal)
premiumYesOption premium paid (per share)
muYesExpected annual return of the underlying (-200% to +200%)
typeNoOption type: "call" (default) or "put"
rNoRisk-free rate (default 0.045)
qNoDividend yield (default 0.013)

The response gives you everything you need for position sizing:

FieldWhat It Tells You
kelly_fractionOptimal fraction of bankroll to allocate (0 to 1, or negative if the trade has negative edge)
expected_edgeExpected profit per dollar risked under the physical measure
win_probabilityProbability of the option finishing in-the-money (physical measure, not risk-neutral)
expected_payoffProbability-weighted expected payoff per share
bankroll_riskRisk metrics: max loss, expected loss, payoff variance

No manual integration over log-normal distributions. No BSM probability measure computation. No expected payoff calculation. One call.

Kelly criterion for options with BSM probability measures

One API call. Growth plan and above. Free tier available for Greeks and IV.

Get API Access

Quick Start: All 6 Languages

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_KEY")
kelly = fa.kelly(spot=590, strike=600, dte=30, sigma=0.20, premium=12.50, mu=0.08, type="call")

print(f"Kelly fraction:   {kelly['kelly_fraction']:.4f}")
print(f"Expected edge:    {kelly['expected_edge']:.4f}")
print(f"Win probability:  {kelly['win_probability']:.2%}")
print(f"Expected payoff:  ${kelly['expected_payoff']:.2f}")
import { FlashAlpha } from 'flashalpha';

const fa = new FlashAlpha('YOUR_KEY');
const kelly = await fa.kelly({
  spot: 590, strike: 600, dte: 30,
  sigma: 0.20, premium: 12.50, mu: 0.08, type: 'call'
});

console.log(`Kelly fraction: ${kelly.kelly_fraction}`);
console.log(`Expected edge:  ${kelly.expected_edge}`);
using FlashAlpha;

var fa = new FlashAlphaClient("YOUR_KEY");
var kelly = await fa.KellyAsync(
    spot: 590, strike: 600, dte: 30,
    sigma: 0.20, premium: 12.50, mu: 0.08, type: "call");

Console.WriteLine($"Kelly fraction: {kelly.KellyFraction:F4}");
Console.WriteLine($"Expected edge:  {kelly.ExpectedEdge:F4}");
fa := flashalpha.NewClient("YOUR_KEY")
kelly, err := fa.Kelly(ctx, &flashalpha.KellyRequest{
    Spot: 590, Strike: 600, DTE: 30,
    Sigma: 0.20, Premium: 12.50, Mu: 0.08, Type: "call",
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Kelly fraction: %.4f\n", kelly.KellyFraction)
FlashAlphaClient fa = new FlashAlphaClient("YOUR_KEY");
JsonObject kelly = fa.kelly(590, 600, 30, 0.20, 12.50, 0.08, "call");

System.out.println("Kelly fraction: " + kelly.get("kelly_fraction"));
System.out.println("Expected edge:  " + kelly.get("expected_edge"));
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/pricing/kelly?spot=590&strike=600&dte=30&sigma=0.20&premium=12.50&mu=0.08&type=call"
$ pip install flashalpha  |  npm install flashalpha  |  dotnet add package FlashAlpha  |  go get github.com/FlashAlpha-lab/flashalpha-go

Full Response Walkthrough

Here's what a real response looks like for a 30-DTE slightly OTM call on SPY at $590, strike $600, with 20% IV and $12.50 premium:

{
  "spot": 590.0,
  "strike": 600.0,
  "dte": 30,
  "type": "call",
  "sigma": 0.20,
  "premium": 12.50,
  "mu": 0.08,
  "r": 0.045,
  "q": 0.013,
  "kelly_fraction": 0.0732,
  "expected_edge": 0.0841,
  "win_probability": 0.4217,
  "expected_payoff": 13.55,
  "bankroll_risk": {
    "max_loss": 12.50,
    "expected_loss": 7.23,
    "payoff_variance": 184.62,
    "payoff_std": 13.59
  }
}

Reading this response:

  • kelly_fraction = 0.0732 — Allocate 7.32% of your bankroll to this trade for maximum long-run growth. On a $100k portfolio, that's $7,320 in premium.
  • expected_edge = 0.0841 — You expect to make 8.41 cents per dollar risked, under your assumed mu of 8%.
  • win_probability = 0.4217 — The option finishes ITM 42.17% of the time under the physical measure (not the risk-neutral 38-40% you'd get from N(d2)).
  • expected_payoff = 13.55 — The probability-weighted expected payoff is $13.55 per share, against a $12.50 premium. The edge comes from the expected payoff exceeding the premium.
  • bankroll_risk — Maximum loss is the premium ($12.50). Expected loss is $7.23 (probability-weighted). High payoff variance (184.62) reflects the asymmetric payoff distribution of options.

A negative kelly_fraction means the trade has negative expected edge — you should not take it (or consider being a seller).

Building the Position Sizing Tool

1. Single Trade Kelly Calculator

The simplest use case: input a trade, get the optimal position size.

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_KEY")

def position_size(bankroll, spot, strike, dte, sigma, premium, mu, option_type="call"):
    kelly = fa.kelly(spot=spot, strike=strike, dte=dte,
                     sigma=sigma, premium=premium, mu=mu, type=option_type)

    fraction = kelly['kelly_fraction']
    if fraction <= 0:
        print(f"Negative edge ({kelly['expected_edge']:.4f}). Do not take this trade.")
        return 0

    dollar_amount = bankroll * fraction
    contracts = int(dollar_amount / (premium * 100))  # options are 100 shares

    print(f"Kelly fraction:    {fraction:.4f} ({fraction:.2%} of bankroll)")
    print(f"Dollar allocation: ${dollar_amount:,.2f}")
    print(f"Contracts:         {contracts}")
    print(f"Expected edge:     {kelly['expected_edge']:.4f}")
    print(f"Win probability:   {kelly['win_probability']:.2%}")
    return contracts

# Example: $100k bankroll, SPY 590/600 call, 30 DTE, 20% IV, $12.50 premium
position_size(100000, spot=590, strike=600, dte=30, sigma=0.20, premium=12.50, mu=0.08)

2. Fractional Kelly: Half-Kelly and Quarter-Kelly

Full Kelly maximizes growth but produces large drawdowns. In practice, most traders use fractional Kelly — typically half-Kelly (50%) or quarter-Kelly (25%) — to reduce volatility at the cost of slightly lower expected growth.

def fractional_kelly(bankroll, spot, strike, dte, sigma, premium, mu,
                     option_type="call", fractions=[1.0, 0.5, 0.25]):
    kelly = fa.kelly(spot=spot, strike=strike, dte=dte,
                     sigma=sigma, premium=premium, mu=mu, type=option_type)

    full_f = kelly['kelly_fraction']
    if full_f <= 0:
        print("Negative edge. No allocation at any Kelly fraction.")
        return

    print(f"Full Kelly: {full_f:.4f}  |  Edge: {kelly['expected_edge']:.4f}")
    print(f"{'Fraction':>10}  {'Allocation':>12}  {'Contracts':>10}  {'Risk':>12}")
    print("-" * 50)

    for mult in fractions:
        f = full_f * mult
        dollars = bankroll * f
        contracts = int(dollars / (premium * 100))
        risk_pct = (contracts * premium * 100) / bankroll
        label = f"{mult:.0%} Kelly"
        print(f"{label:>10}  ${dollars:>10,.2f}  {contracts:>10}  {risk_pct:>11.2%}")

fractional_kelly(100000, spot=590, strike=600, dte=30, sigma=0.20, premium=12.50, mu=0.08)

Output:

Full Kelly: 0.0732  |  Edge: 0.0841
  Fraction    Allocation   Contracts          Risk
--------------------------------------------------
100% Kelly    $7,320.00           5        6.25%
 50% Kelly    $3,660.00           2        2.50%
 25% Kelly    $1,830.00           1        1.25%

Half-Kelly gives you ~75% of the growth rate with ~50% of the drawdown. For most traders, this is the right balance.

3. Multi-Leg Kelly: Spreads and Complex Structures

For vertical spreads, iron condors, or other multi-leg trades, compute Kelly for each leg and combine. The net premium and max loss change the risk profile.

def spread_kelly(bankroll, spot, long_strike, short_strike, dte, sigma,
                 long_premium, short_premium, mu, option_type="call"):
    """Kelly for a vertical spread (buy long_strike, sell short_strike)."""
    net_premium = long_premium - short_premium  # net debit
    max_loss = net_premium
    max_gain = abs(short_strike - long_strike) - net_premium

    # Compute Kelly for each leg
    long_kelly = fa.kelly(spot=spot, strike=long_strike, dte=dte,
                          sigma=sigma, premium=long_premium, mu=mu, type=option_type)
    short_kelly = fa.kelly(spot=spot, strike=short_strike, dte=dte,
                           sigma=sigma, premium=short_premium, mu=mu, type=option_type)

    # Net expected edge based on spread payoff
    net_edge = long_kelly['expected_payoff'] - short_kelly['expected_payoff'] - net_premium
    edge_ratio = net_edge / net_premium if net_premium > 0 else 0

    print(f"Spread: {long_strike}/{short_strike} {option_type}")
    print(f"Net debit:     ${net_premium:.2f}")
    print(f"Max gain:      ${max_gain:.2f}")
    print(f"Max loss:      ${max_loss:.2f}")
    print(f"Long leg win:  {long_kelly['win_probability']:.2%}")
    print(f"Short leg win: {short_kelly['win_probability']:.2%}")
    print(f"Net edge:      ${net_edge:.2f} per share ({edge_ratio:.2%} of premium)")

    return long_kelly, short_kelly

# Example: 590/600 call debit spread
spread_kelly(100000, spot=590, long_strike=590, short_strike=600,
             dte=30, sigma=0.20, long_premium=18.00, short_premium=12.50, mu=0.08)

4. Greeks + Kelly: Risk Metrics Alongside Position Sizing

The Kelly fraction tells you how much to allocate. The Greeks tell you what risks you're taking. Combine both endpoints for a complete picture.

def kelly_with_greeks(bankroll, spot, strike, dte, sigma, premium, mu, option_type="call"):
    """Position sizing with full risk context."""
    kelly = fa.kelly(spot=spot, strike=strike, dte=dte,
                     sigma=sigma, premium=premium, mu=mu, type=option_type)
    greeks = fa.greeks(spot=spot, strike=strike, dte=dte,
                       sigma=sigma, type=option_type)

    fraction = kelly['kelly_fraction']
    contracts = int((bankroll * fraction) / (premium * 100)) if fraction > 0 else 0

    print(f"=== Position Sizing: {spot}/{strike} {option_type}, {dte} DTE ===")
    print(f"\nKelly:")
    print(f"  Fraction:       {fraction:.4f} ({fraction:.2%})")
    print(f"  Contracts:      {contracts}")
    print(f"  Expected edge:  {kelly['expected_edge']:.4f}")
    print(f"\nGreeks (per contract):")
    print(f"  Delta:  {greeks['delta']:+.4f}  (${greeks['delta'] * 100 * contracts:+,.0f} notional)")
    print(f"  Gamma:  {greeks['gamma']:.4f}")
    print(f"  Theta:  {greeks['theta']:+.4f}  (${greeks['theta'] * 100 * contracts:+,.2f}/day)")
    print(f"  Vega:   {greeks['vega']:+.4f}  (${greeks['vega'] * 100 * contracts:+,.2f}/1% IV)")
    print(f"\nPortfolio Impact:")
    dollar_delta = greeks['delta'] * spot * 100 * contracts
    print(f"  Dollar delta:   ${dollar_delta:+,.0f}")
    print(f"  % of bankroll:  {abs(dollar_delta)/bankroll:.2%}")

kelly_with_greeks(100000, spot=590, strike=600, dte=30, sigma=0.20, premium=12.50, mu=0.08)

The /v1/pricing/greeks endpoint returns all BSM Greeks through third order (delta, gamma, theta, vega, rho, vanna, charm, vomma, dual delta, speed, zomma, color, ultima) — all from the BSM model. This endpoint is available on the Free tier.

5. IV Solver + Kelly Pipeline

If you have a market price but not the IV, use the IV solver endpoint to back-solve sigma, then feed it into the Kelly endpoint.

def kelly_from_market_price(bankroll, spot, strike, dte, market_price, mu, option_type="call"):
    """Full pipeline: market price -> IV -> Kelly."""
    # Step 1: Solve IV from market price
    iv_result = fa.iv(spot=spot, strike=strike, dte=dte,
                      price=market_price, type=option_type)
    sigma = iv_result['iv']

    print(f"Market price: ${market_price:.2f}")
    print(f"Solved IV:    {sigma:.4f} ({sigma*100:.2f}%)")

    # Step 2: Compute Kelly with solved IV
    kelly = fa.kelly(spot=spot, strike=strike, dte=dte,
                     sigma=sigma, premium=market_price, mu=mu, type=option_type)

    fraction = kelly['kelly_fraction']
    contracts = int((bankroll * fraction) / (market_price * 100)) if fraction > 0 else 0

    print(f"Kelly fraction:   {fraction:.4f}")
    print(f"Expected edge:    {kelly['expected_edge']:.4f}")
    print(f"Win probability:  {kelly['win_probability']:.2%}")
    print(f"Contracts:        {contracts}")
    return kelly

kelly_from_market_price(100000, spot=590, strike=600, dte=30, market_price=12.50, mu=0.08)

The /v1/pricing/iv endpoint uses Newton-Raphson to solve for the implied volatility that matches the observed market price. Available on the Free tier.

6. Portfolio-Level Risk Aggregation

When you have multiple positions, compute Kelly for each and aggregate to ensure you're not over-allocated.

def portfolio_kelly(bankroll, trades):
    """Compute Kelly for each trade and check total allocation."""
    total_allocation = 0
    results = []

    for t in trades:
        kelly = fa.kelly(spot=t['spot'], strike=t['strike'], dte=t['dte'],
                         sigma=t['sigma'], premium=t['premium'], mu=t['mu'],
                         type=t.get('type', 'call'))
        f = max(kelly['kelly_fraction'], 0)
        half_f = f * 0.5  # use half-Kelly for portfolio safety
        dollars = bankroll * half_f
        total_allocation += half_f
        results.append({**t, 'kelly': f, 'half_kelly': half_f,
                        'dollars': dollars, 'edge': kelly['expected_edge']})

    print(f"{'Trade':>20}  {'Kelly':>8}  {'Half-K':>8}  {'Dollars':>10}  {'Edge':>8}")
    print("-" * 60)
    for r in results:
        label = f"{r['spot']}/{r['strike']} {r.get('type','call')}"
        print(f"{label:>20}  {r['kelly']:>8.4f}  {r['half_kelly']:>8.4f}  "
              f"${r['dollars']:>8,.0f}  {r['edge']:>+7.4f}")

    print(f"\nTotal allocation: {total_allocation:.2%} of bankroll")
    if total_allocation > 0.30:
        print("WARNING: Total allocation exceeds 30%. Consider reducing position sizes.")

trades = [
    {"spot": 590, "strike": 600, "dte": 30, "sigma": 0.20, "premium": 12.50, "mu": 0.08, "type": "call"},
    {"spot": 590, "strike": 570, "dte": 30, "sigma": 0.22, "premium": 8.00, "mu": 0.08, "type": "put"},
    {"spot": 185, "strike": 190, "dte": 45, "sigma": 0.28, "premium": 5.20, "mu": 0.12, "type": "call"},
]
portfolio_kelly(100000, trades)

Sensitivity Analysis: How Kelly Changes with Inputs

The Kelly fraction is sensitive to your assumptions, especially mu (expected return). Understanding this sensitivity is critical before you size a position.

def sensitivity_grid(spot, strike, dte, sigma, premium):
    """Show how Kelly changes across mu and sigma values."""
    mus = [0.04, 0.06, 0.08, 0.10, 0.15, 0.20]
    sigmas = [0.15, 0.20, 0.25, 0.30]

    print(f"Kelly fraction grid: {spot}/{strike} call, {dte} DTE, premium=${premium}")
    header = f"{'mu\\\\sigma':>10}" + "".join(f"{'  ' + f'{s:.0%}':>8}" for s in sigmas)
    print(header)
    print("-" * (10 + 8 * len(sigmas)))

    for mu in mus:
        row = f"{mu:>+9.0%} "
        for s in sigmas:
            kelly = fa.kelly(spot=spot, strike=strike, dte=dte,
                             sigma=s, premium=premium, mu=mu, type="call")
            f = kelly['kelly_fraction']
            row += f"  {f:>6.4f}" if f > 0 else f"  {'neg':>6}"
        print(row)

sensitivity_grid(spot=590, strike=600, dte=30, sigma=0.20, premium=12.50)

Key insight: the Kelly fraction is much more sensitive to mu than to sigma. If you overestimate the expected return of the underlying by even 2-3%, your Kelly fraction could double. This is why fractional Kelly (half or quarter) is standard practice — it provides a buffer against estimation error in mu.

Common Mistakes with Kelly for Options

  1. Using the stock Kelly formula. The binary Kelly formula (f = (bp - q) / b) assumes two outcomes. Options have a continuous payoff distribution. You need the expected utility maximization under log-normal dynamics, not a binomial approximation.
  2. Using risk-neutral probabilities. N(d2) from BSM is the risk-neutral probability. For position sizing you need the physical (real-world) probability, which requires the drift mu. The API handles this distinction.
  3. Ignoring mu sensitivity. The Kelly fraction depends heavily on your expected return assumption. Always run the sensitivity analysis above before committing to a position size.
  4. Full Kelly allocation. Full Kelly maximizes geometric growth but produces drawdowns of 50%+ regularly. Half-Kelly or quarter-Kelly is appropriate for almost all traders.
  5. Independent Kelly on correlated positions. Kelly assumes independent bets. If you're long calls on SPY, QQQ, and AAPL, those positions are correlated. Use the portfolio aggregation approach above and cap total allocation at 25-30% of bankroll.
  6. Static mu for all underlyings. SPY's expected return is different from TSLA's. Use the /v1/volatility/{symbol} endpoint to get realized volatility data as a basis for estimating mu per underlying.

Complete Risk Management Script

This script combines Greeks, IV solver, and Kelly into a single risk management workflow:

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_KEY")

def full_risk_analysis(bankroll, spot, strike, dte, market_price, mu, option_type="call"):
    """Complete risk analysis: IV solve -> Greeks -> Kelly -> position sizing."""

    # 1. Solve IV from market price
    iv_result = fa.iv(spot=spot, strike=strike, dte=dte,
                      price=market_price, type=option_type)
    sigma = iv_result['iv']

    # 2. Get full Greeks
    greeks = fa.greeks(spot=spot, strike=strike, dte=dte,
                       sigma=sigma, type=option_type)

    # 3. Compute Kelly
    kelly = fa.kelly(spot=spot, strike=strike, dte=dte,
                     sigma=sigma, premium=market_price, mu=mu, type=option_type)

    # 4. Position sizing
    fraction = kelly['kelly_fraction']
    half_kelly = fraction * 0.5
    contracts = int((bankroll * half_kelly) / (market_price * 100)) if fraction > 0 else 0

    print(f"{'=' * 60}")
    print(f"RISK ANALYSIS: {spot}/{strike} {option_type.upper()}, {dte} DTE")
    print(f"{'=' * 60}")

    print(f"\n--- Volatility ---")
    print(f"  Solved IV:        {sigma:.4f} ({sigma*100:.2f}%)")
    print(f"  Market price:     ${market_price:.2f}")

    print(f"\n--- Greeks ---")
    print(f"  Delta:   {greeks['delta']:+.4f}     Gamma:  {greeks['gamma']:.6f}")
    print(f"  Theta:   {greeks['theta']:+.4f}     Vega:   {greeks['vega']:+.4f}")
    print(f"  Vanna:   {greeks['vanna']:+.4f}     Charm:  {greeks['charm']:+.6f}")

    print(f"\n--- Kelly Criterion ---")
    print(f"  Full Kelly:       {fraction:.4f} ({fraction:.2%})")
    print(f"  Half Kelly:       {half_kelly:.4f} ({half_kelly:.2%})")
    print(f"  Expected edge:    {kelly['expected_edge']:+.4f}")
    print(f"  Win probability:  {kelly['win_probability']:.2%}")

    print(f"\n--- Position Sizing (half-Kelly) ---")
    print(f"  Contracts:        {contracts}")
    print(f"  Capital at risk:  ${contracts * market_price * 100:,.2f}")
    print(f"  Dollar delta:     ${greeks['delta'] * spot * 100 * contracts:+,.0f}")
    print(f"  Daily theta:      ${greeks['theta'] * 100 * contracts:+,.2f}")

    print(f"\n--- Risk ---")
    print(f"  Max loss:         ${kelly['bankroll_risk']['max_loss'] * 100 * contracts:,.2f}")
    print(f"  Expected loss:    ${kelly['bankroll_risk']['expected_loss'] * 100 * contracts:,.2f}")
    print(f"  Payoff std dev:   ${kelly['bankroll_risk']['payoff_std'] * 100 * contracts:,.2f}")

# Run it
full_risk_analysis(
    bankroll=100000,
    spot=590, strike=600, dte=30,
    market_price=12.50, mu=0.08, option_type="call"
)

Three API calls. Full risk picture. No manual computation.

$ pip install flashalpha
>>> fa = FlashAlpha("YOUR_KEY")
>>> fa.kelly(spot=590, strike=600, dte=30, sigma=0.20, premium=12.50, mu=0.08)
{"kelly_fraction": 0.0732, "expected_edge": 0.0841, "win_probability": 0.4217, ...}

Using Kelly Data with AI Agents

FlashAlpha provides an MCP (Model Context Protocol) server that lets AI coding assistants call the Kelly endpoint directly. If you're building with Claude, Cursor, Windsurf, or any MCP-compatible agent, connect to the FlashAlpha MCP server and the agent can compute optimal position sizes as part of its reasoning.

{
  "mcpServers": {
    "flashalpha": {
      "url": "https://lab.flashalpha.com/mcp",
      "headers": {
        "X-Api-Key": "YOUR_KEY"
      }
    }
  }
}

The agent can then run the full pipeline: solve IV from a market price, compute Greeks for risk context, calculate the Kelly fraction, and recommend a position size — all grounded in live computation rather than hallucinated numbers.

Why Not Build It Yourself?

You can. Here's what that requires:

  1. BSM probability measures. The physical (real-world) probability of an option finishing ITM isn't N(d2). You need to adjust d2 for the drift mu instead of the risk-free rate r. The integral changes form depending on whether you're computing expected payoff under the physical or risk-neutral measure.
  2. Log-normal distribution integration. The expected payoff of a call is E[max(S_T - K, 0)] under the physical measure. This involves integrating S_T * n(S_T) from K to infinity where S_T follows a log-normal distribution with drift mu. Closed-form solutions exist but involve the bivariate normal CDF with correlation terms.
  3. Edge computation for asymmetric payoffs. The "edge" for an options trade isn't (win_prob * payoff - loss_prob * loss). You need the full expected utility: E[ln(1 + f * R)] where R is the return distribution of the option. Maximizing this requires numerical optimization over the return distribution.
  4. Numerical stability. Deep OTM options produce tiny probabilities and large payoffs. The Kelly computation involves log(1 + f * R) where R can be 1000%+. Naive implementations produce numerical overflow or precision loss.
  5. Put option adjustments. The math for puts differs from calls (integration bounds, payoff function). Handle American-style early exercise approximations if needed.
  6. Validation and edge cases. Zero DTE, deep ITM, deep OTM, negative mu, extreme sigma — every combination needs to produce sensible results. Testing the full parameter space takes weeks.

This is 2-4 weeks of quantitative engineering if you've done it before, longer if you haven't. The API handles all of it.

API Access and Pricing

The Kelly endpoint is available on the Growth plan (from $239/mo billed annually) and above. The Greeks and IV solver endpoints are available on the Free tier, so you can build the full risk management pipeline with Kelly on Growth and supplement with free Greeks and IV calls.

PlanPriceKelly AccessGreeks / IVRate Limit
Free$0NoYes5 req/day
Basicfrom $63/moNoYes100 req/day
Growthfrom $239/moYesYes2,500 req/day
Alphafrom $1,199/moYesYesUnlimited

To evaluate before committing, the interactive API playground lets you test Kelly calls in the browser with your API key. SDKs available in Python, JavaScript, C#, Go, and Java with typed exceptions and automatic retries.

Frequently Asked Questions

The Kelly criterion for options determines the optimal fraction of your bankroll to allocate to an options trade to maximize long-run growth. Unlike the simple Kelly formula for binary bets, options require computing expected payoffs under the physical (real-world) probability measure using BSM dynamics, log-normal distribution integration, and numerical optimization of the expected log-wealth function.
The API uses the physical probability measure, not the risk-neutral measure. Instead of N(d2) with the risk-free rate r, it computes d2 using your specified expected return mu. This gives the real-world probability that the option finishes in-the-money, which is what matters for position sizing (as opposed to pricing, where risk-neutral probabilities are correct).
Mu is the expected annualized return of the underlying. For broad indices like SPY, historical averages of 8-10% are common starting points. For individual stocks, use fundamental analysis or the /v1/volatility/{symbol} endpoint to estimate from realized return data. Always run the sensitivity analysis — the Kelly fraction is highly sensitive to mu.
Almost always fractional Kelly. Full Kelly maximizes long-run geometric growth but produces drawdowns exceeding 50%. Half-Kelly (50%) gives approximately 75% of the growth rate with roughly half the variance. Quarter-Kelly is appropriate when you have low confidence in your mu estimate or when trading correlated positions.
Yes. Compute Kelly for each leg independently, then combine using the net premium as your cost basis. The article above shows a vertical spread example. For iron condors or more complex structures, compute each leg and aggregate the expected payoffs.
A negative Kelly fraction means the trade has negative expected edge under your assumptions. You should not take the trade as a buyer. A negative fraction could indicate the option is overpriced relative to the expected move (your mu is too low for the premium being charged), or that the option is too far OTM to overcome the premium cost.

Live Market Pulse

Get tick-by-tick visibility into market shifts with full-chain analytics streaming in real time.

Intelligent Screening

Screen millions of option pairs per second using your custom EV rules, filters, and setups.

Execution-Ready

Instantly send structured orders to Interactive Brokers right from your scan results.

Join the Community

Discord

Engage in real time conversations with us!

Twitter / X

Follow us for real-time updates and insights!

GitHub

Explore our open-source SDK, examples, and analytics resources!