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