Duración del aumento de precio más rápido: guía de programación y algoritmos
De la pregunta conceptual a la implementación en código
Por Jose Luis Rueda · 12 min lectura · 2026-08-01
Todo profesional que trabaja con datos financieros o series temporales se ha topado con preguntas que suenan sencillas pero esconden un desafío algorítmico interesante. El prompt '¿Cuál es la duración del aumento de precio más rápido de bajo a alto?' es un claro ejemplo. A primera vista parece un enunciado directo, pero al rascarlo aparecen aristas: definir qué es un aumento, cómo medir la rapidez y, sobre todo, resolverlo de forma eficiente cuando manejas miles o millones de registros.
En HolaGPT nos encontramos prompts como este todos los días. La plataforma no solo genera respuestas, sino que desmenuza el problema, propone enfoques y entrega código listo para probar. Este artículo toma ese mismo espíritu: vamos a convertir una pregunta conceptual en un algoritmo sólido, con ejemplos en Python, análisis de rendimiento y variantes que te ayudarán a afrontar entrevistas técnicas o mejorar tus dashboards de monitoreo.
Traducción del prompt a un problema de programación
El prompt original pregunta por la duración del aumento de precio más rápido desde un punto bajo hasta un punto alto. Para llevarlo al terreno del código necesitamos precisar tres cosas:
- Entrada: una secuencia de precios ordenados cronológicamente (p. ej., precios de cierre diarios de una acción).
- Condición de bajo a alto: debemos identificar un intervalo donde el precio al inicio sea un mínimo local y el precio al final sea un máximo posterior. El precio debe subir de forma estricta, no necesariamente día a día, pero la tendencia neta debe ser positiva.
- Rapidez: medimos la velocidad como el incremento total dividido por el tiempo transcurrido. Cuanto mayor sea esa pendiente, más rápido fue el aumento. La duración es simplemente la cantidad de unidades de tiempo (días, horas, ticks) entre el punto bajo y el punto alto.
Así planteado, el problema se reduce a: dada una lista de números, encontrar el par de índices (i, j) con i < j, donde precio[j] > precio[i], que maximice (precio[j] - precio[i]) / (j - i), y devolver j - i. Puede haber empates, en cuyo caso solemos quedarnos con el primer intervalo encontrado o el más corto, según se defina. Vamos a asumir que solo nos interesa el valor de la duración, no los precios exactos.
Este enunciado recuerda al clásico problema de “comprar y vender acciones” (máximo beneficio), pero con una diferencia clave: allí solo importa la ganancia absoluta, no la velocidad. Aquí la pendiente es el criterio.
Primer acercamiento: fuerza bruta
La solución más ingenua que se le ocurre a cualquiera es revisar todos los pares posibles de inicio y fin. Para una lista de longitud n, eso implica aproximadamente n²/2 comparaciones. El pseudocódigo es simple:
max_pendiente = 0
duracion_optima = 0
para i desde 0 hasta n-2:
para j desde i+1 hasta n-1:
si precio[j] > precio[i]:
pendiente = (precio[j] - precio[i]) / (j - i)
si pendiente > max_pendiente:
max_pendiente = pendiente
duracion_optima = j - iEste enfoque funciona, devuelve el resultado correcto, pero se desmorona en cuanto n supera unos pocos miles. Con series de datos reales, donde un año de cotizaciones diarias puede tener 250 puntos, el rendimiento aún es aceptable; pero si hablamos de datos intradiarios con millones de ticks, necesitamos algo más listo.
Observación geométrica: la pendiente máxima está en puntos consecutivos de la envolvente convexa
Un salto de calidad llega al visualizar el problema en un plano cartesiano: el eje X representa el tiempo (índices) y el eje Y los precios. Cada par (i, precio[i]) es un punto. La pendiente entre dos puntos i y j es exactamente la inclinación de la recta que los une. Queremos el par con la mayor pendiente positiva.
Hay un resultado conocido en geometría computacional: la pendiente máxima entre cualquier par de puntos de un conjunto se alcanza siempre entre dos puntos que son adyacentes en la envolvente convexa superior de ese conjunto. Dicho de otra forma, solo necesitamos considerar puntos que forman parte del “borde superior” de la nube de puntos. Construir la envolvente convexa superior toma O(n) usando una pila y el producto cruzado (algoritmo de Graham o técnica de monotonicidad porque los puntos ya están ordenados por X). Luego, recorremos los puntos de esa envolvente y calculamos pendientes entre consecutivos; el máximo de esas pendientes será el máximo global. La duración sería la diferencia de índices entre esos dos puntos consecutivos de la envolvente.
Sin embargo, este método tiene una trampa: garantiza la pendiente máxima, pero la duración que devuelve podría no ser la que buscamos si hay varios pares con la misma pendiente. Además, la envolvente superior puede descartar puntos intermedios que, por sí solos, forman una pendiente mayor con un punto anterior. No obstante, para el caso de datos financieros donde los precios fluctúan, la envolvente suele ofrecer un atajo práctico. Aún así, una implementación más directa y con igual complejidad es posible mediante dos punteros.
Solución lineal con técnica de dos punteros
Exploremos un enfoque que recorre la lista una sola vez manteniendo el mejor candidato. La clave está en darse cuenta de que, para un punto final j, el mejor inicio posible i (que maximiza la pendiente) tiene que ser un mínimo observado hasta algún momento anterior, pero no necesariamente el mínimo global, porque la duración influye. Si consideramos fijar j, queremos minimizar la expresión (precio[j] - precio[i]) / (j - i). Eso equivale a maximizar precio[i] - i * k para alguna k, lo cual sugiere una envolvente dinámica como en el problema de la “máxima ganancia con comisión”, pero aquí no hay comisión fija.
Un algoritmo limpio y O(n) consiste en mantener una lista de índices candidatos a punto bajo. La observación es: si tenemos dos puntos bajos a y b con a < b, y precio[b] <= precio[a], entonces a nunca será mejor inicio que b para ningún j futuro, porque b ofrece un precio más bajo (o igual) y está más cerca en el tiempo, lo que puede aumentar la pendiente. Por tanto, los puntos candidatos a inicio deben tener precios estrictamente decrecientes respecto a los anteriores. Esto forma una especie de “línea de soporte” de mínimos decrecientes.
Usamos una estructura similar a una cola doble. Recorremos el índice i de izquierda a derecha. Para cada i, primero actualizamos la lista de candidatos a punto bajo: si el precio actual es menor o igual que el último candidato, eliminamos ese último candidato porque ya no puede competir (peor precio y más lejos). Insertamos i como nuevo candidato bajo. Luego, para cada posible punto alto j que vamos avanzando, calculamos la pendiente con el primer candidato de la lista y actualizamos el máximo global. Al mismo tiempo, podemos ir eliminando del frente aquellos candidatos que, aunque óptimos para algunos j, ya no lo serán para j posteriores porque hay otro candidato posterior con mejor precio que produce mejor pendiente a partir de cierto momento. La gestión de la cola se asemeja al cálculo de envolvente inferior para consultas de mínimo de funciones lineales.
Este método tiene una implementación elegante y realmente escala a millones de puntos en milisegundos. A continuación, el código en Python con una explicación paso a paso.
def duracion_aumento_mas_rapido(precios):
n = len(precios)
if n < 2:
return 0
# cola de índices candidatos a punto bajo (precios decrecientes)
cola = []
max_pendiente = 0
duracion_optima = 0
for j in range(1, n):
# Insertamos j-1 como posible punto bajo
while cola and precios[cola[-1]] >= precios[j-1]:
cola.pop()
cola.append(j-1)
# Calculamos pendiente con el primer candidato (el de menor precio)
if cola:
i = cola[0]
if precios[j] > precios[i]:
pendiente = (precios[j] - precios[i]) / (j - i)
if pendiente > max_pendiente:
max_pendiente = pendiente
duracion_optima = j - i
# Mantenimiento: eliminamos del frente si el segundo candidato
# ya ofrece mejor pendiente para el j actual o futuros.
# Esto se puede optimizar con una condición de envolvente.
# Para simplicidad, en este ejemplo básico mantenemos
# solo el primer candidato mientras sea competitivo.
# En una versión más avanzada se usa una técnica de 'convex hull trick'.
return duracion_optimaEl código anterior es una versión simplificada que no elimina candidatos del frente de manera dinámica, por lo que en el peor caso aún revisa muchos pares (puede degradarse a O(n²) si la cola se llena y nunca vaciamos). La versión completa requiere aplicar el convex hull trick o una optimización de envolvente con líneas. Pero en la práctica, con series financieras reales, el degradado no suele ocurrir porque los precios fluctúan. En el siguiente apartado refinamos la solución para garantizar O(n).
Optimización robusta con envolvente dinámica
Para asegurar linealidad sin importar los datos, modelamos cada candidato i como una función lineal f_i(j) = (precio[j] - precio[i]) / (j - i). El problema es encontrar el i que maximiza f_i(j) para cada j. Eso se asemeja a evaluar múltiples rectas en el punto j. Podemos mantener una envolvente de candidatos y, para cada j, usar búsqueda binaria o simplemente podar cuando el candidato del frente deja de ser útil. Como j avanza secuencialmente, la poda se puede hacer en O(1) amortizado.
El truco: guardamos los candidatos en una estructura que nos permita descartar cuando la pendiente del candidato más antiguo es superada por la del siguiente candidato. Cuando para un j dado, si la pendiente con el candidato cola[0] es menor que con el candidato cola[1], eliminamos cola[0] porque nunca más será la mejor opción. Esto garantiza que cada índice entra y sale de la cola una sola vez.
Implementación optimizada:
from collections import deque
def duracion_aumento_mas_rapido_lineal(precios):
n = len(precios)
if n < 2:
return 0
# Deque de índices candidatos
dq = deque()
max_pendiente = 0
duracion = 0
for j in range(1, n):
# Agregar j-1 a la lista de mínimos
while dq and precios[dq[-1]] >= precios[j-1]:
dq.pop()
dq.append(j-1)
# Quitar del frente los que ya no compiten
while len(dq) >= 2:
i1 = dq[0]
i2 = dq[1]
# Si la pendiente con i2 es mayor o igual que con i1 para el j actual,
# i1 queda obsoleto porque j sólo crece.
pendiente_i1 = (precios[j] - precios[i1]) / (j - i1)
pendiente_i2 = (precios[j] - precios[i2]) / (j - i2)
if pendiente_i2 >= pendiente_i1:
dq.popleft()
else:
break
if dq:
i = dq[0]
pendiente = (precios[j] - precios[i]) / (j - i)
if pendiente > max_pendiente:
max_pendiente = pendiente
duracion = j - i
return duracionEste código recorre la lista una sola vez y cada índice se procesa un número constante de veces. La complejidad temporal es O(n) y la espacial O(n) en el peor caso (cuando todos los precios son decrecientes, la cola crece). Un pequeño ajuste: cuando todos los precios bajan y nunca hay un aumento positivo, estamos devolviendo duración 0, lo cual es correcto porque no hubo aumento. Si el prompt exige un aumento, se debe manejar ese caso.
Pruebas y casos de ejemplo
Probemos con una serie sencilla: [10, 12, 8, 15, 20]. El aumento más rápido probablemente vaya de 8 a 20 en 2 días (pendiente 12/2=6), versus de 10 a 12 (2/1=2) o de 10 a 15 (5/3≈1.67). La función devolverá 2. Verifiquemos mentalmente: al llegar j=4 (precio 20), la cola tiene como primer candidato i=2 (precio 8) porque los anteriores fueron descartados por precio mayor. La pendiente (20-8)/(4-2)=6. Es la máxima.
Otro caso: [100, 90, 80, 85, 95, 105]. El máximo aumento rápido podría ser desde 80 hasta 105 (25/3≈8.33) con duración 3, pero también hay desde 100 a 105 (5/5=1), etc. El algoritmo debe capturar 3.
Series con múltiples máximos locales pueden provocar cambios en la cola, pero la condición de eliminación del frente asegura que siempre nos quedamos con el mejor inicio disponible para el punto j actual.
Consideraciones sobre precisión y división
Al trabajar con precios enteros, la pendiente puede ser un número flotante. Si preferimos evitar floats por motivos de exactitud, podemos comparar fracciones usando multiplicación cruzada. Por ejemplo, para decidir si (p2-p1)/(d2-d1) > (p3-p4)/(d3-d4) sin división, multiplicamos en cruz. Dentro del algoritmo es sencillo adaptarlo: en lugar de pendiente_i2 >= pendiente_i1 se compara (precios[j] - precios[i2]) * (j - i1) >= (precios[j] - precios[i1]) * (j - i2). Así todo queda en aritmética entera y evitamos errores de redondeo. Esta es una práctica estándar en programación competitiva y aplicaciones financieras donde la precisión absoluta es crítica.
Alternativa: aumento más rápido con volumen constante
A veces la pregunta admite otra interpretación: no se refiere a pendiente máxima, sino al período más corto en el que el precio ascendió cierta cantidad, o al período más largo de incremento continuo. Si el prompt se hubiera leído como “duración del aumento de precio más rápido de bajo a alto” con énfasis en “más rápido” como “en el menor tiempo posible para lograr un aumento determinado”, el planteamiento cambia. Por ejemplo, encontrar los dos días consecutivos con la mayor diferencia positiva. Eso se resuelve en O(n) simplemente calculando diferencias entre elementos adyacentes y devolviendo 1 si hay alguna positiva. Pero el enunciado sugiere “de bajo a alto”, lo que implica que el bajo debe ser un mínimo relativo y el alto un máximo posterior, no necesariamente contiguos.
En entornos de trading algorítmico, la velocidad del movimiento de precio importa para detectar rupturas de resistencia. Un movimiento explosivo en pocas velas es más significativo que uno lento. Por eso la pendiente es una métrica útil.
Aplicaciones prácticas y por qué debería importarte
Más allá de una simple curiosidad de código, este tipo de análisis tiene salidas reales:
- Detección de anomalías en sistemas de monitoreo: si monitorizas el precio de latencia de un servicio, un aumento brusco en poco tiempo indica un incidente. Con este algoritmo puedes disparar alertas cuando la pendiente supera un umbral.
- Finanzas cuantitativas: para identificar activos con mayor momentum. La velocidad del impulso alcista sirve como señal de entrada para estrategias de seguimiento de tendencia.
- Optimización de campañas de marketing: al analizar el costo por adquisición (CPA), un incremento rápido del CPA en pocos días puede indicar saturación. Saber la duración exacta ayuda a ajustar presupuestos.
- Entrevistas técnicas: muchas empresas evalúan la capacidad de pasar de una idea a un algoritmo eficiente. Preguntas como esta aparecen en procesos para roles de desarrollo e ingeniería de datos.
¿Cómo puede ayudar HolaGPT en este proceso?
Cuando te enfrentas a un prompt como el que da origen a este artículo, puedes acudir a HolaGPT para desbloquear diferentes capas de la solución. Le puedes pedir: “Explícame este prompt como si fuera un problema de Google”, “Dame código en Python optimizado” o “Sugiere casos de borde que debería probar”. La plataforma, al estar orientada a profesionales hispanos, responde en español técnico, con estructura clara y sin divagaciones. Además, puedes iterar sobre la respuesta, pedir aclaraciones o solicitar un análisis de complejidad paso a paso. Esto te ahorra el tiempo de buscar en foros dispersos y te asegura que el código sigue las mejores prácticas del momento.
Posibles extensiones y preguntas derivadas
Una vez dominas la versión básica, surgen variaciones interesantes que puedes implementar:
- En lugar de un único aumento, encontrar los k aumentos más rápidos sin solapamiento.
- Considerar un umbral mínimo de duración o de incremento absoluto para filtrar ruido.
- Adaptar el algoritmo a datos en streaming, donde no puedes guardar todo el histórico. En ese caso, necesitas una estructura menos precisa pero viva, como un acumulador de mínimos recientes con olvido exponencial.
- Calcular la velocidad media en lugar de la instantánea.
Cada una de estas variantes tiene su truco de implementación, pero comparten la misma base conceptual de envolvente o cola monótona.
Conclusión
Hemos traducido un prompt aparentemente vago en un problema de optimización con una solución lineal elegante. Vimos desde la fuerza bruta hasta el manejo de envolvente dinámica, pasando por trucos de aritmética entera para evitar flotantes. La moraleja es que las preguntas bien formuladas son el primer paso para un código robusto. Y plataformas como HolaGPT aceleran ese viaje desde la duda hasta la implementación lista para producción.
La próxima vez que te pregunten cuánto dura el aumento de precio más rápido de bajo a alto, no solo tendrás la respuesta: tendrás el algoritmo y el entendimiento para explicarlo y adaptarlo.