Cinco perfiles con distinto punto en el compromiso entre variedad visible y
minutos de presencia de mas. Todos dan saldo 0. Se cambian en caliente con
/perfil, con botones, y al cambiar se vuelven a tirar los dados de todos los
dias del horizonte que no se hayan fichado ya.
min/dia madrugon entrada 09:00
discreto 2,8 1,3 2 minutos, moda 75%
natural 4,1 1,8 5 minutos, moda 51% <- por defecto
camuflado 5,5 2,1 4 minutos, moda 43%
disperso 7,9 3,0 6 minutos, moda 29%
sin_madrugar 5,7 1,3 2 minutos, moda 77%
(historico real del usuario: 4 minutos, moda 38%)
La distincion util no es solo el coste total sino DONDE cae. Adelantar la
entrada de la mañana alarga el dia; adelantar la vuelta de comer solo acorta la
comida y se sale a la misma hora. De ahi "sin_madrugar": entrada de la mañana lo
mas pegada posible a las 9 (madrugon 1,26 min/dia, el mas bajo de todos, por
debajo incluso de "discreto") y toda la variedad en la pausa y las salidas.
Para eso los perfiles admiten afinar por tramo con sufijo _w<n>:
entry_median_w0 gana a entry_median para el tramo de la mañana.
tools/perfiles.py mide cada perfil sobre los 226 dias laborables del calendario
real; las cifras de arriba salen de ahi y van guardadas como metadatos para que
/perfil las muestre sin recalcular.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
123 lines
4.3 KiB
Python
123 lines
4.3 KiB
Python
"""Perfiles de jitter intercambiables.
|
|
|
|
El compromiso de fondo no tiene truco: Timenet solo cuenta dentro de la ventana
|
|
teorica, asi que solo se puede variar hacia el lado seguro, y alejarse cuesta
|
|
presencia real. Mas variedad visible = mas minutos ahi. Cada perfil elige un punto
|
|
de ese compromiso.
|
|
|
|
Un perfil puede afinar por tramo con sufijo `_w<n>`: `entry_median_w0` gana a
|
|
`entry_median` para el tramo 0. Sirve para repartir el coste donde menos molesta:
|
|
adelantar la entrada de la mañana ALARGA el dia, mientras que adelantar la vuelta
|
|
de comer solo ACORTA la comida y se sale a la misma hora.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
# Valores comunes. Un perfil solo declara lo que cambia.
|
|
BASE = {
|
|
"entry_floor": 12,
|
|
"entry_median": 58,
|
|
"entry_sigma": 0.95,
|
|
"entry_cap": 330,
|
|
"exit_floor": 10,
|
|
"exit_median": 42,
|
|
"exit_sigma": 0.95,
|
|
"exit_cap": 300,
|
|
"drift_sigma": 0.30,
|
|
"drift_clamp": 0.75,
|
|
"entry_guard": 12,
|
|
}
|
|
|
|
PERFILES: dict[str, dict] = {
|
|
"discreto": {
|
|
"titulo": "Discreto",
|
|
"resumen": "Lo más barato. Casi siempre justo antes de la hora.",
|
|
"stats": {'coste': 2.82, 'madrugon': 1.29, 'mins': 2, 'moda': 75},
|
|
"params": {
|
|
"entry_median": 42, "entry_sigma": 0.80, "entry_cap": 240,
|
|
"exit_median": 30, "exit_sigma": 0.80, "exit_cap": 210,
|
|
},
|
|
},
|
|
"natural": {
|
|
"titulo": "Natural",
|
|
"resumen": "Equilibrio entre variedad y minutos de más.",
|
|
"stats": {'coste': 4.14, 'madrugon': 1.81, 'mins': 5, 'moda': 51},
|
|
"params": {
|
|
"entry_median": 58, "entry_sigma": 0.95, "entry_cap": 330,
|
|
"exit_median": 42, "exit_sigma": 0.95, "exit_cap": 300,
|
|
},
|
|
},
|
|
"camuflado": {
|
|
"titulo": "Camuflado",
|
|
"resumen": "Se parece a tu histórico real. Cuesta algo más.",
|
|
"stats": {'coste': 5.47, 'madrugon': 2.08, 'mins': 4, 'moda': 43},
|
|
"params": {
|
|
"entry_median": 75, "entry_sigma": 1.05, "entry_cap": 450,
|
|
"exit_median": 55, "exit_sigma": 1.05, "exit_cap": 420,
|
|
},
|
|
},
|
|
"disperso": {
|
|
"titulo": "Disperso",
|
|
"resumen": "Máxima variedad. Paga bastantes minutos por ella.",
|
|
"stats": {'coste': 7.87, 'madrugon': 2.99, 'mins': 6, 'moda': 29},
|
|
"params": {
|
|
"entry_median": 110, "entry_sigma": 1.15, "entry_cap": 600,
|
|
"exit_median": 80, "exit_sigma": 1.15, "exit_cap": 540,
|
|
},
|
|
},
|
|
"sin_madrugar": {
|
|
"titulo": "Sin madrugar",
|
|
"resumen": "Entrada de la mañana pegada a las 9. La variedad se la come "
|
|
"la pausa de comer y la salida, así no te levantas antes.",
|
|
"stats": {'coste': 5.68, 'madrugon': 1.26, 'mins': 2, 'moda': 77},
|
|
"params": {
|
|
# tramo 0 = mañana: lo mas ajustado posible, no alarga el dia
|
|
"entry_median_w0": 35, "entry_sigma_w0": 0.70, "entry_cap_w0": 150,
|
|
# tramo 1 = vuelta de comer: generoso, solo acorta la comida
|
|
"entry_median_w1": 120, "entry_sigma_w1": 1.15, "entry_cap_w1": 600,
|
|
# salidas: generosas, no obligan a madrugar
|
|
"exit_median": 75, "exit_sigma": 1.10, "exit_cap": 480,
|
|
},
|
|
},
|
|
}
|
|
|
|
DEFECTO = "natural"
|
|
|
|
|
|
def existe(nombre: str) -> bool:
|
|
return nombre in PERFILES
|
|
|
|
|
|
def nombres() -> list[str]:
|
|
return list(PERFILES)
|
|
|
|
|
|
def params(nombre: str) -> dict:
|
|
"""Parametros efectivos del perfil, con BASE por debajo."""
|
|
p = dict(BASE)
|
|
p.update(PERFILES.get(nombre, PERFILES[DEFECTO])["params"])
|
|
return p
|
|
|
|
|
|
def titulo(nombre: str) -> str:
|
|
return PERFILES.get(nombre, {}).get("titulo", nombre)
|
|
|
|
|
|
def resumen(nombre: str) -> str:
|
|
return PERFILES.get(nombre, {}).get("resumen", "")
|
|
|
|
|
|
def stats(nombre: str) -> dict:
|
|
"""Medidas sobre los 226 dias laborables del año. Ver tools/perfiles.py.
|
|
|
|
coste = presencia total de mas, min/dia
|
|
madrugon = de esos, los que te levantas antes por la entrada de la mañana
|
|
mins = minutos distintos visibles en la entrada de las 09:00
|
|
moda = cuota del minuto mas frecuente
|
|
"""
|
|
return PERFILES.get(nombre, {}).get("stats", {})
|
|
|
|
|
|
def valor(p: dict, clase: str, campo: str, wi: int):
|
|
"""`entry_median_w0` gana a `entry_median` para el tramo 0."""
|
|
return p.get(f"{clase}_{campo}_w{wi}", p[f"{clase}_{campo}"])
|