Refactored UI test client (MT5)

This commit is contained in:
vdemydiuk
2018-03-19 15:10:09 +02:00
parent 95c55e90be
commit 820ed9a631
3 changed files with 593 additions and 580 deletions
+54 -29
View File
@@ -4,7 +4,7 @@
xmlns:mtapi5="clr-namespace:MtApi5;assembly=MtApi5" xmlns:mtapi5="clr-namespace:MtApi5;assembly=MtApi5"
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:mtApi5TestClient="clr-namespace:MtApi5TestClient" xmlns:mtApi5TestClient="clr-namespace:MtApi5TestClient"
Title="MainWindow" Height="700" Width="700" Title="MainWindow" Height="700" Width="900"
Closing="Window_Closing"> Closing="Window_Closing">
<Window.Resources> <Window.Resources>
<DataTemplate x:Key="ConnectionTextBlockTemplate" DataType="{x:Type mtapi5:Mt5ConnectionState}"> <DataTemplate x:Key="ConnectionTextBlockTemplate" DataType="{x:Type mtapi5:Mt5ConnectionState}">
@@ -117,9 +117,9 @@
<Grid x:Name="_MainLayout"> <Grid x:Name="_MainLayout">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="0.65*"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="0.35*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
@@ -129,50 +129,63 @@
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid Margin="10"> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Border Grid.Row="0" Margin="2" Visibility="Visible" BorderThickness="1" BorderBrush="DarkBlue">
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="150"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Host"/> <TextBlock Grid.Row="0" Grid.Column="0" Text="Host" Margin="5,2,2,2" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" <TextBox Grid.Row="0" Grid.Column="1" Margin="2" Text="{Binding Host, UpdateSourceTrigger=PropertyChanged}"/>
Margin="5,0,0,0" <TextBlock Grid.Row="1" Grid.Column="0" Text="Port" Margin="5,2,2,2" VerticalAlignment="Center"/>
Text="{Binding Host, UpdateSourceTrigger=PropertyChanged}"/> <TextBox Grid.Row="1" Grid.Column="1" Margin="2" Width="70" HorizontalAlignment="Left" Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Port"/>
<TextBox Grid.Row="1" Grid.Column="1"
Margin="5,0,0,0"
Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}"/>
<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="5" Orientation="Horizontal">
<Button Content="Connect" Width="70" Command="{Binding ConnectCommand}" />
<Button Margin="5,0,0,0" Width="70" Content="Disconnect" Command="{Binding DisconnectCommand}" />
</StackPanel>
</Grid> </Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="30,5,0,10" >
<Button Content="Connect" Width="70" Command="{Binding ConnectCommand}" />
<Button Margin="10,0,0,0" Width="70" Content="Disconnect" Command="{Binding DisconnectCommand}" />
</StackPanel>
</StackPanel>
</Border>
<ListView Grid.Row="0" Grid.Column="1" <Border Grid.Row="3" Margin="2" Visibility="Visible" BorderThickness="1" BorderBrush="DarkBlue">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Quotes" Background="LightYellow" />
<ListView Grid.Row="1" Margin="2"
ItemsSource="{Binding Quotes}" ItemsSource="{Binding Quotes}"
SelectedItem="{Binding SelectedQuote}" SelectedItem="{Binding SelectedQuote}"
SelectionMode="Single"> SelectionMode="Single">
<ListView.View> <ListView.View>
<GridView> <GridView>
<GridViewColumn Width="140" Header="Instrument" DisplayMemberBinding="{Binding Instrument}" /> <GridViewColumn Width="100" Header="Instrument" DisplayMemberBinding="{Binding Instrument}" />
<GridViewColumn Width="80" Header="Bid" DisplayMemberBinding="{Binding Bid}" /> <GridViewColumn Width="70" Header="Bid" DisplayMemberBinding="{Binding Bid}" />
<GridViewColumn Width="80" Header="Ask" DisplayMemberBinding="{Binding Ask}" /> <GridViewColumn Width="70" Header="Ask" DisplayMemberBinding="{Binding Ask}" />
<GridViewColumn Width="80" Header="Feeds Count" DisplayMemberBinding="{Binding FeedCount}" /> <GridViewColumn Width="80" Header="ExpertHandle" DisplayMemberBinding="{Binding ExpertHandle}" />
</GridView> </GridView>
</ListView.View> </ListView.View>
</ListView> </ListView>
</Grid> </Grid>
</Border>
</Grid>
<TabControl Grid.Row="1" > <TabControl Grid.Column="1" Margin="2">
<TabItem Header="Trade Functions"> <TabItem Header="Trade Functions">
<ScrollViewer> <ScrollViewer>
<Grid> <Grid>
@@ -181,7 +194,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Expander Header="MqlTradeRequest" Margin="10"> <Expander Header="MqlTradeRequest" Margin="10" IsExpanded="True">
<Grid Margin="0,10,0,0"> <Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
@@ -541,9 +554,21 @@
</TabItem> </TabItem>
</TabControl> </TabControl>
<Expander Grid.Row="2" Header="Console" IsExpanded="True"> </Grid>
<ListBox mtApi5TestClient:ListBoxBehavior.ScrollOnNewItem="true" Height="180" ItemsSource="{Binding History}"/>
</Expander> <GridSplitter Grid.Row="1" Height="2" HorizontalAlignment="Stretch" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="DarkBlue" Margin="2">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Output Console" Background="LightYellow" />
<ListBox Grid.Row="1" ItemsSource="{Binding History}"
mtApi5TestClient:ListBoxBehavior.ScrollOnNewItem="true" />
</Grid>
</Border>
<StatusBar Grid.Row="3"> <StatusBar Grid.Row="3">
<StatusBarItem> <StatusBarItem>
+14 -19
View File
@@ -1,8 +1,4 @@
using System; using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace MtApi5TestClient namespace MtApi5TestClient
{ {
@@ -10,38 +6,38 @@ namespace MtApi5TestClient
{ {
#region Properties #region Properties
public string Instrument { get; private set; } public string Instrument { get; }
private double _Bid; private double _bid;
public double Bid public double Bid
{ {
get { return _Bid; } get { return _bid; }
set set
{ {
_Bid = value; _bid = value;
OnPropertyChanged("Bid"); OnPropertyChanged("Bid");
} }
} }
private double _Ask; private double _ask;
public double Ask public double Ask
{ {
get { return _Ask; } get { return _ask; }
set set
{ {
_Ask = value; _ask = value;
OnPropertyChanged("Ask"); OnPropertyChanged("Ask");
} }
} }
private int _FeedCount = 0; private int _expertHandle;
public int FeedCount public int ExpertHandle
{ {
get { return _FeedCount; } get { return _expertHandle; }
set set
{ {
_FeedCount = value; _expertHandle = value;
OnPropertyChanged("FeedCount"); OnPropertyChanged("ExpertHandle");
} }
} }
@@ -59,8 +55,7 @@ namespace MtApi5TestClient
protected virtual void OnPropertyChanged(string propertyName) protected virtual void OnPropertyChanged(string propertyName)
{ {
PropertyChangedEventHandler handler = PropertyChanged; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
} }
#endregion #endregion
} }
+165 -172
View File
@@ -87,138 +87,6 @@ namespace MtApi5TestClient
public DelegateCommand TimeGMTCommand { get; private set; } public DelegateCommand TimeGMTCommand { get; private set; }
#endregion #endregion
#region Chart Commands
private async void ExecuteChartOpen(object o)
{
AddLog("Executed #1");
if (string.IsNullOrEmpty(ChartFunctionsSymbolValue))
{
AddLog("ChartOpen [ERROR]: Symbol is not defined!");
return;
}
AddLog($"Executed #2 s:{ChartFunctionsSymbolValue}");
var result = await Execute(() =>
{
var SymbolAddReturn = _mtApiClient.SymbolSelect(ChartFunctionsSymbolValue, true);
var ChartId = _mtApiClient.ChartOpen(ChartFunctionsSymbolValue, TimeSeriesValues.TimeFrame);
return ChartId;
});
if (result == -1)
{
AddLog("ChartOpen: result is null");
return;
}
AddLog($"ChartOpen: success chartid=>{result}");
}
private async void ExecuteChartTimePriceToXY(object o)
{
const long chartId = 0;
const int subWindow = 0;
var time = DateTime.Now;
const double price = 1.131;
var x = 0;
var y = 0;
var result = await Execute(() => _mtApiClient.ChartTimePriceToXY(chartId, subWindow, time, price, out x, out y));
if (result == false)
{
AddLog("ChartTimePriceToXY: result is false");
return;
}
AddLog($"ChartTimePriceToXY: success. x = {x}; Y = {y}");
}
private async void ExecuteChartXYToTimePrice(object o)
{
const long chartId = 0;
const int x = 0;
const int y = 0;
var subWindow = 0;
DateTime? time = null;
double price = double.NaN;
var result = await Execute(() => _mtApiClient.ChartXYToTimePrice(chartId, x, y, out subWindow, out time, out price));
if (result == false)
{
AddLog("ChartXYToTimePrice: result is false");
return;
}
AddLog($"ChartXYToTimePrice: success. subWindow = {subWindow}; time = {time}; price = {price}");
}
private async void ExecuteChartApplyTemplate(object o)
{
if (string.IsNullOrEmpty(TimeSeriesValues?.SymbolValue)) return;
AddLog($"ExecuteChartApplyTemplate #2 s:{TimeSeriesValues?.SymbolValue}");
var result = await Execute(() =>
{
var SymbolAddReturn = _mtApiClient.SymbolSelect(TimeSeriesValues?.SymbolValue, true);
var ChartId = _mtApiClient.ChartOpen(TimeSeriesValues?.SymbolValue, TimeSeriesValues.TimeFrame);
var MT5Path = _mtApiClient.TerminalInfoString(ENUM_TERMINAL_INFO_STRING.TERMINAL_DATA_PATH);
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Template File (*.tpl)|*.tpl|All files (*.*)|*.*";
if (openFileDialog.ShowDialog() == true)
{
var TemplateName = "\\Files\\mt5api_copy.tpl";
var TemplateStringContent = File.ReadAllLines(openFileDialog.FileName);
var DestPath = $"{MT5Path}\\MQL5{TemplateName}";
File.WriteAllLines($"{MT5Path}\\MQL5{TemplateName}", TemplateStringContent);
_mtApiClient.ChartApplyTemplate(ChartId, TemplateName);
}
return ChartId;
});
if (result == -1)
{
AddLog("ExecuteChartApplyTemplate: result is null");
return;
}
AddLog($"ExecuteChartApplyTemplate: success chartid=>{result}");
}
private async void ExecuteChartSaveTemplate(object o)
{
AddLog($"ExecuteSaveApplyTemplate #1");
var result = await Execute(() =>
{
var MT5Path = _mtApiClient.TerminalInfoString(ENUM_TERMINAL_INFO_STRING.TERMINAL_DATA_PATH);
int ChartId = 0; // Actual Chart
var TemplateName = "\\Files\\exported.tpl";
_mtApiClient.ChartSaveTemplate(ChartId, TemplateName);
var DestPath = $"{MT5Path}\\MQL5{TemplateName}";
AddLog($"Destination: {TemplateName}");
return ChartId;
});
if (result == -1)
{
AddLog("ChartOpen: result is null");
return;
}
AddLog($"ChartOpen: success chartid=>{result}");
}
#endregion
#region Properties #region Properties
private Mt5ConnectionState _connectionState; private Mt5ConnectionState _connectionState;
public Mt5ConnectionState ConnectionState public Mt5ConnectionState ConnectionState
@@ -339,11 +207,10 @@ namespace MtApi5TestClient
_mtApiClient.QuoteAdded += mMtApiClient_QuoteAdded; _mtApiClient.QuoteAdded += mMtApiClient_QuoteAdded;
_mtApiClient.QuoteRemoved += mMtApiClient_QuoteRemoved; _mtApiClient.QuoteRemoved += mMtApiClient_QuoteRemoved;
_mtApiClient.QuoteUpdated += mMtApiClient_QuoteUpdated; _mtApiClient.QuoteUpdated += mMtApiClient_QuoteUpdated;
_mtApiClient.QuoteUpdate += mMtApiClient_QuoteUpdate;
_mtApiClient.OnTradeTransaction += mMtApiClient_OnTradeTransaction; _mtApiClient.OnTradeTransaction += mMtApiClient_OnTradeTransaction;
_mtApiClient.OnBookEvent += _mtApiClient_OnBookEvent; _mtApiClient.OnBookEvent += _mtApiClient_OnBookEvent;
_quotesMap = new Dictionary<string, QuoteViewModel>();
ConnectionState = _mtApiClient.ConnectionState; ConnectionState = _mtApiClient.ConnectionState;
ConnectionMessage = "Disconnected"; ConnectionMessage = "Disconnected";
Port = 8228; //default local port Port = 8228; //default local port
@@ -1193,6 +1060,138 @@ namespace MtApi5TestClient
AddLog($"TimeGMT: {retVal}"); AddLog($"TimeGMT: {retVal}");
} }
#region Chart Commands
private async void ExecuteChartOpen(object o)
{
AddLog("Executed #1");
if (string.IsNullOrEmpty(ChartFunctionsSymbolValue))
{
AddLog("ChartOpen [ERROR]: Symbol is not defined!");
return;
}
AddLog($"Executed #2 s:{ChartFunctionsSymbolValue}");
var result = await Execute(() =>
{
var SymbolAddReturn = _mtApiClient.SymbolSelect(ChartFunctionsSymbolValue, true);
var ChartId = _mtApiClient.ChartOpen(ChartFunctionsSymbolValue, TimeSeriesValues.TimeFrame);
return ChartId;
});
if (result == -1)
{
AddLog("ChartOpen: result is null");
return;
}
AddLog($"ChartOpen: success chartid=>{result}");
}
private async void ExecuteChartTimePriceToXY(object o)
{
const long chartId = 0;
const int subWindow = 0;
var time = DateTime.Now;
const double price = 1.131;
var x = 0;
var y = 0;
var result = await Execute(() => _mtApiClient.ChartTimePriceToXY(chartId, subWindow, time, price, out x, out y));
if (result == false)
{
AddLog("ChartTimePriceToXY: result is false");
return;
}
AddLog($"ChartTimePriceToXY: success. x = {x}; Y = {y}");
}
private async void ExecuteChartXYToTimePrice(object o)
{
const long chartId = 0;
const int x = 0;
const int y = 0;
var subWindow = 0;
DateTime? time = null;
double price = double.NaN;
var result = await Execute(() => _mtApiClient.ChartXYToTimePrice(chartId, x, y, out subWindow, out time, out price));
if (result == false)
{
AddLog("ChartXYToTimePrice: result is false");
return;
}
AddLog($"ChartXYToTimePrice: success. subWindow = {subWindow}; time = {time}; price = {price}");
}
private async void ExecuteChartApplyTemplate(object o)
{
if (string.IsNullOrEmpty(TimeSeriesValues?.SymbolValue)) return;
AddLog($"ExecuteChartApplyTemplate #2 s:{TimeSeriesValues?.SymbolValue}");
var result = await Execute(() =>
{
var SymbolAddReturn = _mtApiClient.SymbolSelect(TimeSeriesValues?.SymbolValue, true);
var ChartId = _mtApiClient.ChartOpen(TimeSeriesValues?.SymbolValue, TimeSeriesValues.TimeFrame);
var MT5Path = _mtApiClient.TerminalInfoString(ENUM_TERMINAL_INFO_STRING.TERMINAL_DATA_PATH);
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Template File (*.tpl)|*.tpl|All files (*.*)|*.*";
if (openFileDialog.ShowDialog() == true)
{
var TemplateName = "\\Files\\mt5api_copy.tpl";
var TemplateStringContent = File.ReadAllLines(openFileDialog.FileName);
var DestPath = $"{MT5Path}\\MQL5{TemplateName}";
File.WriteAllLines($"{MT5Path}\\MQL5{TemplateName}", TemplateStringContent);
_mtApiClient.ChartApplyTemplate(ChartId, TemplateName);
}
return ChartId;
});
if (result == -1)
{
AddLog("ExecuteChartApplyTemplate: result is null");
return;
}
AddLog($"ExecuteChartApplyTemplate: success chartid=>{result}");
}
private async void ExecuteChartSaveTemplate(object o)
{
AddLog("ExecuteSaveApplyTemplate #1");
var result = await Execute(() =>
{
var MT5Path = _mtApiClient.TerminalInfoString(ENUM_TERMINAL_INFO_STRING.TERMINAL_DATA_PATH);
int ChartId = 0; // Actual Chart
var TemplateName = "\\Files\\exported.tpl";
_mtApiClient.ChartSaveTemplate(ChartId, TemplateName);
var DestPath = $"{MT5Path}\\MQL5{TemplateName}";
AddLog($"Destination: {TemplateName}");
return ChartId;
});
if (result == -1)
{
AddLog("ChartOpen: result is null");
return;
}
AddLog($"ChartOpen: success chartid=>{result}");
}
#endregion
private static void RunOnUiThread(Action action) private static void RunOnUiThread(Action action)
{ {
Application.Current?.Dispatcher.Invoke(action); Application.Current?.Dispatcher.Invoke(action);
@@ -1203,27 +1202,29 @@ namespace MtApi5TestClient
Application.Current?.Dispatcher.Invoke(action, args); Application.Current?.Dispatcher.Invoke(action, args);
} }
private void mMtApiClient_QuoteUpdated(object sender, string symbol, double bid, double ask) private static void mMtApiClient_QuoteUpdated(object sender, string symbol, double bid, double ask)
{ {
if (string.IsNullOrEmpty(symbol) == false) Console.WriteLine(@"Quote: Symbol = {0}, Bid = {1}, Ask = {2}", symbol, bid, ask);
{
if (_quotesMap.ContainsKey(symbol))
{
var qvm = _quotesMap[symbol];
qvm.Bid = bid;
qvm.Ask = ask;
} }
if (string.Equals(symbol, TradeRequest.Symbol)) private void mMtApiClient_QuoteUpdate(object sender, Mt5QuoteEventArgs e)
{
if (_quotesMap.ContainsKey(e.Quote.ExpertHandle))
{
var qvm = _quotesMap[e.Quote.ExpertHandle];
qvm.Bid = e.Quote.Bid;
qvm.Ask = e.Quote.Ask;
}
if (string.Equals(e.Quote.Instrument, TradeRequest.Symbol))
{ {
if (TradeRequest.Type == ENUM_ORDER_TYPE.ORDER_TYPE_BUY) if (TradeRequest.Type == ENUM_ORDER_TYPE.ORDER_TYPE_BUY)
{ {
TradeRequest.Price = ask; TradeRequest.Price = e.Quote.Ask;
} }
else if (TradeRequest.Type == ENUM_ORDER_TYPE.ORDER_TYPE_SELL) else if (TradeRequest.Type == ENUM_ORDER_TYPE.ORDER_TYPE_SELL)
{ {
TradeRequest.Price = bid; TradeRequest.Price = e.Quote.Bid;
}
} }
} }
} }
@@ -1269,43 +1270,35 @@ namespace MtApi5TestClient
private void AddQuote(Mt5Quote quote) private void AddQuote(Mt5Quote quote)
{ {
if (quote == null) if (_quotesMap.ContainsKey(quote.ExpertHandle))
{
AddLog($"AddQuote: Quote {quote.Instrument} with handle {quote.ExpertHandle} is present list. Skipped!");
return; return;
}
QuoteViewModel qvm; var qvm = new QuoteViewModel(quote.Instrument)
if (_quotesMap.ContainsKey(quote.Instrument) == false)
{ {
qvm = new QuoteViewModel(quote.Instrument); ExpertHandle = quote.ExpertHandle,
_quotesMap[quote.Instrument] = qvm; Bid = quote.Bid,
Ask = quote.Ask
};
_quotesMap[quote.ExpertHandle] = qvm;
Quotes.Add(qvm); Quotes.Add(qvm);
} }
else
{
qvm = _quotesMap[quote.Instrument];
}
qvm.FeedCount++;
qvm.Bid = quote.Bid;
qvm.Ask = quote.Ask;
}
private void RemoveQuote(Mt5Quote quote) private void RemoveQuote(Mt5Quote quote)
{ {
if (quote == null) return; if (_quotesMap.ContainsKey(quote.ExpertHandle) == false)
if (_quotesMap.ContainsKey(quote.Instrument))
{ {
var qvm = _quotesMap[quote.Instrument]; AddLog($"RemoveQuote: Quote {quote.Instrument} with handle {quote.ExpertHandle} is NOT present list. Skipped!");
qvm.FeedCount--; return;
}
if (qvm.FeedCount <= 0) var qvm = _quotesMap[quote.ExpertHandle];
{ _quotesMap.Remove(quote.ExpertHandle);
_quotesMap.Remove(quote.Instrument);
Quotes.Remove(qvm); Quotes.Remove(qvm);
} }
}
}
private void OnConnected() private void OnConnected()
{ {
@@ -1378,7 +1371,7 @@ namespace MtApi5TestClient
#region Private Fields #region Private Fields
private readonly MtApi5Client _mtApiClient; private readonly MtApi5Client _mtApiClient;
private readonly Dictionary<string, QuoteViewModel> _quotesMap; private readonly Dictionary<int, QuoteViewModel> _quotesMap = new Dictionary<int, QuoteViewModel>();
#endregion #endregion
} }
} }