Back to Community
PFCF with Momentum Strategy

This is my first algorithm that actually beats the benchmark (over a multi-year period). My focus was on the securities with the lowest Price-to-Free-Cash-Flow ratios (that are still positive) sorted by best performing (over a ~5 month period). I also filtered out securities with an Average Trading Volume lower than 50k. I've played around with the lookback period and the number of securities but the Alpha is usually between 0.5 and 1.0.

Did I code any of this wrong (i.e. does it not match my description above)? Suggestions on how to improve?

I think I saw that I'm always holding a certain amount of cash reserve but I don't know how to change this. I thought my algorithm would be "all in" all the time.

Edit: Can't really hide it because I forgot to change the comments, thanks to Karen Rubin for the basic outline that I used to make my algorithm.

Clone Algorithm
40
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 57bc7c58bd512c100e6c3b26
There was a runtime error.
8 responses

Oh, and I wanted to mention that I got the idea for this algorithm from this article: http://www.quant-investing.com/strategies/free-cash-flow-yield-and-5-year-average-free-cash-flow-yield

The backtests in the article are applied to European markets but I figure the principles are the same.

Nice algorithm Martin. Did you try to hedge the portfolio with SPY? The beta is at 0.96 and maybe that could improve it further?

I definitely need some help with hedging, the concept in general kind of escapes me. Would I basically be keeping x% of my portfolio in SPY at all times?

Take a look at this lecture notebook from quantopian

Martin, you definetely have to look at the lecture because the hedging is an important concept but TLDR: you should keep %50 of your portfolio in a SPY SHORT position. Your algorithm currently is 100% long, so you have to reduce the long leg to 50% and short SPY 50% (the market).

Suppose the market has a sudden drawdown of 10%, your long positions would be impacted too (less than 10% if your ranking scheme is good) but your short leg (that is the market itself) will get a +10% that compensates the loss on the long leg . So hedging is a technique to make an algorithm returns independent of the market and to make its performance dependent only on its ranking scheme. To make that statement true, the long and short legs should have a weighting so that their exposure to the market, Beta, is the same.

You might consider the returns of your algorithms made of two components: alpha (the market independent one) and beta (market dependent one).

returns = (long_alpha + long_beta) * long_weight - (short_alpha + short_beta) * short_weight  
returns = long_alpha * long_weight + long_beta * long_weight - short alpha * short_weight - short beta * short_weight  

if you decide the weighting of your long/short legs so that "long_beta * long_weight" is equal to "short beta * short_weight" you have neutralized Beta and your algorithm is hedged. So you get:

returns = long_alpha * long_weight  - short alpha * short_weight

long_weight + short_weight = 1 (or leverage)

returns = (long_alpha - short alpha) * leverage  

You can see that with a market neutral algorithm the returns will depend on the ranking scheme only. Hopefully the ranking scheme is able to give positive alpha to the long positions and negative alpha to the short positions. That maximize your returns.

When SPY is used as the short leg, you lose the ability to make money on the short leg (but you are market neutral at least) because SPY is the market so short_alpha is 0 and short_beta is 1

returns = (long_alpha + long_beta) * long_weight - (0 + 1) * short_weight  
returns = long_alpha * long_weight + long_beta * long_weight - short_weight  

To be market neutral "long_beta * long_weight" must be equal to "short_weight" and you are left with:

returns = long_alpha * long_weight

long_weight =  1 - short_weight

returns = long_alpha * (1 - short_weight)

So your returns miss all the "short alpha * short_weight" part.

I tried a hedged version of the algorithm. I used RollingLinearRegressionOfReturns factor to calculate Beta of the long leg and then weighted SPY short leg accordingly. Unfortunately the algorithm lost its alpha in recent years (it is somehow visible in the original long only version too).

Note: I used past Beta values to have an estimate of the future ones. That's obviously not correct but hey, if you have a better solution please share ;)

Clone Algorithm
3
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 57bf0787560dcb10085d3183
There was a runtime error.

I wonder if you took a look at the positions that the original algorithm held? On 1/2/2004, I found these positions:

ACF       $15.90    6323      $100,535.70    $12,911.57  
CETV      $17.50    3952      $69,160.00     $339.87  
CKC       $4.20     5195      $21,819.00     ($77.92)  
CLSTE     $12.57    7800      $98,046.00     ($2,675.40)  
CNH       $16.63    1490      $24,778.70     ($204.13)  
DHI       $41.53    2350      $97,595.50     ($3,442.75)  
F         $16.06    6306      $101,274.36    ($384.67)  
FCX       $41.82    2408      $100,702.56    $20,236.83  
GP        $30.53    3303      $100,840.59    ($941.36)  
HA        $2.79     9375      $26,156.25     $1,584.38  
INMT_Q    $5.38     987       $5,310.06      ($28.62)  
LU        $3.12     35335     $110,245.20    ($3,286.15)  
MEH       $4.34     6882      $29,867.88     ($1,101.12)  
MSI       $14.47    7121      $103,040.87    $1,409.96  
MYR       $0.78     2365      $1,844.70      ($92.23)  
PFSW      $1.75     16708     $29,239.00     $200.50  
RAI       $57.63    1749      $100,794.87    ($1,726.26)  
SEBL      $13.99    7228      $101,119.72    ($440.91)  
SIX       $7.66     7574      $58,016.84     ($780.12)  
TKC       $26.16    1574      $41,175.84     $39.35  
TSO       $14.80    6942      $102,741.60    $22,332.41  
TWI       $3.13     9740      $30,486.20     $7,967.32  
TXN       $29.31    3429      $100,503.99    ($3,501.01)  
WBR       $0.71     79210     $56,239.10     $14,257.80  
WJCI      $5.13     9637      $49,437.81     $25,701.88  
Cash      --        --        $267,361.30    --  

So, there are a few ways in which the positions deviate from the intent of the algorithm, as far as I can tell:

  1. While the intent is for all the cash to be invested, not all of it actually is. This is because
  2. All positions taken don't seem to get filled.
  3. Market cap and volume filters still admit penny stocks. I can't say if this is intentional.
  4. I believe stocks with _Q suffix are in bankruptcy? The algorithm doesn't check for can_trade, which might filter these out? If these are indeed in bankruptcy, why any of the transactions succeeded is a mystery to me.
  5. The MarketCap factor isn't used.

I'm looking into how the algorithm does when you start controlling for these issues.

Sunil

Here's a version of the algorithm with more carefully managed execution and a dollar-volume pre-filter. The returns are a bit better. I tried a different version of the algorithm with annual re-balancing, and the performance was terrible. I'll look into long/short hedging later.

Sunil

Clone Algorithm
34
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 57d2410797e75913857afcf6
There was a runtime error.