{"componentChunkName":"component---src-templates-blog-post-js","path":"/bs-merton-polymarket/","result":{"data":{"site":{"siteMetadata":{"title":"/dev/yukarinoki"}},"markdownRemark":{"id":"83216695-eaac-529e-8777-6878f45fead8","excerpt":"日本語要約: Black-ScholesモデルとMertonジャンプ拡散モデルを用いて、Polymarketの5分BTCバイナリオプションの理論価格を算出し、実際の市場価格と比較します。Monte Carlo…","html":"<blockquote>\n<p><strong>日本語要約</strong>: Black-ScholesモデルとMertonジャンプ拡散モデルを用いて、Polymarketの5分BTCバイナリオプションの理論価格を算出し、実際の市場価格と比較します。Monte Carloシミュレーションのコード例を交えながら、伝統的金融モデルが暗号通貨の超短期市場でどこまで通用するかを検証します。</p>\n</blockquote>\n<p>I’ve been obsessed with Polymarket’s ultra-short-term BTC binary options lately. These are 5-minute contracts where you’re betting whether BTC/USDT will be above or below a strike price at expiry. The tick-by-tick nature of these contracts makes them a perfect playground for testing option pricing models against reality.</p>\n<p>The question I wanted to answer: can classical Black-Scholes or the more sophisticated Merton jump-diffusion model actually predict what Polymarket traders are pricing in? Spoiler: the answer is nuanced and interesting.</p>\n<h2 id=\"why-crypto-binary-options-are-a-weird-beast\" style=\"position:relative;\"><a href=\"#why-crypto-binary-options-are-a-weird-beast\" aria-label=\"why crypto binary options are a weird beast permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Why Crypto Binary Options Are a Weird Beast</h2>\n<p>Binary options are the simplest derivative you can imagine. You get paid $1 if BTC is above the strike at expiry, $0 otherwise. The “fair” price is just $P(S_T > K)$ - the probability that the spot price exceeds the strike at expiration.</p>\n<p>But crypto makes this deceptively hard to model:</p>\n<ul>\n<li><strong>5-minute expiries</strong> mean you’re in a regime where microstructure noise dominates</li>\n<li><strong>BTC volatility</strong> is extreme - annualized vol ranges from 40% to 120% depending on the regime</li>\n<li><strong>Jumps happen constantly</strong> - a single whale market order can move BTC 0.5% in seconds</li>\n<li><strong>Volatility clusters</strong> - calm periods alternate with chaos in a very non-Gaussian way</li>\n</ul>\n<p>Polymarket runs these contracts with strikes spaced around the current spot price (e.g., if BTC is at $68,450, you’ll see strikes at $68,400, $68,500, $68,600). I pulled about 1,200 trades across multiple contract cycles to compare with model predictions.</p>\n<h2 id=\"black-scholes-for-binary-options\" style=\"position:relative;\"><a href=\"#black-scholes-for-binary-options\" aria-label=\"black scholes for binary options permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Black-Scholes for Binary Options</h2>\n<p>The standard BS framework assumes BTC follows geometric Brownian motion (GBM):</p>\n<p>$$dS = \\mu S , dt + \\sigma S , dW$$</p>\n<p>where $\\mu$ is the drift, $\\sigma$ is the volatility, and $dW$ is a Wiener process.</p>\n<p>For a cash-or-nothing binary call (pays $1 if $S_T > K$), the analytical price is:</p>\n<p>$$C<em>{binary} = e^{-rT} \\cdot N(d</em>2)$$</p>\n<p>where:</p>\n<p>$$d_2 = \\frac{\\ln(S/K) + (r - \\frac{1}{2}\\sigma^2)T}{\\sigma\\sqrt{T}}$$</p>\n<p>and $N(\\cdot)$ is the standard normal CDF.</p>\n<p>For 5-minute expiries, $T$ is tiny (about $9.5 \\times 10^{-6}$ years), $r$ is basically zero, and the whole thing reduces to:</p>\n<p>$$C_{binary} \\approx N\\left(\\frac{\\ln(S/K) - \\frac{1}{2}\\sigma^2 T}{\\sigma\\sqrt{T}}\\right)$$</p>\n<p>The critical input is $\\sigma$. I estimated it using rolling 1-hour realized volatility from Binance tick data, annualized. Typical values during my observation period were 45-65% annualized.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/1e52f7e0cdffea324a8d4945424e87b2/2ad15/bo_price_sim_bs.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 66.21621621621621%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACN0lEQVQ4y21Ty47UMBCcr+cGX4TggDhwRgKBBLvzyDpx4jjxI867qHZ2WEBEU+pJ0umurmqfqrLEsiz499r3HSGEv+C9h3Muwz9HQYwRKSUMw4BT0zSwXQeJt9sNPROMMZimKRdo2xZ1rVFVFbSuYa3lM5ufd/xOovcBaRwxJBaUjyVZXvR9l7tIt3meMTKpbQ0efp5RsJnvDeaR76YXjCkiBofO1mTrD4bGtJnRn9e2bZmNsIthhK4C4UUM/rYjEvu+ou88DFmHEHFyZHW8zMIdkEQWlJFKarxtKxmvUMrzPqAoHCWKbJgYB1yvZNhFTseClemhjYWLI2Y2XlhP4rhsHDnlriLBNI0ZRWFYpMPl0rBBi/O5pmQ2651NKRuLm9JQ2sC6iMb2MBwhpon6HM4JU4HorXXJ2LBonWNK0nBAeHb6VKqnvAJjNmLKWJY5r5KYIp211tSyRk29xbxpmrPmAikicL3lhtAUVWl0/DMySZyVJGElBWX3pMD+nx29IzFXtkRWL5ti1Bmu4RjqgsfHh7yPRVHkYuJyw7F80+KpbBGmBfOy/nZYsHCiyFxhKQt+evOxx6u3Cq8/tLhQy+J6xYW4nwxhOKcRSfMAXBU03fxWWHxXHVya4ccFK00c7gXffW3w/luLzw2wsukyH6Ov63poQ30n3m+839lgJeuhNlBPNR7OFX5cND59udHQkMc/7VPkngwZh2svp+Q+RvAH2653KJVCXZWoaWarKwTXU6bn48f8X46w5JkDfhKOAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"BS Only Dashboard\"\n        title=\"BS Only Dashboard\"\n        src=\"/static/1e52f7e0cdffea324a8d4945424e87b2/fcda8/bo_price_sim_bs.png\"\n        srcset=\"/static/1e52f7e0cdffea324a8d4945424e87b2/12f09/bo_price_sim_bs.png 148w,\n/static/1e52f7e0cdffea324a8d4945424e87b2/e4a3f/bo_price_sim_bs.png 295w,\n/static/1e52f7e0cdffea324a8d4945424e87b2/fcda8/bo_price_sim_bs.png 590w,\n/static/1e52f7e0cdffea324a8d4945424e87b2/2ad15/bo_price_sim_bs.png 801w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>The BS dashboard above shows the model tracking real prices. It works… okay. The shape is right - prices near 0.5 when the spot is at the strike, approaching 1 or 0 as you move away. But there’s consistent mispricing at the tails.</p>\n<h2 id=\"where-black-scholes-breaks-down\" style=\"position:relative;\"><a href=\"#where-black-scholes-breaks-down\" aria-label=\"where black scholes breaks down permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Where Black-Scholes Breaks Down</h2>\n<p>BS assumes returns are log-normally distributed. In a 5-minute window for BTC, the actual return distribution has:</p>\n<ol>\n<li><strong>Fat tails</strong>: the kurtosis of 5-minute BTC returns is typically 8-15 (normal would be 3). Extreme moves are way more likely than BS predicts.</li>\n<li><strong>Jumps</strong>: BTC doesn’t move continuously. Liquidation cascades, large market orders, and news events create discrete jumps. In my dataset, about 3-4% of 5-minute windows had moves exceeding 3 standard deviations.</li>\n<li><strong>Asymmetry</strong>: negative jumps (crashes) are slightly more common and larger than positive jumps, giving negative skewness.</li>\n<li><strong>Volatility clustering</strong>: a big move in the last 5 minutes makes a big move in the <em>next</em> 5 minutes much more likely. BS assumes constant vol.</li>\n</ol>\n<p>The practical effect: BS underprices deep out-of-the-money binaries. If the strike is $200 above spot and you have 5 minutes left, BS says “basically zero probability” but the market says “small but non-trivial” because traders know jumps happen.</p>\n<h2 id=\"merton-jump-diffusion-model\" style=\"position:relative;\"><a href=\"#merton-jump-diffusion-model\" aria-label=\"merton jump diffusion model permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Merton Jump-Diffusion Model</h2>\n<p>Merton (1976) extended GBM by adding a compound Poisson jump process:</p>\n<p>$$dS = (\\mu - \\lambda k)S , dt + \\sigma S , dW + S , dJ$$</p>\n<p>where:</p>\n<ul>\n<li>$\\lambda$ is the jump intensity (average number of jumps per unit time)</li>\n<li>$J$ is the jump size, typically $\\ln(1+J) \\sim N(\\mu<em>J, \\sigma</em>J^2)$</li>\n<li>$k = E[e^J - 1] = e^{\\mu<em>J + \\frac{1}{2}\\sigma</em>J^2} - 1$ is the expected percentage jump size</li>\n</ul>\n<p>The key insight: between jumps, the price follows GBM with reduced drift (to compensate for the expected jump contribution). When a jump arrives, the price instantaneously moves by a log-normal factor.</p>\n<p>For binary option pricing under Merton, the analytical formula is a series expansion:</p>\n<p>$$C<em>{binary}^{Merton} = \\sum</em>{n=0}^{\\infty} \\frac{e^{-\\lambda’ T}(\\lambda’ T)^n}{n!} \\cdot N(d_2^{(n)})$$</p>\n<p>where $\\lambda’ = \\lambda(1+k)$ and:</p>\n<p>$$d<em>2^{(n)} = \\frac{\\ln(S/K) + (r - \\frac{1}{2}\\sigma</em>n^2 - \\lambda k)T + n\\mu<em>J}{\\sigma</em>n\\sqrt{T}}$$</p>\n<p>$$\\sigma<em>n = \\sqrt{\\sigma^2 + n\\sigma</em>J^2 / T}$$</p>\n<p>I calibrated the jump parameters from the empirical 5-minute return distribution:</p>\n<ul>\n<li>$\\lambda = 500$ jumps/year (roughly 1 jump per 10 five-minute intervals)</li>\n<li>$\\mu_J = -0.0002$ (slight downward bias)</li>\n<li>$\\sigma_J = 0.003$ (jump magnitude)</li>\n<li>$\\sigma = 0.50$ (diffusion vol, annualized)</li>\n</ul>\n<h2 id=\"interactive-simulator\" style=\"position:relative;\"><a href=\"#interactive-simulator\" aria-label=\"interactive simulator permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Interactive Simulator</h2>\n<p>Before diving into the code, try the interactive simulator below. You can adjust the model parameters (volatility, jump intensity, etc.) and run Monte Carlo simulations to see how BS and Merton pricing curves diverge in real-time.</p>\n<iframe src=\"/visualizations/bs-merton-sim.html\" width=\"100%\" height=\"580\" frameborder=\"0\" style=\"border: 1px solid #30363d; border-radius: 2px;\"></iframe>\n<h2 id=\"monte-carlo-simulation-approach\" style=\"position:relative;\"><a href=\"#monte-carlo-simulation-approach\" aria-label=\"monte carlo simulation approach permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Monte Carlo Simulation Approach</h2>\n<p>While the analytical Merton formula works, I also ran Monte Carlo simulations for both models. This lets me sanity-check the analytics and easily extend to more complex dynamics later.</p>\n<p>Here’s the simulation code:</p>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-python line-numbers\"><code class=\"language-python\"><span class=\"token keyword\">import</span> numpy <span class=\"token keyword\">as</span> np\n<span class=\"token keyword\">from</span> scipy<span class=\"token punctuation\">.</span>stats <span class=\"token keyword\">import</span> norm\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">simulate_bs_binary</span><span class=\"token punctuation\">(</span>S0<span class=\"token punctuation\">,</span> K<span class=\"token punctuation\">,</span> T<span class=\"token punctuation\">,</span> sigma<span class=\"token punctuation\">,</span> r<span class=\"token operator\">=</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> n_sims<span class=\"token operator\">=</span><span class=\"token number\">100000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token triple-quoted-string string\">\"\"\"\n    Monte Carlo price for a binary call under GBM.\n    Returns P(S_T > K).\n    \"\"\"</span>\n    Z <span class=\"token operator\">=</span> np<span class=\"token punctuation\">.</span>random<span class=\"token punctuation\">.</span>standard_normal<span class=\"token punctuation\">(</span>n_sims<span class=\"token punctuation\">)</span>\n    S_T <span class=\"token operator\">=</span> S0 <span class=\"token operator\">*</span> np<span class=\"token punctuation\">.</span>exp<span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>r <span class=\"token operator\">-</span> <span class=\"token number\">0.5</span> <span class=\"token operator\">*</span> sigma<span class=\"token operator\">**</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> T <span class=\"token operator\">+</span> sigma <span class=\"token operator\">*</span> np<span class=\"token punctuation\">.</span>sqrt<span class=\"token punctuation\">(</span>T<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> Z<span class=\"token punctuation\">)</span>\n    payoff <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>S_T <span class=\"token operator\">></span> K<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>astype<span class=\"token punctuation\">(</span><span class=\"token builtin\">float</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> np<span class=\"token punctuation\">.</span>exp<span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>r <span class=\"token operator\">*</span> T<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> payoff<span class=\"token punctuation\">.</span>mean<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">simulate_merton_binary</span><span class=\"token punctuation\">(</span>S0<span class=\"token punctuation\">,</span> K<span class=\"token punctuation\">,</span> T<span class=\"token punctuation\">,</span> sigma<span class=\"token punctuation\">,</span> lam<span class=\"token punctuation\">,</span> mu_j<span class=\"token punctuation\">,</span> sigma_j<span class=\"token punctuation\">,</span>\n                           r<span class=\"token operator\">=</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> n_sims<span class=\"token operator\">=</span><span class=\"token number\">100000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token triple-quoted-string string\">\"\"\"\n    Monte Carlo price for a binary call under Merton jump-diffusion.\n    Returns P(S_T > K) with Poisson jumps.\n    \"\"\"</span>\n    <span class=\"token comment\"># Number of jumps in [0, T] for each path</span>\n    N_jumps <span class=\"token operator\">=</span> np<span class=\"token punctuation\">.</span>random<span class=\"token punctuation\">.</span>poisson<span class=\"token punctuation\">(</span>lam <span class=\"token operator\">*</span> T<span class=\"token punctuation\">,</span> n_sims<span class=\"token punctuation\">)</span>\n\n    <span class=\"token comment\"># Compensator for jump drift</span>\n    k <span class=\"token operator\">=</span> np<span class=\"token punctuation\">.</span>exp<span class=\"token punctuation\">(</span>mu_j <span class=\"token operator\">+</span> <span class=\"token number\">0.5</span> <span class=\"token operator\">*</span> sigma_j<span class=\"token operator\">**</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span>\n    drift <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>r <span class=\"token operator\">-</span> <span class=\"token number\">0.5</span> <span class=\"token operator\">*</span> sigma<span class=\"token operator\">**</span><span class=\"token number\">2</span> <span class=\"token operator\">-</span> lam <span class=\"token operator\">*</span> k<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> T\n\n    <span class=\"token comment\"># Diffusion component</span>\n    Z <span class=\"token operator\">=</span> np<span class=\"token punctuation\">.</span>random<span class=\"token punctuation\">.</span>standard_normal<span class=\"token punctuation\">(</span>n_sims<span class=\"token punctuation\">)</span>\n    diffusion <span class=\"token operator\">=</span> sigma <span class=\"token operator\">*</span> np<span class=\"token punctuation\">.</span>sqrt<span class=\"token punctuation\">(</span>T<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> Z\n\n    <span class=\"token comment\"># Jump component: sum of N_jumps log-normal jumps</span>\n    jump_component <span class=\"token operator\">=</span> np<span class=\"token punctuation\">.</span>zeros<span class=\"token punctuation\">(</span>n_sims<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">for</span> i <span class=\"token keyword\">in</span> <span class=\"token builtin\">range</span><span class=\"token punctuation\">(</span>n_sims<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> N_jumps<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span> <span class=\"token operator\">></span> <span class=\"token number\">0</span><span class=\"token punctuation\">:</span>\n            jumps <span class=\"token operator\">=</span> np<span class=\"token punctuation\">.</span>random<span class=\"token punctuation\">.</span>normal<span class=\"token punctuation\">(</span>mu_j<span class=\"token punctuation\">,</span> sigma_j<span class=\"token punctuation\">,</span> N_jumps<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n            jump_component<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> jumps<span class=\"token punctuation\">.</span><span class=\"token builtin\">sum</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token comment\"># Terminal price</span>\n    S_T <span class=\"token operator\">=</span> S0 <span class=\"token operator\">*</span> np<span class=\"token punctuation\">.</span>exp<span class=\"token punctuation\">(</span>drift <span class=\"token operator\">+</span> diffusion <span class=\"token operator\">+</span> jump_component<span class=\"token punctuation\">)</span>\n    payoff <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>S_T <span class=\"token operator\">></span> K<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>astype<span class=\"token punctuation\">(</span><span class=\"token builtin\">float</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> np<span class=\"token punctuation\">.</span>exp<span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>r <span class=\"token operator\">*</span> T<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> payoff<span class=\"token punctuation\">.</span>mean<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n\n<span class=\"token comment\"># Example: BTC at $68,450, strike at $68,500, 5 min to expiry</span>\nS0 <span class=\"token operator\">=</span> <span class=\"token number\">68450.0</span>\nK <span class=\"token operator\">=</span> <span class=\"token number\">68500.0</span>\nT <span class=\"token operator\">=</span> <span class=\"token number\">5.0</span> <span class=\"token operator\">/</span> <span class=\"token punctuation\">(</span><span class=\"token number\">365.25</span> <span class=\"token operator\">*</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span><span class=\"token punctuation\">)</span>  <span class=\"token comment\"># 5 minutes in years</span>\nsigma <span class=\"token operator\">=</span> <span class=\"token number\">0.55</span>  <span class=\"token comment\"># 55% annualized vol</span>\n\n<span class=\"token comment\"># BS price</span>\nbs_price <span class=\"token operator\">=</span> simulate_bs_binary<span class=\"token punctuation\">(</span>S0<span class=\"token punctuation\">,</span> K<span class=\"token punctuation\">,</span> T<span class=\"token punctuation\">,</span> sigma<span class=\"token punctuation\">,</span> n_sims<span class=\"token operator\">=</span><span class=\"token number\">500000</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"BS Binary Price: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>bs_price<span class=\"token punctuation\">:</span><span class=\"token format-spec\">.4f</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># Merton price</span>\nlam <span class=\"token operator\">=</span> <span class=\"token number\">500</span>      <span class=\"token comment\"># ~1 jump per 10 five-min intervals</span>\nmu_j <span class=\"token operator\">=</span> <span class=\"token operator\">-</span><span class=\"token number\">0.0002</span>\nsigma_j <span class=\"token operator\">=</span> <span class=\"token number\">0.003</span>\n\nmerton_price <span class=\"token operator\">=</span> simulate_merton_binary<span class=\"token punctuation\">(</span>S0<span class=\"token punctuation\">,</span> K<span class=\"token punctuation\">,</span> T<span class=\"token punctuation\">,</span> sigma<span class=\"token punctuation\">,</span> lam<span class=\"token punctuation\">,</span> mu_j<span class=\"token punctuation\">,</span> sigma_j<span class=\"token punctuation\">,</span>\n                                       n_sims<span class=\"token operator\">=</span><span class=\"token number\">500000</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"Merton Binary Price: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>merton_price<span class=\"token punctuation\">:</span><span class=\"token format-spec\">.4f</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># Analytical BS for comparison</span>\nd2 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>np<span class=\"token punctuation\">.</span>log<span class=\"token punctuation\">(</span>S0<span class=\"token operator\">/</span>K<span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token punctuation\">(</span><span class=\"token number\">0</span> <span class=\"token operator\">-</span> <span class=\"token number\">0.5</span><span class=\"token operator\">*</span>sigma<span class=\"token operator\">**</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token operator\">*</span>T<span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token punctuation\">(</span>sigma<span class=\"token operator\">*</span>np<span class=\"token punctuation\">.</span>sqrt<span class=\"token punctuation\">(</span>T<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nbs_analytical <span class=\"token operator\">=</span> norm<span class=\"token punctuation\">.</span>cdf<span class=\"token punctuation\">(</span>d2<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"BS Analytical: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>bs_analytical<span class=\"token punctuation\">:</span><span class=\"token format-spec\">.4f</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>The loop in <code class=\"language-text\">simulate_merton_binary</code> is intentionally naive for clarity. In practice you’d vectorize it (pre-allocate max jumps and mask), which gets you ~10x speedup. For 500k paths on a 5-minute contract, runtime is about 2 seconds on my machine.</p>\n<h2 id=\"comparison-against-real-polymarket-data\" style=\"position:relative;\"><a href=\"#comparison-against-real-polymarket-data\" aria-label=\"comparison against real polymarket data permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Comparison Against Real Polymarket Data</h2>\n<p>This is where it gets interesting. I collected trade data from Polymarket’s BTC 5-minute binary contracts over several hours, tracking:</p>\n<ul>\n<li>The contract strike price</li>\n<li>Time remaining at each trade</li>\n<li>The trade price (what the market thinks P(S_T > K) is)</li>\n<li>Contemporaneous BTC/USDT spot from Binance</li>\n</ul>\n<p>Then I computed BS and Merton model prices for each observed trade and plotted them together.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/c04da10043d2c419cf76d88a91c63b59/c61d0/marton_bo_vs_gbm_bo.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 101.35135135135135%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsSAAALEgHS3X78AAACsUlEQVQ4y1VU0ZKkNgzk//8sD6mkcleXSlK52Z0dwDYYG8mWsWGHtM1tsuky4BGSLKmb6YjIGOOcm6ZpnuepARut9dygG6y1sC/Lsm1b+kAXQljXlZndB6gBG+89Xl1GbK6fLWrLLUWHTCIC6+2l4na7vby+EnMuZfuB/N+zbrMIU+AaXEpG2eM4SKAUKQtvwmWLR5ajyJ7jv/eS48utHwfj7DAqlXPuYoytTZPzHkM+z2fF+zsurOPYsccD9uM4tF6nyTuKRAF1d6rB2nnbducSUux7DRMppRzP83meZ9mPXA7vU4zIfqLglQgNdNhdQ8Z5Ke1ah+/fF2PCsghyYSGX1gwLsqMgBCMMM6rBKB2RqvXwfB7M0vfeWn7WMg8iYU4wopbyAQw4xlAHhksaAlwEfpxz8t5d5GutmGkYeiYG1aAQhMF+sd0JgtMWomgzzXYxVSIWdN0fD+xH6MOYQSlQLNsWq3MSsHUFR6ms48C3t7dxHK/7MAwYBCrMn1D9AoNnWmdTB5xxchUJSsLAzv/j/RPaOLf77Q/obvFOTxZ66ThERKITjAdT2Y+j8trWZyAXO/vl6y+rX1mS58YzVPt2v0Phxk0U5fxx/vNSSxNIXTANZvj9559C4IXC7BtVnghNxhCdX768PP4e+aaRO1tKQcrVy3VnXr/99uv2XrZc3AqRpI44gBUizhIe6tVGVmvqHc+B+4WVT6vsa4TozpjT17/+VJNCg+4SSWxEg4knuhQOtpdlCGQiGfJqXqZxMrf+cR/HuxpSIGVULrtfmzwvqtAJvhBoGOE5hI24RMkxSoCgiVcbg19dJQ/ChJauqA4fNui8qI4iYALCgzYUmoFstZln2/cDusMrOODPBMeA4HryvCzwA2GE00OsGw4QGSKx9EeKxbkQ8bZWCE/4AP8A2qZysd7C88wAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"BS vs Merton vs Polymarket\"\n        title=\"BS vs Merton vs Polymarket\"\n        src=\"/static/c04da10043d2c419cf76d88a91c63b59/fcda8/marton_bo_vs_gbm_bo.png\"\n        srcset=\"/static/c04da10043d2c419cf76d88a91c63b59/12f09/marton_bo_vs_gbm_bo.png 148w,\n/static/c04da10043d2c419cf76d88a91c63b59/e4a3f/marton_bo_vs_gbm_bo.png 295w,\n/static/c04da10043d2c419cf76d88a91c63b59/fcda8/marton_bo_vs_gbm_bo.png 590w,\n/static/c04da10043d2c419cf76d88a91c63b59/efc66/marton_bo_vs_gbm_bo.png 885w,\n/static/c04da10043d2c419cf76d88a91c63b59/c61d0/marton_bo_vs_gbm_bo.png 1145w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>The chart shows two panels. The top panel is the BTC/USDT mid price over the observation window. The bottom panel overlays three series: Polymarket actual trade prices (what people paid), BS theoretical prices, and Merton Monte Carlo prices.</p>\n<p>Key observations:</p>\n<p><strong>1. Merton tracks the market better than BS at the extremes.</strong> When the spot is far from the strike (contract is deep ITM or OTM), Merton prices are closer to what traders actually pay. BS snaps too aggressively to 0 or 1.</p>\n<p><strong>2. Both models track well near ATM.</strong> When the spot is close to the strike and there’s 2-3 minutes left, all three lines converge. Near ATM, the jump component doesn’t change the probability much since a small move in either direction is equally likely from diffusion alone.</p>\n<p><strong>3. The market has a persistent “crash premium.”</strong> Polymarket prices for OTM puts (binary calls struck above spot) are slightly higher than either model predicts. Traders are assigning extra probability to upward jumps that neither model fully captures. This might reflect order flow imbalance or informed trading.</p>\n<p><strong>4. Discrete price effects matter.</strong> Polymarket contracts trade at discrete prices (0.01 increments), so very OTM contracts have a floor at $0.01-0.02 even when models say the fair value is $0.005. This creates a systematic overpricing of tail events.</p>\n<h2 id=\"where-the-models-diverge-from-the-market\" style=\"position:relative;\"><a href=\"#where-the-models-diverge-from-the-market\" aria-label=\"where the models diverge from the market permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Where the Models Diverge from the Market</h2>\n<p>The biggest systematic errors I noticed:</p>\n<ul>\n<li><strong>Last 30 seconds</strong>: Both models become unreliable in the final half-minute. Microstructure effects dominate - the bid-ask spread on BTC itself creates uncertainty that’s not captured by either model. The market seems to use a “volatility bump” in the last few seconds.</li>\n<li><strong>During high-activity periods</strong>: When BTC is trending (3+ consecutive 5-min candles in one direction), the market prices in momentum that neither model accounts for. BS and Merton are both martingale models - they don’t believe in trends.</li>\n<li><strong>Jump clustering</strong>: The Merton model assumes jumps arrive independently (Poisson). In reality, one jump often triggers another (liquidation cascades). During my observation period, there was a sequence of 3 rapid moves within 2 minutes that pushed the spot $150. The market repriced instantly, but Merton’s independent-jump assumption meant it still underestimated tail probabilities during that episode.</li>\n</ul>\n<h2 id=\"what-would-actually-work-better\" style=\"position:relative;\"><a href=\"#what-would-actually-work-better\" aria-label=\"what would actually work better permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>What Would Actually Work Better?</h2>\n<p>Based on these results, a few extensions seem promising:</p>\n<ol>\n<li><strong>Self-exciting jump process</strong> (Hawkes process instead of Poisson): jumps beget jumps. This would capture the clustering effect.</li>\n<li><strong>Stochastic volatility + jumps</strong> (SVJ or SVJJ): let $\\sigma$ itself be random and correlated with returns. Heston + jumps might nail the dynamics better.</li>\n<li><strong>Regime-switching vol</strong>: simple two-state model (calm vs. volatile) with Markov transitions. Computationally cheap and might capture the bimodal nature of 5-min BTC vol.</li>\n<li><strong>Just use the orderbook</strong>: for ultra-short expiries, the BTC orderbook depth within $100-200 of the spot price is probably more informative than any parametric model. If there’s a $2M bid wall $50 below, the probability of breaking through in 5 minutes is lower than any vol estimate would suggest.</li>\n</ol>\n<h2 id=\"takeaways\" style=\"position:relative;\"><a href=\"#takeaways\" aria-label=\"takeaways permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Takeaways</h2>\n<p>For anyone building pricing models for crypto binary options:</p>\n<ul>\n<li>BS is a reasonable starting point and surprisingly decent for ATM contracts with 2-5 minutes left</li>\n<li>Merton adds real value at the tails - if you’re market-making these contracts, using BS alone will get you picked off on OTM strikes</li>\n<li>Neither model captures the full picture - the market knows about orderbook dynamics, momentum, and jump clustering that parametric models miss</li>\n<li>Monte Carlo is your friend for these short-dated contracts since path count needed is modest and you can layer in arbitrarily complex dynamics</li>\n<li>Calibration window matters enormously - I got best results using the last 1-2 hours of realized vol rather than daily estimates</li>\n</ul>\n<p>The code above is a starting point. The real alpha is in how you estimate $\\sigma$ and the jump parameters in real-time, adapting to the current market regime. But that’s a topic for another post.</p>","tableOfContents":"<ul>\n<li><a href=\"/bs-merton-polymarket/#why-crypto-binary-options-are-a-weird-beast\">Why Crypto Binary Options Are a Weird Beast</a></li>\n<li><a href=\"/bs-merton-polymarket/#black-scholes-for-binary-options\">Black-Scholes for Binary Options</a></li>\n<li><a href=\"/bs-merton-polymarket/#where-black-scholes-breaks-down\">Where Black-Scholes Breaks Down</a></li>\n<li><a href=\"/bs-merton-polymarket/#merton-jump-diffusion-model\">Merton Jump-Diffusion Model</a></li>\n<li><a href=\"/bs-merton-polymarket/#interactive-simulator\">Interactive Simulator</a></li>\n<li><a href=\"/bs-merton-polymarket/#monte-carlo-simulation-approach\">Monte Carlo Simulation Approach</a></li>\n<li><a href=\"/bs-merton-polymarket/#comparison-against-real-polymarket-data\">Comparison Against Real Polymarket Data</a></li>\n<li><a href=\"/bs-merton-polymarket/#where-the-models-diverge-from-the-market\">Where the Models Diverge from the Market</a></li>\n<li><a href=\"/bs-merton-polymarket/#what-would-actually-work-better\">What Would Actually Work Better?</a></li>\n<li><a href=\"/bs-merton-polymarket/#takeaways\">Takeaways</a></li>\n</ul>","frontmatter":{"title":"Comparing Black-Scholes and Merton Jump-Diffusion Models Against Real Polymarket Binary Option Prices","date":"November 15, 2025","description":"An empirical comparison of Black-Scholes and Merton jump-diffusion pricing models against actual binary option prices on Polymarket's BTC 5-minute contracts."}}},"pageContext":{"slug":"/bs-merton-polymarket/","previous":{"fields":{"slug":"/polymarket-binary-options-intro/"},"frontmatter":{"title":"How Binary Options Work on Prediction Markets: A Developer's Introduction to Polymarket Contracts"}},"next":{"fields":{"slug":"/self-exciting-jump-diffusion/"},"frontmatter":{"title":"Self-Exciting Jump-Diffusion for Crypto: Why Vanilla Models Miss Momentum and Volatility Clustering"}}}}}