Trade management is where professional traders make their money. In our first installment, we showed that performance of one-lot trades varies significantly when filtered based on risk as opposed to volatility. When professional traders do trade management with large accounts of several million dollars, they are sizing to maximize risk-adjusted returns and do not need to filter out trades to limit risk. Although most individual traders aren’t moving millions of dollars through the market with each trade, they can still benefit from these concepts.
When designing trade management tools, there is a right way and a wrong way. The wrong way is to create a list of trades and then apply the trade management rules to them. This is incorrect because the real life trade might not be the same due to the trade management rules. Assume we are willing to risk 5% of our account on a given trade and have a $100,000 account. If the risk on a given trade is $5,500, we would skip this trade. However, say that four days later we get another buy signal with a risk on the trade of $4,600. Now, we would buy, but the entry date would not match our original sequence without the risk management overlay. The trades would be different.
It seems simple when stated that way, but most trading software puts the trades on first, and then applies the overlay or risk management strategies. Examples of software that integrate risk management with trade selection are Trading Blox, Mechanica and TradersStudio. (Disclosure: TradersStudio was designed by the author.)
Understanding the process
Here’s how it works. The software processes the trades for the next time period and then passes control to the trade-management engine, which can modify orders or even place new orders. Next, control is then passed back to the system to execute for the next time slice.
We can match intraday with daily. For example, if we have two systems, a 15 minute and a daily, the 15-minute one would run on one 15-minute bar at a time, with risk management modifications, and then at the end of the day we’d run the daily system for a given bar. This means that the systems and markets run in parallel. Looking at how this is done, we will first examine a simple example, built for TradersStudio (see “Percent margin plan,” below).
Dim S As Integer
Dim M As Integer
If tradeplan.MarketType<>0 And tradeplan.MarketType<>4 Then
MsgBox("This trade plan can only be run on Commodities,Futures")
MsgBox "not enough money"
If Tradeplan.SummEquity< Tradeplan.TotalMarginForPlan Then
MsgBox "Margin Call Account below minimum margin required"
' Loop though all the sessions
For S = 0 To TradePlan.SessionCount - 1
TradePlan.Session(S).UnitSize = 1
' For each session Loop though the trading plans.
For M = 0 To TradePlan.Session(S).MarketCount - 1
If DollarsPerMarket>TradePlan.Session(S).Market(M).Margin Then
Under this plan, we stop trading if our account size drops below the margin of a one lot. We divide our money evenly across different systems and markets after seeing how many markets we have across all the sessions. We set the unit size to one contract so we minimize size granularity and can add units quicker. Control is given to this code after each system is run in parallel for the minimum time frame of any of the systems. If the smallest time frame of any system is five-minute bars, we will run that system for a five-minute bar and not run the daily systems until the end of the day.
Once we get control back, we loop through each session and each market in each session. The EntryNumUnits is an object at the market level. If we set EntryNumUnits to zero, we override the size, which effectively skips the trade. If we have enough money to trade that market, then we size the same by dividing the money per market by margin. We use the Min function so we can set a ceiling. Otherwise, if we run a simulation for 20 years, it’s possible that we could buy more contracts than we could actually trade, so this allows us to set a maximum number of contracts. We also have an ExitNumUnits, which we can set for each market. In our case, we set it to the number of contracts held:
Let’s see how this works for each market for which we are setting EntryNumUnits. We have a $250,000 account and are trading 10 markets. This means DollarsPerMarket will be $25,000. If we are using a percent margin of 25%, then we would multiply $25,000 by 0.25, giving us $6,250 per market. If we are trading the 10-year Treasury note with a margin of $2,800, we would trade a floor of $6,250/$2,800, or two contracts. SummEquity object gives us the current account size, including both closed and open profits. Using open profits in futures can cause us to get too much leverage during big trends. We could adjust for this easily, using the code fragment below:
If tradeplan.OpenPandL>0 then
DollarsPerMarket=(tradeplan.SummEquity- .25*tradeplan.OpenPandL )/NumberOfMarkets
If we replace DollarsPerMarket = tradeplan.SummEquity/NumberOfMarkets with the code above, we would use only 25% of the open profit toward reinvestment. A trend following system often can give back 50% or more of open profits. Open profits can, in rare cases, be almost as much as closed profits for 10 years if we have monster trends. This can create a volatile equity curve.
The percentage margin plan is a simple sizing algorithm. The problem is that margin is not changed often and may not be a good measure of current volatility. Also, margins change over time and historical margin data may or may not be available for your market. A solution is to use dynamic margin instead. It’s based on current volatility (find the code online at futuresmag.com/systemcodes).
We use an average true range times the value of a full point in the market. Finally, we multiply by a constant MarginEST, for margin estimate, we normally use a value between 5.0 and 8.0 for this, and replace margin with the value returned from this function. Dynamic margin performs better than standard margin as it reduces risk by adjusting more quickly to market conditions than the exchanges.
Trade plan concepts
Another classic trade plan solution uses ranking. There are two ways to accomplish this with an account of $100,000 or less. One is to create a small portfolio that can be traded within that account size. Another is to trade a larger portfolio but use ranking to take only a given number of positions. For example, you can add 15 markets in a portfolio to trade a trend-following system and only trade the five with the highest average directional index (ADX) values and limit trades to five positions. In addition, you can exit positions if the ADX drops below the top five (the code for this is at futuresmag.com/systemcodes).
This code lets us set ADX ranking both ways. If we use -1 as mmode then we subtract 50 from ADX, we will now rank from lowest ADX to highest. If mmode is not -1, it will select MaxPosition, starting at highest ADX values. LB is the period used in the ADX calculations. We can do ranking based on any objective function we want. We can create a function that produces an array of three columns: symbol, raw ranking value and a true/false column. We can then set that to IsActiveOrder values because we only want to rank functions that have a valid active order:
This function gives us a global ranking regardless of active orders, as well as a ranking within active orders. Our logic in the trade plan code shown earlier will add one or two positions per day, depending on how many positions we have left. If we have one position left, then we will only take the top ranked active order. If we have two positions left, then we will take the top two active orders. The global rank is the rank among all markets, not just ones with active orders. The iActiveOrderCount is the rank within active orders. We can set a property to make the rank be returned relative or ordinal. Ordinal is used in this example.
Real world hurdles
In actual trading, real results don’t always reflect what one-lot trade simulations imply. We’ll demonstrate by starting with a classic triple moving average crossover system. We will use the period Jan. 3, 1991, to June 11, 2010. We will also use $100 slippage and commission and the following markets: cotton, dollar index, Japanese yen, coffee, natural gas, crude oil and the 10-year T-note. Results optimized on a one-lot basis are shown in “First run” (below).
We will use our dynamic margin with a $100,000 starting account. Other parameters include: percent margin, 30%; average true range, 20 days; and margin estimate multiplier, seven. In addition, we set our max unit ceiling high so it will not get hit and affect our results.
We then optimize our system parameters from within the trade plan. We often get different results because one lot results are often different than results adjusted for equal-dollar risk, such as our dynamic margin calculations. This is also because trading a one lot is not risk balanced. Trading a one lot of cotton is not the same risk as a one lot of natural gas. In addition, we have adjustments based on trades that are skipped because we do not have enough money to take a given trade. In fact, using a $100,000 account with many sets of parameters actually made less than we made on a one lot due to skipped trades.
When we optimized using a million dollar account, the best set of parameters for our triple moving average system was five, 30 and 50 days. This was the most robust combination, making $7.5 million with a compound annual growth (CAG) of 11.86, a marginal account return (MAR) of 0.3040 and a Sharpe ratio of 0.4800. This was not the most profitable set of parameters, but was a reasonably robust area of the search range. The best set of parameters (curve fit) was five, 20 and 50, which made more than $11.5 million with a 14.5% CAG, a MAR of 0.3982 and Sharpe ratio of 0.58. These results show how different results are for a positive size adjusted on risk as well as a one lot. Both these sets of parameters performed badly, making about two-thirds of what the better sets of parameters did on a one-lot basis.
Let’s now look at a simple system that works on stock indexes. We created a simple opening range breakout system for stock indexes (see futuresmag.com/systemcodes). The E-mini contracts started in 1997, meaning we will have to simulate a trade plan dating to 1991 by using the big contract data and changing the tick size and value. We will use $35 slippage and commissions and four and zero as our parameters. That’s 40% of the range, not true range above the open.
On one simulated Nasdaq E-mini and one simulated S&P 500 E-mini, we made $204,781, or $75 a trade, after slippage and commission. Let’s now use the same dynamic margin code we used for the triple moving average system. Again, we use 30, 20 and seven for our dynamic margin parameters and find that a system that appeared OK does badly, returning only 6.64% with a 90% drawdown if we don’t limit the maximum number of contracts.
The aim of this article was to explain some simple trade management concepts and show how they can be implemented. This is a broad topic that, until recently, only large hedge funds and commodity trading advisors could study and apply properly. Remember the most successful managers in the business consistently put more emphasis on their money management systems than signal generation. Now, with new software that integrates the trade management function with trade signals, the average trader can see how these strategies help build an account and produce consistent returns.
Function DynamicMargin(mkt As TSProcessor.IMarket,LBPeriod,MARGINEST)
Dim Arr As Array
Arr =mkt.DataArray(0, "TrueRange")
For i = 0 To LBPeriod-1
Dim M As Integer
Dim Index As Integer
Dim SessCount As Integer
Dim CustPer As Array
DollarsPerMarket = TradePlan.SummEquity / MaxPositions
TradePlan.Session(0).UnitSize = 1
CustPer = CustADXFilter(LB,mmode)
' For each session Loop though the trading plans.
For M = 0 To TradePlan.Session(0).MarketCount - 1
If iActiveOrderCount=1 And iGlobalCountTradePlan.Session(0).Market(M).Margin Then
If iActiveOrderCount=2 And tradeplan.OpenTradeCount+1
TradePlan.Session(0).Market(M).EntryNumUnits = 0
If iGlobalCount>MaxPositions Then
Function CustADXFilter(LB,MMode) As Array
Dim MCount As Integer
Dim CustomPer As Array
Dim count As Integer
Dim Args As Array
MCount = TradePlan.Session(0).MarketCount
ReDim(CustomPer, MCount, 3)
For count = 0 To MCount - 1
' If TradePlan.Session(0).Market(count).HasActiveOrder = True Then
CustomPer[count, 0] = TradePlan.Session(0).Market(count).Symbol(0)
Args = LB
Args = 0 'Offset
result=TradePlan.Session(0).Market(count).InvokeFunc("ADX", Args, Res)
If MMode=-1 Then
CustomPer[count, 1] = 50-ADX_Cur
CustomPer[count, 1] = ADX_Cur
CustomPer[count, 2] = TradePlan.Session(0).Market(count).IsActiveOrders(False)
CustADXFilter = CustomPer
dim MyRange as bararray
If SType=0 then
' We do not have a NextOpen on the last bar so we need to set it to zero
' This way the active order is reported as a change off of the open
If barnumber=lastbar then
If Close>Open then
If barssinceentry>5 and marketposition=1 then
If barssinceentry>5 and marketposition=-1 then
Murray A. Ruggiero Jr. is the author of “Cybernetic Trading Strategies” (Wiley). E-mail him at email@example.com.