Página 2 de 2

Re: MachineLearning & NinjaTrader

Publicado: 23 Dic 2025 10:22
por Optiondreamer
La IA, ML y demás técnicas, yo de momento las veo como un excelente optimizador, que un día llegue una superinteligencia artificial está por ver, pero esto es lo que hay hoy y al igual que uno llega a este mundillo y empieza a probar con chartismo, indicadores, osciladores y demás herramientas, el camino recorrido te lleva a generar un "state of art(SotA)" que hace que puedas defenderte mejor o peor, con la IA pasa parecido, depende como la alimentes y que/como le preguntes te dará unos resultados, no puedes meter un input simple y esperar un output complejo, al final como dice Fecho, si no piensas fuera del termo, estarás optimizando simplezas que no sirven. Otra visión que tengo es que son como un cofre sorpresa, las IAs van adquiriendo conocimiento(bueno y malo) y como al buscar en Google, si sabes preguntar, te puedes encontrar "SotA".

Re: MachineLearning & NinjaTrader

Publicado: 26 Abr 2026 03:02
por Fercho
He empezado el tutorial de ML en NT8 y sino fuera por tus instrucciones cls, ni la IA puede entender qué han querido hacer los de NT, qué ensalada madre mia... pues eso GRACIAS cls !!!

1) mejor que pasar los dll directo a :

C:\Users\<user>\Documents\NinjaTrader 8\bin\Custom

crear una carperta "ML" dentro de Custom y moverlos allí, así se evita que distintas versiones de NT8 entren en conflicto con los dll. A medida q avanzan las versiones de Ninja cambian los packages a los que se refieren, empiezan a ser distintos a los de los dll del tutorial, y comienzan los problemas...

Cargar los "references" luego desde ahí, el indicador usará sus dll de la carpeta ML, NT usará los propios de custom, y cada verdura en su cajón.

Además de los links de cls, agrego los que faltan:

C:\Users\<user>\.nuget\packages\newtonsoft.json\13.0.1\lib\net45
C:\Users\<user>\.nuget\packages\system.memory\4.5.5\lib\netstandard2.0
C:\Users\<user>\.nuget\packages\microsoft.ml.cpumath\3.0.0\lib\netstandard2.0

Totalizando 10 dll dentro de:

C:\Users\<user>\Documents\NinjaTrader 8\bin\Custom\ML

FastTreeNative.dll
Microsoft.ML.Core.dll
Microsoft.ML.CpuMath.dll
Microsoft.ML.Data.dll
Microsoft.ML.DataView.dll
Microsoft.ML.dll
Microsoft.ML.FastTree.dll
netstandard.dll
Newtonsoft.Json.dll
System.Memory.dll

y referenciando 6 dentro del código como lo muestra el tutorial:

Microsoft.ML.Core.dll
Microsoft.ML.Data.dll
Microsoft.ML.DataView.dll
Microsoft.ML.dll
Microsoft.ML.FastTree.dll
netstandard.dll

2) Curiosidades:
* el tutorial habla de ATR de 1 período, lo que en realidad es un True Range a secas.
** el uso de "float" en vez de "double" aquí chatGPT hace una buena advertencia, mejor usar doubles y no floats... me remito a la IA para dicha explicación... pero... supongo que Ninja aquí piensa en float porque ML está pensado a procesamiento en GPU y entonces sí float rinde mejor (?)

3) Reorganizada de código (iré actualizando)

Código: Seleccionar todo

#region Using declarations
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Trainers.FastTree;
using NinjaTrader.Gui;
using NinjaTrader.NinjaScript.DrawingTools;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Windows.Media;
#endregion

namespace NinjaTrader.NinjaScript.Indicators.MLNET
{
	public class MLTrendPrediction : Indicator
	{
        private MLContext mlContext;
		private TransformerChain<RegressionPredictionTransformer<FastTreeRegressionModelParameters>> closeModel;

        protected override void OnStateChange()
		{
            #region SetDefaults

			if (State == State.SetDefaults)
            {
                Description = @"Predicting Price Trends Using ML.NET in NinjaTrader 8";
				Name = "MLPrediction";
				Calculate = Calculate.OnBarClose;
				IsOverlay = true;
				DisplayInDataBox = true;
				DrawOnPricePanel = true;
				DrawHorizontalGridLines = true;
				DrawVerticalGridLines = true;
				PaintPriceMarkers = true;
				ScaleJustification = Gui.Chart.ScaleJustification.Right;
				//Disable this property if your indicator requires custom values that cumulate with each new market data event. 
				//See Help Guide for additional information.
				IsSuspendedWhileInactive = true;

				TrainingPeriod    = 500;
				LookbackPeriod    = 12;
				PredictionHorizon = 14;
				PredictionLoop    = 300;

				NumberOfLeaves    = 20;
				MinimumExampleCounterPerLeaf = 20;
				LearningRate      = 0.05;
            }
            #endregion

            else if (State == State.DataLoaded)
			{
				mlContext = new MLContext();
			}
		}

		#region HELPERS

		#region ML DLLs Loader

		static MLTrendPrediction()
	    {
	        AppDomain.CurrentDomain.AssemblyResolve += ResolveML;
	    }

	    private static System.Reflection.Assembly ResolveML(object sender, ResolveEventArgs args)
	    {
	        string basePath = System.IO.Path.Combine(
	            Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
	            @"NinjaTrader 8\bin\Custom\ML");

	        string dllName = new System.Reflection.AssemblyName(args.Name).Name + ".dll";
	        string fullPath = System.IO.Path.Combine(basePath, dllName);

	        if (System.IO.File.Exists(fullPath))
	            return System.Reflection.Assembly.LoadFrom(fullPath);

	        return null;
	    }
		#endregion

		#region BarData class

		public class BarData
		{
			public float Open1 { get; set; }
			public float Open2 { get; set; }
			public float Open3 { get; set; }
			public float Open4 { get; set; }
			public float High1 { get; set; }
			public float High2 { get; set; }
			public float High3 { get; set; }
			public float High4 { get; set; }
			public float Low1 { get; set; }
			public float Low2 { get; set; }
			public float Low3 { get; set; }
			public float Low4 { get; set; }
			public float Close1 { get; set; }
			public float Close2 { get; set; }
			public float Close3 { get; set; }
			public float Close4 { get; set; }
			public float ATR1 { get; set; }
			public float ATR2 { get; set; }
			public float ATR3 { get; set; }
			public float ATR4 { get; set; }
			public float EMA1 { get; set; }
	        public float EMA2 { get; set; }
	        public float EMA3 { get; set; }
	        public float EMA4 { get; set; }
	        public float FutureClose { get; set; }
	    }

		public class ClosePrediction
		{
			[ColumnName("Score")]
			public float FutureClose { get; set; }
		}
		#endregion

		#region CalculateIndices

        private List<int> CalculateIndices(int period)
		{
			var indices = new List<int>();

			double step = period / 3.0;
			for(int i = 0; i <= 3; i++)
			{
				indices.Add((int)Math.Round(i * step));
			}
			return indices;
		}
        #endregion

        #region PrepareData

        public List<BarData> PrepareData()
		{
			List<BarData> data = new List<BarData>();
			List<int> idx = CalculateIndices(LookbackPeriod);

			for(int i = 1; i < TrainingPeriod; i++)
			{
				data.Add(new BarData
				{
					Open1 = (float)Open[i + PredictionHorizon],
					Open2 = (float)Open[i + PredictionHorizon + idx[1]],
					Open3 = (float)Open[i + PredictionHorizon + idx[2]],
					Open4 = (float)Open[i + PredictionHorizon + idx[3]],

					High1 = (float)High[i + PredictionHorizon],
					High2 = (float)High[i + PredictionHorizon + idx[1]],
					High3 = (float)High[i + PredictionHorizon + idx[2]],
					High4 = (float)High[i + PredictionHorizon + idx[3]],

					Low1 = (float)Low[i + PredictionHorizon],
					Low2 = (float)Low[i + PredictionHorizon + idx[1]],
					Low3 = (float)Low[i + PredictionHorizon + idx[2]],
					Low4 = (float)Low[i + PredictionHorizon + idx[3]],

					Close1 = (float)Close[i + PredictionHorizon],
					Close2 = (float)Close[i + PredictionHorizon + idx[1]],
					Close3 = (float)Close[i + PredictionHorizon + idx[2]],
					Close4 = (float)Close[i + PredictionHorizon + idx[3]],

					ATR1 = (float)ATR(1)[i + PredictionHorizon],
					ATR2 = (float)ATR(1)[i + PredictionHorizon + idx[1]],
					ATR3 = (float)ATR(1)[i + PredictionHorizon + idx[2]],
					ATR4 = (float)ATR(1)[i + PredictionHorizon + idx[3]],

					EMA1 = (float)EMA(LookbackPeriod)[i + PredictionHorizon],
					EMA2 = (float)EMA(LookbackPeriod)[i + PredictionHorizon + idx[1]],
					EMA3 = (float)EMA(LookbackPeriod)[i + PredictionHorizon + idx[2]],
					EMA4 = (float)EMA(LookbackPeriod)[i + PredictionHorizon + idx[3]],

					FutureClose = (float)Close[i]
				});
			}
			return data;
		}
        #endregion

		#region TrainModel

        private void TrainModel()
		{
			var data = PrepareData();

			if(!data.Any())
			{
				Print("Error: no hay datos disponibles para el entrenamiento");
				return;
			}

			var trainingDataView = mlContext.Data.LoadFromEnumerable(data);

			try
			{
				var pipelineClose = 
					mlContext.Transforms.Concatenate("Features", 
					"Open1", "Open2", "Open3", "Open4",
					"High1", "High2", "High3", "High4",
					"Low1", "Low2", "Low3", "Low4",
					"Close1", "Close2", "Close3", "Close4",
					"ATR1", "ATR2", "ATR3", "ATR4",
					"EMA1", "EMA2", "EMA3", "EMA4")
					.Append(mlContext.Transforms.CopyColumns(outputColumnName: "Label", inputColumnName: "FutureClose"))
					.Append(mlContext.Regression.Trainers.FastTree(
					numberOfLeaves: NumberOfLeaves,
					minimumExampleCountPerLeaf: MinimumExampleCounterPerLeaf,
					learningRate: LearningRate));

				closeModel = pipelineClose.Fit(trainingDataView);

				var metricsClose = mlContext.Regression.Evaluate(closeModel.Transform(trainingDataView));
				Print("RSquared: " + metricsClose.RSquared + " RMSE: " + metricsClose.RootMeanSquaredError);

                Print("TrainModel completado con éxito");
            }
			catch (Exception ex)
			{
				Print("Error durante el TrainModel: " + ex.ToString());
			}
		}
        #endregion

        #region PredictLevels

        private void PredictLevels()
		{
			if(closeModel == null)
			{
				Print("Error: modelo no entrenado... no hay predicción");
				return;
			}

			try
			{
				var predictionEngineClose = mlContext.Model.CreatePredictionEngine<BarData, ClosePrediction>(closeModel);

				List<int> idx = CalculateIndices(LookbackPeriod);

				var currentbarData = new BarData
				{
					Open1 = (float)Open[0],
					Open2 = (float)Open[idx[1]],
					Open3 = (float)Open[idx[2]],
					Open4 = (float)Open[idx[3]],

					High1 = (float)High[0],
					High2 = (float)High[idx[1]],
					High3 = (float)High[idx[2]],
					High4 = (float)High[idx[3]],

					Low1 = (float)Low[0],
					Low2 = (float)Low[idx[1]],
					Low3 = (float)Low[idx[2]],
					Low4 = (float)Low[idx[3]],

					Close1 = (float)Close[0],
					Close2 = (float)Close[idx[1]],
					Close3 = (float)Close[idx[2]],
					Close4 = (float)Close[idx[3]],

					ATR1 = (float)ATR(1)[0],
					ATR2 = (float)ATR(1)[idx[1]],
					ATR3 = (float)ATR(1)[idx[2]],
					ATR4 = (float)ATR(1)[idx[3]],

					EMA1 = (float)EMA(LookbackPeriod)[0],
					EMA2 = (float)EMA(LookbackPeriod)[idx[1]],
					EMA3 = (float)EMA(LookbackPeriod)[idx[2]],
					EMA4 = (float)EMA(LookbackPeriod)[idx[3]],
				};

				var predictionClose = predictionEngineClose.Predict(currentbarData);

				Print("Predicción = " + predictionClose.FutureClose);

				Draw.ArrowLine(this, "PredictDirection " + CurrentBar + " " + PredictionHorizon, 0, Close[0], 
					-PredictionHorizon, predictionClose.FutureClose, 
					Close[0] < predictionClose.FutureClose ? Brushes.DodgerBlue : 
					Close[0] > predictionClose.FutureClose ? Brushes.Magenta : Brushes.Silver, 
					DashStyleHelper.Solid, 3);
			}
			catch (Exception ex)
			{
				Print("Error durante la predicción: " + ex.ToString());
			}
		}
        #endregion

        #endregion

        protected override void OnBarUpdate()
		{
			if (BarsInProgress != 0 || CurrentBar < Math.Max(BarsRequiredToPlot,  PredictionHorizon + TrainingPeriod))
				return;

			try
			{
				if (CurrentBar % PredictionLoop == 0)
				{
					TrainModel();
					PredictLevels();
				}
			}
			catch (Exception ex)
			{
			    Print("Error en OnBarUpdate: " + ex.ToString());
			}
		}

		#region Properties

		[Range(100, int.MaxValue), NinjaScriptProperty]
		[Display(ResourceType = typeof(Custom.Resource), Name = "Training Period", Description = "Número de barras para el entrenamiento (mín. recomendado 100)", GroupName = "Properties", Order = 0)]
		public int TrainingPeriod
		{ get; set; }

		[Range(1, int.MaxValue), NinjaScriptProperty]
		[Display(ResourceType = typeof(Custom.Resource), Name = "Lookback Period", Description = "Período de los indicadores que se utilicen como features", GroupName = "Properties", Order = 1)]
		public int LookbackPeriod
		{ get; set; }

		[Range(1, int.MaxValue), NinjaScriptProperty]
		[Display(ResourceType = typeof(Custom.Resource), Name = "Prediction Horizon", Description = "Número de barras hacia delante donde se realiza la predicción", GroupName = "Properties", Order = 2)]
		public int PredictionHorizon
		{ get; set; }

		[Range(50, int.MaxValue), NinjaScriptProperty]
		[Display(ResourceType = typeof(Custom.Resource), Name = "Prediction Loop", Description = "Informa cada cuántas barras debe ejecutarse el modelo: entrenamiento + predicción (mín. 50)", GroupName = "Properties", Order = 3)]
		public int PredictionLoop
		{ get; set; }

		[Range(1, int.MaxValue), NinjaScriptProperty]
		[Display(ResourceType = typeof(Custom.Resource), Name = "Number of Leaves", Description = "Controla la complejidad de cada árbol", GroupName = "Model Properties", Order = 0)]
		public int NumberOfLeaves
		{ get; set; }

		[Range(1, int.MaxValue), NinjaScriptProperty]
		[Display(ResourceType = typeof(Custom.Resource), Name = "Min Examples Per Leaf", Description = "Número mínimo de muestras por hoja (evita overfitting)", GroupName = "Model Properties", Order = 1)]
		public int MinimumExampleCounterPerLeaf
		{ get; set; }

		[Range(0.0001, double.MaxValue), NinjaScriptProperty]
		[Display(ResourceType = typeof(Custom.Resource), Name = "Learning Rate", Description = "Velocidad de aprendizaje del modelo (tradeoff entre rapidez y precisión)", GroupName = "Model Properties", Order = 2)]
		public double LearningRate
		{ get; set; }
        #endregion
    }
}

Re: MachineLearning & NinjaTrader

Publicado: 27 Abr 2026 10:14
por cls
De nada Fercho, me alegro de que te haya servido. No he hecho nada más con ML.NET. Hice unas pruebas con Python + LightGBM, pero otra vez agua. Me apoyé en Claude para montar toda la pipeline en Python (sorprendente lo rápido que lo hizo, yo hubiera tardado días como poco lidiando con errores de instalación, librerías incompatibles, sobre todo errores de integración que te obligan a perder horas navegando en la web para encontrar soluciones; eso la IA te lo resuelve al instante).
Luego la programación en NinjaTrader para grabar los datos a fichero para después importarlos en el modelo de LightGBM. Esto es lo único que hice por mi cuenta.
Luego importar los datos y ejecutar el modelo en un Notebook. Y por último interpretar los resultados con Claude. Y como resulta que este modelo tiene un montón de parámetros - supongo que como
cualquier otro modelo de ML - los resultados eran muy dependientes de ellos (demasiado) y la duda final ... no estaré sobreoptimizando? (mi sensación es que sí). Y ahí lo dejé.
Suerte con tu proyecto.

Re: MachineLearning & NinjaTrader

Publicado: 27 Abr 2026 16:18
por Fercho
Que tal Cls, por lo que venimos viendo y discutiendo aquí en el foro, pienso que la IA (y matices) no serviría para generar un algoritmo/estrategia ganadora "per se". Basta con ver los sub rendimientos comparados con cualquier índice de fondos IA que hay dando vueltas.

O aquel hilo en donde X las mostraba compitiendo entre sí, para finalmente verlas todas acabar en una distribución de equities muy parecida a la de un flip coin.... O aquello que alguna vez se comentó, que la IA china esta deepseek nació sí como una técnica exitosa de expimir ganancias, pero que luego "se cayó" al popularizarse entre los fondos cada vez más la técnica... como siempre, digamos.

Además esto del ML/IA no es nuevo, lo que sí es nuevo hoy son por un lado, la disponibilidad de inmensas bases de datos, y por otro un poder computacional extraordinario, algo impensado 20-50 años atrás; que sí "empodera" los algoritmos de ML obteniendo resultados nunca antes vistos, como por ej. lo de Anthropic el haber descubierto el primer bug en Linux que nunca antes ningún programador vió.

Vamos tal vez al fin de los pantallazos azules de Windows o a olvidarnos de la función reiniciar en un PC..... Pero el mercado, sigue siendo al final operado por humanos, porque los humanos son los que ponen su casa y su coche en juego (propio o ajeno :twisted: ) y eso es lo que luego mueve los precios: las emociones; por más que luego se intente codificarlas en un algoritmo (con/sin IA).

Se trata más de reglas de supervivencia pura y básicas, que de complejas fórmulas matemáticas, pythons, robots, etc...

El paper este que Gekko ha compartido no deja de maravillarme, hace una descripción hermosa de porqué el mercado no es matemático/físico sino más bien psicológico/emocional.

Y otra gran verdad es que, por más que tengamos una estrategia ganadora y muy bien hecha, el mercado puede cambiar las reglas, sin avisarnos, y de forma totalmente aleatoria. A veces no somos nosotros... sino él :lol:

Entonces por qué continuar insistiendo en poner tanta energía en fórmulas, códigos, pythons, quants, matemática, etc ? Es decir... como eje principal. Debería ser parte, y no el todo.

Una cuestión de enfoque.

WARNING: Physics Envy May Be Hazardous To Your Wealth

https://www.fpablovi.org/articulos-bioe ... demos-usar