quantitative 0.3.1 → 0.3.3
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/dominant_cycles_source.rb +1 -32
- data/lib/quant/indicators/adx.rb +2 -5
- data/lib/quant/indicators/atr.rb +2 -0
- data/lib/quant/indicators/cci.rb +2 -0
- data/lib/quant/indicators/decycler.rb +2 -0
- data/lib/quant/indicators/dominant_cycles/acr.rb +2 -0
- data/lib/quant/indicators/dominant_cycles/band_pass.rb +2 -0
- data/lib/quant/indicators/dominant_cycles/differential.rb +2 -0
- data/lib/quant/indicators/dominant_cycles/half_period.rb +2 -0
- data/lib/quant/indicators/dominant_cycles/homodyne.rb +2 -0
- data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +2 -0
- data/lib/quant/indicators/ema.rb +67 -0
- data/lib/quant/indicators/frama.rb +1 -0
- data/lib/quant/indicators/indicator.rb +17 -57
- data/lib/quant/indicators/indicator_point.rb +11 -0
- data/lib/quant/indicators/mama.rb +2 -0
- data/lib/quant/indicators/mesa.rb +3 -4
- data/lib/quant/indicators/ping.rb +2 -0
- data/lib/quant/indicators/pivots/atr.rb +11 -16
- data/lib/quant/indicators/pivots/bollinger.rb +10 -25
- data/lib/quant/indicators/pivots/camarilla.rb +30 -31
- data/lib/quant/indicators/pivots/classic.rb +3 -1
- data/lib/quant/indicators/pivots/demark.rb +5 -3
- data/lib/quant/indicators/pivots/donchian.rb +20 -23
- data/lib/quant/indicators/pivots/fibbonacci.rb +12 -9
- data/lib/quant/indicators/pivots/guppy.rb +3 -1
- data/lib/quant/indicators/pivots/keltner.rb +11 -19
- data/lib/quant/indicators/pivots/murrey.rb +8 -13
- data/lib/quant/indicators/pivots/pivot.rb +109 -0
- data/lib/quant/indicators/pivots/traditional.rb +2 -0
- data/lib/quant/indicators/pivots/woodie.rb +2 -0
- data/lib/quant/indicators/rocket_rsi.rb +57 -0
- data/lib/quant/indicators/roofing.rb +59 -0
- data/lib/quant/indicators/rsi.rb +67 -0
- data/lib/quant/indicators/snr.rb +64 -0
- data/lib/quant/indicators_registry.rb +63 -0
- data/lib/quant/indicators_source.rb +14 -9
- data/lib/quant/pivots_source.rb +1 -13
- data/lib/quant/version.rb +1 -1
- data/lib/quantitative.rb +10 -3
- metadata +9 -3
- data/lib/quant/indicators/pivot.rb +0 -107
@@ -4,35 +4,32 @@ module Quant
|
|
4
4
|
module Indicators
|
5
5
|
module Pivots
|
6
6
|
class Donchian < Pivot
|
7
|
-
|
7
|
+
register name: :donchian
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def st_highs; @st_highs ||= [].max_size!(st_period) end
|
14
|
-
def st_lows; @st_lows ||= [].max_size!(st_period) end
|
15
|
-
def mt_highs; @mt_highs ||= [].max_size!(mt_period) end
|
16
|
-
def mt_lows; @mt_lows ||= [].max_size!(mt_period) end
|
17
|
-
def lt_highs; @lt_highs ||= [].max_size!(lt_period) end
|
18
|
-
def lt_lows; @lt_lows ||= [].max_size!(lt_period) end
|
9
|
+
def compute_midpoint
|
10
|
+
p0.midpoint = (p0.high_price + p0.low_price) * 0.5
|
11
|
+
end
|
19
12
|
|
20
13
|
def compute_bands
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
lt_highs << p0.high_price
|
26
|
-
lt_lows << p0.low_price
|
14
|
+
period_points(micro_period).tap do |period_points|
|
15
|
+
p0.l1 = period_points.map(&:low_price).min
|
16
|
+
p0.h1 = period_points.map(&:high_price).max
|
17
|
+
end
|
27
18
|
|
28
|
-
|
29
|
-
|
19
|
+
period_points(min_period).tap do |period_points|
|
20
|
+
p0.l2 = period_points.map(&:low_price).min
|
21
|
+
p0.h2 = period_points.map(&:high_price).max
|
22
|
+
end
|
30
23
|
|
31
|
-
|
32
|
-
|
24
|
+
period_points(half_period).tap do |period_points|
|
25
|
+
p0.l3 = period_points.map(&:low_price).min
|
26
|
+
p0.h3 = period_points.map(&:high_price).max
|
27
|
+
end
|
33
28
|
|
34
|
-
|
35
|
-
|
29
|
+
period_points(max_period).tap do |period_points|
|
30
|
+
p0.l4 = period_points.map(&:low_price).min
|
31
|
+
p0.h4 = period_points.map(&:high_price).max
|
32
|
+
end
|
36
33
|
end
|
37
34
|
end
|
38
35
|
end
|
@@ -2,18 +2,21 @@ module Quant
|
|
2
2
|
module Indicators
|
3
3
|
module Pivots
|
4
4
|
class Fibbonacci < Pivot
|
5
|
-
|
6
|
-
half_period
|
7
|
-
end
|
5
|
+
register name: :fibbonacci
|
8
6
|
|
9
|
-
|
10
|
-
[0.146, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.146]
|
11
|
-
end
|
7
|
+
FIBBONACCI_SERIES = [0.146, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.146].freeze
|
12
8
|
|
13
9
|
def compute_bands
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
period_points(adaptive_period).tap do |period_points|
|
11
|
+
highest = period_points.map(&:high_price).max
|
12
|
+
lowest = period_points.map(&:low_price).min
|
13
|
+
p0.range = highest - lowest
|
14
|
+
p0.midpoint = (highest + lowest) * 0.5
|
15
|
+
end
|
16
|
+
|
17
|
+
FIBBONACCI_SERIES.each_with_index do |ratio, index|
|
18
|
+
p0[index + 1] = p0.midpoint + ratio * p0.range
|
19
|
+
p0[-index - 1] = p0.midpoint - ratio * p0.range
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -2,6 +2,8 @@ module Quant
|
|
2
2
|
module Indicators
|
3
3
|
module Pivots
|
4
4
|
class Guppy < Pivot
|
5
|
+
register name: :guppy
|
6
|
+
|
5
7
|
def guppy_ema(period, band)
|
6
8
|
return p0.input unless p1[band]
|
7
9
|
|
@@ -15,7 +17,7 @@ module Quant
|
|
15
17
|
|
16
18
|
# The short-term MAs are typically set at 3, 5, 8, 10, 12, and 15 periods. The
|
17
19
|
# longer-term MAs are typically set at 30, 35, 40, 45, 50, and 60.
|
18
|
-
def
|
20
|
+
def compute_bands
|
19
21
|
p0[1] = guppy_ema(5, 1)
|
20
22
|
p0[2] = guppy_ema(8, 2)
|
21
23
|
p0[3] = guppy_ema(10, 3)
|
@@ -2,6 +2,7 @@ module Quant
|
|
2
2
|
module Indicators
|
3
3
|
module Pivots
|
4
4
|
class Keltner < Pivot
|
5
|
+
register name: :keltner
|
5
6
|
depends_on Indicators::Atr
|
6
7
|
|
7
8
|
def atr_point
|
@@ -9,33 +10,24 @@ module Quant
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def scale
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def alpha
|
16
|
-
bars_to_alpha(min_period)
|
13
|
+
3.0
|
17
14
|
end
|
18
15
|
|
19
16
|
def compute_midpoint
|
17
|
+
alpha = bars_to_alpha(min_period)
|
20
18
|
p0.midpoint = alpha * p0.input + (1 - alpha) * p1.midpoint
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
atr_value = atr_point.slow * scale
|
21
|
+
KELTNER_SERIES = [0.236, 0.382, 0.500, 0.618, 0.786, 1.0].freeze
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
p0.h4 = p0.midpoint + 0.618 * atr_value
|
29
|
-
p0.h3 = p0.midpoint + 0.500 * atr_value
|
30
|
-
p0.h2 = p0.midpoint + 0.382 * atr_value
|
31
|
-
p0.h1 = p0.midpoint + 0.236 * atr_value
|
23
|
+
def compute_bands
|
24
|
+
atr_value = atr_point.value * scale
|
32
25
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
p0.l6 = p0.midpoint - 1.000 * atr_value
|
26
|
+
KELTNER_SERIES.each_with_index do |ratio, index|
|
27
|
+
offset = ratio * atr_value
|
28
|
+
p0[index + 1] = p0.midpoint + offset
|
29
|
+
p0[-index - 1] = p0.midpoint - offset
|
30
|
+
end
|
39
31
|
end
|
40
32
|
end
|
41
33
|
end
|
@@ -3,6 +3,8 @@ module Quant
|
|
3
3
|
module Indicators
|
4
4
|
module Pivots
|
5
5
|
class Murrey < Pivot
|
6
|
+
register name: :murrey
|
7
|
+
|
6
8
|
def multiplier
|
7
9
|
0.125
|
8
10
|
end
|
@@ -12,20 +14,13 @@ module Quant
|
|
12
14
|
p0.midpoint = p0.lowest + (p0.input * 4.0)
|
13
15
|
end
|
14
16
|
|
15
|
-
|
16
|
-
p0.h6 = p0.midpoint + p0.input * 6.0
|
17
|
-
p0.h5 = p0.midpoint + p0.input * 5.0
|
18
|
-
p0.h4 = p0.midpoint + p0.input * 4.0
|
19
|
-
p0.h3 = p0.midpoint + p0.input * 3.0
|
20
|
-
p0.h2 = p0.midpoint + p0.input * 2.0
|
21
|
-
p0.h1 = p0.midpoint + p0.input * 1.0
|
17
|
+
MURREY_SERIES = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0].freeze
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
p0.l6 = p0.midpoint - p0.input * 6.0
|
19
|
+
def compute_bands
|
20
|
+
MURREY_SERIES.each_with_index do |ratio, index|
|
21
|
+
p0[index + 1] = p0.midpoint + p0.input * ratio
|
22
|
+
p0[-index - 1] = p0.midpoint - p0.input * ratio
|
23
|
+
end
|
29
24
|
end
|
30
25
|
end
|
31
26
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Quant
|
2
|
+
module Indicators
|
3
|
+
module Pivots
|
4
|
+
class PivotPoint < IndicatorPoint
|
5
|
+
attribute :avg_high, default: :high_price
|
6
|
+
attribute :highest, default: :input
|
7
|
+
|
8
|
+
attribute :avg_low, default: :low_price
|
9
|
+
attribute :lowest, default: :input
|
10
|
+
|
11
|
+
attribute :range, default: 0.0
|
12
|
+
attribute :avg_range, default: 0.0
|
13
|
+
attribute :std_dev, default: 0.0
|
14
|
+
|
15
|
+
def bands
|
16
|
+
@bands ||= { 0 => input }
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](band)
|
20
|
+
bands[band]
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(band, value)
|
24
|
+
bands[band] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def key?(band)
|
28
|
+
bands.key?(band)
|
29
|
+
end
|
30
|
+
|
31
|
+
def midpoint
|
32
|
+
bands[0]
|
33
|
+
end
|
34
|
+
alias :h0 :midpoint
|
35
|
+
alias :l0 :midpoint
|
36
|
+
|
37
|
+
def midpoint=(value)
|
38
|
+
bands[0] = value
|
39
|
+
end
|
40
|
+
alias :h0= :midpoint=
|
41
|
+
alias :l0= :midpoint=
|
42
|
+
|
43
|
+
(1..8).each do |band|
|
44
|
+
define_method("h#{band}") { bands[band] }
|
45
|
+
define_method("h#{band}=") { |value| bands[band] = value }
|
46
|
+
|
47
|
+
define_method("l#{band}") { bands[-band] }
|
48
|
+
define_method("l#{band}=") { |value| bands[-band] = value }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Pivot < Indicator
|
53
|
+
def points_class
|
54
|
+
Quant::Indicators::Pivots::PivotPoint
|
55
|
+
end
|
56
|
+
|
57
|
+
def band?(band)
|
58
|
+
p0.key?(band)
|
59
|
+
end
|
60
|
+
|
61
|
+
def period
|
62
|
+
adaptive_period
|
63
|
+
end
|
64
|
+
|
65
|
+
def averaging_period
|
66
|
+
min_period
|
67
|
+
end
|
68
|
+
|
69
|
+
def period_midpoints
|
70
|
+
period_points(period).map(&:midpoint)
|
71
|
+
end
|
72
|
+
|
73
|
+
def smoothed_average_midpoint
|
74
|
+
three_pole_super_smooth :input, previous: :midpoint, period: averaging_period
|
75
|
+
end
|
76
|
+
|
77
|
+
def compute
|
78
|
+
compute_extents
|
79
|
+
compute_value
|
80
|
+
compute_midpoint
|
81
|
+
compute_bands
|
82
|
+
end
|
83
|
+
|
84
|
+
def compute_midpoint
|
85
|
+
p0.midpoint = p0.input
|
86
|
+
end
|
87
|
+
|
88
|
+
def compute_value
|
89
|
+
# No-op -- override in subclasses
|
90
|
+
end
|
91
|
+
|
92
|
+
def compute_bands
|
93
|
+
# No-op -- override in subclasses
|
94
|
+
end
|
95
|
+
|
96
|
+
def compute_extents
|
97
|
+
period_midpoints.tap do |midpoints|
|
98
|
+
p0.highest = midpoints.max
|
99
|
+
p0.lowest = midpoints.min
|
100
|
+
p0.range = p0.high_price - p0.low_price
|
101
|
+
p0.avg_low = three_pole_super_smooth(:low_price, previous: :avg_low, period: averaging_period)
|
102
|
+
p0.avg_high = three_pole_super_smooth(:high_price, previous: :avg_high, period: averaging_period)
|
103
|
+
p0.avg_range = three_pole_super_smooth(:range, previous: :avg_range, period: averaging_period)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Indicators
|
5
|
+
class RocketRsiPoint < IndicatorPoint
|
6
|
+
attribute :hp, default: 0.0
|
7
|
+
|
8
|
+
attribute :delta, default: 0.0
|
9
|
+
attribute :gain, default: 0.0
|
10
|
+
attribute :loss, default: 0.0
|
11
|
+
|
12
|
+
attribute :gains, default: 0.0
|
13
|
+
attribute :losses, default: 0.0
|
14
|
+
attribute :denom, default: 0.0
|
15
|
+
|
16
|
+
attribute :inst_rsi, default: 0.5
|
17
|
+
attribute :rsi, default: 0.0
|
18
|
+
attribute :crosses, default: false
|
19
|
+
end
|
20
|
+
|
21
|
+
class RocketRsi < Indicator
|
22
|
+
register name: :rocket_rsi
|
23
|
+
|
24
|
+
def quarter_period
|
25
|
+
half_period / 2
|
26
|
+
end
|
27
|
+
|
28
|
+
def half_period
|
29
|
+
(dc_period / 2) - 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def compute
|
33
|
+
p0.hp = two_pole_butterworth :input, previous: :hp, period: quarter_period
|
34
|
+
|
35
|
+
lp = p(half_period)
|
36
|
+
p0.delta = p0.hp - lp.hp
|
37
|
+
p0.delta > 0.0 ? p0.gain = p0.delta : p0.loss = p0.delta.abs
|
38
|
+
|
39
|
+
period_points(half_period).tap do |period_points|
|
40
|
+
p0.gains = period_points.map(&:gain).sum
|
41
|
+
p0.losses = period_points.map(&:loss).sum
|
42
|
+
end
|
43
|
+
|
44
|
+
p0.denom = p0.gains + p0.losses
|
45
|
+
|
46
|
+
if p0.denom.zero?
|
47
|
+
p0.inst_rsi = p1.inst_rsi
|
48
|
+
p0.rsi = p1.rsi
|
49
|
+
else
|
50
|
+
p0.inst_rsi = ((p0.gains - p0.losses) / p0.denom)
|
51
|
+
p0.rsi = fisher_transform(p0.inst_rsi).clamp(-1.0, 1.0)
|
52
|
+
end
|
53
|
+
p0.crosses = (p0.rsi >= 0.0 && p1.rsi < 0.0) || (p0.rsi <= 0.0 && p1.rsi > 0.0)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Indicators
|
5
|
+
# The ideal time to buy is when the cycle is at a trough, and the ideal time to exit a long position or to
|
6
|
+
# sell short is when the cycle is at a peak.These conditions are flagged by the filter crossing itself
|
7
|
+
# delayed by two bars, and are included as part of the indicator.
|
8
|
+
class RoofingPoint < IndicatorPoint
|
9
|
+
attribute :hp, default: 0.0
|
10
|
+
attribute :value, default: 0.0
|
11
|
+
attribute :peak, default: 0.0
|
12
|
+
attribute :agc, default: 0.0
|
13
|
+
attribute :direction, default: 0
|
14
|
+
attribute :turned, default: false
|
15
|
+
end
|
16
|
+
|
17
|
+
class Roofing < Indicator
|
18
|
+
register name: :roofing
|
19
|
+
|
20
|
+
def low_pass_period
|
21
|
+
dc_period
|
22
|
+
end
|
23
|
+
|
24
|
+
def high_pass_period
|
25
|
+
low_pass_period * 2
|
26
|
+
end
|
27
|
+
|
28
|
+
# //Highpass filter cyclic components whose periods are shorter than 48 bars
|
29
|
+
# alpha1 = (Cosine(.707*360 / HPPeriod) + Sine (.707*360 / HPPeriod) - 1) / Cosine(.707*360 / HPPeriod);
|
30
|
+
# HP = (1 - alpha1 / 2)*(1 - alpha1 / 2)*(Close - 2*Close[1] + Close[2]) + 2*(1 - alpha1)*HP[1] - (1 - alpha1)*
|
31
|
+
# (1 - alpha1)*HP[2];
|
32
|
+
# //Smooth with a Super Smoother Filter from equation 3-3
|
33
|
+
# a1 = expvalue(-1.414*3.14159 / LPPeriod);
|
34
|
+
# b1 = 2*a1*Cosine(1.414*180 / LPPeriod);
|
35
|
+
# c2 = b1;
|
36
|
+
# c3 = -a1*a1;
|
37
|
+
# c1 = 1 - c2 - c3;
|
38
|
+
# Filt = c1*(HP + HP[1]) / 2 + c2*Filt[1] + c3*Filt[2
|
39
|
+
def compute
|
40
|
+
a = Math.cos(0.707 * deg2rad(360) / high_pass_period)
|
41
|
+
b = Math.sin(0.707 * deg2rad(360) / high_pass_period)
|
42
|
+
alpha1 = (a + b - 1) / a
|
43
|
+
|
44
|
+
p0.hp = (1 - alpha1 / 2)**2 * (p0.input - 2 * p1.input + p2.input) + 2 * (1 - alpha1) * p1.hp - (1 - alpha1)**2 * p2.hp
|
45
|
+
a1 = Math.exp(-1.414 * Math::PI / low_pass_period)
|
46
|
+
c2 = 2 * a1 * Math.cos(1.414 * deg2rad(180) / low_pass_period)
|
47
|
+
c3 = -a1**2
|
48
|
+
c1 = 1 - c2 - c3
|
49
|
+
p0.value = c1 * (p0.hp + p1.hp) / 2 + c2 * p1.value + c3 * p2.value
|
50
|
+
p0.direction = p0.value > p2.value ? 1 : -1
|
51
|
+
p0.turned = p0.direction != p2.direction
|
52
|
+
# Peak = .991 * Peak[1];
|
53
|
+
# If AbsValue(BP) > Peak Then Peak = AbsValue(BP); If Peak <> 0 Then Signal = BP / Peak;
|
54
|
+
p0.peak = [p0.value.abs, 0.991 * p1.peak].max
|
55
|
+
p0.agc = p0.peak == 0 ? 0 : p0.value / p0.peak
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Indicators
|
5
|
+
class RsiPoint < IndicatorPoint
|
6
|
+
attribute :hp, default: 0.0
|
7
|
+
attribute :filter, default: 0.0
|
8
|
+
|
9
|
+
attribute :delta, default: 0.0
|
10
|
+
attribute :gain, default: 0.0
|
11
|
+
attribute :loss, default: 0.0
|
12
|
+
|
13
|
+
attribute :gains, default: 0.0
|
14
|
+
attribute :losses, default: 0.0
|
15
|
+
attribute :denom, default: 0.0
|
16
|
+
|
17
|
+
attribute :inst_rsi, default: 0.0
|
18
|
+
attribute :rsi, default: 0.0
|
19
|
+
end
|
20
|
+
|
21
|
+
# The Relative Strength Index (RSI) is a momentum oscillator that measures the
|
22
|
+
# speed and change of price movements. This RSI indicator is adaptive and
|
23
|
+
# uses the half-period of the dominant cycle to calculate the RSI.
|
24
|
+
# It is further smoothed by an exponential moving average of the last three bars
|
25
|
+
# (or whatever the micro_period is set to).
|
26
|
+
#
|
27
|
+
# The RSI oscillates between 0 and 1. Traditionally, and in this implementation,
|
28
|
+
# the RSI is considered overbought when above 0.7 and oversold when below 0.3.
|
29
|
+
class Rsi < Indicator
|
30
|
+
register name: :rsi
|
31
|
+
|
32
|
+
def quarter_period
|
33
|
+
half_period / 2
|
34
|
+
end
|
35
|
+
|
36
|
+
def half_period
|
37
|
+
(dc_period / 2) - 1
|
38
|
+
end
|
39
|
+
|
40
|
+
def compute
|
41
|
+
# The High Pass filter is half the dominant cycle period while the
|
42
|
+
# Low Pass Filter (super smoother) is the quarter dominant cycle period.
|
43
|
+
p0.hp = high_pass_filter :input, period: half_period
|
44
|
+
p0.filter = ema :hp, previous: :filter, period: quarter_period
|
45
|
+
|
46
|
+
lp = p(half_period)
|
47
|
+
p0.delta = p0.filter - lp.filter
|
48
|
+
p0.delta > 0.0 ? p0.gain = p0.delta : p0.loss = p0.delta.abs
|
49
|
+
|
50
|
+
period_points(half_period).tap do |period_points|
|
51
|
+
p0.gains = period_points.map(&:gain).sum
|
52
|
+
p0.losses = period_points.map(&:loss).sum
|
53
|
+
end
|
54
|
+
|
55
|
+
p0.denom = p0.gains + p0.losses
|
56
|
+
|
57
|
+
if p0.denom > 0.0
|
58
|
+
p0.inst_rsi = (p0.gains / p0.denom)
|
59
|
+
p0.rsi = ema :inst_rsi, previous: :rsi, period: micro_period
|
60
|
+
else
|
61
|
+
p0.inst_rsi = 0.5
|
62
|
+
p0.rsi = 0.5
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Indicators
|
5
|
+
class SnrPoint < IndicatorPoint
|
6
|
+
attribute :smooth, default: 0.0
|
7
|
+
attribute :detrend, default: 0.0
|
8
|
+
attribute :i1, default: 0.0
|
9
|
+
attribute :q1, default: 0.0
|
10
|
+
attribute :noise, default: 0.0
|
11
|
+
attribute :signal, default: 0.0
|
12
|
+
attribute :ratio, default: 0.0
|
13
|
+
attribute :state, default: 0
|
14
|
+
end
|
15
|
+
|
16
|
+
class Snr < Indicator
|
17
|
+
register name: :snr
|
18
|
+
depends_on DominantCycles::Homodyne
|
19
|
+
|
20
|
+
def homodyne_dominant_cycle
|
21
|
+
series.indicators[source].dominant_cycles.homodyne
|
22
|
+
end
|
23
|
+
|
24
|
+
def current_dominant_cycle
|
25
|
+
homodyne_dominant_cycle.points[t0]
|
26
|
+
end
|
27
|
+
|
28
|
+
def threshold
|
29
|
+
@threshold ||= 10 * Math.log(0.5)**2
|
30
|
+
end
|
31
|
+
|
32
|
+
def compute_values
|
33
|
+
current_dominant_cycle.tap do |dc|
|
34
|
+
p0.i1 = dc.i1
|
35
|
+
p0.q1 = dc.q1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def compute_noise
|
40
|
+
noise = (p0.input - p2.input).abs
|
41
|
+
p0.noise = p1.noise.zero? ? noise : (0.1 * noise) + (0.9 * p1.noise)
|
42
|
+
end
|
43
|
+
|
44
|
+
def compute_ratio
|
45
|
+
# p0.ratio = 0.25 * (10 * Math.log(p0.i1**2 + p0.q1**2) / Math.log(10)) + 0.75 * p1.ratio
|
46
|
+
# ratio = .25*(10 * Log(I1*I1 + Q1*Q1)/(Range*Range))/Log(10) + 6) + .75*ratio[1]
|
47
|
+
if p0 == p1
|
48
|
+
p0.signal = 0.0
|
49
|
+
p0.ratio = 1.0
|
50
|
+
else
|
51
|
+
p0.signal = threshold + 10.0 * (Math.log((p0.i1**2 + p0.q1**2)/(p0.noise**2)) / Math.log(10))
|
52
|
+
p0.ratio = (0.25 * p0.signal) + (0.75 * p1.ratio)
|
53
|
+
end
|
54
|
+
p0.state = p0.ratio >= threshold ? 1 : 0
|
55
|
+
end
|
56
|
+
|
57
|
+
def compute
|
58
|
+
compute_values
|
59
|
+
compute_noise
|
60
|
+
compute_ratio
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module IndicatorsRegistry
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
def define_indicator_accessors(indicator_source:)
|
10
|
+
self.class.define_indicator_accessors(indicator_source:)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def registry
|
15
|
+
@registry ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
class RegistryEntry
|
19
|
+
attr_reader :name, :indicator_class
|
20
|
+
|
21
|
+
def initialize(name:, indicator_class:)
|
22
|
+
@name = name
|
23
|
+
@indicator_class = indicator_class
|
24
|
+
end
|
25
|
+
|
26
|
+
def key
|
27
|
+
"#{indicator_class.name}::#{name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def standard?
|
31
|
+
!pivot? && !dominant_cycle?
|
32
|
+
end
|
33
|
+
|
34
|
+
def pivot?
|
35
|
+
indicator_class < Indicators::Pivots::Pivot
|
36
|
+
end
|
37
|
+
|
38
|
+
def dominant_cycle?
|
39
|
+
indicator_class < Indicators::DominantCycles::DominantCycle
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def register(name:, indicator_class:)
|
44
|
+
entry = RegistryEntry.new(name:, indicator_class:)
|
45
|
+
registry[entry.key] = entry
|
46
|
+
# registry[name] = indicator_class
|
47
|
+
end
|
48
|
+
|
49
|
+
def registry_entries_for(indicator_source:)
|
50
|
+
return registry.values.select(&:pivot?) if indicator_source.is_a?(PivotsSource)
|
51
|
+
return registry.values.select(&:dominant_cycle?) if indicator_source.is_a?(DominantCyclesSource)
|
52
|
+
|
53
|
+
registry.values.select(&:standard?)
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_indicator_accessors(indicator_source:)
|
57
|
+
registry_entries_for(indicator_source:).each do |entry|
|
58
|
+
indicator_source.define_singleton_method(entry.name) { indicator(entry.indicator_class) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|