Case study
Jev Odds: what are the odds a price gets there?
Pick a ticker, a percent move and a date, and see the chance of closing past that move and of touching it first. Two models and a historical count sit side by side, with every formula on the page.

Why I built it
I keep coming back to one question when I look at markets: what are the odds this gets there? Not whether it is a good idea, and not what to do about it. Just the chance that a price moves a certain percent by a certain date.
Most answers I find are a paragraph. A paragraph can sound sure of itself without ever saying how sure. I wanted a tool that has to commit to a number, shows exactly how it got that number, and can be checked later. That is also the habit I call Jev decision-making, which is why the app has the name. I explain more about that near the end.
Jev Odds is an educational tool. It is not financial advice, it does not place trades, and it does not touch money. Where I use anything like this near markets, it is on a paper trading account.
What it does
You pick a ticker, a percent move, a direction (up, down or either) and a target date. The app counts the trading days to that date using the NYSE calendar, including the 2026 holidays. Then it returns two probabilities. Close beyond is the chance the price finishes at or past the target on that date. Touch is the chance it reaches the target at any point before then.
For example, on October 8, 2026, I asked about SPY up 5% by January 15, 2027. That was 68 trading days out. The app said 19.0% for close beyond and 38.6% for touch, and the Monte Carlo check came out at 19.6% and 38.6%. Those numbers are a snapshot of that day's data, not a forecast I am making.
Beside the two numbers, the page shows a one sigma range, the spot price, and how often the same move happened in past windows of the same length. It draws the terminal price distribution with the target shaded, and a sample of Monte Carlo paths. A "How this was computed" panel lists every formula and input. The query lives in the page address, so a result can be shared as a link. There is a one-page PDF of the readout, and every result says whether it used live or cached data.
How it's built
The API is ASP.NET Core 8 and the front end is Angular 22 with standalone components and signals. EF Core stores cached prices and query history in SQLite by default, or in MySQL through Pomelo. Chart.js draws the distribution and the paths, QuestPDF writes the PDF, and three.js draws a field of paths you can turn after pressing Rotate. three.js loads only when a result is on screen.
Three estimates, side by side
The math uses daily adjusted closes. Volatility is realized volatility over 20, 60 or 252 sessions, or an equal blend of the three, and you can override it. Drift is zero by default, or the historical mean. From there the app makes three separate estimates.
The analytic numbers come from a lognormal model: a closed-form formula for close beyond and a first-passage formula for touch. The Monte Carlo check runs 20,000 paths by default with a fixed seed, so the same question gives the same answer. It uses a Brownian bridge, so a path that crosses the target between two daily samples still counts as a touch. The third number is not a model at all. It is the fraction of past windows of the same length in which the move actually happened.
I wanted all three on the page because they fail in different ways. More on that below.
Touch is not close
The difference between touch and close is the part people ask me about most, so the methodology page draws it.
A path can reach the target in week three and finish below it. That path counts toward touch and not toward close, which is why touch is always at least as large as close. There is a test for that rule.
Free data, with a fallback
Prices come from free endpoints with no key. The API tries Yahoo Finance first, then Stooq, and saves a fresh series to the database. If both fail, it serves the last saved series. The repo also ships sample history for 15 tickers, about 18,500 daily bars in total on my install, so the app works offline. Nothing in the repo calls a paid API.
The xUnit suite was at 41 passing tests at the last run. It checks the normal CDF, the volatility windows and the blend, the historical counts, the trading calendar, that the analytic and Monte Carlo numbers agree within 0.02, that touch is never below close, the 15 sample files, and the database migrations.
A design review round
Each build went through a design reviewer at 390, 768 and 1440 pixels wide, in light and dark, with reduced and normal motion, against a written rubric. The rounds changed how the 3D view behaves more than how it looks.
- The 3D path field no longer grabs the page. You press Rotate, then drag to turn it. A swipe without Rotate scrolls the page, which matters on a phone.
- The field also used to turn slowly on its own after the page loaded. Now it holds still until you press Rotate, and turning Rotate off puts it back where it started. It only draws a new frame when something changes.
- When WebGL is off, or the browser drops the 3D context, the frame shows a still picture of the same paths from the same camera, and the caption changes to say so. When the context comes back, so does the live view.
- Buttons are now rounded pills, with one shape used across the app.
- The reviewer measured the grey placeholder text in the volatility override field at 3.93 to 1 in dark mode, under the 4.5 to 1 the rubric asks for at that size. The browser default was doing the work, so the fix is a placeholder color set for each scheme. It now measures 5.3 to 1.
What I learned
My first live test found a button that did nothing. The move field had a minimum of 0.1 and a step of 0.5, so the browser only accepted values like 4.6 and 5.1. The default of 5 failed the browser's own validation, and "Calculate odds" quietly did nothing. The preset buttons still worked, which is why it was easy to miss. The fix was to let the field take any value. It was one attribute, and no unit test would have caught it, because the math was fine. I only found it by clicking the button on the real install.
The second thing I learned is why the historical count belongs on the page. In that same SPY readout, 1,186 past windows of 68 sessions were on hand, and in those windows the move closed beyond the target 46.0% of the time and touched it 64.2% of the time. The models said 19.0% and 38.6%. That is a big gap. The models assume a shape for returns, and the history only knows the years it covers. I do not treat either one as the truth. Seeing both is the point, and a single number would have hidden that.
Why I named it Jev
Jev Odds does not call a model. It does not call TypeSafe or anyone else. It is named after a habit: commit to a probability, show what it is based on, and keep it so it can be checked later.
This section is my opinion and my prediction, so I want to label it that way.
When I say Jev decision-making, I mean a model that is asked a typed question and has to commit. It gives a probability and a call, and it is clear about what the call was based on. That answer is logged with the time and the inputs, and later it is scored against what actually happened. Jev is the name TypeSafe gave its first System One model, and it does the first half of that directly. It takes a typed question and returns typed values and probabilities, such as a choice from a list, a score on a rubric, or the probability that a statement is true, instead of a paragraph. The logging and the scoring are the half I build around it.
In the early days of ChatGPT, I felt like I could get a model to give me a number and stand behind it. That was an early capability, and in my experience it faded in the versions that followed: more hedging, more "it depends," fewer committed numbers. I do not know the full reasons for that, and I am not claiming to. It is how it felt from my side of the screen.
My view is that frontier and open model providers will build Jev and TypeSafe style typed probabilistic decision-making into the core of their models. Software needs answers it can branch on, and a typed probability I can score later is more useful to me than a paragraph I have to interpret. TypeSafe and Jev are the clearest version of that idea I have seen so far, and I think it will end up as a standard feature rather than a niche one. That is a prediction, not something I can prove today.
None of this is financial advice. Where I use Jev near markets, it is on a paper trading account with no real money, and I do not publish trading results.
What's next
- Logging each answer with its inputs and scoring it once the date passes, so the app keeps its own record, the way Jev Setup Score does.
- Showing past questions on the page. They are already stored next to the price cache, so the record is there to read.
Tech stack
- ASP.NET Core 8 Web API
- Angular standalone components
- EF Core with SQLite or MySQL
- Yahoo Finance daily data with Stooq as a fallback, cached on the server
- Chart.js for the distribution and paths
- QuestPDF for the readout PDF
- three.js for the path field
Repository
The GitHub repository for this app is github.com/erichers/jev-odds.
Common questions
Is Jev Odds a trading signal?
No. It is an educational tool that shows a probability under a simple model, with the formulas on the page. It is not financial advice.
What is the difference between close and touch?
Close beyond is the chance the price finishes past the target on the date. Touch is the chance it reaches the target at any point before then. Touch is always at least as large as close.
Does it need a paid data feed?
No. It uses free endpoints with no key, Yahoo Finance first and Stooq second, and it ships sample history for 15 tickers so it runs offline.