From 17a2a503db34d513c655a7b658dc24e9e1bd0e5c Mon Sep 17 00:00:00 2001 From: iampratik13 Date: Sun, 12 Oct 2025 15:32:00 +0530 Subject: [PATCH 01/14] create a Time Series Analysis algorithm --- quantitative_finance/time_series_analyzer.r | 494 ++++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 quantitative_finance/time_series_analyzer.r diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r new file mode 100644 index 00000000..b29b1839 --- /dev/null +++ b/quantitative_finance/time_series_analyzer.r @@ -0,0 +1,494 @@ +# Time Series Analysis and ARIMA Modeling +# Implements comprehensive time series analysis with ARIMA models +# Features: Stationarity testing, model selection, forecasting, and diagnostics + +library(R6) + +#' TimeSeriesAnalyzer Class +#' @description R6 class for time series analysis and ARIMA modeling +#' @details Provides functionality for: +#' - Stationarity testing (ADF test) +#' - ACF/PACF analysis +#' - ARIMA model fitting +#' - Model selection using AIC/BIC +#' - Forecasting with confidence intervals +TimeSeriesAnalyzer <- R6Class( + "TimeSeriesAnalyzer", + + public = list( + #' @description Initialize analyzer with time series data + #' @param data Time series data (numeric vector) + #' @param frequency Frequency of the time series (default: 1) + initialize = function(data = NULL, frequency = 1) { + if (!is.null(data)) { + private$validate_input(data) + self$data <- data + self$frequency <- frequency + private$n <- length(data) + } + invisible(self) + }, + + #' @description Test for stationarity using Augmented Dickey-Fuller test + #' @param max_lags Maximum number of lags to consider + test_stationarity = function(max_lags = NULL) { + if (is.null(self$data)) { + stop("No data available. Please initialize with data first.") + } + + if (is.null(max_lags)) { + max_lags <- floor(sqrt(private$n)) + } + + # Perform ADF test + result <- private$adf_test(max_lags) + + # Store results + private$stationarity_results <- result + return(result) + }, + + #' @description Calculate ACF and PACF + #' @param max_lag Maximum lag to consider + calculate_acf_pacf = function(max_lag = NULL) { + if (is.null(self$data)) { + stop("No data available. Please initialize with data first.") + } + + if (is.null(max_lag)) { + max_lag <- min(private$n - 1, floor(10 * log10(private$n))) + } + + # Calculate ACF + acf_result <- private$calculate_acf(max_lag) + + # Calculate PACF + pacf_result <- private$calculate_pacf(max_lag) + + return(list( + acf = acf_result, + pacf = pacf_result, + lags = 1:max_lag + )) + }, + + #' @description Fit ARIMA model to the data + #' @param p AR order + #' @param d Differencing order + #' @param q MA order + fit_arima = function(p = 1, d = 0, q = 1) { + if (is.null(self$data)) { + stop("No data available. Please initialize with data first.") + } + + # Validate parameters + if (any(c(p, d, q) < 0)) { + stop("ARIMA orders must be non-negative") + } + + # Fit ARIMA model + model <- private$fit_arima_model(p, d, q) + private$current_model <- model + + return(model) + }, + + #' @description Automatic model selection using AIC + #' @param max_p Maximum AR order to consider + #' @param max_d Maximum differencing order + #' @param max_q Maximum MA order + select_best_model = function(max_p = 3, max_d = 2, max_q = 3) { + if (is.null(self$data)) { + stop("No data available. Please initialize with data first.") + } + + best_aic <- Inf + best_order <- c(0, 0, 0) + + # Grid search over possible orders + for (p in 0:max_p) { + for (d in 0:max_d) { + for (q in 0:max_q) { + tryCatch({ + model <- self$fit_arima(p, d, q) + if (model$aic < best_aic) { + best_aic <- model$aic + best_order <- c(p, d, q) + private$best_model <- model + } + }, error = function(e) { + # Skip failed models + }) + } + } + } + + return(list( + order = best_order, + aic = best_aic + )) + }, + + #' @description Generate forecasts with confidence intervals + #' @param h Forecast horizon + #' @param level Confidence level (0-1) + forecast = function(h = 10, level = 0.95) { + if (is.null(private$current_model)) { + stop("No model fitted. Please fit a model first.") + } + + # Generate forecasts + forecasts <- private$generate_forecasts(h, level) + return(forecasts) + }, + + #' @description Perform model diagnostics + diagnose_model = function() { + if (is.null(private$current_model)) { + stop("No model fitted. Please fit a model first.") + } + + residuals <- private$current_model$residuals + + # Calculate diagnostic statistics + diagnostics <- list( + residual_mean = mean(residuals), + residual_sd = sd(residuals), + ljung_box = private$ljung_box_test(residuals), + normality = private$normality_test(residuals), + arch_effect = private$arch_test(residuals) + ) + + return(diagnostics) + }, + + # Public fields + data = NULL, + frequency = NULL + ), + + private = list( + n = NULL, + current_model = NULL, + best_model = NULL, + stationarity_results = NULL, + + validate_input = function(data) { + if (!is.numeric(data)) { + stop("Input data must be numeric") + } + if (any(is.na(data))) { + stop("Input data contains missing values") + } + if (length(data) < 3) { + stop("Input data must have at least 3 observations") + } + }, + + adf_test = function(max_lags) { + y <- self$data + n <- length(y) + + # Calculate first differences + dy <- diff(y) + y_1 <- y[-n] + + # Construct regression matrix + X <- matrix(1, n-1, 1) + X <- cbind(X, y_1) + + # Add lagged differences + for (lag in 1:max_lags) { + X <- cbind(X, stats::lag(dy, lag)[-c(1:lag)]) + } + + # Remove NA rows + complete_cases <- stats::complete.cases(X) + X <- X[complete_cases, ] + dy <- dy[complete_cases] + + # Fit regression + fit <- stats::lm(dy ~ X - 1) + + # Calculate test statistic + coef <- stats::coef(fit)[2] + se <- sqrt(diag(stats::vcov(fit)))[2] + t_stat <- (coef - 1) / se + + # Critical values (approximate) + crit_values <- c( + "1%" = -3.43, + "5%" = -2.86, + "10%" = -2.57 + ) + + return(list( + statistic = t_stat, + critical_values = crit_values, + is_stationary = t_stat < -2.86 # 5% level + )) + }, + + calculate_acf = function(max_lag) { + y <- scale(self$data) # Standardize data + n <- length(y) + acf <- numeric(max_lag) + + for (k in 1:max_lag) { + acf[k] <- stats::cor(y[1:(n-k)], y[(k+1):n]) + } + + return(acf) + }, + + calculate_pacf = function(max_lag) { + acf <- private$calculate_acf(max_lag) + pacf <- numeric(max_lag) + + # Durbin-Levinson algorithm + for (k in 1:max_lag) { + if (k == 1) { + pacf[k] <- acf[1] + } else { + r <- matrix(0, k, k) + for (i in 1:k) { + for (j in 1:k) { + r[i,j] <- acf[abs(i-j)+1] + } + } + b <- acf[1:k] + pacf[k] <- solve(r, b)[k] + } + } + + return(pacf) + }, + + fit_arima_model = function(p, d, q) { + # Difference data + y <- self$data + for (i in 1:d) { + y <- diff(y) + } + + # Construct and solve Yule-Walker equations for AR part + if (p > 0) { + r <- private$calculate_acf(p) + phi <- solve(stats::toeplitz(r[1:p]), r[2:(p+1)]) + } else { + phi <- numeric(0) + } + + # Estimate MA parameters using innovation algorithm + if (q > 0) { + theta <- numeric(q) + e <- y + for (i in 1:10) { # Iterate to improve estimates + r <- private$calculate_acf(q) + psi <- numeric(q) + for (j in 1:q) { + psi[j] <- sum(theta[1:(j-1)] * rev(psi[1:(j-1)])) + theta[j] + } + theta <- solve(stats::toeplitz(c(1, psi[1:(q-1)])), r[1:q]) + } + } else { + theta <- numeric(0) + } + + # Calculate residuals and AIC + resid <- private$calculate_residuals(y, phi, theta) + n_params <- p + q + aic <- length(resid) * log(var(resid)) + 2 * n_params + + return(list( + coefficients = list(ar = phi, ma = theta), + residuals = resid, + aic = aic, + order = c(p, d, q) + )) + }, + + calculate_residuals = function(y, phi, theta) { + n <- length(y) + p <- length(phi) + q <- length(theta) + resid <- numeric(n) + + for (t in (max(p,q)+1):n) { + pred <- 0 + if (p > 0) { + pred <- pred + sum(phi * y[(t-1):(t-p)]) + } + if (q > 0) { + pred <- pred + sum(theta * resid[(t-1):(t-q)]) + } + resid[t] <- y[t] - pred + } + + return(resid[(max(p,q)+1):n]) + }, + + generate_forecasts = function(h, level) { + model <- private$current_model + y <- self$data + n <- length(y) + + # Get model orders + p <- length(model$coefficients$ar) + d <- model$order[2] + q <- length(model$coefficients$ma) + + # Generate point forecasts + forecasts <- numeric(h) + for (i in 1:h) { + pred <- 0 + if (p > 0) { + ar_terms <- if (i <= p) { + y[(n-p+i):n] + } else { + forecasts[(i-p):(i-1)] + } + pred <- pred + sum(model$coefficients$ar * ar_terms) + } + forecasts[i] <- pred + } + + # Calculate prediction intervals + sigma <- sd(model$residuals) + z <- stats::qnorm((1 + level) / 2) + se <- sigma * sqrt(cumsum(rep(1, h))) + lower <- forecasts - z * se + upper <- forecasts + z * se + + return(list( + mean = forecasts, + lower = lower, + upper = upper, + level = level + )) + }, + + ljung_box_test = function(residuals) { + max_lag <- min(20, length(residuals) - 1) + acf <- private$calculate_acf(max_lag) + n <- length(residuals) + + Q <- n * (n + 2) * sum((acf^2) / (n - 1:max_lag)) + p_value <- 1 - stats::pchisq(Q, max_lag) + + return(list( + statistic = Q, + p_value = p_value + )) + }, + + normality_test = function(residuals) { + # Jarque-Bera test + n <- length(residuals) + s <- sum((residuals - mean(residuals))^3) / (n * sd(residuals)^3) # skewness + k <- sum((residuals - mean(residuals))^4) / (n * sd(residuals)^4) - 3 # excess kurtosis + JB <- n * (s^2/6 + k^2/24) + p_value <- 1 - stats::pchisq(JB, df = 2) + + return(list( + statistic = JB, + p_value = p_value + )) + }, + + arch_test = function(residuals) { + # ARCH LM test + sq_resid <- residuals^2 + n <- length(sq_resid) + lags <- 5 + + # Regression of squared residuals on lagged squared residuals + X <- matrix(1, n-lags, 1) + for (i in 1:lags) { + X <- cbind(X, stats::lag(sq_resid, i)[-c(1:lags)]) + } + + fit <- stats::lm(sq_resid[-c(1:lags)] ~ X - 1) + R2 <- summary(fit)$r.squared + LM <- n * R2 + p_value <- 1 - stats::pchisq(LM, lags) + + return(list( + statistic = LM, + p_value = p_value + )) + } + ) +) + +# Demonstration +demonstrate_time_series_analysis <- function() { + cat("=== Time Series Analysis Demo ===\n\n") + + # Generate sample time series + set.seed(42) + n <- 500 + + # AR(1) process with trend and seasonality + t <- 1:n + trend <- 0.01 * t + seasonal <- 2 * sin(2 * pi * t / 12) + ar_process <- stats::arima.sim(list(ar = 0.7), n = n) + y <- trend + seasonal + ar_process + + # Initialize analyzer + ts_analyzer <- TimeSeriesAnalyzer$new(y, frequency = 12) + + # Test stationarity + cat("Testing for stationarity...\n") + stat_test <- ts_analyzer$test_stationarity() + cat(sprintf("ADF test statistic: %.3f\n", stat_test$statistic)) + cat("Critical values:\n") + print(stat_test$critical_values) + cat(sprintf("Series is %sstationary at 5%% level\n\n", + ifelse(stat_test$is_stationary, "", "non-"))) + + # Analyze ACF/PACF + cat("Calculating ACF and PACF...\n") + corr <- ts_analyzer$calculate_acf_pacf(20) + cat("First 5 lags:\n") + cat("ACF: ") + cat(sprintf("%.3f ", corr$acf[1:5])) + cat("\nPACF: ") + cat(sprintf("%.3f ", corr$pacf[1:5])) + cat("\n\n") + + # Select best model + cat("Selecting best ARIMA model...\n") + best <- ts_analyzer$select_best_model(max_p = 2, max_d = 1, max_q = 2) + cat(sprintf("Best model: ARIMA(%d,%d,%d)\n", + best$order[1], best$order[2], best$order[3])) + cat(sprintf("AIC: %.2f\n\n", best$aic)) + + # Generate forecasts + cat("Generating forecasts...\n") + h <- 12 # Forecast horizon + forecasts <- ts_analyzer$forecast(h = h, level = 0.95) + + cat("Point forecasts for next 12 periods:\n") + cat(sprintf("%.2f ", forecasts$mean)) + cat("\n\n") + + # Model diagnostics + cat("Performing model diagnostics...\n") + diagnostics <- ts_analyzer$diagnose_model() + + cat("Residual diagnostics:\n") + cat(sprintf("Mean: %.3f\n", diagnostics$residual_mean)) + cat(sprintf("Standard deviation: %.3f\n", diagnostics$residual_sd)) + cat(sprintf("Ljung-Box test p-value: %.3f\n", diagnostics$ljung_box$p_value)) + cat(sprintf("Normality test p-value: %.3f\n", diagnostics$normality$p_value)) + cat(sprintf("ARCH test p-value: %.3f\n", diagnostics$arch_effect$p_value)) + + cat("\n=== Demo Complete ===\n") +} + +# Run demonstration if not in interactive mode +if (!interactive()) { + demonstrate_time_series_analysis() +} \ No newline at end of file From 4b4844e3c8fdf53711b3531f8d095ee13f1a6741 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:27:28 +0530 Subject: [PATCH 02/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index b29b1839..7f769478 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -253,7 +253,12 @@ TimeSeriesAnalyzer <- R6Class( r <- matrix(0, k, k) for (i in 1:k) { for (j in 1:k) { - r[i,j] <- acf[abs(i-j)+1] + idx <- abs(i-j)+1 + if (idx <= length(acf)) { + r[i,j] <- acf[idx] + } else { + r[i,j] <- 0 + } } } b <- acf[1:k] From 0c6a999deeabf607c618d7987bd365d8ddada8fe Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:27:40 +0530 Subject: [PATCH 03/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 7f769478..4e8bc62f 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -278,7 +278,7 @@ TimeSeriesAnalyzer <- R6Class( # Construct and solve Yule-Walker equations for AR part if (p > 0) { - r <- private$calculate_acf(p) + r <- private$calculate_acf(p + 1) phi <- solve(stats::toeplitz(r[1:p]), r[2:(p+1)]) } else { phi <- numeric(0) From 678400a4ebef1711dc6a069fb940cc44280e3843 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:27:47 +0530 Subject: [PATCH 04/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 4e8bc62f..19a2bf77 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -294,7 +294,11 @@ TimeSeriesAnalyzer <- R6Class( for (j in 1:q) { psi[j] <- sum(theta[1:(j-1)] * rev(psi[1:(j-1)])) + theta[j] } - theta <- solve(stats::toeplitz(c(1, psi[1:(q-1)])), r[1:q]) + if (q == 1) { + theta <- solve(stats::toeplitz(1), r[1]) + } else { + theta <- solve(stats::toeplitz(c(1, psi[1:(q-1)])), r[1:q]) + } } } else { theta <- numeric(0) From 809b9027d489cd0eee9defd2f99b69cff4de5a00 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:27:57 +0530 Subject: [PATCH 05/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 19a2bf77..1ff8e620 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -199,7 +199,8 @@ TimeSeriesAnalyzer <- R6Class( # Add lagged differences for (lag in 1:max_lags) { - X <- cbind(X, stats::lag(dy, lag)[-c(1:lag)]) + # Use backward lag: pad with NA, then take first (length-dy-lag) elements + X <- cbind(X, c(rep(NA, lag), dy[1:(length(dy)-lag)])[-c(1:lag)]) } # Remove NA rows From f927a9fc9cc66f763f820e56933b81038b8d8e36 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:28:10 +0530 Subject: [PATCH 06/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 1ff8e620..8a9e3449 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -412,15 +412,17 @@ TimeSeriesAnalyzer <- R6Class( n <- length(sq_resid) lags <- 5 - # Regression of squared residuals on lagged squared residuals - X <- matrix(1, n-lags, 1) - for (i in 1:lags) { - X <- cbind(X, stats::lag(sq_resid, i)[-c(1:lags)]) + # Use embed to construct lagged matrix (current and past lags) + # embed(sq_resid, lags + 1) returns a matrix with columns: t, t-1, ..., t-lags + if (n <= lags) { + stop("Not enough observations for ARCH test lags.") } - - fit <- stats::lm(sq_resid[-c(1:lags)] ~ X - 1) + lagged_mat <- embed(sq_resid, lags + 1) + y <- lagged_mat[, 1] # current squared residuals + X <- lagged_mat[, -1] # lagged squared residuals + fit <- stats::lm(y ~ X) R2 <- summary(fit)$r.squared - LM <- n * R2 + LM <- nrow(lagged_mat) * R2 p_value <- 1 - stats::pchisq(LM, lags) return(list( From 4f57343ca2f74ef9a7016953eed5457ae53c3c17 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:28:20 +0530 Subject: [PATCH 07/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 8a9e3449..3433b4cf 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -380,10 +380,10 @@ TimeSeriesAnalyzer <- R6Class( ljung_box_test = function(residuals) { max_lag <- min(20, length(residuals) - 1) - acf <- private$calculate_acf(max_lag) + acf_vals <- acf(residuals, plot = FALSE, lag.max = max_lag)$acf[-1] n <- length(residuals) - Q <- n * (n + 2) * sum((acf^2) / (n - 1:max_lag)) + Q <- n * (n + 2) * sum((acf_vals^2) / (n - 1:max_lag)) p_value <- 1 - stats::pchisq(Q, max_lag) return(list( From 85af7fc7f9a4a43cf395f2638615b3f1a7efb821 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:52:11 +0530 Subject: [PATCH 08/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 3433b4cf..b206c775 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -198,9 +198,12 @@ TimeSeriesAnalyzer <- R6Class( X <- cbind(X, y_1) # Add lagged differences - for (lag in 1:max_lags) { - # Use backward lag: pad with NA, then take first (length-dy-lag) elements - X <- cbind(X, c(rep(NA, lag), dy[1:(length(dy)-lag)])[-c(1:lag)]) + if (max_lags > 0) { + # Add lagged differences using embed() for clarity + lagged_dy <- embed(dy, max_lags + 1)[, -1, drop = FALSE] + X <- X[(max_lags+1):nrow(X), ] # Align X with lagged_dy rows + X <- cbind(X, lagged_dy) + dy <- dy[(max_lags+1):length(dy)] } # Remove NA rows From 75448f341dc17884fbaca262de60d45709da337b Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:52:25 +0530 Subject: [PATCH 09/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index b206c775..72885bfa 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -217,7 +217,7 @@ TimeSeriesAnalyzer <- R6Class( # Calculate test statistic coef <- stats::coef(fit)[2] se <- sqrt(diag(stats::vcov(fit)))[2] - t_stat <- (coef - 1) / se + t_stat <- coef / se # Critical values (approximate) crit_values <- c( From ad72bf6014b3a2d870f7e8a2bd5723a5ec004d78 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:52:33 +0530 Subject: [PATCH 10/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 72885bfa..0545788a 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -383,7 +383,7 @@ TimeSeriesAnalyzer <- R6Class( ljung_box_test = function(residuals) { max_lag <- min(20, length(residuals) - 1) - acf_vals <- acf(residuals, plot = FALSE, lag.max = max_lag)$acf[-1] + acf_vals <- stats::acf(residuals, plot = FALSE, lag.max = max_lag)$acf[-1] n <- length(residuals) Q <- n * (n + 2) * sum((acf_vals^2) / (n - 1:max_lag)) From 4b97f5b598c7e9c6591a26abdaea34e5ccb4f2ed Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:52:46 +0530 Subject: [PATCH 11/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 0545788a..883824ae 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -420,7 +420,7 @@ TimeSeriesAnalyzer <- R6Class( if (n <= lags) { stop("Not enough observations for ARCH test lags.") } - lagged_mat <- embed(sq_resid, lags + 1) + lagged_mat <- stats::embed(sq_resid, lags + 1) y <- lagged_mat[, 1] # current squared residuals X <- lagged_mat[, -1] # lagged squared residuals fit <- stats::lm(y ~ X) From afb68ec5d9e3cbb60a125961dad5ee66a5fc2acf Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:52:58 +0530 Subject: [PATCH 12/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 883824ae..cc4a3861 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -357,7 +357,7 @@ TimeSeriesAnalyzer <- R6Class( pred <- 0 if (p > 0) { ar_terms <- if (i <= p) { - y[(n-p+i):n] + y[(n-p+1):n] } else { forecasts[(i-p):(i-1)] } From f3bb26c0718572449df523f36816e28a02588dc8 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:56:42 +0530 Subject: [PATCH 13/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index cc4a3861..6c0d0a8c 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -356,10 +356,21 @@ TimeSeriesAnalyzer <- R6Class( for (i in 1:h) { pred <- 0 if (p > 0) { - ar_terms <- if (i <= p) { - y[(n-p+1):n] + # Use the most recent p values (from original data and previous forecasts) + if (i <= p) { + ar_terms <- y[(n-p+i):(n+i-1)] } else { - forecasts[(i-p):(i-1)] + # Combine tail of y and head of forecasts as needed + num_from_y <- max(0, p - (i-1)) + num_from_forecasts <- p - num_from_y + if (num_from_y > 0) { + ar_terms <- c( + y[(n - p + i):(n)], + forecasts[1:num_from_forecasts] + ) + } else { + ar_terms <- forecasts[(i-p):(i-1)] + } } pred <- pred + sum(model$coefficients$ar * ar_terms) } From 4e11f605aca9e44b903948bfd221de7286bf30fc Mon Sep 17 00:00:00 2001 From: Pratik Date: Sun, 12 Oct 2025 18:56:53 +0530 Subject: [PATCH 14/14] Update quantitative_finance/time_series_analyzer.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- quantitative_finance/time_series_analyzer.r | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/quantitative_finance/time_series_analyzer.r b/quantitative_finance/time_series_analyzer.r index 6c0d0a8c..96a5e01c 100644 --- a/quantitative_finance/time_series_analyzer.r +++ b/quantitative_finance/time_series_analyzer.r @@ -296,7 +296,11 @@ TimeSeriesAnalyzer <- R6Class( r <- private$calculate_acf(q) psi <- numeric(q) for (j in 1:q) { - psi[j] <- sum(theta[1:(j-1)] * rev(psi[1:(j-1)])) + theta[j] + if (j > 1) { + psi[j] <- sum(theta[1:(j-1)] * rev(psi[1:(j-1)])) + theta[j] + } else { + psi[j] <- theta[j] + } } if (q == 1) { theta <- solve(stats::toeplitz(1), r[1])