quantitative 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/Guardfile +1 -1
- data/Rakefile +6 -1
- data/lib/quant/attributes.rb +31 -43
- data/lib/quant/config.rb +8 -0
- data/lib/quant/experimental.rb +20 -0
- data/lib/quant/indicators/dominant_cycle_indicators.rb +10 -0
- data/lib/quant/indicators/dominant_cycles/acr.rb +101 -0
- data/lib/quant/indicators/dominant_cycles/band_pass.rb +80 -0
- data/lib/quant/indicators/dominant_cycles/differential.rb +19 -0
- data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +128 -0
- data/lib/quant/indicators/dominant_cycles/homodyne.rb +27 -0
- data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +59 -0
- data/lib/quant/indicators/indicator.rb +30 -8
- data/lib/quant/indicators/indicator_point.rb +12 -2
- data/lib/quant/indicators.rb +9 -2
- data/lib/quant/indicators_proxy.rb +0 -3
- data/lib/quant/indicators_sources.rb +1 -1
- data/lib/quant/interval.rb +6 -9
- data/lib/quant/mixins/filters.rb +5 -42
- data/lib/quant/mixins/functions.rb +7 -3
- data/lib/quant/mixins/high_pass_filters.rb +129 -0
- data/lib/quant/mixins/super_smoother.rb +18 -15
- data/lib/quant/mixins/universal_filters.rb +326 -0
- data/lib/quant/series.rb +1 -1
- data/lib/quant/statistics/correlation.rb +37 -0
- data/lib/quant/ticks/ohlc.rb +5 -4
- data/lib/quant/time_methods.rb +4 -0
- data/lib/quant/time_period.rb +13 -14
- data/lib/quant/version.rb +1 -1
- data/lib/quantitative.rb +1 -1
- metadata +13 -4
- data/lib/quant/indicators/ma.rb +0 -40
- data/lib/quant/mixins/high_pass_filter.rb +0 -54
@@ -7,13 +7,11 @@ module Quant
|
|
7
7
|
include Mixins::Functions
|
8
8
|
include Mixins::Filters
|
9
9
|
include Mixins::MovingAverages
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# include Mixins::HighPassFilter
|
10
|
+
include Mixins::HilbertTransform
|
11
|
+
include Mixins::SuperSmoother
|
12
|
+
include Mixins::Stochastic
|
13
|
+
include Mixins::FisherTransform
|
15
14
|
# include Mixins::Direction
|
16
|
-
# include Mixins::Filters
|
17
15
|
|
18
16
|
attr_reader :source, :series
|
19
17
|
|
@@ -24,6 +22,30 @@ module Quant
|
|
24
22
|
series.each { |tick| self << tick }
|
25
23
|
end
|
26
24
|
|
25
|
+
def min_period
|
26
|
+
Quant.config.indicators.min_period
|
27
|
+
end
|
28
|
+
|
29
|
+
def max_period
|
30
|
+
Quant.config.indicators.max_period
|
31
|
+
end
|
32
|
+
|
33
|
+
def half_period
|
34
|
+
Quant.config.indicators.half_period
|
35
|
+
end
|
36
|
+
|
37
|
+
def micro_period
|
38
|
+
Quant.config.indicators.micro_period
|
39
|
+
end
|
40
|
+
|
41
|
+
def dominant_cycle_kind
|
42
|
+
Quant.config.indicators.dominant_cycle_kind
|
43
|
+
end
|
44
|
+
|
45
|
+
def pivot_kind
|
46
|
+
Quant.config.indicators.pivot_kind
|
47
|
+
end
|
48
|
+
|
27
49
|
def ticks
|
28
50
|
@points.keys
|
29
51
|
end
|
@@ -45,7 +67,7 @@ module Quant
|
|
45
67
|
|
46
68
|
def <<(tick)
|
47
69
|
@t0 = tick
|
48
|
-
@p0 = points_class.new(tick:, source:)
|
70
|
+
@p0 = points_class.new(indicator: self, tick:, source:)
|
49
71
|
@points[tick] = @p0
|
50
72
|
|
51
73
|
@p1 = values[-2] || @p0
|
@@ -64,7 +86,7 @@ module Quant
|
|
64
86
|
end
|
65
87
|
|
66
88
|
def inspect
|
67
|
-
"#<#{self.class.name} symbol=#{series.symbol} source=#{source}
|
89
|
+
"#<#{self.class.name} symbol=#{series.symbol} source=#{source} ticks=#{ticks.size}>"
|
68
90
|
end
|
69
91
|
|
70
92
|
def compute
|
@@ -4,19 +4,29 @@ module Quant
|
|
4
4
|
class Indicators
|
5
5
|
class IndicatorPoint
|
6
6
|
include Quant::Attributes
|
7
|
+
extend Forwardable
|
7
8
|
|
8
|
-
attr_reader :tick
|
9
|
+
attr_reader :indicator, :tick
|
9
10
|
|
10
11
|
attribute :source, key: "src"
|
11
12
|
attribute :input, key: "in"
|
12
13
|
|
13
|
-
def initialize(tick:, source:)
|
14
|
+
def initialize(indicator:, tick:, source:)
|
15
|
+
@indicator = indicator
|
14
16
|
@tick = tick
|
15
17
|
@source = source
|
16
18
|
@input = @tick.send(source)
|
17
19
|
initialize_data_points
|
18
20
|
end
|
19
21
|
|
22
|
+
def_delegator :indicator, :series
|
23
|
+
def_delegator :indicator, :min_period
|
24
|
+
def_delegator :indicator, :max_period
|
25
|
+
def_delegator :indicator, :half_period
|
26
|
+
def_delegator :indicator, :micro_period
|
27
|
+
def_delegator :indicator, :dominant_cycle_kind
|
28
|
+
def_delegator :indicator, :pivot_kind
|
29
|
+
|
20
30
|
def initialize_data_points
|
21
31
|
# No-Op - Override in subclass if needed.
|
22
32
|
end
|
data/lib/quant/indicators.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "indicators_proxy"
|
3
4
|
module Quant
|
4
|
-
# TODO: build an Indicator registry so new indicators can be added and
|
5
|
-
|
5
|
+
# TODO: build an Indicator registry so new indicators can be added and
|
6
|
+
# used outside those shipped with the library.
|
7
|
+
class Indicators < IndicatorsProxy
|
8
|
+
def ping; indicator(Indicators::Ping) end
|
9
|
+
|
10
|
+
def dominant_cycles
|
11
|
+
@dominant_cycles ||= Indicators::DominantCycleIndicators.new(series:, source:)
|
12
|
+
end
|
6
13
|
end
|
7
14
|
end
|
data/lib/quant/interval.rb
CHANGED
@@ -116,10 +116,6 @@ module Quant
|
|
116
116
|
"1D" => :daily,
|
117
117
|
}.freeze
|
118
118
|
|
119
|
-
def self.all_resolutions
|
120
|
-
RESOLUTIONS.keys
|
121
|
-
end
|
122
|
-
|
123
119
|
# Instantiates an Interval from a resolution. For example, TradingView uses resolutions
|
124
120
|
# like "1", "3", "5", "15", "30", "60", "240", "D", "1D" to represent the duration of a
|
125
121
|
# candlestick. +from_resolution+ translates resolutions to the appropriate {Quant::Interval}.
|
@@ -216,6 +212,11 @@ module Quant
|
|
216
212
|
INTERVAL_DISTANCE.keys
|
217
213
|
end
|
218
214
|
|
215
|
+
# Returns the full list of valid resolution Strings that can be used to instantiate an {Quant::Interval}.
|
216
|
+
def self.all_resolutions
|
217
|
+
RESOLUTIONS.keys
|
218
|
+
end
|
219
|
+
|
219
220
|
# Computes the number of ticks from present to given timestamp.
|
220
221
|
# If timestamp doesn't cover a full interval, it will be rounded up to 1
|
221
222
|
# @example
|
@@ -230,7 +231,7 @@ module Quant
|
|
230
231
|
end
|
231
232
|
|
232
233
|
def self.ensure_valid_resolution!(resolution)
|
233
|
-
return if
|
234
|
+
return if all_resolutions.include? resolution
|
234
235
|
|
235
236
|
should_be_one_of = "Should be one of: (#{RESOLUTIONS.keys.join(", ")})"
|
236
237
|
raise Errors::InvalidResolution, "resolution (#{resolution}) not a valid resolution. #{should_be_one_of}"
|
@@ -248,10 +249,6 @@ module Quant
|
|
248
249
|
should_be_one_of = "Should be one of: (#{valid_intervals.join(", ")})"
|
249
250
|
raise Errors::InvalidInterval, "interval (#{interval.inspect}) not a valid interval. #{should_be_one_of}"
|
250
251
|
end
|
251
|
-
|
252
|
-
def ensure_valid_resolution!(resolution)
|
253
|
-
self.class.ensure_valid_resolution!(resolution)
|
254
|
-
end
|
255
252
|
end
|
256
253
|
end
|
257
254
|
# rubocop:enable Layout/HashAlignment
|
data/lib/quant/mixins/filters.rb
CHANGED
@@ -1,51 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "high_pass_filters"
|
4
|
+
require_relative "butterworth_filters"
|
5
|
+
require_relative "universal_filters"
|
3
6
|
module Quant
|
4
7
|
module Mixins
|
5
|
-
# 1. All the common filters useful for traders have a transfer response
|
6
|
-
# that can be written as a ratio of two polynomials.
|
7
|
-
# 2. Lag is very important to traders. More complex filters can be
|
8
|
-
# created using more input data, but more input data increases lag.
|
9
|
-
# Sophisticated filters are not very useful for trading because they
|
10
|
-
# incur too much lag.
|
11
|
-
# 3. Filter transfer response can be viewed in the time domain and
|
12
|
-
# the frequency domain with equal validity.
|
13
|
-
# 4. Nonrecursive filters can have zeros in the transfer response, enabling
|
14
|
-
# the complete cancellation of some selected frequency components.
|
15
|
-
# 5. Nonrecursive filters having coefficients symmetrical about the
|
16
|
-
# center of the filter will have a delay of half the degree of the
|
17
|
-
# transfer response polynomial at all frequencies.
|
18
|
-
# 6. Low-pass filters are smoothers because they attenuate the high-frequency
|
19
|
-
# components of the input data.
|
20
|
-
# 7. High-pass filters are detrenders because they attenuate the
|
21
|
-
# low-frequency components of trends.
|
22
|
-
# 8. Band-pass filters are both detrenders and smoothers because they
|
23
|
-
# attenuate all but the desired frequency components.
|
24
|
-
# 9. Filters provide an output only through their transfer response.
|
25
|
-
# The transfer response is strictly a mathematical function, and
|
26
|
-
# interpretations such as overbought, oversold, convergence, divergence,
|
27
|
-
# and so on are not implied. The validity of such interpretations
|
28
|
-
# must be made on the basis of statistics apart from the filter.
|
29
|
-
# 10. The critical period of a filter output is the frequency at which
|
30
|
-
# the output power of the filter is half the power of the input
|
31
|
-
# wave at that frequency.
|
32
|
-
# 11. A WMA has little or no redeeming virtue.
|
33
|
-
# 12. A median filter is best used when the data contain impulsive noise
|
34
|
-
# or when there are wild variations in the data. Smoothing volume
|
35
|
-
# data is one example of a good application for a median filter.
|
36
|
-
#
|
37
|
-
# == Filter Coefficients forVariousTypes of Filters
|
38
|
-
#
|
39
|
-
# Filter Type b0 b1 b2 a0 a1 a2
|
40
|
-
# EMA α 0 0 1 −(1−α) 0
|
41
|
-
# Two-pole low-pass α**2 0 0 1 −2*(1-α) (1-α)**2
|
42
|
-
# High-pass (1-α/2) -(1-α/2) 0 1 −(1−α) 0
|
43
|
-
# Two-pole high-pass (1-α/2)**2 -2*(1-α/2)**2 (1-α/2)**2 1 -2*(1-α) (1-α)**2
|
44
|
-
# Band-pass (1-σ)/2 0 -(1-σ)/2 1 -λ*(1+σ) σ
|
45
|
-
# Band-stop (1+σ)/2 -2λ*(1+σ)/2 (1+σ)/2 1 -λ*(1+σ) σ
|
46
|
-
#
|
47
8
|
module Filters
|
9
|
+
include Mixins::HighPassFilters
|
48
10
|
include Mixins::ButterworthFilters
|
11
|
+
include Mixins::UniversalFilters
|
49
12
|
end
|
50
13
|
end
|
51
14
|
end
|
@@ -8,7 +8,7 @@ module Quant
|
|
8
8
|
# k = 0.707 for two-pole high-pass filters
|
9
9
|
# k = 1.414 for two-pole low-pass filters
|
10
10
|
def period_to_alpha(period, k: 1.0)
|
11
|
-
radians = deg2rad(k * 360 / period)
|
11
|
+
radians = deg2rad(k * 360 / period.to_f)
|
12
12
|
cos = Math.cos(radians)
|
13
13
|
sin = Math.sin(radians)
|
14
14
|
(cos + sin - 1) / cos
|
@@ -48,8 +48,12 @@ module Quant
|
|
48
48
|
dy2 = line2[1][1] - line1[1][1]
|
49
49
|
|
50
50
|
d = dx1 * dx2 + dy1 * dy2
|
51
|
-
l2 = (dx1**2 + dy1**2) * (dx2**2 + dy2**2)
|
52
|
-
|
51
|
+
l2 = ((dx1**2 + dy1**2) * (dx2**2 + dy2**2))
|
52
|
+
|
53
|
+
radians = d.to_f / Math.sqrt(l2)
|
54
|
+
value = rad2deg Math.acos(radians)
|
55
|
+
|
56
|
+
value.nan? ? 0.0 : value
|
53
57
|
end
|
54
58
|
|
55
59
|
# angle = acos(d/sqrt(l2))
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Mixins
|
5
|
+
# The following are high pass filters that are used to remove low frequency
|
6
|
+
# components from a time series. In simple terms, a high pass filter
|
7
|
+
# allows signals above a certain frequency (the cutoff frequency) to
|
8
|
+
# pass through relatively unaffected, while attenuating or blocking
|
9
|
+
# signals below that frequency.
|
10
|
+
#
|
11
|
+
# HighPass Filters are “detrenders” because they attenuate low frequency components
|
12
|
+
# One pole HighPass and SuperSmoother does not produce a zero mean because low
|
13
|
+
# frequency spectral dilation components are "leaking" through The one pole
|
14
|
+
# HighPass Filter response
|
15
|
+
#
|
16
|
+
# == Experimental
|
17
|
+
# Across the various texts and papers, Ehlers presents varying implementations
|
18
|
+
# of high-pass filters. I believe the two pole high-pass filter is the most
|
19
|
+
# consistently presented while the one pole high-pass filter has been presented
|
20
|
+
# in a few different ways. In some implementations, alpha is based on simple
|
21
|
+
# bars/lag while others use alpha based on phase/trigonometry. I have not been
|
22
|
+
# able to reconcile the differences and have not been able to find a definitive
|
23
|
+
# source for the correct implementation and do not know enough math to reason
|
24
|
+
# these out mathematically nor do I possess an advanced understanding of the
|
25
|
+
# fundamentals around digital signal processing. As such, the single-pole
|
26
|
+
# high-pass filters in this module are marked as experimental and may be incorrect.
|
27
|
+
module HighPassFilters
|
28
|
+
# A two-pole high-pass filter is a more advanced filtering technique
|
29
|
+
# used to remove low-frequency components from financial time series
|
30
|
+
# data, such as stock prices or market indices.
|
31
|
+
#
|
32
|
+
# Similar to a single-pole high-pass filter, a two-pole high-pass filter
|
33
|
+
# is designed to attenuate or eliminate slow-moving trends or macroeconomic
|
34
|
+
# effects from the data while preserving higher-frequency fluctuations.
|
35
|
+
# However, compared to the single-pole filter, the two-pole filter
|
36
|
+
# typically offers a steeper roll-off and better attenuation of lower
|
37
|
+
# frequencies, resulting in a more pronounced emphasis on short-term fluctuations.
|
38
|
+
def two_pole_high_pass_filter(source, period:, previous: :hp)
|
39
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
40
|
+
raise ArgumentError, "previous must be a Symbol" unless previous.is_a?(Symbol)
|
41
|
+
|
42
|
+
alpha = period_to_alpha(period, k: 0.707)
|
43
|
+
|
44
|
+
v1 = p0.send(source) - (2.0 * p1.send(source)) + p2.send(source)
|
45
|
+
v2 = p1.send(previous)
|
46
|
+
v3 = p2.send(previous)
|
47
|
+
|
48
|
+
a = v1 * (1 - (alpha * 0.5))**2
|
49
|
+
b = v2 * 2 * (1 - alpha)
|
50
|
+
c = v3 * (1 - alpha)**2
|
51
|
+
|
52
|
+
a + b - c
|
53
|
+
end
|
54
|
+
|
55
|
+
# A single-pole high-pass filter is used to filter out low-frequency
|
56
|
+
# components from financial time series data. This type of filter is
|
57
|
+
# commonly applied in signal processing techniques to remove noise or
|
58
|
+
# unwanted trends from the data while preserving higher-frequency fluctuations.
|
59
|
+
#
|
60
|
+
# A single-pole high-pass filter can be used to remove slow-moving trends
|
61
|
+
# or macroeconomic effects from the data, focusing instead on short-term
|
62
|
+
# fluctuations or high-frequency trading signals. By filtering out
|
63
|
+
# low-frequency components, traders aim to identify and exploit more
|
64
|
+
# immediate market opportunities, such as short-term price movements
|
65
|
+
# or momentum signals.
|
66
|
+
#
|
67
|
+
# The implementation of a single-pole high-pass filter in algorithmic
|
68
|
+
# trading typically involves applying a mathematical formula or algorithm
|
69
|
+
# to the historical price data of a financial instrument. This algorithm
|
70
|
+
# selectively attenuates or removes the low-frequency components of the
|
71
|
+
# data, leaving behind the higher-frequency fluctuations that traders
|
72
|
+
# are interested in analyzing for potential trading signals.
|
73
|
+
#
|
74
|
+
# Overall, single-pole high-pass filters in algorithmic trading are
|
75
|
+
# used as preprocessing steps to enhance the signal-to-noise ratio in
|
76
|
+
# financial data and to extract actionable trading signals from noisy
|
77
|
+
# or cluttered market data.
|
78
|
+
#
|
79
|
+
# == NOTES
|
80
|
+
# alpha = (Cosine(.707* 2 * PI / 48) + Sine (.707*360 / 48) - 1) / Cosine(.707*360 / 48);
|
81
|
+
# is the same as the following:
|
82
|
+
# radians = Math.sqrt(2) * Math::PI / period
|
83
|
+
# alpha = (Math.cos(radians) + Math.sin(radians) - 1) / Math.cos(radians)
|
84
|
+
def high_pass_filter(source, period:, previous: :hp)
|
85
|
+
Quant.experimental("This method is unproven and may be incorrect.")
|
86
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
87
|
+
raise ArgumentError, "previous must be a Symbol" unless previous.is_a?(Symbol)
|
88
|
+
|
89
|
+
radians = Math.sqrt(2) * Math::PI / period
|
90
|
+
a = Math.exp(-radians)
|
91
|
+
b = 2 * a * Math.cos(radians)
|
92
|
+
|
93
|
+
c2 = b
|
94
|
+
c3 = -a**2
|
95
|
+
c1 = (1 + c2 - c3) / 4
|
96
|
+
|
97
|
+
v0 = p0.send(source)
|
98
|
+
v1 = p1.send(source)
|
99
|
+
v2 = p2.send(source)
|
100
|
+
f1 = p1.send(previous)
|
101
|
+
f2 = p2.send(previous)
|
102
|
+
|
103
|
+
(c1 * (v0 - (2 * v1) + v2)) + (c2 * f1) + (c3 * f2)
|
104
|
+
end
|
105
|
+
|
106
|
+
# HPF = (1 − α/2)2 * (Price − 2 * Price[1] + Price[2]) + 2 * (1 − α) * HPF[1] − (1 − α)2 * HPF[2];
|
107
|
+
# High Pass Filter presented in Ehlers Cybernetic Analysis for Stocks and Futures Equation 2.7
|
108
|
+
def hpf2(source, period:, previous:)
|
109
|
+
Quant.experimental("This method is unproven and may be incorrect.")
|
110
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
111
|
+
raise ArgumentError, "previous must be a Symbol" unless previous.is_a?(Symbol)
|
112
|
+
|
113
|
+
alpha = period_to_alpha(period, k: 1.0)
|
114
|
+
v0 = p0.send(source)
|
115
|
+
v1 = p1.send(source)
|
116
|
+
v2 = p1.send(source)
|
117
|
+
|
118
|
+
f1 = p1.send(previous)
|
119
|
+
f2 = p2.send(previous)
|
120
|
+
|
121
|
+
c1 = (1 - alpha / 2)**2
|
122
|
+
c2 = 2 * (1 - alpha)
|
123
|
+
c3 = (1 - alpha)**2
|
124
|
+
|
125
|
+
(c1 * (v0 - (2 * v1) + v2)) + (c2 * f1) - (c3 * f2)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -6,39 +6,42 @@ module Quant
|
|
6
6
|
def two_pole_super_smooth(source, period:, previous: :ss)
|
7
7
|
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
8
8
|
|
9
|
-
radians = Math
|
9
|
+
radians = Math.sqrt(2) * Math::PI / period
|
10
10
|
a1 = Math.exp(-radians)
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
c3 = -a1**2
|
13
|
+
c2 = 2.0 * a1 * Math.cos(radians)
|
14
|
+
c1 = 1.0 - c2 - c3
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
v1 = (p0.send(source) + p1.send(source)) * 0.5
|
17
|
+
v2 = p2.send(previous)
|
18
|
+
v3 = p3.send(previous)
|
19
|
+
|
20
|
+
(c1 * v1) + (c2 * v2) + (c3 * v3)
|
20
21
|
end
|
22
|
+
|
21
23
|
alias super_smoother two_pole_super_smooth
|
22
24
|
alias ss2p two_pole_super_smooth
|
23
25
|
|
24
26
|
def three_pole_super_smooth(source, period:, previous: :ss)
|
25
27
|
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
+
radians = Math::PI / period
|
30
|
+
a1 = Math.exp(-radians)
|
31
|
+
b1 = 2 * a1 * Math.cos(Math.sqrt(3) * radians)
|
29
32
|
c1 = a1**2
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
c4 = c1**2
|
35
|
+
c3 = -(c1 + b1 * c1)
|
36
|
+
c2 = b1 + c1
|
37
|
+
c1 = 1 - c2 - c3 - c4
|
35
38
|
|
36
39
|
v0 = p0.send(source)
|
37
40
|
v1 = p1.send(previous)
|
38
41
|
v2 = p2.send(previous)
|
39
42
|
v3 = p3.send(previous)
|
40
43
|
|
41
|
-
(
|
44
|
+
(c1 * v0) + (c2 * v1) + (c3 * v2) + (c4 * v3)
|
42
45
|
end
|
43
46
|
alias ss3p three_pole_super_smooth
|
44
47
|
end
|