quantitative 0.3.0 → 0.3.2

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