quantitative 0.3.1 → 0.3.3

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