59 lines
2.6 KiB
Python
59 lines
2.6 KiB
Python
"""
|
||
prima.py — Cuánto se aleja del mercado lo que pagaste (o cobraste) por tu bitcoin.
|
||
|
||
En una compra entre particulares (P2P) se paga algo más que el precio de
|
||
mercado: esa diferencia es la prima. Aquí se compara el precio de cada compra
|
||
o venta de bitcoin apuntada con el cierre de ese día (spot_btc.py):
|
||
|
||
prima = importe / (cantidad × cierre del día) − 1
|
||
|
||
Sirve para dos cosas: ver de un vistazo lo que te costó cada compra frente al
|
||
mercado, y cazar erratas (un importe con un cero de más, una cantidad mal
|
||
copiada, una fecha equivocada). No cambia ninguna cifra de la cartera: el
|
||
coste de una compra es lo que pagaste de verdad, con su prima.
|
||
|
||
Se avisa de una operación cuando queda «lejos»: te salió más de un 15 % peor
|
||
que el mercado, o más de un 5 % MEJOR (comprar por debajo del mercado, o
|
||
vender por encima, es raro). Si la cifra es buena, se marca como revisada
|
||
(«prima_ok» en la operación) y deja de avisar.
|
||
|
||
Una compra «estimada» tiene por importe el propio cierre del día, así que su
|
||
prima es cero: de esas avisa «Para revisar», no esto.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from . import spot_btc
|
||
|
||
EN_CONTRA = 0.15 # más de un 15 % peor que el mercado
|
||
A_FAVOR = 0.05 # más de un 5 % mejor que el mercado
|
||
TIPOS = ("Compra", "Venta")
|
||
|
||
|
||
def es_bitcoin(activo: dict) -> bool:
|
||
return activo.get("tipo") == "Cripto" and (activo.get("codigo") or "").lower() == "bitcoin"
|
||
|
||
|
||
def de_movimientos(activos: list[dict], movimientos: list[dict], precios: list[dict]) -> dict[str, dict]:
|
||
"""{id de operación: {ref, dia, prima, lejos}} de las compras y ventas de
|
||
bitcoin que tienen cantidad e importe y un cierre cercano con que comparar."""
|
||
bitcoins = {a["id"] for a in activos if es_bitcoin(a)}
|
||
guardados: dict[str, list[dict]] = {}
|
||
for p in precios:
|
||
if p.get("activo") in bitcoins:
|
||
guardados.setdefault(p["activo"], []).append(p)
|
||
salida = {}
|
||
for m in movimientos:
|
||
if m["activo"] not in bitcoins or m["tipo"] not in TIPOS:
|
||
continue
|
||
cantidad, importe = float(m.get("cantidad") or 0), float(m.get("importe") or 0)
|
||
if cantidad <= 0 or importe <= 0:
|
||
continue
|
||
ref, dia = spot_btc.precio(m["fecha"], guardados.get(m["activo"]))
|
||
if not ref:
|
||
continue
|
||
prima = importe / (cantidad * ref) - 1
|
||
contra = prima if m["tipo"] == "Compra" else -prima # > 0: te salió peor que el mercado
|
||
salida[m["id"]] = {"ref": ref, "dia": dia, "prima": round(prima, 4),
|
||
"lejos": contra > EN_CONTRA or contra < -A_FAVOR}
|
||
return salida
|