quantitative 0.2.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +5 -0
  4. data/lib/quant/asset.rb +0 -2
  5. data/lib/quant/{dominant_cycle_indicators.rb → dominant_cycles_source.rb} +18 -10
  6. data/lib/quant/experimental.rb +11 -2
  7. data/lib/quant/indicators/adx.rb +3 -4
  8. data/lib/quant/indicators/atr.rb +1 -4
  9. data/lib/quant/indicators/cci.rb +1 -1
  10. data/lib/quant/indicators/decycler.rb +17 -31
  11. data/lib/quant/indicators/dominant_cycles/acr.rb +3 -6
  12. data/lib/quant/indicators/dominant_cycles/band_pass.rb +2 -4
  13. data/lib/quant/indicators/dominant_cycles/differential.rb +2 -2
  14. data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +11 -4
  15. data/lib/quant/indicators/dominant_cycles/half_period.rb +2 -4
  16. data/lib/quant/indicators/dominant_cycles/homodyne.rb +2 -5
  17. data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +2 -4
  18. data/lib/quant/indicators/frama.rb +6 -9
  19. data/lib/quant/indicators/indicator.rb +46 -3
  20. data/lib/quant/indicators/indicator_point.rb +1 -1
  21. data/lib/quant/indicators/mama.rb +1 -1
  22. data/lib/quant/indicators/mesa.rb +1 -1
  23. data/lib/quant/indicators/ping.rb +1 -1
  24. data/lib/quant/indicators/pivot.rb +107 -0
  25. data/lib/quant/indicators/pivots/atr.rb +41 -0
  26. data/lib/quant/indicators/pivots/bollinger.rb +45 -0
  27. data/lib/quant/indicators/pivots/camarilla.rb +61 -0
  28. data/lib/quant/indicators/pivots/classic.rb +24 -0
  29. data/lib/quant/indicators/pivots/demark.rb +50 -0
  30. data/lib/quant/indicators/pivots/donchian.rb +40 -0
  31. data/lib/quant/indicators/pivots/fibbonacci.rb +22 -0
  32. data/lib/quant/indicators/pivots/guppy.rb +39 -0
  33. data/lib/quant/indicators/pivots/keltner.rb +43 -0
  34. data/lib/quant/indicators/pivots/murrey.rb +33 -0
  35. data/lib/quant/indicators/pivots/traditional.rb +36 -0
  36. data/lib/quant/indicators/pivots/woodie.rb +59 -0
  37. data/lib/quant/indicators.rb +1 -13
  38. data/lib/quant/indicators_source.rb +139 -0
  39. data/lib/quant/indicators_sources.rb +36 -10
  40. data/lib/quant/mixins/filters.rb +0 -3
  41. data/lib/quant/mixins/moving_averages.rb +0 -3
  42. data/lib/quant/pivots_source.rb +28 -0
  43. data/lib/quant/refinements/array.rb +14 -0
  44. data/lib/quant/series.rb +8 -19
  45. data/lib/quant/settings/indicators.rb +11 -0
  46. data/lib/quant/ticks/ohlc.rb +0 -2
  47. data/lib/quant/ticks/spot.rb +0 -2
  48. data/lib/quant/version.rb +1 -1
  49. data/lib/quantitative.rb +21 -4
  50. data/possibilities.png +0 -0
  51. metadata +34 -5
  52. data/lib/quant/indicators_proxy.rb +0 -68
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b5786b75635cde82e7919fce6b8fcd568724b27fc2810efe6db36a7e6dd09cc
4
- data.tar.gz: 3a6fb86c25bb4f2b1e4994710539bfb528377ef57531e6845a4417c87fc86e29
3
+ metadata.gz: 0cfda18120bf7e3432b359a53d19d633f70f66e3ea4468098673cfc89ac2f77e
4
+ data.tar.gz: 598dc565f6da96ce9565afee249f38bc909234177d7b484ce82cba226e2e8d60
5
5
  SHA512:
6
- metadata.gz: 5ae30c5b971d6c7bd41e3c2b764e7d95438931790271133dc0e3a476a371affddfdcf06355ffa933240c0c9952fd8fa5e4212c496c95c80f028de7399d3b129f
7
- data.tar.gz: 9ff8aeaf24800a7208e51f836dd7c7c3621b2e2e3a7f1e8820d89f6fc5cd56dd3c4b762c478d221c6ef7279f62c0df75ae1a19f6a4d34e7ba82e116e6a10a790
6
+ metadata.gz: 5b2625954abe0e72a776de95da8d26af213440cb7e2d7beddd48529dfdb70d7247562066b09a3edd1dd3f171ad3ff8a32390ffe337729ca9712f65bdb147084e
7
+ data.tar.gz: 780f81a808f14d2d63b53aa81f8f30920b252979f17409d1ba48105ed5eadc91dadc253ada0cd75c6c21b8e6a8450cf39c75ce542adb450759fb097679899cce
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- quantitative (0.2.2)
4
+ quantitative (0.3.1)
5
5
  oj (~> 3.10)
6
+ zeitwerk (~> 2.6)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -126,6 +127,7 @@ GEM
126
127
  unicode-display_width (2.5.0)
127
128
  vernier (0.5.1)
128
129
  yard (0.9.34)
130
+ zeitwerk (2.6.15)
129
131
 
130
132
  PLATFORMS
131
133
  arm64-darwin-22
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:
data/lib/quant/asset.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "asset_class"
4
-
5
3
  module Quant
6
4
  # A {Quant::Asset} is a representation of a financial instrument such as a stock, option, future, or currency.
7
5
  # It is used to represent the instrument that is being traded, analyzed, or managed.
@@ -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 DominantCycleIndicators < IndicatorsProxy
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
- # Static, arbitrarily set period.
50
- def half_period; indicator(Indicators::DominantCycles::HalfPeriod) end
54
+ private
55
+
56
+ def indicator(indicator_class)
57
+ @indicator_source[indicator_class]
58
+ end
51
59
  end
52
60
  end
@@ -1,14 +1,23 @@
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
- def self.experimental(message)
11
- return if defined?(RSpec)
17
+ module_function
18
+
19
+ def experimental(message)
20
+ return if Experimental.rspec_defined?
12
21
  return if Experimental.tracker[caller.first]
13
22
 
14
23
  Experimental.tracker[caller.first] = message
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "indicator_point"
4
- require_relative "indicator"
5
-
6
3
  module Quant
7
- class Indicators
4
+ module Indicators
8
5
  class AdxPoint < IndicatorPoint
9
6
  attribute :dmu, default: 0.0
10
7
  attribute :dmd, default: 0.0
@@ -24,6 +21,8 @@ module Quant
24
21
  end
25
22
 
26
23
  class Adx < Indicator
24
+ depends_on Indicators::Atr
25
+
27
26
  def alpha
28
27
  bars_to_alpha(dc_period)
29
28
  end
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "indicator_point"
4
- require_relative "indicator"
5
-
6
3
  module Quant
7
- class Indicators
4
+ module Indicators
8
5
  class AtrPoint < IndicatorPoint
9
6
  attribute :tr, default: 0.0
10
7
  attribute :period, default: :min_period
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module Indicators
5
5
  class CciPoint < IndicatorPoint
6
6
  attribute :hp, default: 0.0
7
7
  attribute :real, default: 0.0
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module Indicators
5
5
  # The decycler oscillator can be useful for determining the transition be- tween uptrends and downtrends by the crossing of the zero
6
6
  # line. Alternatively, the changes of slope of the decycler oscillator are easier to identify than the changes in slope of the
7
7
  # original decycler. Optimum cutoff periods can easily be found by experimentation.
@@ -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
- attr_accessor :decycle, :hp1, :hp2, :osc, :peak, :agc, :ift
18
-
19
- def to_h
20
- {
21
- "dc" => decycle,
22
- "hp1" => hp1,
23
- "hp2" => hp2,
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.oc2 + p1.oc2) + (1.0 - alpha) * p1.decycle
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.oc2 - 2 * p1.oc2 + p2.oc2) + 2 * (1 - alpha) * p1.send(hp) - (1 - alpha) * (1 - alpha) * p2.send(hp)
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
- def compute_agc
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 compute_ift
74
- p0.ift = ift(p0.agc, 5.0)
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
- compute_agc
81
- compute_ift
66
+ compute_automatic_gain_control
67
+ compute_inverse_fisher_transform
82
68
  end
83
69
  end
84
70
  end
@@ -1,12 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../indicator_point"
4
- require_relative "dominant_cycle"
5
-
6
3
  module Quant
7
- class Indicators
8
- class DominantCycles
9
- class AcrPoint < DominantCyclePoint
4
+ module Indicators
5
+ module DominantCycles
6
+ class AcrPoint < Quant::Indicators::IndicatorPoint
10
7
  attribute :hp, default: 0.0
11
8
  attribute :filter, default: 0.0
12
9
  attribute :interim_period, default: 0.0
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dominant_cycle"
4
-
5
3
  module Quant
6
- class Indicators
7
- class DominantCycles
4
+ module Indicators
5
+ module DominantCycles
8
6
  class BandPassPoint < Quant::Indicators::IndicatorPoint
9
7
  attribute :hp, default: 0.0
10
8
  attribute :bp, default: 0.0
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
5
- class DominantCycles
4
+ module Indicators
5
+ module DominantCycles
6
6
  # The Dual Differentiator algorithm computes the phase angle from the
7
7
  # analytic signal as the arctangent of the ratio of the imaginary
8
8
  # component to the real component. Further, the angular frequency
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../indicator"
4
-
5
3
  module Quant
6
- class Indicators
4
+ module Indicators
7
5
  # Dominant Cycles measure the primary cycle within a given range. By default, the library
8
6
  # is wired to look for cycles between 10 and 48 bars. These values can be adjusted by setting
9
7
  # the `min_period` and `max_period` configuration values in {Quant::Config}.
@@ -18,7 +16,7 @@ module Quant
18
16
  # The purpose of these indicators is to compute the dominant cycle and underpin the various
19
17
  # indicators that would otherwise be setting an arbitrary lookback period. This makes the
20
18
  # indicators adaptive and auto-tuning to the market dynamics. Or so the theory goes!
21
- class DominantCycles
19
+ module DominantCycles
22
20
  class DominantCyclePoint < Quant::Indicators::IndicatorPoint
23
21
  attribute :smooth, default: 0.0
24
22
  attribute :detrend, default: 0.0
@@ -44,6 +42,15 @@ module Quant
44
42
  end
45
43
 
46
44
  class DominantCycle < Indicators::Indicator
45
+ def priority
46
+ DOMINANT_CYCLES_PRIORITY
47
+ end
48
+
49
+ # Dominant Cycle Indicators should not themselves have a dominant cycle indicator
50
+ def dominant_cycle_indicator_class
51
+ nil
52
+ end
53
+
47
54
  def points_class
48
55
  Object.const_get "Quant::Indicators::DominantCycles::#{indicator_name}Point"
49
56
  rescue NameError
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dominant_cycle"
4
-
5
3
  module Quant
6
- class Indicators
7
- class DominantCycles
4
+ module Indicators
5
+ module DominantCycles
8
6
  # This dominant cycle indicator is based on the half period
9
7
  # that is the midpoint of the `min_period` and `max_period`
10
8
  # configured in the `Quant.config.indicators` object.
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../indicator_point"
4
- require_relative "dominant_cycle"
5
-
6
3
  module Quant
7
- class Indicators
8
- class DominantCycles
4
+ module Indicators
5
+ module DominantCycles
9
6
  # Homodyne means the signal is multiplied by itself. More precisely,
10
7
  # we want to multiply the signal of the current bar with the complex
11
8
  # value of the signal one bar ago
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dominant_cycle"
4
-
5
3
  module Quant
6
- class Indicators
7
- class DominantCycles
4
+ module Indicators
5
+ module DominantCycles
8
6
  # The phase accumulation method of computing the dominant cycle is perhaps
9
7
  # the easiest to comprehend. In this technique, we measure the phase
10
8
  # at each sample by taking the arctangent of the ratio of the quadrature
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module 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
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module 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].dominant_cycle
94
+ series.indicators[source][dominant_cycle_indicator_class]
52
95
  end
53
96
 
54
97
  def dc_period
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module Indicators
5
5
  class IndicatorPoint
6
6
  include Quant::Attributes
7
7
  extend Forwardable
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module Indicators
5
5
  class MamaPoint < IndicatorPoint
6
6
  attribute :smooth, default: 0.0
7
7
  attribute :detrend, default: 0.0
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module Indicators
5
5
  # The MESA inidicator
6
6
  class MesaPoint < IndicatorPoint
7
7
  attribute :mama, default: :input
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
4
+ module Indicators
5
5
  # A simple point used primarily to test the indicator system in unit tests.
6
6
  # It has a simple computation that just sets the pong value to the input value
7
7
  # and increments the compute_count by 1 each time compute is called.
@@ -0,0 +1,107 @@
1
+ module Quant
2
+ module 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