caudal/programa/pruebas/test_prima.py
2026-10-13 08:01:06 -05:00

114 lines
5.2 KiB
Python

"""
Pruebas de la prima (prima.py): cuánto se aleja del mercado de ese día lo que
se pagó o cobró por el bitcoin, y de poner el importe de verdad a una compra.
python3 -m unittest discover -s pruebas -v
"""
from __future__ import annotations
import sys
import tempfile
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from cartera import prima, spot_btc # noqa: E402
from cartera.datos import Datos, escribir, leer # noqa: E402
from cartera.operaciones import Cartera, ErrorOperacion # noqa: E402
from cartera.servidor import App # noqa: E402
DIA = "2024-03-10"
class ConCartera(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.carpeta = Path(self.tmp.name) / "datos"
self.carpeta.mkdir()
escribir(self.carpeta, Datos(
[{"id": "C1", "nombre": "Bitcoin", "tipo": "Cripto", "codigo": "bitcoin", "unidad": "BTC",
"fecha_inicio": "2020-01-01", "estado": "abierta"},
{"id": "F1", "nombre": "Fondo", "tipo": "Fondo", "codigo": "ES0000000001", "unidad": "part.",
"fecha_inicio": "2020-01-01", "estado": "abierta"}], [], [], []))
self.c = Cartera(self.carpeta)
self.addCleanup(self.c.cerrar)
self.cierre = spot_btc.precio(DIA)[0]
def compra(self, veces: float, **extra) -> dict:
"""Una compra de 0,1 BTC pagada a «veces» el cierre de ese día."""
return self.c.guardar_movimiento({"activo": "C1", "tipo": "Compra", "fecha": DIA, "cantidad": 0.1,
"importe": round(0.1 * self.cierre * veces, 2), **extra})
def primas(self) -> dict:
d = self.c.datos
return prima.de_movimientos(d.vivos("activos"), d.vivos("movimientos"), d.vivos("precios"))
class TestPrima(ConCartera):
def test_una_compra_p2p_normal_lleva_su_prima_y_no_avisa(self):
m = self.compra(1.06)
p = self.primas()[m["id"]]
self.assertAlmostEqual(p["prima"], 0.06, places=3)
self.assertEqual((p["ref"], p["dia"], p["lejos"]), (self.cierre, DIA, False))
def test_avisa_de_lo_que_queda_lejos_del_mercado(self):
cara, barata, justa = self.compra(1.2), self.compra(0.9), self.compra(0.97)
p = self.primas()
self.assertEqual([p[m["id"]]["lejos"] for m in (cara, barata, justa)], [True, True, False])
def test_en_una_venta_lo_raro_es_al_reves(self):
self.compra(1.0)
def venta(veces):
return self.c.guardar_movimiento({"activo": "C1", "tipo": "Venta", "fecha": DIA, "cantidad": 0.01,
"importe": round(0.01 * self.cierre * veces, 2)})
regalada, cara, normal = venta(0.8), venta(1.1), venta(0.96)
p = self.primas()
self.assertEqual([p[m["id"]]["lejos"] for m in (regalada, cara, normal)], [True, True, False])
def test_solo_mira_compras_y_ventas_de_bitcoin_con_importe(self):
self.compra(1.0)
self.c.guardar_movimiento({"activo": "C1", "tipo": "Mineria", "fecha": DIA, "cantidad": 0.01})
self.c.guardar_movimiento({"activo": "F1", "tipo": "Compra", "fecha": DIA, "cantidad": 10, "importe": 1000})
self.assertEqual(len(self.primas()), 1)
def test_sin_un_cierre_cercano_no_se_inventa_la_prima(self):
m = self.c.guardar_movimiento({"activo": "C1", "tipo": "Compra", "fecha": "2010-06-01", "cantidad": 1,
"importe": 0.5})
self.assertNotIn(m["id"], self.primas())
class TestImporteDeVerdad(ConCartera):
def test_poner_el_importe_cambia_el_precio_no_la_cantidad_y_deja_de_ser_estimada(self):
m = self.compra(1.0, estimado=True, notas="Compra · del nodo abc…")
self.c.enlazar_txid(m["id"], "ab" * 32)
r = self.c.poner_importe(m["id"], 3500.40)
self.assertEqual((r["importe"], r["cantidad"], r["estimado"]), (3500.40, 0.1, False))
self.assertAlmostEqual(r["precio"], 35004.0)
self.assertEqual((r["notas"], r["txid"]), ("Compra · del nodo abc…", "ab" * 32)) # lo demás, intacto
self.assertEqual(leer(self.carpeta).movimientos[0]["importe"], 3500.40) # y queda guardado
def test_solo_vale_para_compras_y_ventas_con_un_importe_positivo(self):
m = self.compra(1.0)
mineria = self.c.guardar_movimiento({"activo": "C1", "tipo": "Mineria", "fecha": DIA, "cantidad": 0.01})
for mid, importe in ((m["id"], 0), (m["id"], ""), (m["id"], "mucho"), (mineria["id"], 100)):
with self.assertRaises(ErrorOperacion):
self.c.poner_importe(mid, importe)
def test_una_cifra_rara_pero_buena_se_marca_y_se_puede_desmarcar(self):
m = self.compra(1.3)
self.assertEqual(self.c.revisar_prima([m["id"]])["cambiadas"], 1)
self.assertTrue(leer(self.carpeta).movimientos[0]["prima_ok"])
self.c.revisar_prima([m["id"]], ok=False)
self.assertNotIn("prima_ok", leer(self.carpeta).movimientos[0])
def test_la_pantalla_recibe_las_primas_con_el_estado(self):
m = self.compra(1.3)
estado = App(self.c).estado()
self.assertTrue(estado["primas"][m["id"]]["lejos"])
if __name__ == "__main__":
unittest.main()