quantitative 0.2.1 → 0.3.0
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 +9 -4
- data/Gemfile.lock +9 -1
- data/README.md +5 -0
- data/lib/quant/{indicators/dominant_cycle_indicators.rb → dominant_cycles_source.rb} +19 -8
- data/lib/quant/experimental.rb +8 -1
- data/lib/quant/indicators/adx.rb +83 -0
- data/lib/quant/indicators/atr.rb +79 -0
- data/lib/quant/indicators/cci.rb +63 -0
- data/lib/quant/indicators/decycler.rb +71 -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 +3 -1
- data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +12 -6
- data/lib/quant/indicators/dominant_cycles/half_period.rb +2 -0
- data/lib/quant/indicators/dominant_cycles/homodyne.rb +4 -2
- data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +8 -4
- data/lib/quant/indicators/frama.rb +50 -0
- data/lib/quant/indicators/indicator.rb +50 -2
- data/lib/quant/indicators/mama.rb +143 -0
- data/lib/quant/indicators/mesa.rb +86 -0
- data/lib/quant/indicators/pivot.rb +107 -0
- data/lib/quant/indicators/pivots/atr.rb +41 -0
- data/lib/quant/indicators/pivots/bollinger.rb +45 -0
- data/lib/quant/indicators/pivots/camarilla.rb +61 -0
- data/lib/quant/indicators/pivots/classic.rb +24 -0
- data/lib/quant/indicators/pivots/demark.rb +50 -0
- data/lib/quant/indicators/pivots/donchian.rb +40 -0
- data/lib/quant/indicators/pivots/fibbonacci.rb +22 -0
- data/lib/quant/indicators/pivots/guppy.rb +39 -0
- data/lib/quant/indicators/pivots/keltner.rb +43 -0
- data/lib/quant/indicators/pivots/murrey.rb +34 -0
- data/lib/quant/indicators/pivots/traditional.rb +36 -0
- data/lib/quant/indicators/pivots/woodie.rb +59 -0
- data/lib/quant/indicators_source.rb +140 -0
- data/lib/quant/indicators_sources.rb +36 -10
- data/lib/quant/mixins/stochastic.rb +1 -1
- data/lib/quant/mixins/super_smoother.rb +11 -2
- data/lib/quant/pivots_source.rb +28 -0
- data/lib/quant/refinements/array.rb +14 -0
- data/lib/quant/series.rb +8 -19
- data/lib/quant/settings/indicators.rb +11 -0
- data/lib/quant/version.rb +1 -1
- data/possibilities.png +0 -0
- data/quantitative.gemspec +39 -0
- metadata +27 -5
- data/lib/quant/indicators.rb +0 -14
- 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]
|
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
|