make_ret <- function(n = 200, seed = 99) { set.seed(seed) # Simple two-regime synthetic return series s <- sample(c(1, 2), n, replace = TRUE, prob = c(0.7, 0.3)) mu <- c(0, -0.005)[s] sg <- c(0.01, 0.04)[s] rnorm(n, mean = mu, sd = sg) } test_that("fit_hmm_em (Gaussian) returns a valid fit object", { ret <- make_ret() fit <- fit_hmm_em(ret, K = 2L, model = "gaussian", n_starts = 2L, max_iter = 30L, verbose = FALSE) expect_named(fit, c("model","A","delta","params","gamma","xi","logLik","n","K")) expect_true(is.finite(fit$logLik)) expect_equal(dim(fit$A), c(2L, 2L)) expect_equal(rowSums(fit$A), c(1, 1), tolerance = 1e-8) }) test_that("fit_hmm_em (Student-t) returns a valid fit object", { ret <- make_ret() fit <- fit_hmm_em(ret, K = 2L, model = "student", n_starts = 2L, max_iter = 30L, verbose = FALSE, nu_grid = c(4, 8, 15)) expect_true(is.finite(fit$logLik)) expect_true(!is.null(fit$params$nu)) expect_true(all(fit$params$nu >= 2.1)) }) test_that("reorder_fit_by_scale orders states by ascending volatility (Gaussian)", { ret <- make_ret() fit <- fit_hmm_em(ret, K = 2L, model = "gaussian", n_starts = 2L, max_iter = 30L, verbose = FALSE) fit <- KRONX:::reorder_fit_by_scale(fit) expect_lte(fit$params$sigma[1], fit$params$sigma[2]) }) test_that("reorder_fit_by_scale orders states by conditional variance (Student-t)", { ret <- make_ret() fit <- fit_hmm_em(ret, K = 2L, model = "student", n_starts = 2L, max_iter = 30L, verbose = FALSE, nu_grid = c(4, 8, 15)) fit <- KRONX:::reorder_fit_by_scale(fit) cv <- (fit$params$nu / (fit$params$nu - 2)) * fit$params$sigma^2 expect_lte(cv[1], cv[2]) })