quantitative 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +9 -4
  3. data/Gemfile.lock +9 -1
  4. data/README.md +5 -0
  5. data/lib/quant/{indicators/dominant_cycle_indicators.rb → dominant_cycles_source.rb} +19 -8
  6. data/lib/quant/experimental.rb +8 -1
  7. data/lib/quant/indicators/adx.rb +83 -0
  8. data/lib/quant/indicators/atr.rb +79 -0
  9. data/lib/quant/indicators/cci.rb +63 -0
  10. data/lib/quant/indicators/decycler.rb +71 -0
  11. data/lib/quant/indicators/dominant_cycles/acr.rb +2 -0
  12. data/lib/quant/indicators/dominant_cycles/band_pass.rb +2 -0
  13. data/lib/quant/indicators/dominant_cycles/differential.rb +3 -1
  14. data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +12 -6
  15. data/lib/quant/indicators/dominant_cycles/half_period.rb +2 -0
  16. data/lib/quant/indicators/dominant_cycles/homodyne.rb +4 -2
  17. data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +8 -4
  18. data/lib/quant/indicators/frama.rb +50 -0
  19. data/lib/quant/indicators/indicator.rb +50 -2
  20. data/lib/quant/indicators/mama.rb +143 -0
  21. data/lib/quant/indicators/mesa.rb +86 -0
  22. data/lib/quant/indicators/pivot.rb +107 -0
  23. data/lib/quant/indicators/pivots/atr.rb +41 -0
  24. data/lib/quant/indicators/pivots/bollinger.rb +45 -0
  25. data/lib/quant/indicators/pivots/camarilla.rb +61 -0
  26. data/lib/quant/indicators/pivots/classic.rb +24 -0
  27. data/lib/quant/indicators/pivots/demark.rb +50 -0
  28. data/lib/quant/indicators/pivots/donchian.rb +40 -0
  29. data/lib/quant/indicators/pivots/fibbonacci.rb +22 -0
  30. data/lib/quant/indicators/pivots/guppy.rb +39 -0
  31. data/lib/quant/indicators/pivots/keltner.rb +43 -0
  32. data/lib/quant/indicators/pivots/murrey.rb +34 -0
  33. data/lib/quant/indicators/pivots/traditional.rb +36 -0
  34. data/lib/quant/indicators/pivots/woodie.rb +59 -0
  35. data/lib/quant/indicators_source.rb +140 -0
  36. data/lib/quant/indicators_sources.rb +36 -10
  37. data/lib/quant/mixins/stochastic.rb +1 -1
  38. data/lib/quant/mixins/super_smoother.rb +11 -2
  39. data/lib/quant/pivots_source.rb +28 -0
  40. data/lib/quant/refinements/array.rb +14 -0
  41. data/lib/quant/series.rb +8 -19
  42. data/lib/quant/settings/indicators.rb +11 -0
  43. data/lib/quant/version.rb +1 -1
  44. data/possibilities.png +0 -0
  45. data/quantitative.gemspec +39 -0
  46. metadata +27 -5
  47. data/lib/quant/indicators.rb +0 -14
  48. data/lib/quant/indicators_proxy.rb +0 -68
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Quant
4
4
  class Indicators
5
+ # The {Quant::Indicators::Indicator} class is the abstract ancestor for all Indicators.
6
+ #
5
7
  class Indicator
6
8
  include Enumerable
7
9
  include Mixins::Functions
@@ -13,16 +15,57 @@ module Quant
13
15
  include Mixins::FisherTransform
14
16
  # include Mixins::Direction
15
17
 
18
+ # Provides a registry of dependent indicators for each indicator class.
19
+ # NOTE: Internal use only.
20
+ def self.dependent_indicator_classes
21
+ @dependent_indicator_classes ||= Set.new
22
+ end
23
+
24
+ # Use the {depends_on} method to declare dependencies for an indicator.
25
+ # @param indicator_classes [Array<Class>] The classes of the indicators to depend on.
26
+ # @example
27
+ # class BarIndicator < Indicator
28
+ # depends_on FooIndicator
29
+ # end
30
+ def self.depends_on(*indicator_classes)
31
+ Array(indicator_classes).each{ |dependency| dependent_indicator_classes << dependency }
32
+ end
33
+
16
34
  attr_reader :source, :series
17
35
 
18
36
  def initialize(series:, source:)
19
37
  @series = series
20
38
  @source = source
21
39
  @points = {}
22
- series.new_indicator(self)
23
40
  series.each { |tick| self << tick }
24
41
  end
25
42
 
43
+ def dominant_cycle_indicator_class
44
+ Quant.config.indicators.dominant_cycle_indicator_class
45
+ end
46
+
47
+ # The priority drives the order of computations when iterating over each tick
48
+ # in a series. Generally speaking, indicators that feed values to another indicator
49
+ # must have a lower priority value than the indicator that consumes the values.
50
+ # * Most indicators will have a default priority of 1000.
51
+ # * Dominant Cycle indicators will have a priority of 100.
52
+ # * Some indicators will have a "high priority" of 500.
53
+ # Priority values are arbitrary and purposefully gapping so that new indicators
54
+ # introduced outside the core library can be slotted in between.
55
+ #
56
+ # NOTE: Priority is well-managed by the library and should not require overriding
57
+ # for a custom indicator developed outside the library. If you find yourself
58
+ # needing to override this method, please open an issue on the library's GitHub page.
59
+ PRIORITIES = [
60
+ DOMINANT_CYCLES_PRIORITY = 100,
61
+ DEPENDENCY_PRIORITY = 500,
62
+ DEFAULT_PRIORITY = 1000
63
+ ].freeze
64
+
65
+ def priority
66
+ DEFAULT_PRIORITY
67
+ end
68
+
26
69
  def min_period
27
70
  Quant.config.indicators.min_period
28
71
  end
@@ -48,7 +91,7 @@ module Quant
48
91
  end
49
92
 
50
93
  def dominant_cycle
51
- series.indicators[source].dominant_cycle
94
+ series.indicators[source][dominant_cycle_indicator_class]
52
95
  end
53
96
 
54
97
  def dc_period
@@ -71,6 +114,11 @@ module Quant
71
114
  @points.size
72
115
  end
73
116
 
117
+ def period_points(max_period)
118
+ extent = [values.size, max_period].min
119
+ values[-extent, extent]
120
+ end
121
+
74
122
  attr_reader :p0, :p1, :p2, :p3
75
123
  attr_reader :t0, :t1, :t2, :t3
76
124
 
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ class Indicators
5
+ class MamaPoint < IndicatorPoint
6
+ attribute :smooth, default: 0.0
7
+ attribute :detrend, default: 0.0
8
+ attribute :re, default: 0.0
9
+ attribute :im, default: 0.0
10
+ attribute :i1, default: 0.0
11
+ attribute :q1, default: 0.0
12
+ attribute :ji, default: 0.0
13
+ attribute :jq, default: 0.0
14
+ attribute :i2, default: 0.0
15
+ attribute :q2, default: 0.0
16
+ attribute :period, default: :min_period
17
+ attribute :smooth_period, default: :min_period
18
+ attribute :mama, default: :input
19
+ attribute :fama, default: :input
20
+ attribute :gama, default: :input
21
+ attribute :dama, default: :input
22
+ attribute :lama, default: :input
23
+ attribute :faga, default: :input
24
+ attribute :phase, default: 0.0
25
+ attribute :delta_phase, default: 0.0
26
+ attribute :osc, default: 0.0
27
+ attribute :crossed, default: :unchanged
28
+
29
+ def crossed_up?
30
+ @crossed == :up
31
+ end
32
+
33
+ def crossed_down?
34
+ @crossed == :down
35
+ end
36
+ end
37
+
38
+ # https://www.mesasoftware.com/papers/MAMA.pdf
39
+ # MESA Adaptive Moving Average (MAMA) adapts to price movement in an
40
+ # entirely new and unique way. The adapation is based on the rate change
41
+ # of phase as measured by the Hilbert Transform Discriminator.
42
+ #
43
+ # This version of Ehler's MAMA indicator duplicates the computations
44
+ # present in the homodyne version of the dominant cycle indicator.
45
+ # Use this version of the indicator when you're using a different
46
+ # dominant cycle indicator other than the homodyne for the rest
47
+ # of your indicators.
48
+ class Mama < Indicator
49
+ # constrain between 6 and 50 bars
50
+ def constrain_period_bars
51
+ p0.period = p0.period.clamp(min_period, max_period)
52
+ end
53
+
54
+ # constrain magnitude of change in phase
55
+ def constrain_period_magnitude_change
56
+ p0.period = [1.5 * p1.period, p0.period].min
57
+ p0.period = [0.67 * p1.period, p0.period].max
58
+ end
59
+
60
+ # amplitude correction using previous period value
61
+ def compute_smooth_period
62
+ p0.period = ((0.2 * p0.period) + (0.8 * p1.period)).round
63
+ p0.smooth_period = ((0.33333 * p0.period) + (0.666667 * p1.smooth_period)).round
64
+ end
65
+
66
+ def homodyne_discriminator
67
+ p0.re = (p0.i2 * p1.i2) + (p0.q2 * p1.q2)
68
+ p0.im = (p0.i2 * p1.q2) - (p0.q2 * p1.i2)
69
+
70
+ p0.re = (0.2 * p0.re) + (0.8 * p1.re)
71
+ p0.im = (0.2 * p0.im) + (0.8 * p1.im)
72
+
73
+ p0.period = 360.0 / rad2deg(Math.atan(p0.im / p0.re)) if (p0.im != 0) && (p0.re != 0)
74
+
75
+ constrain_period_magnitude_change
76
+ constrain_period_bars
77
+ compute_smooth_period
78
+ end
79
+
80
+ def compute_dominant_cycle
81
+ p0.smooth = wma :input
82
+ p0.detrend = hilbert_transform :smooth, period: p1.period
83
+
84
+ # { Compute Inphase and Quadrature components }
85
+ p0.q1 = hilbert_transform :detrend, period: p1.period
86
+ p0.i1 = p3.detrend
87
+
88
+ # { Advance the phase of I1 and Q1 by 90 degrees }
89
+ p0.ji = hilbert_transform :i1, period: p1.period
90
+ p0.jq = hilbert_transform :q1, period: p1.period
91
+
92
+ # { Smooth the I and Q components before applying the discriminator }
93
+ p0.i2 = (0.2 * (p0.i1 - p0.jq)) + 0.8 * (p1.i2 || (p0.i1 - p0.jq))
94
+ p0.q2 = (0.2 * (p0.q1 + p0.ji)) + 0.8 * (p1.q2 || (p0.q1 + p0.ji))
95
+
96
+ homodyne_discriminator
97
+ end
98
+
99
+ def fast_limit
100
+ @fast_limit ||= bars_to_alpha(min_period / 2)
101
+ end
102
+
103
+ def slow_limit
104
+ @slow_limit ||= bars_to_alpha(max_period)
105
+ end
106
+
107
+ def compute_dominant_cycle_phase
108
+ p0.delta_phase = p1.phase - p0.phase
109
+ p0.delta_phase = 1.0 if p0.delta_phase < 1.0
110
+ end
111
+
112
+ FAMA = 0.500
113
+ GAMA = 0.950
114
+ DAMA = 0.125
115
+ LAMA = 0.100
116
+ FAGA = 0.050
117
+
118
+ def compute_moving_averages
119
+ alpha = [fast_limit / p0.delta_phase, slow_limit].max
120
+ p0.mama = (alpha * p0.input) + ((1.0 - alpha) * p1.mama)
121
+
122
+ p0.fama = (FAMA * alpha * p0.mama) + ((1.0 - (FAMA * alpha)) * p1.fama)
123
+ p0.gama = (GAMA * alpha * p0.mama) + ((1.0 - (GAMA * alpha)) * p1.gama)
124
+ p0.dama = (DAMA * alpha * p0.mama) + ((1.0 - (DAMA * alpha)) * p1.dama)
125
+ p0.lama = (LAMA * alpha * p0.mama) + ((1.0 - (LAMA * alpha)) * p1.lama)
126
+ p0.faga = (FAGA * alpha * p0.fama) + ((1.0 - (FAGA * alpha)) * p1.faga)
127
+ end
128
+
129
+ def compute_oscillator
130
+ p0.osc = p0.mama - p0.fama
131
+ p0.crossed = :up if p0.osc >= 0 && p1.osc < 0
132
+ p0.crossed = :down if p0.osc <= 0 && p1.osc > 0
133
+ end
134
+
135
+ def compute
136
+ compute_dominant_cycle
137
+ compute_dominant_cycle_phase
138
+ compute_moving_averages
139
+ compute_oscillator
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ class Indicators
5
+ # The MESA inidicator
6
+ class MesaPoint < IndicatorPoint
7
+ attribute :mama, default: :input
8
+ attribute :fama, default: :input
9
+ attribute :dama, default: :input
10
+ attribute :gama, default: :input
11
+ attribute :lama, default: :input
12
+ attribute :faga, default: :input
13
+ attribute :osc, default: 0.0
14
+ attribute :crossed, default: :unchanged
15
+
16
+ def crossed_up?
17
+ @crossed == :up
18
+ end
19
+
20
+ def crossed_down?
21
+ @crossed == :down
22
+ end
23
+ end
24
+
25
+ # https://www.mesasoftware.com/papers/MAMA.pdf
26
+ # MESA Adaptive Moving Average (MAMA) adapts to price movement in an
27
+ # entirely new and unique way. The adapation is based on the rate change
28
+ # of phase as measured by the Hilbert Transform Discriminator.
29
+ #
30
+ # This version of Ehler's MAMA indicator ties into the homodyne
31
+ # dominant cycle indicator to provide a more efficient computation
32
+ # for this indicator. If you're using the homodyne in all your
33
+ # indicators for the dominant cycle, then this version is useful
34
+ # as it avoids extra computational steps.
35
+ class Mesa < Indicator
36
+ def period
37
+ dc_period
38
+ end
39
+
40
+ def fast_limit
41
+ @fast_limit ||= bars_to_alpha(min_period / 2)
42
+ end
43
+
44
+ def slow_limit
45
+ @slow_limit ||= bars_to_alpha(max_period)
46
+ end
47
+
48
+ def homodyne_dominant_cycle
49
+ series.indicators[source].dominant_cycles.homodyne
50
+ end
51
+
52
+ def current_dominant_cycle
53
+ homodyne_dominant_cycle.points[t0]
54
+ end
55
+
56
+ def delta_phase
57
+ current_dominant_cycle.delta_phase
58
+ end
59
+
60
+ FAMA = 0.500
61
+ GAMA = 0.950
62
+ DAMA = 0.125
63
+ LAMA = 0.100
64
+ FAGA = 0.050
65
+
66
+ def compute
67
+ alpha = [fast_limit / delta_phase, slow_limit].max
68
+
69
+ p0.mama = (alpha * p0.input) + ((1.0 - alpha) * p1.mama)
70
+ p0.fama = (FAMA * alpha * p0.mama) + ((1.0 - (FAMA * alpha)) * p1.fama)
71
+ p0.gama = (GAMA * alpha * p0.mama) + ((1.0 - (GAMA * alpha)) * p1.gama)
72
+ p0.dama = (DAMA * alpha * p0.mama) + ((1.0 - (DAMA * alpha)) * p1.dama)
73
+ p0.lama = (LAMA * alpha * p0.mama) + ((1.0 - (LAMA * alpha)) * p1.lama)
74
+ p0.faga = (FAGA * alpha * p0.fama) + ((1.0 - (FAGA * alpha)) * p1.faga)
75
+
76
+ compute_oscillator
77
+ end
78
+
79
+ def compute_oscillator
80
+ p0.osc = p0.mama - p0.fama
81
+ p0.crossed = :up if p0.osc >= 0 && p1.osc < 0
82
+ p0.crossed = :down if p0.osc <= 0 && p1.osc > 0
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,107 @@
1
+ module Quant
2
+ class Indicators
3
+ class PivotPoint < IndicatorPoint
4
+ attribute :high_price
5
+ attribute :avg_high, default: :high_price
6
+ attribute :highest, default: :input
7
+
8
+ attribute :low_price
9
+ attribute :avg_low, default: :low_price
10
+ attribute :lowest, default: :input
11
+
12
+ attribute :range, default: 0.0
13
+ attribute :avg_range, default: 0.0
14
+ attribute :std_dev, default: 0.0
15
+
16
+ def bands
17
+ @bands ||= { 0 => input }
18
+ end
19
+
20
+ def [](band)
21
+ bands[band]
22
+ end
23
+
24
+ def []=(band, value)
25
+ bands[band] = value
26
+ end
27
+
28
+ def key?(band)
29
+ bands.key?(band)
30
+ end
31
+
32
+ def midpoint
33
+ bands[0]
34
+ end
35
+ alias :h0 :midpoint
36
+ alias :l0 :midpoint
37
+
38
+ def midpoint=(value)
39
+ bands[0] = value
40
+ end
41
+ alias :h0= :midpoint=
42
+ alias :l0= :midpoint=
43
+
44
+ (1..8).each do |band|
45
+ define_method("h#{band}") { bands[band] }
46
+ define_method("h#{band}=") { |value| bands[band] = value }
47
+
48
+ define_method("l#{band}") { bands[-band] }
49
+ define_method("l#{band}=") { |value| bands[-band] = value }
50
+ end
51
+ end
52
+
53
+ class Pivot < Indicator
54
+ def points_class
55
+ Quant::Indicators::PivotPoint
56
+ end
57
+
58
+ def band?(band)
59
+ p0.key?(band)
60
+ end
61
+
62
+ def period
63
+ dc_period
64
+ end
65
+
66
+ def averaging_period
67
+ min_period
68
+ end
69
+
70
+ def period_midpoints
71
+ period_points(period).map(&:midpoint)
72
+ end
73
+
74
+ def compute
75
+ compute_extents
76
+ compute_value
77
+ compute_midpoint
78
+ compute_bands
79
+ end
80
+
81
+ def compute_midpoint
82
+ p0.midpoint = p0.input
83
+ end
84
+
85
+ def compute_value
86
+ # No-op -- override in subclasses
87
+ end
88
+
89
+ def compute_bands
90
+ # No-op -- override in subclasses
91
+ end
92
+
93
+ def compute_extents
94
+ period_midpoints.tap do |midpoints|
95
+ p0.high_price = t0.high_price
96
+ p0.low_price = t0.low_price
97
+ p0.highest = midpoints.max
98
+ p0.lowest = midpoints.min
99
+ p0.range = p0.high_price - p0.low_price
100
+ p0.avg_low = super_smoother(:low_price, previous: :avg_low, period: averaging_period)
101
+ p0.avg_high = super_smoother(:high_price, previous: :avg_high, period: averaging_period)
102
+ p0.avg_range = super_smoother(:range, previous: :avg_range, period: averaging_period)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,41 @@
1
+ module Quant
2
+ class Indicators
3
+ class Pivots
4
+ class Atr < Pivot
5
+ depends_on Indicators::Atr
6
+
7
+ def atr_point
8
+ series.indicators[source].atr.points[t0]
9
+ end
10
+
11
+ def scale
12
+ 5.0
13
+ end
14
+
15
+ def atr_value
16
+ atr_point.slow * scale
17
+ end
18
+
19
+ def compute_midpoint
20
+ p0.midpoint = two_pole_super_smooth :input, previous: :midpoint, period: averaging_period
21
+ end
22
+
23
+ def compute_bands
24
+ p0.h6 = p0.midpoint + 1.000 * atr_value
25
+ p0.h5 = p0.midpoint + 0.786 * atr_value
26
+ p0.h4 = p0.midpoint + 0.618 * atr_value
27
+ p0.h3 = p0.midpoint + 0.500 * atr_value
28
+ p0.h2 = p0.midpoint + 0.382 * atr_value
29
+ p0.h1 = p0.midpoint + 0.236 * atr_value
30
+
31
+ p0.l1 = p0.midpoint - 0.236 * atr_value
32
+ p0.l2 = p0.midpoint - 0.382 * atr_value
33
+ p0.l3 = p0.midpoint - 0.500 * atr_value
34
+ p0.l4 = p0.midpoint - 0.618 * atr_value
35
+ p0.l5 = p0.midpoint - 0.786 * atr_value
36
+ p0.l6 = p0.midpoint - 1.000 * atr_value
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ class Indicators
5
+ class Pivots
6
+ class Bollinger < Pivot
7
+ using Quant
8
+
9
+ def compute_midpoint
10
+ values = period_points(half_period).map(&:input)
11
+ alpha = bars_to_alpha(half_period)
12
+
13
+ p0.midpoint = alpha * values.mean + (1 - alpha) * p1.midpoint
14
+ p0.std_dev = values.standard_deviation(p0.midpoint)
15
+ end
16
+
17
+ def compute_bands
18
+ p0.h1 = p0.midpoint + p0.std_dev * 1.0
19
+ p0.l1 = p0.midpoint - p0.std_dev * 1.0
20
+
21
+ p0.h2 = p0.midpoint + p0.std_dev * 1.5
22
+ p0.l2 = p0.midpoint - p0.std_dev * 1.5
23
+
24
+ p0.h3 = p0.midpoint + p0.std_dev * 1.75
25
+ p0.l3 = p0.midpoint - p0.std_dev * 1.75
26
+
27
+ p0.h4 = p0.midpoint + p0.std_dev * 2.0
28
+ p0.l4 = p0.midpoint - p0.std_dev * 2.0
29
+
30
+ p0.h5 = p0.midpoint + p0.std_dev * 2.25
31
+ p0.l5 = p0.midpoint - p0.std_dev * 2.25
32
+
33
+ p0.h6 = p0.midpoint + p0.std_dev * 2.5
34
+ p0.l6 = p0.midpoint - p0.std_dev * 2.5
35
+
36
+ p0.h7 = p0.midpoint + p0.std_dev * 2.75
37
+ p0.l7 = p0.midpoint - p0.std_dev * 2.75
38
+
39
+ p0.h8 = p0.midpoint + p0.std_dev * 3.0
40
+ p0.l8 = p0.midpoint - p0.std_dev * 3.0
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ class Indicators
5
+ class Pivots
6
+ # Camarilla pivot point calculations are rather straightforward. We need to
7
+ # input the previous day’s open, high, low and close. The formulas for each
8
+ # resistance and support level are:
9
+ #
10
+ # R4 = Close + (High – Low) * 1.1/2
11
+ # R3 = Close + (High – Low) * 1.1/4
12
+ # R2 = Close + (High – Low) * 1.1/6
13
+ # R1 = Close + (High – Low) * 1.1/12
14
+ # S1 = Close – (High – Low) * 1.1/12
15
+ # S2 = Close – (High – Low) * 1.1/6
16
+ # S3 = Close – (High – Low) * 1.1/4
17
+ # S4 = Close – (High – Low) * 1.1/2
18
+ #
19
+ # The calculation for further resistance and support levels varies from this
20
+ # norm. These levels can come into play during strong trend moves, so it’s
21
+ # important to understand how to identify them. For example, R5, R6, S5 and S6
22
+ # are calculated as follows:
23
+ #
24
+ # R5 = R4 + 1.168 * (R4 – R3)
25
+ # R6 = (High/Low) * Close
26
+ #
27
+ # S5 = S4 – 1.168 * (S3 – S4)
28
+ # S6 = Close – (R6 – Close)
29
+ class Camarilla < Pivot
30
+ def multiplier
31
+ 1.1
32
+ end
33
+
34
+ def compute_midpoint
35
+ p0.midpoint = t0.close_price
36
+ end
37
+
38
+ def compute_bands
39
+ mp_plus_range = p0.midpoint + p0.range
40
+ mp_minus_range = p0.midpoint - p0.range
41
+
42
+ p0.h4 = mp_plus_range * (1.1 / 2.0)
43
+ p0.h3 = mp_plus_range * (1.1 / 4.0)
44
+ p0.h2 = mp_plus_range * (1.1 / 6.0)
45
+ p0.h1 = mp_plus_range * (1.1 / 12.0)
46
+
47
+ p0.l1 = mp_minus_range * (1.1 / 12.0)
48
+ p0.l2 = mp_minus_range * (1.1 / 6.0)
49
+ p0.l3 = mp_minus_range * (1.1 / 4.0)
50
+ p0.l4 = mp_minus_range * (1.1 / 2.0)
51
+
52
+ p0.h5 = p0.h4 + 1.168 * (p0.h4 - p0.h3)
53
+ p0.h6 = p0.midpoint * (p0.high_price / p0.low_price)
54
+
55
+ p0.l5 = p0.l4 - 1.168 * (p0.l3 - p0.l4)
56
+ p0.l6 = p0.midpoint - (p0.h6 - p0.midpoint)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ class Indicators
5
+ class Pivots
6
+ class Classic < Pivot
7
+ def compute_midpoint
8
+ p0.midpoint = super_smoother :input, previous: :midpoint, period: averaging_period
9
+ end
10
+
11
+ def compute_bands
12
+ p0.h1 = p0.midpoint * 2.0 - p0.avg_low
13
+ p0.l1 = p0.midpoint * 2.0 - p0.avg_high
14
+
15
+ p0.h2 = p0.midpoint + p0.avg_range
16
+ p0.l2 = p0.midpoint - p0.avg_range
17
+
18
+ p0.h3 = p0.midpoint + 2.0 * p0.avg_range
19
+ p0.l3 = p0.midpoint - 2.0 * p0.avg_range
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ class Indicators
5
+ class Pivots
6
+ # The value of X in the formula below depends on where the Close of the market is.
7
+ # If Close = Open then X = (H + L + (C * 2))
8
+
9
+ # If Close > Open then X = ((H * 2) + L + C)
10
+
11
+ # If Close < Open then X = (H + (L * 2) + C)
12
+
13
+ # R1 = X / 2 - L
14
+ # PP = X / 4 (this is not an official DeMark number but merely a reference point based on the calculation of X)
15
+ # S1 = X / 2 - H
16
+ class Demark < Pivot
17
+ def averaging_period
18
+ min_period / 2
19
+ end
20
+
21
+ def x_factor
22
+ if t0.close_price == t0.open_price
23
+ ((2.0 * t0.close_price) + p0.avg_high + p0.avg_low)
24
+ elsif t0.close_price > t0.open_price
25
+ ((2.0 * p0.avg_high) + p0.avg_low + t0.close_price)
26
+ else
27
+ ((2.0 * p0.avg_low) + p0.avg_high + t0.close_price)
28
+ end
29
+ end
30
+
31
+ def compute_value
32
+ p0.input = x_factor
33
+ end
34
+
35
+ def compute_midpoint
36
+ p0.midpoint = p0.input / 4.0
37
+ p0.midpoint = super_smoother :midpoint, previous: :midpoint, period: averaging_period
38
+ end
39
+
40
+ def compute_bands
41
+ p0.h1 = (p0.input / 2.0) - p0.avg_high
42
+ p0.h1 = super_smoother :h1, previous: :h1, period: averaging_period
43
+
44
+ p0.l1 = (p0.input / 2.0) - p0.avg_low
45
+ p0.l1 = super_smoother :l1, previous: :l1, period: averaging_period
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ class Indicators
5
+ class Pivots
6
+ class Donchian < Pivot
7
+ using Quant
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
19
+
20
+ 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
27
+
28
+ p0.h1 = @st_highs.maximum
29
+ p0.l1 = @st_lows.minimum
30
+
31
+ p0.h2 = @mt_highs.maximum
32
+ p0.l2 = @mt_lows.minimum
33
+
34
+ p0.h3 = @lt_highs.maximum
35
+ p0.l3 = @lt_lows.minimum
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end