firstact 0.2.0__tar.gz

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.
firstact-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 firstact
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include requirements.txt
3
+ recursive-include firstact/data *.csv
4
+ recursive-include tests *.py
@@ -0,0 +1,374 @@
1
+ Metadata-Version: 2.4
2
+ Name: firstact
3
+ Version: 0.2.0
4
+ Summary: Librería de matemáticas actuariales básicas: tablas de mortalidad, seguros, anualidades y primas netas.
5
+ Author: firstact
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.8
8
+ Classifier: Programming Language :: Python :: 3.9
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: numpy>=1.21
18
+ Requires-Dist: pandas>=1.3
19
+ Dynamic: author
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: license-file
24
+ Dynamic: requires-dist
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+ # firstact
29
+
30
+ Librería de matemáticas actuariales básicas en Python.
31
+ Basada en la **Illustrative Life Table (ILT)** de la SOA a $i = 6\%$.
32
+
33
+ ---
34
+
35
+ ## Instalación
36
+
37
+ ```bash
38
+ pip install -r requirements.txt
39
+ pip install -e .
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Módulos
45
+
46
+ | Módulo | Descripción |
47
+ |---|---|
48
+ | `MortalityTable` | Tablas de mortalidad: lx, qx, px, dx, npx, nqx, ex |
49
+ | `Insurance` | Seguros de vida discretos (K_x, pago al final del año de muerte) |
50
+ | `Annuity` | Anualidades contingentes discretas |
51
+ | `Premium` | Primas netas niveladas y reservas prospectivas |
52
+ | `utils` | Conversiones discreto ↔ continuo bajo UDD |
53
+ | `exceptions` | Excepciones propias de la librería |
54
+
55
+ ---
56
+
57
+ ## Uso rápido
58
+
59
+ ```python
60
+ from firstact import MortalityTable, Insurance, Annuity, Premium
61
+ from firstact.utils import to_cont, to_disc
62
+
63
+ t = MortalityTable.ilt() # ILT incluida (SOA)
64
+ ins = Insurance(t, i=0.06)
65
+ ann = Annuity(t, i=0.06)
66
+ pre = Premium(t, i=0.06)
67
+ ```
68
+
69
+ También puedes cargar tu propia tabla desde un CSV con columnas `x`, `lx`, `qx`:
70
+
71
+ ```python
72
+ t = MortalityTable("mi_tabla.csv")
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Tabla de Mortalidad
78
+
79
+ ### Nota sobre edades en la ILT
80
+
81
+ La ILT tiene edades **0, 5, 10, 15** y luego **20–110 consecutivas**.
82
+ Las funciones de consulta directa (`lx`, `qx`, `npx`, etc.) funcionan para cualquier edad disponible.
83
+ Los cálculos de seguros y anualidades **requieren x >= 20** porque suman año por año.
84
+
85
+ ### Funciones disponibles
86
+
87
+ ```python
88
+ t.lx(35) # l_35 — número de vivos
89
+ t.qx(35) # q_35 — probabilidad de morir entre 35 y 36
90
+ t.px(35) # p_35 = 1 - q_35
91
+ t.dx(35) # d_35 — muertes entre 35 y 36
92
+
93
+ t.npx(10, 35) # 10_p_35 = l_45 / l_35
94
+ t.nqx(10, 35) # 10_q_35 = 1 - 10_p_35
95
+ t.deferred_qx(5, 10, 35) # 5|10_q_35 = 5_p_35 * 10_q_40
96
+
97
+ t.ex(35) # e_35 curtate (años completos)
98
+ t.ex(35, curtate=False) # ê_35 completa (≈ e_35 + 0.5)
99
+
100
+ t.summary(35) # diccionario con todos los valores
101
+ t.ages # array de edades disponibles
102
+ t.omega # edad máxima
103
+ t.primera_consecutiva # primera edad consecutiva (20 en ILT)
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Seguros de Vida
109
+
110
+ Todos los seguros son **discretos**: la variable aleatoria es $K_x$ (años completos) y el beneficio se paga al **final del año de muerte**.
111
+
112
+ Requieren **x >= 20** en la ILT.
113
+ Puedes sobreescribir la tasa `i` en cada función.
114
+
115
+ ### I. Seguro Ordinario de Vida
116
+
117
+ $$A_x = \sum_{k=0}^{\omega-x-1} v^{k+1} \cdot {_k}p_x \cdot q_{x+k}$$
118
+
119
+ ```python
120
+ ins.Ax(35) # A_35
121
+ ins.Ax(35, i=0.05) # A_35 con i=5%
122
+ ```
123
+
124
+ ### II. Seguro Temporal n años
125
+
126
+ $$A^1_{x:\overline{n}|} = \sum_{k=0}^{n-1} v^{k+1} \cdot {_k}p_x \cdot q_{x+k}$$
127
+
128
+ ```python
129
+ ins.Ax_temporal(35, 20) # A^1_{35:20|}
130
+ ins.Ax_temporal(35, 20, i=0.05) # con i=5%
131
+ ```
132
+
133
+ ### III. Dotal Puro n años
134
+
135
+ $$_nE_x = v^n \cdot {_n}p_x$$
136
+
137
+ Solo requiere que `x` y `x+n` estén en la tabla (no necesita x >= 20).
138
+
139
+ ```python
140
+ ins.nEx(35, 20) # 20_E_35
141
+ ins.nEx(0, 5) # 5_E_0 — válido aunque x < 20
142
+ ```
143
+
144
+ ### IV. Dotal Mixto n años
145
+
146
+ $$A_{x:\overline{n}|} = A^1_{x:\overline{n}|} + {_n}E_x$$
147
+
148
+ ```python
149
+ ins.Ax_dotal_mixto(35, 20)
150
+ ```
151
+
152
+ ### V. Seguro Diferido m años
153
+
154
+ $$_{m|}A_x = {_m}E_x \cdot A_{x+m}$$
155
+
156
+ ```python
157
+ ins.Ax_diferido(35, 10) # 10|A_35
158
+ ```
159
+
160
+ ### VI. Seguro Creciente
161
+
162
+ $$(IA)_x = \sum_{k=0}^{\omega-x-1} (k+1) \cdot v^{k+1} \cdot {_k}p_x \cdot q_{x+k}$$
163
+
164
+ ```python
165
+ ins.IAx(35)
166
+ ```
167
+
168
+ ### VII. Seguro Decreciente Temporal
169
+
170
+ $$(DA)^1_{x:\overline{n}|} = \sum_{k=0}^{n-1} (n-k) \cdot v^{k+1} \cdot {_k}p_x \cdot q_{x+k}$$
171
+
172
+ ```python
173
+ ins.DAx(35, 20)
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Anualidades Contingentes
179
+
180
+ Todas las anualidades son **discretas** (variable $K_x$).
181
+ Requieren **x >= 20** en la ILT.
182
+
183
+ ### I. Anticipada Vitalicia
184
+
185
+ $$\ddot{a}_x = \sum_{k=0}^{\omega-x} v^k \cdot {_k}p_x$$
186
+
187
+ ```python
188
+ ann.ax(35)
189
+ ```
190
+
191
+ ### Ibis. Vencida Vitalicia
192
+
193
+ $$a_x = \ddot{a}_x - 1$$
194
+
195
+ ```python
196
+ ann.ax_vencida(35)
197
+ ```
198
+
199
+ ### II. Anticipada Temporal n años
200
+
201
+ $$\ddot{a}_{x:\overline{n}|} = \sum_{k=0}^{n-1} v^k \cdot {_k}p_x$$
202
+
203
+ ```python
204
+ ann.ax_temp(35, 20)
205
+ ```
206
+
207
+ ### IIbis. Vencida Temporal n años
208
+
209
+ $$a_{x:\overline{n}|} = \ddot{a}_{x:\overline{n}|} \cdot v$$
210
+
211
+ ```python
212
+ ann.ax_temp_vencida(35, 20)
213
+ ```
214
+
215
+ ### III. Diferida Vitalicia m años
216
+
217
+ $$_{m|}\ddot{a}_x = {_m}E_x \cdot \ddot{a}_{x+m}$$
218
+
219
+ ```python
220
+ ann.ax_diferida(35, 10)
221
+ ```
222
+
223
+ ### IV. Diferida Temporal m|n años
224
+
225
+ $$_{m|n}\ddot{a}_x = {_m}E_x \cdot \ddot{a}_{x+m:\overline{n}|}$$
226
+
227
+ ```python
228
+ ann.ax_diferida_temp(35, 10, 20)
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Primas Netas
234
+
235
+ Todas bajo el **principio de equivalencia**: $E[Z] = P \cdot E[Y]$
236
+
237
+ ### Primas Únicas Netas (PUN)
238
+
239
+ ```python
240
+ pre.prima_unica_vida_entera(35)
241
+ pre.prima_unica_temporal(35, 20)
242
+ pre.prima_unica_dotal_mixto(35, 20)
243
+ ```
244
+
245
+ ### Primas Anuales Niveladas
246
+
247
+ ```python
248
+ pre.prima_vida_entera(35) # P = A_x / ä_x
249
+ pre.prima_temporal(35, 20) # P = A^1_{x:n|} / ä_{x:n|}
250
+ pre.prima_dotal_mixto(35, 20) # P = A_{x:n|} / ä_{x:n|}
251
+ pre.prima_vida_entera_limitada(35, 10) # h_P = A_x / ä_{x:h|}
252
+ ```
253
+
254
+ ### Primas Fraccionadas (aproximación UDD)
255
+
256
+ $$P^{(m)} = P \cdot \frac{i}{i^{(m)}} \cdot \frac{1}{m}$$
257
+
258
+ ```python
259
+ P = pre.prima_vida_entera(35)
260
+ pre.prima_fraccionada(P, m=12) # prima mensual
261
+ pre.prima_fraccionada(P, m=4) # prima trimestral
262
+ pre.prima_fraccionada(P, m=2) # prima semestral
263
+ ```
264
+
265
+ ---
266
+
267
+ ## Reservas Prospectivas
268
+
269
+ $$_tV = \text{VP(beneficios futuros)} - P \cdot \text{VP(primas futuras)}$$
270
+
271
+ ```python
272
+ pre.reserva_vida_entera(35, t=10) # 10_V(A_35)
273
+ pre.reserva_temporal(35, 20, t=10) # 10_V temporal
274
+ pre.reserva_dotal_mixto(35, 20, t=10) # 10_V dotal mixto
275
+
276
+ # Tabla completa t = 0, 1, ..., n
277
+ pre.tabla_reservas(35, 20, kind='dotal_mixto')
278
+ pre.tabla_reservas(35, 20, kind='temporal')
279
+ pre.tabla_reservas(35, 20, kind='vida_entera')
280
+ ```
281
+
282
+ ---
283
+
284
+ ## Conversiones Discreto ↔ Continuo
285
+
286
+ Bajo el supuesto **UDD** (Distribución Uniforme de Muertes):
287
+
288
+ $$\bar{A}_x = A_x \cdot \frac{i}{\delta} \qquad \bar{a}_x = \frac{1 - \bar{A}_x}{\delta}$$
289
+
290
+ donde $\delta = \ln(1+i)$.
291
+
292
+ ```python
293
+ from firstact.utils import to_cont, to_disc
294
+
295
+ # Discreto → Continuo
296
+ Ax_bar = to_cont(ins.Ax(35), i=0.06, kind='seguro') # Ā_35
297
+ ax_bar = to_cont(ann.ax(35), i=0.06, kind='anualidad') # ā_35
298
+
299
+ # Continuo → Discreto
300
+ Ax = to_disc(Ax_bar, i=0.06, kind='seguro')
301
+ ax = to_disc(ax_bar, i=0.06, kind='anualidad')
302
+ ```
303
+
304
+ ### ⚠️ No aplica para dotal puro ni dotal mixto
305
+
306
+ ```python
307
+ to_cont(ins.nEx(35,20), i=0.06, kind='dotal_puro') # ConversionNoAplicable
308
+ to_cont(ins.Ax_dotal_mixto(35,20), i=0.06, kind='dotal_mixto') # ConversionNoAplicable
309
+ ```
310
+
311
+ El dotal puro $_nE_x = v^n \cdot {_n}p_x$ es un pago de **sobrevivencia**, no de muerte, y no tiene versión continua bajo UDD.
312
+
313
+ ---
314
+
315
+ ## Excepciones
316
+
317
+ | Excepción | Cuándo ocurre |
318
+ |---|---|
319
+ | `EdadNoDisponible` | La edad no existe en la tabla |
320
+ | `EdadFueraDeRango` | x < 20 en la ILT (zona con saltos) para seguros/anualidades |
321
+ | `ConversionNoAplicable` | `to_cont`/`to_disc` con dotal puro o dotal mixto |
322
+ | `ParametroInvalido` | n negativo, t > n, m <= 0, etc. |
323
+
324
+ ```python
325
+ from firstact.exceptions import EdadFueraDeRango, EdadNoDisponible
326
+ from firstact.exceptions import ConversionNoAplicable, ParametroInvalido
327
+
328
+ try:
329
+ ins.Ax(5)
330
+ except EdadFueraDeRango as e:
331
+ print(e)
332
+
333
+ try:
334
+ t.qx(3)
335
+ except EdadNoDisponible as e:
336
+ print(e)
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Ejemplo completo
342
+
343
+ ```python
344
+ from firstact import MortalityTable, Insurance, Annuity, Premium
345
+ from firstact.utils import to_cont
346
+
347
+ t = MortalityTable.ilt()
348
+ ins = Insurance(t, i=0.06)
349
+ ann = Annuity(t, i=0.06)
350
+ pre = Premium(t, i=0.06)
351
+
352
+ # Persona de 30 años, seguro dotal mixto 25 años, SA = $1,000,000
353
+ SA, x, n = 1_000_000, 30, 25
354
+
355
+ P = pre.prima_dotal_mixto(x, n) * SA
356
+ P_mens = pre.prima_fraccionada(P, m=12)
357
+ R10 = pre.reserva_dotal_mixto(x, n, t=10) * SA
358
+
359
+ print(f"Prima anual: ${P:>12,.2f}")
360
+ print(f"Prima mensual: ${P_mens:>12,.2f}")
361
+ print(f"Reserva año 10: ${R10:>12,.2f}")
362
+
363
+ # Versión continua
364
+ Ax_bar = to_cont(ins.Ax(x), i=0.06, kind='seguro')
365
+ print(f"Ā_{x} (continuo): {Ax_bar:.5f}")
366
+ ```
367
+
368
+ ---
369
+
370
+ ## Tests
371
+
372
+ ```bash
373
+ python tests/test_firstact.py
374
+ ```