0

我正在尝试使用 brents 方法来计算内部收益率。使用这个作为模板布伦特方法

我目前有这个代码:

(defn discount-factor
  [discount-rate n]
  (/ 1 (utils/exponent (+ 1 (/ discount-rate 100)) n)))

(defn-spec simple-present-value float?
  "Takes a `future-value` and discounts it with `n` years in the future. Uses a `discount-rate` in percent.
   Returns the discounted present value"
  [future-value number?, n int?, discount-rate ::discount-rate]

  (utils/round (* future-value (discount-factor discount-rate n))))

(defn-spec discount-cashflow float?
  "Takes collection of `cash-flow` starting from year 0. Discounts each year with the `discount-rate`. Returns present value"
  [cashflow ::cashflow, discount-rate ::discount-rate]

  (if (empty? cashflow)
    0.0
    (->> (map (fn [element index]
                (simple-present-value element index discount-rate))
              cashflow
              (range (count cashflow)))
         (reduce +)
         utils/round)))

(def tolerance 0.001)

(defn inverse-quadratic-interpolation [a b c fa fb fc]
  (+ (/ (* a fb fc)
        (* (- fa fb) (- fa fc)))
     (/ (* b fa fc)
        (* (- fb fa) (- fb fc)))
     (/ (* c fa fb)
        (* (- fc fa) (- fc fb)))))

(defn secant-method [a b fa fb]
  (- b (* fb (/ (- b a)
                (- fb fa)))))

(defn bisection-method? [a b c fa fb fc s mflag d]
  (or (not (<= (/ (+ (* 3 a) b)
                  4)
               s b))
      (and mflag (>= (absolute (- s b)) (/ (absolute (- b c)) 2)))
      (and (not mflag) (>= (absolute (- s b)) (/ (absolute (- c d)) 2)))
      (and mflag (< (absolute (- b c)) tolerance))
      (and (not mflag) (< (absolute (- c d)) tolerance))))

(defn bisection [a b]
  (/ (+ a b)
     2))

(defn calculate-s [a b c fa fb fc mflag d]
  (let [s (if (and (not= fa fc) (not= fb fc))
            (inverse-quadratic-interpolation a b c fa fb fc)
            (secant-method a b fa fb))]
    (if (bisection-method? a b c fa fb fc s mflag d)
      {:s (bisection a b)
       :mflag true}
      {:s s
       :mflag false})))

(defn brents-method [a b f]
  (let [test-fa (f a)
        test-fb (f b)
        initial-fa (if (< (absolute test-fa) (absolute test-fb)) test-fb test-fa)
        initial-fb (if (< (absolute test-fa) (absolute test-fb)) test-fa test-fb)
        initial-a (if (< (absolute test-fa) (absolute test-fb)) b a)
        initial-b (if (< (absolute test-fa) (absolute test-fb)) a b)]

    (loop [a initial-a b initial-b c initial-a
           fa initial-fa fb initial-fb fc initial-fa
           mflag true
           d nil]

      (let [{s :s mflag :mflag} (calculate-s a b c fa fb fc mflag d)
            fs (f s)]

        (cond (or (= fb 0) (= fs 0) (< (absolute (- b a)) tolerance))
              b
              (and (< (* fa fs) 0) (< (absolute fa) (absolute fb)))
              (recur s a b (f s) fa fb mflag c)
              (and (>= (* fa fs) 0) (< (absolute fa) (absolute fb)))
              (recur b s b fb (f s) fb mflag c)

              (and (< (* fa fs) 0) (>= (absolute fa) (absolute fb)))
              (recur a s b fa (f s) fb mflag c)
              (and (>= (* fa fs) 0) (>= (absolute fa) (absolute fb)))
              (recur s b b (f s) fb fb mflag c))))))



(defn-spec internal-rate-of-return ::discount-rate
  [cashflow sequential?]
  (brents-method 100 10 (partial discount-cashflow cashflow)))

它似乎无法计算出正确的 irr。我要么得到;1. 接近的东西

Expected:
66.19
Actual:
66.35271788727553
  1. 本节中除以零的错误:
(defn secant-method [a b fa fb]
  (- b (* fb (/ (- b a)
                (- fb fa)))))

这是一个很难解决的问题,因此将不胜感激。考虑到我的模板,该代码并不是特别实用。

测试:

       (fact "Calculates correct internal rate of return of a cashflow"
             (finance/discount-rate :cashflow [-10 10 11]) => 66.19)
       (fact "Calculates correct internal rate of return of a cashflow large numbers"
             (finance/discount-rate :cashflow [-1000 1000 1100]) => 66.19)
       (fact "Calculates correct internal rate of return of a cashflow long lasting cashflows"
             (finance/discount-rate :cashflow [-1000 1000 1100 1100 1100 1100 1100 1100 1100 1100]) => 104.71)
4

0 回答 0