This document optimizes trend-following and mean-reversion strategies for BIST 100 using blotter, tracking portfolio performance, and visualizing equity curves.
# Clear existing environments
rm(list = ls(all.names = TRUE))
gc()
## used (Mb) gc trigger (Mb) limit (Mb) max used (Mb)
## Ncells 616582 33.0 1411540 75.4 NA 700245 37.4
## Vcells 1165743 8.9 8388608 64.0 16384 1963281 15.0
# Load required libraries
libraries <- c("quantstrat", "PerformanceAnalytics", "TTR",
"blotter", "FinancialInstrument", "quantmod",
"ggplot2", "reshape2")
invisible(lapply(libraries, library, character.only = TRUE))
## Loading required package: quantmod
## Loading required package: xts
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
## Loading required package: blotter
## Loading required package: FinancialInstrument
## Loading required package: PerformanceAnalytics
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
## Loading required package: foreach
# Set global options
options(verbose = FALSE)
Sys.setenv(TZ = "UTC")
# Initialize blotter account and portfolio
initDate <- "2000-01-01"
currency("USD")
## [1] "USD"
stock("XU100.IS", currency = "USD", multiplier = 1)
## [1] "XU100.IS"
initEq <- 100000
# Portfolio and account setup
strategy_name <- "BIST_Strategy"
account_name <- "BIST_Account"
portfolio_name <- "BIST_Portfolio"
if (!exists('.blotter')) .blotter <- new.env()
if (!exists('.strategy')) .strategy <- new.env()
initPortf(portfolio_name, symbols = "XU100.IS", initDate = initDate)
## [1] "BIST_Portfolio"
initAcct(account_name, portfolios = portfolio_name, initDate = initDate, initEq = initEq)
## [1] "BIST_Account"
initOrders(portfolio_name, initDate = initDate)
# Retrieve BIST 100 data
XU100.IS <- getSymbols("XU100.IS", src = "yahoo", from = "2010-01-01", to = "2020-01-01", auto.assign = FALSE)
## Warning: XU100.IS contains missing values. Some functions will not work if
## objects contain missing values in the middle of the series. Consider using
## na.omit(), na.approx(), na.fill(), etc to remove or replace them.
XU100.IS <- na.omit(XU100.IS)
prices <- Cl(XU100.IS)
# Diagnostics
cat("Total observations:", nrow(prices), "\n")
## Total observations: 2511
cat("Date range:", as.character(first(index(prices))), "to", as.character(last(index(prices))), "\n")
## Date range: 2010-01-04 to 2019-12-31
# Function to place blotter trades
place_trade <- function(portfolio, symbol, trade_type, qty, price, timestamp) {
addTxn(Portfolio = portfolio, Symbol = symbol, TxnDate = timestamp, TxnPrice = price, TxnQty = qty, TxnFees = 0, verbose = TRUE)
}
# Strategy Optimization Function
optimize_strategy <- function(prices, strategy_type = "trend_following", n_range = 1:8, m_range = 1:10) {
returns <- ROC(prices, type = "discrete") # Calculate returns
returns <- na.omit(returns) # Omit NA values from returns
results <- data.frame(n = integer(), m = integer(), TotalReturn = numeric(), SharpeRatio = numeric(), MaxDrawdown = numeric())
for (n in n_range) {
for (m in m_range) {
tryCatch({
# Create trading signals
if (strategy_type == "trend_following") {
buy_signal <- runSum(returns > 0, n) == n
sell_signal <- runSum(returns < 0, m) == m
} else {
buy_signal <- runSum(returns < 0, n) == n
sell_signal <- runSum(returns > 0, m) == m
}
# Calculate strategy performance
strategy_returns <- ifelse(buy_signal, returns, 0)
total_return <- sum(strategy_returns, na.rm = TRUE)
sharpe_ratio <- tryCatch({
SharpeRatio.annualized(strategy_returns[strategy_returns != 0], Rf = 0)
}, error = function(e) NA)
max_drawdown <- tryCatch({
maxDrawdown(strategy_returns)[[1]]
}, error = function(e) 0)
results <- rbind(results, data.frame(n = n, m = m, TotalReturn = total_return, SharpeRatio = sharpe_ratio, MaxDrawdown = max_drawdown))
}, error = function(e) cat("Error:", conditionMessage(e), "\n"))
}
}
return(results)
}
cat("Running Trend Following Optimization:\n")
## Running Trend Following Optimization:
trend_results <- optimize_strategy(prices, "trend_following")
cat("\nRunning Mean Reversion Optimization:\n")
##
## Running Mean Reversion Optimization:
meanrev_results <- optimize_strategy(prices, "mean_reversion")
# Function to get best parameters based on TotalReturn
get_best_params <- function(results_df) {
best_idx <- which.max(results_df$TotalReturn)
return(results_df[best_idx, ])
}
# Get best parameters
best_trend <- get_best_params(trend_results)
best_meanrev <- get_best_params(meanrev_results)
# Create a data frame for the table
results_table <- data.frame(
Strategy = c("Trend Following", "Mean Reversion"),
n = c(best_trend$n, best_meanrev$n),
m = c(best_trend$m, best_meanrev$m),
TotalReturn = round(c(best_trend$TotalReturn, best_meanrev$TotalReturn), 4),
SharpeRatio = round(c(best_trend$SharpeRatio, best_meanrev$SharpeRatio), 4),
MaxDrawdown = round(c(best_trend$MaxDrawdown, best_meanrev$MaxDrawdown), 4)
)
# Print the table
print(results_table, row.names = FALSE)
## Strategy n m TotalReturn SharpeRatio MaxDrawdown
## Trend Following 1 1 13.5115 10.9634 0.0000
## Mean Reversion 8 1 -0.1302 -1.1754 0.1275
# Heatmap for Trend Following
trend_matrix <- matrix(trend_results$TotalReturn, nrow=length(unique(trend_results$n)),
byrow=TRUE)
trend_plot <- ggplot(trend_results, aes(x=n, y=m, fill=TotalReturn)) +
geom_tile() +
scale_fill_gradient(low="white", high="blue") +
labs(title="Trend Following Returns Heatmap",
x="n (Buy Signal)", y="m (Sell Signal)") +
theme_minimal()
# Heatmap for Mean Reversion
meanrev_matrix <- matrix(meanrev_results$TotalReturn, nrow=length(unique(meanrev_results$n)),
byrow=TRUE)
meanrev_plot <- ggplot(meanrev_results, aes(x=n, y=m, fill=TotalReturn)) +
geom_tile() +
scale_fill_gradient(low="white", high="red") +
labs(title="Mean Reversion Returns Heatmap",
x="n (Buy Signal)", y="m (Sell Signal)") +
theme_minimal()
# Display plots
print(trend_plot)
print(meanrev_plot)
save.image()