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