我们使用StockSharp图形框架编写交易机器人。 第二部分



我们将继续谈论使用StockSharp平台创建交易机器人。 第一篇文章涉及项目的创建和交易系统主要元素的绘制。 在周期的最后阶段,我们将直接实施交易策略。

创建投资组合面板


类似于工具栏,创建一个日志面板。 为此,将另一个UserControl添加到XAML文件夹。 为其命名为PortfolioGridControl。 向其中添加一个PortfolioGrid元素。

<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> 

在PortfolioGridControl构造函数中,我们需要订阅新投资组合的外观事件和连接器上新头寸的外观事件。

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

因此,在创建新投资组合时,它将出现在投资组合面板中,而当新头寸出现在投资组合面板中时,它将被更新。

在MainWindow的中央部分,添加创建的PortfolioGridControl面板:

 <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> 

运行检查:



我们有一个带有投资组合的标签。

创建订单面板


S#.API中的订单栏可以下订单,撤消订单和重新注册。 类似于工具栏,创建一个订单面板,将另一个UserControl添加到XAML文件夹。 为它命名为OrderGridControl。 向其中添加OrderGrid元素:

 <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具有一个OrderRegistering注册事件,OrderReRegisters重新注册事件和OrderCanceling取消事件。

让我们创建他们的处理程序:

 <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> 

在请求注册事件处理程序中,我们创建一个OrderWindow窗口,您需要在其中指定工具,投资组合和市场数据的数据源。 在我们的例子中,全部都是连接器。

然后我们使用ShowModal方法调用OrderWindow。 如果在此窗口中按下了“确定”按钮,则通过连接器,我们使用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); } 

在事件重新注册事件处理程序中,我们以相同的方式进行所有操作。 仅在这种情况下,Order对象才会出现在事件中-这是需要重新注册的订单。 因此,在OrderWindow中,我们指定Order = order.ReRegisterClone(newVolume: order.Balance)来填充OrderWindow窗口的字段。

然后我们使用ShowModal方法调用OrderWindow。 如果在此窗口中单击“确定”按钮,那么我们将使用ReRegisterClone方法通过连接器重新注册应用程序。 我们将必须取消的旧应用程序和必须设置的新应用程序移交给它。

 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); } 

在请求取消事件处理程序中,只需调用CancelOrder方法并将订单传递给它即可,该订单必须取消:

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

为了使订单显示在OrderGrid中,您需要预订新订单出现的事件和OrderGridControl构造函数中的注册错误事件,然后将这些事件传输到OrderGrid:

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

在MainWindow的中央部分,添加创建的OrderGridControl面板:

 <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> 

运行检查:



创建自己的交易面板


类似于工具栏,创建我们自己的交易的面板。 在XAML文件夹中,添加另一个UserControl。 为它命名为MyTradeGridControl。 向其中添加MyTradeGrid元素:

 <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> 

在MyTradeGridControl构造函数中,我们需要订阅一个新的自己交易的外观事件,并将其转移到MyTradeGrid:

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

在MainWindow的中央部分,添加创建的OrderGridControl面板:

 <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> 

运行检查:



用策略创建面板


我们将以与所有以前的面板相同的方式创建一个策略面板。 在XAML文件夹中,添加另一个UserControl。 为它命名为StrategyControl。 使用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> 

这里,StatisticParameterGrid用于显示策略统计信息,EquityCurveChart用于显示损益表。 StatisticParameterGrid需要设置一些MaxHeight值,否则应用程序将无法启动。

右侧将在PropertyGridEx中配置策略的属性。 用于启动和停止策略的按钮也将位于:

 <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> 

完整代码:

 <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> 

在StrategyControl构造函数中,将连接器设置为PropertyGridEx的数据源,几乎在每个控件中我们都执行了类似的操作:

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

我们需要以某种方式将策略转移到我们的控制中。 为此,请在StrategyControl中创建BindStraregy方法,该方法将采用该策略,并将其链接保存到局部变量中,并在PropertyGridEx和StatisticParameterGrid中设置该策略。

使用SetChart方法,我们将Chart蜡烛的图表转移到该策略;之后,您可以使用GetChart方法获得Chart策略。 我们还为策略设置了连接器。

 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; } 

在制定损益表时,应注意该策略将启动和停止,也许有几次,每次启动该策略时都必须清除这首诗。 为此,我们将创建ResetEquityCurveChart方法,在此方法中,我们将首先清除EquityCurveChart。 然后,我们需要为EquityCurveChart创建图形元素,它们可以指定线的名称,颜色和类型。

之后,我们订阅该策略的PnL更改事件,并在此事件的处理程序中在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); }; } 

在用于单击“开始”按钮的事件处理程序中,我们将调用此方法。 我们还将重置策略的状态并运行它:

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

在用于单击“停止”按钮的事件处理程序中,我们将停止该策略。
私有void StopStrategyButton_Click(对象发送者,RoutedEventArgs e):

 { _strategy.Stop(); } 

在MainWindow的中央部分,添加创建的StrategyControl面板:

 <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> 

策略制定


例如,考虑创建一个简单的烛台策略。 如果蜡烛在增长(绿色),她将买入;如果蜡烛在减少(红色),她将卖出。

让我们在项目中创建另一个文件夹-我们将在其中存储所有策略。 在此文件夹中,创建一个新类,将其命名为SimpleStrategy。 所有S#策略都必须继承自策略策略的基类。

 public class SimpleStrategy : Strategy {} 

由于我们的策略使用蜡烛,因此我们将创建公共属性CandleSeries,并在策略的构造函数中将其设置为默认值。

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

在这里,我们指示CandleSeries中的蜡烛将为TimeFrameCandle,间隔为15秒(TimeSpan.FromSeconds(15))。 对于CandleSeries,您可以指定BuildCandlesMode蜡烛创建模式。 我们指示将建立蜡烛(MarketDataBuildModes.Build)。 默认情况下,它们将基于刻度创建,但是您可以指定其他数据类型。

由于我们将CandleSeries设为公共财产,因此可以通过上一段中所述的PropertyGridEx进一步自定义CandleSeries。 所有策略都有可以重写的方法,我们需要重写OnStarted方法。 在启动策略之前将其调用,并允许您将其预先设置为启动状态。

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

在这里,对于CandleSeries,我们定义了PropertyGridEx中指定的工具。 然后创建处理成品蜡烛的规则。 在规则中,指定将处理每个完成的蜡烛的方法-在我们的例子中,这是ProcessCandle方法。 稍后将描述。 设置完所有内容后,我们通过SubscribeCandles方法在连接器中的CandleSeries上预订蜡烛的外观。 在我们的例子中,ProcessCandle方法包含该策略的主要逻辑:

 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))); } } 

首先,我们需要确定蜡烛是实时构建的还是历史悠久的。 如果蜡烛是历史的,那么我们将其忽略。 并非所有策略都需要这样做,例如,基于眼镜的策略并不需要此,因为眼镜始终是实时运行的。 没有通用的方法可以确定蜡烛是“实时”还是历史蜡烛,并且在每种策略中,必须根据要求独立解决此问题。 在这种情况下,我们将简单地将蜡烛关闭的时间与连接器中的时间进行比较,如果不超过一定的延迟,则蜡烛将具有实时状态。

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

接下来,我们看看这是什么蜡烛,以及该策略的当前位置是什么。 如果蜡烛在增长,那么头寸等于0,我们将使用PropertyGridEx中我们设置的交易量的市场订单开仓。 如果蜡烛在增长并且位置小于0,则我们将位置“反转”:

 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))); } 

目前,我们的策略已准备就绪。 它必须传递给我们在上一段落中使用BindStraregy方法创建的SimpleStrategyControl。 我们在初始化MainWindow组件后立即在MainWindow构造函数中执行此操作。

 SimpleStrategyControl.BindStraregy(new SimpleStrategy()); 


运行检查:





该策略有效,达成了交易,但是到目前为止,图表上还没有蜡烛和交易。

从策略中添加蜡烛和交易至图表


在使用SetChart方法的有关策略面板的部分中,我们将Chart蜡烛的图表背叛了该策略。 在策略的OnStarted方法中,我们检查是否为该策略设置了图表,如果已设置,则我们初始化图表,并订阅新的自有事务的外观和烛形变化的事件。

 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(); } 

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); }); } 

在这里,我们将指向Chart的链接保存在本地变量中。 我们清除了时间表。 我们还创建蜡烛和交易的图表元素并将其转移到图表图表元素。 构造_chart.GuiSync(()=> {...}); 为了初始化主线程中的计划,需要此命令。

CandleSeries在图表上绘制蜡烛的流程图:

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

在这里,我们从连接器的CandleSeriesProcessing事件中获得一条蜡烛,创建一个ChartDrawData以便在图表上显示它。 我们指示时间数据。Group(candle.OpenTime),指示应将蜡烛添加到图表的蜡烛元素。添加(_chartCandleElement,Candle);。 并且我们表明图形需要绘制新数据。

我们对交易执行类似的操作:

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

运行检查:



简要结论


要创建一个复杂且专业的外观应用程序,您不需要花费大量时间。 数小时以来,我们创建了一个功能强大的应用程序,能够配置,显示直接交易和算法交易。
不要害怕尝试创建自己的交易程序。 我们希望本文能帮助您熟悉此事。

作者 :伊万·扎鲁茨基(Ivan Zalutsky)

Source: https://habr.com/ru/post/zh-CN433078/


All Articles