Escribimos robots comerciales utilizando el marco gráfico StockSharp. Parte 1



En nuestro blog, escribimos mucho sobre tecnologías y herramientas útiles relacionadas con el comercio de acciones. Una de ellas es la plataforma gratuita StockSharp , que se puede utilizar para el desarrollo profesional de terminales comerciales y robots comerciales en C #. En este artículo, mostraremos cómo usar el marco gráfico incluido en S # .API para crear un terminal comercial con la capacidad de ejecutar estrategias algorítmicas.

Lo que se necesita


  1. Visual Studio 2017 (Comunidad, versión gratuita), en él programaremos.
  2. Conexión a la negociación en la bolsa, en los ejemplos de este texto se utiliza la interfaz SMARTcom de ITI Capital .

Creación de proyectos


Cree una nueva aplicación WPF en Visual Studio:



Luego debe agregar la biblioteca S # .API. Puede averiguar cómo hacerlo en la documentación . La mejor opción es instalar usando Nuget.

Dado que todos los elementos gráficos S # .API se basan en DevExpress, y las bibliotecas DevExpress vienen con S # .API, sería una tontería no usarlos. Vayamos al editor de ventanas MainWindow.xaml:



Reemplace la ventana con DXWindow, necesitaremos esto para usar diferentes esquemas de color:



Visual Studio en sí nos ofrecerá insertar las bibliotecas necesarias.

Dividiremos la ventana en tres partes: en la parte superior habrá una tira con botones para configurar conexiones y conexiones, en la parte inferior habrá una ventana con registros, y en el medio todos los demás paneles. La forma más fácil de romper una ventana es con LayoutControl de DevExpress.

En las tres partes resultantes, agregaremos los elementos que necesitamos.

<dx:DXWindow x:Class="ShellNew.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <dxlc:LayoutControl Padding="0" Name="LayoutControlRoot" Orientation="Vertical"> <dxlc:LayoutGroup HorizontalAlignment="Stretch" Height="25"> <!--  --> </dxlc:LayoutGroup> <dxlc:LayoutGroup HorizontalAlignment="Stretch" > <!--  --> </dxlc:LayoutGroup> <dxlc:LayoutGroup HorizontalAlignment="Stretch" > <!--  --> </dxlc:LayoutGroup> </dxlc:LayoutControl> </dx:DXWindow> 

Configurar una conexión a un conector


Agregue dos botones, uno es el botón de configuración de conexión y el segundo es el botón de conexión. Para hacer esto, use el botón SimpleButton de DevExpress. Los botones se ubicarán en la parte superior de la aplicación. En cada botón colocamos las imágenes familiares de S # .Designer , S # .Data y S # .Terminal .

 <dx:DXWindow x:Class="ShellNew.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" xmlns:xaml="http://schemas.stocksharp.com/xaml" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <dxlc:LayoutControl Padding="0" Name="LayoutControlRoot" Orientation="Vertical"> <dxlc:LayoutGroup HorizontalAlignment="Stretch" Height="25"> <!--  --> <dxlc:LayoutItem Width="40"> <dx:SimpleButton x:Name="SettingsButton" Click="SettingsButton_Click" > <Image Source="{xaml:ThemedIcons Key=Settings}" Width="16" /> </dx:SimpleButton> </dxlc:LayoutItem> <dxlc:LayoutItem Width="40"> <dx:SimpleButton x:Name="ConnectButton" Click="ConnectButton_Click" > <Image Source="{xaml:ThemedIcons Key=Connect}" Width="16" /> </dx:SimpleButton> </dxlc:LayoutItem> </dxlc:LayoutGroup> <dxlc:LayoutGroup HorizontalAlignment="Stretch" View="Tabs"> <!--  --> </dxlc:LayoutGroup> <dxlc:LayoutGroup HorizontalAlignment="Stretch" > <!--  --> </dxlc:LayoutGroup> </dxlc:LayoutControl> </dx:DXWindow> 

En la esquina superior derecha de la pantalla, veremos la siguiente imagen:



Haga doble clic en cada botón para crear controladores de eventos para hacer clic en el botón. En el código de MainWindow, debe declarar el conector, así como la ubicación y el nombre del archivo en el que se almacenarán las configuraciones del conector.

 public readonly Connector Connector; private const string _dir = "Data"; private static readonly string _settingsFile = $@"{_dir}\connection.xml"; 

En el controlador de eventos para hacer clic en el botón de configuración del conector, abriremos la ventana de configuración del conector y la guardaremos en un archivo.

 private void SettingsButton_Click(object sender, RoutedEventArgs e) { if (Connector.Configure(this)) { new XmlSerializer<SettingsStorage>().Serialize(Connector.Save(), _settingsFile); } } 

En el constructor, verificaremos si hay un directorio y un archivo con la configuración del conector, y si hay uno, lo cargaremos en el conector:

 //---------------------------------------------------------------------------------- Directory.CreateDirectory(_dir); Connector = new Connector(); if (File.Exists(_settingsFile)) { Connector.Load(new XmlSerializer<SettingsStorage>().Deserialize(_settingsFile)); } //---------------------------------------------------------------------------------- 

La mayoría de los objetos S # .API tienen métodos Save and Load que se pueden usar para guardar y cargar este objeto desde un archivo XML.

En el controlador de métodos haciendo clic en el botón de conexión, conectamos el conector.

  private void ConnectButton_Click(object sender, RoutedEventArgs e) { Connector.Connect(); } 

Ahora puede ejecutar el programa y verificarlo.

Establecer un tema oscuro


Muchos operadores prefieren temas oscuros a las aplicaciones comerciales. Por lo tanto, inmediatamente hacemos que el tema del programa sea oscuro. Para que necesite encontrar el archivo App.xaml:



Y reemplace la aplicación con gráficos: ExtendedBaseApplication, y Visual Studio nos ofrecerá insertar las bibliotecas necesarias.

 <charting:ExtendedBaseApplication x:Class="ShellNew.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:charting="http://schemas.stocksharp.com/xaml" StartupUri="MainWindow.xaml"> </charting:ExtendedBaseApplication> 

Y en el archivo App.xaml.cs necesita eliminar ": Aplicación".

 namespace ShellNew { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App { } } 

En el constructor de MainWindow, escriba ApplicationThemeHelper.ApplicationThemeName = Theme.VS2017DarkName;

Código completo en este momento:

 public partial class MainWindow { public readonly Connector Connector; private const string _dir = "Data"; private static readonly string _settingsFile = $@"{_dir}\connection.xml"; public MainWindow() { //---------------------------------------------------------------------------------- ApplicationThemeHelper.ApplicationThemeName = Theme.VS2017DarkName; //---------------------------------------------------------------------------------- Directory.CreateDirectory(_dir); Connector = new Connector(); if (File.Exists(_settingsFile)) { Connector.Load(new XmlSerializer<SettingsStorage>().Deserialize(_settingsFile)); } //---------------------------------------------------------------------------------- InitializeComponent(); } private void SettingsButton_Click(object sender, RoutedEventArgs e) { if (Connector.Configure(this)) { new XmlSerializer<SettingsStorage>().Serialize(Connector.Save(), _settingsFile); } } private void ConnectButton_Click(object sender, RoutedEventArgs e) { Connector.Connect(); } } 

Corre para ver el tema oscuro:



Crear barra de herramientas


Agregue una carpeta donde almacenaremos todos los controles que creamos y llámela XAML. Agregue nuestro primer UserControll, asígnele el nombre SecurityGridControl.



Le agregamos un elemento SecurityPicker. Mostrará las herramientas disponibles. Por analogía con la ventana principal, usaremos el LayoutControl de DevExpress.

 <UserControl x:Class="ShellNew.XAML.SecurityGridControl" 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:SecurityPicker x:Name="SecPicker" /> </UserControl> 

Vayamos al diseñador de la ventana principal y cambiemos la parte central a la vista de marcadores. En uno de los marcadores colocaremos el controlador que creamos con SecurityPicker:

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

Ahora que tenemos la barra de herramientas, necesitamos darle una fuente de datos, en nuestro caso es un conector. Simplemente podrías MainWindow
SecurityPanel.SecPicker.SecurityProvider = Connector;
en el constructor MainWindow
SecurityPanel.SecPicker.SecurityProvider = Connector;
MainWindow
SecurityPanel.SecPicker.SecurityProvider = Connector;
.

Pero no debe obstruir MainWindow con código que no se aplica a él. Por lo tanto, crearemos la variable estática Instancia y le asignaremos MainWindow en el constructor MainWindow:

 public static MainWindow Instance; … Instance = this; … 

Ahora, en cualquier parte de nuestro programa, podemos acceder a las propiedades de MainWindow a través del código MainWindow.Instance.XXX.

En el constructor SecurityGridControl, de esta forma especificamos Connector como fuente de datos:

 public SecurityGridControl() { InitializeComponent(); SecPicker.SecurityProvider = MainWindow.Instance.Connector; } 

Corre para verificar:



Agregar registro


El programa, el conector o el robot deben ser controlados. Para esto, S # .API tiene una clase especial LogManager. Esta clase recibe mensajes de las fuentes y los pasa a los oyentes. En nuestro caso, las fuentes serán Conector, estrategias, etc., y el oyente será un archivo y un panel de registro.

En el código MainWindow, declaramos el objeto LogManager y el lugar donde se almacenará:

 public readonly LogManager LogManager; private static readonly string _logsDir = $@"{_dir}\Logs\"; 

En el constructor MainWindow, cree un LogManager, establezca el origen del conector y el archivo de escucha para él:

 //---------------------------------------------------------------------------------- LogManager = new LogManager(); LogManager.Sources.Add(Connector); LogManager.Listeners.Add(new FileLogListener { SeparateByDates = SeparateByDateModes.SubDirectories, LogDirectory = _logsDir }); //---------------------------------------------------------------------------------- 

Por analogía con la barra de herramientas, cree un panel de registro en la carpeta XAML, agregue otro UserControl. Déle el nombre MonitorControl. Agregue el elemento Monitor a él.

 <UserControl x:Class="ShellNew.XAML.MonitorControl" 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:Monitor x:Name="Monitor" /> </UserControl> 

En el constructor MonitorControl, establezca LogManager en Monitor como escucha:

 public MonitorControl() { InitializeComponent(); MainWindow.Instance.LogManager.Listeners.Add(new GuiLogListener(Monitor)); } 

Agregue el MonitorControl creado al final de MainWindow:

 <dxlc:LayoutGroup HorizontalAlignment="Stretch" dxlc:LayoutControl.AllowVerticalSizing="True"> <!--  --> <myxaml:MonitorControl x:Name="MonitorControl" /> </dxlc:LayoutGroup> 

Corre para verificar:



Crea un panel de vidrio


Por analogía con los paneles anteriores, cree un panel de vidrio, agregue otro UserControl a la carpeta XAML. Déle el nombre de MarketDepthControl.

En MainWindow ya usamos LayoutControl, en este control también usaremos LayoutControl. Dividimos el panel en dos partes horizontalmente:

  <UserControl x:Class="ShellNew.XAML.MarketDepthControl" 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" mc:Ignorable="d"> <dxlc:LayoutControl Padding="0" Name="LayoutControlRoot" Orientation="Horizontal"> <dxlc:LayoutGroup> <!--Left--> </dxlc:LayoutGroup> <dxlc:LayoutGroup Orientation="Vertical" dxlc:LayoutControl.AllowHorizontalSizing="True"> <!--Rigth--> </dxlc:LayoutGroup> </dxlc:LayoutControl> </UserControl> 

Agregue SecurityPicker al lado izquierdo: nos reunimos con él cuando creamos la barra de herramientas.

 <dxlc:LayoutGroup> <xaml:SecurityPicker x:Name="SecPicker" SecuritySelected="SecPicker_SecuritySelected" /> </dxlc:LayoutGroup>       .     : <dxlc:LayoutGroup Orientation="Vertical" dxlc:LayoutControl.AllowHorizontalSizing="True"> <dxlc:LayoutItem VerticalAlignment="Stretch"> <xaml:MarketDepthControl x:Name="MarketDepth" MaxHeight="2000" SelectionChanged="MarketDepth_SelectionChanged" /> </dxlc:LayoutItem> </dxlc:LayoutGroup> 

MarketDepthControl necesita establecer algún valor MaxHeight, de lo contrario la aplicación no se iniciará.

Bajo el cristal, colocaremos los elementos de la tarea de cartera, el precio y el volumen de pedidos:

 <dxlc:LayoutItem Label="Portfolio" Height="20"> <xaml:PortfolioComboBox x:Name="PortfolioComboBox" /> </dxlc:LayoutItem> <dxlc:LayoutItem Label="Price" Height="20"> <dxe:SpinEdit MinValue="0" Name="SpinEditPrice" /> </dxlc:LayoutItem> <dxlc:LayoutItem Label="Volume" Height="20"> <dxe:SpinEdit MinValue="0" Name="SpinEditVolume" /> </dxlc:LayoutItem> 

Vale la pena señalar la propiedad Label de LayoutItem, le permite establecer el texto delante del elemento. Además del elemento SpinEdit de DevExpress en el que es conveniente establecer valores numéricos. Estos elementos tienen el siguiente aspecto:



A continuación colocaremos los botones de compra y venta:

 <dxlc:LayoutGroup Orientation="Horizontal" Height="20" VerticalAlignment="Stretch"> <dxlc:LayoutItem VerticalAlignment="Stretch"> <dx:SimpleButton Content="Buy" x:Name="BuyButton" Click="BuyButton_Click" /> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch"> <dx:SimpleButton Content="Sell" x:Name="SelltButton" Click="SelltButton_Click" /> </dxlc:LayoutItem> </dxlc:LayoutGroup> 

Código completo:

 <UserControl x:Class="ShellNew.XAML.MarketDepthControl" 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:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:xaml="http://schemas.stocksharp.com/xaml" xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" mc:Ignorable="d"> <dxlc:LayoutControl Padding="0" Name="LayoutControlRoot" Orientation="Horizontal"> <dxlc:LayoutGroup> <xaml:SecurityPicker x:Name="SecPicker" SecuritySelected="SecPicker_SecuritySelected" /> </dxlc:LayoutGroup> <dxlc:LayoutGroup Orientation="Vertical" dxlc:LayoutControl.AllowHorizontalSizing="True"> <dxlc:LayoutItem VerticalAlignment="Stretch"> <xaml:MarketDepthControl x:Name="MarketDepth" MaxHeight="2000" SelectionChanged="MarketDepth_SelectionChanged" /> </dxlc:LayoutItem> <dxlc:LayoutItem Label="Portfolio" Height="20"> <xaml:PortfolioComboBox x:Name="PortfolioComboBox" /> </dxlc:LayoutItem> <dxlc:LayoutItem Label="Price" Height="20"> <dxe:SpinEdit MinValue="0" Name="SpinEditPrice" /> </dxlc:LayoutItem> <dxlc:LayoutItem Label="Volume" Height="20"> <dxe:SpinEdit MinValue="0" Name="SpinEditVolume" /> </dxlc:LayoutItem> <dxlc:LayoutGroup Orientation="Horizontal" Height="20" VerticalAlignment="Stretch"> <dxlc:LayoutItem VerticalAlignment="Stretch"> <dx:SimpleButton Content="Buy" x:Name="BuyButton" Click="BuyButton_Click" /> </dxlc:LayoutItem> <dxlc:LayoutItem VerticalAlignment="Stretch"> <dx:SimpleButton Content="Sell" x:Name="SelltButton" Click="SelltButton_Click" /> </dxlc:LayoutItem> </dxlc:LayoutGroup> </dxlc:LayoutGroup> </dxlc:LayoutControl> </UserControl> 

En el constructor MarketDepthControl, configure la fuente de herramientas para SecurityPicker y la fuente de carteras para PortfolioComboBox, en nuestro caso será Connector:

 public MarketDepthControl() { InitializeComponent(); SecPicker.SecurityProvider = MainWindow.Instance.Connector; PortfolioComboBox.Portfolios = new PortfolioDataSource(MainWindow.Instance.Connector); } 

Cree un controlador de eventos de selección de herramientas en SecurityPicker. En él verificamos si la herramienta recibida no es igual a cero. Si no es igual a cero, guardamos la herramienta recibida en una variable local, nos será útil al actualizar el vidrio. Luego limpiamos y registramos la herramienta recibida en el conector para recibir un vaso utilizando el método RegisterMarketDepth. Usando el método GetMarketDepth, obtenemos el vaso actual del instrumento para actualizar MarketDepthControl con él.

 private Security _selectedSecurity; private void SecPicker_SecuritySelected(Security security) { if (security == null) return; _selectedSecurity = security; MainWindow.Instance.Connector.RegisterMarketDepth(_selectedSecurity); var marketDepth = MainWindow.Instance.Connector.GetMarketDepth(_selectedSecurity); MarketDepth.UpdateDepth(marketDepth); } 

Para que el vidrio se actualice constantemente en el constructor MarketDepthControl, nos suscribimos al evento de cambio de vidrio MarketDepthChanged en el conector. En el controlador de este evento, comprobaremos a qué herramienta pertenece el vidrio recibido, y si pertenece a la herramienta seleccionada en SecurityPicker, lo actualizaremos: MarketDepthControl.

 public MarketDepthControl() { InitializeComponent(); SecPicker.SecurityProvider = MainWindow.Instance.Connector; PortfolioComboBox.Portfolios = new PortfolioDataSource(MainWindow.Instance.Connector); MainWindow.Instance.Connector.MarketDepthChanged += Connector_MarketDepthChanged; } private void Connector_MarketDepthChanged(MarketDepth marketDepth) { if (_selectedSecurity == null || marketDepth.Security != _selectedSecurity) return; MarketDepth.UpdateDepth(marketDepth); } 

En la parte central de MainWindow, agregue el panel MarketDepthControl creado:

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

En esta etapa, puede ejecutar el programa y verificar el funcionamiento de la actualización de lentes.
Cree un controlador de eventos para hacer clic en los botones de compra y venta. En cada controlador creamos un Pedido, en él indicamos la herramienta seleccionada en SecurityPicker, la cartera seleccionada en PortfolioComboBox, el volumen y el precio del SpinEdit correspondiente. Registre la aplicación en Connector utilizando el método RegisterOrder.

 private void BuyButton_Click(object sender, RoutedEventArgs e) { Order order = new Order() { Security = _selectedSecurity, Portfolio = PortfolioComboBox.SelectedPortfolio, Volume = SpinEditVolume.Value, Price = SpinEditPrice.Value, Direction = StockSharp.Messages.Sides.Buy, }; MainWindow.Instance.Connector.RegisterOrder(order); } private void SelltButton_Click(object sender, RoutedEventArgs e) { Order order = new Order() { Security = _selectedSecurity, Portfolio = PortfolioComboBox.SelectedPortfolio, Volume = SpinEditVolume.Value, Price = SpinEditPrice.Value, Direction = StockSharp.Messages.Sides.Sell, }; MainWindow.Instance.Connector.RegisterOrder(order); } 

Ambos procesadores difieren solo en la dirección de la aplicación.

Hagamos que el valor de SpinEditPrice cambie por el precio de la cotización seleccionada al seleccionar cotizaciones en un vaso. Para hacer esto, cree un controlador de eventos SelectionChanged para MarketDepthControl. En el cual actualizaremos el valor de SpinEditPrice al precio de la cotización seleccionada si la cotización seleccionada no es igual a cero.

 private void MarketDepth_SelectionChanged(object sender, GridSelectionChangedEventArgs e) { if (MarketDepth.SelectedQuote == null) return; SpinEditPrice.Value = MarketDepth.SelectedQuote.Price; } 

Corre para verificar:



Guardar datos del mercado


Para guardar carteras, herramientas, plataformas, necesitamos la clase CsvEntityRegistry. Es necesario rehacer la ubicación de almacenamiento de la entidad y llamar al método Init para cargarlos.

  _csvEntityRegistry = new CsvEntityRegistry(_csvEntityRegistryDir); _csvEntityRegistry.Init(); 

Para guardar velas, ofertas, etc. necesitamos StorageRegistry:

  _storageRegistry = new StorageRegistry { DefaultDrive = new LocalMarketDataDrive(_storageRegistryDir), }; 

También necesitamos el registro SnapshotRegistry de las tiendas de instantáneas:

 _snapshotRegistry = new SnapshotRegistry(_snapshotRegistryDir); 

Todo esto lo pasamos al conector cuando se crea:

 Connector = new Connector(_csvEntityRegistry, _storageRegistry, _snapshotRegistry) { IsRestoreSubscriptionOnReconnect = true, StorageAdapter = { DaysLoad = TimeSpan.FromDays(3) }, }; Connector.LookupAll(); 

Aquí también indicamos que el conector se volverá a conectar cuando se desconecte la conexión, y también indicaremos cuántos días de historial para descargar. String Connector.LookupAll (); solicita datos disponibles:

 //---------------------------------------------------------------------------------- Directory.CreateDirectory(_dir); _csvEntityRegistry = new CsvEntityRegistry(_csvEntityRegistryDir); _csvEntityRegistry.Init(); _storageRegistry = new StorageRegistry { DefaultDrive = new LocalMarketDataDrive(_storageRegistryDir), }; _snapshotRegistry = new SnapshotRegistry(_snapshotRegistryDir); Connector = new Connector(_csvEntityRegistry, _storageRegistry, _snapshotRegistry) { IsRestoreSubscriptionOnReconnect = true, StorageAdapter = { DaysLoad = TimeSpan.FromDays(3) }, }; Connector.LookupAll(); if (File.Exists(_settingsFile)) { Connector.Load(new XmlSerializer<SettingsStorage>().Deserialize(_settingsFile)); } //---------------------------------------------------------------------------------- 

Después de cargar la aplicación, yendo a la carpeta Datos, veremos que han aparecido nuevas carpetas:



Al volver a conectar, las barras de herramientas y las carteras ya estarán llenas.

Nos acercamos sin problemas al final de la primera parte. En esta etapa, el programa le permite mostrar todos los datos del mercado disponibles para nosotros. La siguiente parte demostrará lo más delicioso, es decir, el comercio en modo manual y automático.

Continuará ...

Autor : Ivan Zalutsky

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


All Articles