Description
Keep you favorite symbols and periods at your fingertips
Customizable buttons to switch charts symbol and period fast.
For symbols list you need to specify the name of a watchlist from where symbols will be taken.
Periods specified as a comma separated values.
Customization:
- Buttons size
- Font size
- Button label on one or two lines
- Position top or bottom
WORKS ONLY ON NEW CTRADER 4.0
Hide buttons to keep chart clean
using System;
using cAlgo.API;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Favorites : Indicator
{
[Parameter("Watchlist name", DefaultValue = "My Watchlist", Group = "Symbols")]
public string WatchlistName { get; set; }
[Parameter("Periods", DefaultValue = "m1,m5,m15,m30,h1,h4,D1,W1", Group = "Periods")]
public string Timeframes { get; set; }
[Parameter("Position", DefaultValue = "0", Group = "Position")]
public Position Position { get; set; }
[Parameter("Buttons width", DefaultValue = "21", Group = "Size")]
public int ButtonsWidth { get; set; }
[Parameter("Buttons height", DefaultValue = "21", Group = "Size")]
public int ButtonsHeight { get; set; }
[Parameter("Font size for symbols", DefaultValue = "7", Group = "Size")]
public int SymbolsFontSize { get; set; }
[Parameter("Font size 1 for periods", DefaultValue = "10", Group = "Size")]
public int TimeframeFontSize1 { get; set; }
[Parameter("Font size 2 for periods", DefaultValue = "7", Group = "Size")]
public int TimeframeFontSize2 { get; set; }
[Parameter("Split symbols", DefaultValue = true, Group = "Split")]
public bool SplitSymbolNames { get; set; }
[Parameter("Split periods", DefaultValue = true, Group = "Split")]
public bool SplitTimeframes { get; set; }
private Dictionary<Button, string> ButtonsSymbolsDictionary;
private Dictionary<Button, TimeFrame> ButtonsTimeframesDictionary;
private Panel SymbolsPanel;
protected override void Initialize()
{
if (Application.Version < new Version(4, 0))
{
var currentVersion = new Version(Application.Version.Major, Application.Version.Minor);
var errorMessage = string.Format("Favorites indicators works on cTrader 4.0 and above.\nCurrent cTrader version is {0}.", currentVersion);
Chart.AddControl(new ErrorMessage(errorMessage));
return;
}
var rootPanel = new Grid(2, 1)
{
HorizontalAlignment = HorizontalAlignment.Left
};
Chart.AddControl(rootPanel);
SymbolsPanel = new WrapPanel
{
VerticalAlignment = Position == Position.Top ? VerticalAlignment.Top : VerticalAlignment.Bottom,
HorizontalAlignment = HorizontalAlignment.Left,
Orientation = Orientation.Vertical
};
var timeframesPanel = new WrapPanel
{
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Orientation = Orientation.Horizontal,
Margin = Position == Position.Top ? "0 3 0 0" : "0 0 0 3"
};
var symbolRowIndex = Position == Position.Top ? 1 : 0;
var timeframesRowIndex = Position == Position.Top ? 0 : 1;
rootPanel.Rows[timeframesRowIndex].SetHeightToAuto();
rootPanel.AddChild(SymbolsPanel, symbolRowIndex, 0);
rootPanel.AddChild(timeframesPanel, timeframesRowIndex, 0);
var toggleButton = new ToggleButton
{
Width = ButtonsWidth,
Height = ButtonsHeight,
Margin = 2,
Padding = 1,
Text = "☰"
};
timeframesPanel.AddChild(toggleButton);
toggleButton.Click += ToggleButton_Click;
AddSymbolsButtons(SymbolsPanel);
AddTimeframesButtons(timeframesPanel);
}
private void ToggleButton_Click(ToggleButtonEventArgs obj)
{
var show = !obj.ToggleButton.IsChecked;
foreach (var button in ButtonsSymbolsDictionary.Keys)
{
button.IsVisible = show;
button.IsHitTestVisible = show;
}
foreach (var button in ButtonsTimeframesDictionary.Keys)
{
button.IsVisible = show;
button.IsHitTestVisible = show;
}
}
private void AddSymbolsButtons(Panel panel)
{
var watchlist = Watchlists.FirstOrDefault(w => w.Name == WatchlistName);
var symbolNames = watchlist == null ? new string[0]
{
} : watchlist.SymbolNames.ToArray();
ButtonsSymbolsDictionary = new Dictionary<Button, string>();
foreach (var symbolName in symbolNames)
{
AddSymbolButton(panel, symbolName);
}
Watchlists.WatchlistSymbolAdded += Watchlists_WatchlistSymbolAdded;
Watchlists.WatchlistSymbolRemoved += Watchlists_WatchlistSymbolRemoved;
}
private void AddSymbolButton(Panel panel, string symbolName)
{
string buttonText;
if (SplitSymbolNames)
{
var symbolNameSplitted = SplitSymbolForTwoLines(symbolName);
var firstLine = symbolNameSplitted.Item1;
var secondLine = symbolNameSplitted.Item2;
buttonText = firstLine + "\n" + secondLine;
}
else
{
buttonText = symbolName;
}
var button = new Button
{
Width = ButtonsWidth,
Height = ButtonsHeight,
Margin = 2,
Text = buttonText,
FontSize = SymbolsFontSize,
Padding = 1,
IsEnabled = symbolName != SymbolName
};
ButtonsSymbolsDictionary[button] = symbolName;
button.Click += SymbolButtonClicked;
panel.AddChild(button);
}
private void Watchlists_WatchlistSymbolAdded(WatchlistSymbolAddedEventArgs obj)
{
if (obj.Watchlist.Name != WatchlistName)
return;
AddSymbolButton(SymbolsPanel, obj.SymbolName);
}
private void Watchlists_WatchlistSymbolRemoved(WatchlistSymbolRemovedEventArgs obj)
{
var button = ButtonsSymbolsDictionary.FirstOrDefault(p => p.Value == obj.SymbolName).Key;
if (button != null)
{
button.Click -= SymbolButtonClicked;
SymbolsPanel.RemoveChild(button);
ButtonsSymbolsDictionary.Remove(button);
}
}
private void AddTimeframesButtons(Panel panel)
{
ButtonsTimeframesDictionary = new Dictionary<Button, TimeFrame>();
var timeframes = ParseTimeframe();
foreach (var timeframe in timeframes)
{
var button = new Button
{
Width = ButtonsWidth,
Height = ButtonsHeight,
Margin = 2,
Padding = 1,
IsEnabled = TimeFrame != timeframe
};
if (SplitTimeframes)
{
var timeframeNameSplitted = SplitTimeframeForTwoLines(timeframe);
var typeText = timeframeNameSplitted.Item1;
var periodText = timeframeNameSplitted.Item2;
var typeLabel = new TextBlock
{
Text = typeText,
FontSize = TimeframeFontSize1,
Margin = "3 0 0 0"
};
var periodLabel = new TextBlock
{
Text = periodText,
FontSize = TimeframeFontSize2,
Margin = "0 0 3 0",
HorizontalAlignment = HorizontalAlignment.Right
};
var grid = new Grid(2, 1)
{
Width = 19
};
grid.AddChild(typeLabel, 0, 0);
grid.AddChild(periodLabel, 1, 0);
button.Content = grid;
button.VerticalContentAlignment = VerticalAlignment.Stretch;
button.HorizontalContentAlignment = HorizontalAlignment.Stretch;
}
else
{
button.Text = timeframe.ShortName;
button.FontSize = TimeframeFontSize1;
}
ButtonsTimeframesDictionary[button] = timeframe;
button.Click += TimeframeButton_Clicked;
panel.AddChild(button);
}
}
private void TimeframeButton_Clicked(ButtonClickEventArgs obj)
{
var timeframe = ButtonsTimeframesDictionary[obj.Button];
Chart.TryChangeTimeFrame(timeframe);
}
private void SymbolButtonClicked(ButtonClickEventArgs obj)
{
var symbolName = ButtonsSymbolsDictionary[obj.Button];
Chart.TryChangeTimeFrameAndSymbol(TimeFrame, symbolName);
}
private IEnumerable<TimeFrame> ParseTimeframe()
{
foreach (var t in Timeframes.Split(',').Select(t => t.Trim()))
{
TimeFrame typed;
if (TimeFrame.TryParse(t, out typed))
{
yield return typed;
}
}
}
private Tuple<string, string> SplitSymbolForTwoLines(string symbolName)
{
string firstLine;
string secondLine;
if (symbolName.Length <= 3)
{
firstLine = symbolName;
secondLine = "";
}
else if (symbolName.Length == 7 && symbolName[3] == '/')
{
firstLine = symbolName.Substring(0, 3);
secondLine = symbolName.Substring(4);
}
else
{
var len = symbolName.Length / 2;
firstLine = symbolName.Substring(0, len);
secondLine = symbolName.Substring(len);
}
return new Tuple<string, string>(firstLine, secondLine);
}
private Tuple<string, string> SplitTimeframeForTwoLines(TimeFrame timeframe)
{
var shortName = timeframe.ShortName;
var typeText = Regex.Match(shortName, "[a-zA-Z]+").Value;
var periodText = Regex.Match(shortName, "\\d+").Value;
periodText = Regex.Replace(periodText, "0{3}$", "k");
if (Regex.Match(periodText, "\\d500$").Success)
periodText = Regex.Replace(periodText, "500$", ".5k");
return new Tuple<string, string>(typeText, periodText);
}
public override void Calculate(int index)
{
}
}
public class ErrorMessage : CustomControl
{
public ErrorMessage(string text)
{
VerticalAlignment = VerticalAlignment.Center;
HorizontalAlignment = HorizontalAlignment.Center;
IsHitTestVisible = false;
var border = new Border
{
CornerRadius = 8,
Padding = "40 20",
BorderColor = Color.DimGray,
BackgroundColor = "#aa3b3b3b",
BorderThickness = 1
};
var textBlock = new TextBlock
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Text = text,
ForegroundColor = Color.White
};
border.Child = textBlock;
AddChild(border);
}
}
public enum Position
{
Top = 0,
Bottom = 1
}
}
bart1
Joined on 03.08.2017
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: Favorites.algo
- Rating: 5
- Installs: 1763
- Modified: 13/10/2021 09:54
Note that publishing copyrighted material is strictly prohibited. If you believe there is copyrighted material in this section, please use the Copyright Infringement Notification form to submit a claim.
Comments
Log in to add a comment.
Cool indicator, thank you.
It only miss the top right/ top left positions option like the old one.
I meant top right/ bottom right