quantitative 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +5 -0
  4. data/lib/quant/{dominant_cycle_indicators.rb → dominant_cycles_source.rb} +18 -10
  5. data/lib/quant/experimental.rb +8 -1
  6. data/lib/quant/indicators/adx.rb +3 -0
  7. data/lib/quant/indicators/decycler.rb +16 -30
  8. data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +9 -0
  9. data/lib/quant/indicators/frama.rb +5 -8
  10. data/lib/quant/indicators/indicator.rb +45 -2
  11. data/lib/quant/indicators/pivot.rb +107 -0
  12. data/lib/quant/indicators/pivots/atr.rb +41 -0
  13. data/lib/quant/indicators/pivots/bollinger.rb +45 -0
  14. data/lib/quant/indicators/pivots/camarilla.rb +61 -0
  15. data/lib/quant/indicators/pivots/classic.rb +24 -0
  16. data/lib/quant/indicators/pivots/demark.rb +50 -0
  17. data/lib/quant/indicators/pivots/donchian.rb +40 -0
  18. data/lib/quant/indicators/pivots/fibbonacci.rb +22 -0
  19. data/lib/quant/indicators/pivots/guppy.rb +39 -0
  20. data/lib/quant/indicators/pivots/keltner.rb +43 -0
  21. data/lib/quant/indicators/pivots/murrey.rb +34 -0
  22. data/lib/quant/indicators/pivots/traditional.rb +36 -0
  23. data/lib/quant/indicators/pivots/woodie.rb +59 -0
  24. data/lib/quant/indicators_source.rb +140 -0
  25. data/lib/quant/indicators_sources.rb +36 -10
  26. data/lib/quant/pivots_source.rb +28 -0
  27. data/lib/quant/refinements/array.rb +14 -0
  28. data/lib/quant/series.rb +8 -19
  29. data/lib/quant/settings/indicators.rb +11 -0
  30. data/lib/quant/version.rb +1 -1
  31. data/possibilities.png +0 -0
  32. data/quantitative.gemspec +39 -0
  33. metadata +20 -5
  34. data/lib/quant/indicators.rb +0 -18
  35. data/lib/quant/indicators_proxy.rb +0 -68
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b5786b75635cde82e7919fce6b8fcd568724b27fc2810efe6db36a7e6dd09cc
4
- data.tar.gz: 3a6fb86c25bb4f2b1e4994710539bfb528377ef57531e6845a4417c87fc86e29
3
+ metadata.gz: '06693d3511af25d58463fd872f004265889bb0c8d8344dd4fc8436bd4a66f63b'
4
+ data.tar.gz: b1e7db72820532999567608e1d3b864a7e4b1029b363de4c05c1032b177abc5a
5
5
  SHA512:
6
- metadata.gz: 5ae30c5b971d6c7bd41e3c2b764e7d95438931790271133dc0e3a476a371affddfdcf06355ffa933240c0c9952fd8fa5e4212c496c95c80f028de7399d3b129f
7
- data.tar.gz: 9ff8aeaf24800a7208e51f836dd7c7c3621b2e2e3a7f1e8820d89f6fc5cd56dd3c4b762c478d221c6ef7279f62c0df75ae1a19f6a4d34e7ba82e116e6a10a790
6
+ metadata.gz: 6dcc22eaa684535f9452287e1a486d2de3bc3d38b12be207130e62fb0264ea90087e20c9a9693952b8be3030738a3eebd8681813ccf2f0c2838aa9c33c9ad7b1
7
+ data.tar.gz: 05d9470a2c3426c46b6e64113b728672d841640dea56887d1efc9fcd9c7839b561f09cb2da4d6453bd339222bd07f48885bbae443e55646b90b855cd6bec716f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- quantitative (0.2.2)
4
+ quantitative (0.3.0)
5
5
  oj (~> 3.10)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -22,6 +22,11 @@ The information provided by this library should not be construed as an endorseme
22
22
 
23
23
  Past performance is not necessarily indicative of future results. By using this library, you agree that the developers and contributors will not be liable for any losses or damages arising from your use of the library. Use at your own risk.
24
24
 
25
+ ## Possibilties
26
+ While charting and automated trading systems are not part of this library, Quantitative goes a long ways towards giving you powerful tools to build such a system. It is extracted from an automated trading system built in Ruby on Rails and has been used to generate considerable net profits over the years.
27
+
28
+ ![Possibilities](https://github.com/mwlang/quantitative/blob/main/possibilities.png)
29
+
25
30
  ## Installation
26
31
 
27
32
  Install the gem and add to the application's Gemfile by executing:
@@ -1,6 +1,4 @@
1
1
 
2
- require_relative 'indicators_proxy'
3
-
4
2
  module Quant
5
3
  # Dominant Cycles measure the primary cycle within a given range. By default, the library
6
4
  # is wired to look for cycles between 10 and 48 bars. These values can be adjusted by setting
@@ -16,7 +14,11 @@ module Quant
16
14
  # The purpose of these indicators is to compute the dominant cycle and underpin the various
17
15
  # indicators that would otherwise be setting an arbitrary lookback period. This makes the
18
16
  # indicators adaptive and auto-tuning to the market dynamics. Or so the theory goes!
19
- class DominantCycleIndicators < IndicatorsProxy
17
+ class DominantCyclesSource
18
+ def initialize(indicator_source:)
19
+ @indicator_source = indicator_source
20
+ end
21
+
20
22
  # Auto-Correlation Reversals is a method of computing the dominant cycle
21
23
  # by correlating the data stream with itself delayed by a lag.
22
24
  def acr; indicator(Indicators::DominantCycles::Acr) end
@@ -28,11 +30,6 @@ module Quant
28
30
  # and this is the `period` of the dominant cycle.
29
31
  def band_pass; indicator(Indicators::DominantCycles::BandPass) end
30
32
 
31
- # Homodyne means the signal is multiplied by itself. More precisely,
32
- # we want to multiply the signal of the current bar with the complex
33
- # value of the signal one bar ago
34
- def homodyne; indicator(Indicators::DominantCycles::Homodyne) end
35
-
36
33
  # The Dual Differentiator algorithm computes the phase angle from the
37
34
  # analytic signal as the arctangent of the ratio of the imaginary
38
35
  # component to the real component. Further, the angular frequency
@@ -40,13 +37,24 @@ module Quant
40
37
  # derive the cycle period.
41
38
  def differential; indicator(Indicators::DominantCycles::Differential) end
42
39
 
40
+ # Static, arbitrarily set period.
41
+ def half_period; indicator(Indicators::DominantCycles::HalfPeriod) end
42
+
43
+ # Homodyne means the signal is multiplied by itself. More precisely,
44
+ # we want to multiply the signal of the current bar with the complex
45
+ # value of the signal one bar ago
46
+ def homodyne; indicator(Indicators::DominantCycles::Homodyne) end
47
+
43
48
  # The phase accumulation method of computing the dominant cycle measures
44
49
  # the phase at each sample by taking the arctangent of the ratio of the
45
50
  # quadrature component to the in-phase component. The phase is then
46
51
  # accumulated and the period is derived from the phase.
47
52
  def phase_accumulator; indicator(Indicators::DominantCycles::PhaseAccumulator) end
48
53
 
49
- # Static, arbitrarily set period.
50
- def half_period; indicator(Indicators::DominantCycles::HalfPeriod) end
54
+ private
55
+
56
+ def indicator(indicator_class)
57
+ @indicator_source[indicator_class]
58
+ end
51
59
  end
52
60
  end
@@ -1,14 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
+ # {Quant::Experimental} is an alert emitter for experimental code paths.
5
+ # It will typically be used for new indicators or computations that are not yet
6
+ # fully vetted or tested.
4
7
  module Experimental
5
8
  def self.tracker
6
9
  @tracker ||= {}
7
10
  end
11
+
12
+ def self.rspec_defined?
13
+ defined?("RSpec")
14
+ end
8
15
  end
9
16
 
10
17
  def self.experimental(message)
11
- return if defined?(RSpec)
18
+ return if Experimental.rspec_defined?
12
19
  return if Experimental.tracker[caller.first]
13
20
 
14
21
  Experimental.tracker[caller.first] = message
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "indicator_point"
4
4
  require_relative "indicator"
5
+ require_relative "atr"
5
6
 
6
7
  module Quant
7
8
  class Indicators
@@ -24,6 +25,8 @@ module Quant
24
25
  end
25
26
 
26
27
  class Adx < Indicator
28
+ depends_on Indicators::Atr
29
+
27
30
  def alpha
28
31
  bars_to_alpha(dc_period)
29
32
  end
@@ -14,25 +14,13 @@ module Quant
14
14
  # output of another high-pass filter having a longer cutoff period.
15
15
  # 5. A decycler oscillator shows transitions between uptrends and down-trends at the zero crossings.
16
16
  class DecyclerPoint < IndicatorPoint
17
- attr_accessor :decycle, :hp1, :hp2, :osc, :peak, :agc, :ift
18
-
19
- def to_h
20
- {
21
- "dc" => decycle,
22
- "hp1" => hp1,
23
- "hp2" => hp2,
24
- "osc" => osc,
25
- }
26
- end
27
-
28
- def initialize_data_points(indicator:)
29
- @decycle = oc2
30
- @hp1 = 0.0
31
- @hp2 = 0.0
32
- @osc = 0.0
33
- @peak = 0.0
34
- @agc = 0.0
35
- end
17
+ attribute :decycle, default: :input
18
+ attribute :hp1, default: 0.0
19
+ attribute :hp2, default: 0.0
20
+ attribute :osc, default: 0.0
21
+ attribute :peak, default: 0.0
22
+ attribute :agc, default: 0.0
23
+ attribute :ift, default: 0.0
36
24
  end
37
25
 
38
26
  class Decycler < Indicator
@@ -40,13 +28,9 @@ module Quant
40
28
  dc_period
41
29
  end
42
30
 
43
- def min_period
44
- settings.min_period
45
- end
46
-
47
31
  def compute_decycler
48
32
  alpha = period_to_alpha(max_period)
49
- p0.decycle = (alpha / 2) * (p0.oc2 + p1.oc2) + (1.0 - alpha) * p1.decycle
33
+ p0.decycle = (alpha / 2) * (p0.input + p1.input) + (1.0 - alpha) * p1.decycle
50
34
  end
51
35
 
52
36
  # alpha1 = (Cosine(.707*360 / HPPeriod1) + Sine (.707*360 / HPPeriod1) - 1) / Cosine(.707*360 / HPPeriod1);
@@ -56,7 +40,7 @@ module Quant
56
40
  c = Math.cos(0.707 * radians / period)
57
41
  s = Math.sin(0.707 * radians / period)
58
42
  alpha = (c + s - 1) / c
59
- (1 - alpha / 2)**2 * (p0.oc2 - 2 * p1.oc2 + p2.oc2) + 2 * (1 - alpha) * p1.send(hp) - (1 - alpha) * (1 - alpha) * p2.send(hp)
43
+ (1 - alpha / 2)**2 * (p0.input - 2 * p1.input + p2.input) + 2 * (1 - alpha) * p1.send(hp) - (1 - alpha) * (1 - alpha) * p2.send(hp)
60
44
  end
61
45
 
62
46
  def compute_oscillator
@@ -65,20 +49,22 @@ module Quant
65
49
  p0.osc = p0.hp2 - p0.hp1
66
50
  end
67
51
 
68
- def compute_agc
52
+ # AGC is constrained to -1.0 to 1.0
53
+ # The peak decays at a rate of 0.991 per bar
54
+ def compute_automatic_gain_control
69
55
  p0.peak = [p0.osc.abs, 0.991 * p1.peak].max
70
56
  p0.agc = p0.peak.zero? ? p0.osc : p0.osc / p0.peak
71
57
  end
72
58
 
73
- def compute_ift
74
- p0.ift = ift(p0.agc, 5.0)
59
+ def compute_inverse_fisher_transform
60
+ p0.ift = inverse_fisher_transform(p0.agc, scale_factor: 5.0)
75
61
  end
76
62
 
77
63
  def compute
78
64
  compute_decycler
79
65
  compute_oscillator
80
- compute_agc
81
- compute_ift
66
+ compute_automatic_gain_control
67
+ compute_inverse_fisher_transform
82
68
  end
83
69
  end
84
70
  end
@@ -44,6 +44,15 @@ module Quant
44
44
  end
45
45
 
46
46
  class DominantCycle < Indicators::Indicator
47
+ def priority
48
+ DOMINANT_CYCLES_PRIORITY
49
+ end
50
+
51
+ # Dominant Cycle Indicators should not themselves have a dominant cycle indicator
52
+ def dominant_cycle_indicator_class
53
+ nil
54
+ end
55
+
47
56
  def points_class
48
57
  Object.const_get "Quant::Indicators::DominantCycles::#{indicator_name}Point"
49
58
  rescue NameError
@@ -4,6 +4,8 @@ module Quant
4
4
  class Indicators
5
5
  class FramaPoint < IndicatorPoint
6
6
  attribute :frama, default: :input
7
+ attribute :dimension, default: 0.0
8
+ attribute :alpha, default: 0.0
7
9
  end
8
10
 
9
11
  # FRAMA (FRactal Adaptive Moving Average). A nonlinear moving average
@@ -23,11 +25,6 @@ module Quant
23
25
  end
24
26
  end
25
27
 
26
- # def max_period
27
- # mp = dc_period
28
- # mp.even? ? mp : mp + 1
29
- # end
30
-
31
28
  def half_period
32
29
  max_period / 2
33
30
  end
@@ -44,9 +41,9 @@ module Quant
44
41
  ppn1 = pp.last(half_period)
45
42
  n1 = (ppn1.maximum - ppn1.minimum) / half_period
46
43
 
47
- dimension = (Math.log(n1 + n2) - Math.log(n3)) / Math.log(2)
48
- alpha = Math.exp(-4.6 * (dimension - 1.0)).clamp(0.01, 1.0)
49
- p0.frama = (alpha * p0.input) + ((1 - alpha) * p1.frama)
44
+ p0.dimension = (Math.log(n1 + n2) - Math.log(n3)) / Math.log(2)
45
+ p0.alpha = Math.exp(-4.6 * (p0.dimension - 1.0)).clamp(0.01, 1.0)
46
+ p0.frama = (p0.alpha * p0.input) + ((1 - p0.alpha) * p1.frama)
50
47
  end
51
48
  end
52
49
  end
@@ -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
@@ -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