finrb 0.1.12 → 1.0.0

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