quantitative 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|