quantitative 0.2.2 → 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.lock +1 -1
- data/README.md +5 -0
- data/lib/quant/{dominant_cycle_indicators.rb → dominant_cycles_source.rb} +18 -10
- data/lib/quant/experimental.rb +8 -1
- data/lib/quant/indicators/adx.rb +3 -0
- data/lib/quant/indicators/decycler.rb +16 -30
- data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +9 -0
- data/lib/quant/indicators/frama.rb +5 -8
- data/lib/quant/indicators/indicator.rb +45 -2
- 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/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 +20 -5
- data/lib/quant/indicators.rb +0 -18
- data/lib/quant/indicators_proxy.rb +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '06693d3511af25d58463fd872f004265889bb0c8d8344dd4fc8436bd4a66f63b'
|
4
|
+
data.tar.gz: b1e7db72820532999567608e1d3b864a7e4b1029b363de4c05c1032b177abc5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6dcc22eaa684535f9452287e1a486d2de3bc3d38b12be207130e62fb0264ea90087e20c9a9693952b8be3030738a3eebd8681813ccf2f0c2838aa9c33c9ad7b1
|
7
|
+
data.tar.gz: 05d9470a2c3426c46b6e64113b728672d841640dea56887d1efc9fcd9c7839b561f09cb2da4d6453bd339222bd07f48885bbae443e55646b90b855cd6bec716f
|
data/Gemfile.lock
CHANGED
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
|
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
|
-
|
50
|
-
|
54
|
+
private
|
55
|
+
|
56
|
+
def indicator(indicator_class)
|
57
|
+
@indicator_source[indicator_class]
|
58
|
+
end
|
51
59
|
end
|
52
60
|
end
|
data/lib/quant/experimental.rb
CHANGED
@@ -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
|
18
|
+
return if Experimental.rspec_defined?
|
12
19
|
return if Experimental.tracker[caller.first]
|
13
20
|
|
14
21
|
Experimental.tracker[caller.first] = message
|
data/lib/quant/indicators/adx.rb
CHANGED
@@ -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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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.
|
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.
|
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
|
-
|
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
|
74
|
-
p0.ift =
|
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
|
-
|
81
|
-
|
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]
|
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
|