Version 1.0 · Last updated [DATE] · Applies to the live app

Quartermaster downloads daily market data for a crypto asset, audits that data before doing any arithmetic, and then computes a small set of risk and return figures. All calculations run on pandas and NumPy using the daily closing price unless stated otherwise.

One convention matters throughout: crypto markets trade every day of the year, so annualisation uses 365 periods per year rather than the 252 trading days conventionally used for equities.

Limitations

Read this section first. A tool that hides its weaknesses is not worth trusting, and these affect how much weight you should put on any figure below.

  • Data quality depends on Yahoo Finance. It is a free, aggregated source. It can carry gaps, delayed updates, and occasional errors, and its figures differ from exchange-level data. The Tick Inspector catches structural problems, not every possible inaccuracy.
  • Short histories make statistics unreliable. Skewness, excess kurtosis, and volatility all need a reasonable sample. Over very short ranges the moving averages and rolling volatility may not appear at all, because their windows have not filled.
  • Stablecoins and wrapped assets behave differently. A stablecoin's near-zero returns and extreme kurtosis reflect its peg, not typical market behaviour. Reading them like a freely floating asset will mislead you.
  • Annualisation is an approximation. The $\sqrt{365}$ scaling assumes daily returns are independent of one another. That is the conventional assumption, not a measured fact.
  • Gaps distort some figures. If a date is missing, the following return spans more than one day while being treated as a single day. See the integrity checks.
  • Nothing here is predictive. Every figure describes what already happened.
  • No costs are modelled. Transaction fees, slippage, spreads, and taxes appear nowhere in these calculations.

How the pipeline works

The app runs a fixed sequence, and it will stop rather than show you numbers it does not trust.

Select asset + dates  →  Download  →  Tick Inspector  →  Quant Analyzer  →  Dashboard
                                            │
                                       bad data?
                                            │
                                   Hard Stop: report the problem, render nothing

This is a Hard Stop methodology, and it works in three parts:

  1. The Gatekeeper. Every download passes through the Tick Inspector before any calculation runs.
  2. The Health Check. If the inspector finds corrupted data, the pipeline halts and shows you exactly which rows or dates are affected. Bad data never reaches a chart.
  3. The Render. If the data passes, the analytics run and the dashboard is drawn.

Missing calendar dates are treated differently from corrupted values. A small number of gaps produces a visible warning but the analysis still runs. The reasoning is in the next section.

Data source

  • Provider: Yahoo Finance, accessed through the yfinance library.
  • Frequency: daily bars (open, high, low, close, volume).
  • Price adjustment: auto_adjust=True is set explicitly, so the output does not depend on a library default that could change between versions. Crypto has no splits or dividends, so adjusted and raw prices are effectively identical here.
  • Date range: the date pickers are bounded by the earliest and latest dates Yahoo Finance holds for the selected asset. Yahoo treats a download's end date as exclusive, so the app adds one day internally — which makes the date you select inclusive, as you would expect.
  • Column handling: columns are lower-cased to open, high, low, close, volume, and the index is named date.
  • Caching: downloads are cached for one hour, and the available date range for each asset for 24 hours, so repeated interaction does not hammer Yahoo Finance. Failures are never cached — a temporary outage cannot be stored and served to the next visitor.

Integrity checks (the Tick Inspector)

Before any analysis, the downloaded data is audited. Four checks are fatal, and one is tolerated up to a limit.

Check What it looks for If it fails
Duplicate rows Repeated timestamps in the index Hard stop
Corrupted high/low Rows where low > high Hard stop
Open/close above high Rows where open > high or close > high Hard stop
Negative volume Rows where volume < 0 Hard stop
Missing dates Calendar days between the first and last row that are absent Warning up to 5, then hard stop

Why missing dates are treated differently

A full daily calendar is built between the first and last date in the downloaded data, and any dates absent from that calendar are counted. Up to five missing dates are tolerated: the analysis runs, and a warning above the dashboard lists exactly which dates were missing. Beyond that limit the analysis stops.

The reason for tolerating a few is that a gap does not corrupt a price, it only shifts a return. Returns are computed row to row, so the row after a gap carries a multi-day move while being treated as a single day. The effect on cumulative return is negligible — the compounding still arrives at the correct final price — but it can slightly distort rolling volatility and the returns distribution. That is why the warning names those two figures specifically.

Returns

Two kinds of daily return are computed from the closing price $P_t$:

Simple (percentage) return

$$r_t = \frac{P_t}{P_{t-1}} - 1$$

Log return

$$\ell_t = \ln\left(\frac{P_t}{P_{t-1}}\right)$$

Each is used deliberately:

  • Simple returns drive cumulative return and drawdown, because they compound directly into portfolio value.
  • Log returns drive volatility, skewness, excess kurtosis, and the histogram. They are the standard choice for statistical work, and at daily frequency the two measures are very close.
  • The first row of each series is empty, since there is no previous day, and it is excluded from the statistics.

Moving averages

Simple moving averages of the closing price:

$$\text{SMA}_n(t) = \frac{1}{n}\sum_{i=0}^{n-1} P_{t-i}$$

The 20-day and 50-day SMAs are overlaid on the candlestick chart. Each line only begins once its window has filled, so the 20-day line starts after 20 observations and the 50-day after 50. A short date range may show one line, or neither.

Rolling volatility

$$\sigma_{\text{ann}}(t) = \text{std}\left(\ell_{t-29},\ \ldots,\ \ell_t\right) \times \sqrt{365}$$

This is the standard deviation of daily log returns over a rolling 30-day window, scaled by $\sqrt{365}$ to express it as an annual figure. Volatility is a measure of risk: how widely returns have been spread recently.

Two things to keep in mind. The window length controls only how smooth the line is, while the $\sqrt{365}$ term is what changes the scale from per-day to per-year. And that scaling assumes returns are independent from one day to the next, which is a convention rather than a measured property of the market.

Cumulative return

$$R_{\text{cum}}(t) = \prod_{s=1}^{t}\left(1 + r_s\right) - 1$$

The total percentage gain or loss since the start of the selected period — that is, the first close in your chosen range, not the asset's all-time first price. A value of 1.5 means +150%.

Drawdown and maximum drawdown

A growth index is built by compounding the daily simple returns, starting at 1:

$$W_t = \prod_{s=1}^{t}\left(1 + r_s\right), \qquad W_0 = 1$$

The running peak and the drawdown follow from it:

$$M_t = \max_{s \le t} W_s, \qquad D_t = \frac{W_t - M_t}{M_t}$$

  • Drawdown $D_t$ is how far the asset sits below its highest point so far, as a percentage. It is always zero or negative.
  • Maximum drawdown is the lowest value $D_t$ reaches over the selected period: the worst peak-to-trough loss someone holding throughout would have lived through.
  • The peak starts at the first close in your range, so if the price falls immediately, the drawdown is measured from that starting point.

On the chart this is drawn as a filled "underwater" area, which makes the depth and the duration of each decline visible at once.

Distribution statistics

Both are computed on daily log returns using the pandas estimators .skew() and .kurt().

Skewness

A measure of asymmetry — which side holds the bigger surprises. A negative value means the left tail is longer, so the largest moves have tended to be drops. A positive value means the extremes lean toward gains. Near zero means balanced.

Excess kurtosis

A measure of tail weight relative to a normal distribution. Pandas .kurt() returns excess kurtosis — kurtosis minus 3 — so a normal distribution scores 0. Large positive values indicate "fat tails": extreme days occur far more often than a bell curve predicts. Crypto assets typically score high.

Average daily returns

The tape also shows the mean daily log return and the mean daily simple return. These are arithmetic daily averages. They are not annualised and should not be read as an expected future return — a small average can conceal very large swings, which is why it belongs next to volatility and drawdown rather than on its own.

Returns histogram

  • Daily log returns are grouped into fixed-width bins of 0.5%, so each bar always covers the same width no matter how extreme the underlying data is.
  • The x-axis is zoomed to the 1st–99th percentile of returns so the bulk of the distribution stays readable. The extreme days are still in the data and still in the chart — zoom out and you will see them.
  • Hovering a bar shows the return range it covers and how many days fell inside it.

Reading the charts

Risk vs. cumulative return

Cumulative return answers "how much did it make?" Annualised volatility answers "how bumpy was the ride?" Together they show that the same return can be earned through very different amounts of discomfort.

Drawdown

How far below its own peak the asset sat, day by day. The depth tells you the worst loss; the width tells you how long it took to recover. Both matter, and the second is routinely underestimated.

Price action with SMAs

Daily candlesticks with the 20- and 50-day simple moving averages overlaid. The moving averages are descriptive context, not signals.

Returns distribution

How often the price moved by each amount in a single day. Most days cluster near zero. The rare, far-out bars are the fat tails — and they are where most of the risk actually lives.

Changelog

Corrections and methodology changes are recorded here. If a published figure was wrong, the fix and its date appear below rather than being quietly amended.

Date Version Change
[DATE] 1.0 First public release.

Spotted an error in any of the above? Tell me — that is the most useful message you can send.

Launch the app