Learn a four-step process for developing trading strategies using permutation tests and Python, demonstrated with Bitcoin data.
Key Takeaways
- Develop trading strategies through a structured four-step process combining optimization and statistical testing.
- Permutation tests help identify if in-sample performance is genuine or due to data mining bias.
- High granularity returns at the bar level improve stability in performance metrics.
- Walk forward testing is crucial for validating strategy robustness on out-of-sample data.
- Optimization should be balanced to avoid overfitting and ensure meaningful strategy improvements.
What the video covers
- The video presents a generic four-step approach to developing trading strategies: in-sample excellence, in-sample Monte Carlo permutation test, walk forward test, and walk forward Monte Carlo permutation test.
- An example strategy using a Donchian channel breakout on hourly Bitcoin data from 2016-2019 is used to illustrate the process.
- The strategy signals are generated by comparing current prices to historical highs and lows over a lookback period, optimized via grid search for the best profit factor.
- Strategy returns are calculated at the bar level by multiplying position signals with shifted returns, providing higher granularity for more stable objective function calculations.
- In-sample performance is evaluated for excellence and overfitting, with emphasis on understanding periods of inconsistency and potential improvements.
- The Monte Carlo permutation test is used to assess whether good in-sample performance is due to genuine data patterns or data mining bias by comparing real data results to randomized permutations.
- The video discusses the limitations of permutation tests, such as ignoring volatility clustering and long memory in real price data.
- Practical considerations include running a large number of permutations (e.g., 1,000) for statistical confidence, which can be computationally intensive.
- Walk forward testing is introduced as a validation method to test strategy performance on unseen data, distinct from the development (in-sample) phase.
- The approach is applicable to various types of trading strategies, from simple trend followers to complex machine learning models.
Chapters
- 00:00Introduction and Four-Step Trading Strategy Development Process
- 01:32Example Strategy Optimization on Bitcoin Data
- 03:12Monte Carlo Permutation Test Explanation and Application
- 04:49Permutation Algorithm Details and Limitations
- 07:02Statistical Analysis of Permutation Test Results
- 09:01Practical Considerations and Computational Costs
- 14:44Walk Forward Testing and Validation
Full Transcript — Download SRT & Markdown
Speaker A
In this video, I'll show the thought process and four steps I always use when developing a new trading strategy. The approach is generic and compatible with almost every strategy. I've shown bits of this process in my past videos, but I wanted to make a standalone dedicated video I can reference back to in the future. The four steps are in-sample excellence, in-sample Monte Carlo permutation test, walk forward test, and walk forward Monte Carlo permutation test. I'll show an example going through all four steps and generalize the concepts so you can try with your own strategies.
Speaker A
wanted to make a standalone dedicated video I can reference back to in the future the four steps are insample excellence in Sample Monte Carlo permutation test walk forward test and walk forward Monte Carlo permutation test I'll show an example going through
Speaker A
First, I'll show how I assess a trading strategy using a moving average crossover as an example. We load in candlestick data and compute a fast and slow moving average. At each bar, we check if the fast moving average is above the slow moving average. We create a signal that denotes the position of the strategy at each bar. One means we have a long position following that bar, and zero means we have no position. Regardless of what the strategy is, one should be able to create a similar signal, a value denoting the position of the strategy after each bar.
Speaker A
slow moving average at each bar we check if the fast moving average is above the slow moving average we create a signal that denotes the position of the strategy at each bar one means we have a long position following that bar and
Speaker A
Now, if we compute close-to-close returns and shift them forward by one bar, we can multiply the position's signal by the shifted returns to get a return for each bar that is attributable to the strategy. These strategy returns, at the same granularity as the bars, are what I use to compute objective functions such as the profit factor or the Sharpe ratio. By having a return for each bar instead of each trade, objective functions are fed much more data, and the calculations and results are much more stable. The book "Testing and Tuning Market Trading Systems" provides convincing arguments for why these higher granularity returns are superior. It's where I got the idea, and I'm thoroughly convinced both from what the book says and my own experience.
Speaker A
them forward by one bar we can multiply the positions signal by the shifted returns to get a return for each bar that is attributable to the strategy these strategy returns at the same granularity of the bars are what I use
Speaker A
I'll scrap the moving average crossover because it's lame, and instead, I'll use the slightly more interesting Donchian channel breakout strategy I showed in my previous video. Briefly, the strategy goes long when the current close is the highest over a given lookback, and it goes short when the current close is the lowest over a given lookback. The idea of this is to trade in the direction of range breakouts so we can ride the trend when extended trends occur. The code for the Donchian breakout is short, and the output is a signal vector. It will have a value on each bar denoting the strategy's position, one or zero. The strategy needs a lookback, so we can test several and pick the best. Here's a grid search to do that. We look through a wide variety of lookback values and find the one with the best profit factor.
Speaker A
testing and tuning market trading systems provides convincing Arguments for why these higher granularity returns are superior it's where I got the idea and I'm thoroughly convinced both from what the book says and and my own experience I'll scrap the moving average
Speaker A
I ran the optimization on hourly Bitcoin data from 2016 through 2019. Over those four years, the best lookback was 19 with a profit factor of 1.08. I first like to look at the in-sample performance. Since these are log returns, the cumulative sum of the strategy returns will give us a crude backtest. Cool. So, what are we actually doing here? Well, we're optimizing a mediocre trend follower, but more generally, we have an idea for a trading strategy, a set of development or in-sample data, and a way to fit, optimize, or select the best version of the strategy for the data. These are the generic components for essentially every trading strategy, whether we're optimizing a lookback for a trend follower, selecting the best chart patterns, or training a fancy neural network. This is how trading strategies are optimized.
Speaker A
close is the lowest over a given look back the idea of this is to trade in the direction of range breakouts so we can ride the trend when extended Trends occur the code for the Donan breakout is short and the output is a signal Vector
Speaker A
So, with that in mind, let's look at our in-sample performance again. Currently, we're in the development stage here. I ask myself two questions: Is this excellent? And is it obviously overfit? These are in-sample results, so they should be pretty damn good. Maybe you could come up with some threshold for an objective function to decide if it is excellent, but really, I think it depends on the nature of the strategy. I'm only looking if the strategy has periods of inconsistency. I would drill down and really look at what is happening when the strategy is working poorly versus working well. Maybe there are ways one could improve this strategy. Maybe some YouTuber made a video about improving the very strategy you're looking at. But this is the development stage. This is the time to study your strategy, test potential improvements, and refine your optimization process.
Speaker A
values and find the one with the best profit factor I ran the optimization on hourly Bitcoin data from 2016 through 2019 over those four years the best look back was 19 with a profit factor of 1.08 I first like to look at the ins sample
Speaker A
If this was a strategy I was working on, I'd study it and try to improve it further. I would not say this is excellent. I expect a bit more from in-sample performance than this, but to keep the video going, I'll say it looks good enough here. Now, the other question is, is this obviously overfit? This one is a bit harder to answer. I suppose one gets a feel for it eventually, but if your results are suspiciously good, like a 100% win rate, you're probably overfitting, or maybe you just accidentally allowed a future leak, which obviously needs to be fixed. But if you suspect overfitting, you may want to dial back the complexity of the strategy. Ultimately, the answer to this question in the development stage should either be yes or not obviously. I'll show an obviously overfit strategy later.
Speaker A
idea for a trading strategy a set of development or ins sample data and a way to fit optimize or select the best version of the strategy for the data these are the generic components for essentially every trading strategy
Speaker A
Once we're satisfied with our in-sample results, the question becomes: Was this excellent in-sample performance found due to patterns intrinsic in the data, or was the good in-sample performance found just because our optimization process was powerful enough to find something in noise? In other words, is data mining bias the main contributor to our excellence in-sample performance? The problem with optimization is it works. If we compare multiple configurations of a strategy, one will be the best, but it will always have a data mining or selection bias. Of course, if we optimize even a great strategy, there will be some data mining bias, but a good strategy's in-sample performance will mostly be from patterns in the data. But if our strategy is trash, then its in-sample performance will be entirely due to data mining bias. Our null hypothesis is that our strategy is garbage. We will use the in-sample Monte Carlo permutation test to disprove our null hypothesis.
Speaker A
development stage here I ask myself two questions is this excellent and is it obviously overfit these are ins sample results so they should be pretty damn good maybe you could come up with some threshold for an objective function to
Speaker A
So, how does this test work? We optimized the Donchian breakout on these four years of price data, and our optimized strategy has a profit factor of 1.08. But if we create a random permutation of this data, we get something like this. In this random permutation, any legitimate patterns that existed in the real data are no longer present. The permutation is just noise with nearly identical statistical properties. If we optimize the Donchian breakout on this permuted data, we get a profit factor of 1.02. The optimized strategy did better on real data than it did on permuted data. This gives a small amount of evidence that our null hypothesis is false because if the optimized strategy did just as well or better on random data, we could presume the main contributor to the good in-sample performance we saw was from data mining bias.
Speaker A
poorly versus working well maybe there are ways one could improve this strategy maybe some YouTuber made a video about improving the very strategy you're looking at but this is the development stage this is the time to study your
Speaker A
But this was only one permutation. If we created many more and on each permutation we optimized the strategy, we could get an idea of how powerful the data mining bias induced by our optimization is. If the optimized strategy profit factor of 1.08 that we found on real data is better than what we found on the vast majority of permutations, we can disprove our null hypothesis. I don't think there's any value in looking at the equity curves of permutations, as we can simply compute the objective function on each of these, but I think this helps visualize what we're actually doing, and it looks cool.
Speaker A
than this but to keep the video going I'll say it looks good enough here now the other question is this obviously overfit this one is a bit harder to answer I suppose one gets a feel for it eventually but if your results are
Speaker A
Now, I'll show the algorithm I'm using to generate these permutations of price. This function has a few parameters. The first is the data. It takes either a data frame of open, high, low, close prices or a list of such data frames. The option for the list of data frames is for permuting multiple markets. I'll talk about this option later. Start index is where the permutation starts. When set to the default zero, the function will permute all the data given. We will use this parameter later when we talk ab-
Speaker A
strategy ultimately the answer to this question in the development stage should either be yes or not obviously I'll show an obviously overfit strategy later once we're satisfied with our ample results the question becomes was this excellent ins sample performance found due to
Speaker A
patterns intrinsic in the data or was the good and Sample performance found just because our optimization process was powerful enough to find something in noise in other words is data mining bias the main contributor to our excellence in Sample performance the problem with
Speaker A
optimization is it works if we compare multiple configurations of a strategy one will be the best but it will always have a data mining or selection bias of course if we optimize even a great strategy there will be some data mining
Speaker A
bias but a good strategy's insample performance will mostly be from patterns in the data but if our strategy is trash then its insample performance will be entirely due to data mining bias our null hypothesis is that our strategy is
Speaker A
garbage we will use the ins sample Monte Carlo permutation test to disprove our null hypothesis so how does this test work we optimized the Donan breakout on these four years of price data and our optimized strategy has a profit factor
Speaker A
of 1.08 but if we create a random permutation of this data we get something like this and this random permutation any legitimate patterns that existed in the real data are no longer present the permutation is just noise with nearly identical statistical
Speaker A
properties if we optimize the Donan breakout on this permeated data we get a profit factor of 1.02 the optimized strategy did better on real data than it did on permuted data this gives a small amount of evidence that our null hypothesis is
Speaker A
false because if the optimized strategy did just as good or better on random data we could presume the main contributor to the good and Sample performance we saw was from data mining bias but this was only one permutation
Speaker A
if we created many more and on each permutation we optimized the strategy we could get an idea of how powerful the data mining bias induced by our optimization is if the optimized strategy profit factor of 1.08 that we
Speaker A
found on real data is better than what we found on the vast majority of permutations we can disprove our null hypothesis I don't think there's any value in looking at the equity curves of permutations as we can simply compute
Speaker A
the objective function on each of these but I think this helps visualize what we're actually doing and it looks cool now I'll show the algorithm I'm using to generate these permutations of price this function has a few parameters the
Speaker A
first is the data it takes either a data frame of open high low close prices or a list of such data frames the option for the list of data frames is for permuting multiple markets I'll talk about this
Speaker A
option later start index is where the permutation starts when set to the default zero the function will permute all the data given we will use this parameter later when we talk about the walk forward permutation test since we
Speaker A
pass data is either a list of data frames or a single data frame we handle that first if it's a single data frame I put it into a list and set end markets to one if we have multiple markets we
Speaker A
ensure that their indexes are identical then we allocate space to store relative prices for each market and bar and the first bar of each market the first bar will be unchanged it has a size of four to handle the Open high low and close
Speaker A
price now we compute prices on each bar relative to that Bar's open we Loop through each of the markets and get the logarithmic prices I copy the first bar at the start index here the open is subtracted from the high low and close
Speaker A
prices since we're dealing with log prices we're essentially recording the percentage off the open of each of these prices the relative open is the current open minus the prior close the gap then we copy over these prices into the
Speaker A
arrays we made earlier now we get the indices of the real data we'll use these to shuffle the relative prices we Shuffle the indices once for the intrabar quantities and again for the gaps the gaps have little effect on
Speaker A
crypto data the crypto Market never actually closes so the open of one bar will usually only be at most a few ticks away from the prior bars close but for daily stock data the open can be quite far away from the prior close after
Speaker A
shuffling we can now string together our permutation we Loop through each market and allocate space to store the permuted bars we get the log prices of the real data we copy it into the permuted data before the start index if the start
Speaker A
index is set to zero nothing happens here then we copy the start bar the first bar of the permutation we loop from the start bar to the end of the data we first set the open price the zero is the index of the open and the
Speaker A
three is the index of the close to to get the permuted bars open we add the relative open value to the prior bars close then we add the relative high low and close to that open value to get the
Speaker A
rest of the permuted bars prices after the loop we exponentiate the prices to get them to the normal scale and add the bars to a data frame the function will return either a single data frame or a list of data frames the same as what was
Speaker A
passed to the function now we can pass in a data frame of real data and we're returned to a data frame of permuted data the first open and last close are exactly the same on both the real and
Speaker A
permuted data so the overall trend of the data is preserved but the path the price takes between those two prices is completely different the goal of a permutation algorithm is to create bars that have the same statistical properties as the original if we compute
Speaker A
some Clos to close returns we can see that the mean standard deviation skew and cryosis are all nearly identical now let's load in ethereum data for the same time period here's a plot of Bitcoin and ethereum in 2018 and 2019 we can see
Speaker A
that they're obviously correlated and if we per them together the correlation between the two markets stays the same I won't cover the multimarket case here Beyond this but if your strategy involves two or more markets the permutation tests can still be applied
Speaker A
while the algorithm produces permutations with many similar statistical properties to the original it is not without its flaws as price is not a random walk real prices have volatility clustering and long memory both of which could be a topic for a
Speaker A
different time but the permutation algorithm will destroy both of these properties if your strategy is heavily focused on one of these properties or some other property that the permutation algorithm doesn't preserve the Monte Carlo permutation tests can be
Speaker A
optimistically biased but this really isn't a horrid problem as if your strategy cannot pass the permutation tests even with a potential optimistic bias then you know your strategy is probably overfitting now that we've gone over the bar permutation algorithm we
Speaker A
can return to where we were we optimized the Donan breakout on hourly Bitcoin data from 2016 through 2019 and the best look back gave a profit factor of 1.0 08 when we optimized the strategy on many different price permutations we found
Speaker A
that the results were worse than what we got on real prices we've essentially already done the in Sample permutation test but now I'll show you the code and how to apply it first we load our data and get the four years we're using to
Speaker A
train we call the optimized Donan function and this gives us our real profit Factor now we can do the permutation test we set the number of permutations then Loop that many times we get a permutation of the bars and
Speaker A
optimize our strategy on them to get a permuted profit Factor if the permutation profit factor is just as good or better than the real profit Factor we increment the permutation was better count after the loop we can calculate a quas I value the number of
Speaker A
times the permutation was better divided by the total number of permutations this value is roughly the probability that our real profit Factor was found mainly due to data mining bias this next part isn't really necessary but I like to
Speaker A
plot a histogram of the profit factors or whatever objective function we used from the permutations then add a line showing where in the the distribution the real profit Factor fell I ran this test with 1,000 permutations and got
Speaker A
this only a couple permutations did better than the original so the P value is very low 0.3% if a sufficient amount of permutations are done the permutation distribution should be roughly bell shaped if the distribution looks really weird there's probably an issue with
Speaker A
your code I like to see the P value below 1% so I would call this a pass now I'll quickly show you a strategy that is overfit this function fits a decision tree we compute three indicators just basic price differences then create a
Speaker A
classification Target whether the next 24 hours go up or down then we create a decision Tree Train it with our indicators and Target and return the model notice that I've set the minimum samples per Leaf very low this is one of
Speaker A
the key regularization parameters and it's pretty much guaranteed to overfit to test the model we can use this function we compute the same indicators and use them to predict the model then we can use the model's predictions to
Speaker A
create a position Vector we'll go long when the tree predicts the price will go up and we'll go short when the tree predicts the price will go down then finally we compute the profer factor of that signal here are the ins sample
Speaker A
results for our decision tree again this is when I ask myself is this obviously overfit the answer is yes generally speaking if your back test ever looks like this you have a future leak or you're horribly overfit but if we didn't
Speaker A
know any better we can use the ins sample permutation test to crush our dreams and the test does the job the model performs just as good or better on the permutations when you see this it's time to throw your strategy idea in the
Speaker A
trash ideally you should use the test with as many permutations as possible I think 1,000 is a reasonable minimum this of course means we have to optimize our strategy 1,000 times and it will probably take some time if optimizing
Speaker A
your strategy 1,000 times is simply not feasible you probably have a very complex strategy or a very poorly coded strategy in which case I suppose 100 would be sufficient but I would say that's a hard minimum this test provides
Speaker A
a quas IP value that roughly indicates the probability that your end sample results were primarily found from data mining bias I generally don't continue if it is over 1% but don't treat that like a Target this is a measure if a
Speaker A
measure becomes a Target it is no longer a good measure basically if you fiddle with your strategy enough you could probably make this test pass on anything so don't overuse it we've now seen the insample permutation test pass the Donan
Speaker A
Channel breakout and reject the decision tree nonsense but why even do this couldn't we just try the strategy on 20 20 data if it worked on data that wasn't used for the optimization then the strategy is probably not overfit well
Speaker A
sure we could but once out of sample data is used even once it is no longer truly out of sample suppose we optimize strategy a on 2016 through 2019 then test it on 2020 data and we find our out
Speaker A
of sample results to be decent but then we come up with another idea strategy B we optimize strategy B just the same on 2016 through 2019 then we also test it on 2020 we find that strategy B did
Speaker A
better than strategy a so one might think the new idea was better but now there is a selection bias strategy B did better compared to strategy a the results of B on 2020 data are inflated by selection bias now realistically I've
Speaker A
already tested many things on 2020 data and it definitely isn't out of sample for me rather it is a validation set it is a good idea to walk forward and optimize strategy testing it on data it did not use to optimize that is how the
Speaker A
strategy will have to trade in reality after all all and if we test a strategy on data it did not use to optimize the results will not benefit from any data mining bias however if we walk forward test 100 different strategies on 2020
Speaker A
data and select the best one there will be a massive selection bias selection bias can allow us to effectively overfit the validation data despite it not being used for strategy optimization every time we reuse out of sample data or
Speaker A
rather validation data the selection bias is adding up this is why we use the ample permutation test we can detect that our idea is bad before we waste the out of sample data or stack up even more selection bias on the validation data
Speaker A
anyways with all that in mind our optimization of the Donan breakouts look back passed the insample permutation test so now let's walk forward the Donan breakout this function will return the walk forward signal one of the parameters is the train look back how
Speaker A
much data to optimize on I have it set to optimize on the last four years by default assuming hourly data is used the train step is how often we reoptimize I set it to 30 days ideally you should
Speaker A
retrain strategies as often as is feasible but to make the code run fast enough to accommodate my brain rot I used 30 days we set the index of the next optimization and loop through all the data every time the index is equal
Speaker A
to the next train variable we reoptimize compute the new signal and increment the optimization index by the train step this is pretty inefficient code but it's simple and it works here are the results of the Walk forward signal on 2020 data
Speaker A
it had a profit factor of 1.04 which is worse than what we saw in Sample generally that is to be expected as these results do not benefit from any data mining bias the only bias in play here is the potential selection bias if
Speaker A
we had already walked forward tested other strategies using this data at this stage I ask myself is this worth trading the answer is subjective it depends on your standards maybe you have higher or lower standards than me for me I
Speaker A
wouldn't bother with this it kind of sucks but the line did go up and to keep the video going I'll say this is good enough to get these results we optimized the Donan breakout on these four years of data these four years are the first
Speaker A
training fold of the Walk forward after the first training fold the walk forward function can output a signal that we can test since our walk forward results were satisfactory we're assuming that whatever patterns the strategy learned or optimized on from past data were also
Speaker A
present in this future unseen data but what if our optimized strategy is actually worthless what is the chance a worthless strategy could have achieved walk forward results just as good as what we found if we generate a permutation of the data after the first
Speaker A
training fold any legitimate patterns in this data will no longer be present there are no legitimate patterns in this permutation if we walk forward the same strategy on this permutation and compute its profit Factor we get an estimate of
Speaker A
a profit factor that a worthless strategy could produce and if we generate many permutations we get a distribution of what worthless strategies can produce if our real walk forward results are to be attributed to patterns learned from past data
Speaker A
reoccurring in this future data then then our real walk forward profit Factor should be better than the vast majority of profit factors produced by worthless strategies this is the walk forward permutation test you will notice the code is very similar to the ins sample
Speaker A
permutation test we load in our data and set the train window to 4 years then we compute the walk forward signal with the signal we can compute our real walk forward profit Factor then we set the number of permutations and loop through
Speaker A
them we call the same get permutation function but we set the start index to the train window to only permute dat after the first training fold we compute the profit factor of the Walk forward signal in the same way then we compare
Speaker A
the profit Factor found on the permutation to the profit Factor we found on real data we compute our quas IP value and make a histogram of the permutation profit factors I ran the test with 200 permutations and got this
Speaker A
the P value is 22% roughly meaning there's a 22% chance the walk forward profit factor of 1.04 could have been achieved by a worthless strategy in other words a 22% chance our walk forward results were just dumb luck
Speaker A
ideally this probability is very low and a great strategy will have a very low P value I tend to be slightly more lenient with this test don't get me wrong these results are not good but they're only from 2020 just one year generally I'm
Speaker A
willing to accept around a 5% P value on just one year of data but if we had done the walk forward permutation test on two or more years of data then I won't accept a P value above 1% I also used
Speaker A
just 200 permutations this is because the walk forward permutation test can take forever to run even on an extremely simple strategy this is the test to start before going to sleeve overall I don't think these results are good
Speaker A
enough and I would not trade the Donan Channel with an optimized look back going forward if you want you can test the strategy in 2021 and Beyond to verify it sucks for yourself I learned about these two permutation tests from
Speaker A
the book permutation and randomization tests for trading system development by Timothy Masters the author has a PHD in statistics and is the goat of Al rmic trading this video only covers two of the Min tests covered in the book and
Speaker A
the book provides much more detail than I did here my copy is very beaten up and I've spilled coffee on it several times as I always have it on my desk if you're serious about algorithmic trading this is a must read I'll leave a link to it
Speaker A
below the Donan Channel with an optimized look back did not farewell against the walk forward permutation test in my experience optimizing look backs of indicators rarely generalize as well rather when dealing with strategies that require a look back I find a stable
Speaker A
look back value meaning a large variety of look backs have decent performance then I pick a reasonable look back value and stick with it then look to improve the strategy with the chosen look back there are many tests and tools to help
Speaker A
validate trading strategies and I only covered two of them in this video some tests or tools are more useful for certain types of strategies but I always use these two tests regardless of the strategy I will not use a trading
Speaker A
strategy if it did not have very low P values for both the ins sample and walk forward permutation test this video has been a rough outline of how I develop and validate a trading strategy but the process and steps
Speaker A
should be applicable to most strategies and certainly all price-based strategies no process is bulletproof if you are irresponsible with your development process no amount of fancy tests can save you but this is what I do and I'll keep doing it until I find a better way
Speaker A
here we compared The Profit factor of a trading strategy between the real price and price permutations you can use permutation tests in many different ways in my previous video I compared the percentage of times price bounced off a
Speaker A
moving average between the real price and price permutations you can use permutation tests to help verify or disprove any assumptions or theories you may have about the markets
Topics:trading strategy developmentpermutation testMonte CarloDonchian channelBitcoin tradingstrategy optimizationwalk forward testingdata mining biasPython tradingfinancial backtesting











