Class: Finrb::Numerical::RateSearch

Inherits:
Object
  • Object
show all
Defined in:
lib/finrb/numerical/rate_search.rb,
sig/finrb.rbs

Overview

Locates the nearest sign-changing rate interval around a caller's guess. Search happens in log(1 + rate) space, which covers the entire financial domain rate > -1 without stepping across its singular boundary.

Instance Method Summary collapse

Constructor Details

#initialize(step: DEFAULT_STEP, max_steps: DEFAULT_MAX_STEPS) ⇒ RateSearch

Returns a new instance of RateSearch.

Parameters:

  • step: (number) (defaults to: DEFAULT_STEP)
  • max_steps: (Integer) (defaults to: DEFAULT_MAX_STEPS)

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
# File 'lib/finrb/numerical/rate_search.rb', line 16

def initialize(step: DEFAULT_STEP, max_steps: DEFAULT_MAX_STEPS)
  @step = decimal(step)
  @max_steps = Integer(max_steps)

  raise(ArgumentError, 'Search step must be positive.') unless @step.positive?
  raise(ArgumentError, 'Maximum search steps must be positive.') unless @max_steps.positive?
end

Instance Method Details

#bracket(function, guess:) ⇒ [decimal, decimal]

Parameters:

  • guess: (number)

Returns:

  • ([decimal, decimal])

Raises:



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/finrb/numerical/rate_search.rb', line 24

def bracket(function, guess:)
  guess = decimal(guess)
  raise(DomainError, 'Rate guess must be greater than -1.') if guess <= -1

  center_coordinate = (guess + 1).ln
  center = [guess, evaluate(function, guess)]
  return [guess, guess] if center.last.zero?

  left = center
  right = center

  1.upto(@max_steps) do |distance|
    next_left = point(function, center_coordinate - (@step * distance))
    next_right = point(function, center_coordinate + (@step * distance))
    candidates = []
    candidates << [next_left.first, left.first] if opposite_signs?(next_left.last, left.last)
    candidates << [right.first, next_right.first] if opposite_signs?(right.last, next_right.last)
    return nearest(candidates, guess) unless candidates.empty?

    left = next_left
    right = next_right
  end

  raise(ConvergenceError, "Could not bracket a root near guess #{guess}.")
end