How to Fix the Fixed Fractional Position Size

The Fixed Fractional position sizing scheme is the most popular, so why does it need fixed?

Problems solved with Fixed Fractional:

  1. Efficient usage of trading capital
  2. Trade size normalization between different futures contracts
  3. Trade size normalization across different market environments

These are very good reasons why you should use positions sizing.  Problem #2 doesn’t apply if you are only trading one market.  This sounds idyllic, right?  It solves these two problems, but it introduces a rather bothersome side effect – huge draw down.  Well, huge draw down in absolute terms.  Draw downs when using a fixed fractional approach are proportional to the prior run up.  If you make a ton of money on a prior trade, then your position sizing reflects that big blip in the equity curve.  So, if you have a large loser following a large winner, the draw down will be a function of the run up.  In most cases, a winning trading system using fixed fractional position sizing will scale profit up as well as draw down.  A professional money manager will look at the profit to draw down ratio instead of the absolute draw down value.  The efficient use of capital will reflect a smaller position size after a draw down, so that is good right?  It is unless you believe in a Martingale betting algorithm – double up on losses and halve winners.  Are we just stuck with large “absolute” draw downs when using this size scheme?

Possible solutions to fixing Fixed Fractional (FF)

The first thing you can do is risk less than the industry standard 2% per trade.  Using 1% will cause equity to grow at a slower rate and also reduce the inevitable draw down.  But this doesn’t really solve the problem as we are giving up the upside.  And that might be okay with you.  Profit will increase and you are using an algorithm for size normalization.  In this blog I am going to propose a trading equity adjustment feature while using FF.  What if we act like money managers, and you should even if you are trading your own personal money, and at the end of the year or month we take a little off the table (theoretically – we are not removing funds from the account just from the position sizing calculation) that is if there is any extra on the table.  This way we are getting the benefit of FF while removing a portion of the compounding effect, which reduces our allocation for the next time interval.  How do you program such a thing?  Well first off let’s code up the FF scheme.

positionSize = round((tradingCapital * riskPerTrade) / (avgTrueRange(30)*bigPointValue),0);

Nothing new here.  Simply multiply tradingCapital by the riskPerTrade (1 or 2%) and then divide by a formula that defines current and inherent market risk.  This is where you can become very creative.  You could risk the distance between entry and exit if you know those values ahead of time or you can use a value in terms of the current market.  Here I have chosen the 30-day average true range.  This value gives a value that predicts the market movement into the future.  However, this value only gives the expected market movement for a short period of time into the future.  You could us a multiplier since you will probably remain in a trade for more than a few days – that is if you are trying to capture the trend.  In my experiment I just use one as my multiplier.

Capture and store the prior year/month NetProfit

When I come up with a trading idea I usually just jump in and program it.  I don’t usually take time to see if Easy Language already provides a solution for my problem.   Many hours have been used to reinvent the wheel, which isn’t always a bad thing.  So, I try to take time and search the functions to see if the wheel already exists.  This time it looks like I need to create the wheel.  I will show the code first and then explain afterward.

inputs: useAllocateYearly(True),useAllocateMonthly(False),initCapital(100000),removePerProfit(0.50),riskPerTrade(0.01);
vars: tradingCapital(0),prevNetProfit(0),tradingCapitalAdjustment(0);
vars: oLRSlope(0),oLRAngle(0),oLRIntercept(0), oLRValueRaw(0),mp(0);
arrays: yearProfit[250](0),snapShotNetProfit[250](0);vars: ypIndex(0);


once
begin
tradingCapital = initCapital;
end;

if useAllocateYearly then
begin
value1 = year(d);
value2 = year(d[1]);
end;

if useAllocateMonthly then //remember make sure your array is 12XNumYears
begin
value1 = month(d);
value2 = month(d[1]);
end;

if useAllocateYearly or useAllocateMonthly then
begin
if value1 <> value2 then
begin
if ypIndex > 0 then
yearProfit[ypIndex] = prevNetProfit - snapShotNetProfit[ypIndex-1]
else
yearProfit[ypIndex] = prevNetProfit;

snapShotNetProfit[ypIndex] = prevNetProfit;
tradingCapitalAdjustment = yearProfit[ypIndex];
if yearProfit[ypIndex] > 0 then
tradingCapitalAdjustment = yearProfit[ypIndex] * (1-removePerProfit);
tradingCapital = tradingCapital + tradingCapitalAdjustment;
print(d,",",netProfit,",",yearProfit[ypIndex],",",tradingCapitalAdjustment,",",tradingCapital);
ypIndex +=1;
end;
end
else
tradingCapital = initCapital + netProfit;
Capture either the prior years or months net profit

I wanted to test the idea of profit retention on a monthly and yearly basis to see if it made a difference.  I also wanted to just use the vanilla version of FF.  The use of Arrays may not be necessary, but I didn’t know ahead of time.  When you program on the fly, which is also called “ad hoc” programming you create first and then refine later.  Many times, the “ad hoc” version turns out to be the best approach but may not be the most efficient.  Like writing a book, many times your code needs revisions.  When applying a study or strategy that uses dates to a chart, you don’t know exactly when the data starts so you always need to assume you are starting in the middle of a year.   If you are storing yearly data into an array, make sure you dimension you array sufficiently.  You will need 12X the number of years as the size you need to dimension your array if you want to store monthly data.

 


//250 element array will contain more than 20 years of monthly data
//You could increase these if you like just to be safe
arrays: yearProfit[250](0),snapShotNetProfit[250](0);
//Remember you dimension you arrray variable first and then
//Pass it a value that you want to initiate all the values
//in the array to equal
vars: ypIndex(0);
Dimension and Initiate Your Arrays

The first thing we need to do is capture the beginning of the year or month.  We can do this by using the year and month function.  If the current month or year value is not the same as the prior day’s month or year value, then we know we crossed the respective timeline boundary.  We are using two arrays, snapShotNetProfit and yearProfit (to save time I use this array to store monthlty values as well, when that option is chosen) and a single array index ypIndex.  If we have crossed the time boundary, the first thing we need to do is capture the EasyLanguage function NetProfit’s value.  NetProfit keeps track of the cumulative closed out trade profits and losses. Going forward in this description I am going to refer to a yearly reallocation.  If it’s the first year, the ypIndex will be zero, and in turn the first year’s profit will be the same as netProfit.  We store netProfit in the yearProfit array at the ypIndex location.  Since we are in a new year, we take a snapshot of netProfit and store it in the snapShotNetProfit array at the same ypIndex location.  You will notice I use the variable prevNetProfit in the code for netProfit.  Here is where the devil is in the details.  Since we are comparing today’s year value with yesterday’s year value and when they are different, we are already inside the new year, so we need to know yesterday’s netProfit.   Before you say it, you can’t pass netProfit a variable for prior values; you know like netProfit(1) or netProfit[1] – this is a function that has no historic values, but you can record the prior day’s value by using our own prevNetProfit  variable.  Now we need to calculate the tradingCapitalAdjustment.  The first thing we do is assign the variable the value held in  yearProfit[ypIndex].  We then test the yearProfit[ypIndex] value to see if it is positive.  If it is, then we multiply it by (1-removePerProfit).  If you want to take 75% of the prior year’s profit off the table, then you would multiply the prior year’s profit by 25%.  Let’s say you make $10,000 and you want to remove $7,500, then all you do is multiply $10,000 by 25%.  If the prior year’s netProfit is a loss, then this value flows directly through the code to the position sizing calculation (auto deallocation on a losing year).   If not, the adjusted profit portion of funds are deallocated in the position sizing equation.

The next time we encounter a new year, then we know this is the second year in our data stream, so we need to subtract last year’s snapshot of netProfit (or prevNetProfit) from the current netProfit.  This will give us the change in the yearly net profit.  We stuff this information into the yearProfit array.  The snapShotNetProfit is stuffed with the current prevNetProfit.  ypIndex is incremented every time we encounter a new yearNotice how I increment the ypIndex – it is incremented after all the calculations in the new year.  The tradingCapitalAdjustment is then calculated with the latest information.

Here is a table of how the tradingCapital and profit retention adjustment are calculated.  A yearly profit adjustment only takes place after a profitable year.  A losing year passes without adjustment.

All tests were carried out on the trend friendly crude oil futures with no execution costs from 2006 thru 2/28/2204.

See how money is removed from the allocation model after winning years.

Here are some optimization tests with 75% profit retention on yearly and monthly intervals.

Yearly First-

Yearly retention with no stop loss or break-even levels.

Now Monthly-

Monthly retention with no stop loss or break-even levels.

What if we didn’t reallocate on any specific interval?

Huge drawdowns with very little change in total profit.

Add some trade management into the mix.

Here we optimize a protective stop and a break-even level to see if we can juice the results.  All the trade management is on a position basis.

200K with No Reallocating with $14k and $8.5 stop/break-even levels [ranked by Profit Factor]
No position sizing and no reallocation with $5K and $10K stop/break-even levels

200K and monthly reallocating with $7K and $5.5K stop/break-even levels [BEST PROFIT FACTOR]
200K and monthly reallocating with $7K and $8.5K stop/break-even levels [2ND BEST PROFIT FACTOR]

Are we really using our capital in the most efficient manner?

If we retain profit, should we remove some of the loss form the position sizing engine as well.  All the tests I performed retained profit from the position size calculations.  I let the loss go full bore into the calculation.  This is a very risk averse approach.  Why don’t I retain 25% of losses and deduct that amount from the yearly loss and feed that into the position sizing engine.  This will be less risk averse – let’s see what it does.

Not as good.  But I could spend a week working different permutations and optimization sessions.

Are you wondering what Trend Following System I was using as the foundation of this strategy?  I used EasyLanguage’s Linear Regression function to create buy and short levels.  Here is the very simple code.

Value1 = LinearReg (Close, 60, 1, oLRSlope, oLRAngle, oLRIntercept, oLRValueRaw);

Value2 = oLRSlope;

Value3 = oLRAngle;

Value4 = oLRIntercept;

Value5 = oLRValueRaw;


//Basically I am buying/shorting on the change of the linear regression slope
//Also I have a volatility filter but it really isn't used
If value2 >=0 and value2[1] < 0 and avgTrueRange(30)*bigPointValue < 10000 then
buy positionSize contracts next bar at market;
If value2 <=0 and value2[1] > 0 and avgTrueRange(30)*bigPointValue < 10000 then
sellShort positionSize contracts next bar at market;

mp = marketPosition;

//I also have incorporated a 3XATR(30) disaster stop
if mp = 1 and c <= entryPrice - 3 * avgTrueRange(30) then sell next bar at market;
if mp = -1 and c >= entryPrice + 3 * avgTrueRange(30) then buyToCover next bar at market

If you want to see some more Trend Following models and their codes in EasyLanguage and TS-18 Python check out my TrendFollowing Guide and Kit.