quantitative 0.2.2 → 0.3.1
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.lock +3 -1
- data/README.md +5 -0
- data/lib/quant/asset.rb +0 -2
- data/lib/quant/{dominant_cycle_indicators.rb → dominant_cycles_source.rb} +18 -10
- data/lib/quant/experimental.rb +11 -2
- data/lib/quant/indicators/adx.rb +3 -4
- data/lib/quant/indicators/atr.rb +1 -4
- data/lib/quant/indicators/cci.rb +1 -1
- data/lib/quant/indicators/decycler.rb +17 -31
- data/lib/quant/indicators/dominant_cycles/acr.rb +3 -6
- data/lib/quant/indicators/dominant_cycles/band_pass.rb +2 -4
- data/lib/quant/indicators/dominant_cycles/differential.rb +2 -2
- data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +11 -4
- data/lib/quant/indicators/dominant_cycles/half_period.rb +2 -4
- data/lib/quant/indicators/dominant_cycles/homodyne.rb +2 -5
- data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +2 -4
- data/lib/quant/indicators/frama.rb +6 -9
- data/lib/quant/indicators/indicator.rb +46 -3
- data/lib/quant/indicators/indicator_point.rb +1 -1
- data/lib/quant/indicators/mama.rb +1 -1
- data/lib/quant/indicators/mesa.rb +1 -1
- data/lib/quant/indicators/ping.rb +1 -1
- 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 +33 -0
- data/lib/quant/indicators/pivots/traditional.rb +36 -0
- data/lib/quant/indicators/pivots/woodie.rb +59 -0
- data/lib/quant/indicators.rb +1 -13
- data/lib/quant/indicators_source.rb +139 -0
- data/lib/quant/indicators_sources.rb +36 -10
- data/lib/quant/mixins/filters.rb +0 -3
- data/lib/quant/mixins/moving_averages.rb +0 -3
- 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/ticks/ohlc.rb +0 -2
- data/lib/quant/ticks/spot.rb +0 -2
- data/lib/quant/version.rb +1 -1
- data/lib/quantitative.rb +21 -4
- data/possibilities.png +0 -0
- metadata +34 -5
- data/lib/quant/indicators_proxy.rb +0 -68
@@ -0,0 +1,41 @@
|
|
1
|
+
module Quant
|
2
|
+
module Indicators
|
3
|
+
module 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
|
+
module Indicators
|
5
|
+
module 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
|
+
module Indicators
|
5
|
+
module 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
|
+
module Indicators
|
5
|
+
module 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
|
+
module Indicators
|
5
|
+
module 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
|
+
module Indicators
|
5
|
+
module 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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Quant
|
2
|
+
module Indicators
|
3
|
+
module Pivots
|
4
|
+
class Fibbonacci < Pivot
|
5
|
+
def averaging_period
|
6
|
+
half_period
|
7
|
+
end
|
8
|
+
|
9
|
+
def fibbonacci_series
|
10
|
+
[0.146, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.146]
|
11
|
+
end
|
12
|
+
|
13
|
+
def compute_bands
|
14
|
+
fibbonacci_series.each_with_index do |ratio, index|
|
15
|
+
p0[index + 1] = p0.midpoint + ratio * p0.avg_range
|
16
|
+
p0[-index - 1] = p0.midpoint - ratio * p0.avg_range
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Quant
|
2
|
+
module Indicators
|
3
|
+
module Pivots
|
4
|
+
class Guppy < Pivot
|
5
|
+
def guppy_ema(period, band)
|
6
|
+
return p0.input unless p1[band]
|
7
|
+
|
8
|
+
alpha = bars_to_alpha(period)
|
9
|
+
alpha * p0.input + (1 - alpha) * p1[band]
|
10
|
+
end
|
11
|
+
|
12
|
+
def compute_midpoint
|
13
|
+
p0.midpoint = guppy_ema(3, 0)
|
14
|
+
end
|
15
|
+
|
16
|
+
# The short-term MAs are typically set at 3, 5, 8, 10, 12, and 15 periods. The
|
17
|
+
# longer-term MAs are typically set at 30, 35, 40, 45, 50, and 60.
|
18
|
+
def compute
|
19
|
+
p0[1] = guppy_ema(5, 1)
|
20
|
+
p0[2] = guppy_ema(8, 2)
|
21
|
+
p0[3] = guppy_ema(10, 3)
|
22
|
+
p0[4] = guppy_ema(12, 4)
|
23
|
+
p0[5] = guppy_ema(15, 5)
|
24
|
+
p0[6] = guppy_ema(20, 6)
|
25
|
+
p0[7] = guppy_ema(25, 7)
|
26
|
+
|
27
|
+
p0[-1] = guppy_ema(30, -1)
|
28
|
+
p0[-2] = guppy_ema(35, -2)
|
29
|
+
p0[-3] = guppy_ema(40, -3)
|
30
|
+
p0[-4] = guppy_ema(45, -4)
|
31
|
+
p0[-5] = guppy_ema(50, -5)
|
32
|
+
p0[-6] = guppy_ema(60, -6)
|
33
|
+
p0[-7] = guppy_ema(120, -7)
|
34
|
+
p0[-8] = guppy_ema(200, -8)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Quant
|
2
|
+
module Indicators
|
3
|
+
module Pivots
|
4
|
+
class Keltner < 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 alpha
|
16
|
+
bars_to_alpha(min_period)
|
17
|
+
end
|
18
|
+
|
19
|
+
def compute_midpoint
|
20
|
+
p0.midpoint = alpha * p0.input + (1 - alpha) * p1.midpoint
|
21
|
+
end
|
22
|
+
|
23
|
+
def compute_bands
|
24
|
+
atr_value = atr_point.slow * scale
|
25
|
+
|
26
|
+
p0.h6 = p0.midpoint + 1.000 * atr_value
|
27
|
+
p0.h5 = p0.midpoint + 0.786 * atr_value
|
28
|
+
p0.h4 = p0.midpoint + 0.618 * atr_value
|
29
|
+
p0.h3 = p0.midpoint + 0.500 * atr_value
|
30
|
+
p0.h2 = p0.midpoint + 0.382 * atr_value
|
31
|
+
p0.h1 = p0.midpoint + 0.236 * atr_value
|
32
|
+
|
33
|
+
p0.l1 = p0.midpoint - 0.236 * atr_value
|
34
|
+
p0.l2 = p0.midpoint - 0.382 * atr_value
|
35
|
+
p0.l3 = p0.midpoint - 0.500 * atr_value
|
36
|
+
p0.l4 = p0.midpoint - 0.618 * atr_value
|
37
|
+
p0.l5 = p0.midpoint - 0.786 * atr_value
|
38
|
+
p0.l6 = p0.midpoint - 1.000 * atr_value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module Quant
|
3
|
+
module Indicators
|
4
|
+
module Pivots
|
5
|
+
class Murrey < Pivot
|
6
|
+
def multiplier
|
7
|
+
0.125
|
8
|
+
end
|
9
|
+
|
10
|
+
def compute_midpoint
|
11
|
+
p0.input = (p0.highest - p0.lowest) * multiplier
|
12
|
+
p0.midpoint = p0.lowest + (p0.input * 4.0)
|
13
|
+
end
|
14
|
+
|
15
|
+
def compute_bands
|
16
|
+
p0.h6 = p0.midpoint + p0.input * 6.0
|
17
|
+
p0.h5 = p0.midpoint + p0.input * 5.0
|
18
|
+
p0.h4 = p0.midpoint + p0.input * 4.0
|
19
|
+
p0.h3 = p0.midpoint + p0.input * 3.0
|
20
|
+
p0.h2 = p0.midpoint + p0.input * 2.0
|
21
|
+
p0.h1 = p0.midpoint + p0.input * 1.0
|
22
|
+
|
23
|
+
p0.l1 = p0.midpoint - p0.input * 1.0
|
24
|
+
p0.l2 = p0.midpoint - p0.input * 2.0
|
25
|
+
p0.l3 = p0.midpoint - p0.input * 3.0
|
26
|
+
p0.l4 = p0.midpoint - p0.input * 4.0
|
27
|
+
p0.l5 = p0.midpoint - p0.input * 5.0
|
28
|
+
p0.l6 = p0.midpoint - p0.input * 6.0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Quant
|
2
|
+
module Indicators
|
3
|
+
module Pivots
|
4
|
+
class Traditional < Pivot
|
5
|
+
def multiplier
|
6
|
+
2.0
|
7
|
+
end
|
8
|
+
|
9
|
+
# Pivot Point (PP) = (High + Low + Close) / 3
|
10
|
+
def compute_midpoint
|
11
|
+
p0.midpoint = p0.input
|
12
|
+
end
|
13
|
+
|
14
|
+
def compute_bands
|
15
|
+
# Third Resistance (R3) = High + 2 × (PP - Low)
|
16
|
+
p0.h3 = p0.high_price + (multiplier * (p0.midpoint - p0.low_price))
|
17
|
+
|
18
|
+
# Second Resistance (R2) = PP + (High - Low)
|
19
|
+
p0.h2 = p0.midpoint + p0.range
|
20
|
+
|
21
|
+
# First Resistance (R1) = (2 × PP) - Low
|
22
|
+
p0.h1 = p0.midpoint * multiplier - p0.low_price
|
23
|
+
|
24
|
+
# First Support (S1) = (2 × PP) - High
|
25
|
+
p0.l1 = p0.midpoint * multiplier - p0.high_price
|
26
|
+
|
27
|
+
# Second Support (S2) = PP - (High - Low)
|
28
|
+
p0.l2 = p0.midpoint - p0.range
|
29
|
+
|
30
|
+
# Third Support (S3) = Low - 2 × (High - PP)
|
31
|
+
p0.l3 = p0.low_price - (multiplier * (p0.high_price - p0.midpoint))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
module Indicators
|
5
|
+
module Pivots
|
6
|
+
# One of the key differences in calculating Woodie's Pivot Point to other pivot
|
7
|
+
# points is that the current session's open price is used in the PP formula with
|
8
|
+
# the previous session's high and low. At the time-of-day that we calculate the
|
9
|
+
# pivot points on this site in our Daily Notes we do not have the opening price
|
10
|
+
# so we use the Classic formula for the Pivot Point and vary the R3 and R4
|
11
|
+
# formula as per Woodie's formulas.
|
12
|
+
|
13
|
+
# Formulas:
|
14
|
+
# R4 = R3 + RANGE
|
15
|
+
# R3 = H + 2 * (PP - L) (same as: R1 + RANGE)
|
16
|
+
# R2 = PP + RANGE
|
17
|
+
# R1 = (2 * PP) - LOW
|
18
|
+
|
19
|
+
# PP = (HIGH + LOW + (TODAY'S OPEN * 2)) / 4
|
20
|
+
# S1 = (2 * PP) - HIGH
|
21
|
+
# S2 = PP - RANGE
|
22
|
+
# S3 = L - 2 * (H - PP) (same as: S1 - RANGE)
|
23
|
+
# S4 = S3 - RANGE
|
24
|
+
class Woodie < Pivot
|
25
|
+
def compute_value
|
26
|
+
p0.input = (t1.high_price + t1.low_price + 2.0 * t0.open_price) / 4.0
|
27
|
+
end
|
28
|
+
|
29
|
+
def compute_bands
|
30
|
+
Quant.experimental("Woodie appears erratic, is unproven and may be incorrect.")
|
31
|
+
|
32
|
+
# R1 = (2 * PP) - LOW
|
33
|
+
p0.h1 = 2.0 * p0.midpoint - t1.low_price
|
34
|
+
|
35
|
+
# R2 = PP + RANGE
|
36
|
+
p0.h2 = p0.midpoint + p0.range
|
37
|
+
|
38
|
+
# R3 = H + 2 * (PP - L) (same as: R1 + RANGE)
|
39
|
+
p0.h3 = t1.high_price + 2.0 * (p0.midpoint - t1.low_price)
|
40
|
+
|
41
|
+
# R4 = R3 + RANGE
|
42
|
+
p0.h4 = p0.h3 + p0.range
|
43
|
+
|
44
|
+
# S1 = (2 * PP) - HIGH
|
45
|
+
p0.l1 = 2.0 * p0.midpoint - t1.high_price
|
46
|
+
|
47
|
+
# S2 = PP - RANGE
|
48
|
+
p0.l2 = p0.midpoint - p0.range
|
49
|
+
|
50
|
+
# S3 = L - 2 * (H - PP) (same as: S1 - RANGE)
|
51
|
+
p0.l3 = t1.low_price - 2.0 * (t1.high_price - p0.midpoint)
|
52
|
+
|
53
|
+
# S4 = S3 - RANGE
|
54
|
+
p0.l4 = p0.l3 - p0.range
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/quant/indicators.rb
CHANGED
@@ -1,18 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "indicators_proxy"
|
4
3
|
module Quant
|
5
|
-
|
6
|
-
# used outside those shipped with the library.
|
7
|
-
class Indicators < IndicatorsProxy
|
8
|
-
def ping; indicator(Indicators::Ping) end
|
9
|
-
def adx; indicator(Indicators::Adx) end
|
10
|
-
def atr; indicator(Indicators::Atr) end
|
11
|
-
def mesa; indicator(Indicators::Mesa) end
|
12
|
-
def mama; indicator(Indicators::MAMA) end
|
13
|
-
|
14
|
-
def dominant_cycles
|
15
|
-
@dominant_cycles ||= Quant::DominantCycleIndicators.new(series:, source:)
|
16
|
-
end
|
4
|
+
module Indicators
|
17
5
|
end
|
18
6
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quant
|
4
|
+
# {Quant::IndicatorSource} holds a collection of {Quant::Indicators::Indicator} for a given input source.
|
5
|
+
# This class ensures dominant cycle computations come before other indicators that depend on them.
|
6
|
+
#
|
7
|
+
# The {Quant::IndicatorSource} class is responsible for lazily loading indicators
|
8
|
+
# so that not all indicators are always engaged and computing their values.
|
9
|
+
# If the indicator is never accessed, it's never computed, saving valuable
|
10
|
+
# processing CPU cycles.
|
11
|
+
#
|
12
|
+
# Indicators are generally built around the concept of a source input value and
|
13
|
+
# that source is designated by the source parameter when instantiating the
|
14
|
+
# {Quant::IndicatorSource} class.
|
15
|
+
#
|
16
|
+
# By design, the {Quant::Indicators::Indicator} class holds the {Quant::Ticks::Tick} instance
|
17
|
+
# alongside the indicator's computed values for that tick.
|
18
|
+
class IndicatorsSource
|
19
|
+
attr_reader :series, :source, :dominant_cycles, :pivots
|
20
|
+
|
21
|
+
def initialize(series:, source:)
|
22
|
+
@series = series
|
23
|
+
@source = source
|
24
|
+
@indicators = {}
|
25
|
+
@ordered_indicators = []
|
26
|
+
@dominant_cycles = DominantCyclesSource.new(indicator_source: self)
|
27
|
+
@pivots = PivotsSource.new(indicator_source: self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def [](indicator_class)
|
31
|
+
indicator(indicator_class)
|
32
|
+
end
|
33
|
+
|
34
|
+
def <<(tick)
|
35
|
+
@ordered_indicators.each { |indicator| indicator << tick }
|
36
|
+
end
|
37
|
+
|
38
|
+
def adx; indicator(Indicators::Adx) end
|
39
|
+
def atr; indicator(Indicators::Atr) end
|
40
|
+
def cci; indicator(Indicators::Cci) end
|
41
|
+
def decycler; indicator(Indicators::Decycler) end
|
42
|
+
def frama; indicator(Indicators::Frama) end
|
43
|
+
def mama; indicator(Indicators::Mama) end
|
44
|
+
def mesa; indicator(Indicators::Mesa) end
|
45
|
+
def ping; indicator(Indicators::Ping) end
|
46
|
+
|
47
|
+
# Attaches a given Indicator class and defines the method for
|
48
|
+
# accessing it using the given name. Indicators take care of
|
49
|
+
# computing their values when first attached to a populated
|
50
|
+
# series.
|
51
|
+
#
|
52
|
+
# The indicators shipped with the library are all wired into the framework, thus
|
53
|
+
# this method should be used for custom indicators not shipped with the library.
|
54
|
+
#
|
55
|
+
# @param name [Symbol] The name of the method to define for accessing the indicator.
|
56
|
+
# @param indicator_class [Class] The class of the indicator to attach.
|
57
|
+
# @example
|
58
|
+
# series.indicators.oc2.attach(name: :foo, indicator_class: Indicators::Foo)
|
59
|
+
def attach(name:, indicator_class:)
|
60
|
+
define_singleton_method(name) { indicator(indicator_class) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def dominant_cycle
|
64
|
+
indicator(dominant_cycle_indicator_class)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
attr_reader :indicators, :ordered_indicators
|
70
|
+
|
71
|
+
def dominant_cycle_indicator_class
|
72
|
+
Quant.config.indicators.dominant_cycle_indicator_class
|
73
|
+
end
|
74
|
+
|
75
|
+
# Instantiates the indicator class and stores it in the indicators hash. Once
|
76
|
+
# prepared, the indicator becomes active and all ticks pushed into the series
|
77
|
+
# are sent to the indicator for processing.
|
78
|
+
def indicator(indicator_class)
|
79
|
+
indicators[indicator_class] ||= new_indicator(indicator_class)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Instantiates a new indicator and adds it to the collection of indicators.
|
83
|
+
# This method is responsible for adding dependent indicators and the dominant cycle
|
84
|
+
# indicator.
|
85
|
+
def new_indicator(indicator_class)
|
86
|
+
indicator_class.new(series:, source:).tap do |indicator|
|
87
|
+
add_dominant_cycle_indicator(indicator.dominant_cycle_indicator_class, indicator)
|
88
|
+
add_dependent_indicators(indicator_class.dependent_indicator_classes, indicator)
|
89
|
+
add_indicator(indicator_class, indicator)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Adds a new indicator to the collection of indicators. Once added, every
|
94
|
+
# tick added to the series triggers the indicator's compute to fire.
|
95
|
+
# The ordered indicators list is adjusted after adding the new indicator.
|
96
|
+
def add_indicator(indicator_class, new_indicator)
|
97
|
+
return if indicators[indicator_class]
|
98
|
+
|
99
|
+
indicators[indicator_class] = new_indicator
|
100
|
+
@ordered_indicators = (ordered_indicators << new_indicator).sort_by(&:priority)
|
101
|
+
new_indicator
|
102
|
+
end
|
103
|
+
|
104
|
+
# Adds dependent indicators to the indicator collection. This method is reentrant and
|
105
|
+
# will also add depencies of the dependent indicators.
|
106
|
+
# Dependent indicators automatically adjust priority based on the dependency.
|
107
|
+
def add_dependent_indicators(indicator_classes, indicator)
|
108
|
+
return if indicator_classes.empty?
|
109
|
+
|
110
|
+
# Dependent indicators should come after dominant cycle indicators, but before the
|
111
|
+
# indicators that depend on them.
|
112
|
+
dependency_priority = (Quant::Indicators::Indicator::DEPENDENCY_PRIORITY + indicator.priority) / 2
|
113
|
+
|
114
|
+
indicator_classes.each_with_index do |indicator_class, index|
|
115
|
+
next if indicators[indicator_class]
|
116
|
+
|
117
|
+
new_indicator = indicator_class.new(series:, source:)
|
118
|
+
new_indicator.define_singleton_method(:priority) { dependency_priority + index }
|
119
|
+
add_dependent_indicators(indicator_class.dependent_indicator_classes, new_indicator)
|
120
|
+
add_indicator(indicator_class, new_indicator)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Adds the dominant cycle indicator to the collection of indicators. Indicators added
|
125
|
+
# by this method must be a subclass of {Quant::Indicators::DominantCycles::DominantCycle}.
|
126
|
+
def add_dominant_cycle_indicator(dominant_cycle_class, indicator)
|
127
|
+
return if indicator.is_a?(Indicators::DominantCycles::DominantCycle)
|
128
|
+
return unless dominant_cycle_class
|
129
|
+
return if indicators[dominant_cycle_class]
|
130
|
+
|
131
|
+
dominant_cycle = dominant_cycle_class.new(series:, source:)
|
132
|
+
add_indicator(dominant_cycle_class, dominant_cycle)
|
133
|
+
end
|
134
|
+
|
135
|
+
def invalid_source_error(source:)
|
136
|
+
raise InvalidIndicatorSource, "Invalid indicator source: #{source.inspect}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|