diff --git a/Plugins/.samples.json b/Plugins/.samples.json index 9300052..b842e77 100644 --- a/Plugins/.samples.json +++ b/Plugins/.samples.json @@ -5,10 +5,52 @@ { "name": "All placements" }, + { + "name": "ActiveFrameChanged Sample" + }, + { + "name": "AlgoRegistry Sample" + }, + { + "name": "BacktestingInPlugins Sample" + }, + { + "name": "ChartId Sample" + }, + { + "name": "ChartIndicators Sample" + }, + { + "name": "ChartRobots Sample" + }, + { + "name": "Commands Sample" + }, + { + "name": "CoordinatesConversion Sample" + }, + { + "name": "Custom Frame Sample" + }, + { + "name": "IndicatorTitles Sample" + }, { "name": "Interactive WebView" }, { "name": "Order by Margin" + }, + { + "name": "PositionCurrentPrice Sample" + }, + { + "name": "SmoothMouseMove Sample" + }, + { + "name": "TradeWatch Tab Sample" + }, + { + "name": "WebSocket Sample" } ] \ No newline at end of file diff --git a/Plugins/AlgoRegistry Sample/AlgoRegistry Sample.sln b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample.sln index 16b63a5..2da6388 100644 --- a/Plugins/AlgoRegistry Sample/AlgoRegistry Sample.sln +++ b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlgoRegistry Sample", "AlgoRegistry Sample\AlgoRegistry Sample.csproj", "{253c1137-a441-477f-bbdd-8dffcad26446}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlgoRegistry Sample", "AlgoRegistry Sample\AlgoRegistry Sample.csproj", "{43349484-218e-4bf3-a452-0e44bf636cfe}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +11,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {253c1137-a441-477f-bbdd-8dffcad26446}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {253c1137-a441-477f-bbdd-8dffcad26446}.Debug|Any CPU.Build.0 = Debug|Any CPU - {253c1137-a441-477f-bbdd-8dffcad26446}.Release|Any CPU.ActiveCfg = Release|Any CPU - {253c1137-a441-477f-bbdd-8dffcad26446}.Release|Any CPU.Build.0 = Release|Any CPU + {43349484-218e-4bf3-a452-0e44bf636cfe}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43349484-218e-4bf3-a452-0e44bf636cfe}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43349484-218e-4bf3-a452-0e44bf636cfe}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43349484-218e-4bf3-a452-0e44bf636cfe}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoRegistry Sample.cs b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoRegistry Sample.cs index 0351fe9..9ceb1ec 100644 --- a/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoRegistry Sample.cs +++ b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoRegistry Sample.cs @@ -5,99 +5,31 @@ // This code is intended to be used as a sample and does not guarantee any particular outcome or // profit of any kind. Use it at your own risk. // -// This sample adds a new block into the ASP. The block displays statistics about the number of -// different types of algorithms installed on the user's machine. Information in the block is -// updated every second to make sure that it is always accurate. +// This sample adds a trade watch tab, and uses AlgoRegistry API to show stats about installed algo types. // // ------------------------------------------------------------------------------------------------- - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using cAlgo.API; -using cAlgo.API.Collections; -using cAlgo.API.Indicators; -using cAlgo.API.Internals; namespace cAlgo.Plugins { [Plugin(AccessRights = AccessRights.None)] public class AlgoRegistrySample : Plugin { - // Declaring the TextBlock for displaying data about - // custom indicators - private TextBlock _customIndicatorsBlock = new TextBlock - { - FontSize = 12, - FontWeight = FontWeight.Bold, - TextAlignment = TextAlignment.Left, - Padding = new Thickness(5, 5, 5, 5), - }; - - // Declaring the TextBlock for displaying data about - // built-in indicators - private TextBlock _indicatorsBlock = new TextBlock - { - FontSize = 12, - FontWeight = FontWeight.Bold, - TextAlignment = TextAlignment.Left, - Padding = new Thickness(5, 5, 5, 5), - }; - - // Declaring the TextBlock for displaying data about - // cBots - private TextBlock _robotsBlock = new TextBlock - { - FontSize = 12, - FontWeight = FontWeight.Bold, - TextAlignment = TextAlignment.Left, - Padding = new Thickness(5, 5, 5, 5), - }; - - // Declaring the TextBlock for displaying data about - // plugins - private TextBlock _pluginsBlock = new TextBlock - { - FontSize = 12, - FontWeight = FontWeight.Bold, - TextAlignment = TextAlignment.Left, - Padding = new Thickness(5, 5, 5, 5), - }; - - // Declaring the Grid to store custom controls - private Grid _grid = new Grid(4, 1); - protected override void OnStart() { - // Adding a new block into the ASP and adding - // the Grid with all TextBlocks as a child - var aspBlock = Asp.SymbolTab.AddBlock("Algo Registry"); - aspBlock.IsExpanded = true; - aspBlock.Height = 200; - - _grid.AddChild(_robotsBlock, 0, 0); - _grid.AddChild(_indicatorsBlock, 1, 0); - _grid.AddChild(_customIndicatorsBlock, 2, 0); - _grid.AddChild(_pluginsBlock, 3, 0); + var tradeWatchTab = TradeWatch.AddTab("Algo Registry"); - aspBlock.Child = _grid; + var panel = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + }; + + panel.AddChild(new AlgoStatsControl(AlgoRegistry) {Margin = 10, VerticalAlignment = VerticalAlignment.Top}); + panel.AddChild(new AlgoTypeInfoControl(AlgoRegistry) {Margin = 10, VerticalAlignment = VerticalAlignment.Top}); - // Starting the Timer to refresh information - // in all TextBlocks - Timer.Start(TimeSpan.FromSeconds(1)); + tradeWatchTab.Child = panel; } - - protected override void OnTimer() - { - // Using the AlgoRegistry to attain information about the - // different AlgoKinds installed on the user's machine - _robotsBlock.Text = $"cBots: {AlgoRegistry.GetCount(AlgoKind.Robot)}"; - _customIndicatorsBlock.Text = $"Custom indicators: {AlgoRegistry.GetCount(AlgoKind.CustomIndicator)}"; - _indicatorsBlock.Text = $"Indicators: {AlgoRegistry.GetCount(AlgoKind.StandardIndicator)}"; - _pluginsBlock.Text = $"Plugins: {AlgoRegistry.GetCount(AlgoKind.Plugin)}"; - } - - } + } } \ No newline at end of file diff --git a/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoStatsControl.cs b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoStatsControl.cs new file mode 100644 index 0000000..eeff422 --- /dev/null +++ b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoStatsControl.cs @@ -0,0 +1,84 @@ +using System.Linq; +using cAlgo.API; + +namespace cAlgo.Plugins; + +public class AlgoStatsControl: CustomControl +{ + private const string FontFamily = "Calibri"; + + private readonly AlgoRegistry _algoRegistry; + private readonly TextBlock _algosCountTextBlock; + private readonly TextBlock _customIndicatorsCountTextBlock; + private readonly TextBlock _standardIndicatorsCountTextBlock; + private readonly TextBlock _botsCountTextBlock; + private readonly TextBlock _pluginsCountTextBlock; + + public AlgoStatsControl(AlgoRegistry algoRegistry) + { + _algoRegistry = algoRegistry; + + var panel = new Grid(6, 2); + + var titleTextBlock = GetTextBlock("Algo Stats"); + + titleTextBlock.HorizontalAlignment = HorizontalAlignment.Center; + + panel.AddChild(titleTextBlock, 0, 0, 1, 2); + + panel.AddChild(GetTextBlock("Algos #"), 1, 0); + + _algosCountTextBlock = GetTextBlock(); + + panel.AddChild(_algosCountTextBlock, 1, 1); + + panel.AddChild(GetTextBlock("Standard Indicators #"), 2, 0); + + _standardIndicatorsCountTextBlock = GetTextBlock(); + + panel.AddChild(_standardIndicatorsCountTextBlock, 2, 1); + + panel.AddChild(GetTextBlock("Custom Indicators #"), 3, 0); + + _customIndicatorsCountTextBlock = GetTextBlock(); + + panel.AddChild(_customIndicatorsCountTextBlock, 3, 1); + + panel.AddChild(GetTextBlock("cBots #"), 4, 0); + + _botsCountTextBlock = GetTextBlock(); + + panel.AddChild(_botsCountTextBlock, 4, 1); + + panel.AddChild(GetTextBlock("Plugins #"), 5, 0); + + _pluginsCountTextBlock = GetTextBlock(); + + panel.AddChild(_pluginsCountTextBlock, 5, 1); + + AddChild(panel); + + Populate(); + + _algoRegistry.AlgoTypeInstalled += _ => Populate(); + _algoRegistry.AlgoTypeDeleted += _ => Populate(); + } + + private void Populate() + { + _algosCountTextBlock.Text = _algoRegistry.Count.ToString(); + _botsCountTextBlock.Text = _algoRegistry.Count(type => type.AlgoKind == AlgoKind.Robot).ToString(); + _customIndicatorsCountTextBlock.Text = _algoRegistry.Count(type => type.AlgoKind == AlgoKind.CustomIndicator).ToString(); + _standardIndicatorsCountTextBlock.Text = _algoRegistry.Count(type => type.AlgoKind == AlgoKind.StandardIndicator).ToString(); + _pluginsCountTextBlock.Text = _algoRegistry.Count(type => type.AlgoKind == AlgoKind.Plugin).ToString(); + } + + private TextBlock GetTextBlock(string text = null) => new() + { + Margin = 3, + FontSize = 20, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + Text = text + }; +} \ No newline at end of file diff --git a/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoTypeInfoControl.cs b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoTypeInfoControl.cs new file mode 100644 index 0000000..15dba5a --- /dev/null +++ b/Plugins/AlgoRegistry Sample/AlgoRegistry Sample/AlgoTypeInfoControl.cs @@ -0,0 +1,114 @@ +using System.Linq; +using cAlgo.API; + +namespace cAlgo.Plugins; + +public class AlgoTypeInfoControl: CustomControl +{ + private const string FontFamily = "Calibri"; + + private readonly AlgoRegistry _algoRegistry; + private readonly ComboBox _algoTypesComboBox; + private readonly TextBlock _algoTypeKindTextBlock; + private readonly TextBlock _algoTypeParametersTextBlock; + private readonly TextBlock _algoTypeOutputsTextBlock; + + public AlgoTypeInfoControl(AlgoRegistry algoRegistry) + { + _algoRegistry = algoRegistry; + + var panel = new Grid(5, 2) {BackgroundColor = Color.Gray}; + + var titleTextBlock = GetTextBlock("Algo Type Info"); + + titleTextBlock.HorizontalAlignment = HorizontalAlignment.Center; + + panel.AddChild(titleTextBlock, 0, 0, 1, 2); + + panel.AddChild(GetTextBlock("Types"), 1, 0); + + _algoTypesComboBox = new ComboBox + { + Margin = 3, + FontSize = 20, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + Padding = 2 + }; + + panel.AddChild(_algoTypesComboBox, 1, 1); + + panel.AddChild(GetTextBlock("Kind"), 2, 0); + + _algoTypeKindTextBlock = GetTextBlock(); + + panel.AddChild(_algoTypeKindTextBlock, 2, 1); + + panel.AddChild(GetTextBlock("Parameters"), 3, 0); + + _algoTypeParametersTextBlock = GetTextBlock(); + + panel.AddChild(_algoTypeParametersTextBlock, 3, 1); + + panel.AddChild(GetTextBlock("Outputs"), 4, 0); + + _algoTypeOutputsTextBlock = GetTextBlock(); + + panel.AddChild(_algoTypeOutputsTextBlock, 4, 1); + + AddChild(panel); + + _algoTypesComboBox.SelectedItemChanged += _ => OnAlgoTypesComboBoxSelectedItemChanged(); + + PopulateTypes(); + + _algoRegistry.AlgoTypeInstalled += _ => PopulateTypes(); + _algoRegistry.AlgoTypeDeleted += _ => PopulateTypes(); + } + + private void OnAlgoTypesComboBoxSelectedItemChanged() + { + if (_algoRegistry.Get(_algoTypesComboBox.SelectedItem) is not { } algoType) + { + _algoTypeKindTextBlock.Text = null; + _algoTypeParametersTextBlock.Text = null; + _algoTypeOutputsTextBlock.Text = null; + + return; + } + + _algoTypeKindTextBlock.Text = algoType.AlgoKind.ToString(); + _algoTypeParametersTextBlock.Text = algoType switch + { + IndicatorType indicatorType => string.Join(", ", indicatorType.Parameters.Select(p => p.Name)), + RobotType robotType => string.Join(", ", robotType.Parameters.Select(p => p.Name)), + _ => null + }; + _algoTypeOutputsTextBlock.Text = algoType switch + { + IndicatorType indicatorType => string.Join(", ", indicatorType.Outputs.Select(p => p.Name)), + _ => null + }; + } + + private void PopulateTypes() + { + foreach (var algoType in _algoRegistry) + { + _algoTypesComboBox.AddItem(algoType.Name); + } + + _algoTypesComboBox.SelectedItem = _algoRegistry.FirstOrDefault()?.Name; + + OnAlgoTypesComboBoxSelectedItemChanged(); + } + + private TextBlock GetTextBlock(string text = null) => new() + { + Margin = 3, + FontSize = 20, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + Text = text + }; +} \ No newline at end of file diff --git a/Plugins/ChartIndicators Sample/ChartIndicators Sample.sln b/Plugins/ChartIndicators Sample/ChartIndicators Sample.sln index 33a723b..587163f 100644 --- a/Plugins/ChartIndicators Sample/ChartIndicators Sample.sln +++ b/Plugins/ChartIndicators Sample/ChartIndicators Sample.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChartIndicators Sample", "ChartIndicators Sample\ChartIndicators Sample.csproj", "{262c4ff4-ecb5-4698-8358-c3ec3b0a32a6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChartIndicators Sample", "ChartIndicators Sample\ChartIndicators Sample.csproj", "{0f0958d7-23c6-4b65-8419-c72f5415c1dd}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +11,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {262c4ff4-ecb5-4698-8358-c3ec3b0a32a6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {262c4ff4-ecb5-4698-8358-c3ec3b0a32a6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {262c4ff4-ecb5-4698-8358-c3ec3b0a32a6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {262c4ff4-ecb5-4698-8358-c3ec3b0a32a6}.Release|Any CPU.Build.0 = Release|Any CPU + {0f0958d7-23c6-4b65-8419-c72f5415c1dd}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0f0958d7-23c6-4b65-8419-c72f5415c1dd}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0f0958d7-23c6-4b65-8419-c72f5415c1dd}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0f0958d7-23c6-4b65-8419-c72f5415c1dd}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Plugins/ChartIndicators Sample/ChartIndicators Sample/ChartIndicators Sample.cs b/Plugins/ChartIndicators Sample/ChartIndicators Sample/ChartIndicators Sample.cs index fcf205e..8e28585 100644 --- a/Plugins/ChartIndicators Sample/ChartIndicators Sample/ChartIndicators Sample.cs +++ b/Plugins/ChartIndicators Sample/ChartIndicators Sample/ChartIndicators Sample.cs @@ -5,61 +5,42 @@ // This code is intended to be used as a sample and does not guarantee any particular outcome or // profit of any kind. Use it at your own risk. // -// This sample adds a new block into the Asp. The block contains a single button. Upon the user -// pressing the button, the plugin uses the Chart.Indicators.Remove() method to delete all indicators -// attached to the currently active chart. +// This sample adds an active symbol panel tab, and uses ChartIndicators API to show stats about +// active chart indicators and lets you add and remove indicators to active chart. // // ------------------------------------------------------------------------------------------------- -using System; using cAlgo.API; -using cAlgo.API.Collections; -using cAlgo.API.Indicators; -using cAlgo.API.Internals; namespace cAlgo.Plugins { [Plugin(AccessRights = AccessRights.None)] public class ChartIndicatorsSample : Plugin { - - // Declaring the button to be placed inside - // the custom ASP block - private Button _removeIndicatorsButton; + private ChartIndicatorsControl _chartIndicatorsControl; protected override void OnStart() { - // Initialising the button - _removeIndicatorsButton = new Button + var aspTab = Asp.AddTab("Chart Indicators"); + + _chartIndicatorsControl = new ChartIndicatorsControl(AlgoRegistry) { - BackgroundColor = Color.Orange, - Text = "Remove All Indicators", + VerticalAlignment = VerticalAlignment.Top }; - - // Handling the Click event for the button - _removeIndicatorsButton.Click += RemoveIndicatorsButtonOnClick; - - // Adding a new block into the ASP and setting its child - Asp.SymbolTab.AddBlock("Indicators Removal").Child = _removeIndicatorsButton; - - + + aspTab.Child = _chartIndicatorsControl; + + SetControlChart(); + + ChartManager.ActiveFrameChanged += _ => SetControlChart(); } - protected void RemoveIndicatorsButtonOnClick(ButtonClickEventArgs args) + private void SetControlChart() { - // Attaining the currently active ChartFrame - var activeChartFrame = ChartManager.ChartContainers.MainChartContainer.ActiveFrame as ChartFrame; - - // Attaining the Chart from the ChartFrame - var activeChart = activeChartFrame.Chart; - - // Iterating over all indicators attached to the Chart - // and deleting them - foreach (var indicator in activeChart.Indicators) - { - activeChart.Indicators.Remove(indicator); - } + if (ChartManager.ActiveFrame is not ChartFrame chartFrame) + return; + + _chartIndicatorsControl.Chart = chartFrame.Chart; } } - -} +} \ No newline at end of file diff --git a/Plugins/ChartIndicators Sample/ChartIndicators Sample/ChartIndicatorsControl.cs b/Plugins/ChartIndicators Sample/ChartIndicators Sample/ChartIndicatorsControl.cs new file mode 100644 index 0000000..2b44eed --- /dev/null +++ b/Plugins/ChartIndicators Sample/ChartIndicators Sample/ChartIndicatorsControl.cs @@ -0,0 +1,163 @@ +using System.Linq; +using cAlgo.API; + +namespace cAlgo.Plugins; + +public class ChartIndicatorsControl: CustomControl +{ + private readonly AlgoRegistry _algoRegistry; + + private const string FontFamily = "Calibri"; + + private readonly Grid _panel; + private readonly TextBlock _indicatorsCountTextBlock; + private readonly TextBlock _indicatorsTextBlock; + private readonly ComboBox _indicatorTypesComboBox; + private readonly Button _addIndicatorButton; + private readonly Button _removeIndicatorsButton; + + private Chart _chart; + + public ChartIndicatorsControl(AlgoRegistry algoRegistry) + { + _algoRegistry = algoRegistry; + + _panel = new Grid(6, 2); + + _panel.AddChild(GetTextBlock("Indicators #"), 0, 0); + + _indicatorsCountTextBlock = GetTextBlock(); + + _panel.AddChild(_indicatorsCountTextBlock, 0, 1); + + _panel.AddChild(GetTextBlock("Indicators"), 1, 0); + + _indicatorsTextBlock = GetTextBlock(); + + _panel.AddChild(_indicatorsTextBlock, 1, 1); + + _indicatorTypesComboBox = new ComboBox + { + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + PopulateTypes(); + + _addIndicatorButton = new Button + { + Text = "Add Indicator", + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + _addIndicatorButton.Click += OnAddIndicatorButtonClick; + + var addIndicatorPanel = new StackPanel {Orientation = Orientation.Horizontal}; + + addIndicatorPanel.AddChild(_indicatorTypesComboBox); + addIndicatorPanel.AddChild(_addIndicatorButton); + + _panel.AddChild(addIndicatorPanel, 2, 0, 1, 2); + + _removeIndicatorsButton = new Button + { + Text = "Remove All Indicators", + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + _removeIndicatorsButton.Click += OnRemoveIndicatorsButtonClick; + + _panel.AddChild(_removeIndicatorsButton, 3, 0, 1, 2); + + AddChild(_panel); + + _algoRegistry.AlgoTypeInstalled += _ => PopulateTypes(); + _algoRegistry.AlgoTypeDeleted += _ => PopulateTypes(); + } + + public Chart Chart + { + get => _chart; + set + { + if (_chart == value) + return; + + UpdateChart(value); + } + } + + private void UpdateChart(Chart newChart) + { + var previousChart = _chart; + + _chart = newChart; + + UpdateStatus(); + + newChart.Indicators.IndicatorAdded += OnIndicatorsAdded; + newChart.Indicators.IndicatorRemoved += OnIndicatorRemoved; + + if (previousChart is null) + return; + + previousChart.Indicators.IndicatorAdded -= OnIndicatorsAdded; + previousChart.Indicators.IndicatorRemoved -= OnIndicatorRemoved; + } + + private void OnIndicatorRemoved(ChartIndicatorRemovedEventArgs obj) => UpdateStatus(); + + private void OnIndicatorsAdded(ChartIndicatorAddedEventArgs obj) => UpdateStatus(); + + private void OnRemoveIndicatorsButtonClick(ButtonClickEventArgs obj) + { + foreach (var chartIndicator in _chart.Indicators) + { + _chart.Indicators.Remove(chartIndicator); + } + } + + private void OnAddIndicatorButtonClick(ButtonClickEventArgs obj) + { + if (_algoRegistry.Get(_indicatorTypesComboBox.SelectedItem) is not {AlgoKind: AlgoKind.CustomIndicator or AlgoKind.StandardIndicator} indicatorType) + return; + + _chart.Indicators.Add(indicatorType.Name); + } + + private void UpdateStatus() + { + _indicatorsCountTextBlock.Text = _chart.Indicators.Count.ToString(); + _indicatorsTextBlock.Text = string.Join(", ", _chart.Indicators.Select(i => i.Name)); + } + + private void PopulateTypes() + { + foreach (var algoType in _algoRegistry) + { + if (algoType.AlgoKind is not (AlgoKind.CustomIndicator or AlgoKind.StandardIndicator)) + continue; + + _indicatorTypesComboBox.AddItem(algoType.Name); + } + + _indicatorTypesComboBox.SelectedItem = _algoRegistry.FirstOrDefault(i => i.AlgoKind == AlgoKind.StandardIndicator)?.Name; + } + + private TextBlock GetTextBlock(string text = null) => new() + { + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + Text = text + }; +} \ No newline at end of file diff --git a/Plugins/ChartRobots Sample/ChartRobots Sample.sln b/Plugins/ChartRobots Sample/ChartRobots Sample.sln new file mode 100644 index 0000000..716aebf --- /dev/null +++ b/Plugins/ChartRobots Sample/ChartRobots Sample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChartRobots Sample", "ChartRobots Sample\ChartRobots Sample.csproj", "{9c7a50e7-7abb-4661-a843-c73ff1f2dc58}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9c7a50e7-7abb-4661-a843-c73ff1f2dc58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9c7a50e7-7abb-4661-a843-c73ff1f2dc58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9c7a50e7-7abb-4661-a843-c73ff1f2dc58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9c7a50e7-7abb-4661-a843-c73ff1f2dc58}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobots Sample.cs b/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobots Sample.cs new file mode 100644 index 0000000..2377f85 --- /dev/null +++ b/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobots Sample.cs @@ -0,0 +1,46 @@ +// ------------------------------------------------------------------------------------------------- +// +// This code is a cTrader Algo API example. +// +// This code is intended to be used as a sample and does not guarantee any particular outcome or +// profit of any kind. Use it at your own risk. +// +// This sample adds an active symbol panel tab, and uses ChartRobots API to show stats about +// active chart cBots and lets you add and remove cBots to active chart. +// +// ------------------------------------------------------------------------------------------------- + +using cAlgo.API; + +namespace cAlgo.Plugins +{ + [Plugin(AccessRights = AccessRights.None)] + public class ChartRobotsSample : Plugin + { + private ChartRobotsControl _chartRobotsControl; + + protected override void OnStart() + { + var aspTab = Asp.AddTab("Chart Robots"); + + _chartRobotsControl = new ChartRobotsControl(AlgoRegistry) + { + VerticalAlignment = VerticalAlignment.Top + }; + + aspTab.Child = _chartRobotsControl; + + SetControlChart(); + + ChartManager.ActiveFrameChanged += _ => SetControlChart(); + } + + private void SetControlChart() + { + if (ChartManager.ActiveFrame is not ChartFrame chartFrame) + return; + + _chartRobotsControl.Chart = chartFrame.Chart; + } + } +} \ No newline at end of file diff --git a/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobots Sample.csproj b/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobots Sample.csproj new file mode 100644 index 0000000..fba7cb1 --- /dev/null +++ b/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobots Sample.csproj @@ -0,0 +1,9 @@ + + + net6.0 + + + + + + diff --git a/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobotsControl.cs b/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobotsControl.cs new file mode 100644 index 0000000..bd63a87 --- /dev/null +++ b/Plugins/ChartRobots Sample/ChartRobots Sample/ChartRobotsControl.cs @@ -0,0 +1,229 @@ +using System.Linq; +using cAlgo.API; + +namespace cAlgo.Plugins; + +public class ChartRobotsControl: CustomControl +{ + private readonly AlgoRegistry _algoRegistry; + + private const string FontFamily = "Calibri"; + + private readonly Grid _panel; + private readonly TextBlock _robotsCountTextBlock; + private readonly TextBlock _robotsTextBlock; + private readonly TextBlock _runningRobotsCountTextBlock; + private readonly ComboBox _robotTypesComboBox; + private readonly Button _addRobotButton; + private readonly Button _removeRobotsButton; + private readonly Button _startRobotsButton; + private readonly Button _stopRobotsButton; + + private Chart _chart; + + public ChartRobotsControl(AlgoRegistry algoRegistry) + { + _algoRegistry = algoRegistry; + + _panel = new Grid(7, 2); + + _panel.AddChild(GetTextBlock("Robots #"), 0, 0); + + _robotsCountTextBlock = GetTextBlock(); + + _panel.AddChild(_robotsCountTextBlock, 0, 1); + + _panel.AddChild(GetTextBlock("Running Robots #"), 1, 0); + + _runningRobotsCountTextBlock = GetTextBlock(); + + _panel.AddChild(_runningRobotsCountTextBlock, 1, 1); + + _panel.AddChild(GetTextBlock("Robots"), 2, 0); + + _robotsTextBlock = GetTextBlock(); + + _panel.AddChild(_robotsTextBlock, 2, 1); + + _robotTypesComboBox = new ComboBox + { + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + PopulateTypes(); + + _addRobotButton = new Button + { + Text = "Add Robot", + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + _addRobotButton.Click += OnAddRobotButtonClick; + + var addRobotPanel = new StackPanel {Orientation = Orientation.Horizontal}; + + addRobotPanel.AddChild(_robotTypesComboBox); + addRobotPanel.AddChild(_addRobotButton); + + _panel.AddChild(addRobotPanel, 3, 0, 1, 2); + + _removeRobotsButton = new Button + { + Text = "Remove All Robots", + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + _removeRobotsButton.Click += OnRemoveRobotsButtonClick; + + _panel.AddChild(_removeRobotsButton, 4, 0, 1, 2); + + _startRobotsButton = new Button + { + Text = "Start All Robots", + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + _startRobotsButton.Click += OnStartRobotsButtonClick; + + _panel.AddChild(_startRobotsButton, 5, 0, 1, 2); + + _stopRobotsButton = new Button + { + Text = "Stop All Robots", + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + }; + + _stopRobotsButton.Click += OnStopRobotsButtonClick; + + _panel.AddChild(_stopRobotsButton, 6, 0, 1, 2); + + AddChild(_panel); + + _algoRegistry.AlgoTypeInstalled += _ => PopulateTypes(); + _algoRegistry.AlgoTypeDeleted += _ => PopulateTypes(); + } + + public Chart Chart + { + get => _chart; + set + { + if (_chart == value) + return; + + UpdateChart(value); + } + } + + private void UpdateChart(Chart newChart) + { + var previousChart = _chart; + + _chart = newChart; + + UpdateStatus(); + + newChart.Robots.RobotAdded += OnRobotsAdded; + newChart.Robots.RobotRemoved += OnRobotRemoved; + newChart.Robots.RobotStarted += OnRobotStarted; + newChart.Robots.RobotStopped += OnRobotStopped; + + if (previousChart is null) + return; + + previousChart.Robots.RobotAdded -= OnRobotsAdded; + previousChart.Robots.RobotRemoved -= OnRobotRemoved; + previousChart.Robots.RobotStarted -= OnRobotStarted; + previousChart.Robots.RobotStopped -= OnRobotStopped; + } + + private void OnRobotRemoved(ChartRobotRemovedEventArgs obj) => UpdateStatus(); + + private void OnRobotsAdded(ChartRobotAddedEventArgs obj) => UpdateStatus(); + + private void OnRobotStopped(ChartRobotStoppedEventArgs obj) => UpdateStatus(); + + private void OnRobotStarted(ChartRobotStartedEventArgs obj) => UpdateStatus(); + + private void OnRemoveRobotsButtonClick(ButtonClickEventArgs obj) + { + foreach (var chartRobot in _chart.Robots) + { + _chart.Robots.Remove(chartRobot); + } + } + + private void OnAddRobotButtonClick(ButtonClickEventArgs obj) + { + if (_algoRegistry.Get(_robotTypesComboBox.SelectedItem) is not {AlgoKind: AlgoKind.Robot} robotType) + return; + + _chart.Robots.Add(robotType.Name); + } + + private void UpdateStatus() + { + _robotsCountTextBlock.Text = _chart.Robots.Count.ToString(); + _runningRobotsCountTextBlock.Text = _chart.Robots.Count(r => r.State == RobotState.Running).ToString(); + _robotsTextBlock.Text = string.Join(", ", _chart.Robots.Select(r => r.Name)); + } + + private void PopulateTypes() + { + foreach (var algoType in _algoRegistry) + { + if (algoType.AlgoKind != AlgoKind.Robot) + continue; + + _robotTypesComboBox.AddItem(algoType.Name); + } + + _robotTypesComboBox.SelectedItem = _algoRegistry.FirstOrDefault(i => i.AlgoKind == AlgoKind.Robot)?.Name; + } + + private void OnStopRobotsButtonClick(ButtonClickEventArgs obj) + { + foreach (var chartRobot in _chart.Robots) + { + if (chartRobot.State is (RobotState.Stopped or RobotState.Stopping)) + continue; + + chartRobot.Stop(); + } + } + + private void OnStartRobotsButtonClick(ButtonClickEventArgs obj) + { + foreach (var chartRobot in _chart.Robots) + { + if (chartRobot.State is (RobotState.Running or RobotState.Restarting)) + continue; + + chartRobot.Start(); + } + } + + private TextBlock GetTextBlock(string text = null) => new() + { + Margin = 3, + FontSize = 16, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + Text = text + }; +} \ No newline at end of file diff --git a/Plugins/Commands Sample/Commands Sample.sln b/Plugins/Commands Sample/Commands Sample.sln new file mode 100644 index 0000000..d1fdb7e --- /dev/null +++ b/Plugins/Commands Sample/Commands Sample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands Sample", "Commands Sample\Commands Sample.csproj", "{1cbf6575-23ba-459f-bb12-bfa06d988bdc}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1cbf6575-23ba-459f-bb12-bfa06d988bdc}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1cbf6575-23ba-459f-bb12-bfa06d988bdc}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1cbf6575-23ba-459f-bb12-bfa06d988bdc}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1cbf6575-23ba-459f-bb12-bfa06d988bdc}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Plugins/Commands Sample/Commands Sample/Commands Sample.cs b/Plugins/Commands Sample/Commands Sample/Commands Sample.cs new file mode 100644 index 0000000..ee23f24 --- /dev/null +++ b/Plugins/Commands Sample/Commands Sample/Commands Sample.cs @@ -0,0 +1,96 @@ +// ------------------------------------------------------------------------------------------------- +// +// This code is a cTrader Algo API example. +// +// This code is intended to be used as a sample and does not guarantee any particular outcome or +// profit of any kind. Use it at your own risk. +// +// This sample adds several commands to chart container toolbar, and uses .NET project assembly embedded resource +// to store and load SVG icons. +// +// ------------------------------------------------------------------------------------------------- + +using System; +using System.IO; +using System.Reflection; +using System.Text; +using cAlgo.API; + +namespace cAlgo.Plugins +{ + [Plugin(AccessRights = AccessRights.None)] + public class CommandsSample : Plugin + { + protected override void OnStart() + { + var commandWithoutResultIcon = new SvgIcon(GetSvgIconAsString("growth-icon.svg")); + var commandWithoutResult = Commands.Add(CommandType.ChartContainerToolbar, CommandWithoutResultCallback, commandWithoutResultIcon); + commandWithoutResult.ToolTip = "Without Result"; + + var commandWithResultIcon = new SvgIcon(GetSvgIconAsString("innovation-creativity-icon.svg")); + var commandWithResult = Commands.Add(CommandType.ChartContainerToolbar, CommandWithResultCallback, commandWithResultIcon); + commandWithResult.ToolTip = "With Result"; + + var disabledCommandIcon = new SvgIcon(GetSvgIconAsString("motor-pump-color-icon.svg")); + var disabledCommand = Commands.Add(CommandType.ChartContainerToolbar, args => throw new InvalidOperationException("Shouldn't be executed!"), disabledCommandIcon); + + disabledCommand.ToolTip = "Disabled Command"; + disabledCommand.IsEnabled = false; + + var drawOnActiveChartCommand = Commands.Add(CommandType.ChartContainerToolbar, DrawOnActiveChart); + drawOnActiveChartCommand.ToolTip = "Draws Text on active chart"; + } + + private void DrawOnActiveChart(CommandArgs obj) + { + if (ChartManager.ActiveFrame is not ChartFrame {Chart: var chart}) + return; + + chart.DrawStaticText( + "CommandText", + "Command drawing", + VerticalAlignment.Center, + HorizontalAlignment.Center, + Application.DrawingColor); + } + + private void CommandWithoutResultCallback(CommandArgs commandArgs) + { + if (commandArgs.Context is not ChartContainer chartContainer) + return; + + MessageBox.Show( + $"Command was executed for chart container {chartContainer.Id} which has {chartContainer.Count} charts and has {chartContainer.Mode} mode.", + "Command Without Result"); + } + + private CommandResult CommandWithResultCallback(CommandArgs commandArgs) + { + var webView = new WebView {Width = 300, Height = 350}; + + webView.Loaded += OnWebViewLoaded; + + return new CommandResult(webView); + } + + private void OnWebViewLoaded(WebViewLoadedEventArgs obj) => obj.WebView.NavigateAsync("https://ctrader.com/"); + + private string GetSvgIconAsString(string fileName) + { + var assembly = Assembly.GetAssembly(typeof(CommandsSample)); + + var assemblyName = assembly!.GetName().Name!.Replace(' ', '_'); + + var embeddedResourceName = $"{assemblyName}.{fileName}"; + + using var stream = assembly.GetManifestResourceStream(embeddedResourceName); + + if (stream is null) + throw new InvalidOperationException($"Resource {embeddedResourceName} not found."); + + using var streamReader = new StreamReader(stream, Encoding.UTF8); + + return streamReader.ReadToEnd(); + } + } +} \ No newline at end of file diff --git a/Plugins/Commands Sample/Commands Sample/Commands Sample.csproj b/Plugins/Commands Sample/Commands Sample/Commands Sample.csproj new file mode 100644 index 0000000..9e4d53a --- /dev/null +++ b/Plugins/Commands Sample/Commands Sample/Commands Sample.csproj @@ -0,0 +1,15 @@ + + + net6.0 + + + + + + + + + + + + diff --git a/Plugins/Commands Sample/Commands Sample/growth-icon.svg b/Plugins/Commands Sample/Commands Sample/growth-icon.svg new file mode 100644 index 0000000..0b2bbaf --- /dev/null +++ b/Plugins/Commands Sample/Commands Sample/growth-icon.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/Plugins/Commands Sample/Commands Sample/innovation-creativity-icon.svg b/Plugins/Commands Sample/Commands Sample/innovation-creativity-icon.svg new file mode 100644 index 0000000..fbb6ac6 --- /dev/null +++ b/Plugins/Commands Sample/Commands Sample/innovation-creativity-icon.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/Plugins/Commands Sample/Commands Sample/motor-pump-color-icon.svg b/Plugins/Commands Sample/Commands Sample/motor-pump-color-icon.svg new file mode 100644 index 0000000..2f7a0f9 --- /dev/null +++ b/Plugins/Commands Sample/Commands Sample/motor-pump-color-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Plugins/Custom Frame Sample/Custom Frame Sample.sln b/Plugins/Custom Frame Sample/Custom Frame Sample.sln new file mode 100644 index 0000000..c9e4332 --- /dev/null +++ b/Plugins/Custom Frame Sample/Custom Frame Sample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Custom Frame Sample", "Custom Frame Sample\Custom Frame Sample.csproj", "{19f46947-3f3e-46d5-b28f-3cbeb3445476}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {19f46947-3f3e-46d5-b28f-3cbeb3445476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19f46947-3f3e-46d5-b28f-3cbeb3445476}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19f46947-3f3e-46d5-b28f-3cbeb3445476}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19f46947-3f3e-46d5-b28f-3cbeb3445476}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Plugins/Custom Frame Sample/Custom Frame Sample/Custom Frame Sample.cs b/Plugins/Custom Frame Sample/Custom Frame Sample/Custom Frame Sample.cs new file mode 100644 index 0000000..737caec --- /dev/null +++ b/Plugins/Custom Frame Sample/Custom Frame Sample/Custom Frame Sample.cs @@ -0,0 +1,90 @@ +// ------------------------------------------------------------------------------------------------- +// +// This code is a cTrader Algo API example. +// +// This code is intended to be used as a sample and does not guarantee any particular outcome or +// profit of any kind. Use it at your own risk. +// +// This sample adds a ASP block that you can use to interact with custom frame API. +// +// ------------------------------------------------------------------------------------------------- + +using System.Linq; +using cAlgo.API; + +namespace cAlgo.Plugins +{ + [Plugin(AccessRights = AccessRights.None)] + public class CustomFrameSample : Plugin + { + protected override void OnStart() + { + var aspBlock = Asp.SymbolTab.AddBlock("Custom Frame Sample"); + + var panel = new StackPanel(); + + var addCustomFrameButton = new Button {Text = "Add Custom Frame", Margin = 5}; + + addCustomFrameButton.Click += OnAddCustomFrameButtonClick; + + panel.AddChild(addCustomFrameButton); + + var removeCustomFrameButton = new Button {Text = "Remove Custom Frame", Margin = 5}; + + removeCustomFrameButton.Click += OnRemoveCustomFrameButtonClick; + + panel.AddChild(removeCustomFrameButton); + + var detachCustomFrameButton = new Button {Text = "Detach Custom Frame", Margin = 5}; + + detachCustomFrameButton.Click += OnDetachCustomFrameButtonClick; + + panel.AddChild(detachCustomFrameButton); + + var attachCustomFrameButton = new Button {Text = "Attach Custom Frame", Margin = 5}; + + attachCustomFrameButton.Click += OnAttachCustomFrameButtonClick; + + panel.AddChild(attachCustomFrameButton); + + aspBlock.Child = panel; + } + + private void OnAttachCustomFrameButtonClick(ButtonClickEventArgs obj) + { + if (ChartManager.OfType().FirstOrDefault(c => !c.IsAttached) is not {} customFrame) + return; + + customFrame.Attach(); + } + + private void OnDetachCustomFrameButtonClick(ButtonClickEventArgs obj) + { + if (ChartManager.OfType().FirstOrDefault(c => c.IsAttached) is not {} customFrame) + return; + + customFrame.Detach(); + } + + private void OnRemoveCustomFrameButtonClick(ButtonClickEventArgs obj) + { + if (ChartManager.OfType().FirstOrDefault() is not {} customFrame) + return; + + ChartManager.RemoveFrame(customFrame.Id); + } + + private void OnAddCustomFrameButtonClick(ButtonClickEventArgs obj) + { + var customFrame = ChartManager.AddCustomFrame("Custom Frame"); + + customFrame.Child = new TextBlock + { + Text = $"Custom Frame {customFrame.Id} Child Control", + FontSize = 32, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center + }; + } + } +} \ No newline at end of file diff --git a/Plugins/Custom Frame Sample/Custom Frame Sample/Custom Frame Sample.csproj b/Plugins/Custom Frame Sample/Custom Frame Sample/Custom Frame Sample.csproj new file mode 100644 index 0000000..fba7cb1 --- /dev/null +++ b/Plugins/Custom Frame Sample/Custom Frame Sample/Custom Frame Sample.csproj @@ -0,0 +1,9 @@ + + + net6.0 + + + + + + diff --git a/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample.sln b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample.sln new file mode 100644 index 0000000..82a4597 --- /dev/null +++ b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TradeWatch Tab Sample", "TradeWatch Tab Sample\TradeWatch Tab Sample.csproj", "{c92cfe2d-f1aa-4db1-98f7-9e1d2a1c2799}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {c92cfe2d-f1aa-4db1-98f7-9e1d2a1c2799}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {c92cfe2d-f1aa-4db1-98f7-9e1d2a1c2799}.Debug|Any CPU.Build.0 = Debug|Any CPU + {c92cfe2d-f1aa-4db1-98f7-9e1d2a1c2799}.Release|Any CPU.ActiveCfg = Release|Any CPU + {c92cfe2d-f1aa-4db1-98f7-9e1d2a1c2799}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/SymbolStatsControl.cs b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/SymbolStatsControl.cs new file mode 100644 index 0000000..0a3ff85 --- /dev/null +++ b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/SymbolStatsControl.cs @@ -0,0 +1,127 @@ +using System; +using cAlgo.API; +using cAlgo.API.Internals; + +namespace cAlgo.Plugins; + +public class SymbolStatsControl : CustomControl +{ + private const string FontFamily = "Calibri"; + private readonly TextBlock _commissionTextBlock; + + private readonly StackPanel _panel; + private readonly TextBlock _spreadTextBlock; + private readonly TextBlock _symbolNameTextBlock; + private readonly TextBlock _symbolPriceTextBlock; + + private readonly TextBlock _unrealizedGrossProfitTextBlock; + private readonly TextBlock _unrealizedNetProfitTextBlock; + + private Symbol _symbol; + + public SymbolStatsControl() + { + _panel = new StackPanel + { + Orientation = Orientation.Vertical, + }; + + _symbolNameTextBlock = GetTextBlock(); + _symbolPriceTextBlock = GetTextBlock(); + + _panel.AddChild(WrapInHorizontalPanel(_symbolNameTextBlock, _symbolPriceTextBlock)); + + var grid = new Grid(10, 2); + + _spreadTextBlock = GetTextBlock(); + + grid.AddChild(GetTextBlock("Spread"), 0, 0); + grid.AddChild(_spreadTextBlock, 0, 1); + + _unrealizedNetProfitTextBlock = GetTextBlock(); + + grid.AddChild(GetTextBlock("Unrealized Net Profit"), 1, 0); + grid.AddChild(_unrealizedNetProfitTextBlock, 1, 1); + + _unrealizedGrossProfitTextBlock = GetTextBlock(); + + grid.AddChild(GetTextBlock("Unrealized Gross Profit"), 2, 0); + grid.AddChild(_unrealizedGrossProfitTextBlock, 2, 1); + + _commissionTextBlock = GetTextBlock(); + + grid.AddChild(GetTextBlock("Commission"), 3, 0); + grid.AddChild(_commissionTextBlock, 3, 1); + + _panel.AddChild(grid); + + AddChild(_panel); + } + + public Symbol Symbol + { + get => _symbol; + set + { + if (value is null || _symbol == value) + return; + + Update(value); + } + } + + private StackPanel WrapInHorizontalPanel(params ControlBase[] controls) + { + var panel = new StackPanel + { + Orientation = Orientation.Horizontal + }; + + foreach (var control in controls) + { + if (control.Margin == 0) + control.Margin = 3; + + panel.AddChild(control); + } + + return panel; + } + + private void Update(Symbol newSymbol) + { + var previousSymbol = _symbol; + _symbol = newSymbol; + + _symbolNameTextBlock.Text = $"{newSymbol.Name} ({newSymbol.Description})"; + _symbolPriceTextBlock.Text = $"Bid: {newSymbol.Bid}, Ask: {newSymbol.Ask}"; + _spreadTextBlock.Text = $"{GetSpreadInPips(newSymbol)} Pips"; + _unrealizedNetProfitTextBlock.Text = newSymbol.UnrealizedNetProfit.ToString(); + _unrealizedGrossProfitTextBlock.Text = newSymbol.UnrealizedGrossProfit.ToString(); + _commissionTextBlock.Text = $"{newSymbol.Commission} {newSymbol.CommissionType}"; + + if (previousSymbol is not null) previousSymbol.Tick -= OnSymbolTick; + + newSymbol.Tick += OnSymbolTick; + } + + private void OnSymbolTick(SymbolTickEventArgs obj) + { + _symbolPriceTextBlock.Text = $"Bid: {obj.Bid}, Ask: {obj.Ask}"; + _spreadTextBlock.Text = $"{GetSpreadInPips(obj.Symbol)} Pips"; + _unrealizedNetProfitTextBlock.Text = obj.Symbol.UnrealizedNetProfit.ToString(); + _unrealizedGrossProfitTextBlock.Text = obj.Symbol.UnrealizedGrossProfit.ToString(); + } + + private double GetSpreadInPips(Symbol symbol) => + (symbol.Spread * Math.Pow(10, symbol.Digits)) / (Symbol.PipSize / Symbol.TickSize); + + private TextBlock GetTextBlock(string text = null) => new() + { + Margin = 3, + FontSize = 20, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + Text = text + }; +} \ No newline at end of file diff --git a/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeControl.cs b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeControl.cs new file mode 100644 index 0000000..2612586 --- /dev/null +++ b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeControl.cs @@ -0,0 +1,82 @@ +using System; +using cAlgo.API; + +namespace cAlgo.Plugins; + +public readonly record struct TradeEventArgs(int Volume, TradeType TradeType); + +public class TradeControl : CustomControl +{ + private const string FontFamily = "Calibri"; + + private readonly Grid _panel; + private readonly ComboBox _tradeTypeComboBox; + private readonly TextBox _volumeTextBox; + + public TradeControl() + { + _panel = new Grid(10, 2); + + _volumeTextBox = new TextBox + { + MinWidth = 200, + FontFamily = FontFamily, + FontSize = 20, + FontWeight = FontWeight.Bold, + Margin = 3 + }; + + _panel.AddChild(GetTextBlock("Volume"), 0, 0); + _panel.AddChild(_volumeTextBox, 0, 1); + + _tradeTypeComboBox = new ComboBox + { + MinWidth = 200, + FontFamily = FontFamily, + FontSize = 20, + FontWeight = FontWeight.Bold, + Margin = 3 + }; + + _tradeTypeComboBox.AddItem("Buy"); + _tradeTypeComboBox.AddItem("Sell"); + + _tradeTypeComboBox.SelectedItem = "Buy"; + + var tradeTypeTextBlock = GetTextBlock("Trade Type"); + + tradeTypeTextBlock.VerticalAlignment = VerticalAlignment.Center; + + _panel.AddChild(tradeTypeTextBlock, 1, 0); + _panel.AddChild(_tradeTypeComboBox, 1, 1); + + var executeButton = new Button {Text = "Execute", FontFamily = FontFamily, BackgroundColor = Color.Red}; + + executeButton.Click += ExecuteButtonOnClick; + _panel.AddChild(executeButton, 2, 0, 1, 2); + + AddChild(_panel); + } + + public event EventHandler Trade; + + private void ExecuteButtonOnClick(ButtonClickEventArgs obj) + { + if (!int.TryParse(_volumeTextBox.Text, out var volume)) + return; + + if (!Enum.TryParse(_tradeTypeComboBox.SelectedItem, out TradeType tradeType)) + return; + + Trade?.Invoke(this, new TradeEventArgs(volume, tradeType)); + } + + private TextBlock GetTextBlock(string text = null) => new() + { + Margin = 3, + FontSize = 20, + FontWeight = FontWeight.Bold, + FontFamily = FontFamily, + Text = text + }; +} \ No newline at end of file diff --git a/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeWatch Tab Sample.cs b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeWatch Tab Sample.cs new file mode 100644 index 0000000..9dfa797 --- /dev/null +++ b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeWatch Tab Sample.cs @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------------------------------- +// +// This code is a cTrader Algo API example. +// +// This code is intended to be used as a sample and does not guarantee any particular outcome or +// profit of any kind. Use it at your own risk. +// +// This sample adds a trade watch tab, and shows some of the active symbol stats with a basic trading panel on it. +// +// ------------------------------------------------------------------------------------------------- + +using cAlgo.API; + +namespace cAlgo.Plugins +{ + [Plugin(AccessRights = AccessRights.None)] + public class TradeWatchTabSample : Plugin + { + private SymbolStatsControl _symbolStatsControl; + private TradeControl _tradeControl; + + protected override void OnStart() + { + var tab = TradeWatch.AddTab("Active Chart Symbol Stats"); + + var panel = new StackPanel + {Orientation = Orientation.Vertical, HorizontalAlignment = HorizontalAlignment.Center}; + + _symbolStatsControl = new SymbolStatsControl {Margin = 10}; + _tradeControl = new TradeControl {Margin = 10}; + + panel.AddChild(_symbolStatsControl); + panel.AddChild(_tradeControl); + + tab.Child = panel; + + SetSymbolStats(); + + _tradeControl.Trade += TradeControlOnTrade; + ChartManager.ActiveFrameChanged += _ => SetSymbolStats(); + } + + private void TradeControlOnTrade(object sender, TradeEventArgs e) + { + if (ChartManager.ActiveFrame is not ChartFrame chartFrame) + return; + + ExecuteMarketOrder(e.TradeType, chartFrame.Symbol.Name, e.Volume); + } + + private void SetSymbolStats() + { + if (ChartManager.ActiveFrame is not ChartFrame chartFrame) + return; + + _symbolStatsControl.Symbol = chartFrame.Symbol; + } + } +} \ No newline at end of file diff --git a/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeWatch Tab Sample.csproj b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeWatch Tab Sample.csproj new file mode 100644 index 0000000..c11c8c9 --- /dev/null +++ b/Plugins/TradeWatch Tab Sample/TradeWatch Tab Sample/TradeWatch Tab Sample.csproj @@ -0,0 +1,9 @@ + + + net6.0 + + + + + +