quantitative 0.3.0 → 0.3.2

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/lib/quant/asset.rb +0 -2
  4. data/lib/quant/dominant_cycles_source.rb +1 -32
  5. data/lib/quant/experimental.rb +3 -1
  6. data/lib/quant/indicators/adx.rb +3 -10
  7. data/lib/quant/indicators/atr.rb +3 -4
  8. data/lib/quant/indicators/cci.rb +3 -1
  9. data/lib/quant/indicators/decycler.rb +3 -1
  10. data/lib/quant/indicators/dominant_cycles/acr.rb +5 -6
  11. data/lib/quant/indicators/dominant_cycles/band_pass.rb +4 -4
  12. data/lib/quant/indicators/dominant_cycles/differential.rb +4 -2
  13. data/lib/quant/indicators/dominant_cycles/dominant_cycle.rb +2 -4
  14. data/lib/quant/indicators/dominant_cycles/half_period.rb +4 -4
  15. data/lib/quant/indicators/dominant_cycles/homodyne.rb +4 -5
  16. data/lib/quant/indicators/dominant_cycles/phase_accumulator.rb +4 -4
  17. data/lib/quant/indicators/ema.rb +67 -0
  18. data/lib/quant/indicators/frama.rb +2 -1
  19. data/lib/quant/indicators/indicator.rb +5 -1
  20. data/lib/quant/indicators/indicator_point.rb +1 -1
  21. data/lib/quant/indicators/mama.rb +3 -1
  22. data/lib/quant/indicators/mesa.rb +4 -5
  23. data/lib/quant/indicators/ping.rb +3 -1
  24. data/lib/quant/indicators/pivots/atr.rb +3 -2
  25. data/lib/quant/indicators/pivots/bollinger.rb +4 -2
  26. data/lib/quant/indicators/pivots/camarilla.rb +3 -5
  27. data/lib/quant/indicators/pivots/classic.rb +4 -2
  28. data/lib/quant/indicators/pivots/demark.rb +4 -2
  29. data/lib/quant/indicators/pivots/donchian.rb +3 -2
  30. data/lib/quant/indicators/pivots/fibbonacci.rb +4 -2
  31. data/lib/quant/indicators/pivots/guppy.rb +5 -3
  32. data/lib/quant/indicators/pivots/keltner.rb +3 -2
  33. data/lib/quant/indicators/pivots/murrey.rb +4 -3
  34. data/lib/quant/indicators/pivots/pivot.rb +109 -0
  35. data/lib/quant/indicators/pivots/traditional.rb +4 -2
  36. data/lib/quant/indicators/pivots/woodie.rb +5 -3
  37. data/lib/quant/indicators/rocket_rsi.rb +57 -0
  38. data/lib/quant/indicators/roofing.rb +59 -0
  39. data/lib/quant/indicators/rsi.rb +67 -0
  40. data/lib/quant/indicators/snr.rb +64 -0
  41. data/lib/quant/indicators.rb +6 -0
  42. data/lib/quant/indicators_registry.rb +63 -0
  43. data/lib/quant/indicators_source.rb +14 -10
  44. data/lib/quant/mixins/filters.rb +0 -3
  45. data/lib/quant/mixins/moving_averages.rb +0 -3
  46. data/lib/quant/pivots_source.rb +1 -13
  47. data/lib/quant/ticks/ohlc.rb +0 -2
  48. data/lib/quant/ticks/spot.rb +0 -2
  49. data/lib/quant/version.rb +1 -1
  50. data/lib/quantitative.rb +29 -6
  51. metadata +25 -5
  52. data/lib/quant/indicators/pivot.rb +0 -107
  53. data/quantitative.gemspec +0 -39
@@ -1,9 +1,10 @@
1
- require_relative '../pivot'
2
1
 
3
2
  module Quant
4
- class Indicators
5
- class Pivots
3
+ module Indicators
4
+ module Pivots
6
5
  class Murrey < Pivot
6
+ register name: :murrey
7
+
7
8
  def multiplier
8
9
  0.125
9
10
  end
@@ -0,0 +1,109 @@
1
+ module Quant
2
+ module Indicators
3
+ module Pivots
4
+ class PivotPoint < IndicatorPoint
5
+ attribute :high_price
6
+ attribute :avg_high, default: :high_price
7
+ attribute :highest, default: :input
8
+
9
+ attribute :low_price
10
+ attribute :avg_low, default: :low_price
11
+ attribute :lowest, default: :input
12
+
13
+ attribute :range, default: 0.0
14
+ attribute :avg_range, default: 0.0
15
+ attribute :std_dev, default: 0.0
16
+
17
+ def bands
18
+ @bands ||= { 0 => input }
19
+ end
20
+
21
+ def [](band)
22
+ bands[band]
23
+ end
24
+
25
+ def []=(band, value)
26
+ bands[band] = value
27
+ end
28
+
29
+ def key?(band)
30
+ bands.key?(band)
31
+ end
32
+
33
+ def midpoint
34
+ bands[0]
35
+ end
36
+ alias :h0 :midpoint
37
+ alias :l0 :midpoint
38
+
39
+ def midpoint=(value)
40
+ bands[0] = value
41
+ end
42
+ alias :h0= :midpoint=
43
+ alias :l0= :midpoint=
44
+
45
+ (1..8).each do |band|
46
+ define_method("h#{band}") { bands[band] }
47
+ define_method("h#{band}=") { |value| bands[band] = value }
48
+
49
+ define_method("l#{band}") { bands[-band] }
50
+ define_method("l#{band}=") { |value| bands[-band] = value }
51
+ end
52
+ end
53
+
54
+ class Pivot < Indicator
55
+ def points_class
56
+ Quant::Indicators::Pivots::PivotPoint
57
+ end
58
+
59
+ def band?(band)
60
+ p0.key?(band)
61
+ end
62
+
63
+ def period
64
+ dc_period
65
+ end
66
+
67
+ def averaging_period
68
+ min_period
69
+ end
70
+
71
+ def period_midpoints
72
+ period_points(period).map(&:midpoint)
73
+ end
74
+
75
+ def compute
76
+ compute_extents
77
+ compute_value
78
+ compute_midpoint
79
+ compute_bands
80
+ end
81
+
82
+ def compute_midpoint
83
+ p0.midpoint = p0.input
84
+ end
85
+
86
+ def compute_value
87
+ # No-op -- override in subclasses
88
+ end
89
+
90
+ def compute_bands
91
+ # No-op -- override in subclasses
92
+ end
93
+
94
+ def compute_extents
95
+ period_midpoints.tap do |midpoints|
96
+ p0.high_price = t0.high_price
97
+ p0.low_price = t0.low_price
98
+ p0.highest = midpoints.max
99
+ p0.lowest = midpoints.min
100
+ p0.range = p0.high_price - p0.low_price
101
+ p0.avg_low = super_smoother(:low_price, previous: :avg_low, period: averaging_period)
102
+ p0.avg_high = super_smoother(:high_price, previous: :avg_high, period: averaging_period)
103
+ p0.avg_range = super_smoother(:range, previous: :avg_range, period: averaging_period)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,7 +1,9 @@
1
1
  module Quant
2
- class Indicators
3
- class Pivots
2
+ module Indicators
3
+ module Pivots
4
4
  class Traditional < Pivot
5
+ register name: :traditional
6
+
5
7
  def multiplier
6
8
  2.0
7
9
  end
@@ -1,8 +1,8 @@
1
- require_relative '../pivot'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- class Indicators
5
- class Pivots
4
+ module Indicators
5
+ module Pivots
6
6
  # One of the key differences in calculating Woodie's Pivot Point to other pivot
7
7
  # points is that the current session's open price is used in the PP formula with
8
8
  # the previous session's high and low. At the time-of-day that we calculate the
@@ -22,6 +22,8 @@ module Quant
22
22
  # S3 = L - 2 * (H - PP) (same as: S1 - RANGE)
23
23
  # S4 = S3 - RANGE
24
24
  class Woodie < Pivot
25
+ register name: :woodie
26
+
25
27
  def compute_value
26
28
  p0.input = (t1.high_price + t1.low_price + 2.0 * t0.open_price) / 4.0
27
29
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ module Indicators
5
+ class RocketRsiPoint < IndicatorPoint
6
+ attribute :hp, default: 0.0
7
+
8
+ attribute :delta, default: 0.0
9
+ attribute :gain, default: 0.0
10
+ attribute :loss, default: 0.0
11
+
12
+ attribute :gains, default: 0.0
13
+ attribute :losses, default: 0.0
14
+ attribute :denom, default: 0.0
15
+
16
+ attribute :inst_rsi, default: 0.5
17
+ attribute :rsi, default: 0.0
18
+ attribute :crosses, default: false
19
+ end
20
+
21
+ class RocketRsi < Indicator
22
+ register name: :rocket_rsi
23
+
24
+ def quarter_period
25
+ half_period / 2
26
+ end
27
+
28
+ def half_period
29
+ (dc_period / 2) - 1
30
+ end
31
+
32
+ def compute
33
+ p0.hp = two_pole_butterworth :input, previous: :hp, period: quarter_period
34
+
35
+ lp = p(half_period)
36
+ p0.delta = p0.hp - lp.hp
37
+ p0.delta > 0.0 ? p0.gain = p0.delta : p0.loss = p0.delta.abs
38
+
39
+ period_points(half_period).tap do |period_points|
40
+ p0.gains = period_points.map(&:gain).sum
41
+ p0.losses = period_points.map(&:loss).sum
42
+ end
43
+
44
+ p0.denom = p0.gains + p0.losses
45
+
46
+ if p0.denom.zero?
47
+ p0.inst_rsi = p1.inst_rsi
48
+ p0.rsi = p1.rsi
49
+ else
50
+ p0.inst_rsi = ((p0.gains - p0.losses) / p0.denom)
51
+ p0.rsi = fisher_transform(p0.inst_rsi).clamp(-1.0, 1.0)
52
+ end
53
+ p0.crosses = (p0.rsi >= 0.0 && p1.rsi < 0.0) || (p0.rsi <= 0.0 && p1.rsi > 0.0)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ module Indicators
5
+ # The ideal time to buy is when the cycle is at a trough, and the ideal time to exit a long position or to
6
+ # sell short is when the cycle is at a peak.These conditions are flagged by the filter crossing itself
7
+ # delayed by two bars, and are included as part of the indicator.
8
+ class RoofingPoint < IndicatorPoint
9
+ attribute :hp, default: 0.0
10
+ attribute :value, default: 0.0
11
+ attribute :peak, default: 0.0
12
+ attribute :agc, default: 0.0
13
+ attribute :direction, default: 0
14
+ attribute :turned, default: false
15
+ end
16
+
17
+ class Roofing < Indicator
18
+ register name: :roofing
19
+
20
+ def low_pass_period
21
+ dc_period
22
+ end
23
+
24
+ def high_pass_period
25
+ low_pass_period * 2
26
+ end
27
+
28
+ # //Highpass filter cyclic components whose periods are shorter than 48 bars
29
+ # alpha1 = (Cosine(.707*360 / HPPeriod) + Sine (.707*360 / HPPeriod) - 1) / Cosine(.707*360 / HPPeriod);
30
+ # HP = (1 - alpha1 / 2)*(1 - alpha1 / 2)*(Close - 2*Close[1] + Close[2]) + 2*(1 - alpha1)*HP[1] - (1 - alpha1)*
31
+ # (1 - alpha1)*HP[2];
32
+ # //Smooth with a Super Smoother Filter from equation 3-3
33
+ # a1 = expvalue(-1.414*3.14159 / LPPeriod);
34
+ # b1 = 2*a1*Cosine(1.414*180 / LPPeriod);
35
+ # c2 = b1;
36
+ # c3 = -a1*a1;
37
+ # c1 = 1 - c2 - c3;
38
+ # Filt = c1*(HP + HP[1]) / 2 + c2*Filt[1] + c3*Filt[2
39
+ def compute
40
+ a = Math.cos(0.707 * deg2rad(360) / high_pass_period)
41
+ b = Math.sin(0.707 * deg2rad(360) / high_pass_period)
42
+ alpha1 = (a + b - 1) / a
43
+
44
+ p0.hp = (1 - alpha1 / 2)**2 * (p0.input - 2 * p1.input + p2.input) + 2 * (1 - alpha1) * p1.hp - (1 - alpha1)**2 * p2.hp
45
+ a1 = Math.exp(-1.414 * Math::PI / low_pass_period)
46
+ c2 = 2 * a1 * Math.cos(1.414 * deg2rad(180) / low_pass_period)
47
+ c3 = -a1**2
48
+ c1 = 1 - c2 - c3
49
+ p0.value = c1 * (p0.hp + p1.hp) / 2 + c2 * p1.value + c3 * p2.value
50
+ p0.direction = p0.value > p2.value ? 1 : -1
51
+ p0.turned = p0.direction != p2.direction
52
+ # Peak = .991 * Peak[1];
53
+ # If AbsValue(BP) > Peak Then Peak = AbsValue(BP); If Peak <> 0 Then Signal = BP / Peak;
54
+ p0.peak = [p0.value.abs, 0.991 * p1.peak].max
55
+ p0.agc = p0.peak == 0 ? 0 : p0.value / p0.peak
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ module Indicators
5
+ class RsiPoint < IndicatorPoint
6
+ attribute :hp, default: 0.0
7
+ attribute :filter, default: 0.0
8
+
9
+ attribute :delta, default: 0.0
10
+ attribute :gain, default: 0.0
11
+ attribute :loss, default: 0.0
12
+
13
+ attribute :gains, default: 0.0
14
+ attribute :losses, default: 0.0
15
+ attribute :denom, default: 0.0
16
+
17
+ attribute :inst_rsi, default: 0.0
18
+ attribute :rsi, default: 0.0
19
+ end
20
+
21
+ # The Relative Strength Index (RSI) is a momentum oscillator that measures the
22
+ # speed and change of price movements. This RSI indicator is adaptive and
23
+ # uses the half-period of the dominant cycle to calculate the RSI.
24
+ # It is further smoothed by an exponential moving average of the last three bars
25
+ # (or whatever the micro_period is set to).
26
+ #
27
+ # The RSI oscillates between 0 and 1. Traditionally, and in this implementation,
28
+ # the RSI is considered overbought when above 0.7 and oversold when below 0.3.
29
+ class Rsi < Indicator
30
+ register name: :rsi
31
+
32
+ def quarter_period
33
+ half_period / 2
34
+ end
35
+
36
+ def half_period
37
+ (dc_period / 2) - 1
38
+ end
39
+
40
+ def compute
41
+ # The High Pass filter is half the dominant cycle period while the
42
+ # Low Pass Filter (super smoother) is the quarter dominant cycle period.
43
+ p0.hp = high_pass_filter :input, period: half_period
44
+ p0.filter = ema :hp, previous: :filter, period: quarter_period
45
+
46
+ lp = p(half_period)
47
+ p0.delta = p0.filter - lp.filter
48
+ p0.delta > 0.0 ? p0.gain = p0.delta : p0.loss = p0.delta.abs
49
+
50
+ period_points(half_period).tap do |period_points|
51
+ p0.gains = period_points.map(&:gain).sum
52
+ p0.losses = period_points.map(&:loss).sum
53
+ end
54
+
55
+ p0.denom = p0.gains + p0.losses
56
+
57
+ if p0.denom > 0.0
58
+ p0.inst_rsi = (p0.gains / p0.denom)
59
+ p0.rsi = ema :inst_rsi, previous: :rsi, period: micro_period
60
+ else
61
+ p0.inst_rsi = 0.5
62
+ p0.rsi = 0.5
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ module Indicators
5
+ class SnrPoint < IndicatorPoint
6
+ attribute :smooth, default: 0.0
7
+ attribute :detrend, default: 0.0
8
+ attribute :i1, default: 0.0
9
+ attribute :q1, default: 0.0
10
+ attribute :noise, default: 0.0
11
+ attribute :signal, default: 0.0
12
+ attribute :ratio, default: 0.0
13
+ attribute :state, default: 0
14
+ end
15
+
16
+ class Snr < Indicator
17
+ register name: :snr
18
+ depends_on DominantCycles::Homodyne
19
+
20
+ def homodyne_dominant_cycle
21
+ series.indicators[source].dominant_cycles.homodyne
22
+ end
23
+
24
+ def current_dominant_cycle
25
+ homodyne_dominant_cycle.points[t0]
26
+ end
27
+
28
+ def threshold
29
+ @threshold ||= 10 * Math.log(0.5)**2
30
+ end
31
+
32
+ def compute_values
33
+ current_dominant_cycle.tap do |dc|
34
+ p0.i1 = dc.i1
35
+ p0.q1 = dc.q1
36
+ end
37
+ end
38
+
39
+ def compute_noise
40
+ noise = (p0.input - p2.input).abs
41
+ p0.noise = p1.noise.zero? ? noise : (0.1 * noise) + (0.9 * p1.noise)
42
+ end
43
+
44
+ def compute_ratio
45
+ # p0.ratio = 0.25 * (10 * Math.log(p0.i1**2 + p0.q1**2) / Math.log(10)) + 0.75 * p1.ratio
46
+ # ratio = .25*(10 * Log(I1*I1 + Q1*Q1)/(Range*Range))/Log(10) + 6) + .75*ratio[1]
47
+ if p0 == p1
48
+ p0.signal = 0.0
49
+ p0.ratio = 1.0
50
+ else
51
+ p0.signal = threshold + 10.0 * (Math.log((p0.i1**2 + p0.q1**2)/(p0.noise**2)) / Math.log(10))
52
+ p0.ratio = (0.25 * p0.signal) + (0.75 * p1.ratio)
53
+ end
54
+ p0.state = p0.ratio >= threshold ? 1 : 0
55
+ end
56
+
57
+ def compute
58
+ compute_values
59
+ compute_noise
60
+ compute_ratio
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ module Indicators
5
+ end
6
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ module IndicatorsRegistry
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def define_indicator_accessors(indicator_source:)
10
+ self.class.define_indicator_accessors(indicator_source:)
11
+ end
12
+
13
+ module ClassMethods
14
+ def registry
15
+ @registry ||= {}
16
+ end
17
+
18
+ class RegistryEntry
19
+ attr_reader :name, :indicator_class
20
+
21
+ def initialize(name:, indicator_class:)
22
+ @name = name
23
+ @indicator_class = indicator_class
24
+ end
25
+
26
+ def key
27
+ "#{indicator_class.name}::#{name}"
28
+ end
29
+
30
+ def standard?
31
+ !pivot? && !dominant_cycle?
32
+ end
33
+
34
+ def pivot?
35
+ indicator_class < Indicators::Pivots::Pivot
36
+ end
37
+
38
+ def dominant_cycle?
39
+ indicator_class < Indicators::DominantCycles::DominantCycle
40
+ end
41
+ end
42
+
43
+ def register(name:, indicator_class:)
44
+ entry = RegistryEntry.new(name:, indicator_class:)
45
+ registry[entry.key] = entry
46
+ # registry[name] = indicator_class
47
+ end
48
+
49
+ def registry_entries_for(indicator_source:)
50
+ return registry.values.select(&:pivot?) if indicator_source.is_a?(PivotsSource)
51
+ return registry.values.select(&:dominant_cycle?) if indicator_source.is_a?(DominantCyclesSource)
52
+
53
+ registry.values.select(&:standard?)
54
+ end
55
+
56
+ def define_indicator_accessors(indicator_source:)
57
+ registry_entries_for(indicator_source:).each do |entry|
58
+ indicator_source.define_singleton_method(entry.name) { indicator(entry.indicator_class) }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dominant_cycles_source"
4
3
  module Quant
5
4
  # {Quant::IndicatorSource} holds a collection of {Quant::Indicators::Indicator} for a given input source.
6
5
  # This class ensures dominant cycle computations come before other indicators that depend on them.
@@ -17,13 +16,18 @@ module Quant
17
16
  # By design, the {Quant::Indicators::Indicator} class holds the {Quant::Ticks::Tick} instance
18
17
  # alongside the indicator's computed values for that tick.
19
18
  class IndicatorsSource
19
+ include IndicatorsRegistry
20
+
20
21
  attr_reader :series, :source, :dominant_cycles, :pivots
21
22
 
22
23
  def initialize(series:, source:)
23
24
  @series = series
24
25
  @source = source
26
+
25
27
  @indicators = {}
26
28
  @ordered_indicators = []
29
+ define_indicator_accessors(indicator_source: self)
30
+
27
31
  @dominant_cycles = DominantCyclesSource.new(indicator_source: self)
28
32
  @pivots = PivotsSource.new(indicator_source: self)
29
33
  end
@@ -36,20 +40,19 @@ module Quant
36
40
  @ordered_indicators.each { |indicator| indicator << tick }
37
41
  end
38
42
 
39
- def adx; indicator(Indicators::Adx) end
40
- def atr; indicator(Indicators::Atr) end
41
- def cci; indicator(Indicators::Cci) end
42
- def decycler; indicator(Indicators::Decycler) end
43
- def frama; indicator(Indicators::Frama) end
44
- def mama; indicator(Indicators::Mama) end
45
- def mesa; indicator(Indicators::Mesa) end
46
- def ping; indicator(Indicators::Ping) end
47
-
48
43
  # Attaches a given Indicator class and defines the method for
49
44
  # accessing it using the given name. Indicators take care of
50
45
  # computing their values when first attached to a populated
51
46
  # series.
52
47
  #
48
+ # NOTE: You can also use the `register` method on the indicator class to
49
+ # accomplish the same thing. `attach` lets you inject a custom indicator
50
+ # at run-time when you have an instance of {Quant::IndicatorsSource} while
51
+ # the `register` method is used to define the indicator at load-time.
52
+ #
53
+ # NOTE Calling `attach` also registers the indicator with the framework, so
54
+ # you only have to `attach` once.
55
+ #
53
56
  # The indicators shipped with the library are all wired into the framework, thus
54
57
  # this method should be used for custom indicators not shipped with the library.
55
58
  #
@@ -58,6 +61,7 @@ module Quant
58
61
  # @example
59
62
  # series.indicators.oc2.attach(name: :foo, indicator_class: Indicators::Foo)
60
63
  def attach(name:, indicator_class:)
64
+ self.class.register(name:, indicator_class:)
61
65
  define_singleton_method(name) { indicator(indicator_class) }
62
66
  end
63
67
 
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "high_pass_filters"
4
- require_relative "butterworth_filters"
5
- require_relative "universal_filters"
6
3
  module Quant
7
4
  module Mixins
8
5
  module Filters
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "weighted_moving_average"
4
- require_relative "simple_moving_average"
5
- require_relative "exponential_moving_average"
6
3
  module Quant
7
4
  module Mixins
8
5
  module MovingAverages
@@ -4,21 +4,9 @@ module Quant
4
4
  class PivotsSource
5
5
  def initialize(indicator_source:)
6
6
  @indicator_source = indicator_source
7
+ indicator_source.define_indicator_accessors(indicator_source: self)
7
8
  end
8
9
 
9
- def atr; indicator(Indicators::Pivots::Atr) end
10
- def bollinger; indicator(Indicators::Pivots::Bollinger) end
11
- def camarilla; indicator(Indicators::Pivots::Camarilla) end
12
- def classic; indicator(Indicators::Pivots::Classic) end
13
- def demark; indicator(Indicators::Pivots::Demark) end
14
- def donchian; indicator(Indicators::Pivots::Donchian) end
15
- def fibbonacci; indicator(Indicators::Pivots::Fibbonacci) end
16
- def guppy; indicator(Indicators::Pivots::Guppy) end
17
- def keltner; indicator(Indicators::Pivots::Keltner) end
18
- def murrey; indicator(Indicators::Pivots::Murrey) end
19
- def traditional; indicator(Indicators::Pivots::Traditional) end
20
- def woodie; indicator(Indicators::Pivots::Woodie) end
21
-
22
10
  private
23
11
 
24
12
  def indicator(indicator_class)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "tick"
4
-
5
3
  module Quant
6
4
  module Ticks
7
5
  # A {Quant::Ticks::OHLC} is a bar or candle for a point in time that
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "tick"
4
-
5
3
  module Quant
6
4
  module Ticks
7
5
  # A +Spot+ is a single price point in time. It is the most basic form of a {Quant::Ticks::Tick} and is usually used to represent
data/lib/quant/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
data/lib/quantitative.rb CHANGED
@@ -4,14 +4,37 @@ require "time"
4
4
  require "date"
5
5
  require "oj"
6
6
  require "csv"
7
+ require "zeitwerk"
7
8
 
8
9
  lib_folder = File.expand_path(File.join(File.dirname(__FILE__)))
9
10
  quant_folder = File.join(lib_folder, "quant")
10
11
 
11
- # require top-level files
12
- Dir.glob(File.join(quant_folder, "*.rb")).each { |fn| require fn }
13
-
14
- # require sub-folders and their sub-folders
15
- %w(refinements mixins statistics settings ticks indicators).each do |sub_folder|
16
- Dir.glob(File.join(quant_folder, sub_folder, "**/*.rb")).each { |fn| require fn }
12
+ # Explicitly require module functions since Zeitwerk isn't configured, yet.
13
+ require_relative "quant/time_methods"
14
+ require_relative "quant/config"
15
+ require_relative "quant/experimental"
16
+ module Quant
17
+ include TimeMethods
18
+ include Config
19
+ include Experimental
17
20
  end
21
+
22
+ # Configure Zeitwerk to autoload the Quant module.
23
+ loader = Zeitwerk::Loader.for_gem
24
+ loader.push_dir(quant_folder, namespace: Quant)
25
+
26
+ loader.inflector.inflect "ohlc" => "OHLC"
27
+ loader.inflector.inflect "version" => "VERSION"
28
+
29
+ loader.setup
30
+
31
+ # Refinements aren't autoloaded by Zeitwerk, so we need to require them manually.
32
+ # %w(refinements).each do |sub_folder|
33
+ # Dir.glob(File.join(quant_folder, sub_folder, "**/*.rb")).each { |fn| require fn }
34
+ # end
35
+
36
+ refinements_folder = File.join(quant_folder, "refinements")
37
+ indicators_folder = File.join(quant_folder, "indicators")
38
+
39
+ loader.eager_load_dir(refinements_folder)
40
+ loader.eager_load_dir(indicators_folder)