finrb 0.0.1
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.
- checksums.yaml +7 -0
- data/.dockerignore +2 -0
- data/.gitattributes +83 -0
- data/.gitignore +114 -0
- data/.rubocop.yml +19 -0
- data/.ruby-version +1 -0
- data/.semver +5 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +74 -0
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/Dockerfile +35 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +116 -0
- data/README.md +99 -0
- data/Rakefile +33 -0
- data/docs/.gitkeep +0 -0
- data/docs/api.md +1104 -0
- data/finrb.gemspec +98 -0
- data/lib/finrb/amortization.rb +199 -0
- data/lib/finrb/cashflows.rb +171 -0
- data/lib/finrb/config.rb +11 -0
- data/lib/finrb/decimal.rb +23 -0
- data/lib/finrb/rates.rb +167 -0
- data/lib/finrb/transaction.rb +124 -0
- data/lib/finrb/utils.rb +1171 -0
- data/lib/finrb.rb +23 -0
- metadata +306 -0
data/lib/finrb/utils.rb
ADDED
@@ -0,0 +1,1171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/array/wrap'
|
4
|
+
require_relative 'decimal'
|
5
|
+
require 'bigdecimal'
|
6
|
+
require 'bigdecimal/newton'
|
7
|
+
|
8
|
+
include Newton
|
9
|
+
|
10
|
+
module Finrb
|
11
|
+
class Utils
|
12
|
+
class NlFunctionStub
|
13
|
+
attr_accessor :func
|
14
|
+
|
15
|
+
values = { eps: Finrb.config.eps, one: '1.0', two: '2.0', ten: '10.0', zero: '0.0' }
|
16
|
+
|
17
|
+
values.each do |key, value|
|
18
|
+
define_method key do
|
19
|
+
BigDecimal(value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def values(x)
|
24
|
+
@func.call(x)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Computing bank discount yield (BDY) for a T-bill
|
29
|
+
#
|
30
|
+
# @param d the dollar discount, which is equal to the difference between the face value of the bill and the purchase price
|
31
|
+
# @param f the face value (par value) of the bill
|
32
|
+
# @param t number of days remaining until maturity
|
33
|
+
# @export
|
34
|
+
# @examples
|
35
|
+
# bdy(d=1500,f=100000,t=120)
|
36
|
+
def self.bdy(d, f, t)
|
37
|
+
d = d.to_d
|
38
|
+
f = f.to_d
|
39
|
+
t = t.to_d
|
40
|
+
|
41
|
+
(360 * d / f / t)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Computing money market yield (MMY) for a T-bill
|
45
|
+
#
|
46
|
+
# @param bdy bank discount yield
|
47
|
+
# @param t number of days remaining until maturity
|
48
|
+
# @export
|
49
|
+
# @examples
|
50
|
+
# bdy2mmy(bdy=0.045,t=120)
|
51
|
+
def self.bdy2mmy(bdy, t)
|
52
|
+
bdy = bdy.to_d
|
53
|
+
t = t.to_d
|
54
|
+
|
55
|
+
(360 * bdy / (360 - (t * bdy)))
|
56
|
+
end
|
57
|
+
|
58
|
+
# cash ratio -- Liquidity ratios measure the firm's ability to satisfy its short-term obligations as they come due.
|
59
|
+
#
|
60
|
+
# @param cash cash
|
61
|
+
# @param ms marketable securities
|
62
|
+
# @param cl current liabilities
|
63
|
+
# @export
|
64
|
+
# @examples
|
65
|
+
# Finrb::Utils.cash_ratio(cash=3000,ms=2000,cl=2000)
|
66
|
+
def self.cash_ratio(cash, ms, cl)
|
67
|
+
cash = cash.to_d
|
68
|
+
ms = ms.to_d
|
69
|
+
cl = cl.to_d
|
70
|
+
|
71
|
+
((cash + ms) / cl)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Computing Coefficient of variation
|
75
|
+
#
|
76
|
+
# @param sd standard deviation
|
77
|
+
# @param avg average value
|
78
|
+
# @export
|
79
|
+
# @examples
|
80
|
+
# Finrb::Utils.coefficient_variation(sd=0.15,avg=0.39)
|
81
|
+
def self.coefficient_variation(sd, avg)
|
82
|
+
sd = sd.to_d
|
83
|
+
avg = avg.to_d
|
84
|
+
|
85
|
+
(sd / avg)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Cost of goods sold and ending inventory under three methods (FIFO,LIFO,Weighted average)
|
89
|
+
#
|
90
|
+
# @param uinv units of beginning inventory
|
91
|
+
# @param pinv prince of beginning inventory
|
92
|
+
# @param units nx1 vector of inventory units. inventory purchased ordered by time (from first to last)
|
93
|
+
# @param price nx1 vector of inventory price. same order as units
|
94
|
+
# @param sinv units of sold inventory
|
95
|
+
# @param method inventory methods: FIFO (first in first out, permitted under both US and IFRS), LIFO (late in first out, US only), WAC (weighted average cost,US and IFRS)
|
96
|
+
# @export
|
97
|
+
# @examples
|
98
|
+
# cogs(uinv=2,pinv=2,units=c(3,5),price=c(3,5),sinv=7,method="FIFO")
|
99
|
+
#
|
100
|
+
# cogs(uinv=2,pinv=2,units=c(3,5),price=c(3,5),sinv=7,method="LIFO")
|
101
|
+
#
|
102
|
+
# cogs(uinv=2,pinv=2,units=c(3,5),price=c(3,5),sinv=7,method="WAC")
|
103
|
+
def self.cogs(uinv, pinv, units, price, sinv, method = 'FIFO')
|
104
|
+
uinv = uinv.to_d
|
105
|
+
pinv = pinv.to_d
|
106
|
+
units = Array.wrap(units).map(&:to_d)
|
107
|
+
price = Array.wrap(price).map(&:to_d)
|
108
|
+
sinv = sinv.to_d
|
109
|
+
method = method.to_s
|
110
|
+
|
111
|
+
n = units.size
|
112
|
+
m = price.size
|
113
|
+
costOfGoods = 0
|
114
|
+
endingInventory = 0
|
115
|
+
if m == n
|
116
|
+
case method
|
117
|
+
when 'FIFO'
|
118
|
+
if sinv <= uinv
|
119
|
+
costOfGoods = sinv * pinv
|
120
|
+
endingInventory = (uinv - sinv) * pinv
|
121
|
+
(0...n).each do |i|
|
122
|
+
endingInventory += (units[i] * price[i])
|
123
|
+
end
|
124
|
+
else
|
125
|
+
costOfGoods = uinv * pinv
|
126
|
+
sinv -= uinv
|
127
|
+
(0...n).each do |i|
|
128
|
+
if sinv <= units[i]
|
129
|
+
costOfGoods += (sinv * price[i])
|
130
|
+
endingInventory = (units[i] - sinv) * price[i]
|
131
|
+
if i < n
|
132
|
+
temp = i + 1
|
133
|
+
(temp...n).each do |j|
|
134
|
+
endingInventory += (units[j] * price[j])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
sinv = 0
|
138
|
+
next
|
139
|
+
else
|
140
|
+
costOfGoods += (units[i] * price[i])
|
141
|
+
sinv -= units[i]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
raise(FinrbError, "Inventory is not enough to sell\n") if sinv.positive?
|
145
|
+
end
|
146
|
+
when 'WAC'
|
147
|
+
endingInventory = uinv * pinv
|
148
|
+
tu = uinv
|
149
|
+
(0...n).each do |i|
|
150
|
+
endingInventory += (units[i] * price[i])
|
151
|
+
tu += units[i]
|
152
|
+
end
|
153
|
+
if tu >= sinv
|
154
|
+
costOfGoods = endingInventory / tu * sinv
|
155
|
+
endingInventory = endingInventory / tu * (tu - sinv)
|
156
|
+
else
|
157
|
+
raise(FinrbError, "Inventory is not enough to sell\n")
|
158
|
+
end
|
159
|
+
|
160
|
+
when 'LIFO'
|
161
|
+
(n - 1).downto(0).each do |i|
|
162
|
+
if sinv <= units[i]
|
163
|
+
costOfGoods += (sinv * price[i])
|
164
|
+
endingInventory = (units[i] - sinv) * price[i]
|
165
|
+
if i > 1
|
166
|
+
temp = i - 1
|
167
|
+
(temp).downto(0).each do |j|
|
168
|
+
endingInventory += (units[j] * price[j])
|
169
|
+
end
|
170
|
+
end
|
171
|
+
endingInventory += (uinv * pinv)
|
172
|
+
sinv = 0
|
173
|
+
next
|
174
|
+
else
|
175
|
+
costOfGoods += (units[i] * price[i])
|
176
|
+
sinv -= units[i]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
if sinv.positive?
|
180
|
+
if sinv <= uinv
|
181
|
+
costOfGoods += (sinv * pinv)
|
182
|
+
endingInventory += ((uinv - sinv) * pinv)
|
183
|
+
else
|
184
|
+
raise(FinrbError, "Inventory is not enough to sell\n")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
else
|
190
|
+
raise(FinrbError, "length of units and price are not the same\n")
|
191
|
+
end
|
192
|
+
|
193
|
+
[costOfGoods, endingInventory]
|
194
|
+
end
|
195
|
+
|
196
|
+
# current ratio -- Liquidity ratios measure the firm's ability to satisfy its short-term obligations as they come due.
|
197
|
+
#
|
198
|
+
# @param ca current assets
|
199
|
+
# @param cl current liabilities
|
200
|
+
# @export
|
201
|
+
# @examples
|
202
|
+
# Finrb::Utils.current_ratio(ca=8000,cl=2000)
|
203
|
+
def self.current_ratio(ca, cl)
|
204
|
+
ca = ca.to_d
|
205
|
+
cl = cl.to_d
|
206
|
+
|
207
|
+
(ca / cl)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Depreciation Expense Recognition -- double-declining balance (DDB), the most common declining balance method, which applies two times the straight-line rate to the declining balance.
|
211
|
+
#
|
212
|
+
# @param cost cost of long-lived assets
|
213
|
+
# @param rv residual value of the long-lived assets at the end of its useful life. DDB does not explicitly use the asset's residual value in the calculations, but depreciation ends once the estimated residual value has been reached. If the asset is expected to have no residual value, the DB method will never fully depreciate it, so the DB method is typically changed to straight-line at some point in the asset's life.
|
214
|
+
# @param t length of the useful life
|
215
|
+
# @export
|
216
|
+
# @examples
|
217
|
+
# ddb(cost=1200,rv=200,t=5)
|
218
|
+
def self.ddb(cost, rv, t)
|
219
|
+
cost = cost.to_d
|
220
|
+
rv = rv.to_d
|
221
|
+
t = t.to_d
|
222
|
+
|
223
|
+
raise(FinrbError, 't should be larger than 1') if t < 2
|
224
|
+
|
225
|
+
ddb = [0] * t
|
226
|
+
ddb[0] = 2 * cost / t
|
227
|
+
if cost - ddb[0] <= rv
|
228
|
+
ddb[0] = cost - rv
|
229
|
+
else
|
230
|
+
cost -= ddb[0]
|
231
|
+
(1...t).each do |i|
|
232
|
+
ddb[i] = 2 * cost / t
|
233
|
+
if cost - ddb[i] <= rv
|
234
|
+
ddb[i] = cost - rv
|
235
|
+
break
|
236
|
+
else
|
237
|
+
cost -= ddb[i]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
{ t: (0...t).to_a, ddb: }
|
242
|
+
end
|
243
|
+
|
244
|
+
# debt ratio -- Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
245
|
+
#
|
246
|
+
# @param td total debt
|
247
|
+
# @param ta total assets
|
248
|
+
# @export
|
249
|
+
# @examples
|
250
|
+
# Finrb::Utils.debt_ratio(td=6000,ta=20000)
|
251
|
+
def self.debt_ratio(td, ta)
|
252
|
+
td = td.to_d
|
253
|
+
ta = ta.to_d
|
254
|
+
|
255
|
+
(td / ta)
|
256
|
+
end
|
257
|
+
|
258
|
+
# diluted Earnings Per Share
|
259
|
+
#
|
260
|
+
# @param ni net income
|
261
|
+
# @param pd preferred dividends
|
262
|
+
# @param cpd dividends on convertible preferred stock
|
263
|
+
# @param cdi interest on convertible debt
|
264
|
+
# @param tax tax rate
|
265
|
+
# @param w weighted average number of common shares outstanding
|
266
|
+
# @param cps shares from conversion of convertible preferred stock
|
267
|
+
# @param cds shares from conversion of convertible debt
|
268
|
+
# @param iss shares issuable from stock options
|
269
|
+
# @export
|
270
|
+
# @examples
|
271
|
+
# Finrb::Utils.diluted_eps(ni=115600,pd=10000,cdi=42000,tax=0.4,w=200000,cds=60000)
|
272
|
+
#
|
273
|
+
# Finrb::Utils.diluted_eps(ni=115600,pd=10000,cpd=10000,w=200000,cps=40000)
|
274
|
+
#
|
275
|
+
# Finrb::Utils.diluted_eps(ni=115600,pd=10000,w=200000,iss=2500)
|
276
|
+
#
|
277
|
+
# Finrb::Utils.diluted_eps(ni=115600,pd=10000,cpd=10000,cdi=42000,tax=0.4,w=200000,cps=40000,cds=60000,iss=2500)
|
278
|
+
def self.diluted_eps(ni, pd, w, cpd = 0, cdi = 0, tax = 0, cps = 0, cds = 0, iss = 0)
|
279
|
+
ni = ni.to_d
|
280
|
+
pd = pd.to_d
|
281
|
+
w = w.to_d
|
282
|
+
cpd = cpd.to_d
|
283
|
+
cdi = cdi.to_d
|
284
|
+
tax = tax.to_d
|
285
|
+
cps = cps.to_d
|
286
|
+
cds = cds.to_d
|
287
|
+
iss = iss.to_d
|
288
|
+
|
289
|
+
basic = (ni - pd) / w
|
290
|
+
diluted = (ni - pd + cpd + (cdi * (1 - tax))) / (w + cps + cds + iss)
|
291
|
+
diluted = (ni - pd + cpd) / (w + cps + iss) if diluted > basic
|
292
|
+
diluted
|
293
|
+
end
|
294
|
+
|
295
|
+
# Computing the rate of return for each period
|
296
|
+
#
|
297
|
+
# @param n number of periods
|
298
|
+
# @param pv present value
|
299
|
+
# @param fv future value
|
300
|
+
# @param pmt payment per period
|
301
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
302
|
+
# @param lower the lower end points of the rate of return to be searched.
|
303
|
+
# @param upper the upper end points of the rate of return to be searched.
|
304
|
+
# @importFrom stats uniroot
|
305
|
+
# @export
|
306
|
+
# @examples
|
307
|
+
# Finrb::Utils.discount_rate(n=5,pv=0,fv=600,pmt=-100,type=0)
|
308
|
+
def self.discount_rate(n, pv, fv, pmt, type = 0, lower = 0.0001, upper = 100)
|
309
|
+
n = n.to_d
|
310
|
+
pv = pv.to_d
|
311
|
+
fv = fv.to_d
|
312
|
+
pmt = pmt.to_d
|
313
|
+
type = type.to_d
|
314
|
+
lower = lower.to_d
|
315
|
+
upper = upper.to_d
|
316
|
+
|
317
|
+
nlfunc = NlFunctionStub.new
|
318
|
+
nlfunc.func = lambda do |x|
|
319
|
+
BigDecimal((Finrb::Utils.fv_simple(x[0],n,pv) + Finrb::Utils.fv_annuity(x[0],n,pmt,type) - fv).to_s)
|
320
|
+
end
|
321
|
+
|
322
|
+
root = [(upper - lower) / 2]
|
323
|
+
nlsolve(nlfunc, root)
|
324
|
+
root[0]
|
325
|
+
end
|
326
|
+
|
327
|
+
# Convert stated annual rate to the effective annual rate
|
328
|
+
#
|
329
|
+
# @param r stated annual rate
|
330
|
+
# @param m number of compounding periods per year
|
331
|
+
# @export
|
332
|
+
# @examples
|
333
|
+
# ear(r=0.12,m=12)
|
334
|
+
#
|
335
|
+
# ear(0.04,365)
|
336
|
+
def self.ear(r, m)
|
337
|
+
r = r.to_d
|
338
|
+
m = m.to_d
|
339
|
+
|
340
|
+
(((1 + (r / m))**m) - 1)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Convert stated annual rate to the effective annual rate with continuous compounding
|
344
|
+
#
|
345
|
+
# @param r stated annual rate
|
346
|
+
# @export
|
347
|
+
# @examples
|
348
|
+
# Finrb::Utils.ear_continuous(r=0.1)
|
349
|
+
#
|
350
|
+
# Finrb::Utils.ear_continuous(0.03)
|
351
|
+
def self.ear_continuous(r)
|
352
|
+
r = r.to_d
|
353
|
+
|
354
|
+
(r.to_d.exp - 1)
|
355
|
+
end
|
356
|
+
|
357
|
+
# bond-equivalent yield (BEY), 2 x the semiannual discount rate
|
358
|
+
#
|
359
|
+
# @param ear effective annual rate
|
360
|
+
# @export
|
361
|
+
# @examples
|
362
|
+
# ear2bey(ear=0.08)
|
363
|
+
def self.ear2bey(ear)
|
364
|
+
ear = ear.to_d
|
365
|
+
|
366
|
+
((((1 + ear)**0.5) - 1) * 2)
|
367
|
+
end
|
368
|
+
|
369
|
+
# Computing HPR, the holding period return
|
370
|
+
#
|
371
|
+
# @param ear effective annual rate
|
372
|
+
# @param t number of days remaining until maturity
|
373
|
+
# @export
|
374
|
+
# @examples
|
375
|
+
# ear2hpr(ear=0.05039,t=150)
|
376
|
+
def self.ear2hpr(ear, t)
|
377
|
+
ear = ear.to_d
|
378
|
+
t = t.to_d
|
379
|
+
|
380
|
+
(((1 + ear)**(t / 365)) - 1)
|
381
|
+
end
|
382
|
+
|
383
|
+
# Equivalent/proportional Interest Rates
|
384
|
+
# @description An interest rate to be applied n times p.a. can be converted to an equivalent rate to be applied p times p.a.
|
385
|
+
# @param r interest rate to be applied n times per year (r is annual rate!)
|
386
|
+
# @param n times that the interest rate r were compounded per year
|
387
|
+
# @param p times that the equivalent rate were compounded per year
|
388
|
+
# @param type equivalent interest rates ('e',default) or proportional interest rates ('p')
|
389
|
+
# @export
|
390
|
+
# @examples
|
391
|
+
# # monthly interest rat equivalent to 5% compounded per year
|
392
|
+
# Finrb::Utils.eir(r=0.05,n=1,p=12)
|
393
|
+
#
|
394
|
+
# # monthly interest rat equivalent to 5% compounded per half year
|
395
|
+
# Finrb::Utils.eir(r=0.05,n=2,p=12)
|
396
|
+
#
|
397
|
+
# # monthly interest rat equivalent to 5% compounded per quarter
|
398
|
+
# Finrb::Utils.eir(r=0.05,n=4,p=12)
|
399
|
+
#
|
400
|
+
# # annual interest rate equivalent to 5% compounded per month
|
401
|
+
# Finrb::Utils.eir(r=0.05,n=12,p=1)
|
402
|
+
# # this is equivalent to
|
403
|
+
# ear(r=0.05,m=12)
|
404
|
+
#
|
405
|
+
# # quarter interest rate equivalent to 5% compounded per year
|
406
|
+
# Finrb::Utils.eir(r=0.05,n=1,p=4)
|
407
|
+
#
|
408
|
+
# # quarter interest rate equivalent to 5% compounded per month
|
409
|
+
# Finrb::Utils.eir(r=0.05,n=12,p=4)
|
410
|
+
#
|
411
|
+
# # monthly proportional interest rate which is equivalent to a simple annual interest
|
412
|
+
# Finrb::Utils.eir(r=0.05,p=12,type='p')
|
413
|
+
def self.eir(r, n = 1, p = 12, type = 'e')
|
414
|
+
r = r.to_d
|
415
|
+
n = n.to_d
|
416
|
+
p = p.to_d
|
417
|
+
type = type.to_s
|
418
|
+
|
419
|
+
case type
|
420
|
+
when 'e'
|
421
|
+
eir = ((1 + (r / n))**(n / p)) - 1
|
422
|
+
when 'p'
|
423
|
+
eir = r / p
|
424
|
+
else
|
425
|
+
raise(FinrbError, "type must be 'e' or 'p'")
|
426
|
+
end
|
427
|
+
eir
|
428
|
+
end
|
429
|
+
|
430
|
+
# Basic Earnings Per Share
|
431
|
+
#
|
432
|
+
# @param ni net income
|
433
|
+
# @param pd preferred dividends
|
434
|
+
# @param w weighted average number of common shares outstanding
|
435
|
+
# @export
|
436
|
+
# @examples
|
437
|
+
# Finrb::Utils.eps(ni=10000,pd=1000,w=11000)
|
438
|
+
def self.eps(ni, pd, w)
|
439
|
+
ni = ni.to_d
|
440
|
+
pd = pd.to_d
|
441
|
+
w = w.to_d
|
442
|
+
|
443
|
+
((ni - pd) / w)
|
444
|
+
end
|
445
|
+
|
446
|
+
# financial leverage -- Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
447
|
+
#
|
448
|
+
# @param te total equity
|
449
|
+
# @param ta total assets
|
450
|
+
# @export
|
451
|
+
# @examples
|
452
|
+
# Finrb::Utils.financial_leverage(te=16000,ta=20000)
|
453
|
+
def self.financial_leverage(te, ta)
|
454
|
+
te = te.to_d
|
455
|
+
ta = ta.to_d
|
456
|
+
|
457
|
+
(ta / te)
|
458
|
+
end
|
459
|
+
|
460
|
+
# Estimate future value (fv)
|
461
|
+
#
|
462
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
463
|
+
# @param n number of periods
|
464
|
+
# @param pv present value
|
465
|
+
# @param pmt payment per period
|
466
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
467
|
+
# @export
|
468
|
+
# @examples
|
469
|
+
# fv(r=0.07,n=10,pv=1000,pmt=10)
|
470
|
+
def self.fv(r, n, pv = 0, pmt = 0, type = 0)
|
471
|
+
r = r.to_d
|
472
|
+
n = n.to_d
|
473
|
+
pv = pv.to_d
|
474
|
+
pmt = pmt.to_d
|
475
|
+
type = type.to_d
|
476
|
+
|
477
|
+
if type != 0 && type != 1
|
478
|
+
raise(FinrbError, 'Error: type should be 0 or 1!')
|
479
|
+
else
|
480
|
+
(Finrb::Utils.fv_simple(r, n, pv) + Finrb::Utils.fv_annuity(r, n, pmt, type))
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
# Estimate future value of an annuity
|
485
|
+
#
|
486
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
487
|
+
# @param n number of periods
|
488
|
+
# @param pmt payment per period
|
489
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
490
|
+
# @export
|
491
|
+
# @examples
|
492
|
+
# Finrb::Utils.fv_annuity(0.03,12,-1000)
|
493
|
+
#
|
494
|
+
# Finrb::Utils.fv_annuity(r=0.03,n=12,pmt=-1000,type=1)
|
495
|
+
def self.fv_annuity(r, n, pmt, type = 0)
|
496
|
+
r = r.to_d
|
497
|
+
n = n.to_d
|
498
|
+
pmt = pmt.to_d
|
499
|
+
type = type.to_d
|
500
|
+
|
501
|
+
if type != 0 && type != 1
|
502
|
+
raise(FinrbError, 'Error: type should be 0 or 1!')
|
503
|
+
else
|
504
|
+
(pmt / r * (((1 + r)**n) - 1)) * ((1 + r)**type) * -1
|
505
|
+
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# Estimate future value (fv) of a single sum
|
510
|
+
#
|
511
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
512
|
+
# @param n number of periods
|
513
|
+
# @param pv present value
|
514
|
+
# @export
|
515
|
+
# @examples
|
516
|
+
# Finrb::Utils.fv_simple(0.08,10,-300)
|
517
|
+
#
|
518
|
+
# Finrb::Utils.fv_simple(r=0.04,n=20,pv=-50000)
|
519
|
+
def self.fv_simple(r, n, pv)
|
520
|
+
r = r.to_d
|
521
|
+
n = n.to_d
|
522
|
+
pv = pv.to_d
|
523
|
+
|
524
|
+
((pv * ((1 + r)**n)) * -1)
|
525
|
+
end
|
526
|
+
|
527
|
+
# Computing the future value of an uneven cash flow series
|
528
|
+
#
|
529
|
+
# @param r stated annual rate
|
530
|
+
# @param cf uneven cash flow
|
531
|
+
# @export
|
532
|
+
# @examples
|
533
|
+
# Finrb::Utils.fv_uneven(r=0.1, cf=c(-1000, -500, 0, 4000, 3500, 2000))
|
534
|
+
def self.fv_uneven(r, cf)
|
535
|
+
r = r.to_d
|
536
|
+
cf = Array.wrap(cf).map(&:to_d)
|
537
|
+
|
538
|
+
m = cf.size
|
539
|
+
sum = 0
|
540
|
+
(0...m).each do |i|
|
541
|
+
n = m - i
|
542
|
+
sum += Finrb::Utils.fv_simple(r, n, cf[i])
|
543
|
+
end
|
544
|
+
sum
|
545
|
+
end
|
546
|
+
|
547
|
+
# Geometric mean return
|
548
|
+
#
|
549
|
+
# @param r returns over multiple periods
|
550
|
+
# @export
|
551
|
+
# @examples
|
552
|
+
# Finrb::Utils.geometric_mean(r=c(-0.0934, 0.2345, 0.0892))
|
553
|
+
def self.geometric_mean(r)
|
554
|
+
r = Array.wrap(r).map(&:to_d)
|
555
|
+
|
556
|
+
rs = r + 1
|
557
|
+
((rs.reduce(:*)**(1 / rs.size)) - 1)
|
558
|
+
end
|
559
|
+
|
560
|
+
# gross profit margin -- Evaluate a company's financial performance
|
561
|
+
#
|
562
|
+
# @param gp gross profit, equal to revenue minus cost of goods sold (cogs)
|
563
|
+
# @param rv revenue (sales)
|
564
|
+
# @export
|
565
|
+
# @examples
|
566
|
+
# gpm(gp=1000,rv=20000)
|
567
|
+
def self.gpm(gp, rv)
|
568
|
+
gp = gp.to_d
|
569
|
+
rv = rv.to_d
|
570
|
+
|
571
|
+
(gp / rv)
|
572
|
+
end
|
573
|
+
|
574
|
+
# harmonic mean, average price
|
575
|
+
# @param p price over multiple periods
|
576
|
+
# @export
|
577
|
+
# @examples
|
578
|
+
# Finrb::Utils.harmonic_mean(p=c(8,9,10))
|
579
|
+
def self.harmonic_mean(p)
|
580
|
+
p = Array.wrap(p).map(&:to_d)
|
581
|
+
|
582
|
+
(1 / (p.sum { |val| 1 / val } / p.size))
|
583
|
+
end
|
584
|
+
|
585
|
+
# Computing HPR, the holding period return
|
586
|
+
#
|
587
|
+
# @param ev ending value
|
588
|
+
# @param bv beginning value
|
589
|
+
# @param cfr cash flow received
|
590
|
+
# @export
|
591
|
+
# @examples
|
592
|
+
# hpr(ev=33,bv=30,cfr=0.5)
|
593
|
+
def self.hpr(ev, bv, cfr = 0)
|
594
|
+
ev = ev.to_d
|
595
|
+
bv = bv.to_d
|
596
|
+
cfr = cfr.to_d
|
597
|
+
|
598
|
+
((ev - bv + cfr) / bv)
|
599
|
+
end
|
600
|
+
|
601
|
+
# bond-equivalent yield (BEY), 2 x the semiannual discount rate
|
602
|
+
#
|
603
|
+
# @param hpr holding period return
|
604
|
+
# @param t number of month remaining until maturity
|
605
|
+
# @export
|
606
|
+
# @examples
|
607
|
+
# hpr2bey(hpr=0.02,t=3)
|
608
|
+
def self.hpr2bey(hpr, t)
|
609
|
+
hpr = hpr.to_d
|
610
|
+
t = t.to_d
|
611
|
+
|
612
|
+
((((1 + hpr)**(6 / t)) - 1) * 2)
|
613
|
+
end
|
614
|
+
|
615
|
+
# Convert holding period return to the effective annual rate
|
616
|
+
#
|
617
|
+
# @param hpr holding period return
|
618
|
+
# @param t number of days remaining until maturity
|
619
|
+
# @export
|
620
|
+
# @examples
|
621
|
+
# hpr2ear(hpr=0.015228,t=120)
|
622
|
+
def self.hpr2ear(hpr, t)
|
623
|
+
hpr = hpr.to_d
|
624
|
+
t = t.to_d
|
625
|
+
|
626
|
+
(((1 + hpr)**(365 / t)) - 1)
|
627
|
+
end
|
628
|
+
|
629
|
+
# Computing money market yield (MMY) for a T-bill
|
630
|
+
#
|
631
|
+
# @param hpr holding period return
|
632
|
+
# @param t number of days remaining until maturity
|
633
|
+
# @export
|
634
|
+
# @examples
|
635
|
+
# hpr2mmy(hpr=0.01523,t=120)
|
636
|
+
def self.hpr2mmy(hpr, t)
|
637
|
+
hpr = hpr.to_d
|
638
|
+
t = t.to_d
|
639
|
+
|
640
|
+
(360 * hpr / t)
|
641
|
+
end
|
642
|
+
|
643
|
+
# Computing IRR, the internal rate of return
|
644
|
+
#
|
645
|
+
# @param cf cash flow,the first cash flow is the initial outlay
|
646
|
+
# @importFrom stats uniroot
|
647
|
+
# @export
|
648
|
+
# @examples
|
649
|
+
# irr(cf=c(-5, 1.6, 2.4, 2.8))
|
650
|
+
def self.irr(cf)
|
651
|
+
cf = Array.wrap(cf).map(&:to_d)
|
652
|
+
|
653
|
+
n = cf.size
|
654
|
+
subcf = cf.drop(1)
|
655
|
+
|
656
|
+
nlfunc = NlFunctionStub.new
|
657
|
+
nlfunc.func = lambda do |x|
|
658
|
+
BigDecimal((-1 * Finrb::Utils.pv_uneven(x[0], subcf) + cf[0]).to_s)
|
659
|
+
end
|
660
|
+
|
661
|
+
root = [0]
|
662
|
+
nlsolve(nlfunc, root)
|
663
|
+
root[0]
|
664
|
+
end
|
665
|
+
|
666
|
+
# Computing IRR, the internal rate of return
|
667
|
+
# @description This function is the same as irr but can calculate negative value. This function may take a very long time. You can use larger cutoff and larger step to get a less precision irr first. Then based on the result, change from and to, to narrow down the interval, and use a smaller step to get a more precision irr.
|
668
|
+
# @param cf cash flow,the first cash flow is the initial outlay
|
669
|
+
# @param cutoff threshold to take npv as zero
|
670
|
+
# @param from smallest irr to try
|
671
|
+
# @param to largest irr to try
|
672
|
+
# @param step increment of the irr
|
673
|
+
# @export
|
674
|
+
# @examples
|
675
|
+
# irr2(cf=c(-5, 1.6, 2.4, 2.8))
|
676
|
+
# irr2(cf=c(-200, 50, 60, -70, 30, 20))
|
677
|
+
def self.irr2(cf, cutoff = 0.1, from = -1, to = 10, step = 0.000001)
|
678
|
+
cf = Array.wrap(cf).map(&:to_d)
|
679
|
+
cutoff = cutoff.to_d
|
680
|
+
from = from.to_d
|
681
|
+
to = to.to_d
|
682
|
+
step = step.to_d
|
683
|
+
|
684
|
+
r0 = nil
|
685
|
+
n = cf.size
|
686
|
+
from.step((to - 1), step).each do |r|
|
687
|
+
npv = cf[0]
|
688
|
+
(1...n).each do |i|
|
689
|
+
npv += (cf[i] / ((1 + r)**(i - 1)))
|
690
|
+
end
|
691
|
+
next if npv.nil?
|
692
|
+
|
693
|
+
if npv.abs < cutoff
|
694
|
+
r0 = r
|
695
|
+
break
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
if r0.nil?
|
700
|
+
raise(
|
701
|
+
FinrbError,
|
702
|
+
'can not find irr in the given interval, you can try smaller step, and/or larger to, and/or larger cutoff'
|
703
|
+
)
|
704
|
+
end
|
705
|
+
|
706
|
+
r0
|
707
|
+
end
|
708
|
+
|
709
|
+
# calculate the net increase in common shares from the potential exercise of stock options or warrants
|
710
|
+
#
|
711
|
+
# @param amp average market price over the year
|
712
|
+
# @param ep exercise price of the options or warrants
|
713
|
+
# @param n number of common shares that the options and warrants can be convened into
|
714
|
+
# @export
|
715
|
+
# @examples
|
716
|
+
# iss(amp=20,ep=15,n=10000)
|
717
|
+
def self.iss(amp, ep, n)
|
718
|
+
amp = amp.to_d
|
719
|
+
ep = ep.to_d
|
720
|
+
n = n.to_d
|
721
|
+
|
722
|
+
if amp > ep
|
723
|
+
((amp - ep) * n / amp)
|
724
|
+
else
|
725
|
+
raise(FinrbError, 'amp must larger than ep')
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
# long-term debt-to-equity -- Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
730
|
+
#
|
731
|
+
# @param ltd long-term debt
|
732
|
+
# @param te total equity
|
733
|
+
# @export
|
734
|
+
# @examples
|
735
|
+
# Finrb::Utils.lt_d2e(ltd=8000,te=20000)
|
736
|
+
def self.lt_d2e(ltd, te)
|
737
|
+
ltd = ltd.to_d
|
738
|
+
te = te.to_d
|
739
|
+
|
740
|
+
(ltd / te)
|
741
|
+
end
|
742
|
+
|
743
|
+
# Computing HPR, the holding period return
|
744
|
+
#
|
745
|
+
# @param mmy money market yield
|
746
|
+
# @param t number of days remaining until maturity
|
747
|
+
# @export
|
748
|
+
# @examples
|
749
|
+
# mmy2hpr(mmy=0.04898,t=150)
|
750
|
+
def self.mmy2hpr(mmy, t)
|
751
|
+
mmy = mmy.to_d
|
752
|
+
t = t.to_d
|
753
|
+
|
754
|
+
(mmy * t / 360)
|
755
|
+
end
|
756
|
+
|
757
|
+
# Estimate the number of periods
|
758
|
+
#
|
759
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
760
|
+
# @param pv present value
|
761
|
+
# @param fv future value
|
762
|
+
# @param pmt payment per period
|
763
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
764
|
+
# @export
|
765
|
+
# @examples
|
766
|
+
# Finrb::Utils.n_period(0.1,-10000,60000000,-50000,0)
|
767
|
+
#
|
768
|
+
# Finrb::Utils.n_period(r=0.1,pv=-10000,fv=60000000,pmt=-50000,type=1)
|
769
|
+
def self.n_period(r, pv, fv, pmt, type = 0)
|
770
|
+
r = r.to_d
|
771
|
+
pv = pv.to_d
|
772
|
+
fv = fv.to_d
|
773
|
+
pmt = pmt.to_d
|
774
|
+
type = type.to_d
|
775
|
+
|
776
|
+
if type != 0 && type != 1
|
777
|
+
raise(FinrbError, 'Error: type should be 0 or 1!')
|
778
|
+
else
|
779
|
+
(-1 * ((fv * r) - (pmt * ((1 + r)**type))) / ((pv * r) + (pmt * ((1 + r)**type)))).to_d.log / (1 + r).to_d.log
|
780
|
+
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
# net profit margin -- Evaluate a company's financial performance
|
785
|
+
#
|
786
|
+
# @param ni net income
|
787
|
+
# @param rv revenue (sales)
|
788
|
+
# @export
|
789
|
+
# @examples
|
790
|
+
# npm(ni=8000,rv=20000)
|
791
|
+
def self.npm(ni, rv)
|
792
|
+
ni = ni.to_d
|
793
|
+
rv = rv.to_d
|
794
|
+
|
795
|
+
(ni / rv)
|
796
|
+
end
|
797
|
+
|
798
|
+
# Computing NPV, the PV of the cash flows less the initial (time = 0) outlay
|
799
|
+
#
|
800
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
801
|
+
# @param cf cash flow,the first cash flow is the initial outlay
|
802
|
+
# @export
|
803
|
+
# @examples
|
804
|
+
# npv(r=0.12, cf=c(-5, 1.6, 2.4, 2.8))
|
805
|
+
def self.npv(r, cf)
|
806
|
+
r = r.to_d
|
807
|
+
cf = Array.wrap(cf).map(&:to_d)
|
808
|
+
|
809
|
+
n = cf.size
|
810
|
+
subcf = cf.drop(1)
|
811
|
+
((-1 * Finrb::Utils.pv_uneven(r, subcf)) + cf[0])
|
812
|
+
end
|
813
|
+
|
814
|
+
# Estimate period payment
|
815
|
+
#
|
816
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
817
|
+
# @param n number of periods
|
818
|
+
# @param pv present value
|
819
|
+
# @param fv future value
|
820
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
821
|
+
# @export
|
822
|
+
# @examples
|
823
|
+
# pmt(0.08,10,-1000,10)
|
824
|
+
#
|
825
|
+
# pmt(r=0.08,n=10,pv=-1000,fv=0)
|
826
|
+
#
|
827
|
+
# pmt(0.08,10,-1000,10,1)
|
828
|
+
def self.pmt(r, n, pv, fv, type = 0)
|
829
|
+
r = r.to_d
|
830
|
+
n = n.to_d
|
831
|
+
pv = pv.to_d
|
832
|
+
fv = fv.to_d
|
833
|
+
type = type.to_d
|
834
|
+
|
835
|
+
if type != 0 && type != 1
|
836
|
+
raise(FinrbError, 'Error: type should be 0 or 1!')
|
837
|
+
else
|
838
|
+
(pv + (fv / ((1 + r)**n))) * r / (1 - (1 / ((1 + r)**n))) * -1 * ((1 + r)**(-1 * type))
|
839
|
+
|
840
|
+
end
|
841
|
+
end
|
842
|
+
|
843
|
+
# Estimate present value (pv)
|
844
|
+
#
|
845
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
846
|
+
# @param n number of periods
|
847
|
+
# @param fv future value
|
848
|
+
# @param pmt payment per period
|
849
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
850
|
+
# @export
|
851
|
+
# @examples
|
852
|
+
# pv(0.07,10,1000,10)
|
853
|
+
#
|
854
|
+
# pv(r=0.05,n=20,fv=1000,pmt=10,type=1)
|
855
|
+
def self.pv(r, n, fv = 0, pmt = 0, type = 0)
|
856
|
+
r = r.to_d
|
857
|
+
n = n.to_d
|
858
|
+
fv = fv.to_d
|
859
|
+
pmt = pmt.to_d
|
860
|
+
type = type.to_d
|
861
|
+
|
862
|
+
if type != 0 && type != 1
|
863
|
+
raise(FinrbError, 'Error: type should be 0 or 1!')
|
864
|
+
else
|
865
|
+
Finrb::Utils.pv_simple(r, n, fv) + Finrb::Utils.pv_annuity(r, n, pmt, type)
|
866
|
+
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
# Estimate present value (pv) of an annuity
|
871
|
+
#
|
872
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
873
|
+
# @param n number of periods
|
874
|
+
# @param pmt payment per period
|
875
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
876
|
+
# @export
|
877
|
+
# @examples
|
878
|
+
# Finrb::Utils.pv_annuity(0.03,12,1000)
|
879
|
+
#
|
880
|
+
# Finrb::Utils.pv_annuity(r=0.0425,n=3,pmt=30000)
|
881
|
+
def self.pv_annuity(r, n, pmt, type = 0)
|
882
|
+
r = r.to_d
|
883
|
+
n = n.to_d
|
884
|
+
pmt = pmt.to_d
|
885
|
+
type = type.to_d
|
886
|
+
|
887
|
+
if type != 0 && type != 1
|
888
|
+
raise(FinrbError, 'Error: type should be 0 or 1!')
|
889
|
+
else
|
890
|
+
(pmt / r * (1 - (1 / ((1 + r)**n)))) * ((1 + r)**type) * -1
|
891
|
+
|
892
|
+
end
|
893
|
+
end
|
894
|
+
|
895
|
+
# Estimate present value of a perpetuity
|
896
|
+
#
|
897
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
898
|
+
# @param g growth rate of perpetuity
|
899
|
+
# @param pmt payment per period
|
900
|
+
# @param type payments occur at the end of each period (type=0); payments occur at the beginning of each period (type=1)
|
901
|
+
# @export
|
902
|
+
# @examples
|
903
|
+
# Finrb::Utils.pv_perpetuity(r=0.1,pmt=1000,g=0.02)
|
904
|
+
#
|
905
|
+
# Finrb::Utils.pv_perpetuity(r=0.1,pmt=1000,type=1)
|
906
|
+
#
|
907
|
+
# Finrb::Utils.pv_perpetuity(r=0.1,pmt=1000)
|
908
|
+
def self.pv_perpetuity(r, pmt, g = 0, type = 0)
|
909
|
+
r = r.to_d
|
910
|
+
pmt = pmt.to_d
|
911
|
+
g = g.to_d
|
912
|
+
type = type.to_d
|
913
|
+
|
914
|
+
if type != 0 && type != 1
|
915
|
+
raise(FinrbError, 'Error: type should be 0 or 1!')
|
916
|
+
elsif g >= r
|
917
|
+
raise(FinrbError, 'Error: g is not smaller than r!')
|
918
|
+
else
|
919
|
+
(pmt / (r - g)) * ((1 + r)**type) * -1
|
920
|
+
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
# Estimate present value (pv) of a single sum
|
925
|
+
#
|
926
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
927
|
+
# @param n number of periods
|
928
|
+
# @param fv future value
|
929
|
+
# @export
|
930
|
+
# @examples
|
931
|
+
# Finrb::Utils.pv_simple(0.07,10,100)
|
932
|
+
#
|
933
|
+
# Finrb::Utils.pv_simple(r=0.03,n=3,fv=1000)
|
934
|
+
def self.pv_simple(r, n, fv)
|
935
|
+
r = r.to_d
|
936
|
+
n = n.to_d
|
937
|
+
fv = fv.to_d
|
938
|
+
|
939
|
+
((fv / ((1 + r)**n)) * -1)
|
940
|
+
end
|
941
|
+
|
942
|
+
# Computing the present value of an uneven cash flow series
|
943
|
+
#
|
944
|
+
# @param r discount rate, or the interest rate at which the amount will be compounded each period
|
945
|
+
# @param cf uneven cash flow
|
946
|
+
# @export
|
947
|
+
# @examples
|
948
|
+
# Finrb::Utils.pv_uneven(r=0.1, cf=c(-1000, -500, 0, 4000, 3500, 2000))
|
949
|
+
def self.pv_uneven(r, cf)
|
950
|
+
r = r.to_d
|
951
|
+
cf = Array.wrap(cf).map(&:to_d)
|
952
|
+
|
953
|
+
n = cf.size
|
954
|
+
sum = 0
|
955
|
+
(0...n).each do |i|
|
956
|
+
sum += Finrb::Utils.pv_simple(r, i, cf[i])
|
957
|
+
end
|
958
|
+
sum
|
959
|
+
end
|
960
|
+
|
961
|
+
# quick ratio -- Liquidity ratios measure the firm's ability to satisfy its short-term obligations as they come due.
|
962
|
+
#
|
963
|
+
# @param cash cash
|
964
|
+
# @param ms marketable securities
|
965
|
+
# @param rc receivables
|
966
|
+
# @param cl current liabilities
|
967
|
+
# @export
|
968
|
+
# @examples
|
969
|
+
# Finrb::Utils.quick_ratio(cash=3000,ms=2000,rc=1000,cl=2000)
|
970
|
+
def self.quick_ratio(cash, ms, rc, cl)
|
971
|
+
cash = cash.to_d
|
972
|
+
ms = ms.to_d
|
973
|
+
rc = rc.to_d
|
974
|
+
cl = cl.to_d
|
975
|
+
|
976
|
+
((cash + ms + rc) / cl)
|
977
|
+
end
|
978
|
+
|
979
|
+
# Convert a given norminal rate to a continuous compounded rate
|
980
|
+
#
|
981
|
+
# @param r norminal rate
|
982
|
+
# @param m number of times compounded each year
|
983
|
+
# @export
|
984
|
+
# @examples
|
985
|
+
# Finrb::Utils.r_continuous(r=0.03,m=4)
|
986
|
+
def self.r_continuous(r, m)
|
987
|
+
r = r.to_d
|
988
|
+
m = m.to_d
|
989
|
+
|
990
|
+
(m * (1 + (r / m)).to_d.log)
|
991
|
+
end
|
992
|
+
|
993
|
+
# Convert a given continuous compounded rate to a norminal rate
|
994
|
+
#
|
995
|
+
# @param rc continuous compounded rate
|
996
|
+
# @param m number of desired times compounded each year
|
997
|
+
# @export
|
998
|
+
# @examples
|
999
|
+
# Finrb::Utils.r_norminal(0.03,1)
|
1000
|
+
#
|
1001
|
+
# Finrb::Utils.r_norminal(rc=0.03,m=4)
|
1002
|
+
def self.r_norminal(rc, m)
|
1003
|
+
rc = rc.to_d
|
1004
|
+
m = m.to_d
|
1005
|
+
|
1006
|
+
(m * ((rc / m).to_d.exp - 1))
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
# Rate of return for a perpetuity
|
1010
|
+
#
|
1011
|
+
# @param pmt payment per period
|
1012
|
+
# @param pv present value
|
1013
|
+
# @export
|
1014
|
+
# @examples
|
1015
|
+
# Finrb::Utils.r_perpetuity(pmt=4.5,pv=-75)
|
1016
|
+
def self.r_perpetuity(pmt, pv)
|
1017
|
+
pmt = pmt.to_d
|
1018
|
+
pv = pv.to_d
|
1019
|
+
|
1020
|
+
(-1 * pmt / pv)
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
# Computing Sampling error
|
1024
|
+
#
|
1025
|
+
# @param sm sample mean
|
1026
|
+
# @param mu population mean
|
1027
|
+
# @export
|
1028
|
+
# @examples
|
1029
|
+
# Finrb::Utils.sampling_error(sm=0.45, mu=0.5)
|
1030
|
+
def self.sampling_error(sm, mu)
|
1031
|
+
sm = sm.to_d
|
1032
|
+
mu = mu.to_d
|
1033
|
+
|
1034
|
+
(sm - mu)
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# Computing Roy's safety-first ratio
|
1038
|
+
#
|
1039
|
+
# @param rp portfolio return
|
1040
|
+
# @param rl threshold level return
|
1041
|
+
# @param sd standard deviation of portfolio retwns
|
1042
|
+
# @export
|
1043
|
+
# @examples
|
1044
|
+
# Finrb::Utils.sf_ratio(rp=0.09,rl=0.03,sd=0.12)
|
1045
|
+
def self.sf_ratio(rp, rl, sd)
|
1046
|
+
rp = rp.to_d
|
1047
|
+
rl = rl.to_d
|
1048
|
+
sd = sd.to_d
|
1049
|
+
|
1050
|
+
((rp - rl) / sd)
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
# Computing Sharpe Ratio
|
1054
|
+
#
|
1055
|
+
# @param rp portfolio return
|
1056
|
+
# @param rf risk-free return
|
1057
|
+
# @param sd standard deviation of portfolio retwns
|
1058
|
+
# @export
|
1059
|
+
# @examples
|
1060
|
+
# Finrb::Utils.sharpe_ratio(rp=0.038,rf=0.015,sd=0.07)
|
1061
|
+
def self.sharpe_ratio(rp, rf, sd)
|
1062
|
+
rp = rp.to_d
|
1063
|
+
rf = rf.to_d
|
1064
|
+
sd = sd.to_d
|
1065
|
+
|
1066
|
+
((rp - rf) / sd)
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
# Depreciation Expense Recognition -- Straight-line depreciation (SL) allocates an equal amount of depreciation each year over the asset's useful life
|
1070
|
+
#
|
1071
|
+
# @param cost cost of long-lived assets
|
1072
|
+
# @param rv residual value of the long-lived assets at the end of its useful life
|
1073
|
+
# @param t length of the useful life
|
1074
|
+
# @export
|
1075
|
+
# @examples
|
1076
|
+
# slde(cost=1200,rv=200,t=5)
|
1077
|
+
def self.slde(cost, rv, t)
|
1078
|
+
cost = cost.to_d
|
1079
|
+
rv = rv.to_d
|
1080
|
+
t = t.to_d
|
1081
|
+
|
1082
|
+
((cost - rv) / t)
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
# total debt-to-equity -- Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
1086
|
+
#
|
1087
|
+
# @param td total debt
|
1088
|
+
# @param te total equity
|
1089
|
+
# @export
|
1090
|
+
# @examples
|
1091
|
+
# Finrb::Utils.total_d2e(td=6000,te=20000)
|
1092
|
+
def self.total_d2e(td, te)
|
1093
|
+
td = td.to_d
|
1094
|
+
te = te.to_d
|
1095
|
+
|
1096
|
+
(td / te)
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
# Computing TWRR, the time-weighted rate of return
|
1100
|
+
#
|
1101
|
+
# @param ev ordered ending value list
|
1102
|
+
# @param bv ordered beginning value list
|
1103
|
+
# @param cfr ordered cash flow received list
|
1104
|
+
# @export
|
1105
|
+
# @examples
|
1106
|
+
# twrr(ev=c(120,260),bv=c(100,240),cfr=c(2,4))
|
1107
|
+
def self.twrr(ev, bv, cfr)
|
1108
|
+
ev = Array.wrap(ev).map(&:to_d)
|
1109
|
+
bv = Array.wrap(bv).map(&:to_d)
|
1110
|
+
cfr = Array.wrap(cfr).map(&:to_d)
|
1111
|
+
|
1112
|
+
r = ev.size
|
1113
|
+
s = bv.size
|
1114
|
+
t = cfr.size
|
1115
|
+
wr = 1
|
1116
|
+
if r != s || r != t || s != t
|
1117
|
+
raise(FinrbError, 'Different number of values!')
|
1118
|
+
else
|
1119
|
+
(0...r).each do |i|
|
1120
|
+
wr *= (hpr(ev[i], bv[i], cfr[i]) + 1)
|
1121
|
+
end
|
1122
|
+
((wr**(1 / r)) - 1)
|
1123
|
+
end
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
# calculate weighted average shares -- weighted average number of common shares
|
1127
|
+
#
|
1128
|
+
# @param ns n x 1 vector vector of number of shares
|
1129
|
+
# @param nm n x 1 vector vector of number of months relate to ns
|
1130
|
+
# @export
|
1131
|
+
# @examples
|
1132
|
+
# s=c(10000,2000);m=c(12,6);was(ns=s,nm=m)
|
1133
|
+
#
|
1134
|
+
# s=c(11000,4400,-3000);m=c(12,9,4);was(ns=s,nm=m)
|
1135
|
+
def self.was(ns, nm)
|
1136
|
+
ns = Array.wrap(ns).map(&:to_d)
|
1137
|
+
nm = Array.wrap(nm).map(&:to_d)
|
1138
|
+
|
1139
|
+
m = ns.size
|
1140
|
+
n = nm.size
|
1141
|
+
sum = 0
|
1142
|
+
if m == n
|
1143
|
+
(0...m).each do |i|
|
1144
|
+
sum += (ns[i] * nm[i])
|
1145
|
+
end
|
1146
|
+
else
|
1147
|
+
raise(FinrbError, 'length of ns and nm must be equal')
|
1148
|
+
end
|
1149
|
+
sum /= 12
|
1150
|
+
sum
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
# Weighted mean as a portfolio return
|
1154
|
+
#
|
1155
|
+
# @param r returns of the individual assets in the portfolio
|
1156
|
+
# @param w corresponding weights associated with each of the individual assets
|
1157
|
+
# @export
|
1158
|
+
# @examples
|
1159
|
+
# wpr(r=c(0.12, 0.07, 0.03),w=c(0.5,0.4,0.1))
|
1160
|
+
def self.wpr(r, w)
|
1161
|
+
r = Array.wrap(r).map(&:to_d)
|
1162
|
+
w = Array.wrap(w).map(&:to_d)
|
1163
|
+
|
1164
|
+
if w.sum != 1
|
1165
|
+
puts('sum of weights is NOT equal to 1!') # TODO: need to change
|
1166
|
+
else
|
1167
|
+
end
|
1168
|
+
r.zip(w).sum { |arr| arr.reduce(:*) }
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
end
|