Trabajo con gráficos en estrategias

En StockSharp, la clase Strategy proporciona una interfaz cómoda para visualizar actividad de trading en un gráfico. En este artículo veremos cómo acceder a un gráfico desde una estrategia, crear áreas (ChartArea), agregar varios elementos (velas, indicadores, operaciones) y renderizar datos.

Acceso al gráfico

Método GetChart

Para acceder al gráfico desde una estrategia, use el método Strategy.GetChart():

protected override void OnStarted2(DateTime time)
{
	base.OnStarted2(time);
	
	// Obtener el gráfico
	_chart = GetChart();
	
	// Comprobar disponibilidad del gráfico
	if (_chart != null)
	{
		// Inicializar el gráfico
		InitializeChart();
	}
	else
	{
		// El gráfico no está disponible, por ejemplo, al ejecutarse en modo consola
		LogInfo("El gráfico no está disponible. Visualización desactivada.");
	}
}

El método GetChart() devuelve una interfaz IChart que proporciona acceso a las funciones del gráfico. Es importante comprobar el resultado contra null, ya que el gráfico puede no estar disponible, por ejemplo, al ejecutar una estrategia en modo consola o en pruebas en la nube.

Método SetChart

En algunos casos, el gráfico puede establecerse desde fuera. Para ello, use el método Strategy.SetChart:

// Establecer el gráfico desde una fuente externa
public void ConfigureVisualization(IChart chart)
{
	SetChart(chart);
	
	if (chart != null)
	{
		InitializeChart();
	}
}

Creación de áreas de gráfico

Después de obtener acceso al gráfico, puede crear una o varias áreas para mostrar distintos datos. Use el método CreateChartArea:

private void InitializeChart()
{
	// Crear el área principal para velas e indicadores
	_mainArea = CreateChartArea();
	
	// Crear un área adicional para volumen
	_volumeArea = CreateChartArea();
	
	// Configurar áreas y agregar elementos
	ConfigureChartElements();
}

También puede usar directamente el método IChart.AddArea:

private void InitializeChart()
{
	// Limpiar áreas existentes si es necesario
	foreach (var area in _chart.Areas.ToArray())
		_chart.RemoveArea(area);
	
	// Crear el área principal para velas e indicadores
	_mainArea = _chart.AddArea();
	
	// Crear un área adicional para volumen
	_volumeArea = _chart.AddArea();
	
	// Configurar áreas y agregar elementos
	ConfigureChartElements();
}

Agregar elementos al gráfico

Después de crear áreas de gráfico, puede agregar distintos elementos para mostrar datos. StockSharp admite distintos tipos de elementos, como velas, indicadores, operaciones y órdenes.

Agregar velas

Para mostrar velas, use el método AddCandles del área de gráfico:

private void ConfigureChartElements()
{
	// Agregar un elemento de velas al área principal
	_candleElement = _mainArea.AddCandles();
	
	// Configurar visualización de velas
	_candleElement.DrawStyle = ChartCandleDrawStyles.CandleStick; // Velas japonesas
	_candleElement.AntiAliasing = true; // Suavizado
	_candleElement.UpFillColor = Color.Green; // Color del cuerpo de vela alcista
	_candleElement.DownFillColor = Color.Red; // Color del cuerpo de vela bajista
	_candleElement.UpBorderColor = Color.DarkGreen; // Color del borde de vela alcista
	_candleElement.DownBorderColor = Color.DarkRed; // Color del borde de vela bajista
	_candleElement.StrokeThickness = 1; // Grosor de línea
	_candleElement.ShowAxisMarker = true; // Mostrar marcador del eje Y
}

La interfaz IChartCandleElement proporciona muchas propiedades para configurar la visualización de velas:

  • DrawStyle - estilo de visualización de velas:

    • CandleStick - velas japonesas
    • Ohlc - barras
    • LineOpen/LineHigh/LineLow/LineClose - líneas para los precios correspondientes
    • BoxVolume - cajas de volumen
    • ClusterProfile - perfil de clúster
    • Area - área
    • PnF - gráfico punto y figura
  • Ajustes de color:

    • UpFillColor/DownFillColor - color del cuerpo de vela alcista/bajista
    • UpBorderColor/DownBorderColor - color del borde de vela alcista/bajista
    • LineColor - color de línea para gráficos de tipo línea
    • AreaColor - color de área para tipo Area
  • Otros ajustes:

    • StrokeThickness - grosor de línea
    • AntiAliasing - suavizado
    • ShowAxisMarker - mostrar marcador del eje Y

Agregar indicadores

Para mostrar indicadores, use el método DrawIndicator:

// Crear indicadores
_sma = new SimpleMovingAverage { Length = SmaLength };
_bollinger = new BollingerBands
{
	Length = BollingerLength,
	Deviation = BollingerDeviation
};

// Agregar indicadores a la colección de la estrategia
Indicators.Add(_sma);
Indicators.Add(_bollinger);

// Visualizar indicadores
_smaElement = DrawIndicator(_mainArea, _sma, Color.Blue);
_bollingerUpperElement = DrawIndicator(_mainArea, _bollinger, Color.Purple);
_bollingerLowerElement = DrawIndicator(_mainArea, _bollinger, Color.Purple);
_bollingerMiddleElement = DrawIndicator(_mainArea, _bollinger, Color.Gray);

El método DrawIndicator crea automáticamente un elemento de indicador y lo agrega al área de gráfico especificada. Puede especificar un color y un color adicional para la visualización.

También puede agregar un elemento de indicador directamente mediante el método AddIndicator del área de gráfico:

// Agregar SMA directamente mediante el área de gráfico
var smaElement = _mainArea.AddIndicator(_sma);
smaElement.Color = Color.Blue;
smaElement.StrokeThickness = 2;
smaElement.DrawStyle = DrawStyles.Line;
smaElement.AntiAliasing = true;
smaElement.ShowAxisMarker = true;
smaElement.AutoAssignYAxis = true; // Asignar eje Y automáticamente

La interfaz IChartIndicatorElement proporciona las siguientes propiedades para configuración:

  • Color - color principal del indicador
  • AdditionalColor - color adicional (para indicadores con dos líneas)
  • StrokeThickness - grosor de línea
  • AntiAliasing - suavizado
  • DrawStyle - estilo de dibujo (línea, puntos, histograma, etc.)
  • ShowAxisMarker - mostrar marcador del eje Y
  • AutoAssignYAxis - asignar eje Y automáticamente

Agregar operaciones

Para mostrar operaciones, use el método DrawOwnTrades:

// Agregar un elemento para mostrar operaciones
_tradesElement = DrawOwnTrades(_mainArea);

// Configurar visualización de operaciones
_tradesElement.BuyBrush = Color.Green;  // Color de compra
_tradesElement.SellBrush = Color.Red;   // Color de venta
_tradesElement.PointSize = 10;          // Tamaño del punto

Agregar órdenes

Para mostrar órdenes, use el método DrawOrders:

// Agregar un elemento para mostrar órdenes
_ordersElement = DrawOrders(_mainArea);

// Configurar visualización de órdenes
_ordersElement.ActiveBrush = Color.Blue;     // Color de órdenes activas
_ordersElement.CanceledBrush = Color.Gray;   // Color de órdenes canceladas
_ordersElement.DoneBrush = Color.Green;      // Color de órdenes completadas
_ordersElement.ErrorColor = Color.Red;       // Color de error
_ordersElement.PointSize = 8;                // Tamaño del punto

La interfaz IChartOrderElement proporciona las siguientes propiedades para configuración:

  • ActiveBrush - color de órdenes activas
  • CanceledBrush - color de órdenes canceladas
  • DoneBrush - color de órdenes completadas
  • ErrorColor - color de error
  • ErrorStrokeColor - color de borde de error
  • Filter - filtro de visualización de órdenes

Dibujo de datos en el gráfico

Después de configurar todos los elementos de gráfico, puede pasar a dibujar datos. Se usan distintos métodos según el tipo de datos.

Dibujar velas e indicadores

La forma más eficiente de dibujar datos es usar el método IChart.Draw con un objeto IChartDrawData:

private void ProcessCandle(ICandleMessage candle)
{
	// Procesar vela en indicadores
	var smaValue = _sma.Process(candle);
	var bollingerValue = _bollinger.Process(candle);
	
	// Si el gráfico no está disponible, omitir dibujo
	if (_chart == null)
		return;
	
	// Crear datos para dibujar
	var drawData = _chart.CreateData();
	
	// Agrupar datos por hora de vela
	var group = drawData.Group(candle.OpenTime);
	
	// Agregar vela
	group.Add(_candleElement, 
		candle.DataType, 
		candle.SecurityId, 
		candle.OpenPrice, 
		candle.HighPrice, 
		candle.LowPrice, 
		candle.ClosePrice, 
		candle.PriceLevels, 
		candle.State);
	
	// Agregar valores de indicadores
	group.Add(_smaElement, smaValue);
	
	if (bollingerValue != null)
	{
		group.Add(_bollingerUpperElement, bollingerValue);
		group.Add(_bollingerMiddleElement, bollingerValue);
		group.Add(_bollingerLowerElement, bollingerValue);
	}
	
	// Dibujar datos en el gráfico
	_chart.Draw(drawData);
}

El método IChart.CreateData crea un objeto IChartDrawData usado para agrupar y agregar datos para distintos elementos de gráfico. La agrupación de datos se realiza por timestamp mediante el método Group.

Para agregar datos de distintos tipos, se usan varias sobrecargas del método Add del objeto IChartDrawDataItem.

Dibujar operaciones y órdenes

Para dibujar operaciones y órdenes, normalmente se usa un mecanismo automático que se activa cuando se reciben nuevas operaciones o cambian órdenes. Sin embargo, si se requiere dibujo manual, puede usar el siguiente código:

// Dibujar una operación
var tradeDrawData = _chart.CreateData();
var tradeGroup = tradeDrawData.Group(trade.Time);
tradeGroup.Add(_tradesElement, trade.Id, trade.StringId, trade.Side, trade.Price, trade.Volume);
_chart.Draw(tradeDrawData);

// Dibujar una orden
var orderDrawData = _chart.CreateData();
var orderGroup = orderDrawData.Group(order.Time);
orderGroup.Add(_ordersElement, order.Id, order.StringId, order.Side, order.Price, order.Volume);
_chart.Draw(orderDrawData);

Ejemplo completo de renderizado de gráfico en una estrategia

A continuación se muestra un ejemplo completo de una estrategia con configuración y renderizado de gráfico:

public class SmaStrategy : Strategy
{
	private readonly StrategyParam<int> _smaLength;
	private readonly StrategyParam<int> _bollingerLength;
	private readonly StrategyParam<decimal> _bollingerDeviation;
	
	private SimpleMovingAverage _sma;
	private BollingerBands _bollinger;
	
	private IChart _chart;
	private IChartArea _mainArea;
	private IChartArea _volumeArea;
	
	private IChartCandleElement _candleElement;
	private IChartIndicatorElement _smaElement;
	private IChartIndicatorElement _bollingerUpperElement;
	private IChartIndicatorElement _bollingerMiddleElement;
	private IChartIndicatorElement _bollingerLowerElement;
	private IChartOrderElement _ordersElement;
	private IChartTradeElement _tradesElement;
	
	public SmaStrategy()
	{
		_smaLength = Param(nameof(SmaLength), 20);
		_bollingerLength = Param(nameof(BollingerLength), 20);
		_bollingerDeviation = Param(nameof(BollingerDeviation), 2m);
	}
	
	public int SmaLength
	{
		get => _smaLength.Value;
		set => _smaLength.Value = value;
	}
	
	public int BollingerLength
	{
		get => _bollingerLength.Value;
		set => _bollingerLength.Value = value;
	}
	
	public decimal BollingerDeviation
	{
		get => _bollingerDeviation.Value;
		set => _bollingerDeviation.Value = value;
	}
	
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		
		// Crear indicadores
		_sma = new SimpleMovingAverage { Length = SmaLength };
		_bollinger = new BollingerBands
		{
			Length = BollingerLength,
			Deviation = BollingerDeviation
		};
		
		// Agregar indicadores a la colección de la estrategia
		Indicators.Add(_sma);
		Indicators.Add(_bollinger);
		
		// Obtener el gráfico
		_chart = GetChart();
		
		// Inicializar el gráfico si está disponible
		if (_chart != null)
		{
			InitializeChart();
		}
		
		// Suscribirse a velas
		var subscription = new Subscription(
			DataType.TimeFrame(TimeSpan.FromMinutes(5)),
			Security);
		
		subscription
			.WhenCandlesFinished(this)
			.Do(ProcessCandle)
			.Apply(this);
		
		Subscribe(subscription);
	}
	
	private void InitializeChart()
	{
		// Limpiar áreas existentes
		foreach (var area in _chart.Areas.ToArray())
			_chart.RemoveArea(area);
		
		// Crear el área principal para velas e indicadores
		_mainArea = _chart.AddArea();
		
		// Crear un área adicional para volumen
		_volumeArea = _chart.AddArea();
		
		// Configurar elementos del gráfico
		ConfigureChartElements();
	}
	
	private void ConfigureChartElements()
	{
		// Agregar un elemento para mostrar velas
		_candleElement = _mainArea.AddCandles();
		_candleElement.DrawStyle = ChartCandleDrawStyles.CandleStick;
		_candleElement.AntiAliasing = true;
		_candleElement.UpFillColor = Color.Green;
		_candleElement.DownFillColor = Color.Red;
		_candleElement.UpBorderColor = Color.DarkGreen;
		_candleElement.DownBorderColor = Color.DarkRed;
		_candleElement.StrokeThickness = 1;
		_candleElement.ShowAxisMarker = true;
		
		// Agregar elementos para indicadores
		_smaElement = _mainArea.AddIndicator(_sma);
		_smaElement.Color = Color.Blue;
		_smaElement.StrokeThickness = 2;
		
		_bollingerUpperElement = _mainArea.AddIndicator(_bollinger);
		_bollingerUpperElement.Color = Color.Purple;
		_bollingerUpperElement.StrokeThickness = 1;
		
		_bollingerMiddleElement = _mainArea.AddIndicator(_bollinger);
		_bollingerMiddleElement.Color = Color.Gray;
		_bollingerMiddleElement.StrokeThickness = 1;
		
		_bollingerLowerElement = _mainArea.AddIndicator(_bollinger);
		_bollingerLowerElement.Color = Color.Purple;
		_bollingerLowerElement.StrokeThickness = 1;
		
		// Agregar elementos para órdenes y operaciones
		_ordersElement = DrawOrders(_mainArea);
		_tradesElement = DrawOwnTrades(_mainArea);
	}
	
	private void ProcessCandle(ICandleMessage candle)
	{
		// Procesar vela con indicadores
		var smaValue = _sma.Process(candle);
		var bollingerValue = _bollinger.Process(candle);
		
		// Si el gráfico no está disponible, omitir dibujo
		if (_chart == null)
			return;
		
		// Dibujar datos en el gráfico
		var drawData = _chart.CreateData();
		var group = drawData.Group(candle.OpenTime);
		
		// Agregar vela
		group.Add(_candleElement, 
			candle.DataType, 
			candle.SecurityId, 
			candle.OpenPrice, 
			candle.HighPrice, 
			candle.LowPrice, 
			candle.ClosePrice, 
			candle.PriceLevels, 
			candle.State);
		
		// Agregar valores de indicadores
		group.Add(_smaElement, smaValue);
		
		if (bollingerValue != null)
		{
			group.Add(_bollingerUpperElement, bollingerValue);
			group.Add(_bollingerMiddleElement, bollingerValue);
			group.Add(_bollingerLowerElement, bollingerValue);
		}
		
		// Dibujar datos en el gráfico
		_chart.Draw(drawData);
		
		// Lógica de trading
		if (!IsFormed)
			return;
			
		// ... implementación de lógica de trading ...
	}
}

Conclusión

Usar gráficos en estrategias StockSharp permite visualizar la actividad de trading, lo que simplifica significativamente el desarrollo, la depuración y la supervisión de estrategias de trading. La clase Strategy proporciona muchos métodos para trabajar con gráficos, lo que permite agregar fácilmente distintos elementos y renderizar datos.

Al desarrollar una estrategia con interfaz gráfica, considere siempre que el gráfico puede no estar disponible, por ejemplo, al ejecutarse en modo consola o en pruebas en la nube. Por lo tanto, es importante comprobar el resultado del método GetChart() contra null y proporcionar un escenario alternativo para que la estrategia funcione sin visualización.

Ver también