Identifying types of trading philosophies

What is strategy?

A strategy is a set of signals, applied to a chart.

Signals systematically specify market entry or exit points according to a set of trading rules implemented in the signals’ algorithms, and can be viewed as the basic building blocks of strategies.

While signals can be based on the same formulas as indicators, signal calculations are substantially more complex and take in to account a far greater number of factors, such as trading costs, order generation priorities, and strategy performance measurements, that affect market entry and exit conditions. Signal algorithms utilize price action models and make use of backtesting concepts. Signal logic can make assumptions about price movement within bars that allow appropriate market entry and exit points to be determined when intra-bar data is not available. Signals compile strategy performance data and support backtesting and Automated Trade Execution.

How to prevent overfitting

When developing a trading system, a major pitfall that can creep into system development is the desire to find a strategy that worked phenomenally in the past. This is known as overfitting. Research by leading authors in the quantitative field has shown that not only is an overfitted system unlikely to generate profits in the future, but also that its performance can lead to losses.

The following steps can you take to reduce the chance of overfitting a trading system:

Plotting financial data

Trading strategies developed using quantstrat contain several characteristics, including:

The first step in developing any trading system is to obtain market data, and maybe even examine how it looks.

We will obtain data for SPY, an exchange traded fund (ETF) that tracks the top 500 companies in the United States by market cap. This data is from Yahoo! Finance, which is a sufficient source of data for strategies that do not require instantaneous “see the close, buy the close” execution. We will then plot it and add a trendline to it.

# Get SPY from yahoo
getSymbols("SPY", 
           from = "2000-01-01", 
           to = "2016-06-30", 
           src =  "yahoo", 
           adjust =  TRUE)
## 'getSymbols' currently uses auto.assign=TRUE by default, but will
## use auto.assign=FALSE in 0.5-0. You will still be able to use
## 'loadSymbols' to automatically load data. getOption("getSymbols.env")
## and getOption("getSymbols.auto.assign") will still be checked for
## alternate defaults.
## 
## This message is shown once per session and may be disabled by setting 
## options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.
## 
## WARNING: There have been significant changes to Yahoo Finance data.
## Please see the Warning section of '?getSymbols.yahoo' for details.
## 
## This message is shown once per session and may be disabled by setting
## options("getSymbols.yahoo.warning"=FALSE).
## [1] "SPY"
# Plot the closing price of SPY
plot(Cl(SPY))

Adding indicators to financial data

One of the most popular indicators to add to a trading strategy is the 200-day simple moving average (SMA). This is a technical indicator of the average closing price of a stock over the past 200 days. Other moving averages can be of varying length, such as 50-day, 100-day, etc.

Whenever the price is above the 200-day moving average, a whole assortment of good things usually happen, such as the asset appreciating in price, low volatility, and so on. Getting a long-lasting visual might shed light on why this indicator is mentioned so often.

# Plot the closing prices of SPY
plot(Cl(SPY))

# Add a 200-day SMA using lines()
lines(SMA(Cl(SPY), n = 200), col = "red")

Understanding initialization settings

Let’s get started with creating our first strategy in quantstrat. In this exercise, we will need to fill in three dates:

We should also specify:

The functions Sys.setenv() and currency() will be used for that purpose.

# Create initdate, from, and to strings
initdate <- "1999-01-01"
from <- "2003-01-01"
to <- "2015-12-31"

# Set the timezone to UTC
Sys.setenv(TZ = "UTC")

# Set the currency to USD 
currency("USD")
## [1] "USD"
# Retrieve SPY from yahoo
getSymbols("SPY", src = "yahoo", adjust = TRUE, from = from, to = to)
## [1] "SPY"
# Use stock() to initialize SPY and set currency to USD
stock("SPY", currency = "USD")
## [1] "SPY"

Let’s continue the setup of our strategy.

First, we will set a trade size of 100,000 USD in an object called tradesize which determines the amount you wager on each trade. Second, we will set our initial equity to 100,000 USD in an object called initeq.

Quantstrat requires three different objects to work:

Finally, before proceeding, you must remove any existing strategies using the strategy removal command rm.strat() which takes in a string of the name of a strategy.

# Define your trade size and initial equity
tradesize <- 100000
initeq <- 100000

# Define the names of your strategy, portfolio and account
# Remove the existing strategy if it exists
rm.strat("firststrat")

strategy.st <- "firststrat"
portfolio.st <- "firststrat"
account.st <- "firststrat"

Now that everything has been named, we must initialize the portfolio, the account, the orders, and the strategy to produce results.

# Initialize the portfolio
initPortf(portfolio.st, symbols = "SPY", initDate = initdate, currency = "USD")
## [1] "firststrat"
# Initialize the account
initAcct(account.st, portfolios = portfolio.st, initDate = initdate, currency = "USD", initEq = initeq)
## [1] "firststrat"
# Initialize the orders
initOrders(portfolio.st, initDate = initdate)

# Store the strategy
strategy(strategy.st, store = TRUE)

Indicators

An indicator is a transformation of market data that is used to generate signals or filter noise. Indicators form the backbone of many trading systems.

The simple moving average (SMA) and relative strength index (RSI) are two classic indicators. SMA is an arithmetic moving average of past prices, while the RSI is a bounded oscillating indicator that ranges from 0 to 100. Then we will visualize these indicators to understand why you might want to use the indicator and what it may represent. Recall that a trend indicator attempts to predict whether a price will continue in its current diretion, whereas a reversion indicator attempts to predict whether an increasing price will soon decrease, or the opposite.

# Create a 200-day SMA
spy_sma <- SMA(Cl(SPY), n = 200)

# Plot the closing prices of SPY
plot(Cl(SPY))

# Overlay a 200-day SMA
lines(spy_sma, col = "red")

# What kind of indicator?
"trend"
## [1] "trend"
# Create an RSI with a 3-day lookback period
spy_rsi <- RSI(Cl(SPY), n = 3)

The Relative Strength Index (RSI) is another indicator that rises with positive price changes and falls with negative price changes. It is equal to 100 - 100/(1 + RS), where RS is the average gain over average loss over the lookback period. At various lengths, this indicator can range from a reversion indicator, to a trend filter, or anywhere in between. There are various ways of computing the RSI.

# Plot the closing price of SPY
plot(Cl(SPY))

# Plot the RSI 2
plot(RSI(Cl(SPY), n = 2))

# What kind of indicator?
"reversion"
## [1] "reversion"

Implementing an indicator

At this point, it’s time to start getting into the mechanics of implementing an indicator.

# Add a 200-day SMA indicator to strategy.st
add.indicator(strategy = strategy.st, 
              
              # Add the SMA function
              name = "SMA", 
              
              # Create a lookback period
              arguments = list(x = quote(Cl(mktdata)), n = 200), 
              
              # Label your indicator SMA200
              label = "SMA200")
## [1] "firststrat"

Now, we’ll make your strategy even more robust by adding a 50-day SMA. A fast moving average with a slower moving average is a simple and standard way to predict when prices in an asset are expected to rise in the future. While a single indicator can provide a lot of information, a truly robust trading system requires multiple indicators to operate effectively.

# Add a 50-day SMA indicator to strategy.st
add.indicator(strategy = strategy.st, 
              
              # Add the SMA function
              name = "SMA", 
              
              # Create a lookback period
              arguments = list(x = quote(Cl(mktdata)), n = 50), 
              
              # Label your indicator SMA50
              label = "SMA50")
## [1] "firststrat"

In financial markets, the goal is to buy low and sell high. The RSI can predict when a price has sufficiently pulled back, especially with a short period such as 2 or 3.

Here, we’ll create a 3-period RSI, or RSI 3, to give you more practice in implementing pre-coded indicators.

# Add an RSI 3 indicator to strategy.st
add.indicator(strategy = strategy.st, 
              
              # Add the RSI 3 function
              name = "RSI",
              
              # Create a lookback period
              arguments = list(price = quote(Cl(mktdata)), n = 3), 
              
              # Label your indicator RSI_3
              label = "RSI_3")
## [1] "firststrat"

Code our own indicator

So far, we’ve used indicators that have been completely pre-written for you by using the add.indicator() function. Now it’s time to write and apply our own indicator.

Our indicator function will calculate the average of two different indicators to create an RSI of 3.5. Here’s how:

  • Take in a price series.

  • Calculate RSI 3.

  • Calculate RSI 4.

  • Return the average of RSI 3 and RSI 4.

This RSI can be thought of as an RSI 3.5, because it’s longer than an RSI 3 and shorter than an RSI 4. By averaging, this indicator takes into account the impact of four days ago while still being faster than a simple RSI 4, and also removes the noise of both RSI 3 and RSI 4.

# Write the calc_RSI_avg function
calc_RSI_avg <- function(price, n1, n2) {
  
  # RSI 1 takes an input of the price and n1
  RSI_1 <- RSI(price = price, n = n1)
  
  # RSI 2 takes an input of the price and n2
  RSI_2 <- RSI(price = price, n = n2)
  
  # RSI_avg is the average of RSI_1 and RSI_2
  RSI_avg <- (RSI_1 + RSI_2)/2
  
  # Your output of RSI_avg needs a column name of RSI_avg
  colnames(RSI_avg) <- "RSI_avg"
  return(RSI_avg)
}

# Add this function as RSI_3_4 to your strategy with n1 = 3 and n2 = 4
add.indicator(strategy.st,
name = "calc_RSI_avg",
arguments = list(price = quote(Cl(mktdata)), n1 = 3, n2 = 4), label = "RSI_3_4")
## [1] "firststrat"

While the RSI is decent, it is somewhat outdated as far as indicators go. So, we will code a simplified version of another indicator from scratch. The indicator is called the David Varadi Oscillator (DVO), originated by David Varadi, a quantitative research director.

The purpose of this oscillator is similar to something like the RSI in that it attempts to find opportunities to buy a temporary dip and sell in a temporary uptrend. In addition to obligatory market data, an oscillator function takes in two lookback periods.

  • First, the function computes a ratio between the closing price and average of high and low prices.

  • Next, it applies an SMA to that quantity to smooth out noise, usually on a very small time frame, such as two days.

  • Finally, it uses the runPercentRank() function to take a running percentage rank of this average ratio, and multiplies it by 100 to convert it to a 0-100 quantity.

Think about the way that students get percentile scores after taking a standardized test (that is, if a student got an 800 on her math section, she might be in the 95th percentile nationally). runPercentRank() does the same thing, except over time. This indicator provides the rank for the latest observation when taken in the context over some past period that the user specifies. For example, if something has a runPercentRank value of .90 when using a lookback period of 126, it means it’s in the 90th percentile when compared to itself and the past 125 observations.

# Declare the DVO function
DVO <- function(HLC, navg = 2, percentlookback = 126) {
  
  # Compute the ratio between closing prices to the average of high and low
  ratio <- Cl(HLC)/((Hi(HLC) + Lo(HLC))/2)
  
  # Smooth out the ratio outputs using a moving average
  avgratio <- SMA(ratio, n = navg)
  
  # Convert ratio into a 0-100 value using runPercentRank()
  out <- runPercentRank(avgratio, n = percentlookback, exact.multiplier = 1) * 100
  colnames(out) <- "DVO"
  return(out)
}
# Add the DVO indicator to our strategy
add.indicator(strategy = strategy.st, name = "DVO", 
              arguments = list(HLC = quote(HLC(mktdata)), navg = 2, percentlookback = 126),
              label = "DVO_2_126")
## [1] "firststrat"

Let us test what we have did so far

# Use applyIndicators to test out our indicators
test <- applyIndicators(strategy = strategy.st, mktdata = OHLC(SPY))

# Subset your data between Sep. 1 and Sep. 5 of 2013
test_subset <- test["2013-09-01/2013-09-05"]
test_subset
##            SPY.Open SPY.High  SPY.Low SPY.Close SMA.SMA200 SMA.SMA50
## 2013-09-03 157.1632 157.4961 155.7079  156.3642   147.9019  158.0697
## 2013-09-04 156.4022 157.9241 156.1169  157.6578   148.0556  158.2350
## 2013-09-05 157.7529 158.2761 157.6388  157.8576   148.2071  158.3756
##            rsi.RSI_3 RSI_avg.RSI_3_4 DVO.DVO_2_126
## 2013-09-03  53.20365        50.28074      23.01587
## 2013-09-04  75.54413        70.99256      65.07937
## 2013-09-05  77.97914        73.37544      73.80952

Signals

A signal is an interaction of market data with indicators, or indicators with other indicators, which tells you whether you may wish to buy or sell an asset.

Signals can be triggered for a variety of reasons. For example, a signal may be triggered by a shorter lookback moving average going from less than to greater than a longer lookback moving average. Another signal may be triggered when an oscillator goes from being above a certain set quantity (for example, 20) to below, and so on.

sigComparison

A sigComparison signal is a simple and useful way to compare two (hopefully related) quantities, such as two moving averages. Often, a sigComparison signal does not create a buy or sell signal by itself (as such a signal would involve buying or selling on every such day), but is most often useful as a filter for when another buy or sell rule should be followed.

We will use sigComparison() to generate a signal comparison that specifies that the 50-day simple moving average (SMA) must be above the 200-day simple moving average (SMA). We will label this signal longfilter, because it signals that the short-term average is above the long-term average.

# Add a sigComparison which specifies that SMA50 must be greater than SMA200, call it longfilter
add.signal(strategy.st, name = "sigComparison", 
           
           # We are interested in the relationship between the SMA50 and the SMA200
           arguments = list(columns = c("SMA50", "SMA200"), 
                            
                            # Particularly, we are interested when the SMA50 is greater than the SMA200
                            relationship = "gt"),
           
           # Label this signal longfilter
           label = "longfilter")
## [1] "firststrat"

sigCrossover

While having a long filter is necessary, it is not sufficient to put on a trade for this strategy. However, the moment the condition does not hold, the strategy should not hold any position whatsoever.

For this exercise, we will implement the opposite of the rule specified above using the sigCrossover() function.

As opposed to sigComparison(), which will always state whether or not a condition holds, sigCrossover() only gives a positive the moment the signal first occurs, and then not again. This is useful for a signal that will be used to initiate a transaction, as we only want one transaction in most cases, rather than having transactions fire again and again.

In this case, we will implement the sigCrossover() function specifying that the SMA50 crosses under the SMA200.

We will label this signal filterexit, as it will exit your position when the moving average filter states that the environment is not conducive for the strategy to hold a position.

# Add a sigCrossover which specifies that the SMA50 is less than the SMA200 and label it filterexit
add.signal(strategy.st, name = "sigCrossover",
           
           # We're interested in the relationship between the SMA50 and the SMA200
           arguments = list(columns = c("SMA50", "SMA200"),
                            
                            # The relationship is that the SMA50 crosses under the SMA200
                            relationship = "lt"),
           
           # Label it filterexit
           label = "filterexit")
## [1] "firststrat"

sigThreshold

The sigThreshold signal is mainly used for comparing an indicator to a fixed number, which usually has applications for bounded oscillators, or perhaps rolling statistical scores (for example, for a trading strategy that might choose to go long when a ratio of mean to standard deviation is at -2, or vice versa).

Whereas sigComparison and sigCrossover deal with quantities that are usually based off of an indicator that takes values in the same general area as prices,

sigThreshold exists specifically to cover those situations outside the bounds of indicators that take values similar to prices.

Furthermore, the sigThreshold() function takes the cross argument, which specifies whether it will function similarly to sigComparison (cross = FALSE) or sigCrossover (cross = TRUE), respectively.

We will implement a variant of sigThreshold that functions similarly to sigComparison.

Our job will be to implement a sigThreshold that checks whether or not DVO_2_126 is under 20. This signal will serve as one of the two switches that need to be “on” in order to enter into a long position in the strategy.

# Implement a sigThreshold which specifies that DVO_2_126 must be less than 20, label it longthreshold
add.signal(strategy.st, name = "sigThreshold", 
           
           # Use the DVO_2_126 column
           arguments = list(column = "DVO_2_126", 
                            
                            # The threshold is 20
                            threshold = 20, 
                            
                            # We want the oscillator to be under this value
                            relationship = "lt", 
                            
                            # We're interested in every instance that the oscillator is less than 20
                            cross = FALSE), 
           
           # Label it longthreshold
           label = "longthreshold")
## [1] "firststrat"

Next, we will implement a signal to exit a position given a certain threshold value of the DVO. While there are two entry signals that are both necessary but neither sufficient on its own, the two exit signals (this one and the one we implemented before) are both sufficient on their own (but neither necessary in the existence of the other) to exit a position.

We will again use sigThreshold(), this time counting when the DVO_2_126 crosses above a threshold of 80. To mimic a sigCrossover signal, set cross equal to TRUE Label this signal thresholdexit.

# Add a sigThreshold signal to your strategy that specifies that DVO_2_126 must cross above 80 and label it thresholdexit
add.signal(strategy.st, name = "sigThreshold", 
           
           # Reference the column of DVO_2_126
           arguments = list(column = "DVO_2_126", 
                            
                            # Set a threshold of 80
                            threshold = 80, 
                            
                            # The oscillator must be greater than 80
                            relationship = "gt", 
                            
                            # We are interested only in the cross
                            cross = TRUE), 
           
           # Label it thresholdexit
           label = "thresholdexit")
## [1] "firststrat"

sigFormula

The last signal function is a bit more open-ended. The sigFormula() function uses string evaluation to offer immense flexibility in combining various indicators and signals we already added to our strategy in order to create composite signals.

While such catch-all functionality may seem complicated at first, with proper signal implementation and labeling, a sigFormula signal turns out to be the simplest of logical programming statements encapsulated in some quantstrat syntactical structuring.

# Create dataset: test containing information about whether 
# longfilter is equal to 1 AND longthreshold is equal to 1.
test_init <- applyIndicators(strategy.st, mktdata = OHLC(SPY))
test <- applySignals(strategy = strategy.st, mktdata = test_init)

# Now, let's inspect the data set  on October 8, 2013. Are longfilter and longthreshold both equal to 1 on that date?
test["2013-10-08"]
##            SPY.Open SPY.High  SPY.Low SPY.Close SMA.SMA200 SMA.SMA50
## 2013-10-08 160.0033 160.2136 158.0535  158.1682   151.4826  160.0628
##            rsi.RSI_3 RSI_avg.RSI_3_4 DVO.DVO_2_126 longfilter filterexit
## 2013-10-08   17.8146        19.85266      3.174603          1         NA
##            longthreshold thresholdexit
## 2013-10-08             1             0

We approximated a sigFormula signal by comparing the value of two other signals. Next, we will take this one step futher by using the sigFormula() function to generate a sigFormula signal.

We want to enter into a position when both longfilter and longthreshold become true at the same time. The idea is this: We don’t want to keep entering into a position for as long as conditions hold true, but we do want to hold a position when there’s a pullback in an uptrending environment.

# Add a sigFormula signal to your code specifying that both longfilter and longthreshold must be TRUE, label it longentry
add.signal(strategy.st, name = "sigFormula",
           
           # Specify that longfilter and longthreshold must be TRUE
           arguments = list(formula = "longfilter & longthreshold", 
                            
                            # Specify that cross must be TRUE
                            cross = TRUE),
           
           # Label it longentry
           label = "longentry")
## [1] "firststrat"

Rules

Rules are the final mechanic in the trinity of quantstrat mechanics – indicators, signals, and rules. Rules are a way for you to specify exactly how you will shape your transaction once you decide you wish to execute on a signal.

To understand this command, we will explore each argument individually.

sigcol

First, add.rule() takes the argument sigcol, which specifies the signal column in your strategy. Like signals and indicators, all rules reference a column already present in your strategy. Rules relies on signals, and must therefore reference the signal columns in your strategy.

sigval

The next argument to specify in add.rule() is sigval, or the value that your signal should take to trigger the rule.

Remember that all signal outputs are either 1s or 0s. Effectively, a signal is either “on” or “off” at any given time. For our purposes, this is equivalent to two possible logical values: TRUE or FALSE. When specifying sigval in our add.rule() command, we need to indicate whether the rule is triggered when the signal value is TRUE or FALSE.

orderqty

The orderqty argument in the ruleSignal specifies exactly how much of an asset you want to buy or sell, in numbers of shares.

However, one salient feature of the exit rule type is that you can reduce your position to zero instantly with the all argument (hence, exiting).

ordertype

Next we specify the type of order you will execute ordertype.

While there are multiple types of orders in quantstrat, we will stick to market orders (ordertype = "market"). A market order is an order that states that you will buy or sell the asset at the prevailing price, regardless of the conditions in the market.

An alternative type of orders is a limit order, which specifies that the transaction will only take place if certain price conditions are met (namely, if the price falls below a certain further threshold on the day of the order). The mechanics of limit orders are outside the scope of this course.

orderside

The next critical argument to specify in our order is orderside, which can take two values: either long or short. In quantstrat, long and short side trades are siloed off separately so that quantstrat knows whether a trade is a long trade or a short trade.

A long trade -> is one that profits by buying an asset in the hopes that the asset’s price will rise. A short trade -> is one that sells an asset before owning it, hoping to buy it back later at a lower price.

For our strategy, you will want to take only long orders.

replace

In quantstrat, the replace argument specifies whether or not to ignore all other signals on the same date when the strategy acts upon one signal. This is generally not a desired quality in a well-crafted trading system. Therefore, for our exit rule, you should set replace to FALSE.

prefer

Lastly, of the basic rule arguments, there is the aspect of the prefer argument. In quantstrat, orders have a “next-bar” mechanism. That is, if you would gain a signal on Tuesday, the earliest that a position would actually fulfil itself would be on the Wednesday after. However, this can be solved by placing orders to execute on the next possible opening price, rather than wait for an entire day to pass before being able to actually purchase/sell the asset.

# Fill in the rule's type as exit
add.rule(strategy.st, name = "ruleSignal", 
         arguments = list(sigcol = "filterexit", sigval = TRUE, orderqty = "all", 
                        ordertype = "market", orderside = "long", 
                        replace = FALSE, prefer = "Open"), 
         type = "exit")
## [1] "firststrat"

enter rule

The opposite of an exit rule is an enter rule. On enter rules, orderqty cannot be set to “all” because there is no initial position on which to act. In this exercise, you will implement an enter rule that references the longentry signal in our strategy and will buy one share of an asset.

# Create an entry rule of 1 share when all conditions line up to enter into a position
add.rule(strategy.st, name = "ruleSignal", 
         
         # Use the longentry column as the sigcol
         arguments=list(sigcol = "longentry", 
                        
                        # Set sigval to TRUE
                        sigval = TRUE, 
                        
                        # Set orderqty to 1
                        orderqty = 1,
                        
                        # Use a market type of order
                        ordertype = "market",
                        
                        # Take the long orderside
                        orderside = "long",
                        
                        # Do not replace other signals
                        replace = FALSE, 
                        
                        # Buy at the next day's opening price
                        prefer = "Open"),
         
         # This is an enter type rule, not an exit
         type = "enter")
## [1] "firststrat"

order sizing

In quantstrat, the amount of an asset transacted may not always be a fixed quantity in regards to the actual shares. The constructs that allow quantstrat to vary the amount of shares bought or sold are called order sizing functions.

Using a pre-coded order sizing function is straightforward. The first thing to know is that when using an order sizing function, the orderqty argument is no longer relevant, as the order quantity is determined by the order sizing function. We can build our own sizing function in another tutorial.

Calling an order sizing function with our add.rule() call is fairly straightforward. The inputs for the order sizing function are mixed in with the rest of the inputs inside the arguments that we learnt in previous sections.

We will use the osFUN argument to specify a function called osMaxDollar.

The additional arguments to this function are tradeSize and maxSize, both of which should take tradesize, which you defined earlier.

# Add a rule that uses an osFUN to size an entry position
add.rule(strategy = strategy.st, name = "ruleSignal",
         arguments = list(sigcol = "longentry", sigval = TRUE, ordertype = "market",
                          orderside = "long", replace = FALSE, prefer = "Open",
                          
                          # Use the osFUN called osMaxDollar
                          osFUN = osMaxDollar,
                          
                          # The tradeSize argument should be equal to tradesize (defined earlier)
                          tradeSize = tradesize,
                          
                          # The maxSize argument should be equal to tradesize as well
                          maxSize = tradesize),
         type = "enter")
## [1] "firststrat"

Running our strategy

Our strategy uses three separate indicators and five separate signals. The strategy requires:

The strategy sells when the DVO_2_126 crosses above 80, or the SMA50 crosses under the SMA200.

For this strategy to work properly, We specified five separate signals:

The strategy invests $100,000 (our initeq) into each trade, and may have some small dollar cost averaging if the DVO_2_126 oscillates around 20 (though the effect is mostly negligible compared to the initial allocation).

Now, we need to run our strategy and fill in some more boilerplate code to make sure quantstrat records everything.

# Use applyStrategy() to apply our strategy. Save this to out
out <- applyStrategy(strategy = strategy.st, portfolios = portfolio.st)
## [1] "2003-11-04 00:00:00 SPY 1 @ 82.8469797716932"
## [1] "2003-11-04 00:00:00 SPY 1209 @ 82.8469797716932"
## [1] "2003-11-11 00:00:00 SPY 1 @ 82.1434940759158"
## [1] "2003-11-17 00:00:00 SPY 1 @ 82.0028035026122"
## [1] "2003-11-19 00:00:00 SPY 1 @ 81.3149484426093"
## [1] "2003-11-19 00:00:00 SPY 3 @ 81.3149484426093"
## [1] "2003-12-04 00:00:00 SPY 1 @ 83.7693256342774"
## [1] "2003-12-17 00:00:00 SPY 1 @ 84.4649932763959"
## [1] "2004-01-26 00:00:00 SPY 1 @ 89.8353171743056"
## [1] "2004-01-29 00:00:00 SPY 1 @ 89.1834821909869"
## [1] "2004-02-05 00:00:00 SPY 1 @ 88.8771986521788"
## [1] "2004-02-17 00:00:00 SPY 1 @ 90.9819163034757"
## [1] "2004-02-20 00:00:00 SPY 1 @ 90.6913435395237"
## [1] "2004-03-04 00:00:00 SPY 1 @ 90.8798241465669"
## [1] "2004-03-09 00:00:00 SPY 1 @ 90.3929094980755"
## [1] "2004-03-23 00:00:00 SPY 1 @ 86.8875342758494"
## [1] "2004-04-14 00:00:00 SPY 1 @ 88.7474405595549"
## [1] "2004-04-21 00:00:00 SPY 1 @ 88.424318232088"
## [1] "2004-04-29 00:00:00 SPY 1 @ 88.8341310699436"
## [1] "2004-05-10 00:00:00 SPY 1 @ 86.2491784573608"
## [1] "2004-05-20 00:00:00 SPY 1 @ 86.2570554723729"
## [1] "2004-06-07 00:00:00 SPY 1 @ 89.393678121629"
## [1] "2004-06-22 00:00:00 SPY 1 @ 89.4826958798143"
## [1] "2004-06-28 00:00:00 SPY 1 @ 90.5821474008193"
## [1] "2004-07-06 00:00:00 SPY 1 @ 88.8815616645231"
## [1] "2004-07-16 00:00:00 SPY 1 @ 88.2487814377903"
## [1] "2004-07-22 00:00:00 SPY 1 @ 86.5007334075964"
## [1] "2004-08-06 00:00:00 SPY 1 @ 85.1323481348304"
## [1] "2004-08-26 00:00:00 SPY -1238 @ 87.7662875332834"
## [1] "2004-11-11 00:00:00 SPY 1 @ 93.0719479103073"
## [1] "2004-11-11 00:00:00 SPY 1075 @ 93.0719479103073"
## [1] "2004-11-30 00:00:00 SPY 1 @ 94.0009986376769"
## [1] "2004-12-06 00:00:00 SPY 1 @ 94.9649063864107"
## [1] "2004-12-08 00:00:00 SPY 1 @ 94.168287753888"
## [1] "2004-12-28 00:00:00 SPY 1 @ 96.5580481208993"
## [1] "2005-01-03 00:00:00 SPY 1 @ 97.294398489347"
## [1] "2005-01-21 00:00:00 SPY 1 @ 94.2769618617021"
## [1] "2005-02-10 00:00:00 SPY 1 @ 95.7736780516635"
## [1] "2005-02-23 00:00:00 SPY 1 @ 95.1893962053046"
## [1] "2005-03-10 00:00:00 SPY 1 @ 97.0062602750755"
## [1] "2005-03-17 00:00:00 SPY 1 @ 95.493539652536"
## [1] "2005-03-23 00:00:00 SPY 1 @ 93.9723078282696"
## [1] "2005-04-04 00:00:00 SPY 1 @ 94.3017565079375"
## [1] "2005-04-12 00:00:00 SPY 1 @ 94.7276234295448"
## [1] "2005-04-15 00:00:00 SPY 1 @ 93.000042745613"
## [1] "2005-04-21 00:00:00 SPY 1 @ 92.2366959067077"
## [1] "2005-05-16 00:00:00 SPY 1 @ 92.9679009210566"
## [1] "2005-06-09 00:00:00 SPY 1 @ 96.2141448486945"
## [1] "2005-06-24 00:00:00 SPY 1 @ 96.7154112589483"
## [1] "2005-07-01 00:00:00 SPY 1 @ 96.3685007827881"
## [1] "2005-08-08 00:00:00 SPY 1 @ 99.3535484487066"
## [1] "2005-08-18 00:00:00 SPY 1 @ 98.4661038513446"
## [1] "2005-08-26 00:00:00 SPY 1 @ 98.0062457782951"
## [1] "2005-09-14 00:00:00 SPY 1 @ 99.8295386656661"
## [1] "2005-09-21 00:00:00 SPY 1 @ 98.6745977066603"
## [1] "2005-10-05 00:00:00 SPY 1 @ 98.2370873938375"
## [1] "2005-10-11 00:00:00 SPY 1 @ 96.4060274846891"
## [1] "2005-10-24 00:00:00 SPY 1 @ 95.9604191950539"
## [1] "2005-10-28 00:00:00 SPY 1 @ 95.9523155468221"
## [1] "2005-11-02 00:00:00 SPY 1 @ 97.362066768192"
## [1] "2005-11-30 00:00:00 SPY 1 @ 102.21518629736"
## [1] "2005-12-08 00:00:00 SPY 1 @ 102.263796033709"
## [1] "2005-12-19 00:00:00 SPY 1 @ 103.221294141165"
## [1] "2005-12-28 00:00:00 SPY 1 @ 102.41493735992"
## [1] "2006-01-03 00:00:00 SPY 1 @ 101.966966890824"
## [1] "2006-01-23 00:00:00 SPY 1 @ 102.797752086655"
## [1] "2006-02-01 00:00:00 SPY 1 @ 104.109094174989"
## [1] "2006-02-06 00:00:00 SPY 1 @ 102.985088997839"
## [1] "2006-02-09 00:00:00 SPY 1 @ 103.376044628943"
## [1] "2006-03-01 00:00:00 SPY 1 @ 104.744407256753"
## [1] "2006-03-07 00:00:00 SPY 1 @ 104.141674896911"
## [1] "2006-03-22 00:00:00 SPY 1 @ 105.905073244405"
## [1] "2006-04-10 00:00:00 SPY 1 @ 106.093160858006"
## [1] "2006-05-15 00:00:00 SPY 1 @ 105.316301199853"
## [1] "2006-05-18 00:00:00 SPY 1 @ 104.138764470378"
## [1] "2006-05-24 00:00:00 SPY 1 @ 102.77314585146"
## [1] "2006-06-06 00:00:00 SPY 1 @ 104.024282153016"
## [1] "2006-06-08 00:00:00 SPY 1 @ 102.691373819006"
## [1] "2006-06-13 00:00:00 SPY 1 @ 101.18673505819"
## [1] "2006-06-20 00:00:00 SPY 1 @ 101.855750672747"
## [1] "2006-07-14 00:00:00 SPY 1 @ 101.954308697139"
## [1] "2006-07-24 00:00:00 SPY 1 @ 102.208931642692"
## [1] "2006-07-26 00:00:00 SPY -1127 @ 103.974831564312"
## [1] "2006-10-03 00:00:00 SPY 1 @ 109.629383841193"
## [1] "2006-10-03 00:00:00 SPY 907 @ 109.629383841193"
## [1] "2006-11-02 00:00:00 SPY 1 @ 112.615748006849"
## [1] "2006-11-28 00:00:00 SPY 1 @ 114.042942918176"
## [1] "2006-12-08 00:00:00 SPY 1 @ 116.427087110254"
## [1] "2006-12-19 00:00:00 SPY 1 @ 117.424194661511"
## [1] "2006-12-22 00:00:00 SPY 1 @ 117.498851726851"
## [1] "2007-01-03 00:00:00 SPY 1 @ 118.00488404511"
## [1] "2007-01-18 00:00:00 SPY 1 @ 118.768077418128"
## [1] "2007-01-26 00:00:00 SPY 1 @ 118.270348993642"
## [1] "2007-02-13 00:00:00 SPY 1 @ 119.265818286011"
## [1] "2007-02-27 00:00:00 SPY 1 @ 119.3570706955"
## [1] "2007-03-06 00:00:00 SPY 1 @ 115.126310648685"
## [1] "2007-03-09 00:00:00 SPY 1 @ 117.225096157503"
## [1] "2007-03-14 00:00:00 SPY 1 @ 114.835959735187"
## [1] "2007-03-19 00:00:00 SPY 1 @ 115.982705480462"
## [1] "2007-05-01 00:00:00 SPY 1 @ 123.611615205391"
## [1] "2007-05-11 00:00:00 SPY 1 @ 124.719307549157"
## [1] "2007-05-16 00:00:00 SPY 1 @ 125.593802688286"
## [1] "2007-05-23 00:00:00 SPY 1 @ 127.384425479036"
## [1] "2007-06-07 00:00:00 SPY 1 @ 126.226764625787"
## [1] "2007-06-13 00:00:00 SPY 1 @ 125.34394514957"
## [1] "2007-06-19 00:00:00 SPY 1 @ 127.598882115112"
## [1] "2007-06-21 00:00:00 SPY 1 @ 126.369314887191"
## [1] "2007-06-26 00:00:00 SPY 1 @ 125.641616511166"
## [1] "2007-07-11 00:00:00 SPY 1 @ 126.09328810602"
## [1] "2007-07-18 00:00:00 SPY 1 @ 129.004094993156"
## [1] "2007-07-23 00:00:00 SPY 1 @ 128.962270497732"
## [1] "2007-07-30 00:00:00 SPY 1 @ 122.061642790437"
## [1] "2007-08-10 00:00:00 SPY 1 @ 120.773530637047"
## [1] "2007-08-29 00:00:00 SPY 1 @ 120.756798496846"
## [1] "2007-09-21 00:00:00 SPY 1 @ 128.338675286705"
## [1] "2007-10-22 00:00:00 SPY 1 @ 125.103100358823"
## [1] "2007-11-09 00:00:00 SPY 1 @ 122.439008592262"
## [1] "2007-11-16 00:00:00 SPY 1 @ 122.960058043351"
## [1] "2007-11-27 00:00:00 SPY 1 @ 119.119400451805"
## [1] "2007-12-05 00:00:00 SPY 1 @ 124.321514416482"
## [1] "2007-12-12 00:00:00 SPY 1 @ 126.951994914992"
## [1] "2007-12-18 00:00:00 SPY 1 @ 122.783579136498"
## [1] "2007-12-31 00:00:00 SPY -945 @ 124.280097755846"
## [1] "2009-06-23 00:00:00 SPY 1 @ 78.4042349612545"
## [1] "2009-06-23 00:00:00 SPY 1252 @ 78.4042349612545"
## [1] "2009-07-06 00:00:00 SPY 1 @ 77.9397869265973"
## [1] "2009-08-26 00:00:00 SPY 1 @ 90.1206115980536"
## [1] "2009-09-02 00:00:00 SPY 1 @ 87.4390790051487"
## [1] "2009-09-24 00:00:00 SPY 1 @ 93.693236148955"
## [1] "2009-10-02 00:00:00 SPY 1 @ 89.827866850157"
## [1] "2009-10-22 00:00:00 SPY 1 @ 95.2605114679059"
## [1] "2009-10-27 00:00:00 SPY 1 @ 94.2391372462444"
## [1] "2009-11-02 00:00:00 SPY 1 @ 91.6857065348007"
## [1] "2009-12-04 00:00:00 SPY 1 @ 98.4743046915605"
## [1] "2010-01-04 00:00:00 SPY 1 @ 99.4736408052207"
## [1] "2010-01-22 00:00:00 SPY 1 @ 98.4379128219799"
## [1] "2010-02-01 00:00:00 SPY 1 @ 95.7379564369319"
## [1] "2010-02-05 00:00:00 SPY 1 @ 94.3304323419573"
## [1] "2010-02-10 00:00:00 SPY 1 @ 94.7642009640224"
## [1] "2010-02-24 00:00:00 SPY 1 @ 97.4995675564178"
## [1] "2010-03-29 00:00:00 SPY 1 @ 104.149890602334"
## [1] "2010-04-19 00:00:00 SPY 1 @ 105.785430574844"
## [1] "2010-04-28 00:00:00 SPY 1 @ 105.820986603222"
## [1] "2010-05-03 00:00:00 SPY 1 @ 106.114311170825"
## [1] "2010-05-06 00:00:00 SPY 1 @ 103.341014734225"
## [1] "2010-05-17 00:00:00 SPY 1 @ 101.509920605588"
## [1] "2010-05-19 00:00:00 SPY 1 @ 99.3499458809687"
## [1] "2010-06-02 00:00:00 SPY 1 @ 96.0699887064951"
## [1] "2010-06-07 00:00:00 SPY 1 @ 95.2877711931047"
## [1] "2010-06-22 00:00:00 SPY 1 @ 99.5011087745894"
## [1] "2010-06-30 00:00:00 SPY 1 @ 92.8117283332394"
## [1] "2010-07-07 00:00:00 SPY 1 @ 92.1061725248666"
## [1] "2010-07-08 00:00:00 SPY -1280 @ 95.5625011815013"
## [1] "2010-11-16 00:00:00 SPY 1 @ 107.109157637633"
## [1] "2010-11-16 00:00:00 SPY 923 @ 107.109157637633"
## [1] "2010-12-08 00:00:00 SPY 1 @ 110.422369160711"
## [1] "2011-01-20 00:00:00 SPY 1 @ 115.498083952559"
## [1] "2011-01-31 00:00:00 SPY 1 @ 115.597378367367"
## [1] "2011-02-23 00:00:00 SPY 1 @ 118.918979991159"
## [1] "2011-03-02 00:00:00 SPY 1 @ 118.016369137336"
## [1] "2011-03-08 00:00:00 SPY 1 @ 118.819691894627"
## [1] "2011-03-11 00:00:00 SPY 1 @ 116.906161397577"
## [1] "2011-03-18 00:00:00 SPY 1 @ 116.797572842177"
## [1] "2011-03-29 00:00:00 SPY 1 @ 118.637831794623"
## [1] "2011-04-04 00:00:00 SPY 1 @ 120.958551774161"
## [1] "2011-04-11 00:00:00 SPY 1 @ 120.568749381283"
## [1] "2011-05-06 00:00:00 SPY 1 @ 122.327423177803"
## [1] "2011-05-17 00:00:00 SPY 1 @ 120.287726289774"
## [1] "2011-05-24 00:00:00 SPY 1 @ 120.061093302215"
## [1] "2011-06-02 00:00:00 SPY 1 @ 119.625962498762"
## [1] "2011-06-07 00:00:00 SPY 1 @ 117.577191225911"
## [1] "2011-06-14 00:00:00 SPY 1 @ 116.824767894152"
## [1] "2011-06-16 00:00:00 SPY 1 @ 115.183947783822"
## [1] "2011-07-12 00:00:00 SPY 1 @ 119.973048800428"
## [1] "2011-07-28 00:00:00 SPY 1 @ 118.980033831073"
## [1] "2011-08-03 00:00:00 SPY 1 @ 114.479562329674"
## [1] "2011-08-05 00:00:00 SPY 1 @ 110.92655812919"
## [1] "2011-08-18 00:00:00 SPY -946 @ 106.134558227509"
## [1] "2012-04-10 00:00:00 SPY 1 @ 127.684572214756"
## [1] "2012-04-10 00:00:00 SPY 782 @ 127.684572214756"
## [1] "2012-04-17 00:00:00 SPY 1 @ 127.582756840102"
## [1] "2012-04-20 00:00:00 SPY 1 @ 128.036299484924"
## [1] "2012-05-07 00:00:00 SPY 1 @ 126.351726666682"
## [1] "2012-05-14 00:00:00 SPY 1 @ 124.31544046206"
## [1] "2012-05-14 00:00:00 SPY 13 @ 124.31544046206"
## [1] "2012-06-01 00:00:00 SPY 1 @ 119.780075102502"
## [1] "2012-06-01 00:00:00 SPY 20 @ 119.780075102502"
## [1] "2012-06-12 00:00:00 SPY 1 @ 121.98295936455"
## [1] "2012-06-22 00:00:00 SPY 1 @ 123.861727654451"
## [1] "2012-07-11 00:00:00 SPY 1 @ 124.866541810285"
## [1] "2012-07-24 00:00:00 SPY 1 @ 125.778311277978"
## [1] "2012-08-01 00:00:00 SPY 1 @ 129.043946585048"
## [1] "2012-09-26 00:00:00 SPY 1 @ 134.755630628714"
## [1] "2012-10-10 00:00:00 SPY 1 @ 134.85850584264"
## [1] "2012-10-22 00:00:00 SPY 1 @ 133.895098068307"
## [1] "2012-11-06 00:00:00 SPY 1 @ 133.08134975726"
## [1] "2012-11-09 00:00:00 SPY 1 @ 128.722623115757"
## [1] "2012-11-14 00:00:00 SPY 1 @ 129.274489814414"
## [1] "2012-12-04 00:00:00 SPY 1 @ 132.295660023371"
## [1] "2012-12-13 00:00:00 SPY 1 @ 134.147645840393"
## [1] "2013-02-01 00:00:00 SPY 1 @ 141.909600999605"
## [1] "2013-02-06 00:00:00 SPY 1 @ 141.787152743591"
## [1] "2013-02-22 00:00:00 SPY 1 @ 142.380592060513"
## [1] "2013-02-26 00:00:00 SPY 1 @ 141.033564220192"
## [1] "2013-04-04 00:00:00 SPY 1 @ 147.063471254373"
## [1] "2013-04-16 00:00:00 SPY 1 @ 147.877178974727"
## [1] "2013-04-19 00:00:00 SPY 1 @ 146.183538133471"
## [1] "2013-05-22 00:00:00 SPY 1 @ 158.332379848031"
## [1] "2013-05-30 00:00:00 SPY 1 @ 156.449507491719"
## [1] "2013-06-03 00:00:00 SPY 1 @ 155.011322619894"
## [1] "2013-06-06 00:00:00 SPY 1 @ 152.522886139578"
## [1] "2013-06-12 00:00:00 SPY 1 @ 155.380328663185"
## [1] "2013-06-18 00:00:00 SPY 1 @ 155.673640018876"
## [1] "2013-06-20 00:00:00 SPY 1 @ 153.147363291049"
## [1] "2013-07-01 00:00:00 SPY 1 @ 153.387005257542"
## [1] "2013-07-25 00:00:00 SPY 1 @ 160.007211818472"
## [1] "2013-08-01 00:00:00 SPY 1 @ 161.690801185158"
## [1] "2013-08-16 00:00:00 SPY 1 @ 157.952663872359"
## [1] "2013-08-28 00:00:00 SPY 1 @ 155.289361824743"
## [1] "2013-09-23 00:00:00 SPY 1 @ 162.956791155278"
## [1] "2013-09-26 00:00:00 SPY 1 @ 161.838490292198"
## [1] "2013-10-09 00:00:00 SPY 1 @ 158.474020001439"
## [1] "2013-11-01 00:00:00 SPY 1 @ 168.242443485055"
## [1] "2013-11-08 00:00:00 SPY 1 @ 167.143248394764"
## [1] "2013-11-21 00:00:00 SPY 1 @ 171.062092912819"
## [1] "2013-12-12 00:00:00 SPY 1 @ 170.746672269862"
## [1] "2014-01-06 00:00:00 SPY 1 @ 176.334783551261"
## [1] "2014-01-14 00:00:00 SPY 1 @ 175.181566206922"
## [1] "2014-01-27 00:00:00 SPY 1 @ 172.077525367223"
## [1] "2014-02-04 00:00:00 SPY 1 @ 168.127794499155"
## [1] "2014-02-20 00:00:00 SPY 1 @ 176.123361524672"
## [1] "2014-02-25 00:00:00 SPY 1 @ 177.843554428629"
## [1] "2014-03-14 00:00:00 SPY 1 @ 177.641751099518"
## [1] "2014-03-24 00:00:00 SPY 1 @ 180.346233311664"
## [1] "2014-03-27 00:00:00 SPY 1 @ 178.328876673333"
## [1] "2014-04-07 00:00:00 SPY 1 @ 179.487166887251"
## [1] "2014-04-11 00:00:00 SPY 1 @ 175.809588194598"
## [1] "2014-05-15 00:00:00 SPY 1 @ 182.12227985073"
## [1] "2014-06-25 00:00:00 SPY 1 @ 188.4356398323"
## [1] "2014-07-18 00:00:00 SPY 1 @ 190.433580445319"
## [1] "2014-07-31 00:00:00 SPY 1 @ 189.715873303016"
## [1] "2014-08-07 00:00:00 SPY 1 @ 187.126326810436"
## [1] "2014-08-13 00:00:00 SPY 1 @ 188.4356398323"
## [1] "2014-09-05 00:00:00 SPY 1 @ 194.138468306807"
## [1] "2014-09-10 00:00:00 SPY 1 @ 193.420761164504"
## [1] "2014-09-23 00:00:00 SPY 1 @ 193.350670278239"
## [1] "2014-09-26 00:00:00 SPY 1 @ 191.664957946542"
## [1] "2014-10-02 00:00:00 SPY 1 @ 189.209459888323"
## [1] "2014-10-08 00:00:00 SPY 1 @ 188.420195856932"
## [1] "2014-10-13 00:00:00 SPY 1 @ 185.584696435725"
## [1] "2014-10-24 00:00:00 SPY 1 @ 190.252077324954"
## [1] "2014-12-02 00:00:00 SPY 1 @ 200.541765192034"
## [1] "2014-12-09 00:00:00 SPY 1 @ 199.138622748479"
## [1] "2014-12-12 00:00:00 SPY 1 @ 197.452910416782"
## [1] "2014-12-22 00:00:00 SPY 1 @ 202.569516040724"
## [1] "2015-01-02 00:00:00 SPY 1 @ 202.207002337762"
## [1] "2015-01-13 00:00:00 SPY 1 @ 199.992689728585"
## [1] "2015-01-29 00:00:00 SPY 1 @ 196.328322307559"
## [1] "2015-02-10 00:00:00 SPY 1 @ 201.717112335245"
## [1] "2015-03-09 00:00:00 SPY 1 @ 203.539503144607"
## [1] "2015-03-12 00:00:00 SPY 1 @ 201.109638934324"
## [1] "2015-03-25 00:00:00 SPY 1 @ 205.756791408288"
## [1] "2015-04-28 00:00:00 SPY 1 @ 207.400324285476"
## [1] "2015-05-06 00:00:00 SPY 1 @ 206.23901732594"
## [1] "2015-05-27 00:00:00 SPY 1 @ 207.902237191779"
## [1] "2015-06-05 00:00:00 SPY 1 @ 206.622835856603"
## [1] "2015-06-25 00:00:00 SPY 1 @ 208.765588957926"
## [1] "2015-07-10 00:00:00 SPY 1 @ 204.997708402383"
## [1] "2015-07-24 00:00:00 SPY 1 @ 207.974432668413"
## [1] "2015-08-07 00:00:00 SPY 1 @ 205.858098519164"
## [1] "2015-08-20 00:00:00 SPY 1 @ 204.226335890646"
## [1] "2015-09-02 00:00:00 SPY 1 @ 192.467819632196"
## [1] "2015-09-04 00:00:00 SPY -906 @ 190.717403784108"
## [1] "2015-12-10 00:00:00 SPY 1 @ 204.205136155999"
## [1] "2015-12-10 00:00:00 SPY 487 @ 204.205136155999"
## [1] "2015-12-21 00:00:00 SPY 1 @ 201.410004"
## [1] "2015-12-21 00:00:00 SPY 5 @ 201.410004"
# Update our portfolio (portfolio.st)
updatePortf(portfolio.st)
## [1] "firststrat"
daterange <- time(getPortfolio(portfolio.st)$summary)[-1]

# Update our account (account.st)
updateAcct(account.st, daterange)
## [1] "firststrat"
updateEndEq(account.st)
## [1] "firststrat"

Profit factor

One of the most vital statistics of any systematic trading strategy is the profit factor. The profit factor is how many dollars you make for each dollar you lose. A profit factor above 1 means your strategy is profitable. A profit factor below 1 means you should head back to the drawing board.

We will explore the profit factor in our strategy by creating an object called tstats that displays the trade statistics for our system.

# Get the tradeStats for your portfolio
tstats <- tradeStats(Portfolios = portfolio.st)

# Print the profit factor
tstats$Profit.Factor
## [1] 92.3031

Percent positive

While profit factor is one important statistic, it may be heavily influenced by only a few good trades. The percent positive statistic lets us know how many of our trades were winners. A trading system based on oscillation trading (such as ours!) will likely have a high percentage of winners. This is certainly a statistic you should look for in our own trade statistics.

tstats$Percent.Positive
## [1] 83.33333

Perfect! This percent positive statistic means that approximately 83.3333333 of our trades returned a positive result. That’s a great start.

chart.Posn

One of the most enlightening things about a trading system is exploring what positions it took over the course of the trading simulation, as well as when it had its profits and drawdowns. Looking at a picture of the performance can deliver a great deal of insight in terms of refining similar trading systems in the future.

In order to do this, we will use the chart.Posn() function. This generates a crisp and informative visualization of the performance of your trading system over the course of the simulation.

# Use chart.Posn to view our system's performance on SPY
chart.Posn(Portfolio = portfolio.st, Symbol = "SPY")

Adding an indicator

One of the more interesting things we can do with the chart.Posn() function is to superimpose indicators on top of it. This can help show what the strategy has actually been doing and why. However, in order to do this, we will need to recalculate the indicators outside the scope of your strategy. Once this is done, we simply add them to the chart.Posn plot.

Now, we will add the three indicators from your strategy to the chart.Posn plot we just created. The two moving averages (SMA50 and SMA200) will be superimposed on the price series, while the DVO_2_126 will have its own window.

# Compute the SMA50
sma50 <- SMA(x = Cl(SPY), n = 50)

# Compute the SMA200
sma200 <- SMA(x = Cl(SPY), n = 200)

# Compute the DVO_2_126 with an navg of 2 and a percentlookback of 126
dvo <- DVO(HLC = HLC(SPY), navg = 2, percentlookback = 126)

# Recreate the chart.Posn of the strategy from the previous exercise
chart.Posn(Portfolio = portfolio.st, Symbol = "SPY")

# Overlay the SMA50 on our plot as a blue line
add_TA(sma50, on = 1, col = "blue")

# Overlay the SMA200 on our plot as a red line
add_TA(sma200, on = 1, col = "red")

# Add the DVO_2_126 to the plot in a new window
add_TA(dvo)

The plot may look a bit complicated, but only because it contains so much vital information about your portfolio performance.

Cash Sharpe ratio

When working with cash profit and loss statistics, quantstrat offers a way to compute a Sharpe ratio not just from returns, but from the actual profit and loss statistics themselves. A Sharpe ratio is a metric that compares the average reward to the average risk taken. Generally, a Sharpe ratio above 1 is a marker of a strong strategy.

Now. we will see that because of trading P&L (profit and loss), one can compute a Sharpe ratio based on these metrics. The code below can be used to compute the Sharpe ratio based off of P&L.

portpl <- .blotter$portfolio.firststrat$summary$Net.Trading.PL
SharpeRatio.annualized(portpl, geometric=FALSE)
##                                 Net.Trading.PL
## Annualized Sharpe Ratio (Rf=0%)      0.5628211

As a Sharpe ratio above 1 is indicative of a strong strategy. The Sharpe ratio identified here is a bit low.

Returns Sharpe

One of the main reasons to include an initial equity (in this case, initeq, which is set to 100,000) in our strategy is to be able to work with returns, which are based off of our profit and loss over our initial equity.

Now, we will compute the standard returns-based Sharpe ratio as well.

# Get instrument returns
instrets <- PortfReturns(portfolio.st)

# Compute Sharpe ratio from returns
SharpeRatio.annualized(instrets, geometric = FALSE)
##                                 SPY.DailyEqPL
## Annualized Sharpe Ratio (Rf=0%)     0.5629072