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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/lib/quant/dominant_cycles_source.rb +1 -32
  4. data/lib/quant/indicators/adx.rb +2 -5
  5. data/lib/quant/indicators/atr.rb +2 -0
  6. data/lib/quant/indicators/cci.rb +2 -0
  7. data/lib/quant/indicators/decycler.rb +2 -0
  8. data/lib/quant/indicators/dominant_cycles/acr.rb +2 -0
  9. data/lib/quant/indicators/dominant_cycles/band_pass.rb +2 -0
  10. data/lib/quant/indicators/dominant_cycles/differential.rb +2 -0
  11. data/lib/quant/indicators/dominant_cycles/half_period.rb +2 -0
  12. data/lib/quant/indicators/dominant_cycles/homodyne.rb +2 -0
  13. data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +2 -0
  14. data/lib/quant/indicators/ema.rb +67 -0
  15. data/lib/quant/indicators/frama.rb +1 -0
  16. data/lib/quant/indicators/indicator.rb +17 -57
  17. data/lib/quant/indicators/indicator_point.rb +11 -0
  18. data/lib/quant/indicators/mama.rb +2 -0
  19. data/lib/quant/indicators/mesa.rb +3 -4
  20. data/lib/quant/indicators/ping.rb +2 -0
  21. data/lib/quant/indicators/pivots/atr.rb +11 -16
  22. data/lib/quant/indicators/pivots/bollinger.rb +10 -25
  23. data/lib/quant/indicators/pivots/camarilla.rb +30 -31
  24. data/lib/quant/indicators/pivots/classic.rb +3 -1
  25. data/lib/quant/indicators/pivots/demark.rb +5 -3
  26. data/lib/quant/indicators/pivots/donchian.rb +20 -23
  27. data/lib/quant/indicators/pivots/fibbonacci.rb +12 -9
  28. data/lib/quant/indicators/pivots/guppy.rb +3 -1
  29. data/lib/quant/indicators/pivots/keltner.rb +11 -19
  30. data/lib/quant/indicators/pivots/murrey.rb +8 -13
  31. data/lib/quant/indicators/pivots/pivot.rb +109 -0
  32. data/lib/quant/indicators/pivots/traditional.rb +2 -0
  33. data/lib/quant/indicators/pivots/woodie.rb +2 -0
  34. data/lib/quant/indicators/rocket_rsi.rb +57 -0
  35. data/lib/quant/indicators/roofing.rb +59 -0
  36. data/lib/quant/indicators/rsi.rb +67 -0
  37. data/lib/quant/indicators/snr.rb +64 -0
  38. data/lib/quant/indicators_registry.rb +63 -0
  39. data/lib/quant/indicators_source.rb +14 -9
  40. data/lib/quant/pivots_source.rb +1 -13
  41. data/lib/quant/version.rb +1 -1
  42. data/lib/quantitative.rb +10 -3
  43. metadata +9 -3
  44. 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
- using Quant
7
+ register name: :donchian
8
8
 
9
- def st_period; min_period end
10
- def mt_period; half_period end
11
- def lt_period; max_period end
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
- st_highs << p0.high_price
22
- st_lows << p0.low_price
23
- mt_highs << p0.high_price
24
- mt_lows << p0.low_price
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
- p0.h1 = @st_highs.maximum
29
- p0.l1 = @st_lows.minimum
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
- p0.h2 = @mt_highs.maximum
32
- p0.l2 = @mt_lows.minimum
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
- p0.h3 = @lt_highs.maximum
35
- p0.l3 = @lt_lows.minimum
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
- def averaging_period
6
- half_period
7
- end
5
+ register name: :fibbonacci
8
6
 
9
- def fibbonacci_series
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
- fibbonacci_series.each_with_index do |ratio, index|
15
- p0[index + 1] = p0.midpoint + ratio * p0.avg_range
16
- p0[-index - 1] = p0.midpoint - ratio * p0.avg_range
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 compute
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
- 5.0
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
- def compute_bands
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
- p0.h6 = p0.midpoint + 1.000 * atr_value
27
- p0.h5 = p0.midpoint + 0.786 * atr_value
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
- p0.l1 = p0.midpoint - 0.236 * atr_value
34
- p0.l2 = p0.midpoint - 0.382 * atr_value
35
- p0.l3 = p0.midpoint - 0.500 * atr_value
36
- p0.l4 = p0.midpoint - 0.618 * atr_value
37
- p0.l5 = p0.midpoint - 0.786 * atr_value
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
- def compute_bands
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
- p0.l1 = p0.midpoint - p0.input * 1.0
24
- p0.l2 = p0.midpoint - p0.input * 2.0
25
- p0.l3 = p0.midpoint - p0.input * 3.0
26
- p0.l4 = p0.midpoint - p0.input * 4.0
27
- p0.l5 = p0.midpoint - p0.input * 5.0
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
@@ -2,6 +2,8 @@ module Quant
2
2
  module Indicators
3
3
  module Pivots
4
4
  class Traditional < Pivot
5
+ register name: :traditional
6
+
5
7
  def multiplier
6
8
  2.0
7
9
  end
@@ -22,6 +22,8 @@ module Quant
22
22
  # S3 = L - 2 * (H - PP) (same as: S1 - RANGE)
23
23
  # S4 = S3 - RANGE
24
24
  class Woodie < Pivot
25
+ register name: :woodie
26
+
25
27
  def compute_value
26
28
  p0.input = (t1.high_price + t1.low_price + 2.0 * t0.open_price) / 4.0
27
29
  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