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