Escrevemos robôs de negociação usando a estrutura gráfica StockSharp. Parte 2



Continuamos a falar sobre a criação de robôs de negociação usando a plataforma StockSharp . O primeiro artigo tratou da criação do projeto e do desenho dos principais elementos do sistema de negociação. No material final do ciclo, implementaremos diretamente a estratégia de negociação.

Criando um painel de portfólio


Por analogia com a barra de ferramentas, crie um painel de log. Para fazer isso, adicione outro UserControl à pasta XAML. Atribua a ele o nome PortfolioGridControl. Adicione um elemento PortfolioGrid a ele.

<UserControl x:Class="ShellNew.XAML.PortfolioGridControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:xaml="http://schemas.stocksharp.com/xaml" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <xaml:PortfolioGrid x:Name="PortfolioGrid" /> </UserControl> 

No construtor PortfolioGridControl, precisamos assinar os eventos da aparência de um novo portfólio e o evento da aparência de uma nova posição no Connector.

 public PortfolioGridControl() { InitializeComponent(); MainWindow.Instance.Connector.NewPortfolio += PortfolioGrid.Portfolios.Add; MainWindow.Instance.Connector.NewPosition += PortfolioGrid.Positions.Add; } 

Assim, ao criar um novo portfólio, ele aparecerá no painel do portfólio e, quando uma nova posição aparecer no painel do portfólio, ele será atualizado.

Na parte central do MainWindow, adicione o painel PortfolioGridControl criado:

 <dxlc:LayoutGroup HorizontalAlignment="Stretch" View="Tabs"> <!--  --> <dxlc:LayoutGroup Header="Securities"> <myxaml:SecurityGridControl x:Name="SecurityPanel" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Portfolios"> <myxaml:PortfolioGridControl x:Name="PortfolioGridControl" /> </dxlc:LayoutGroup> </dxlc:LayoutGroup> 

Execute para verificar:



Temos uma guia com portfólios.

Criando um painel de pedidos


A barra de pedidos no S # .API tem a capacidade de fazer pedidos, retirar pedidos e registrar novamente. Por analogia com a barra de ferramentas, crie um painel de pedidos, adicione outro UserControl à pasta XAML. Atribua a ele o nome OrderGridControl. Adicione o elemento OrderGrid a ele:

 <UserControl x:Class="ShellNew.XAML.OrderGridControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:xaml="http://schemas.stocksharp.com/xaml" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <xaml:OrderGrid x:Name="OrderGrid" /> </UserControl> 

OrderGrid tem um evento de registro OrderRegistering, evento de re-registro OrderReRegistering e evento de cancelamento OrderCanceling.

Vamos criar seus manipuladores:

 <UserControl x:Class="ShellNew.XAML.OrderGridControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:xaml="http://schemas.stocksharp.com/xaml" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <xaml:OrderGrid x:Name="OrderGrid" OrderRegistering="OrderGrid_OnOrderRegistering" OrderReRegistering="OrderGrid_OnOrderReRegistering" OrderCanceling="OrderGrid_OnOrderCanceling" /> </UserControl> 

No manipulador de eventos de solicitação de registro, criamos uma janela OrderWindow na qual você precisa especificar fontes de dados para instrumentos, portfólios e dados de mercado. No nosso caso, tudo será Connector.

Em seguida, chamamos OrderWindow com o método ShowModal. Se o botão OK foi pressionado nessa janela, através do conector, registramos usando o método RegisterOrder:

 private void OrderGrid_OnOrderRegistering() { var newOrder = new OrderWindow { Title = "Order registering", Order = new Order(), SecurityProvider = MainWindow.Instance.Connector, MarketDataProvider = MainWindow.Instance.Connector, Portfolios = new PortfolioDataSource(MainWindow.Instance.Connector), }; if (newOrder.ShowModal(this)) MainWindow.Instance.Connector.RegisterOrder(newOrder.Order); } 

No manipulador de eventos de recadastramento de eventos, fazemos tudo da mesma maneira. Somente nesse caso o objeto Order chega ao evento - esse é um pedido que precisa ser registrado novamente. Portanto, no OrderWindow, especificamos Order = order.ReRegisterClone(newVolume: order.Balance) para preencher os campos da janela OrderWindow.

Em seguida, chamamos OrderWindow com o método ShowModal. Se o botão OK foi clicado nesta janela, registraremos novamente o aplicativo através do conector usando o método ReRegisterClone. Transferimos para ele o aplicativo antigo, que deve ser cancelado, e o novo, que deve ser definido.

 private void OrderGrid_OnOrderReRegistering(Order order) { var window = new OrderWindow { Title = "Order re-registering", SecurityProvider = MainWindow.Instance.Connector, MarketDataProvider = MainWindow.Instance.Connector, Portfolios = new PortfolioDataSource(MainWindow.Instance.Connector), Order = order.ReRegisterClone(newVolume: order.Balance) }; if (window.ShowModal(this)) MainWindow.Instance.Connector.ReRegisterOrder(order, window.Order); } 

No manipulador de eventos de cancelamento de solicitação, basta chamar o método CancelOrder e passar o pedido para ele, que deve ser cancelado:

 private void OrderGrid_OnOrderCanceling(Order order) { MainWindow.Instance.Connector.CancelOrder(order); } 

Para que os pedidos sejam exibidos no OrderGrid, você precisa se inscrever nos eventos de aparência de um novo pedido e no evento de erro de registro no construtor OrderGridControl e depois transferir esses eventos para o OrderGrid:

 public OrderGridControl() { InitializeComponent(); MainWindow.Instance.Connector.NewOrder += OrderGrid.Orders.Add; MainWindow.Instance.Connector.OrderRegisterFailed += OrderGrid.AddRegistrationFail; } 

Na parte central do MainWindow, adicione o painel OrderGridControl criado:

 <dxlc:LayoutGroup HorizontalAlignment="Stretch" View="Tabs"> <!--  --> <dxlc:LayoutGroup Header="Securities"> <myxaml:SecurityGridControl x:Name="SecurityPanel" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Portfolios"> <myxaml:PortfolioGridControl x:Name="PortfolioGridControl" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Orders"> <myxaml:OrderGridControl x:Name="OrderGridControl" /> </dxlc:LayoutGroup> </dxlc:LayoutGroup> 

Execute para verificar:



Criando seu próprio painel de transações


Por analogia com a barra de ferramentas, crie um painel de nossas próprias transações. Na pasta XAML, adicione outro UserControl. Atribua a ele o nome MyTradeGridControl. Adicione o elemento MyTradeGrid a ele:

 <UserControl x:Class="ShellNew.XAML.MyTradeGridControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:xaml="http://schemas.stocksharp.com/xaml" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <xaml:MyTradeGrid x:Name="MyTradeGrid" /> </UserControl> 

No construtor MyTradeGridControl, precisamos assinar os eventos de aparência de uma nova transação própria e transferi-la para MyTradeGrid:

 public MyTradeGridControl() { InitializeComponent(); MainWindow.Instance.Connector.NewMyTrade += MyTradeGrid.Trades.Add; } 

Na parte central do MainWindow, adicione o painel OrderGridControl criado:

 <dxlc:LayoutGroup HorizontalAlignment="Stretch" View="Tabs"> <!--  --> <dxlc:LayoutGroup Header="Securities"> <myxaml:SecurityGridControl x:Name="SecurityPanel" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Portfolios"> <myxaml:PortfolioGridControl x:Name="PortfolioGridControl" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Orders"> <myxaml:OrderGridControl x:Name="OrderGridControl" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="MyTrades"> <myxaml:MyTradeGridControl x:Name="MyTradeGridControl" /> </dxlc:LayoutGroup> </dxlc:LayoutGroup> 

Execute para verificar:



Criando um painel com uma estratégia


Criaremos um painel de estratégia da mesma maneira que todos os painéis anteriores. Na pasta XAML, adicione outro UserControl. Atribua a ele o nome StrategyControl. Usando o LayoutControl, dividimos a tela em duas partes. No lado esquerdo, haverá uma guia com um gráfico de velas e uma guia com estatísticas da estratégia:

 <dxlc:LayoutGroup Orientation="Vertical"> <dxlc:LayoutGroup View="Tabs" Name="StatistisAndChartLayoutGroup"> <dxlc:LayoutGroup Header="Chart"> <xaml:Chart x:Name="Chart" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Statistic"> <dxlc:LayoutItem VerticalAlignment="Stretch" dxlc:LayoutControl.AllowHorizontalSizing="True" > <xaml:StatisticParameterGrid x:Name="StatisticParameterGrid" MaxHeight="2000"/> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch" > <xaml:EquityCurveChart x:Name="EquityCurveChart" /> </dxlc:LayoutItem> </dxlc:LayoutGroup> </dxlc:LayoutGroup> </dxlc:LayoutGroup> 

Aqui, StatisticParameterGrid é usado para exibir estatísticas da estratégia e EquityCurveChart é usado para exibir o gráfico de ganhos e perdas. O StatisticParameterGrid precisa definir algum valor MaxHeight, caso contrário, o aplicativo não será iniciado.

O lado direito irá configurar as propriedades da estratégia em PropertyGridEx. Os botões para iniciar e parar a estratégia também estarão localizados:

 <dxlc:LayoutGroup View="Group" dxlc:LayoutControl.AllowHorizontalSizing="True" dxlc:DockLayoutControl.Dock="Right" Orientation="Vertical"> <dxlc:LayoutItem VerticalAlignment="Stretch"> <xaml:PropertyGridEx x:Name="PropertyGridEx" /> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch" Height="20"> <dx:SimpleButton x:Name="StartStrategyButton" Content="Start strategy" ToolTip="Start strategy" Click="StartStrategyButton_Click" /> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch" Height="20"> <dx:SimpleButton x:Name="StopStrategyButton" Content="Stop strategy" ToolTip="Stop strategy" Click="StopStrategyButton_Click" /> </dxlc:LayoutItem> </dxlc:LayoutGroup> 

Código completo:

 <UserControl x:Class="ShellNew.XAML.StrategyControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" xmlns:xaml="http://schemas.stocksharp.com/xaml" xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" mc:Ignorable="d"> <dxlc:LayoutControl> <dxlc:LayoutGroup Orientation="Vertical"> <dxlc:LayoutGroup View="Tabs" Name="StatistisAndChartLayoutGroup"> <dxlc:LayoutGroup Header="Chart"> <xaml:Chart x:Name="Chart" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Statistic"> <dxlc:LayoutItem VerticalAlignment="Stretch" dxlc:LayoutControl.AllowHorizontalSizing="True" > <xaml:StatisticParameterGrid x:Name="StatisticParameterGrid" MaxHeight="2000"/> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch" > <xaml:EquityCurveChart x:Name="EquityCurveChart" /> </dxlc:LayoutItem> </dxlc:LayoutGroup> </dxlc:LayoutGroup> </dxlc:LayoutGroup> <dxlc:LayoutGroup View="Group" dxlc:LayoutControl.AllowHorizontalSizing="True" dxlc:DockLayoutControl.Dock="Right" Orientation="Vertical"> <dxlc:LayoutItem VerticalAlignment="Stretch"> <xaml:PropertyGridEx x:Name="PropertyGridEx" /> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch" Height="20"> <dx:SimpleButton x:Name="StartStrategyButton" Content="Start strategy" ToolTip="Start strategy" Click="StartStrategyButton_Click" /> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch" Height="20"> <dx:SimpleButton x:Name="StopStrategyButton" Content="Stop strategy" ToolTip="Stop strategy" Click="StopStrategyButton_Click" /> </dxlc:LayoutItem> </dxlc:LayoutGroup> </dxlc:LayoutControl> </UserControl> 

No construtor StrategyControl, defina o Connector como a fonte de dados para o PropertyGridEx, em quase todos os controles que realizamos ações semelhantes:

 public StrategyControl() { InitializeComponent(); PropertyGridEx.SecurityProvider = MainWindow.Instance.Connector; PropertyGridEx.Portfolios = new PortfolioDataSource(MainWindow.Instance.Connector); } 

De alguma forma, precisamos transferir a estratégia para nosso controle. Para fazer isso, crie um método BindStraregy no StrategyControl, que utilizará a estratégia, salve o link em uma variável local e defina a estratégia em PropertyGridEx e StatisticParameterGrid.

Usando o método SetChart, transferimos o gráfico de velas do Chart para a estratégia; depois disso, você pode obter a estratégia do Chart usando o método GetChart. Também definimos o Conector para a estratégia.

 private Strategy _strategy; public void BindStraregy(Strategy strategy) { _strategy = strategy; PropertyGridEx.SelectedObject = strategy; StatisticParameterGrid.Parameters. AddRange(_strategy.StatisticManager.Parameters); _strategy.SetChart(Chart); _strategy.Connector = MainWindow.Instance.Connector; } 

Ao trabalhar com o cronograma de lucros e perdas, deve-se notar que a estratégia será iniciada e interrompida, talvez várias vezes, o poema deve ser limpo a cada lançamento da estratégia. Para isso, criaremos o método ResetEquityCurveChart no qual limparemos primeiro o EquityCurveChart. Depois disso, precisamos criar elementos gráficos para o EquityCurveChart, eles podem especificar o nome, a cor e o tipo de linha.

Depois disso, assinamos o evento de mudança de PnL da estratégia e, no manipulador deste evento, desenhamos um novo valor no gráfico de ganhos e perdas do EquityCurveChart:

 private void ResetEquityCurveChart() { EquityCurveChart.Clear(); var pnl = EquityCurveChart. CreateCurve("PNL", Colors.Green, ChartIndicatorDrawStyles.Area); var unrealizedPnL = EquityCurveChart. CreateCurve("unrealizedPnL", Colors.Black, ChartIndicatorDrawStyles.Line); var commissionCurve = EquityCurveChart .CreateCurve("commissionCurve", Colors.Red, ChartIndicatorDrawStyles.Line); _strategy.PnLChanged += () => { var data = new ChartDrawData(); data.Group(_strategy.CurrentTime) .Add(pnl, _strategy.PnL) .Add(unrealizedPnL, _strategy.PnLManager.UnrealizedPnL ?? 0) .Add(commissionCurve, _strategy.Commission ?? 0); EquityCurveChart.Draw(data); }; } 

No manipulador de eventos para clicar no botão Iniciar, chamaremos esse método. E também redefiniremos o estado da estratégia e a executaremos:

 private void StartStrategyButton_Click(object sender, RoutedEventArgs e) { ResetEquityCurveChart(); _strategy.Reset(); _strategy.Start(); } 

No manipulador de eventos para clicar no botão Parar, interromperemos a estratégia.
private void StopStrategyButton_Click (remetente do objeto, RoutedEventArgs e):

 { _strategy.Stop(); } 

Na parte central do MainWindow, adicione o painel StrategyControl criado:

 <dxlc:LayoutGroup HorizontalAlignment="Stretch" View="Tabs"> <!--  --> <dxlc:LayoutGroup Header="Securities"> <myxaml:SecurityGridControl x:Name="SecurityPanel" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Portfolios"> <myxaml:PortfolioGridControl x:Name="PortfolioGridControl" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="Orders"> <myxaml:OrderGridControl x:Name="OrderGridControl" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="MyTrades"> <myxaml:MyTradeGridControl x:Name="MyTradeGridControl" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="MarketDepth"> <myxaml:MarketDepthControl x:Name="MarketDepthControl" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Header="SimpleStrategyControl"> <myxaml:StrategyControl x:Name="SimpleStrategyControl" /> </dxlc:LayoutGroup> </dxlc:LayoutGroup> 

Criação de estratégia


Por exemplo, considere criar uma estratégia simples de candlestick. Ela comprará se a vela estiver crescendo (verde) e venderá se a vela estiver diminuindo (vermelha).

Vamos criar outra pasta no projeto - armazenaremos todas as nossas estratégias nele. Nesta pasta, crie uma nova classe, chame de SimpleStrategy. Todas as estratégias de S # devem herdar da classe base da estratégia de estratégia.

 public class SimpleStrategy : Strategy {} 

Como nossa estratégia usa velas, criaremos a propriedade pública CandleSeries e, no construtor de nossa estratégia, a definiremos como o valor padrão.

 public class SimpleStrategy : Strategy { public CandleSeries Series { get; set; } public SimpleStrategy() { Series = new CandleSeries(typeof(TimeFrameCandle), new Security(), TimeSpan.FromSeconds(15)) { BuildCandlesMode = MarketDataBuildModes.Build }; } } 

Aqui indicamos que as velas no CandleSeries serão TimeFrameCandle, com um intervalo de 15 segundos (TimeSpan.FromSeconds (15)). Para CandleSeries, você pode especificar o modo de criação de vela BuildCandlesMode. Indicamos que as velas serão construídas (MarketDataBuildModes.Build). Por padrão, eles serão criados a partir de ticks, mas você pode especificar outros tipos de dados.

Como fizemos do CandleSeries uma propriedade pública, o CandleSeries pode ser personalizado ainda mais a partir do PropertyGridEx descrito no parágrafo anterior. Todas as estratégias têm métodos que podem ser substituídos, precisamos substituir o método OnStarted. Ele é chamado antes de iniciar a estratégia e permite predefinir um estado inicial.

 protected override void OnStarted() { _connector = (Connector)Connector; Series.Security = Security; _connector .WhenCandlesFinished(Series) .Do(ProcessCandle) .Apply(this); _connector.SubscribeCandles(Series); base.OnStarted(); } 

Aqui, para o CandleSeries, definimos a ferramenta especificada no PropertyGridEx. Em seguida, crie uma regra para processar a vela finalizada. Na regra, especifique o método que processará cada vela finalizada - no nosso caso, este é o método ProcessCandle. Será descrito mais tarde. Depois que tudo estiver definido, assinamos a aparência de velas no CandleSeries no conector por meio do método SubscribeCandles. No nosso caso, o método ProcessCandle contém a principal lógica da estratégia:

 private void ProcessCandle(Candle candle) { if (!IsRealTime(candle)) return; if (candle.OpenPrice < candle.ClosePrice && Position <= 0) { RegisterOrder(this.BuyAtMarket(Volume + Math.Abs(Position))); } else if (candle.OpenPrice > candle.ClosePrice && Position >= 0) { RegisterOrder(this.SellAtMarket(Volume + Math.Abs(Position))); } } 

Primeiro de tudo, precisamos determinar se a vela está sendo construída em tempo real ou é histórica. Se a vela é histórica, nós a ignoramos. Nem todas as estratégias exigem isso, por exemplo, estratégias baseadas em óculos não exigem isso, já que os óculos sempre funcionam em tempo real. Não existe uma maneira universal de determinar se uma vela é “tempo real” ou histórica, e em cada estratégia esse problema deverá ser resolvido independentemente, dependendo dos requisitos. Nesse caso, simplesmente compararemos o tempo que a vela fecha com a hora no conector e, se não exceder um certo atraso, a vela terá status em tempo real.

 private bool IsRealTime(Candle candle) { return (Connector.CurrentTime - candle.CloseTime).TotalSeconds < 10; } 

A seguir, analisamos que tipo de vela é e qual é a posição atual da estratégia. Se a vela estiver crescendo, com uma posição igual a 0, abriremos a posição com uma ordem de mercado para o volume definido por nós no PropertyGridEx. Se a vela estiver crescendo e a posição for menor que 0, então "inverteremos" a posição:

 if (candle.OpenPrice < candle.ClosePrice && Position <= 0) { RegisterOrder(this.BuyAtMarket(Volume + Math.Abs(Position))); } 


Fazemos o oposto para uma vela minguante:

 else if (candle.OpenPrice > candle.ClosePrice && Position >= 0) { RegisterOrder(this.SellAtMarket(Volume + Math.Abs(Position))); } 

No momento, nossa estratégia está pronta para funcionar. Ele deve ser passado para o SimpleStrategyControl, que criamos no parágrafo anterior, usando o método BindStraregy. Fazemos isso no construtor MainWindow imediatamente após inicializar os componentes MainWindow.

 SimpleStrategyControl.BindStraregy(new SimpleStrategy()); 


Execute para verificar:





A estratégia funciona, acordos são feitos, mas até agora não há velas e acordos no gráfico.

Adicionando velas e ofertas ao gráfico da estratégia


Na seção sobre o painel de estratégia usando o método SetChart, traímos o gráfico de velas do Chart para a estratégia. No método OnStarted da estratégia, verificamos se o gráfico está definido para a estratégia e se está definido, inicializamos o gráfico e também assinamos os eventos de aparência de uma nova transação própria e uma troca de velas.

 protected override void OnStarted() { _connector = (Connector)Connector; if (this.GetChart() is Chart chart) { InitChart(chart); NewMyTrade += DrawMyTrade; _connector.CandleSeriesProcessing += CandleSeriesProcessing; } Series.Security = Security; _connector .WhenCandlesFinished(Series) .Do(ProcessCandle) .Apply(this); _connector.SubscribeCandles(Series); base.OnStarted(); } 

Método de inicialização do gráfico InitChart:

 private ChartCandleElement _chartCandleElement; private ChartTradeElement _tradesElem; private Chart _chart; private void InitChart(Chart chart) { _chart = chart; _chart.GuiSync(() => { _chart.ClearAreas(); var candlesArea = new ChartArea(); _chart.AddArea(candlesArea); _chartCandleElement = new ChartCandleElement(); _chart.AddElement(candlesArea, _chartCandleElement); _tradesElem = new ChartTradeElement { FullTitle = "Trade" }; _chart.AddElement(candlesArea, _tradesElem); }); } 

Aqui, salvamos o link para o gráfico em uma variável local. Limpamos a programação. Também criamos e transferimos para os elementos do gráfico do gráfico para velas e ofertas. Construção _chart.GuiSync (() => {...}); necessário para inicializar a programação no encadeamento principal.

Gráfico de processamento para desenhar velas em um gráfico:

 private void CandleSeriesProcessing(CandleSeries candleSeries, Candle candle) { var data = new ChartDrawData(); data.Group(candle.OpenTime) .Add(_chartCandleElement, candle); _chart.Draw(data); } 

Aqui obtemos uma vela do evento CandleSeriesProcessing do conector, crie um ChartDrawData para exibi-la no gráfico. Indicamos o data time.Group (candle.OpenTime), indica que a vela deve ser adicionada ao elemento candle do gráfico .Add (_chartCandleElement, candle);. E indicamos que os gráficos precisam desenhar novos dados.

Realizamos ações semelhantes para transações:

 public void DrawMyTrade(MyTrade myTrade) { var data = new ChartDrawData(); data.Group(myTrade.Trade.Time) .Add(_tradesElem, myTrade); _chart.Draw(data); } 

Execute para verificar:



Breve conclusão


Para criar um aplicativo complexo e com aparência profissional, você não precisa gastar muito tempo. Por várias horas, criamos um aplicativo completo com a capacidade de configurar, exibir negociação direta e negociação algorítmica.
Não tenha medo de tentar criar seus próprios programas de negociação. Esperamos que este artigo o ajude a se sentir confortável nesse assunto.

Autor : Ivan Zalutsky

Source: https://habr.com/ru/post/pt433078/


All Articles