This repository contains a research-style Python workflow for simulating asset dynamics with fractional Brownian motion (fBM) and the Multifractal Model of Asset Returns (MMAR), then pricing European call and put options from the simulated paths.
It is a lightweight script project rather than a packaged library. The main workflow lives in main.py, and the reusable numerical routines live in mmar.py.
Running python main.py performs a full end-to-end experiment:
- Downloads historical price data for a hardcoded ticker with
yfinance - Estimates Hurst exponents across rolling segments of the historical series
- Simulates and visualizes fractional Brownian motion paths using the empirical Hurst estimate
- Computes multifractal scaling statistics such as
tau(q) - Estimates the multifractal spectrum
f(alpha) - Builds a log-normal multiplicative cascade and trading-time transformation
- Calibrates an fBM magnitude parameter to historical return volatility
- Simulates MMAR return paths and converts them to price paths
- Prices ATM and strike-grid European options from the simulated terminal prices
The script also produces several plots that help inspect intermediate steps of the workflow.
.
|-- main.py # End-to-end workflow, plotting, experiment parameters
|-- mmar.py # MMAR/fBM simulation, scaling, and option pricing helpers
|-- requirements.txt # Runtime dependencies
|-- AGENTS.md # Repo-specific guidance for coding agents
|-- plots/ # Generated plot outputs from script runs
Create and activate a virtual environment:
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txtThe checked-in dependency pins target modern Python versions. If installation fails, verify the interpreter in the active venv with python --version before debugging package issues.
Optional development tools used in the README checks:
python -m pip install ruff black mypy pytestThe runtime code depends on:
numpypandasmatplotlibyfinancenoldshurstfbmscikit-learntqdm
These are listed in requirements.txt.
Run the full workflow:
python main.pyOverride the main experiment inputs from the command line:
python main.py \
--ticker-symbol COIN \
--start-date 2020-01-01 \
--end-date 2025-03-24 \
--risk-free-rate 0.043 \
--days-to-expiration 45Important runtime notes:
main.pydownloads historical market data, so network access is required.- The workflow uses Monte Carlo simulation and repeated calibration loops, so runtime can be noticeable.
- The script exposes the main market and option inputs as CLI flags; other simulation settings remain in
main.py.
The main script accepts these core experiment inputs as optional CLI parameters:
--ticker-symbolwith defaultCOIN--start-datewith default2020-01-01--end-datewith default2025-03-24--risk-free-ratewith default0.043--days-to-expirationwith default45
If you run python main.py with no arguments, these defaults are used.
The script still sets other simulation parameters directly in code, such as:
- the q-moment grid used for multifractal estimation
- the number of calibration paths
- the number of simulated MMAR paths
- the strike spacing used for option pricing
If you want to change those deeper simulation settings, main.py is still the first place to edit.
main.py downloads OHLCV data with yfinance and works primarily with the Close series.
The first plot saved or displayed is the historical close-price chart.
The script divides the price history into equal-length segments and computes a Hurst exponent for each segment using nolds.hurst_rs.
This produces:
- a histogram of segment-level Hurst exponents
- an empirical mean Hurst estimate used later in both fBM and MMAR steps
The workflow uses the empirical Hurst estimate to:
- simulate a sample fBM series with
nolds.fbm - generate a separate price path with
generate_fbm_path()frommmar.py
These plots are meant as diagnostic comparisons before the multifractal modeling stage.
The script converts historical close prices to log returns and evaluates scaling behavior over a range of q-moments and time windows.
Key functions in mmar.py:
define_time_window()builds logarithmically spaced window sizescalculate_scaling_exponent()computes partition valuesFqand estimatestau(q)estimate_multifractal_spectrum()fits a quadratic approximation and derives spectrum parameters
This stage generates plots for:
- q-value distribution
tau(q)values
Using the estimated multifractal parameters, the script:
- derives log-normal cascade parameters
- builds a dyadic multiplicative cascade with
calculate_lognormal_cascade() - converts the cascade into a cumulative trading-time process with
calculate_trading_time()
The trading-time series is then truncated to the chosen option days-to-expiration horizon.
Before generating MMAR paths, the script calibrates an fBM magnitude parameter so that simulated volatility better matches the historical return standard deviation.
This happens in calculate_magnitude_parameter() and can take noticeable time because it repeatedly simulates multiple paths until the target mismatch falls below a tolerance.
calculate_mmar_returns():
- simulates fBM paths with the calibrated magnitude parameter
- samples them along the MMAR trading-time clock
- treats the sampled process as log returns
- converts returns into simulated price paths via
S0 * exp(return)
The script then visualizes:
- simulated MMAR returns
- simulated MMAR prices
- mean MMAR-generated returns
- prices implied by the mean return path
European options are priced from the simulated MMAR terminal prices using option_pricer().
main.py prints:
- the fair ATM call price
- the fair ATM put price
- a strike grid of call and put prices around the current spot level
When the script runs in a non-interactive or headless environment, plots are saved to plots/*.png.
Typical outputs include:
historical_close_prices.pnghurst_distribution.pngsimulated_fbm_with_empirical_hurst.pngsimulated_fbm_price_path.pngq_values_distribution.pngtau_q_values.pngtrading_time_theta_t.pngsimulated_mmar_returns.pngsimulated_mmar_prices.pngmean_of_mmar_generated_returns.pngprices_derived_from_the_mean_return.png
In an interactive environment, Matplotlib may display figures instead of writing them to disk.
main.py configures the backend automatically:
- If
MMAR_MPL_BACKENDis set, that backend is used. - If a display is available, it tries
TkAgg. - Otherwise it falls back to
Agg.
Examples:
MMAR_MPL_BACKEND=Agg python main.py
MMAR_MPL_BACKEND=TkAgg python main.pyUsing Agg is useful for servers, CI, or other headless environments.
If you do not want to run the full experiment, use a fast syntax check:
python -m py_compile main.py mmar.pyAdditional optional checks:
ruff check main.py mmar.py
black --check main.py mmar.py
mypy main.py mmar.pyThere is currently no committed tests/ directory in this repository.
If a test suite is added later, use pytest in the normal way.
- This codebase is research/prototyping oriented rather than productionized.
- The workflow is controlled by script-level constants rather than config files or command-line arguments.
- Plot files under
plots/are generated artifacts from running the script. - Because the script downloads live market data, results may vary over time.
- Monte Carlo outputs also vary unless you add explicit random seeding.