Navigation: Backtest Engine Details >

Scaling In or Out of Positions





Most strategies developed and tested in RealTest involve simple round-trip trades. The entire position is bought (or shorted) on entry day, and sold (or covered) on exit day.

RealTest can also model strategies that scale in and/or scale out of positions. Such models can be structured in two different ways:

1.For a small fixed number of entries and/or exits per position, separate strategies can be used for each "leg".

2.A single strategy can enter multiple positions in the same symbol (this "adding to a position") and/or exit part of the total position ("scaling out").

Here's a simple example where we buy at a new 100-day high. The exit is a 2*ATR trailing stop. We want to sell half the position at a 1*ATR profit if achieved. Position size is 10% of account.

Here's how to implement this concept with separate strategies:

The use of a common template avoids the need to copy and paste all of the elements.

The "leg1" strategy holds half the position (5% of account) and implements both exits as a bracket.

The "leg2" strategy holds the other half of the position and only implements the stop.

For scaling in to positions using separate strategies, structure like the above but with different EntrySetup conditions for each leg.

Here's how to implement this concept in a single strategy:

Here the position size is 10% since there's only one entry. The position will be divided at exit time if the target is hit.

The ExitNum variable in the ExitLimit formula controls whether a limit price is set (when no exit has happened yet) or not (0 means no limit order).

The ExitLimitQty formula specifies the number of shares to sell when the target is hit, i.e., the limit order size.

Note that Shares will always represent the share quantity of the remaining position. In the above example Shares is only referenced once when the position is still its original size so that's not a problem.

Here's a different scaling-out concept -- sell one-fifth of the original position every day over 5 days:

Using Shares in this example would not work as intended. If the original size was 20, the first exit would be 4 leaving 16, the second exit would be 3 (16/5 rounded down), the third would be 2 (13/5), and so on. Instead use FillQty, which is the original position size at the time of entry, to specify equal fractions for each leg.

For scaling in to positions in a single strategy, we need to specify MaxSameSym as the largest allowable number of simultaneous "legs". By default RealTest sets MaxSameSym to 1. This automatically prevents pyramiding without having to add "and Shares = 0" to every EntrySetup formula.

This simple entry example would buy the first two times prices crosses above the 8-period EMA:

The Examples folder includes a script called tf_dynamic_size.rts, which demonstrates a mixture of scaling in and scaling out:

(Not show are the Import and Settings sections -- the example uses the S&P 100 Current & Past universe.)

This is a far more complex example than the snippets above.

The strategy's EntrySetup rule serves two purposes here:

1.Enter a new position if the condition specifed in the Data section "setup" variable is met and there is no current position.

2.Add a new position portion whenever the target quantity exceeds the current total position quantity by more than X%.

There are two separate exits:

1.The entire position will be exited if the trailing ExitStop is hit.

2.A partial exit will occur whenever the total position quantity exceeds the target quantity by more than X%.

As the test runs and partial positions are dynamically added and closed at various sizes, the total position will consist of any number of sub positions. This is why MaxSameSym is set to an arbitrarily large number (99).

Notice the special technique in the Library section of referring to extern(@tf_dynamic, shares). This will always return the current total number of shares held by this strategy in this symbol. In contrast, when multiple sub positions are open, "shares" only returns the quantity of the sub position currently being processed for potential exit.

On each day of the backtest, the engine will evaluate the exit formulas for each sub position separately. If a partial or complete exit of that sub position is indicated, that exit is simulated before the engine proceeds to the next sub position. This is why it works to specify the ExitQty as is shown above.

The ExitRule formula (along with the Library formulas it references) is evaluated for each sub position after the prior sub position's exit has been processed. Once the remaining quantity has reached the target quantity, ExitRule will stop returning "true" and no further exits will occur that day.

Note that if ExitQty returns more shares than the size of the current sub position, that entire sub position is exited. If ExitQty returns fewer shares than the size of the sub position, that sub position is reduced by the difference.

Adding and subtracting sub positions repeatedly in this manner will result, at times in a large number of small sub positions comprising the total current position. Nevertheless the overall result is an accurate model of dynamic position size adjustments.





Copyright © 2020-2024 Systematic Solutions, LLC