# Introduction to StochSimR

#### Ayush Kundu

#### 2026-05-28

## Overview

StochSimR is a modular simulation engine for stochastic processes. It provides a unified interface for simulating, analysing, and visualising paths from a wide range of processes used in finance, physics, queueing theory, and applied probability.

Every simulator returns a `stoch_path` object that plugs directly into the plotting and analysis functions.

    library(StochSimR)

## Classic Processes

### Brownian Motion

Standard Brownian motion (Wiener process) is the building block for most continuous-time models. The `sim_brownian()` function supports both exact simulation and Brownian bridge interpolation.

    bm <- sim_brownian(T_max = 1, n_steps = 1000, mu = 0, sigma = 1,
                       n_paths = 100)
    bm
    #> StochSimR path object
    #>   Process : Brownian Motion
    #>   Method  : exact
    #>   Paths   : 100
    #>   Steps   : 1001
    #>   Time    : [0, 1]
    #>   Params  : mu = 0, sigma = 1, x0 = 0, T_max = 1
    plot_paths(bm, max_paths = 50, show_mean = TRUE, show_bands = TRUE)

The red line is the sample mean across 100 paths; the shaded band shows the pointwise 2.5%–97.5% quantile envelope.

### Poisson Process

The counting process for random arrivals. Supports both homogeneous (constant rate) and inhomogeneous (time-varying rate) specifications.

    # Homogeneous
    pp <- sim_poisson(T_max = 10, lambda = 3, n_paths = 20)
    plot_paths(pp, show_bands = FALSE)

    # Inhomogeneous with sinusoidal rate
    pp_ih <- sim_poisson(
      T_max = 10,
      lambda = function(t) 3 + 2 * sin(2 * pi * t / 5),
      lambda_max = 5,
      n_paths = 20
    )
    plot_paths(pp_ih, show_bands = FALSE,
               title = "Inhomogeneous Poisson (sinusoidal rate)")

### Markov Chain

Discrete-time Markov chains are simulated exactly by sampling from the transition matrix row at each step.

    P <- matrix(c(0.7, 0.2, 0.1,
                  0.3, 0.4, 0.3,
                  0.2, 0.3, 0.5), nrow = 3, byrow = TRUE)

    mc <- sim_markov(P, n_steps = 300, x0 = 1, n_paths = 10)
    plot_paths(mc, show_bands = FALSE, show_mean = FALSE)

Verify convergence to the stationary distribution:

    long_mc <- sim_markov(P, n_steps = 50000, x0 = 1, n_paths = 1)
    freq <- table(factor(long_mc$values, levels = 1:3)) / length(long_mc$values)
    freq
    #>
    #>         1         2         3
    #> 0.4585308 0.2789344 0.2625347

## Advanced Processes

### Geometric Brownian Motion (GBM)

The standard model for stock prices. Two methods are available: `"exact"` uses the known log-normal solution; `"euler"` uses Euler–Maruyama discretisation.

    stock <- sim_gbm(T_max = 1, n_steps = 252, mu = 0.08, sigma = 0.25,
                     x0 = 100, n_paths = 200)
    plot_paths(stock, max_paths = 80)

    plot_distribution(stock)

### Ornstein–Uhlenbeck Process

A mean-reverting diffusion. With `method = "exact"`, the known Gaussian transition density is used – no discretisation error.

    ou <- sim_ou(T_max = 10, n_steps = 1000, theta = 2, mu = 5,
                 sigma = 0.5, x0 = 0, n_paths = 50)
    plot_paths(ou, show_mean = TRUE, show_bands = TRUE)

Note how all paths converge toward the long-run mean of 5.

### Jump-Diffusion (Merton Model)

Combines GBM with compound Poisson jumps in log-price.

    jd <- sim_jump_diffusion(
      T_max = 1, n_steps = 1000,
      mu = 0.05, sigma = 0.2,
      lambda = 3, mu_j = -0.02, sigma_j = 0.1,
      x0 = 100, n_paths = 50
    )
    plot_paths(jd, max_paths = 30)

### Hawkes (Self-Exciting) Process

A point process where each event temporarily increases the rate of future events. The branching ratio `alpha/beta` controls the degree of clustering.

    hk <- sim_hawkes(T_max = 50, mu = 0.5, alpha = 0.8, beta = 1.2,
                     n_paths = 15)
    plot_paths(hk, show_bands = FALSE, show_mean = TRUE)

### Levy Processes

Four families are available:

    # Gamma subordinator (non-decreasing)
    gam <- sim_levy(T_max = 5, n_steps = 500, type = "gamma",
                    shape = 2, rate = 1, n_paths = 20)
    plot_paths(gam, show_bands = FALSE)

    # Variance-Gamma
    vg <- sim_levy(T_max = 1, n_steps = 500, type = "variance_gamma",
                   theta = -0.1, sigma = 0.3, nu = 0.5, n_paths = 30)
    plot_paths(vg, max_paths = 20)

## Summary Statistics

The `path_summary()` function computes terminal value distribution, extremes, increment moments, skewness, and excess kurtosis:

    path_summary(stock)
    #>            statistic        value
    #> 1            n_paths 200.00000000
    #> 2            n_steps 252.00000000
    #> 3              T_max   1.00000000
    #> 4      terminal_mean 109.07224277
    #> 5        terminal_sd  28.53336114
    #> 6    terminal_median 106.09773145
    #> 7       terminal_q05  71.43366397
    #> 8       terminal_q95 168.93447104
    #> 9           max_mean 125.97579084
    #> 10          min_mean  85.23655245
    #> 11    increment_mean   0.03600096
    #> 12      increment_sd   1.66925307
    #> 13 terminal_skewness   0.75091748
    #> 14 terminal_kurtosis   0.53570260

The `plot_acf_paths()` function shows the autocorrelation of path increments, useful for verifying independence (Brownian) or detecting dependence (OU):

    plot_acf_paths(ou, path_idx = 1)

## Variance Reduction

StochSimR includes four variance reduction techniques. Each returns the estimate, standard error, and the variance reduction factor versus crude Monte Carlo.

### Control Variates

Use the terminal stock price (whose expectation is known analytically) as a control for pricing a European call:

    h_call <- function(vals) pmax(vals[nrow(vals), ] - 100, 0)
    g_term <- function(vals) vals[nrow(vals), ]
    E_g <- 100 * exp(0.05)

    cv <- vr_control_variate(sim_gbm, h = h_call, g = g_term, E_g = E_g,
      n_paths = 5000, T_max = 1, n_steps = 252,
      mu = 0.05, sigma = 0.2, x0 = 100)

    cat("Estimate:", round(cv$estimate, 4), "\n")
    #> Estimate: 10.8007
    cat("SE:      ", round(cv$se, 4), "\n")
    #> SE:       0.0822
    cat("Reduction:", round(cv$reduction_factor, 1), "x\n")
    #> Reduction: 6.7 x
    cat("Correlation(h, g):", round(cv$correlation, 3), "\n")
    #> Correlation(h, g): 0.922

### Importance Sampling

Tilt the drift upward to estimate a tail probability more efficiently:

    h_tail <- function(vals) as.numeric(vals[nrow(vals), ] > 150)
    is_result <- vr_importance(sim_gbm, h = h_tail, drift_shift = 0.4,
      n_paths = 10000, T_max = 1, n_steps = 252,
      mu = 0.05, sigma = 0.2, x0 = 100)

    cat("P(S_T > 150):", round(is_result$estimate, 6), "\n")
    #> P(S_T > 150): 0.030385
    cat("ESS:         ", round(is_result$ess), "/", is_result$n_samples, "\n")
    #> ESS:          149 / 10000
    cat("Reduction:   ", round(is_result$reduction_factor, 1), "x\n")
    #> Reduction:    13.9 x

## Monte Carlo Estimation

The `mc_estimate()` function provides a general-purpose estimator with batch-means standard errors and a convergence trace:

    h_pos <- function(vals) pmax(vals[nrow(vals), ], 0)
    mc <- mc_estimate(sim_brownian, h = h_pos, n_paths = 10000,
                      batch_size = 500, T_max = 1, n_steps = 200)

    cat("E[max(B_1, 0)] =", round(mc$estimate, 4),
        "+/-", round(mc$se, 4), "\n")
    #> E[max(B_1, 0)] = 0.3985 +/- 0.0058
    cat("Exact answer:    ", round(1 / sqrt(2 * pi), 4), "\n")
    #> Exact answer:     0.3989

## Method Comparison

Compare simulation methods on the same process to measure bias and speed:

    comp <- compare_methods(sim_ou, methods = c("exact", "euler"),
      n_paths = 500, n_reps = 20,
      T_max = 5, n_steps = 500, theta = 2, mu = 1, sigma = 0.5, x0 = 0)
    print(comp)
    #>   method mean_terminal sd_terminal bias rmse time_seconds
    #> 1  exact     1.0003178  0.01585589   NA   NA      0.26365
    #> 2  euler     0.9982826  0.01037739   NA   NA      0.22705

The exact method uses the known Gaussian transition density and has zero discretisation bias; the Euler method is faster per step but introduces \(O(\Delta t)\) weak error.

## Session Info

    sessionInfo()
    #> R version 4.5.3 (2026-03-11)
    #> Platform: aarch64-apple-darwin20
    #> Running under: macOS Tahoe 26.5
    #>
    #> Matrix products: default
    #> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib
    #> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
    #>
    #> locale:
    #> [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
    #>
    #> time zone: Asia/Kolkata
    #> tzcode source: internal
    #>
    #> attached base packages:
    #> [1] stats     graphics  grDevices utils     datasets  methods   base
    #>
    #> other attached packages:
    #> [1] StochSimR_1.1.0
    #>
    #> loaded via a namespace (and not attached):
    #>  [1] gtable_0.3.6        future.apply_1.20.2 jsonlite_2.0.0
    #>  [4] dplyr_1.2.0         compiler_4.5.3      tidyselect_1.2.1
    #>  [7] parallel_4.5.3      jquerylib_0.1.4     globals_0.19.1
    #> [10] scales_1.4.0        yaml_2.3.12         fastmap_1.2.0
    #> [13] ggplot2_4.0.3       R6_2.6.1            labeling_0.4.3
    #> [16] generics_0.1.4      knitr_1.51          future_1.70.0
    #> [19] tibble_3.3.1        bslib_0.10.0        pillar_1.11.1
    #> [22] RColorBrewer_1.1-3  rlang_1.2.0         cachem_1.1.0
    #> [25] xfun_0.57           sass_0.4.10         S7_0.2.1
    #> [28] otel_0.2.0          cli_3.6.6           withr_3.0.2
    #> [31] magrittr_2.0.4      digest_0.6.39       grid_4.5.3
    #> [34] rstudioapi_0.18.0   lifecycle_1.0.5     vctrs_0.7.1
    #> [37] evaluate_1.0.5      glue_1.8.0          farver_2.1.2
    #> [40] listenv_0.10.1      codetools_0.2-20    parallelly_1.46.1
    #> [43] rmarkdown_2.31      tools_4.5.3         pkgconfig_2.0.3
    #> [46] htmltools_0.5.9