in the next 60 Minutes you're going to learn everything you need to know about back testing. py how to get started and how to implement your own strategies welcome to this course my name is Chad and I'll be your instructor and before we get started I think it'd be a good idea to explain a little bit about back testing. py what I like about it what I don't like about it what it's good for what it's not good for so we'll start with the positives here it's a really lightweight back testing framework so you might be familiar with something like vector BT or back trader and those are really great libraries but they have lots and lots of different pieces of functionality built into them and it gets really overwhelming and the documentation is very hard to read because there's so much of it and it can it can all get a bit too much whereas back testing. py just focuses on the essentials of trading it doesn't try to include its own indicator Library it doesn't try and integrate with telegram if you want to build that stuff in that's great but back testing. py just focuses on the back testing portion of things it won't try and integrate with your broker and the result of that is that the documentation is really simple and easy to understand I'll be working through it with you over the course of this tutorial and as well as that it's also very very very quick given that it's an event driven back tester I'll explain a little bit more about what that means later on but you'll see the speed for yourself later on especially when testing single strategies they can be tested pretty much immediately it's also got a builtin parameter Optimizer if you're into that kind of thing if you want to plot nice heat Maps back testing. py has got you covered here some things I don't like about the library is that there's no real way way of trading multiple assets at least not in the stock version here and it also doesn't trade fractional shares which can be a problem and we'll see that later on when we talk about how to customize the sizing of your orders and that kind of thing just to show you here how small the documentation is if I go over here this is it this is literally every single function in the library you can learn pretty much all of it in an afternoon and indeed you will over the course of this video we'll be covering pretty much everything in here with a few different examples it's built with Frameworks that you're very familiar with so it will be mostly interacting with either Panda's data frames or nump arrays just to make life a lot easier and so let's go ahead and get started dig into some code and get up and running with our first strategy here if you'd prefer a text version of this tutorial links will be down below so let's go ahead and get started here and start writing some code get up and running I'll show you how to install everything let's go so just go ahead and open up any folder or workspace that you use whatever your IDE is I just use Vim so I'll just be in the terminal here and we'll want to create a virtual environment here so however you do that in your editor go ahead and get that set up so virtual EnV VV and then and Source VNV bin activate Okay so we're in a virtual environment here clean python environment so I can show you which packages we need to install at a minimum you're definitely going to need the back testing package so it should just be pip install back testing and that'll install all the dependencies that you need like num and pandas and whatnot I'm also going to install a TA Library here so I'm going to install talb so pip install talb now if you have a different package that you like to use to work with technical indicators you're more than welcome to use that I know TB can be a bit hard to install sometimes I will leave a link in the description for a tutorial on how to install it if it's something you're struggling with it's just my preferred TI library of choice just to calculate things like the RS SI which I'm going to be using quite a lot in this tutorial but you're more than welcome to use any library that you want it should work just fine so I think with just those two libraries that we're ready to dig in here so I'm just going to make a python file I'll call it example.py and we're going to need to do some importing here so from back testing we're going to need a few things so we want to import back test itself we're not going to need that for now be using that to actually run the back test later on we'll also need strategy that's how we're going to Define our trading logic where do we buy where do we sell that kind of thing we need to make a strategy if you're familiar with back trader this will all be very very familiar to you and that's no accident the author of this library was very influenced by back trader but wanted to make a more lightweight smoother version and then we'll also want to grab some data helpfully back testing py comes with some data built in so we can do from back testing. test import goog so this has some Google price data from I think about 2006 to 2013 or something like that so if I print goog here we should have some data in a nice Panda data frame you're obviously more than welcome to pull in your own data and I'm assuming that you'll want to if you if you want to use this tool meaning for it in the future but just so that we're on the same page we can use this data here which comes packaged with the library obviously if you bring your own dator in make sure it looks the same as this you know it has the Open high low close volume columns and it has a date time axis those are the really two important features that you need to worry about in general you will want both the open and the closed columns because of the way this Library works is it calculates the indicators based on the close value or at least that's what we'll be doing and it buys based on the value of the next open candle so if we were looking at this day's data it would calculate the indicator based on this value and then if it was a buy it would buy based on the next day's open price back testing libraries do differ quite a lot on how this works so I thought I just mentioned that straight up okay so now that we've got that let's just go ahead and write out a strategy so a strategy you'll write out as a class which extends the strategy class that we imported so we'll have a class called RSI and I'm call it oscillator here so oscillator and it'll inherit from strategy now there are two functions that we need to worry about here there's an init function and we need to pass self into that and there's also a next function so Define next here and we'll pass self into that as well so these two functions are mainly what you'll be working with when defining strategies there are ways to do things where you only use the init function here I'll probably talk about that a bit later on but for now this is how we're going to be doing things so the init function here gets runs unsurprisingly as soon as this object is created so it gets runs one time at initialization so in here you want to try and pack things in that can be calculated straight off the bat in one big batch and therefore can be calculated most efficiently so for example if you wanted to calculate the RSI we're going to do it inside here because you can calculate the values of the RSI for the full data frame and it's just sort of one calculation now the next function here goes through each candle individually one by one and evaluates the criteria and then decides whether it wants to buy on the next candle it'll make more sense once we start filling in the IC so let's go ahead and just get started here so in in it we'll want to Define our indicator because like I said we can calculate the value of this indicator anyway straight off we don't need any fancy stuff we just need a price data frame and we can turn that straight into the data frame for the RSI values and that can be fed one by one into next so I'm going to go ahead and import T alib you can import whatever you want here so I'll import t alib and then in init here we'll do self. RSI so we're going to assign a class variable here it's going to be self. RSI is equal to self DOI so I here is how we're going to build indicators within the back testing. py framework so it takes a couple of parameters that we need to think about the first one is the function so the actual function that calculates the indicator values so in our case that's going to be tb. RSI for the second value here you want to pass in the data so I'm going to pass in the close price here it'll get this data later on when we run the strategy for right now you just need to know it's self. data and then you know you can use do open here I suppose if you wanted to you could also throw in the volume or something like that and then any other arguments that you pass in here they going to get passed straight into this function so if me this is an RSI so the RSI takes the price and a window so you can just leave it as a number here for now so we could say we want the RSI and into this indicator we're going to pass the closing price and we're also going to pass the time window here as another variable I'll show you how you can make your own custom indicators after this but for now we'll just use one pre-installed from talip so that's pretty much all we're going to be doing being right now in this init section we're now going to concern ourselves with the trading logic so we've pre-calculated all of these values and they're going to get fed one by one into next year so what I'm going to implement here is I want a simple crossover strategy I want when the RSI gets above a certain value it sells when it gets below it buys that's what I want and so in order to implement that we'll import the crossover library from back testing. py so really helpful little function if you want to do crossover strategies you can of course implement it yourself just using regular old python so if I do back testing. lib import crossover in here we can now say if crossover self. RSI and self. _ bound so it's if the first series is bigger than the second series that's essentially what crossover does here and well if the RSI is greater than our upper bound well what do we want it to do well we want it to sell everything we've got so we'll do self. position do close hopefully that makes sense and then what about if the RSI goes below the lower bound what do we do then well same sort of deal here so L if crossover and then self. lowerbound and self. RSI well in this case we just want to do a buy so this is kind of the opposite where the lower bound here is above the RSI while we want to start buying now I realize I've just used these values here and I haven't told back testing. py what they are so we can go ahead up here and we can just Define some variables here for this class so I'm going to say upper SC bound is going to be equal to 70 let's use the Traditional Values here and we'll say lower bound is equal to 30 so now this function should be able to access them in here that's all we need for a very very basic strategy I know that might be information overload there's all this weird stuff with what are these how do you access the positions how do you buy cell what are the arguments for all of this we'll get into that later on for right now let's just get something up and running so in order to actually do the back test here we'll set BT for back test and we'll set that equal to back test then you want provide the data so for us that's goia then provide the strategy name so for us that's RSI oscillator and then we want to select the amount of cash so I'm going to say just $110,000 and you can also select Commission in here if that's something you want I'm going to leave that out just for now and just see how the back testing does on its own we can then print out some stats from the back test so stats is equal to bt. run and let's see how that goes so I told you it was quick that finished pretty much as soon as I pressed the Go Button there I just forgot to print out the stats so you can see that maybe took a second at most to initialize everything and get it running so you can see in here things like when it started what the end time was how many days exposure time various various stats obviously we did way worse than a buy and hold over that time period we had massive draw Downs not the best strategy in the world we can go ahead and plot it quite easily with bt. plot I really like the plotting library that they use it's really nice for zooming in on different things and it gives you that back trader field but it's not like awkwardly done in a map plot lib type thing it's in the web browser so you can very easily interact with it so just to let you know how this sort of screen breaks down the red and green boxes here indicate the months so you'll notice that this one starts on the 1st and it ends on the 30th of June there it's the same with every other one of these boxes it's just kind of a I guess an indication of how the stock is generally doing over time sometimes it can look like there isn't one of these boxes but it's just cuz it's very very small and if you're looking for your trades they're these sort of Dash lines here so you can see this one was a profitable trade because the lines in green and you know it started on this candle and finished on this candle if you go up to here you can select that particular trade you can look at the profit and loss the size of it so you can see that was 27 shares of Google this one was 33 shares of Google this one was 24 shares of Google so on and so forth we can also see some stats here so you can see what the peak of our value was or peak of our portfolio value you can also see the final value so that was 157% the Max draw down which is down here so we had like a you know basically a 50 % retracement during the 2008 2009 crash and it also tells you the maximum draw down duration here which seems to be a very very long time here basically from sort of 2008 through 2012 we were constantly in the red on this position so depending on your style not too nice of a strategy and then down here you just have the indicators so if you're calculating lots of different indicators they'll all appear down here you can turn that off with an option if you just do like plot equals false when you're defining the indicator with the I function that we did before you can turn that off and here is just a volume chart if you happen to have that data so that's just a quick summary of this dashboard if we haven't seen something like this before just to get our bearings figure out what's happening I guess you can turn on and off the trades here if you want to and so this is obviously a really really basic back test but it shows you how quick quickly you can get up and running here in back testing. py with you know maybe 30 lines of code we wrote a nice little strategy here that implemented an RSI crossover now that's really interesting but there are lots of other things that we can do in this Library which are significantly more interesting one of them which I know people like is looking at strategy optimization so we have some parameters here and in fact we Define them as class variables here so we have the upper bound of 70 for the RSI and the lower bound of 30 I'm also going to turn this one into a parameter here so I'll say RSI window and I'll just set equal to 14 by default and we'll plug it in here as self. RSI window now what that allowed us to do is we can access them from the optimizer down here so instead of doing bt. run we can call bt. optimize so bt. optimize and in here here we can put all of our different ranges of values that we want to test for upper and lower bounds as well as the RSI window so we can say upper bound is equal to range and then let's say we want to go from 50 up to 85 in steps of five we do the same thing for the lower bound here except you know we do want go from 50 here we want to go maybe from 10 to 45 in steps of five and as you might have guessed we'll do the same same thing for the RSI window here we'll have a range of 10 to 30 in steps of two now what's really interesting about this indicator is that you can give it a particular statistic to optimize so you can actually pick any one of these so you can pick the sharp ratio you can pick volatility I don't know if you wanted a really volatile portfolio you can pick any of these statistics in here and it will attempt to simulate all the different options and find the highest value so do be careful if you're picking something that you know you want it to be small so for example volatility you can Define custom metrics to optimize and I'll show you how to do that in a bit for now let's pick uh the Shar ratio here and see what kind of functions it comes up with so I want to maximize the sharp ratio so you want to leave that as a string and just call it sharp ratio like that so that should be good here if I just go ahead and let it run it should just work as before so you can see it's testing 490 different combinations obviously this can take a little bit longer and if I pull it up here apparently this is the portfolio that produces the highest sharp ratio out of all of the 490 it tested let's go look at the stats here it's got a sharp ratio of 0.59 so not exactly Stellar but that's probably more indicative of our strategy rather than the abilities of this Optimizer so let's go back here there's one more thing I'd like to show you about this optimize function here which is really cool I haven't seen something exactly like this before you can apply a constraint here so I can apply a constraint and what that lets me do is it lets me select only a subset of these values so for example let's say I set the whole thing here and I I set this from 10 to 85 and this one was also from 10 to 85 well it makes no sense to have the upper bound bigger than the lower bound and obviously you might have some circumstances that are a bit more complex than this one and so what we can do is we can create a Lambda function here and you know that'll take in params and we can create a function that returns true or false based on any of these parameters so I can pick the upper bound the lower bound the RSI window whatever I want so I only want to go ahead with this back test if pam. up bound is greater than pam. lower bound otherwise this makes no sense and so imagine if you had something more complicated I don't know maybe you only want to do it if the RSI window is between the two of these there are lots of different things you can do many different Lambda functions that you can come up with here but I thought that was an important part of the optimizer to look at and something that differentiates a little bit from other libraries that I've seen this way that we can easily get rid of ranges of values that we don't like and we don't have to be too scared about passing in dodgy parameters because we can check for it here so I'll run this again and just to show you this works it might do more than we did before yeah it's got like a thousand different configurations here I'll pull up H stop here just to show you what's happening so you can see it's using 100% of my processing power here so hopefully the video still works basically this just means it's it's very well multi-threaded so it's running separate back on each of the cores of my computer which means that it can efficiently use the whole thing and we don't have to wait for it to just go one by one through the different strategies and if you have an extremely powerful computer you'll notice a really big difference so what's happened here is it's picked one with an upper bound of 30 and a lower bound of 25 so basically it's only making the tiniest possible trades here obviously this is a massively over fit back test because it seems to know exactly when the up trend's coming and it just takes a tiny amount of it and make sure that we don't stay in the market which reduces volatility and therefore increases the sharp ratio so I bet we've got yeah a much higher sharp ratio than we had before obviously that's the danger of over optimizing basically so I'll change this back to the way we had it before where this was from say 55 to 85 this is from 10 to 45 so all that works great and that's how you can optimize your strategy here now something that I mentioned in brief a couple of minutes ago was that you can choose your own optimization function here so it's trying to maximize the sharp ratio in this instance but we can convince it to try and optimize a different parameter as I mentioned before you can pick any one of these but maybe this doesn't quite sum up what you want want to optimize maybe you want to optimize the sharp ratio multiplied by The satino Ratio or you know the Max draw down plus the volatility any metric you can think of that uses some of these stats it'll work just fine so how do we do that well you just Define it as a regular python function up here so we can just Define a function and call my optim funk and it's going to take in a variable called series and all series is going to be is just that list of stats that we printed out and that's what gets passed in there so what I'm going to do is well the variable I want to return is the amount of equity that we have so series I'll copy and paste it from here just to be safe so I want the final Equity here and then let's paste that in here so go series you just index into it like you would any other Panda series and I'm going to divide that by the exposure time series and then I'll go grab the exposure time from here so exposure time percent so this is just how long does it stay in the market it's only in the market 2.7% of the time so that's traditionally seen as something that's a bit safer so what this metric is trying to do is it's trying to figure out how do we make the most money while being in the market for the least amount of time it might not go quite to plan but let's at least try it here so we'll go over here and instead of saying sharp ratio we'll use optim Funk and let go so if I go ahead and look at this here we can see this is again one of those which is does optimize the statistic no doubt about it but it's not quite what we had in mind right it's just taking one swing Trader and then obviously because this is so steep it's only in the market for a couple of days and that minimizes our time in the market so thus is the danger of defining metrics is sometimes you get what you want so what I'm going to do here just to make things a little bit more interesting and just show how flexible this is here this sort of um optimization function and how we can get with it is well I'm going to ask for the number of Trades so what I can do is I can do if series and then I can index back into the series and I'm going to ask for the number of Trades and if the number of Trades is less than 10 well then return minus one so you could return minus infinity here I suppose if you wanted to so this always returns a positive number since the equity is always going to be positive we're not using margin or anything and the exposure time is always going to be positive because it's a percent so if we return a negative number that's guaranteed to be the lowest so it won't it won't appear as the optimum value basically and so we're essentially just filtering out everything that doesn't have at least 10 trades here so we can do the same thing here so if we go to example Y and run it it'll take couple of seconds here but not too bad for 420 back tests and I bring it over here this starts to look a lot more reasonable in terms of trading strategies it's actually making some trades in the market now it had to force its hand obviously to see how many trades it's actually making so it made 17 trades I only asked for a Minima of 10 so it showed that putting that lower Bound in there sort of got it out of that local Minima that it got stuck in and it actually started to find more sensible parameter values for us you may have seen there while I was back in this section that every time you run a simulation with new variables here so you can see you know it prints out the different ones it creates for you a HTML file which you can open so if I go into the fault here I can just open one of these HTML files drag it over and this is the back test you've got it nice and safe here this can be quite handy if you're you know if you're running a load of Mac tests and then a couple of hours later you're like hm what combination of variables did I use or what did that backc test look like this can be super handy it's just saved as a you know like a regular old HTML file here so very compatible with whatever kind of viewer you want to use it does get kind of annoying because it tends to clog up your folder that you're using I believe there is an option here if I go to back test so if I go over here yeah if you go over here to the plot you can see here there's going to be file name here so you could set that to a particular folder so it doesn't clog up your entire folder that you're working in here so that might be something to look into you could set a file name on the plot I'll just show you what that looks like so if you go over here uh I can make a folder M de plots and then vim example.py and I go down to bt. plot here and then you know we could do like file name equals it's going to be like examples SL plots. HTML something like that and then if I run this that should indeed work the folder is actually called plots so that's why that didn't save there so plots plot. HTML so you can see over here it's running April video plot. HTML and you know if we go into this file it saved it here if you wanted to keep the same naming convention you could do that very easily as well so if I just get rid of this and I'm not going to optimize things just because that takes a long time I'll do stats is equal to bt. run so we're just running one instance of it there's an item in stats here which is of particular interest to us so you can go over here to strategy so if I print out stats strategy and then let's see what happens you can see it prints out an object of type RSI oscillator here so if I if I don't print out stats here it just prints out an RSI oscillator and you can get the parameters based on that so like upper Bound for example I can get that 70 you know I can get the lower bound so you can extract all of them from stats like that and then of course you can put that in here so you could say um lower bound is equal to this and then just put that in the file name here Dash lower bound make that an F string there you go that's just a little aside how to name your different plots how to keep them organized stop them from clogging up your directories etc etc for the remainder of this tutorial I'll just leave it as blank but that's just a little bit about how you can name them so okay we've done some optimization here and we've created our own unique function that we want to maximize as this as python you can do literally whatever you want you can run some crazy function on it in order to maximize whatever metric you're interested in one other detail about optimize that's quite helpful to know about is what happens if you've got a really wide range here of different values that you want to test so if I ran this right now i' I'd get something like 20,000 different back tests that want to try and simulate and I mean I could definitely sit through here and wait for the 20,000 B test to finish but that's that's really not nice it's not optimal so what you can do is you can set a max number of Tri here and what that's going to do is well if it if it's less than the number of possible combinations it's going to do a randomized grid search so instead of trying every possible combination it'll just try the number that you tell it to so I've told it to do 100 here there's definitely more than 100 different combinations of these three different variables so let's see what happens I think I forgot to get rid of the dot run maybe stats to BT to optimize I think it just finished really quickly cuz it only had to do a 100 of them and it should get a different one each time so I'll run it a few times and then I can show you the different ones yeah so if I pull these both over here so I just ran this the exact same test twice there and yet I got two completely different back tests and that's because it's doing only 100 different options so I told it to do 100 and there's maybe 20,000 in here it's picking them random randomly and obviously if you pick them randomly sometimes you get good one sometimes you get bad ones this can be just a nice way of testing out a strategy if you don't want to do an exhaustive grid search that would take hours and hours and hours especially while you're in development just using Max tries here can be a really nice way to evaluate the strategy without having to do some crazy back testing and it also reduces the chance that you're over fitting because it's it's it's randomly selecting values rather than grid searching every Poss POS value that you'd possibly want and finding the absolute maximum which is almost guaranteed to be massively overfit if you're running 20,000 different simulations so I thought that was just an important little argument here for bt. optimize that's well worth using if you want to save some time so something that we might want to add to this optimization here to sort of make things look a bit prettier is to print out some heat maps of these different values here so we could compare for example you know what happens as we vary the upper and lower bounds how does that affect things so what I'm going to do here is I'm just going to set the value of the RSI window just straight to 14 just to prove my point here in what's happening so we've got stat Speedy optimize normal sellup except that we want to return a heat map here so that's another fancy option of bt. optimize it's return heat map and we'll set that equal to true and if I go over here and we get stats and we also want to get heat map out of this so let's go ahead and just print out heat map here as well as stats and we have no need to plot this really for now I think it did like that I changed the RSI window there so I'm just going to change that back we go over here let's see if it's okay now oh it's actually nothing to do with that it's the fact that I missed spelled return good old spelling errors you can always count on those return heat map so there we go you might notice that this looks nothing like a heat map I actually didn't want to plot that out so we'll go down here yeah you might notice that the information that we get back here looks absolutely nothing like a heat map don't worry we'll fix that in just a second here so what we want to do to this data is just a little bit of a transform here so what we're going to do is a regular pandas Group by statement so I'll say hm for heat map is equal to heat map do group by so we're going to select two different columns that we want to group by so we want to group by the upper bound and the lower bound now this is easier to explain once you actually see it physically so I'm just going to type this out and then you'll see what's happening mean and then I'll print out hm here so we've got the two different versions here we've got the one with three columns and the one with two columns so essentially what the group by statement does is it finds all unique combinations of the columns that you mention so in our case I mentioned the upper bound and the lower bound if you go back to here I mention explicitly the upper bound and the lower bound so it finds all unique combinations of those which is every single Row in our case so 55 and 10 55 and 15 55 and 20 so on and so forth and then there's all these other columns here and it bundles those together so say if we had a 55 10 14 but we also had a 55 10 15 well the 14 and 15 rsis here those would get sort of uh bundled together in a group and then to that group would apply an aggregation function so in this case it's the mean so it take that whatever the average of the return was for those two values it's just a regular pandas group buy you don't really need to worry about it in our case to be honest pretty much all it's done in our example is it's just got rid of this column if you actually compare these values that there's no change and that's because every row has a unique combination of these two values I'll go through some more examples in a second it can be a bit weird to get your head around at first Okay so we've got that and what I'm going to do is I'm going to unstack it so if we put in out again we'll now get a nice 2x two Matrix of all our different lower bound values and all our different upper bound values and well this can be plotted quite neatly in the caborn module so you have to go and import caborn if we import caborn as SNS and then we go down here and we'll do SNS do heatmap hm and then we'll want to do PLT Dosh show so this is just a a thing from M plot lib that you might may or may not be familiar with we'll have to go ahead and import M plot lib as well so import Matt plot libp plot as PLT basically because uh caborn uses map plot lib we use the map plot lib way of plotting things not terribly important for this tutorial this is just how you can plot single heat Maps based on that information if I let that go we should be good okay we don't have caborn installed pip install CN there we go so if I let that run now we should get a nice heat map so there you go um obviously this is using our slightly strange custom indicator we can bring this back to any statistic that we like so let's find where we printed out some stats that there should be somewhere up here there we go so let's try and optimize for Sharp ratio for example we'll go down to the bottom and instead of our optim Funk we'll just use sharp ratio just makes things a little bit cleaner same thing evidently these ones here aren't making any trades so we get just no value at all and these ones we can see roughly where the best sharp ratio is to be had you can also change the coloring of the Heat Map There's a nice documentation page here for map plot lib if you you if you Google choosing color maps in map block Li you'll probably find it it has a nice list of all of the different color Maps there are many many many of them that you can pick from I'm just going to stick to some of these here so we could try veridus up here this one's quite nice and so plasma so I'll show you what those look like so when you're plotting the heat map here in seor you say color map so C map is equal to plasma let's try plasma so you can see the color slam slightly different there maybe I'll pick one that's a bit more dramatic so I think that's how you spell that y so you can see it's sort of a um like a green color here that can be important if you want to make a really pretty picture out of the parameters that you're optimizing okay so that's great we had a singular heat map here what about if we have more than two parameters because a heat map is a 2d form you can't plot more than one heat map really you can't plot more than two variables on a on a 2d ship that that just doesn't work so what we can do is we can plot every possible combination of heat Maps so there there's a nice function that comes with back testing. py that I'll show you here so if I now have a range of RSI window values and I I get rid of all this other heat map stuff that we did before there's an inbuilt function that comes with it and we can go ahead and import that so it's from this Library here that we get crossover from it's plot heat Maps very memorable name and then all you have to do is just run plot unor heat Maps then you'll want to give it the heat map that you get from the optimizer it understands how to read this format you don't have to do any unstacking or anything and you also give it an aggregation function now you'll see what that does in a moment when I run it this could take a little while depending on how many values there are so we'll we'll see what happens so that's come up on my other window here I'll drag it across also seems to have uh retained the color scheme that we last use which is interesting so you'll notice that there are three different heat Maps here and they represent all the the three different combinations of those three variables so you've got the lower bound and the upper bound you've got the RSI window and the upper bound and the RSI window and the lower bound now what happens to the third variable well it averages across all of those values so I'll explain it just for one of them maybe it's a little bit easier to understand so in this case what's happened is it picks a value of upper bound say 65 and a value of lower bound say 35 obviously in our calculations we've simulated that for a wide range of RSI values different RSI window lengths in this case what happens is it takes all of those simulations where the upper bound is 65 the lower bound is 35 and the RSI can be wherever it wants and it takes an aggregate of them now we supplied the aggregation function so I selected a mean value here so what happens is it picks the average of all of the simulations with these upper and lower bound values that's basically how this works you you could provide a different aggregation function if you wanted to of course this can be a nice way of just thinking about how do different variables relate to each other what happens when you vary one against another and you keep the other one constant or you take the average of it so it's not having too much of an effect as you get more and more variables this gets obviously crazier and crazier cuz it's got to take every single pair of them so it's going to be you know your number of variables choose two so if you were crazy enough to try and optimize 10 different parameters you'd have a real struggle on your hands here so that's pretty much everything to do with heat Maps parameter optimization that kind of thing that's pretty much everything in this library that covers those kind of topics what I'm going to move on to now is different kinds of strategies so we're going to start looking at how we can do multi-time frames strategies I know that's something that a lot of people use especially if you're using a TA based strategy you often want to say look at the RSI on a weekly basis and the moving average on a hourly basis or vice versa or whatever you may need so how do we Implement that well I'm just going to get rid of this optimization here because it's going to slow us down quite a lot I'll just do stats is equal to bt. run and we'll print stats so we'll not look at the plot for right now and so we'll just go ahead and re-evaluate how we've written this strategy here so currently it's very simple we have a daily RSI whenever that goes over the upper bound we sell whenever it goes below the lower bound we buy well I want to add another RSI in here just just to make sure that we're actually in an overheated period I want to add a weekly RSI on top of the daily RSI and I want to only buy when they're both at the values that I care about so in order to do that we'll have to import another function from the back testing Library here and that's going to be the resample apply so this function is going to really help us out when it comes to finding other time frames and essentially how we do it is we start off with our highest resolution data so in our case it's going to be the RSI here this daily RSI value I'm going to go through and change all references to it to daily RSI just so that we're clear on which one's which so this is daily RSI perfect now in this init section here we can create a weekly RSI so let's do that self. Weekly RSI well that's going to be equal to resample apply and then you want to give it a panda time offset here so for example if you wanted hourly it's like a capital H like that if you wanted minutely it's a capital T like that there are many these different time offsets that you can use W is weekly which is the one that we're going to be using and you can also choose to Anchor it on a specific day so when we think about the weekly close we think about Fridays values we don't think about Monday's values and so since we're interested in the close value here that's what be using I guess if you were in crypto maybe you'd think about Sundays the end of the week I'm not entirely sure how that works but you can implement it however you want you then want to put the function in here that you want to apply the set of data that we're using so it's going to be self. dat. close and then any arguments that go into the function so for example for us it's going to be self. RS iore window I'm just going to keep it like a 14 week RSI you know whatever this value happens to be I'm fine with it being the same for the daily and the weekly although if you wanted to put that in as another parameter and optimize it that would be great too there's an example on the document mentation here of pretty much what the resample applies doing behind the scenes it's essentially just leveraging pandas so it's downsampling the data then it's applying our function to it then reindexing back to the smaller time frame and then doing a forward fill don't worry if you're not too clued up on exactly what this is doing thankfully we have a library to do that all for us all you need to know is you put your time frame in here that you want to down sample to the function and any arguments to it and back testing. py here will magically do the rest of it for us so that's great we've got a daily and a weekly RSI now so let's try and use this in our strategy here so I only want to do a sell here if there's a crossover on The Daily and the weekly is above our ound so and self. weekly RSI minus one so the most recent value Mac testing. py uses the same indexing as python so minus one is the most recent one minus 2 is the next most recent one so on and so forth that can be really helpful like in this case where we want the most recent value of RSI and so we want to know if the RSI is greater than self. uper bound and we'll close out that bracket there so now it we'll only do that buy if it's confirmed on the weekly and it has the daily crossover we can do the exact same thing over here but with the buying so that will just be very similar we'll do and self. weekly RSI the last value of there is less than self. lowerbound and then we'll go ahead and plot this out cuz it can be a bit difficult to tell what's going on just from a list of snaps so here we go it only seems to have made maybe one trade in the whole thing so it Port here I guess we've just put in our manual values here hence why the strategy is not too great we could optimize this I'm sure using the optimizer and we' get some interesting things so all we're interested in is is the strategy actually doing what we wanted it to do so yes we have a weekly RSI of less than 30 here so that's good and do we also have a crossover here that is the question so yeah we go from so we're at a weekly of 29.7 and then we go from 3 4 to 29 that triggers the buy and then on the next opening candle it buys the assets and I'm sure if we were to check you know it wouldn't buy if it wasn't confirmed on the right time frame and it wouldn't sell if it wasn't confirmed on the right time frame so not the greatest strategy in the world it only made one trade in sort of 10 years but again I'm sure you could put that through the optimizer and come out with something a bit more interesting that's just how we can use different time frames within our overall strategy you could also do something I don't know say on this strategy you could say well I only want to buy if there's a crossover on The Daily and the weekly is less than the bound and the daily is less so self. daily RSI the latest value is less than self. weekly RSI minus one so you can endless combinations like that you could also say maybe I only want to buy if the daily has been decreasing the last day so you could do something like this so if the most recent value here is bigger than the next most recent value or it's bigger than the RSI value of two days ago or 7 days ago or infinite options here I'll let you decide what exactly you want to do that's how we can use different time frames in our strategies so there's a few more things that we can run through here in one thing that you also might be interested in in this library is how do we do different order types so at the minute we've just been doing long only we buy at this point and then we sell at this point we don't do anything too crazy other than that but how would we do something like reverse our position if a certain condition is met or do two Buys in a row or various other options how do we do stop loss take profit I'll cover that right now so let's try and implement some kind of reversal strategy based on the daily RSI here so we'll get rid of the weekly we don't need it anymore and we'll go over here and we'll simplify this so we'll get back to how we were before very simple buy and sell here but instead of just closing our position here let's initiate a short so we'll do buy do sell and then when we do a buy here well we want to close out any shorts that we have so we'll do self do position do close so let's run this and see what happens so when the RSI goes below the lower bound close any short positions buy and do the inverse over here so if it goes above the upper bound sell any long positions and go short let's just see what that looks like on a graph here apparently I've missed some syntax my brackets here okay if I drag this over we can have a little look you'll see that every trade is connected to every single other one here that's because every time we open a position every time we close a position we also open a position U you might not necessarily want this cuz it's giving us you know lots of time in the market and we've lost 80% of our Equity by the end of it so let's see if we can follow the logic here of what's happening so at this point it's decided to it it's made a loss so over here the RSI is 70 so it's gone ahead and bought something so it's had a crossover to the upside so we know from our strategy it's bought and it's closed out a short I believe it's closed out a short loss the price increased but we were shorting so that wasn't good and it's gone long then at this point here the RSI has crossed again above 70 and so it sold our position and gone short again so remember we didn't have any logic to stop it taking multiple positions or freaking out like it is right now so this is why you got to be careful about your trading logic so it's taking a short position here and then when there's a crossover to the upside again it closes a short position and reopens the same short position which is not exactly the behavior that we want and I believe it's doing the same thing here so let's try and fix that and see if it mildly improves the performance of our portfolio here so what's happening this crossover is happening it's closing the short and then taking out another short position and that that doesn't really make sense at least not to me so we can go back to our trusty documentation here and if we go to the position class here there's a nice couple of variables that is long and a short so we only want to close out here when this is a long otherwise it makes no sense initiating a short when we already have a short position so I mean unless you wanted to double stack them but that's not what this is doing that's not what we intend to do so what we can do is we can say if self. position do is score long well then and only then will we close out our position and actually initiate short and you can do the same thing on the other side here so we only actually want to close this and initiate a buy if we're actually short so we'll do the same thing over here if self. position. isore short that'll return true or false which you know it'll give us our regular if statement here so let's run that see what happens and if I pull that over you'll notice that we've made no trades now that's because our logic only buys or sells anything if we already have a position so if I look at what happens here well we can only buy if we already have a short position and we can only sell if we have a long position where about if we have no position nothing happens so again trading logic can be tricky to fix this fairly obvious all we have to do is at some point either up here or up here we include a little phrase like or or we don't have a position basically and so you can Implement that as or not self. position so self. position is true or it evaluates to true if we have some kind of position and it evaluates to false if we don't have a position so now if we run this back test we should get something more interesting here we go so it should at least be initiating the right sort of things it shouldn't be doing the same thing over and over again so here it looks like it had a profitable long position engages in a short which which is unprofitable I guess at this point the RSI goes down to 22 so it goes in a long but then that you know that doesn't work out either at least we have 70% of our money rather than 20% of our money and the strategy appears to be working as implemented although obviously is not a great strategy again we can worry about optimizing that later on so that's how shorting works here that was just a very quick example of how you can buy and sell here I'll be showing you how to change your order sizes in a second one one thing that might be interesting to you is to print out the value of position here so print self. position so just before it makes any kind of trade print out position we'll see what that looks like so it's not actually very interesting here it just has an ID basically but we can print out different attributes of it so I'll go back to the documentation here and you can print out a few attributes so say we could print out the current size for example the size we can print out the percentage profit and loss at the current moment so you can imagine building your trading logic based on these if you wanted to lots of things you can do like that I guess these are right before we sell short this is what we get so these values should be in here somewhere that might be interesting too depending on what what kind of uh things you want to build okay so that's shorting how about take profit and stop loss well that's also really nice nicely implemented here in back testing. py I'll go ahead and simplify this again back to something that's really really easy so we'll do uh this we won't bother with any shorts just because that complicates life I think this is what you want so this is back to our really simple bu hold strategy but as a Twist we're going to add a tick profit and a stop loss so let's run the back test and think about what values we might want to add a take profit and a stop loss so you can see here we take a 20% loss we kind of want to avoid that so maybe it would be a good idea to have a 10% stop loss and see how that affects the strategy so you go in here and all you have to do is set SL and this needs to be an actual number so I'm going to set it to be 0.95 time the current price what is the current price you might ask well to extract that we can just index into the closing price data so go to is equal to self. data. close and then we just get the most recent value here so that should Now set a stop loss where I shouldn't encounter much more than a 5% loss obviously the way back testing. py works it won't trigger the stop loss like intrabar intraday if you wanted that you'd have to use hourly data or something like that so you might end up with a scenario where you do actually lose more than 5% so you can see here what's happened is that the stop loss has triggered here yeah there's been some strange interaction here where the the buy and the stop loss happened at the same time can happen if you're not careful but I guess just to see the stop loss at work here we can zoom out and we can see that we don't really lose more than 5% on any of our trades just because of how the stop loss is working now often times because of our buying conditions well the stop loss triggers but it buys again immediately so that can lead to further selling so something that you might want to keep in mind when you're setting your stop losses take profits is also pretty easy to do so it's just TP is equal to and then you can use whatever you want in here it doesn't have to be a multiple of price it can be you know you can add the average true range you can add any indicator that you want you can add a constant value whenever you make $10 then you take profits whatever it is you happen to want to need I'm going to use 1.15 times price so whenever we get a 15% profit we cut and run I'll drag that across so you can see and now basically our profits and losses are now range bound within this region obviously one might want to alter our selling tactics here because it's selling and then immediately reing the candle after which is is not ideal but this was just an example to show you how stop loss and Tech profits work so I'm not going to worry too much about that right now another thing that we might want to think about here is is how do we size up our positions when we buy or when we sell well we can do that pretty easily using the size parameter here in the buy so I'll get rid of the stop loss and the take profit we've seen how that works and there are basically two separate ways here of using this so you can either provide a size is equal to say some decimal here so 0.1 now back testing. py will interpret that as saying you want to spend 10% of your available cash on every B here so if I press go we should see that on the graph it's not buying huge positions so we shouldn't end up with a massive payout here at the top whenever it gets a cross over here it's just buying 10% of our available funds and so obviously you know this is 10% and this is 10% of the remainder 10% of that so on and so forth so you can end up with these nice little patterns here but they're much smaller than and if we go all in by default it just spends 100% of the available cash and buys as many shares as it can again I don't think by default back testing. py can do fractional shares which you know that can be a little bit of an issue here so we can see it's essentially buying one at a time here so if we look at these individual trades you know it's buying one share of Google at a time and then selling them all at the end here when the sell conditions met but because we had lots of crossovers here the buy condition was met many many many times another way of implementing that is to instead of giving a percentage here of the available cash you can just tell it how many of the asset you want to buy so again no fractions you have to give it an integer but I could just say that I just want to buy one share every time that condition is met I want you to buy one share and then eventually when we get to the sell here we just close out every single one of those positions let's see what that looks like so it looks like this this very similar to the previous one we saw here except that every single one of these is going to have a size of one so it's just buying one share and then selling one share obviously that limits your downside but also limits your upside but it can be very helpful if you have some strategy that revolves around laddering into a position or some kind of dollar cost averaging so we could modify this slightly and instead of working on a crossover let's just buy any time that the latest value of the RSI is smaller than the lower bound so take the minus one there and that should work nicely of course the lower bound is just a number it's not a vector so you can't index into it this should work though so here we go go over here and so now we see a lot more buying action and basically every time the RSI is low we buy so this is kind of like a funky dollar cost averaging strategy basically where where we just buy during these dips and then we sell up here we're not in the market that much and there's not that many opportunities to trade but you could imagine this across multiple stocks where you're hopping in and out of them and although this is only returning 40% over the time period we're not actually in the market very long at all I think if we go look at the stats here we're in the market 30% of the time so the other 60% of the time you can find something else to do with the money again just an example but it shows how you can you can Ladder into positions by intelligently sizing your positions and of course you don't have to set some kind of um like straight up value for this I mean you could have this be a class variable in here so you have you know self. position size and then you set self. position size up here position size is equal to I don't know one and then you can optimize that and see what it comes up with as the best possible position site for your amount of cash that you have on hand the same goes with any of this stuff really I mean you could um you could do the same thing for this you could say minus one times some variable and see which candle you're best off looking at maybe you're actually best off looking at the candle from 5 days ago so as with all the things I'm mentioning to here just keep in mind that they can pretty much all be used with the optimizer so just just have that in the back of your head there and you can make some cool charts and cool heat maps and massively overfit things so that was position sizing let's have a look at how you can extract some trades out of the final back test here so when we print out stats you'll notice down here there's a thing called trads so similar if you remember we got the strategy earlier and we extracted what the parameter values were right there's the same thing over here this is trades so this is a panda's data frame with all the different trades on it and lots of details about them just because I've noticed here this sqn this is the default parameter which back testing. py optimizes by if you don't pick anything else so if you don't pick a metric to optimize buy it uses this you can look up exactly the formula for it in the docs I just thought and mention that so back to trades here what I can do is I can go over here and down at the bottom instead of printing out stats I'll print out stats but I'll take the trads go in here and run it and it's going to give us this because it's too big so so let's go down here and instead of doing that we'll take two uncore string so we'll convert it to a big string and that way it'll print it all and it won't abbreviate it we'll have to zoom out a little bit here but we now have a very clear data frame here of all of the different trades pretty much every metric you want to know about it exactly when it entered and exited we have the entry and exit as a date time object here and you can plug this into your favorite visualization tool if if you don't like the way that back testing. py does things you can always export this and plug it into whatever you want you can plug it into Excel Power by other Python scripts because it's open source of course you can modify back testing. py itself or maybe you just want to keep this for some kind of historic records feed this into another algorithm always worth mentioning with back testing libraries how you can extract the actual raw TR trade data for whatever further analysis that you want to do maybe you want to work out the average duration of the trades many different options one final thing I'm going to show you here is a really nice function I discovered in the documentation and it's called bars since so we import it from the library here it's pretty much the last of the the useful ones out of here or at least the useful ones that I found so it's called bars since and what this does is you give it some kind kind of condition that will return true or false and it tells you how many bars ago it was last true so for example I could go over here and I could say that well I only want to sell after we've had a crossover to the upside here so the daily RSI is over the upper bound and I want it to remain above the upper Bound for 3 days and then I want to sell so we could use this really nice function to do that for us otherwise we'd have have to write out you know a load of if statements we'd have to compare you know what were yesterday's values what were the day before values it' get a little bit messy so this is a much nicer way of doing it so we can say and go down here bars since self do RSI is less than self do bound so this is our condition here I'll Zoom back in I notice we zoomed out there so this is our condition so this evaluates to true or false and so I want the daily RSI to cross over and I want it to have been at least 3 days so I'll say equal to three cuz then it'll it'll trade on the third day so I want it to have been 3 days since the RSI was below the upper bound here just an interesting tweak on this strategy that we've got going here and I think this bars since function has a good amount of potential if you use it in the right circumstances so let's just verify that that's actually working of course I've made a syntax error here I need a bracket and of course it's called The Daily RSI now not just regular RSI so I can go over here now now evidently what's happened is that I've made some kind of mistake here because it's just not selling at all so let's go have a look at this strategy again yeah so it's CU I've used a crossover and a bars since so this only is true on the exact bar on which there's a crossover but this this is only true 3 days after there's a crossover so you can see the problem here in order to fix this all I have to do is not delete everything I have to go over here and we'll just do self. daily RSI pick the most recent value minus one and that just needs to be greater than self uper bound I think that will do the job that we actually intended it for of course I need to get my brackets right um actually greater than up m so there we go that's something more like what you would expect we have all these different buys here of size one and then we eventually sell when we get a sustained rally so let's go over here we can see well where's the cross over here so it crosses over at this point it crosses over on this day here I think this one so over on this bar and then one two and then on this day well how many bars is it been since it was less it's been 1 2 3 this was the last day right and so on this day it triggers the buy the sell rather which actually occurs on this day just because of how back testing. py works so that's the bars since function it's quite nice I like it I can imagine some really cool scenarios and different things you can do with it so that brings us to the end here of this course in how to use back test dopy I'd highly encourage you to go look at the docs now that you've run through most of the important features of this Library as you can see the docks are very short yet very well documented so I'm sure you'll have no problem finding whatever you're after I hope that you've had fun exploring this library and it's giv you some new ideas for back test that you can try out I'll mention again the text versions of this tutorial are in the description and so with that being said I wish you luck in your back testing adventures and have fun