Quantstrat Example in R - EMA Crossover Strategy

Our first quantstrat example case study is based on the Exponential Moving Average (EMA) Crossover.  

Let’s briefly review what moving averages and crossovers are.

Moving Averages

A moving average is the average price of a security over a set amount of time. Moving averages smooth the price data to form a trend following indicator (TFI). Once the day-to-day fluctuations are removed, traders are better able to identify the true trend and increase the probability that it will work in their favor.

EMA refers to Exponential Moving Average. This type of moving average reacts faster to recent price changes than a simple moving average. The 12- and 26-day EMAs are the most popular short-term averages. Exponential Moving Average shows the average value of the underlying data, most often the price of a security, for a given time period, attributing more weight to the latest changes and less to the changes that lie further away.

Note that moving averages do not predict price direction, but define the current direction with a lag (because of basing on past prices). Despite this lag, moving averages help smooth price action and filter out the noise. 

They also form the building blocks for many other technical indicators and overlays, such as Bollinger Bands, MACD and the McClellan Oscillator. 

These moving averages can be used to identify the direction of the trend or define potential support and resistance levels.

Crossovers

Crossovers are one of the main moving average strategies. A crossover is a signal of a trend reversal is when one moving average crosses another. Two moving averages can be used together to generate crossover signals. 

  • The two series chosen are such that one has relatively short moving average and the other has relatively long moving average. 5-day EMA and 35-day EMA would be deemed short-term, while a system using a 50-day SMA and 200-day SMA would be  long-term.
  • A bullish crossover occurs when the shorter moving average crosses above the longer moving average. This is also known as a golden cross. 
  • A bearish crossover occurs when the shorter moving average crosses below the longer moving average. This is known as a dead cross.

In the below chart, the red line represents the 10-period moving average and the blueline represents the 20-period moving average. Notice how the short-term average crossing the long-term average signals the beginning of an uptrend.

To start building our EMA Crossover strategy, we will first define some variables that the strategy will use, as well as get the historical data for the symbol.  

We will use the QQQ ETF that tracks the Nasdaq 100 Index, as the instrument and over it we will overlay 10-period EMA and 30-period EMA to observe the crossover.

Load the Quantstrat Package in R

#Load quantstrat in your R environment.
 
library(quantstrat) 

# The search command lists all attached packages.
search()

Loading quantstrat causes these other libraries to be loaded automatically: 

  • blotter 
  • foreach 
  • quantmod 
  • PerformanceAnalytics 
  • FinancialInstrument 
  • Defaults 
  • TTR 
  • xts 
  • zoo

Initialize Currency and Instruments

First, we specify the symbols and currencies that we are using. Then we define the stock metadata using the stock() function from the FinancialInstrument package.

symbolstring <- 'QQQ'
 
currency('USD')
 
stock(symbolstring,currency='USD',multiplier=1)

The FinancialInstrument's functions build and manipulate objects that are stored in an environment named ".instrument" at the top level of the package (i.e. "FinancialInstrument:::.instrument") rather than the global environment. 

You can see what you’ve created in this environment using the ls() function as shown below:

# Currency and trading instrument objects stored in the 
# .instrument environment
 
ls(envir=FinancialInstrument:::.instrument)
 
[1] "QQQ" "USD"
 
# blotter functions used for instrument initialization 
# quantstrat creates a private storage area called .strategy
 
ls(all=T)
 
[1] ".blotter"     ".strategy" 

Load Historical Data

We will now load the historical data. We need to create three date variables: initialization date, start date and end date. We also set the initial equity and also set the timezone.

# The initDate should be lower than the startDate. The initDate will be used later while initializing the strategy.
 
initDate <- '2010-01-01'
 
startDate <- '2011-01-01'
 
endDate <- '2019-08-10'
 
init_equity <- 50000
 
 
# Set UTC TIME
 
Sys.setenv(TZ="UTC")

The initDate variable needs a date that must occur before the start of data in a backtest. If this isn’t the case, the portfolio will demonstrate a massive drawdown on the initialization date, and many of the backtest statistics will be misleading or nonsensical.

The startDate and endDate variables are endpoints on the data that we will use to fetch for the symbol from yahoo.

We can now fetch the data using the getSymbols() function.

getSymbols(symbolstring,from=startDate,to=endDate,adjust=TRUE,src='yahoo')

Initialize Portfolio and Account

In quantstrat, we need to understand three main concepts: 1) Account, 2) Portfolio, and 3) Strategy.

An account may contain one more multiple portfolios. And each portfolio may contain one more multiple strategies. You can look at it this way:

Account -> Portfolio -> Strategy

In the quantstrat backtesting infrastructure, each strategy runs on a specific portfolio and account. These elements need to be defined and initialized in order to build and backtest a strategy. At first we need to assign names to the strategy, portfolio and account variables. These names could be any string names to identify these elements. We will use the name ‘FirstPortfolio’ for portfolio name, account name, as well as strategy name.

# Define names for portfolio, account and strategy. 
 
portfolioName <- accountName <- strategyName <- "FirstPortfolio"
 
# The function rm.strat removes any strategy, portfolio, account, or order book object with the given name. This is important
 
rm.strat(strategyName)

Once we have the strategy, portfolio and account names, we need to initialize and create objects for the portfolio and the account. These objects would hold the backtesting results as well as the positions, transactions and other aggregate variables of the backtesting. 

Initialization means to create the objects in the context of the Quantstrat package. For this purpose, it is necessary to provide the correct parameters for each of these objects. botter functions are used for portfolio and account initialization.

Initialize Portfolio

To create the portfolio object, we need to provide the portfolio name, stocks symbols or instruments that the portfolio will contain and the initialization date of the backtesting.

We use the following function to construct and initialize a portfolio object:

initPort(name=..., symbols,, initDate=...)
 
# name: is the string name of the portfolio
 
# symbols: tickers that the portfolio store
 
# initDate: initial data of the backtesting. Should be prior to 
 
# the date of the loaded prices.

Initialize Account

After we create the portfolio object, we need to define the account object. The account can be related to one or more than one portfolios. 

We use the following function to construct and initialize the account object:

initAcct(name=..., portfolios, initDate=..., initEq=...)
 
# name: is a string that identifies the account name
 
# portfolios: is a character vector of strings with the names of the portfolios included in the account, With this parameter we link the portfolio or portfolios to a specific account
 
# initDate: is the initial date of the backtesting and requires a date prior to the first close price given. This date will be the date that counts for the initial account equity and initial position
 
# initEq: is the initial equity of the portfolio.

Both the portfolio and account are initialized below:

initPortf(name = portfolioName,
          symbols = symbolstring,
          initDate = initDate)
 
initAcct(name = accountName,
         portfolios = portfolioName,
         initDate = initDate,
         initEq = init_equity)
 

Initialize Order and Strategy

We will now initialize the account object. The function initOrders sets up an order container (order book) for the portfolio. This is a quantstrat specific initialization.

You can check the parameters of initOrders function using the args(initOrders) command. 

initOrders(portfolio = portfolioName,
           symbols = symbolstring,
           initDate = initDate)

You can check that the order book is initialized successfully using the get.orderbook(portfolioName) command.

Finally, the strategy object is created with the following code:

strategy(name, assets, constrains, store)
 
# name: the string name of the strategy
 
# assets: optional list of assets to apply the strategy to.  
 
# Normally these are defined in the portfolio object
 
# contstrains: optional portfolio constraints
 
# store: can be True or False. If True store the strategy in the environment. Default is False

We create the strategy object below. The strategy name should be passed as the first parameter when we generate the indicators, signals and rules of the strategy in order to link all these elements to the strategy defined. 

strategy(strategyName, store = TRUE)

View Created Objects

Let’s ensure that all the objects are created successfully. We should have the portfolio, account, orderbook and strategy objects.

ls(all=T)
 [1] ".blotter" ".strategy" "accountName"   
 [4] "endDate"  "init_equity"   "initDate"
 [7] "portfolioName"    "startDate" "strategyName"         
 [10] "symbolstring"  
 
# .blotter holds the portfolio and account object 
  
ls(.blotter)
[1] "account.FirstPortfolio"   "portfolio.FirstPortfolio"
 
# .strategy holds the orderbook and strategy object
 
ls(.strategy)
[1] "FirstPortfolio"            "order_book.FirstPortfolio"

Adding Indicators

Once we have initialized the objects, the next step is to create the indicators that the strategy would use. For this purpose, quantstrat has a full list of technical indicators which are calculated from the market data of the symbol that is loaded in the environment.

The indicators are path independent, which means that they do not depend on account / portfolio characteristics, current positions, or trades. 

Since we are creating an EMA crossover strategy, we will add two indicators: 10-period EMA and 30-period EMA

In quantstrat, we use the add.indicator() function to add indicators. The parameters of the add.indicator() function are: strategynamearguments and label.  strategy is the string name of the strategy (defined in the Initialization step) to add the indicator to; name is the indicator function which in this case is “EMA”, but could be RSI, MACD, etc. The arguments parameter has a list with the required inputs to calculate the indicator passed in the name argument. 

add.indicator(strategy = strategyName, 
              name = "EMA", 
              arguments = list(x = quote(Cl(mktdata)), 
                               n = 10), label = "nFast")
 
add.indicator(strategy = strategyName, 
              name = "EMA", 
              arguments = list(x = quote(Cl(mktdata)), 
                               n = 30), 
              label = "nSlow")

Our strategy should now have two indicators. You can confirm this by executing this function: summary(getStrategy(strategyName)).

When we calculate an Exponential Moving Average we need the close price and the lookback period n for the calculation. Observe that we use the mktdata object to get the close prices. This is different from the common procedure in R to retrieve closes price column of a dataframe with the “character(dataframe” character (dataframeClose).

mktdata is an internal object that has OHLCV information loaded in the environment (when we get the data using getSymbols), and every time we add an indicator or a signal, a new column is generated in the mktdata object. The label argument would be the column name in the mktdata object with the indicator values. Both signals and indicators are created in a vectorized form and just as indicators, signals are path independent too.

Adding Signals

The next step is to add signals which are generated from the interaction of indicators and market data. Examples include the point at which the fast Moving Average and the slow Moving Average cross each other (or in other cases when the RSI or MACD are greater than or lower than a defined threshold).

In this example we generate a long trade when the fast EMA (10-period EMA) crosses up the slow EMA (30-period EMA), and a short trade when the fast EMA cross down the slow EMA.

In quantstrat, a signal is added using the add.signal() function which takes the following form:

add.signal(strategy, name, arguments, label) 

The important parameters are strategynamearguments and label.  

strategy: This is the strategy name that we define in the Initialization step. In our example it is stored in the variable strategyName.

name: The name parameter accepts built-in functions that specify the signal type (crossovers, threshold, etc.). It is also possible to create custom functions. We should pass the string name of the function as the parameter value.  Quantstrat has the following built in functions to generate signals: sigComparisonsigCrossoversigFormulasigPeaksigThreshold.  

arguments: These are the arguments to be passed to the indicator function

label: When we create indicators and signals, we must pass a label parameter. In quantstrat labels are logical links between indicators, signals and rules, so is important to create smart labels with an appropriate name of indicators and signals. 

The logic of the signals above is given by the combination of the function on the name parameter, and the arguments parameter that has a list containing the mktdata columns (for the indicators), and the relationship(("gt", "lt", "eq", "gte", "lte") that these columns should hold for creating the new signal in a specific moment of time.

A crossover signal is created using the sigCrossover() function.

Below is the specific code to generate these signals:

# Add long signal when the fast EMA crosses over slow EMA.
 
add.signal(strategy = strategyName,
           name="sigCrossover",
           arguments = list(columns = c("nFast", "nSlow"),
                            relationship = "gte"),
           label = "longSignal")
 
# Add short signal when the fast EMA goes below slow EMA.
 
add.signal(strategy = strategyName, 
           name = "sigCrossover",
           arguments = list(columns = c("nFast", "nSlow"),
                            relationship = "lt"),
           label = "shortSignal")

The first signal indicates that a new column labeled longSignal will be created in the mktdata object. The value of longSignal would be true or have the value of 1 when the nFast column (fast EMA)  is greater or equal (“gte”) than the nSlow column (slow EMA). Every time that fast EMA crosses up the slow EMA, the longSignal column will have the value of 1.

The second signal means that the shortSignal column will have the value of 1 when the nFast column (fast EMA)  crosses below (“lt”) the nSlow (slow EMA) column. Every time the fast EMA crosses below the slow EMA, the shortSignal will have the value of 1 and in other case it would have a value of 0. 

Is important to indicate that if we use the sigCrossover function for the signal generation, both columns (shortSignal and longSignal) would have a value of 1 only when the fast EMA crosses up or down the slow EMA. However, if  we use the sigComparison function, the columns longSignal and shortSignal are 1 every time the fast EMA is higher than the slow EMA (for long signal) or every time the fast EMA is lower than the slow EMA (for the slow signal). 

To get the crossover behavior while using the sigComparison function, we can add the parameter Cross = True within the list of the arguments parameter. 

The built-in functions are explained in some more details below:

The sigComparison function compares two columns on the mktdata object, and will return TRUE (1) as long as the specified relationship comparing the first column to the second column (passed as parameters) holds.

The sigCrossover is similar to the above function but only returns TRUE on the timestamp (bar) that the relationship moves from FALSE to TRUE. In the EMA crossover setup, the sigCrossover is only TRUE in the crossover, while the sigComparison is TRUE as long as the condition is achieved.

sigThreshold uses a threshold value to be compared with the indicator. This is useful when we generate signals with indicators based on threshold values such as RSI, MACD, and other oscillators. When the indicator crosses a certain value (threshold) the function returns TRUE .

sigFormula apply a formula to multiple variables. With this function, signals can be generated by chaining conditions with the amp character “&” or evaluate that any of both conditions are True with the “|” character. 

sigPeak identifies local minima or maxima of an indicator (peak/valley signals).

Adding Rules

After indicators and signals are created, the next important step is the generation of the strategy rules. Rules use market data, indicators, signals, and current account / portfolio properties to generate orders. The strategy rules are path dependent because they are related with the portfolio, and with the orders and risk management of the strategy. 

It is not common to send naïve orders, so the rules have to interact with the portfolio and previous orders, to enable different trade sizes and manage risk with different order types. The package provides functionality to simulate and optimize order types. 

In quantstrat, the add.rule() function is used to generate rules and determines the positions we take based on our signals, the types of orders we place, the trade size, and transactions cost of the broker. Overall, the rules allow functionality to enter positions (long and short) and to exit positions (long and short).

The following rule creates a long order to buy 100 shares when the value of the longSignal column in the mktdata object is TRUE.

# go long when 10-period EMA (nFast) >= 30-period EMA (nSlow)
 
add.rule(strategyName,
         name= "ruleSignal",
         arguments=list(sigcol="longSignal",
                        sigval=TRUE,
                        orderqty=100,
                        ordertype="market",
                        orderside="long",
                        replace = TRUE, 
                        TxnFees = -10),
         type="enter",
         label="EnterLong") 

The above rule establishes that when the longSignal column on the mktdata object is TRUE, the strategy will send a market order with an order quantity of 100. 

Rules have just one function in the name parameter which is called “ruleSignal”. Even though the add.rule() has only the ruleSignal function, this function has many different arguments that can take in one of several values. We can customize many features with the add.rule() functions, such as the trade size, the preferred price (open, high, close, low) to trigger and order, transactions fees, stop loss and take profit among others.

The ordertype parameter specifies the type of the order (market, limit, stop loss, stop limit, trailing stop). The replace command tells that this order would replace any open order if it is equal to TRUE. The above rule would create a new column in the mktdata object called EnterLong.

The following rule creates a short order to sell 100 shares when the value of the shortSignal column on the mktdata object is TRUE.

# go short when 10-period EMA (nFast) < 30-period EMA (nSlow)
 
add.rule(strategyName, 
         name = "ruleSignal", 
         arguments = list(sigcol = "shortSignal", 
                          sigval = TRUE, 
                          orderside = "short", 
                          ordertype = "market", 
                          orderqty = -100, 
                          TxnFees = -10,                     
                          replace = TRUE), 
         type = "enter", 
         label = "EnterShort")
 

Next we should define rules to close both long and short positions. To close a long position we should use the shortSignal column of the mktdata object and pass the order information in the add.rule() function:

# Close long positions when the shortSignal column is True
 
add.rule(strategyName, 
         name = "ruleSignal", 
         arguments = list(sigcol = "shortSignal", 
                          sigval = TRUE, 
                          orderside = "long", 
                          ordertype = "market", 
                          orderqty = "all", 
                          TxnFees = -10, 
                          replace = TRUE), 
         type = "exit", 
         label = "ExitLong")

In the rule, we should always specify the ordertype, orderside, orderqty and the sigcol (column name) in which the rule is created. The orderside can be long or short, ordertype in this case simulates a market order (quantstrat supports different order types).

In quantstrat, market orders are executed at the next bar after receiving the signal. This can cause some gaps with daily data, as the signal is generated in the current bar, and the order bar is the next bar of the signal. However with intraday data, the open of the next bar should be very similar to the close of the current bar. When using monthly data, quantstrat uses current bar execution. 

In the rule above we pass the “exit” value to the type parameter to specify that this order is to exit a current position. We label the rule with the “ExitLong” string, and the orderside is “long” because this order is to close a long position.

The orderside parameter is very important to maintain coherence in the order process.  We should group with "long" those orders which are for buy and sell a long position. And we should group with “short” in the orderside parameter, those orders which are placed to enter and liquidate a short position.

This scheme separates rule executions, such that long sells (orders that are placed to close a long position) won’t work on short positions and vice versa.

The orderqty parameter can be a flat value like 100 shares or 50 shares, but when the order size has a custom function (OsFUN different from Null), the orderqty parameter doesn’t apply. 

The type argument generally has the value “enter” and “exit”. There are other rules types such as “chain” for stop loss orders. When the rule type is “exit”, the string “all” in the orderqty parameter, means to liquidate the position. 

The label argument in the add.rules() can be used for deeper analysis and sometimes is not used because the rule contain the last point of the strategy logic. 

Below, we create the rule for closing a short position has the orderside “short” and is triggered when the longSignal column is TRUE. 

# Close Short positions when the longSignal column is True
 
add.rule(strategyName, 
         name = "ruleSignal", 
         arguments = list(sigcol = "longSignal", 
                          sigval = TRUE, 
                          orderside = "short", 
                          ordertype = "market", 
                          orderqty = "all", 
                          TxnFees = -10, 
                          replace = TRUE), 
         type = "exit", 
         label = "ExitShort")

View Strategy Object

We have now created everything required for our strategy. The strategy object now contains a complete set of quantitative trading rules ready to be applied to a portfolio.

We can review the strategy using the getStrategy and summary command.

summary(getStrategy(strategyName))

# Summary results are produced below

            Length Class  Mode     
name        1      -none- character
assets      0      -none- NULL     
indicators  2      -none- list     
signals     2      -none- list     
rules       3      -none- list     
constraints 0      -none- NULL     
init        0      -none- list     
wrapup      0      -none- list     
trials      1      -none- numeric  
call        3      -none- call

Apply Strategy to Portfolio

To run the strategy we should use the applyStrategy() method. The applyStrategy function applies the strategy to arbitrary market data. The function takes three main arguments:

  • strategy: an object of type ’strategy’ 
  • portfolios: a list of portfolios to apply the strategy to 
  • symbols: the symbol of the instrument

For this example we pass the strategy and portfolio names, and the symbol that is loaded in the environment.

Calling the applyStrategy generates transactions in the specified portfolio.

results <- applyStrategy(strategy= strategyName, portfolios = portfolioName,symbols=symbolstring)
 
# The applyStrategy() outputs all transactions(from the oldest to recent transactions)that the strategy sends. The first few rows of the applyStrategy() output are shown below
 
[1] "2014-03-27 00:00:00 QQQ -100 @ 86.879997"
[1] "2014-05-14 00:00:00 QQQ 100 @ 87.830002"
[1] "2014-10-03 00:00:00 QQQ -100 @ 98.169998"
[1] "2014-10-29 00:00:00 QQQ 100 @ 99.809998"
[1] "2015-01-07 00:00:00 QQQ -100 @ 101.360001"
[1] "2015-01-27 00:00:00 QQQ 100 @ 101.440002"
[1] "2015-01-28 00:00:00 QQQ -100 @ 100.919998"
[1] "2015-02-06 00:00:00 QQQ 100 @ 103.129997"
[1] "2015-04-01 00:00:00 QQQ -100 @ 105.050003"
[1] "2015-04-13 00:00:00 QQQ 100 @ 107.480003"
[1] "2015-06-16 00:00:00 QQQ -100 @ 108.800003"
 

You can view the transactions table using the getTxns() function.

getTxns(Portfolio=portfolioName, Symbol=symbolstring)
 
           Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Net.Txn.Realized.PL
2010-01-01       0      0.00        0         0         0.00              0.0000
2014-03-27    -100     86.88      -10     -8688        86.88            -10.0000
2014-05-14     100     87.83      -10      8783        87.83           -105.0005
2014-10-03    -100     98.17      -10     -9817        98.17            -10.0000
2014-10-29     100     99.81      -10      9981        99.81           -174.0000
2015-01-07    -100    101.36      -10    -10136       101.36            -10.0000
2015-01-27     100    101.44      -10     10144       101.44            -18.0001
2015-01-28    -100    100.92      -10    -10092       100.92            -10.0000
2015-02-06     100    103.13      -10     10313       103.13           -230.9999
2015-04-01    -100    105.05      -10    -10505       105.05            -10.0000
2015-04-13     100    107.48      -10     10748       107.48           -253.0000
2015-06-16    -100    108.80      -10    -10880       108.80            -10.0000
2015-06-19     100    109.89      -10     10989       109.89           -118.9996
2015-06-30    -100    107.07      -10    -10707       107.07            -10.0000
2015-07-17     100    113.59      -10     11359       113.59           -661.9996
2015-08-21    -100    102.40      -10    -10240       102.40            -10.0000
2015-10-13     100    106.10      -10     10610       106.10           -379.9996
2015-12-21    -100    111.05      -10    -11105       111.05            -10.0000
2015-12-30     100    113.27      -10     11327       113.27           -231.9994
2016-01-05    -100    109.31      -10    -10931       109.31            -10.0000
2016-03-02     100    105.83      -10     10583       105.83            337.9996
2016-05-02    -100    106.72      -10    -10672       106.72            -10.0000
2016-05-27     100    110.13      -10     11013       110.13           -350.9996
2016-06-20    -100    107.16      -10    -10716       107.16            -10.0000
2016-07-11     100    110.93      -10     11093       110.93           -386.9996
2016-11-02    -100    115.18      -10    -11518       115.18            -10.0000
2016-11-22     100    118.90      -10     11890       118.90           -382.0002
2016-12-05    -100    116.60      -10    -11660       116.60            -10.0000
2016-12-08     100    118.57      -10     11857       118.57           -207.0002
2017-07-03    -100    136.19      -10    -13619       136.19            -10.0000
2017-07-14     100    142.12      -10     14212       142.12           -602.9993
2017-08-28    -100    142.41      -10    -14241       142.41            -10.0000
2017-08-30     100    144.65      -10     14465       144.65           -233.9990
2018-02-09    -100    156.10      -10    -15610       156.10            -10.0000
2018-02-21     100    164.82      -10     16482       164.82           -882.0001
2018-03-26    -100    164.40      -10    -16440       164.40            -10.0000
2018-04-20     100    162.30      -10     16230       162.30            199.9991
2018-04-23    -100    161.89      -10    -16189       161.89            -10.0000
2018-05-08     100    166.07      -10     16607       166.07           -428.0008
2018-10-09    -100    179.63      -10    -17963       179.63            -10.0000
2019-01-17     100    163.63      -10     16363       163.63           1590.0000
2019-05-15    -100    183.09      -10    -18309       183.09            -10.0000
2019-06-17     100    183.74      -10     18374       183.74            -75.0009
 

Executing the applyStrategy also creates a special variable called mktdata. It is a time series object which contains the historic price data as well as the calculated indicators, signals, and rules. It is helpful to look at what the mktdata object contains.

mktdata["2019"]
 
           QQQ.Open QQQ.High QQQ.Low QQQ.Close QQQ.Volume QQQ.Adjusted EMA.nFast EMA.nSlow longSignal shortSignal
2019-01-02   150.99   155.75  150.88    154.88   58576700     154.2576  154.0980  159.4152         NA          NA
2019-01-03   152.60   153.26  149.49    149.82   74820200     149.2179  153.3202  158.7962         NA          NA
2019-01-04   152.34   157.00  151.74    156.23   74709300     155.6022  153.8492  158.6306         NA          NA
2019-01-07   156.62   158.86  156.11    158.09   52059300     157.4547  154.6203  158.5957         NA          NA
2019-01-08   159.54   160.11  157.20    159.52   49388700     158.8789  155.5111  158.6554         NA          NA
2019-01-09   160.14   161.52  159.47    160.82   46491700     160.1737  156.4764  158.7950         NA          NA
2019-01-10   159.60   161.37  158.70    161.28   38943400     160.6319  157.3498  158.9553         NA          NA
2019-01-11   160.33   160.86  159.79    160.69   30176600     160.0442  157.9571  159.0672         NA          NA
2019-01-14   159.33   159.96  158.59    159.27   30710200     158.6299  158.1958  159.0803         NA          NA
2019-01-15   160.00   162.60  159.91    162.38   40874200     161.7274  158.9566  159.2932         NA          NA
2019-01-16   162.65   163.78  162.29    162.35   33812200     161.6976  159.5736  159.4904          1          NA
2019-01-17   161.83   164.36  161.57    163.63   39329000     162.9724  160.3111  159.7575         NA          NA
2019-01-18   164.78   166.02  163.82    165.25   57183200     164.5859  161.2091  160.1118         NA          NA
2019-01-22   164.06   164.14  160.76    161.94   56711700     161.2892  161.3420  160.2298         NA          NA
2019-01-23   162.77   163.51  160.32    162.15   38106100     161.4984  161.4889  160.3537         NA          NA
2019-01-24   162.68   163.44  162.06    163.20   32417800     162.5441  161.8000  160.5373         NA          NA
2019-01-25   164.51   165.65  163.95    165.15   36515900     164.4863  162.4091  160.8349         NA          NA
2019-01-28   163.02   163.12  161.75    163.11   33491200     162.4545  162.5365  160.9817         NA          NA
2019-01-29   163.20   163.24  160.99    161.57   30784200     160.9207  162.3608  161.0196         NA          NA
2019-01-30   163.40   166.28  162.89    165.68   41346500     165.0142  162.9643  161.3203         NA          NA
2019-01-31   166.70   168.99  166.47    168.16   37258400     167.4842  163.9090  161.7616         NA          NA
 

Update Portfolio, Account and Equity

After executing the strategy, we should update the portfolio, account and the final equity for the account. 

updatePortf(portfolioName)
 
dateRange <- time(getPortfolio(portfolioName)$summary)[-1]
 
updateAcct(portfolioName,dateRange)
 
updateEndEq(accountName)
 

updatePortf() calculates the Profit & Loss for each symbol in symbols.

updateAcct() calculates the equity from the portfolio data. 

updateEndEq() updates the ending equity for the account. 

The three functions must be called in order.