If you enjoy this article you may be interested in the book I am working on called Building and Deploying Crypto Trading Bots. You can find more info about it here.
Let's look at an example of the strategy pattern in Ruby.
A common design pattern in software development is known as the "Strategy" pattern. The pattern proposes that when you have a scenario with multiple execution paths, each path be considered as a "strategy" that is recomposed into individual objects (or functions in functional languages). Each Strategy contains the algorithm implementation to execute that code path.
The benefit of using this technique is two fold. First, it allows the algorithm to vary from the clients that uses it. Second, it liberates instances of logic ("strategies") from individual client implementations allow for higher reusability within the software. If you enjoy UML diagrams you can see a visualization below:

To provide a more concrete example consider if you needed to write a program that forecasted the moving trend of a particular stock. In technical charting terms this can be computed by calculating the "Simple Moving Average" of a stock price. We want to write a program that calculates the twenty four and twelve day moving averages and tells us that direction the stock price is going. Sounds easy enough – if we know the strategy pattern.
We begin by defining an abstract base class called Strategy:
# Abstract base class
class Strategy
def initialize
raise "abstract"
end
def exec
raise "abstract"
end
end
This base class is what all our Strategy objects will inherit from. We then create two concrete strategy objects TwelveDayMovingAverage and TwentyFourDayMovingAverage that inherit from the base Strategy class and override the interface's "exec" method with their own algorithm to return a result.
class TwelveDayMovingAverage < Strategy
attr_accessor :data
def initialize(data)
@data = data
end
def exec
# data analysis and return 'up' or 'down'
end
end
class TwentyFourDayMovingAverage < Strategy
attr_accessor :data
def initialize(data)
@data = data
end
def exec
# data analysis and return 'up' or 'down'
end
end
Finally we have the context under which the strategies will be called:
class TradingBot
attr_reader :trade_strategy
def initialize(trade_strategy)
@trade_strategy = trade_strategy
end
def should_trade?(data)
case strategy
when :twelve_day
TwelveDayMovingAverage.new(data).exec
when :twenty_four_day
TwentyFourDayMovingAverage.new(data).exec
else
raise "invalid strategy"
end
end
end
In the listing above a simple TradingBot
class is instantiated with a trading strategy value. This initialization value is used to select the appropriate algorithm to use at run time when the trend?
method is called by a client class. A full use case may look as follows:
# !/bin/ruby
# Program to demonstrate market indicators for Apple, Inc stock
# Instantiate two trading bots
bot12day = TradingBot.new(:twelve_day)
bot24day = TradingBot.new(:twenty_four_day)
# Read JSON from file
data = File.read('testdata/APPL.json')
# Ask the bots what the trend is, they will determine the correct
# algorithm to use
12_day_trend = bot12day.trend?(data)
24_day_trend = bot24day.trend?(data)
# Tell the people
puts <<-MSG
Based on two analyses the twelve day moving average suggests APPL is going #{12_day_trend} while the twenty four day moving average suggests it is going #{24_day_trend}
MSG
By abstracting the strategy here we gain the flexibility to reuse the same TradingBot
class and can also implement the algorithms elsewhere.
Have fun!