quantitative 0.1.6 → 0.1.7
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/lib/quant/indicators/indicator.rb +2 -4
- data/lib/quant/mixins/exponential_moving_average.rb +35 -0
- data/lib/quant/mixins/filters.rb +42 -55
- data/lib/quant/mixins/{trig.rb → functions.rb} +24 -1
- data/lib/quant/mixins/moving_averages.rb +6 -78
- data/lib/quant/mixins/simple_moving_average.rb +21 -0
- data/lib/quant/mixins/super_smoother.rb +20 -106
- data/lib/quant/mixins/weighted_moving_average.rb +45 -0
- data/lib/quant/ticks/ohlc.rb +9 -5
- data/lib/quant/ticks/tick.rb +30 -19
- data/lib/quant/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c2f8345b245cd469b234752f2fb757a1006b6a04322412acbb3fc10bd759827
|
4
|
+
data.tar.gz: fa5388e69c9f82fe686424dcfd81d8795ea6814a4b2b98b0edb6a52598ffbdaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb3c3cc8db589f5051d9dcd921fc1c01292897c440cfdb72c45df568051213ddd3fad5aa27e40652aff452619eefa835dcc4675120d050c9a46bbf34e83be3cc
|
7
|
+
data.tar.gz: cfcc8acf5989103a9a59b84965b3d1d3eb1f3288737afa3636f684ea5f378e10d82f4bda5e487f9a1ddcb867abd0fb82cef4048219acac6ee98bcfd412124db5
|
data/Gemfile.lock
CHANGED
@@ -4,10 +4,8 @@ module Quant
|
|
4
4
|
class Indicators
|
5
5
|
class Indicator
|
6
6
|
include Enumerable
|
7
|
-
|
8
|
-
|
9
|
-
# include Mixins::Trig
|
10
|
-
# include Mixins::WeightedAverage
|
7
|
+
include Mixins::Functions
|
8
|
+
include Mixins::MovingAverages
|
11
9
|
# include Mixins::HilbertTransform
|
12
10
|
# include Mixins::SuperSmoother
|
13
11
|
# include Mixins::Stochastic
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Mixins
|
5
|
+
module ExponentialMovingAverage
|
6
|
+
# Computes the Exponential Moving Average (EMA) of the given period.
|
7
|
+
#
|
8
|
+
# The EMA computation is optimized to compute using just the last two
|
9
|
+
# indicator data points and is expected to be called in each indicator's
|
10
|
+
# `#compute` method for each iteration on the series.
|
11
|
+
#
|
12
|
+
# @param source [Symbol] the source of the data points to be used in the calculation.
|
13
|
+
# @param previous [Symbol] the previous EMA value.
|
14
|
+
# @param period [Integer] the number of elements to compute the EMA over.
|
15
|
+
# @return [Float] the exponential moving average of the period.
|
16
|
+
# @raise [ArgumentError] if the source is not a Symbol.
|
17
|
+
# @example
|
18
|
+
# def compute
|
19
|
+
# p0.ema = exponential_moving_average(:close_price, period: 3)
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def compute
|
23
|
+
# p0.ema = exponential_moving_average(:close_price, previous: :ema, period: 3)
|
24
|
+
# end
|
25
|
+
def exponential_moving_average(source, period:, previous: :ema)
|
26
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
27
|
+
raise ArgumentError, "previous must be a Symbol" unless previous.is_a?(Symbol)
|
28
|
+
|
29
|
+
alpha = bars_to_alpha(period)
|
30
|
+
p0.send(source) * alpha + p1.send(previous) * (1.0 - alpha)
|
31
|
+
end
|
32
|
+
alias ema exponential_moving_average
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/quant/mixins/filters.rb
CHANGED
@@ -1,66 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "functions"
|
4
4
|
|
5
5
|
module Quant
|
6
6
|
module Mixins
|
7
|
-
# 1. All the common filters useful for traders have a transfer response
|
8
|
-
# as a ratio of two polynomials.
|
9
|
-
# 2. Lag is very important to traders. More complex filters can be
|
10
|
-
# but more input data increases lag.
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
7
|
+
# 1. All the common filters useful for traders have a transfer response
|
8
|
+
# that can be written as a ratio of two polynomials.
|
9
|
+
# 2. Lag is very important to traders. More complex filters can be
|
10
|
+
# created using more input data, but more input data increases lag.
|
11
|
+
# Sophisticated filters are not very useful for trading because they
|
12
|
+
# incur too much lag.
|
13
|
+
# 3. Filter transfer response can be viewed in the time domain and
|
14
|
+
# the frequency domain with equal validity.
|
15
|
+
# 4. Nonrecursive filters can have zeros in the transfer response, enabling
|
16
|
+
# the complete cancellation of some selected frequency components.
|
17
|
+
# 5. Nonrecursive filters having coefficients symmetrical about the
|
18
|
+
# center of the filter will have a delay of half the degree of the
|
19
|
+
# transfer response polynomial at all frequencies.
|
20
|
+
# 6. Low-pass filters are smoothers because they attenuate the high-frequency
|
21
|
+
# components of the input data.
|
22
|
+
# 7. High-pass filters are detrenders because they attenuate the
|
23
|
+
# low-frequency components of trends.
|
24
|
+
# 8. Band-pass filters are both detrenders and smoothers because they
|
25
|
+
# attenuate all but the desired frequency components.
|
26
|
+
# 9. Filters provide an output only through their transfer response.
|
27
|
+
# The transfer response is strictly a mathematical function, and
|
28
|
+
# interpretations such as overbought, oversold, convergence, divergence,
|
29
|
+
# and so on are not implied. The validity of such interpretations
|
30
|
+
# must be made on the basis of statistics apart from the filter.
|
31
|
+
# 10. The critical period of a filter output is the frequency at which
|
32
|
+
# the output power of the filter is half the power of the input
|
33
|
+
# wave at that frequency.
|
26
34
|
# 11. A WMA has little or no redeeming virtue.
|
27
|
-
# 12. A median filter is best used when the data contain impulsive noise
|
28
|
-
# variations in the data. Smoothing volume
|
29
|
-
# median filter.
|
35
|
+
# 12. A median filter is best used when the data contain impulsive noise
|
36
|
+
# or when there are wild variations in the data. Smoothing volume
|
37
|
+
# data is one example of a good application for a median filter.
|
38
|
+
#
|
39
|
+
# == Filter Coefficients forVariousTypes of Filters
|
40
|
+
#
|
41
|
+
# Filter Type b0 b1 b2 a0 a1 a2
|
42
|
+
# EMA α 0 0 1 −(1−α) 0
|
43
|
+
# Two-pole low-pass α**2 0 0 1 −2*(1-α) (1-α)**2
|
44
|
+
# High-pass (1-α/2) -(1-α/2) 0 1 −(1−α) 0
|
45
|
+
# Two-pole high-pass (1-α/2)**2 -2*(1-α/2)**2 (1-α/2)**2 1 -2*(1-α) (1-α)**2
|
46
|
+
# Band-pass (1-σ)/2 0 -(1-σ)/2 1 -λ*(1+σ) σ
|
47
|
+
# Band-stop (1+σ)/2 -2λ*(1+σ)/2 (1+σ)/2 1 -λ*(1+σ) σ
|
30
48
|
#
|
31
|
-
# Filter Coefficients forVariousTypes of Filters
|
32
|
-
# Filter Type b0 b1 b2 a0 a1 a2
|
33
|
-
# EMA α 0 0 1 −(1−α) 0
|
34
|
-
# Two-pole low-pass α**2 0 0 1 −2*(1-α) (1-α)**2
|
35
|
-
# High-pass (1-α/2) -(1-α/2) 0 1 −(1−α) 0
|
36
|
-
# Two-pole high-pass (1-α/2)**2 -2*(1-α/2)**2 (1-α/2)**2 1 -2*(1-α) (1-α)**2
|
37
|
-
# Band-pass (1-σ)/2 0 -(1-σ)/2 1 -λ*(1+σ) σ
|
38
|
-
# Band-stop (1+σ)/2 -2λ*(1+σ)/2 (1+σ)/2 1 -λ*(1+σ) σ
|
39
49
|
module Filters
|
40
|
-
include Mixins::
|
41
|
-
|
42
|
-
# α = Cos(K*360/Period)+Sin(K*360/Period)−1 / Cos(K*360/Period)
|
43
|
-
# k = 1.0 for single-pole filters
|
44
|
-
# k = 0.707 for two-pole high-pass filters
|
45
|
-
# k = 1.414 for two-pole low-pass filters
|
46
|
-
def period_to_alpha(period, k: 1.0)
|
47
|
-
radians = deg2rad(k * 360 / period)
|
48
|
-
cos = Math.cos(radians)
|
49
|
-
sin = Math.sin(radians)
|
50
|
-
(cos + sin - 1) / cos
|
51
|
-
end
|
52
|
-
|
53
|
-
# 3 bars = 0.5
|
54
|
-
# 4 bars = 0.4
|
55
|
-
# 5 bars = 0.333
|
56
|
-
# 6 bars = 0.285
|
57
|
-
# 10 bars = 0.182
|
58
|
-
# 20 bars = 0.0952
|
59
|
-
# 40 bars = 0.0488
|
60
|
-
# 50 bars = 0.0392
|
61
|
-
def bars_to_alpha(bars)
|
62
|
-
2.0 / (bars + 1)
|
63
|
-
end
|
50
|
+
include Mixins::Functions
|
64
51
|
|
65
52
|
def ema(source, prev_source, period)
|
66
53
|
alpha = bars_to_alpha(period)
|
@@ -2,7 +2,30 @@
|
|
2
2
|
|
3
3
|
module Quant
|
4
4
|
module Mixins
|
5
|
-
module
|
5
|
+
module Functions
|
6
|
+
# α = Cos(K*360/Period)+Sin(K*360/Period)−1 / Cos(K*360/Period)
|
7
|
+
# k = 1.0 for single-pole filters
|
8
|
+
# k = 0.707 for two-pole high-pass filters
|
9
|
+
# k = 1.414 for two-pole low-pass filters
|
10
|
+
def period_to_alpha(period, k: 1.0)
|
11
|
+
radians = deg2rad(k * 360 / period)
|
12
|
+
cos = Math.cos(radians)
|
13
|
+
sin = Math.sin(radians)
|
14
|
+
(cos + sin - 1) / cos
|
15
|
+
end
|
16
|
+
|
17
|
+
# 3 bars = 0.5
|
18
|
+
# 4 bars = 0.4
|
19
|
+
# 5 bars = 0.333
|
20
|
+
# 6 bars = 0.285
|
21
|
+
# 10 bars = 0.182
|
22
|
+
# 20 bars = 0.0952
|
23
|
+
# 40 bars = 0.0488
|
24
|
+
# 50 bars = 0.0392
|
25
|
+
def bars_to_alpha(bars)
|
26
|
+
2.0 / (bars + 1)
|
27
|
+
end
|
28
|
+
|
6
29
|
def deg2rad(degrees)
|
7
30
|
degrees * Math::PI / 180.0
|
8
31
|
end
|
@@ -1,86 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "weighted_moving_average"
|
4
|
+
require_relative "simple_moving_average"
|
5
|
+
require_relative "exponential_moving_average"
|
3
6
|
module Quant
|
4
7
|
module Mixins
|
5
8
|
module MovingAverages
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
#
|
10
|
-
# @param source [Symbol] the source of the data points to be used in the calculation.
|
11
|
-
# @return [Float] the weighted average of the series.
|
12
|
-
# @raise [ArgumentError] if the source is not a Symbol.
|
13
|
-
# @example
|
14
|
-
# p0.wma = weighted_average(:close_price)
|
15
|
-
def weighted_moving_average(source)
|
16
|
-
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
17
|
-
|
18
|
-
[4.0 * p0.send(source),
|
19
|
-
3.0 * p1.send(source),
|
20
|
-
2.0 * p2.send(source),
|
21
|
-
p3.send(source)].sum / 10.0
|
22
|
-
end
|
23
|
-
alias wma weighted_moving_average
|
24
|
-
|
25
|
-
# Computes the Weighted Moving Average (WMA) of the series, using the seven most recent data points.
|
26
|
-
#
|
27
|
-
# @param source [Symbol] the source of the data points to be used in the calculation.
|
28
|
-
# @return [Float] the weighted average of the series.
|
29
|
-
# @raise [ArgumentError] if the source is not a Symbol.
|
30
|
-
# @example
|
31
|
-
# p0.wma = weighted_average(:close_price)
|
32
|
-
def extended_weighted_moving_average(source)
|
33
|
-
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
34
|
-
|
35
|
-
[7.0 * p0.send(source),
|
36
|
-
6.0 * p1.send(source),
|
37
|
-
5.0 * p2.send(source),
|
38
|
-
4.0 * p3.send(source),
|
39
|
-
3.0 * p(4).send(source),
|
40
|
-
2.0 * p(5).send(source),
|
41
|
-
p(6).send(source)].sum / 28.0
|
42
|
-
end
|
43
|
-
alias ewma extended_weighted_moving_average
|
44
|
-
|
45
|
-
# Computes the Simple Moving Average (SMA) of the given period.
|
46
|
-
#
|
47
|
-
# @param source [Symbol] the source of the data points to be used in the calculation.
|
48
|
-
# @param period [Integer] the number of elements to compute the SMA over.
|
49
|
-
# @return [Float] the simple moving average of the period.
|
50
|
-
def simple_moving_average(source, period:)
|
51
|
-
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
52
|
-
|
53
|
-
values.last(period).map { |value| value.send(source) }.mean
|
54
|
-
end
|
55
|
-
alias sma simple_moving_average
|
56
|
-
|
57
|
-
# Computes the Exponential Moving Average (EMA) of the given period.
|
58
|
-
#
|
59
|
-
# The EMA computation is optimized to compute using just the last two
|
60
|
-
# indicator data points and is expected to be called in each indicator's
|
61
|
-
# `#compute` method for each iteration on the series.
|
62
|
-
#
|
63
|
-
# @param source [Symbol] the source of the data points to be used in the calculation.
|
64
|
-
# @param previous [Symbol] the previous EMA value.
|
65
|
-
# @param period [Integer] the number of elements to compute the EMA over.
|
66
|
-
# @return [Float] the exponential moving average of the period.
|
67
|
-
# @raise [ArgumentError] if the source is not a Symbol.
|
68
|
-
# @example
|
69
|
-
# def compute
|
70
|
-
# p0.ema = exponential_moving_average(:close_price, period: 3)
|
71
|
-
# end
|
72
|
-
#
|
73
|
-
# def compute
|
74
|
-
# p0.ema = exponential_moving_average(:close_price, previous: :ema, period: 3)
|
75
|
-
# end
|
76
|
-
def exponential_moving_average(source, previous: :ema, period:)
|
77
|
-
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
78
|
-
raise ArgumentError, "previous must be a Symbol" unless previous.is_a?(Symbol)
|
79
|
-
|
80
|
-
alpha = 2.0 / (period + 1)
|
81
|
-
p0.send(source) * alpha + p1.send(previous) * (1.0 - alpha)
|
82
|
-
end
|
83
|
-
alias ema exponential_moving_average
|
9
|
+
include WeightedMovingAverage
|
10
|
+
include SimpleMovingAverage
|
11
|
+
include ExponentialMovingAverage
|
84
12
|
end
|
85
13
|
end
|
86
14
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Mixins
|
5
|
+
module SimpleMovingAverage
|
6
|
+
using Quant
|
7
|
+
|
8
|
+
# Computes the Simple Moving Average (SMA) of the given period.
|
9
|
+
#
|
10
|
+
# @param source [Symbol] the source of the data points to be used in the calculation.
|
11
|
+
# @param period [Integer] the number of elements to compute the SMA over.
|
12
|
+
# @return [Float] the simple moving average of the period.
|
13
|
+
def simple_moving_average(source, period:)
|
14
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
15
|
+
|
16
|
+
values.last(period).map { |value| value.send(source) }.mean
|
17
|
+
end
|
18
|
+
alias sma simple_moving_average
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,73 +3,29 @@
|
|
3
3
|
module Quant
|
4
4
|
module Mixins
|
5
5
|
module SuperSmoother
|
6
|
-
def
|
7
|
-
|
8
|
-
return v0.to_f if points.size < 4
|
6
|
+
def two_pole_super_smooth(source, period:, previous: :ss)
|
7
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
9
8
|
|
10
|
-
|
11
|
-
coef3 = -k**2
|
12
|
-
coef2 = 2.0 * k * Math.cos(Math.sqrt(2) * (Math::PI / 2) / period)
|
13
|
-
coef1 = 1.0 - coef2 - coef3
|
14
|
-
|
15
|
-
v1 = p1.send(prev_source).to_d
|
16
|
-
v2 = p2.send(prev_source).to_d
|
17
|
-
p3.send(prev_source).to_d
|
18
|
-
((coef1 * (v0 + v1)) / 2.0 + (coef2 * v1) + (coef3 * v2)).to_f
|
19
|
-
end
|
20
|
-
|
21
|
-
def two_pole_super_smooth(source, prev_source, ssperiod)
|
22
|
-
return p1.send(source) if [p1 == p3]
|
23
|
-
|
24
|
-
radians = Math::PI * Math.sqrt(2) / ssperiod
|
9
|
+
radians = Math::PI * Math.sqrt(2) / period
|
25
10
|
a1 = Math.exp(-radians)
|
26
11
|
|
27
|
-
coef2 = 2.
|
12
|
+
coef2 = 2.0r * a1 * Math.cos(radians)
|
28
13
|
coef3 = -a1 * a1
|
29
14
|
coef1 = 1.0 - coef2 - coef3
|
30
15
|
|
31
|
-
v0 = (
|
32
|
-
v1 = p2.send(
|
33
|
-
v2 = p3.send(
|
34
|
-
(coef1 * v0) + (coef2 * v1) + (coef3 * v2)
|
16
|
+
v0 = (p0.send(source) + p1.send(source))/2.0
|
17
|
+
v1 = p2.send(previous)
|
18
|
+
v2 = p3.send(previous)
|
19
|
+
((coef1 * v0) + (coef2 * v1) + (coef3 * v2)).to_f
|
35
20
|
end
|
21
|
+
alias super_smoother two_pole_super_smooth
|
22
|
+
alias ss2p two_pole_super_smooth
|
36
23
|
|
37
|
-
def three_pole_super_smooth(source,
|
38
|
-
|
39
|
-
b1 = 2 * a1 * Math.cos(Math::PI * Math.sqrt(3) / ssperiod)
|
40
|
-
c1 = a1**2
|
24
|
+
def three_pole_super_smooth(source, period:, previous: :ss)
|
25
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
41
26
|
|
42
|
-
|
43
|
-
|
44
|
-
coef4 = c1**2
|
45
|
-
coef1 = 1 - coef2 - coef3 - coef4
|
46
|
-
|
47
|
-
p0 = prev(0)
|
48
|
-
p1 = prev(1)
|
49
|
-
p2 = prev(2)
|
50
|
-
p3 = prev(3)
|
51
|
-
|
52
|
-
v0 = source.is_a?(Symbol) ? p0.send(source) : source
|
53
|
-
return v0 if [p0, p1, p2].include?(p3)
|
54
|
-
|
55
|
-
v1 = p1.send(prev_source)
|
56
|
-
v2 = p2.send(prev_source)
|
57
|
-
v3 = p3.send(prev_source)
|
58
|
-
(coef1 * v0) + (coef2 * v1) + (coef3 * v2) + (coef4 * v3)
|
59
|
-
end
|
60
|
-
|
61
|
-
# super smoother 3 pole
|
62
|
-
def ss3p(source, prev_source, ssperiod)
|
63
|
-
p0 = points[-1]
|
64
|
-
p1 = points[-2] || p0
|
65
|
-
p2 = points[-3] || p1
|
66
|
-
p3 = points[-4] || p2
|
67
|
-
|
68
|
-
v0 = source.is_a?(Symbol) ? p0.send(source) : source
|
69
|
-
return v0 if p0 == p3
|
70
|
-
|
71
|
-
a1 = Math.exp(-Math::PI / ssperiod)
|
72
|
-
b1 = 2 * a1 * Math.cos(Math::PI * Math.sqrt(3) / ssperiod)
|
27
|
+
a1 = Math.exp(-Math::PI / period)
|
28
|
+
b1 = 2 * a1 * Math.cos(Math::PI * Math.sqrt(3) / period)
|
73
29
|
c1 = a1**2
|
74
30
|
|
75
31
|
coef2 = b1 + c1
|
@@ -77,56 +33,14 @@ module Quant
|
|
77
33
|
coef4 = c1**2
|
78
34
|
coef1 = 1 - coef2 - coef3 - coef4
|
79
35
|
|
80
|
-
|
81
|
-
|
82
|
-
|
36
|
+
v0 = p0.send(source)
|
37
|
+
v1 = p1.send(previous)
|
38
|
+
v2 = p2.send(previous)
|
39
|
+
v3 = p3.send(previous)
|
40
|
+
|
83
41
|
(coef1 * v0) + (coef2 * v1) + (coef3 * v2) + (coef4 * v3)
|
84
42
|
end
|
85
|
-
|
86
|
-
# attr_reader :hpfs, :value1s, :hpf_psns
|
87
|
-
|
88
|
-
# def hpf
|
89
|
-
# @hpfs[-1]
|
90
|
-
# end
|
91
|
-
|
92
|
-
# def hpf_psn
|
93
|
-
# @hpf_psns[-1]
|
94
|
-
# end
|
95
|
-
|
96
|
-
# def prev offset, source
|
97
|
-
# idx = offset + 1
|
98
|
-
# source[[-idx, -source.size].max]
|
99
|
-
# end
|
100
|
-
|
101
|
-
# def weighted_average source
|
102
|
-
# [ 4.0 * prev(0, source),
|
103
|
-
# 3.0 * prev(1, source),
|
104
|
-
# 2.0 * prev(2, source),
|
105
|
-
# prev(3, source),
|
106
|
-
# ].sum / 10.0
|
107
|
-
# end
|
108
|
-
|
109
|
-
# def compute_hpf
|
110
|
-
# @hpfs ||= []
|
111
|
-
# @value1s ||= []
|
112
|
-
# @hpf_psns ||= []
|
113
|
-
# max_cycle = period * 10
|
114
|
-
|
115
|
-
# r = (360.0 / max_cycle) * (Math::PI / 180)
|
116
|
-
# alpha = (1 - Math::sin(r)) / Math::cos(r)
|
117
|
-
# hpf = @hpfs.empty? ? 0.0 : (0.5 * (1.0 + alpha) * (current_value - prev_value(1))) + (alpha * (@hpfs[-1]))
|
118
|
-
|
119
|
-
# @hpfs << hpf
|
120
|
-
# @hpfs.shift if @hpfs.size > max_cycle
|
121
|
-
|
122
|
-
# hh = @hpfs.max
|
123
|
-
# ll = @hpfs.min
|
124
|
-
# @value1s << value1 = (hh == ll ? 0.0 : 100 * (hpf - ll) / (hh - ll))
|
125
|
-
# @value1s.shift if @value1s.size > max_cycle
|
126
|
-
|
127
|
-
# @hpf_psns << weighted_average(@value1s)
|
128
|
-
# @hpf_psns.shift if @hpf_psns.size > max_cycle
|
129
|
-
# end
|
43
|
+
alias ss3p three_pole_super_smooth
|
130
44
|
end
|
131
45
|
end
|
132
46
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Mixins
|
5
|
+
module WeightedMovingAverage
|
6
|
+
using Quant
|
7
|
+
# Computes the Weighted Moving Average (WMA) of the series, using the four most recent data points.
|
8
|
+
#
|
9
|
+
# @param source [Symbol] the source of the data points to be used in the calculation.
|
10
|
+
# @return [Float] the weighted average of the series.
|
11
|
+
# @raise [ArgumentError] if the source is not a Symbol.
|
12
|
+
# @example
|
13
|
+
# p0.wma = weighted_average(:close_price)
|
14
|
+
def weighted_moving_average(source)
|
15
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
16
|
+
|
17
|
+
[4.0 * p0.send(source),
|
18
|
+
3.0 * p1.send(source),
|
19
|
+
2.0 * p2.send(source),
|
20
|
+
p3.send(source)].sum / 10.0
|
21
|
+
end
|
22
|
+
alias wma weighted_moving_average
|
23
|
+
|
24
|
+
# Computes the Weighted Moving Average (WMA) of the series, using the seven most recent data points.
|
25
|
+
#
|
26
|
+
# @param source [Symbol] the source of the data points to be used in the calculation.
|
27
|
+
# @return [Float] the weighted average of the series.
|
28
|
+
# @raise [ArgumentError] if the source is not a Symbol.
|
29
|
+
# @example
|
30
|
+
# p0.wma = weighted_average(:close_price)
|
31
|
+
def extended_weighted_moving_average(source)
|
32
|
+
raise ArgumentError, "source must be a Symbol" unless source.is_a?(Symbol)
|
33
|
+
|
34
|
+
[7.0 * p0.send(source),
|
35
|
+
6.0 * p1.send(source),
|
36
|
+
5.0 * p2.send(source),
|
37
|
+
4.0 * p3.send(source),
|
38
|
+
3.0 * p(4).send(source),
|
39
|
+
2.0 * p(5).send(source),
|
40
|
+
p(6).send(source)].sum / 28.0
|
41
|
+
end
|
42
|
+
alias ewma extended_weighted_moving_average
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/quant/ticks/ohlc.rb
CHANGED
@@ -4,11 +4,15 @@ require_relative "tick"
|
|
4
4
|
|
5
5
|
module Quant
|
6
6
|
module Ticks
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
7
|
+
# A {Quant::Ticks::OHLC} is a bar or candle for a point in time that
|
8
|
+
# has an open, high, low, and close price. It is the most common form
|
9
|
+
# of a {Quant::Ticks::Tick} and is usually used to representa time
|
10
|
+
# period such as a minute, hour, day, week, or month.
|
11
|
+
#
|
12
|
+
# The {Quant::Ticks::OHLC} is used to represent the price action of
|
13
|
+
# an asset The interval of the {Quant::Ticks::OHLC} is the time period
|
14
|
+
# that the {Quant::Ticks::OHLC} represents, such has hourly, daily,
|
15
|
+
# weekly, etc.
|
12
16
|
class OHLC < Tick
|
13
17
|
include TimeMethods
|
14
18
|
|
data/lib/quant/ticks/tick.rb
CHANGED
@@ -2,22 +2,29 @@
|
|
2
2
|
|
3
3
|
module Quant
|
4
4
|
module Ticks
|
5
|
-
# {Quant::Ticks::Tick} is the abstract ancestor for all Ticks and holds
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# for the integrity of the series and indicators that depend on the
|
15
|
-
#
|
16
|
-
|
17
|
-
#
|
5
|
+
# {Quant::Ticks::Tick} is the abstract ancestor for all Ticks and holds
|
6
|
+
# the logic for interacting with series and indicators. The public
|
7
|
+
# interface is devoid of properties around price, volume, and timestamp, etc.
|
8
|
+
# Descendant classes are responsible for defining the properties and
|
9
|
+
# how they are represented.
|
10
|
+
|
11
|
+
# The {Quant::Ticks::Tick} class is designed to be immutable and is
|
12
|
+
# intended to be used as a value object. This means that once a
|
13
|
+
# {Quant::Ticks::Tick} is created, it cannot be changed. This is important
|
14
|
+
# for the integrity of the series and indicators that depend on the
|
15
|
+
# ticks within the series.
|
16
|
+
|
17
|
+
# When a tick is added to a series, it is locked into the series and
|
18
|
+
# ownership cannot be changed. This is important for the integrity
|
19
|
+
# of the series and indicators that depend on the ticks within the series.
|
20
|
+
# This is a key design to being able to being able to not only compute
|
21
|
+
# indicators on the ticks just once, but also avoid recomputing indicators
|
22
|
+
# when series are limited/sliced/filtered into subsets of the original series.
|
23
|
+
|
18
24
|
# Ticks can be serialized to and from Ruby Hash, JSON strings, and CSV strings.
|
19
25
|
class Tick
|
20
|
-
# Returns a {Quant::Ticks::Tick} from a Ruby +Hash+. The default
|
26
|
+
# Returns a {Quant::Ticks::Tick} from a Ruby +Hash+. The default
|
27
|
+
# serializer is used to generate the {Quant::Ticks::Tick}.
|
21
28
|
# @param hash [Hash]
|
22
29
|
# @param serializer_class [Class] The serializer class to use for the conversion.
|
23
30
|
# @return [Quant::Ticks::Tick]
|
@@ -30,7 +37,8 @@ module Quant
|
|
30
37
|
serializer_class.from(hash, tick_class: self)
|
31
38
|
end
|
32
39
|
|
33
|
-
# Returns a {Quant::Ticks::Tick} from a JSON string. The default
|
40
|
+
# Returns a {Quant::Ticks::Tick} from a JSON string. The default
|
41
|
+
# serializer is used to generate the {Quant::Ticks::Tick}.
|
34
42
|
# @param json [String]
|
35
43
|
# @param serializer_class [Class] The serializer class to use for the conversion.
|
36
44
|
# @return [Quant::Ticks::Tick]
|
@@ -80,7 +88,8 @@ module Quant
|
|
80
88
|
self
|
81
89
|
end
|
82
90
|
|
83
|
-
# Returns a Ruby hash for the Tick. The default serializer is used
|
91
|
+
# Returns a Ruby hash for the Tick. The default serializer is used
|
92
|
+
# to generate the hash.
|
84
93
|
#
|
85
94
|
# @param serializer_class [Class] the serializer class to use for the conversion.
|
86
95
|
# @example
|
@@ -90,7 +99,8 @@ module Quant
|
|
90
99
|
serializer_class.to_h(self)
|
91
100
|
end
|
92
101
|
|
93
|
-
# Returns a JSON string for the Tick. The default serializer is used
|
102
|
+
# Returns a JSON string for the Tick. The default serializer is used
|
103
|
+
# to generate the JSON string.
|
94
104
|
#
|
95
105
|
# @param serializer_class [Class] the serializer class to use for the conversion.
|
96
106
|
# @example
|
@@ -100,8 +110,9 @@ module Quant
|
|
100
110
|
serializer_class.to_json(self)
|
101
111
|
end
|
102
112
|
|
103
|
-
# Returns a CSV row as a String for the Tick. The default serializer
|
104
|
-
# If headers is true, two lines
|
113
|
+
# Returns a CSV row as a String for the Tick. The default serializer
|
114
|
+
# is used to generate the CSV string. If headers is true, two lines
|
115
|
+
# returned separated by newline.
|
105
116
|
# The first line is the header row and the second line is the data row.
|
106
117
|
#
|
107
118
|
# @param serializer_class [Class] the serializer class to use for the conversion.
|
data/lib/quant/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quantitative
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Lang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -58,14 +58,17 @@ files:
|
|
58
58
|
- lib/quant/indicators_sources.rb
|
59
59
|
- lib/quant/interval.rb
|
60
60
|
- lib/quant/mixins/direction.rb
|
61
|
+
- lib/quant/mixins/exponential_moving_average.rb
|
61
62
|
- lib/quant/mixins/filters.rb
|
62
63
|
- lib/quant/mixins/fisher_transform.rb
|
64
|
+
- lib/quant/mixins/functions.rb
|
63
65
|
- lib/quant/mixins/high_pass_filter.rb
|
64
66
|
- lib/quant/mixins/hilbert_transform.rb
|
65
67
|
- lib/quant/mixins/moving_averages.rb
|
68
|
+
- lib/quant/mixins/simple_moving_average.rb
|
66
69
|
- lib/quant/mixins/stochastic.rb
|
67
70
|
- lib/quant/mixins/super_smoother.rb
|
68
|
-
- lib/quant/mixins/
|
71
|
+
- lib/quant/mixins/weighted_moving_average.rb
|
69
72
|
- lib/quant/refinements/array.rb
|
70
73
|
- lib/quant/series.rb
|
71
74
|
- lib/quant/settings.rb
|