## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5, message = FALSE, warning = FALSE ) ## ----install, eval=FALSE------------------------------------------------------ # # Install from GitHub # # (Skip during CRAN checks and vignette builds.) # devtools::install_github("alb3rtazzo/PortfolioTesteR") ## ----load--------------------------------------------------------------------- library(PortfolioTesteR) ## ----strategy1---------------------------------------------------------------- # Load included weekly prices data(sample_prices_weekly) # 1) Momentum signal momentum <- calc_momentum(sample_prices_weekly, lookback = 12) # 2) Select top 10 by momentum selected <- filter_top_n(momentum, n = 10) # 3) Equal weights weights <- weight_equally(selected) # 4) Backtest result1 <- run_backtest( prices = sample_prices_weekly, weights = weights, initial_capital = 100000, name = "Simple Momentum" ) # 5) Results print(result1) summary(result1) ## ----strategy1_plot----------------------------------------------------------- plot(result1, type = "performance") ## ----strategy2---------------------------------------------------------------- # Need daily data for volatility data(sample_prices_daily) # A) Momentum (12-week) momentum <- calc_momentum(sample_prices_weekly, lookback = 12) # B) Daily volatility → align weekly → invert (low vol = high score) daily_vol <- calc_rolling_volatility(sample_prices_daily, lookback = 20) weekly_vol <- align_to_timeframe( high_freq_data = daily_vol, low_freq_dates = sample_prices_weekly$Date, method = "forward_fill" ) stability_signal <- invert_signal(weekly_vol) # Select top 20 for each signal m_sel <- filter_top_n(momentum, n = 20) s_sel <- filter_top_n(stability_signal, n = 20) # AND-combine the selections both <- combine_filters(m_sel, s_sel, op = "and") # Weight each way then blend 60/40 w_mom <- weight_by_signal(both, momentum) w_stab <- weight_by_signal(both, stability_signal) weights2 <- combine_weights(list(w_mom, w_stab), weights = c(0.6, 0.4)) # Backtest result2 <- run_backtest( prices = sample_prices_weekly, weights = weights2, initial_capital = 100000, name = "Momentum + Low Vol" ) print(result2) summary(result2) ## ----strategy2_plot----------------------------------------------------------- plot(result2, type = "performance") ## ----strategy3---------------------------------------------------------------- # Signals and selection momentum <- calc_momentum(sample_prices_weekly, lookback = 12) sel <- filter_top_n(momentum, n = 10) weights_mom <- weight_by_signal(sel, momentum) # With 15% stop-loss (daily monitoring) result3_with <- run_backtest( prices = sample_prices_weekly, weights = weights_mom, initial_capital = 100000, name = "Momentum with 15% Stop Loss", stop_loss = 0.15, stop_monitoring_prices = sample_prices_daily ) # Without stop-loss result3_no <- run_backtest( prices = sample_prices_weekly, weights = weights_mom, initial_capital = 100000, name = "Momentum without Stop Loss" ) cat("WITH Stop Loss:\n") print(result3_with) cat("\nWITHOUT Stop Loss:\n") print(result3_no) ## ----strategy3_plot----------------------------------------------------------- # Plot both separately to avoid cramped figures plot(result3_with, type = "performance") plot(result3_no, type = "performance") ## ----strategy4---------------------------------------------------------------- # Extract SPY for regime detection spy_prices <- sample_prices_weekly[, .(Date, SPY)] # Trading universe (exclude SPY) trading_symbols <- setdiff(names(sample_prices_weekly), c("Date", "SPY")) trading_prices <- sample_prices_weekly[, c("Date", trading_symbols), with = FALSE] trading_daily <- sample_prices_daily[, c("Date", trading_symbols), with = FALSE] # SPY weekly returns & 20-week rolling volatility (annualized) spy_returns <- c(NA, diff(spy_prices$SPY) / head(spy_prices$SPY, -1)) spy_vol <- zoo::rollapply(spy_returns, width = 20, FUN = sd, fill = NA, align = "right") * sqrt(52) # High-vol regime = above median vol_threshold <- median(spy_vol, na.rm = TRUE) high_vol <- spy_vol > vol_threshold # Selection by momentum mom <- calc_momentum(trading_prices, lookback = 12) sel <- filter_top_n(mom, n = 15) # Defensive (prefer low vol) vs Aggressive (prefer high vol) weights w_def <- weight_by_volatility( selected_df = sel, vol_timeframe_data = trading_daily, strategy_timeframe_data = trading_prices, lookback_periods = 20, low_vol_preference = TRUE, vol_method = "std" ) w_agg <- weight_by_volatility( selected_df = sel, vol_timeframe_data = trading_daily, strategy_timeframe_data = trading_prices, lookback_periods = 20, low_vol_preference = FALSE, vol_method = "std" ) # Switch weights by regime (defensive when high-vol is TRUE) weights4 <- switch_weights( weights_a = w_agg, # used when condition is FALSE (low vol) weights_b = w_def, # used when condition is TRUE (high vol) use_b_condition = high_vol ) result4 <- run_backtest( prices = trading_prices, weights = weights4, initial_capital = 100000, name = "Regime-Adaptive Strategy" ) print(result4) summary(result4) ## ----strategy4_plot----------------------------------------------------------- plot(result4, type = "performance") ## ----strategy5---------------------------------------------------------------- # Signals momentum <- calc_momentum(sample_prices_weekly, lookback = 12) daily_vol <- calc_rolling_volatility(sample_prices_daily, lookback = 20) weekly_vol <- align_to_timeframe(daily_vol, sample_prices_weekly$Date, method = "forward_fill") stability <- invert_signal(weekly_vol) # Selection & position cap top30 <- filter_top_n(momentum, n = 30) sel15 <- limit_positions(top30, momentum, max_positions = 15) # Weights and blend (70/30) w_m <- weight_by_signal(sel15, momentum) w_s <- weight_by_signal(sel15, stability) weights5 <- combine_weights(list(w_m, w_s), weights = c(0.7, 0.3)) # Backtest result5 <- run_backtest( prices = sample_prices_weekly, weights = weights5, initial_capital = 100000, name = "Multi-Factor with Position Limits" ) print(result5) summary(result5) ## ----strategy5_plot----------------------------------------------------------- plot(result5, type = "performance") ## ----strategy6---------------------------------------------------------------- # Data data(sample_prices_weekly) data(sample_prices_daily) # Exclude broad ETFs from stock-selection universe symbols_all <- setdiff(names(sample_prices_weekly), "Date") stock_symbols <- setdiff(symbols_all, c("SPY", "TLT")) weekly_stocks <- sample_prices_weekly[, c("Date", stock_symbols), with = FALSE] daily_stocks <- sample_prices_daily[, c("Date", stock_symbols), with = FALSE] # StochRSI "acceleration" signal (weekly) stochrsi <- calc_stochrsi(weekly_stocks, length = 14) # in [0,1] stochrsi_ma <- calc_moving_average(stochrsi, window = 5) accel <- calc_distance(stochrsi, stochrsi_ma) # positive = rising # Gate to high StochRSI zone, then take top-12 by acceleration high_zone <- filter_above(stochrsi, value = 0.80) sel <- filter_top_n_where( signal_df = accel, n = 12, condition_df = high_zone, min_qualified = 8, ascending = FALSE ) # Allocation: inverse-volatility risk parity (DAILY prices) w_ivol <- weight_by_risk_parity( selected_df = sel, prices_df = daily_stocks, method = "inverse_vol", lookback_periods = 126, # ~6 months min_periods = 60 ) # Backtest on the weekly grid res_stochrsi <- run_backtest( prices = weekly_stocks, weights = w_ivol, initial_capital = 100000, name = "StochRSI Accel + InvVol RP" ) print(res_stochrsi) summary(res_stochrsi) ## ----strategy6_plot----------------------------------------------------------- plot(res_stochrsi, type = "performance") ## ----live_data, eval = identical(Sys.getenv("RUN_LIVE"), "true")-------------- # library(PortfolioTesteR) # # # Fetch weekly data for a small set of tickers # tickers <- c("AAPL","MSFT","AMZN","GOOGL","META") # px_weekly <- yahoo_adapter( # symbols = tickers, # frequency = "weekly" # ) # # # Simple momentum: top-3 by 12-week return, equal weight # mom <- calc_momentum(px_weekly, lookback = 12) # sel <- filter_top_n(mom, n = 3) # w_eq <- weight_equally(sel) # # res_yh <- run_backtest( # prices = px_weekly, # weights = w_eq, # initial_capital = 100000, # name = "Yahoo: Simple Momentum (Top 3)" # ) # # print(res_yh) # summary(res_yh) # # ## ----help, eval=FALSE--------------------------------------------------------- # ?run_backtest # ?calc_momentum # ?filter_top_n # ?analyze_performance