Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

¿Puede un LLM encontrar factores alpha?

Un LLM puede crear cien factores alpha en una hora. Evalúa 240 factores de cara o cruz con diez años de precios reales y prueba los que sobrevivan.

Un LLM puede proponer factores alpha durante todo el día. Si se le entrega a un modelo capaz un diccionario de datos y un sistema de evaluación, escribirá cien expresiones de factores plausibles antes del almuerzo. La pregunta más difícil está debajo: ¿cómo podría alguien saber si alguno es real, cuando la búsqueda que lo produjo es una máquina para fabricar ganadores a partir del ruido?

¿Qué es un factor alpha?

Un factor es una regla que convierte los datos de mercado en un número para cada acción en cada fecha. La variación de precio a doce meses es un factor. También lo es la razón de deuda a capital. Un factor se convierte en una estrategia cuando se clasifica un universo con base en él, se compra el segmento superior, se vende el inferior y se rebalancea según un calendario. Alpha es el rendimiento que queda después de restar lo que una exposición simple al mercado habría entregado de todos modos.

Los candidatos se evalúan con el índice de Sharpe: el rendimiento promedio dividido entre la desviación estándar de ese rendimiento, anualizado. Es el rendimiento por unidad de variación. Un Sharpe de largo plazo cercano a 1 en una estrategia en vivo es respetable, algo que conviene tener presente la próxima vez que un backtest muestre 3.

Cómo funciona realmente la investigación de factores con LLM

Todos los proyectos de este campo ejecutan una versión del mismo ciclo.

  1. El modelo escribe expresiones de factores en un lenguaje pequeño que el sistema puede evaluar.
  2. Un backtester califica cada expresión sobre un historial fijo de precios y fundamentales.
  3. Las expresiones que superan un umbral de puntuación se conservan. Las demás se descartan.
  4. Las expresiones conservadas regresan al contexto del modelo como ejemplos resueltos y el ciclo vuelve a ejecutarse.

Los sistemas de trading multiagente distribuyen esas tareas entre roles separados: uno propone y otro prueba. La infraestructura es realmente útil, y las habilidades de datos de mercado que necesita un agente de IA son las mismas que necesita una persona.

Nada en el ciclo es deshonesto. Así es como se hace investigación. El problema es aritmético y aparece en cuanto el paso 2 se ejecuta más de unas pocas veces.

Por qué una búsqueda de factores alpha con LLM fabrica ganadores

Un historial de precios. Miles de hipótesis baratas. Cada hipótesis se evalúa contra la misma muestra finita, y esa muestra contiene mucha suerte. Pruebe suficientes reglas y algunas se ajustarán estrechamente a esa suerte. La puntuación no puede decirle qué tipo de ajuste obtuvo, porque una regla que coincide con el ruido y una regla que coincide con el mercado producen el mismo número.

Esta es la hipótesis nula, extraída 240 veces. Cada «factor» de abajo es un lanzamiento de moneda: un hash del ticker, el mes y un número de prueba divide cada mes 40 acciones estadounidenses grandes en dos mitades, y la estrategia mantiene una mitad en largo y la otra en corto. Por construcción, no contiene información. Evaluadas con rendimientos reales de cierre mensual desde enero de 2016 hasta junio de 2021, las 240 pruebas quedan así.

Consulta240 factores aleatorios evaluados con precios reales: Sharpe anualizado, ene. 2016 a jun. 2021
El SQL exacto detrás de cada cifra
WITH month_end AS (
    SELECT ticker,
           toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(toFloat64(close), window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
                     'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
                     'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
                     'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2021-06-30')
      AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, month_start
),
lagged AS (
    SELECT ticker,
           month_start,
           close_px,
           lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
                                      ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
    FROM month_end
),
monthly_return AS (
    SELECT ticker, month_start, close_px / prev_px - 1 AS ret
    FROM lagged
    WHERE prev_px > 0
      AND month_start >= toDate('2016-01-01')
),
trial AS (
    SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
    SELECT t.n AS trial_id,
           m.month_start AS month_start,
           avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
         - avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
    FROM monthly_return AS m
    CROSS JOIN trial AS t
    GROUP BY trial_id, month_start
    HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
       AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
    SELECT trial_id,
           avg(long_short_ret) / stddevSamp(long_short_ret) * sqrt(12) AS sharpe
    FROM factor_month
    GROUP BY trial_id
    HAVING stddevSamp(long_short_ret) > 0
)
SELECT multiIf(sharpe < -1.2, 'below -1.2',
               sharpe < -0.8, '-1.2 to -0.8',
               sharpe < -0.4, '-0.8 to -0.4',
               sharpe <  0.0, '-0.4 to 0.0',
               sharpe <  0.4, '0.0 to 0.4',
               sharpe <  0.8, '0.4 to 0.8',
               sharpe <  1.2, '0.8 to 1.2',
               '1.2 and above') AS sharpe_bucket,
       count() AS factor_count,
       round(100 * count() / 240, 1) AS share_pct
FROM scored
GROUP BY sharpe_bucket
ORDER BY min(sharpe)
Run this yourself

La dispersión es el punto central. Nada en ese gráfico predice nada y, aun así, 1 pruebas quedaron en la banda superior (1.2 and above), 0.4% de la búsqueda, y 1 en la banda inferior (below -1.2). Un investigador que ejecutara una prueba afortunada y se detuviera tendría un gráfico y un Sharpe, sin forma de distinguir ninguno de un descubrimiento. Los rendimientos aquí se calculan de cierre mensual a cierre mensual; cómo se calculan los rendimientos mensuales explica esa aritmética.

El número importante es cuántas pruebas realizó

Un backtest presentado por sí solo no incluye su denominador. Las mismas 240 pruebas, interpretadas como una búsqueda que sigue ampliándose: en cada paso, la mejor puntuación registrada junto con el promedio de todo lo probado hasta ese momento.

ConsultaEl mejor resultado sube con el tamaño de la búsqueda: Sharpe mejor y promedio según pruebas ejecutadas
El SQL exacto detrás de cada cifra
WITH month_end AS (
    SELECT ticker,
           toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(toFloat64(close), window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
                     'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
                     'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
                     'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2021-06-30')
      AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, month_start
),
lagged AS (
    SELECT ticker,
           month_start,
           close_px,
           lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
                                      ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
    FROM month_end
),
monthly_return AS (
    SELECT ticker, month_start, close_px / prev_px - 1 AS ret
    FROM lagged
    WHERE prev_px > 0
      AND month_start >= toDate('2016-01-01')
),
trial AS (
    SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
    SELECT t.n AS trial_id,
           m.month_start AS month_start,
           avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
         - avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
    FROM monthly_return AS m
    CROSS JOIN trial AS t
    GROUP BY trial_id, month_start
    HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
       AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
    SELECT trial_id,
           avg(long_short_ret) / stddevSamp(long_short_ret) * sqrt(12) AS sharpe
    FROM factor_month
    GROUP BY trial_id
    HAVING stddevSamp(long_short_ret) > 0
),
ladder AS (
    SELECT arrayJoin([1, 2, 5, 10, 25, 50, 100, 160, 240]) AS n
)
SELECT l.n AS factors_tried,
       round(max(s.sharpe), 2) AS best_sharpe,
       round(avg(s.sharpe), 2) AS average_sharpe
FROM ladder AS l
CROSS JOIN scored AS s
WHERE s.trial_id <= l.n
GROUP BY factors_tried
ORDER BY factors_tried
Run this yourself

Un máximo acumulado solo puede subir, que es precisamente la trampa. La primera regla evaluada obtuvo 0.44. Después de 240 pruebas, la mejor registrada marca 1.59, mientras que el promedio de todas se sitúa en 0.01. El resultado destacado mejoró sin que mejorara una sola regla. Un sistema que evalúa diez mil expresiones está recorriendo esta curva mucho más a la derecha de lo que muestra el gráfico, y el número que reporta es su punto máximo.

Qué les ocurre a los ganadores en un periodo reservado

La defensa estándar es un periodo reservado: evaluar en un periodo y volver a evaluar a los sobrevivientes en un periodo posterior que la búsqueda nunca tocó. Tome los doce lanzamientos de moneda con mejores resultados en la ventana de entrenamiento y ejecute las mismas reglas durante los cinco años siguientes, de julio de 2021 a junio de 2026.

ConsultaLas 12 mejores pruebas en entrenamiento, reevaluadas en cinco años intactos (jul. 2021 a jun. 2026)
El SQL exacto detrás de cada cifra
WITH month_end AS (
    SELECT ticker,
           toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(toFloat64(close), window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
                     'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
                     'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
                     'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
      AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, month_start
),
lagged AS (
    SELECT ticker,
           month_start,
           close_px,
           lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
                                      ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
    FROM month_end
),
monthly_return AS (
    SELECT ticker, month_start, close_px / prev_px - 1 AS ret
    FROM lagged
    WHERE prev_px > 0
      AND month_start >= toDate('2016-01-01')
),
trial AS (
    SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
    SELECT t.n AS trial_id,
           m.month_start AS month_start,
           avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
         - avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
    FROM monthly_return AS m
    CROSS JOIN trial AS t
    GROUP BY trial_id, month_start
    HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
       AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
    SELECT trial_id,
           avgIf(long_short_ret, month_start <  toDate('2021-07-01'))
             / stddevSampIf(long_short_ret, month_start <  toDate('2021-07-01')) * sqrt(12) AS in_sample_sharpe,
           avgIf(long_short_ret, month_start >= toDate('2021-07-01'))
             / stddevSampIf(long_short_ret, month_start >= toDate('2021-07-01')) * sqrt(12) AS out_of_sample_sharpe
    FROM factor_month
    GROUP BY trial_id
    HAVING countIf(month_start <  toDate('2021-07-01')) >= 24
       AND countIf(month_start >= toDate('2021-07-01')) >= 24
)
SELECT concat('trial ', toString(trial_id)) AS factor_label,
       round(in_sample_sharpe, 2) AS in_sample_sharpe,
       round(out_of_sample_sharpe, 2) AS out_of_sample_sharpe
FROM scored
ORDER BY in_sample_sharpe DESC
LIMIT 12
Run this yourself

Cada par de barras representa una regla. La barra izquierda es la puntuación que le ganó un lugar en el informe. La barra derecha es la misma regla durante los cinco años siguientes. La prueba ubicada en primer lugar obtuvo 1.59 en entrenamiento y -0.51 después; la ubicada en duodécimo lugar obtuvo 0.74 y luego 0.49.

Doce reglas también constituyen una muestra pequeña. Si se ordenan las 240 en quintiles según la puntuación de entrenamiento y luego se promedia la puntuación del periodo reservado de cada quintil, se obtiene una visión más clara.

ConsultaRango en entrenamiento frente al resultado fuera de muestra: 240 pruebas divididas en quintiles
El SQL exacto detrás de cada cifra
WITH month_end AS (
    SELECT ticker,
           toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(toFloat64(close), window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
                     'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
                     'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
                     'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
      AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, month_start
),
lagged AS (
    SELECT ticker,
           month_start,
           close_px,
           lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
                                      ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
    FROM month_end
),
monthly_return AS (
    SELECT ticker, month_start, close_px / prev_px - 1 AS ret
    FROM lagged
    WHERE prev_px > 0
      AND month_start >= toDate('2016-01-01')
),
trial AS (
    SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
    SELECT t.n AS trial_id,
           m.month_start AS month_start,
           avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
         - avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
    FROM monthly_return AS m
    CROSS JOIN trial AS t
    GROUP BY trial_id, month_start
    HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
       AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
    SELECT trial_id,
           avgIf(long_short_ret, month_start <  toDate('2021-07-01'))
             / stddevSampIf(long_short_ret, month_start <  toDate('2021-07-01')) * sqrt(12) AS in_sample_sharpe,
           avgIf(long_short_ret, month_start >= toDate('2021-07-01'))
             / stddevSampIf(long_short_ret, month_start >= toDate('2021-07-01')) * sqrt(12) AS out_of_sample_sharpe
    FROM factor_month
    GROUP BY trial_id
    HAVING countIf(month_start <  toDate('2021-07-01')) >= 24
       AND countIf(month_start >= toDate('2021-07-01')) >= 24
),
ranked AS (
    SELECT trial_id,
           in_sample_sharpe,
           out_of_sample_sharpe,
           row_number() OVER (ORDER BY in_sample_sharpe DESC) AS in_sample_rank
    FROM scored
)
SELECT multiIf(in_sample_rank <=  48, 'best fifth in training',
               in_sample_rank <=  96, 'second fifth',
               in_sample_rank <= 144, 'middle fifth',
               in_sample_rank <= 192, 'fourth fifth',
               'worst fifth in training') AS training_group,
       round(avg(in_sample_sharpe), 2) AS avg_in_sample_sharpe,
       round(avg(out_of_sample_sharpe), 2) AS avg_out_of_sample_sharpe
FROM ranked
GROUP BY training_group
ORDER BY min(in_sample_rank)
Run this yourself

En entrenamiento, los grupos van de 0.66 en la parte superior a -0.63 en la inferior: una escalera amplia y perfectamente ordenada, algo garantizado porque los grupos se formaron con esa misma puntuación. En el periodo reservado, los mismos dos extremos promedian 0.01 y 0.13. La escalera se aplana. Un periodo reservado es la única parte del proceso contra la que no se ha optimizado, por lo que vale la pena usarlo con cuidado.

Defensas que realmente funcionan

Un periodo reservado que se utiliza una sola vez. Cada consulta lo convierte en datos de entrenamiento. Las pruebas walk-forward, en las que la ventana avanza y cada puntuación proviene de datos posteriores al ajuste, son la versión que resiste el uso repetido.

Un ajuste por pruebas múltiples. El índice de Sharpe deflactado, introducido por Bailey y López de Prado en 2014, descuenta un Sharpe observado según cuántas pruebas se ejecutaron, cuánto dura la muestra, qué tan sesgados son los rendimientos y qué tan gruesas son sus colas. Si se le proporciona un conteo honesto de pruebas, un Sharpe destacado proveniente de una búsqueda de diez mil expresiones suele quedar en nada después de la deflación.

Un registro de auditoría que cubra cada expresión probada, incluidas las descartadas. Este es el elemento esencial y la razón por la que «auditable» es la palabra interesante en la descripción de un proyecto de investigación de factores. La deflación necesita un conteo de pruebas. Un proceso que registra solo a los ganadores ha destruido el insumo de su propia corrección. Los borradores descartados, las búsquedas de parámetros abandonadas, los reinicios del propio investigador y cada versión anterior del código de evaluación cuentan para ese número.

Comprobaciones de costos y de sesgo de anticipación antes de creer en la puntuación. Un factor clasificado con base en un campo de fundamentales fechado según el día en que el proveedor lo cargó, en vez del día en que el mercado pudo verlo, produce un backtest excelente y opera mal.

Cómo leer la palabra «auditable»

En este campo aparecen nuevos repositorios casi todas las semanas, y un proyecto con unas pocas docenas de estrellas es un prototipo, no un historial. El número de estrellas también cambia más rápido que el código, por lo que esta página evalúa el patrón y no un proyecto específico. Esto es lo primero que debe abrir en cualquiera que encuentre.

  • ¿Registra cada candidato con su expresión y su puntuación, con marca de tiempo, o solo a los que conserva?
  • ¿El sistema aplica el periodo reservado o depende de la disciplina del investigador?
  • ¿Toda puntuación reportada incluye un conteo de pruebas?
  • ¿Para qué mercado se construyó? Una biblioteca ajustada para acciones A de China incorpora límites diarios de precio y una restricción para vender acciones compradas en la misma sesión; el comportamiento de un factor bajo esas reglas no se traslada a las acciones estadounidenses.
  • ¿Puede volver a ejecutarlo y reproducir los números? Fije el commit exacto que leyó, porque un proyecto en esta etapa reescribe su código de evaluación entre un fin de semana y el siguiente.

Nada de esto vuelve inútil a un LLM en la investigación de factores. Generar hipótesis es un cuello de botella real y los modelos son buenos para esa tarea. Lo que cambia es dónde recae la carga: en contabilizar cuántas hipótesis se consumieron. Antes de que cualquiera llegue a un libro de órdenes en vivo, el paper trading es donde se hace visible la distancia entre un backtest y una ejecución.

Preguntas frecuentes sobre factores alpha con LLM

¿Puede un LLM encontrar factores alpha?

Puede proponerlos por miles, pero una propuesta no es un hallazgo. La afirmación se formula en el paso de evaluación, y una puntuación obtenida de una búsqueda amplia tiene un problema de selección que la propia puntuación no puede detectar. Evalúe primero la disciplina del periodo reservado y el conteo registrado de pruebas, antes que la expresión.

¿Qué es el índice de Sharpe deflactado?

Es una corrección que convierte un índice de Sharpe observado en la probabilidad de que una búsqueda de ese tamaño lo hubiera producido sin una ventaja real. Bailey y López de Prado lo publicaron en 2014. Su insumo central es el número de pruebas, precisamente el número que un proceso de investigación no auditable no puede proporcionar.

¿Cuántos backtests son demasiados?

No existe un umbral; solo existe un ajuste que debe aplicarse. Un backtest con una puntuación de 1.0 y diez mil backtests cuyo mejor resultado es 1.0 representan afirmaciones distintas sobre el mundo. Los lanzamientos de moneda anteriores alcanzaron 1.59 en 240 pruebas sin que hubiera información alguna en los datos.

¿Por qué se debilitan los factores publicados después de su publicación?

La investigación académica ha documentado el deterioro de las anomalías publicadas durante los años posteriores a su aparición. La congestión es uno de los mecanismos propuestos, y un resultado original sobreajustado a su propia muestra es otro; ambos producen la misma forma en un gráfico. La hipótesis de mercados eficientes explica el primero, y las pruebas anteriores demuestran el segundo.


Cada panel aquí es una consulta almacenada sobre precios reales de cierre mensual, con el SQL disponible debajo. Copie una, aumente el número de pruebas y observe cómo sube el mejor resultado en la terminal de Strasmore.

#llm#factor research#overfitting#multiple testing#quant