# Todos:
# - Change the custom open dates from csv to save maybe?
straddle <- function(progress.int, t) {
shiny::withProgress(message = "Progress Bar", detail = "Opening Trades", {
p.positions <- data.frame()
c.positions <- data.frame()
put.results <- data.frame()
call.results <- data.frame()
results <- data.frame()
# Set the first possible opening date based on the stock loaded
min.date <- min(complete.data$date)
# Set the last day for expiration dates to keep us from opening trades
# we can't close
max.date <- max(complete.data$date)
trade.data <- dplyr::filter(complete.data, expiration <= max.date)
# Filter first day month/week data to dates we have options chains for
first.day <- dplyr::filter(first.day, date >= min.date)
for (i in unique(first.day$date)) {
incProgress(progress.int)
t = t + 1
start.data <- dplyr::filter(trade.data, date == i) # Set open date
# Check for data set and that iv rank is within range
if (nrow(start.data) > 0 && start.data[1, iv.rank] >= low.iv &&
start.data[1, iv.rank] <= high.iv) {
put.data <- dplyr::mutate(start.data, dte.diff = abs(o.dte - dte))
put.data <- dplyr::filter(put.data, dte.diff == min(dte.diff)) # Closest to DTE
put.data <- dplyr::filter(put.data, dte == max(dte)) # Max DTE if there are two same diff from target DTE
put.data <- dplyr::filter(put.data, call.put == "P")
put.data <- dplyr::filter(put.data, strike >= price)
put.trade <- dplyr::filter(put.data, strike == min(strike))
call.data <- dplyr::filter(start.data, dte == put.trade$dte)
call.data <- dplyr::filter(call.data, call.put == "C")
call.trade <- dplyr::filter(call.data, strike == put.trade$strike)
call.trade <- dplyr::mutate(call.trade, trade.num = t, type = "call") # Assign trade num
put.trade <- dplyr::mutate(put.trade, trade.num = t, type = "put") # Assign trade num
c.positions <- dplyr::bind_rows(c.positions, call.trade, fill = TRUE)
p.positions <- dplyr::bind_rows(p.positions, put.trade, fill = TRUE)
}
}
}) # End opening trades progress bar
shiny::withProgress(message = "Progress Bar", detail = "Closing Trades", value = .5, {
# Loop through all trades and find the results
for (i in 1:nrow(c.positions)) {
incProgress(progress.int)
ocp <- c.positions[i, mid.price]
opp <- p.positions[i, mid.price]
od <- c.positions[i, date]
e <- c.positions[i, expiration]
s <- p.positions[i, strike]
j <- c.positions[i, trade.num]
# Close the put at expiration
put.close <- dplyr::filter(trade.data,
date == e, expiration == e,
strike == s, call.put == "P")
put.close <- dplyr::mutate(put.close, trade.num = j,
exit.reason = "Expiration",
close.price = mid.price)
put.results <- dplyr::bind_rows(put.results, put.close, fill = TRUE)
# Close the call at the expiration
call.close <- dplyr::filter(trade.data,
date == e, expiration == e,
strike == s, call.put == "C")
call.close <- dplyr::mutate(call.close, trade.num = j,
exit.reason = "Expiration",
close.price = mid.price)
call.results <- dplyr::bind_rows(call.results, call.close, fill = TRUE)
# Close the Strangle if the profit target is met
profit.target <- (ocp + opp) * prof.targ
possible.close <- dplyr::filter(trade.data, date >= od, date <= e)
possible.close <- dplyr::filter(possible.close, expiration == e,
strike == s)
possible.close <- dplyr::group_by(possible.close, date)
possible.close <- dplyr::mutate(possible.close,
curr.price = sum(mid.price))
possible.close <- dplyr::filter(possible.close,
curr.price <= (ocp + opp) - profit.target)
if (nrow(possible.close) > 0) {
possible.close <- dplyr::filter(possible.close, date == min(date))
possible.close <- dplyr::mutate(possible.close, trade.num = j,
exit.reason = "Profit target",
close.price = ((ocp + opp) - profit.target) / 2 )
put.close <- dplyr::filter(possible.close, call.put == "P")
call.close <- dplyr::filter(possible.close, call.put == "C")
put.results <- dplyr::bind_rows(put.results, put.close, fill = TRUE)
call.results <- dplyr::bind_rows(call.results, call.close, fill = TRUE)
}
# Close the Strangle if the loss limit is met
loss.limit <- (ocp + opp) * loss.lim
possible.close <- dplyr::filter(trade.data, date >= od, date <= e)
possible.close <- dplyr::filter(possible.close, expiration == e,
strike == s)
possible.close <- dplyr::group_by(possible.close, date)
possible.close <- dplyr::mutate(possible.close, curr.price = sum(mid.price))
possible.close <- dplyr::filter(possible.close, curr.price >= loss.limit)
if (nrow(possible.close) > 0) {
possible.close <- dplyr::filter(possible.close, date == min(date))
possible.close <- dplyr::mutate(possible.close, trade.num = j,
exit.reason = "Loss limit",
close.price = loss.limit / 2)
put.close <- dplyr::filter(possible.close, call.put == "P")
call.close <- dplyr::filter(possible.close, call.put == "C")
put.results <- dplyr::bind_rows(put.results, put.close, fill = TRUE)
call.results <- dplyr::bind_rows(call.results, call.close, fill = TRUE)
}
# Close by gamma days input
if (g > 0) {
gamma.date <- as.Date(e, origin = "1970-01-01") - g
ifelse(weekdays(gamma.date, abbreviate = FALSE) == "Sunday",
gamma.date <- gamma.date - 2, gamma.date)
ifelse(weekdays(gamma.date, abbreviate = FALSE) == "Saturday",
gamma.date <- gamma.date - 1, gamma.date)
g.put.close <- dplyr::filter(trade.data, date == gamma.date,
expiration == e, strike == s)
g.put.close <- dplyr::filter(g.put.close, call.put == "P")
g.put.close <- dplyr::mutate(g.put.close, trade.num = j,
exit.reason = "Gamma risk",
close.price = mid.price)
put.results <- dplyr::bind_rows(put.results, g.put.close, fill = TRUE)
g.call.close <- dplyr::filter(trade.data, date == gamma.date,
expiration == e, strike == s)
g.call.close <- dplyr::filter(g.call.close, call.put == "C")
g.call.close <- dplyr::mutate(g.call.close, trade.num = j,
exit.reason = "Gamma risk",
close.price = mid.price)
call.results <- dplyr::bind_rows(call.results, g.call.close, fill = TRUE)
}
# Close prior to earnings
if (earn.close == "Yes") {
e.dates <- dplyr::filter(earn.dates, earn.date > od, earn.date < e)
e.dates <- dplyr::filter(e.dates, earn.date == min(earn.date))
if (nrow(e.dates) > 0 ) {
e.date <- as.Date(e.dates$earn.date)
e.put.close <- dplyr::filter(trade.data, date == e.date,
expiration == e, strike == s)
e.put.close <- dplyr::filter(e.put.close, call.put == "P")
e.put.close <- dplyr::mutate(e.put.close, trade.num = j,
exit.reason = "Earnings",
close.price = mid.price)
put.results <- dplyr::bind_rows(put.results, e.put.close, fill = TRUE)
e.call.close <- dplyr::filter(trade.data, date == e.date,
expiration == e, strike == s)
e.call.close <- dplyr::filter(e.call.close, call.put == "C")
e.call.close <- dplyr::mutate(e.call.close, trade.num = j,
exit.reason = "Earnings",
close.price = mid.price)
call.results <- dplyr::bind_rows(call.results, e.call.close, fill = TRUE)
}
}
}
# Filter down results to the first exit for each trade as multiple targets
# could be hit
ord.put.results <- dplyr::group_by(put.results, trade.num)
put.results <- dplyr::filter(ord.put.results,
rank(date, ties.method = "first") == 1)
put.results <- ungroup(put.results)
ord.call.results <- dplyr::group_by(call.results, trade.num)
call.results <- dplyr::filter(ord.call.results,
rank(date, ties.method = "first") == 1)
call.results <- ungroup(call.results)
# Merge the opening and closing data frames to calculate profit loss
merge.call.results <- merge(call.results, c.positions, by = "trade.num")
call.results <- dplyr::mutate(merge.call.results,
profit.loss = 100 * (mid.price.y - close.price))
call.results <- dplyr::mutate(call.results, year = year(date.y))
call.results <- dplyr::select(call.results,
call.expiration = expiration.x,
trade.num,
call.close.date = date.x,
call.strike = strike.x,
call.close.price = close.price,
call.close.rsi = rsi.14.x,
call.close.ivrank = iv.rank.x,
call.open.date = date.y,
call.price = price.y,
call.open.price = mid.price.y,
call.delta = delta.y,
call.dte = dte.y,
call.open.rsi = rsi.14.y,
call.open.ivrank = iv.rank.y,
call.profit = profit.loss,
call.year = year,
exit.reason)
# Add in commission at a rate of $1.5 per trade excluding closing for <= .05
call.results <- dplyr::mutate(call.results,
call.profit = ifelse(call.close.price <= .05,
call.profit - 1.5, call.profit - 3))
# Merge the opening and closing data frames to calculate profit loss
merge.put.results <- merge(put.results, p.positions, by = "trade.num")
put.results <- dplyr::mutate(merge.put.results,
profit.loss = 100 * (mid.price.y - close.price))
put.results <- dplyr::mutate(put.results, year = year(date.y))
put.results <- dplyr::select(put.results,
put.expiration = expiration.x,
trade.num,
put.close.date = date.x,
put.strike = strike.x,
put.close.price = close.price,
put.close.rsi = rsi.14.x,
put.close.ivrank = iv.rank.x,
put.open.date = date.y,
put.price = price.y,
put.open.price = mid.price.y,
put.delta = delta.y,
put.dte = dte.y,
put.open.rsi = rsi.14.y,
put.open.ivrank = iv.rank.y,
put.profit = profit.loss,
put.year = year,
exit.reason)
# Add in commission at a rate of $1.5 per trade excluding closing for <= .05
put.results <- dplyr::mutate(put.results,
put.profit = ifelse(put.close.price <= .05,
put.profit - 1.5, put.profit - 3))
# Combine put and call results to enable calculation of totals
for (i in 1:nrow(put.results)) {
t <- put.results[i, trade.num]
ps <- put.results[i, put.strike]
od <- put.results[i, put.open.date]
cd <- put.results[i, put.close.date]
d <- put.results[i, put.dte]
pop <- put.results[i, put.open.price]
iv <- put.results[i, put.open.ivrank]
rsi <- put.results[i, put.open.rsi]
pp <- put.results[i, put.profit]
er <- put.results[i, exit.reason]
df1 <- dplyr::filter(call.results, trade.num == t)
cp <- df1$call.profit
cs <- df1$call.strike
cop <- df1$call.open.price
tp <- pp + cp
this.case <- as.data.frame(matrix(nrow = 1,
c(t, ps, od, cd, d, pop, iv, rsi, pp, cp,
cs, cop, tp, er)),
stringsAsFactors = FALSE)
results <- dplyr::bind_rows(results, this.case)
}
colnames(results) <- c("trade.num", "put.strike", "open.date", "close.date",
"dte", "put.credit", "open.ivrank", "open.rsi",
"put.profit", "call.profit", "call.strike",
"call.credit", "profit", "exit.reason")
results <- dplyr::mutate(results,
trade.num = as.integer(trade.num),
put.strike = as.numeric(put.strike),
open.date = as.Date(as.numeric(open.date),
origin = "1970-01-01"),
close.date = as.Date(as.numeric(close.date),
origin = "1970-01-01"),
year = year(open.date),
dte = as.numeric(dte),
put.credit = as.numeric(put.credit) * 100,
open.ivrank = as.numeric(open.ivrank),
open.rsi = as.numeric(open.rsi),
put.profit = as.numeric(put.profit),
call.profit = as.numeric(call.profit),
call.strike = as.numeric(call.strike),
call.credit = as.numeric(call.credit) * 100,
days.held = as.numeric(close.date) - as.numeric(open.date),
profit = as.numeric(profit),
has_profit = ifelse(profit > 0, "Yes", "No"))
}) # End closing trades progress bar
shiny::withProgress(message = "Progress Bar", detail = "Creating Plot", value = .95, {
# Send results to global data frame for viewing
results.table <- dplyr::select(results,
open.date, close.date, dte, days.held,
open.ivrank, put.strike, call.strike,
exit.reason, profit)
# Write out closing dates to csv so can be used as the open dates with
# the custom open option
#close.dates <- dplyr::select(results, close.date, exit.reason)
#write.csv(close.dates, file = "data/customDates.csv")
# Calculate totals for display
assign("num_t", nrow(results), envir = .GlobalEnv)
assign("tot_profit", sum(results$profit), envir = .GlobalEnv)
assign("avg_profit", tot_profit/num_t, envir = .GlobalEnv)
assign("tot_days", sum(results$days.held), envir = .GlobalEnv)
assign("avg_days", tot_days/num_t, envir = .GlobalEnv)
assign("avg_prof_day", avg_profit/avg_days, envir = .GlobalEnv)
assign("maximum_loss",
ifelse(min(results$profit) >= 0,
0, min(results$profit)), envir = .GlobalEnv)
assign("percent_winners",
scales::percent(length(which(results$profit > 0)) / num_t), .GlobalEnv)
assign("exit.expiration",
ifelse(scales::percent(length(which(results$exit.reason ==
"Expiration")) / num_t) == "NaN%",
0, scales::percent(length(which(results$exit.reason ==
"Expiration")) / num_t)),
envir = .GlobalEnv)
assign("exit.profit.target",
ifelse(scales::percent(length(which(results$exit.reason ==
"Profit target")) / num_t) == "NaN%",
0, scales::percent(length(which(results$exit.reason ==
"Profit target")) / num_t)),
envir = .GlobalEnv)
assign("exit.loss.limit",
ifelse(scales::percent(length(which(results$exit.reason ==
"Loss limit")) / num_t) == "NaN%",
0, scales::percent(length(which(results$exit.reason ==
"Loss limit")) / num_t)),
envir = .GlobalEnv)
assign("exit.gamma.risk",
ifelse(scales::percent(length(which(results$exit.reason ==
"Gamma risk")) / num_t) == "NaN%",
0, scales::percent(length(which(results$exit.reason ==
"Gamma risk")) / num_t)),
envir = .GlobalEnv)
assign("exit.earnings",
ifelse(scales::percent(length(which(results$exit.reason ==
"Earnings")) / num_t) == "NaN%",
0, scales::percent(length(which(results$exit.reason ==
"Earnings")) / num_t)),
envir = .GlobalEnv)
}) # End creating plot progress bar
# Send results to global environment for further processing in main script
assign("results", results, envir = .GlobalEnv)
assign("results.table", results.table, envir = .GlobalEnv)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.