Solving A Common Issue With The Strategy Pattern // In Python

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
is an issue that occurs quite often with the strategy pattern the idea of the pattern is that you can swap out algorithms without changing the code that uses the algorithm for example you could use the strategy pattern to switch between different file compression algorithms but what if you want to change some settings that are specific to a particular compression algorithm for example zip has a compression level option but zlib also has window bits chunk size and more today i'm going to show you three ways to solve this but actually only one of those three is a good solution generally before you even apply things like the strategy pattern it's important that you understand the problem that you're trying to solve i wrote a guide to help you with this it's available for free at ioncodes.com design guide it describes my process for designing a new feature or new software application and it may help you structure your thoughts as well so iongcodes.com design guide now let's dive into today's example we're looking at a trading example today we have two files one contains a simulator of an exchange it's an exchange class it has a connect method it has a method for verifying that the connection was established and then there's a couple of methods so one is a method to get market data that returns fake price data for particular symbols like bitcoin us dollar or ethereum use dollar then we have a buy method and a sell method so normally of course these fixed prices here you would get them from an actual exchange but this is just to keep things simple for this example and so can run standalone the example itself has a main method that creates the exchange connects to it and then creates trading strategy and the trading bots and then runs that bot for a particular symbol so the way this is set up is that it uses the strategy pattern for the trading strategy so there is a trading strategy abstract class that has a method should buy and should sell we have an average trading strategy and we have a min max trading strategy the average trading strategy checks whether the current price is lower or higher than the average over the last few prices the min maxer id checks if a price is below or above a certain fixed value and based on that determines whether you should buy or sell now strictly speaking this is not a strategy pattern because the strategy pattern has class with a single strategy function so i'm taking a bit of creative freedom here if you really wanted to make this a classical strategy pattern you should probably split the trading strategy into two abstract classes a trading buy strategy and a trading cell strategy and each of those would have a single method but i don't think it makes much sense in that case it adds a lot of extra complication and what i'm going to explain works just as well for this example the trading bot is another class that gets initialized the exchange and the trading strategy and then has a run method that for a particular symbol gets the prices determines whether they should buy or sell and then call the buy or sell method what i'm using here by the way is dependency injection because i'm injecting the dependencies of the trading bot on the exchange and the strategy here and that means that the main function is responsible for creating all these things so the exchange the trading strategy the trading bot and then it patches up everything near the end and then it runs the strategy the issue with the example is that even though the strategy pattern helps separate concerns there are still hard-coded parameters in each of the different strategies the average trading strategy has a window size for example and the min max trading strategy has minimum and maximum prices and that's an issue because actually these prices their bitcoin prices if i want to change my coin to let's say ethereum then i'm going to get something really weird actually let's let's run this example as is to just show you what it's doing at the moment so currently this is what's happening it's selling bitcoin which is a good choice i'm giving off way too much about bitcoin if i change it to ethereum like so it's now the problem that actually what i would want is to be able to change these values here to something else but unfortunately at the moment i can't do that easily now would be nice if strategies would have extra options to change these values but how do you set that up what's the right place to do it and that's what i'm going to look at today so let's look at three possible ways of solving this issue the first thing you could do is add keyword arguments to each of the strategy functions and then use that to pass parameters so let's change these strategies so that they accept extra keyword arguments so what i'm gonna do i'm gonna add here to the should buy and shoot cell method my keyword arguments parameter and that's of type float actually dictionary of float so let's copy this to all the other should buy and should sell functions as well so there we have it and now what we can do is instead of taking this number as a hard-coded value we can get it from the keyword arguments so let's say that in the average trading strategy we're going to have an argument called window size so we're using get and as a default value we're going to get 3 and then let's put window size here let's do the same thing for shift cell and the min max we can also get these numbers from the keyword arguments this is not really needed anymore and let's add a max price as well that's 3300 let's make sure these are floats so now what i could do is pass specific values to these different trading strategies there's two problems with this one thing is that you're losing a lot of typing information for example it's not clear that should buy and should sell expect a window size it's also not clear that min max expects a min price and the max price you kind of have to guess that from the code or maybe you have to put it explicitly in a dock stream so that's not so nice another thing is you can only indicate one type here well actually window size is probably more logical that that's an integer and min price and max price should be floats or probably decimal in a production system so it this is not ideal so you could maybe use a union type like float or in but that's still quite ugly and there's also something called a typed dict which might have been useful except that you can't use it with keyword arguments unfortunately so let's look at a second solution that offers a bit more control over the type and for that what we're going to do is create a parameters class that contains all the parameters that we need in our strategies and then instead of passing the keyword arguments we're passing an instance of this parameter class and then we can use typing so let's first create a class called strategy parameters actually let's use a data class for this and this is going to have a window size it's going to have a min price and a max price so this is a class that represents a union of possible parameters for the difference strategies so i have window size min price max price and now our trading strategy gets an extra parameter which is an instance of strategy parameters so let's copy this to all the other should buy and should sell methods there we go almost there yes there we go and now what we can do is instead of having this keyword arkstop get we're going to change that and then rename this to params.windowsize and we can remove this from here and let's do the same thing here and here is the same thing and now what we can do is add extra parameters here for example i could add a strategy parameters with a min price of 31 000 and i could add a max price of 34 000 or whatever it doesn't matter now you see i'm able to supply these parameters to my strategy and i have the typing that helps me establish what i'm supposed to provide so this works but this also has a problem one is you now have direct coupling between all the different strategies because they share the same set of parameters window size belongs to average trading strategy min price and max price belong to the min max trading strategy so this is becoming a mess because every time you add an extra strategy you're going to need to add parameters here and strategy parameters is going to be a huge collection of all kinds of unrelated parameters and that means that it's still not clear in the trading bots which options belong to what strategies so that also is quite messy i've seen a few papers that have discussed a generic abstract parameters class and then having arrays of parameter subclass instances so you could have an int parameter subclass and a bool parameter subclass etc it might solve this issue but it still doesn't feel very clean to me to kind of re-implement the different basic types that are already in there in python so it's a pretty complicated way of solving that and actually it doesn't solve the bigger problem that's in this code the overarching problem of the two solutions that i've shown you until now is that the run method in the trading bot needs to know implementation details of specific strategies to set those options so we tried decoupling everything using the strategy pattern but because of this the decoupling is completely undone so let's look at one more solution and i think this is how you should do it so i'll remove this strategy parameters monster from my code there we go and let's uh remove this one as well from the should bind cell methods and then we're going to do this slightly differently and what we're going to do in this solution is actually attach the parameters options to each of the specific trading strategies themselves so what we'll do is we have the average trading strategy and we're going to add an initializer that allows you to set these parameters and we can even use a data class for this to make it really simple so the average trading strategy is going to have a window size which is an int and has a default value of three and then here we don't need parameters we can just say self.windowsize and here as well and the min max strategy is going to have a minimum and a maximum price i'll just copy that over from the class we have here there we go and now we just write self dot and self dot and i can remove the strategy parameters instance here because we don't need that anymore there we go and now this class is not used so i'm going to throw that away and now we have again clean should buy and should sell methods average strain strategy has a specific parameter to that strategy and the min max as well and where do you set these values well we're going to do that when we create them so i have here my min max and i'm going to say min price equals 31 000 and a max price equals four 34 so what's the result well now we have our specific strategy each of them has parameters options that are specific to that strategy so that's where they belong it makes the most sense and we're setting them at the place where we already have the coupling because this is the place where we create the actual strategy instance up there if we set the options there it makes total sense because we know at this place that we're using a min max trading strategy and then our trading bots is again very clean because it just gets the strategy the options are already set so it simply runs the strategy and does what it needs to do it doesn't need to know anything about how the strategy is implemented what options the strategy has etc this also shows you the advantage of using something like a class for a strategy pattern because a class allows you to combine data with behavior and if you're setting options like this that's data that's part of of an object so class allows you to do this kind of things if you would use pure functions for this just passing functions around that do particular things and this would be much harder so in short being able to combine behavior with data is one of the powers of classes so don't be afraid to use them so conclusion if you want to have strategies that have different parameters or settings set them in the initializer because that's the place where the coupling happens if you can't set these parameters in the initializer for some reason you could add separate methods for getting and setting these values but you have to watch out that you're not introducing new coupling where you shouldn't in fact this might be a reason to reconsider how you set up everything because it points to a potential issue in your design that was it for today i hope you enjoyed this if you did give this video a like subscribe to my channel thanks for watching take care and see you next week
Info
Channel: ArjanCodes
Views: 46,508
Rating: undefined out of 5
Keywords: strategy pattern, strategy pattern example, design patterns, strategy design pattern, software engineer, system design, software design, strategy patterns, object oriented design, strategy pattern examples, head first design patterns, design patterns interview questions, strategy pattern example python, software design patterns, software design principles, software design and architecture, software design and development, software engineering, software engineering tutorial
Id: UPBOOIRrl40
Channel Id: undefined
Length: 14min 43sec (883 seconds)
Published: Fri Aug 06 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.