Module: Quant::Refinements::Array
- Defined in:
- lib/quant/refinements/array.rb
Overview
The behavior of out of bound indexes into the Array deviates from standard Ruby and always returns an element. If an array only has three elements and 4 or more are requested for n
, the method constrains itself to the size of the array. This is an intentional design decision, but it may be a gotcha if you’re not expecting it. The refined behavior generally only exists within the library’s scope, but if you call ‘using Quant` in your own code, you may encounter the changed behavior unexpectedly.
Refinements for the standard Ruby Array class. These refinements add statistical methods to the Array class as well as some optimizations that greatly speed up some of the computations performed by the various indicators.
In addtion to the statistical methods, the refinements also add a max_size!
method to the Array class, which allows us to bound the array to a maximum number of elements, which is useful for indicators that are computing averages or sums over a fixed number of lookback ticks.
There are some various performance benchmarks in the spec/performance directory that show the performance improvements of using these refinements.
Keep in mind that within this library, we’re generally concerned with adding to the tail of the arrays and rarely with removing or popping, so there’s few optimizations or protections for those operations in conjunction with the max_size
setting. The max_size
has also been designed to be set only once, to avoid adding additional complexity to the code that is unnecessary until a use-case presents itself.
Usage: Call using Quant in the file or scope where you want to use these refinements. It does not matter if the arrays were instantiated outside the scope of the refinement, the refinements will still be applied.
Instance Method Summary collapse
-
#<<(value) ⇒ Object
Overrides the standard << method to track the
maximum
andminimum
values while also respecting themax_size
setting. -
#ema(n: size) ⇒ Array<Float>
Computes the Exponential Moving Average (EMA) of the array.
-
#max_size!(max_size) ⇒ Object
Sets the maximum size of the array.
-
#maximum ⇒ Object
Returns the maximum element value in the array.
-
#mean(n: size) ⇒ Float
Computes the mean of the array.
-
#minimum ⇒ Object
Returns the minimum element value in the array.
-
#peak(n: size) ⇒ Float
Returns the largest absolute value in the array.
-
#prev(index) ⇒ Object
Treats the tail of the array as starting at zero and counting up.
-
#push(*objects) ⇒ Object
Overrides the standard
push
method to track themaximum
andminimum
values while also respecting themax_size
setting. -
#sma(n: size) ⇒ Array<Float>
Computes the Simple Moving Average (SMA) of the array.
-
#stddev(reference_value, n: size) ⇒ Float
Computes the Standard Deviation of the array.
- #variance(reference_value, n: size) ⇒ Object
-
#wma(n: size) ⇒ Array<Float>
Computes the Weighted Moving Average (WMA) of the array.
Instance Method Details
#<<(value) ⇒ Object
Overrides the standard << method to track the maximum
and minimum
values while also respecting the max_size
setting.
39 40 41 |
# File 'lib/quant/refinements/array.rb', line 39 def <<(value) push(value) end |
#ema(n: size) ⇒ Array<Float>
Computes the Exponential Moving Average (EMA) of the array. When n
is specified, the EMA is computed over the last n
elements, otherwise it is computed over the entire array. An Array of EMA’s is returned, with the first entry always the first value in the subset.
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/quant/refinements/array.rb', line 136 def ema(n: size) subset = last(n) return [] if subset.empty? alpha = 2.0 / (subset.size + 1) naught_alpha = (1.0 - alpha) pvalue = subset[0] subset.map do |value| pvalue = (alpha * value) + (naught_alpha * pvalue) end end |
#max_size!(max_size) ⇒ Object
Sets the maximum size of the array. When the size of the array exceeds the max_size
, the first element is removed from the array. This setting modifies :<< and :push methods.
92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/quant/refinements/array.rb', line 92 def max_size!(max_size) # These guards are maybe unnecessary, but they are here until a use-case is found. # Some indicators are built specifically against the +max_size+ of a given array. # Adjusting the +max_size+ after the fact could lead to unexpected, unintended behavior. raise Errors::ArrayMaxSizeError, "Cannot set max_size to nil." unless max_size raise Errors::ArrayMaxSizeError, "The max_size can only be set once." if @max_size raise Errors::ArrayMaxSizeError, "The size of Array #{size} exceeds max_size #{max_size}." if size > max_size @max_size = max_size self end |
#maximum ⇒ Object
Returns the maximum element value in the array. It is an optimized version of the standard max
method.
61 62 63 |
# File 'lib/quant/refinements/array.rb', line 61 def maximum @maximum || max end |
#mean(n: size) ⇒ Float
Computes the mean of the array. When n
is specified, the mean is computed over the last n
elements, otherwise it is computed over the entire array. If the array is empty, 0.0 is returned.
110 111 112 113 114 115 |
# File 'lib/quant/refinements/array.rb', line 110 def mean(n: size) subset = last(n) return 0.0 if subset.empty? subset.sum / subset.size.to_f end |
#minimum ⇒ Object
Returns the minimum element value in the array. It is an optimized version of the standard min
method.
66 67 68 |
# File 'lib/quant/refinements/array.rb', line 66 def minimum @minimum || min end |
#peak(n: size) ⇒ Float
Returns the largest absolute value in the array. When n
is specified, the peak is computed over the last n
elements, otherwise it is computed over the entire array. If the array is empty, 0.0 is returned.
123 124 125 126 127 128 |
# File 'lib/quant/refinements/array.rb', line 123 def peak(n: size) subset = last(n) return 0.0 if subset.empty? [subset.max.abs, subset.min.abs].max end |
#prev(index) ⇒ Object
Treats the tail of the array as starting at zero and counting up. Does not overflow the head of the array. That is, if the Array has 5 elements, prev(10) would return the first element in the array.
Useful for when translating TradingView or MQ4 indicators to Ruby where those programs’ indexing starts at 0 for most recent bar and counts up to the oldest bar.
83 84 85 86 87 |
# File 'lib/quant/refinements/array.rb', line 83 def prev(index) raise ArgumentError, "index must be positive" if index < 0 self[[size - (index + 1), 0].max] end |
#push(*objects) ⇒ Object
Overrides the standard push
method to track the maximum
and minimum
values while also respecting the max_size
setting.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/quant/refinements/array.rb', line 45 def push(*objects) Array(objects).each do |object| super(object) if @max_size && size > @max_size voted_off = shift @minimum = min if voted_off == @minimum @maximum = max if voted_off == @maximum else @maximum = object if @maximum.nil? || object > @maximum @minimum = object if @minimum.nil? || object < @minimum end end self end |
#sma(n: size) ⇒ Array<Float>
Computes the Simple Moving Average (SMA) of the array. When n
is specified, the SMA is computed over the last n
elements, otherwise it is computed over the entire array. An Array of SMA’s is returned, with the first entry always the first value in the subset.
154 155 156 157 158 159 160 161 162 |
# File 'lib/quant/refinements/array.rb', line 154 def sma(n: size) subset = last(n) return [] if subset.empty? pvalue = subset[0] subset.map do |value| pvalue = (pvalue + value) / 2.0 end end |
#stddev(reference_value, n: size) ⇒ Float
Computes the Standard Deviation of the array. When n
is specified, the Standard Deviation is computed over the last n
elements, otherwise it is computed over the entire array.
192 193 194 |
# File 'lib/quant/refinements/array.rb', line 192 def stddev(reference_value, n: size) variance(reference_value, n:)**0.5 end |
#variance(reference_value, n: size) ⇒ Object
196 197 198 199 200 201 |
# File 'lib/quant/refinements/array.rb', line 196 def variance(reference_value, n: size) subset = last(n) return 0.0 if subset.empty? subset.empty? ? 0.0 : subset.map{ |v| (v - reference_value)**2 }.mean end |
#wma(n: size) ⇒ Array<Float>
Computes the Weighted Moving Average (WMA) of the array. When n
is specified, the WMA is computed over the last n
elements, otherwise it is computed over the entire array. An Array of WMA’s is returned, with the first entry always the first value in the subset.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/quant/refinements/array.rb', line 170 def wma(n: size) subset = last(n) return [] if subset.empty? # ensures we return not more than number of elements in full array, # yet have enough values to compute each iteration max_size = [size, n].min while subset.size <= max_size + 2 subset.unshift(subset[0]) end subset.each_cons(4).map do |v1, v2, v3, v4| (4.0 * v4 + 3.0 * v3 + 2.0 * v2 + v1) / 10.0 end end |