library(quantmod)
## Cargando paquete requerido: xts
## Cargando paquete requerido: zoo
##
## Adjuntando el paquete: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Cargando paquete requerido: TTR
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
library(TTR)
library(caret)
## Cargando paquete requerido: ggplot2
## Cargando paquete requerido: lattice
getSymbols("F", src = "yahoo", from = "2020-01-01", to = Sys.Date())
## [1] "F"
F_Close <- Cl(F)
SMA20 <- SMA(F_Close, n = 20)
SMA50 <- SMA(F_Close, n = 50)
RSI_14 <- RSI(F_Close, n = 14)
macd <- MACD(F_Close, nFast = 12, nSlow = 26, nSig = 9)
MACD_Line <- macd[, 1]
Signal_Line <- macd[, 2]
bb <- BBands(HLC(F), n = 20)
BB_Up <- bb[, "up"]
BB_Dn <- bb[, "dn"]
# Crear Target sin usar Lag() para evitar problemas de alineación:
close_vec <- as.numeric(F_Close)
# Target: 1 si mañana > hoy, 0 si mañana <= hoy. El último día no tiene "mañana" -> NA
Target <- c( ifelse(close_vec[-1] > close_vec[-length(close_vec)], 1, 0), NA )
# Construir dataframe con todas las series convertidas a numeric y con misma longitud
df <- data.frame(
Close = close_vec,
SMA20 = as.numeric(SMA20),
SMA50 = as.numeric(SMA50),
RSI_14 = as.numeric(RSI_14),
MACD_Line = as.numeric(MACD_Line),
Signal_Line = as.numeric(Signal_Line),
BB_Up = as.numeric(BB_Up),
BB_Dn = as.numeric(BB_Dn),
Target = Target
)
# Eliminar filas con NA (causadas por indicadores y por el último Target NA)
df <- na.omit(df)
# Verificar que Target tenga al menos dos clases
if(length(unique(df$Target)) < 2) stop("Target tiene una sola clase después de na.omit; no se puede entrenar el modelo.")
# Convertir Target a factor (requerido para createDataPartition y confusionMatrix)
df$Target <- factor(df$Target, levels = c(0,1), labels = c("Down","Up"))
# División entrenamiento/prueba
set.seed(123)
trainIndex <- createDataPartition(df$Target, p = 0.8, list = FALSE)
train <- df[trainIndex, ]
test <- df[-trainIndex, ]
# Ajuste modelo logístico
modelo <- glm(Target ~ SMA20 + SMA50 + RSI_14 + MACD_Line + Signal_Line + BB_Up + BB_Dn,
data = train, family = binomial())
# Predicción en test
pred_probs <- predict(modelo, newdata = test, type = "response")
pred_class <- ifelse(pred_probs > 0.5, "Up", "Down")
pred_class <- factor(pred_class, levels = c("Down","Up"))
# Evaluación
conf <- confusionMatrix(pred_class, test$Target)
print(conf)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Down Up
## Down 68 53
## Up 72 87
##
## Accuracy : 0.5536
## 95% CI : (0.4933, 0.6127)
## No Information Rate : 0.5
## P-Value [Acc > NIR] : 0.04145
##
## Kappa : 0.1071
##
## Mcnemar's Test P-Value : 0.10740
##
## Sensitivity : 0.4857
## Specificity : 0.6214
## Pos Pred Value : 0.5620
## Neg Pred Value : 0.5472
## Prevalence : 0.5000
## Detection Rate : 0.2429
## Detection Prevalence : 0.4321
## Balanced Accuracy : 0.5536
##
## 'Positive' Class : Down
##
# Gráfica simple: precio real (últimas n) vs señales predichas (para inspección)
n_plot <- min(200, nrow(test))
idx_plot <- (nrow(test)-n_plot+1):nrow(test)
plot(test$Close[idx_plot], type = "l", main = "Precio cierre (test) y señales predichas", ylab = "Precio", xlab = "Observaciones")
points(idx_plot[pred_class[idx_plot]=="Up"] - idx_plot[1] + 1, test$Close[idx_plot][pred_class[idx_plot]=="Up"], pch = 24, bg = "green")
points(idx_plot[pred_class[idx_plot]=="Down"] - idx_plot[1] + 1, test$Close[idx_plot][pred_class[idx_plot]=="Down"], pch = 25, bg = "red")
##Reflexión
#Estos modelos con indicadores SMA, MACD y RSI pueden llegar a capturar tendencias pero no siempre son muy utiles en mercados volatiles, el modelo hecho tiene cierta capacidad de prediccion y aunque es mejor que advinar ya que tiene mas de 50% de probabilidad de prediccion sigue sin ser un modelo confiable en mi opinion para basarse como herramienta unica de inversion.Este tipo de modelos que no son tan complejos pueden llegar a ser muy efectivos y utiles para complementar un analsis de inversion pero aun no llega a ser lo suficientemente complejo y/o efectivo para basarse solo en esto.