Description
As a big fan of both cTrader and TradingView, I developed a plugin to embed TradingView charts directly inside cTrader. It allows me to analyze graphs in TradingView but trade in cTrader.
Advantages:
- Add as many TradingView charts as you want.
- Place a TradingView chart next to a cTrader chart to compare.
Disadvantages:
- TradingView charts are not persisted, so all chart data is lost when you close cTrader.
- TradingView prices may differ from your broker's prices, so double-check the actual price in cTrader before trading.
How to use the plugin:
- Download the plugin
- Double click on the downloaded file to install it
- Find new “TV” icon in the top menu
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using cAlgo.API;
namespace cAlgo.Plugins
{
#if DEBUG
[Plugin(AccessRights = AccessRights.FullAccess)]
#else
[Plugin(AccessRights = AccessRights.None)]
#endif
public class TradingView : Plugin
{
private const string SvgIcon = @"<svg width='32' height='32' xmlns='http://www.w3.org/2000/svg'>
<g>
<title>Layer 1</title>
<text font-weight='bold' xml:space='preserve' text-anchor='start' font-family='Noto Sans JP' font-size='24' id='svg_1' y='24.22783' x='-0.83541' stroke-width='0' stroke='#BFBFBF' fill='#BFBFBF'>TV</text>
</g>
</svg>";
private const string Html = @"
<style>
.trade-button {
color:white;
border:0;
border-radius:3px;
font-size:1.2rem;
padding-left: 10px;
padding-right: 10px;
}
.sell-button {
background-color: #A83D19;
margin-right:5px;
}
.sell-button:hover {
background-color: #EF5824;
}
.sell-button:active {
background-color: #A83D19;
}
.buy-button {
background-color: #006730;
margin-left:5px;
}
.buy-button:hover {
background-color: #10A651;
}
.buy-button:active {
background-color: #006730;
}
.back-button {
color:white;
border:0;
border-radius:3px;
font-size:1.2rem;
padding-left: 10px;
padding-right: 10px;
background-color: #131722;
height: 35;
}
.back-button:hover {
background-color: #2A2E39;
}
.back-button:active {
background-color: #131722;
}
.trade-buttons {
position: fixed;
top:0;
right:0;
margin-top:6px;
margin-right:10px;
display: {TRADE_BUTTONS_DISPLAY}
}
</style>
<script>
const symbolName = '{SYMBOL_NAME}';
const frameId = '{FRAME_ID}';
const timeFrame = '{TIMEFRAME}';
const timezone = '{TIMEZONE}';
const locale = '{LANGUAGE}';
const style = '{STYLE}';
</script>
<body style='overflow: hidden;margin:0px;'>
<!-- TradingView Widget BEGIN -->
<div class='tradingview-widget-container'>
<div id='tradingview_bcda9'></div>
<div class='tradingview-widget-copyright'><a href='https://www.tradingview.com/' rel='noopener nofollow' target='_blank'><span class='blue-text'>Track all markets on TradingView</span></a></div>
<script type='text/javascript' src='https://s3.tradingview.com/tv.js'></script>
<script type='text/javascript'>
new TradingView.widget(
{
'autosize': true,
'symbol': symbolName,
'interval': timeFrame,
'timezone': timezone,
'theme': 'dark',
'style': style,
'locale': locale,
'enable_publishing': false,
'allow_symbol_change': true,
'container_id': 'tradingview_bcda9',
'hide_side_toolbar': false,
'save_image': false
}
);
</script>
</div>
</body>
<!-- TradingView Widget END -->
";
private readonly Dictionary<string, int> _barTypes = new()
{
{"Bars", 0},
{"Candles", 1},
{"Hollow Candles", 9},
{"Heiken Ashi", 8},
{"Line", 2},
{"Area", 3},
{"Renko", 4},
{"Line Break", 7},
{"Kagi", 5},
{"Point and Figure", 6},
};
private readonly List<string> _ianaTimeZoneIds = new()
{
"Etc/GMT+12", "Etc/GMT+11", "America/Adak", "Pacific/Honolulu", "Pacific/Marquesas", "America/Anchorage",
"Etc/GMT+9", "America/Tijuana", "Etc/GMT+8", "America/Los_Angeles", "America/Phoenix", "America/Chihuahua",
"America/Denver", "America/Whitehorse", "America/Guatemala", "America/Chicago", "Pacific/Easter",
"America/Mexico_City", "America/Regina", "America/Bogota", "America/Cancun", "America/New_York",
"America/Port-au-Prince", "America/Havana", "America/Indianapolis", "America/Grand_Turk",
"America/Asuncion", "America/Halifax", "America/Caracas", "America/Cuiaba", "America/La_Paz",
"America/Santiago", "America/St_Johns", "America/Araguaina", "America/Sao_Paulo", "America/Cayenne",
"America/Buenos_Aires", "America/Montevideo", "America/Punta_Arenas", "America/Miquelon", "America/Bahia",
"Etc/GMT+2", "America/Godthab", "Atlantic/Azores", "Atlantic/Cape_Verde", "Etc/UTC", "Europe/London",
"Atlantic/Reykjavik", "Africa/Sao_Tome", "Africa/Casablanca", "Europe/Berlin", "Europe/Budapest",
"Europe/Paris", "Europe/Warsaw", "Africa/Lagos", "Europe/Bucharest", "Asia/Beirut", "Africa/Cairo",
"Europe/Chisinau", "Asia/Hebron", "Africa/Johannesburg", "Europe/Kiev", "Asia/Jerusalem", "Africa/Juba",
"Europe/Kaliningrad", "Africa/Khartoum", "Africa/Tripoli", "Africa/Windhoek", "Asia/Amman", "Asia/Baghdad",
"Asia/Damascus", "Europe/Istanbul", "Asia/Riyadh", "Europe/Minsk", "Europe/Moscow", "Africa/Nairobi",
"Europe/Volgograd", "Asia/Tehran", "Asia/Dubai", "Europe/Astrakhan", "Asia/Baku", "Europe/Samara",
"Indian/Mauritius", "Europe/Saratov", "Asia/Tbilisi", "Asia/Yerevan", "Asia/Kabul", "Asia/Tashkent",
"Asia/Qyzylorda", "Asia/Yekaterinburg", "Asia/Karachi", "Asia/Calcutta", "Asia/Colombo", "Asia/Katmandu",
"Asia/Almaty", "Asia/Dhaka", "Asia/Omsk", "Asia/Rangoon", "Asia/Bangkok", "Asia/Barnaul", "Asia/Hovd",
"Asia/Krasnoyarsk", "Asia/Novosibirsk", "Asia/Tomsk", "Asia/Shanghai", "Asia/Irkutsk", "Asia/Singapore",
"Australia/Perth", "Asia/Taipei", "Asia/Ulaanbaatar", "Australia/Eucla", "Asia/Chita", "Asia/Tokyo",
"Asia/Pyongyang", "Asia/Seoul", "Asia/Yakutsk", "Australia/Adelaide", "Australia/Darwin",
"Australia/Brisbane", "Australia/Sydney", "Pacific/Port_Moresby", "Australia/Hobart", "Asia/Vladivostok",
"Australia/Lord_Howe", "Pacific/Bougainville", "Asia/Srednekolymsk", "Asia/Magadan", "Pacific/Norfolk",
"Asia/Sakhalin", "Pacific/Guadalcanal", "Asia/Kamchatka", "Pacific/Auckland", "Etc/GMT-12", "Pacific/Fiji",
"Pacific/Chatham", "Etc/GMT-13", "Pacific/Tongatapu", "Pacific/Apia", "Pacific/Kiritimati"
};
private readonly Dictionary<string, string> _languages = new();
private WidgetSettings _settings;
protected override void OnStart()
{
foreach (var cultureInfo in CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.OrderBy(c => c.DisplayName))
_languages.TryAdd(cultureInfo.DisplayName, cultureInfo.IetfLanguageTag);
var icon = new SvgIcon(SvgIcon);
_ = Commands.Add(CommandType.ChartContainerToolbar, CommandCallback, icon);
_settings = LocalStorage.GetObject<WidgetSettings>("settings") ?? new WidgetSettings
{
Locale = "English",
Interval = "60",
Style = "Candles",
Symbol = "EURUSD",
Timezone = "Etc/UTC",
Title = "TradingView"
};
}
private CommandResult CommandCallback(CommandArgs args)
{
var rootPanel = new StackPanel();
rootPanel.Margin = new Thickness(10);
rootPanel.Width = 250;
rootPanel.Height = 210;
var title = new TextBox {Text = "Trading View"};
title.Text = _settings.Title;
title.TextChanged += _ => UpdateTitleSetting(title.Text);
rootPanel.AddChild(WrapWithTitle(title, "Title").SetMargin(0, 0, 0, 10));
var symbolName = new TextBox {Text = "EURUSD"};
symbolName.Text = _settings.Symbol;
symbolName.TextChanged += _ => UpdateSymbolSetting(symbolName.Text);
rootPanel.AddChild(WrapWithTitle(symbolName, "Symbol").SetMargin(0, 0, 0, 10));
var comboBoxStyle = new Style();
comboBoxStyle.Set(ControlProperty.Height, 18);
comboBoxStyle.Set(ControlProperty.HorizontalContentAlignment, HorizontalAlignment.Left);
var grid = new Grid();
rootPanel.AddChild(grid);
grid.AddColumn().SetWidthInStars(1);
grid.AddColumn().SetWidthInPixels(10);
grid.AddColumn().SetWidthInStars(1);
var intervalRow = grid.AddRow().Auto();
var interval = new ComboBox { Style = comboBoxStyle };
FillIntervals(interval);
interval.SelectedItem = TvTimeFrames.GetDisplayValue(_settings.Interval);
interval.SelectedItemChanged += _ => UpdateIntervalSetting(interval.SelectedItem);
grid.AddChild(WrapWithTitle(interval, "Interval"), intervalRow.Index, 0);
var timezone = new ComboBox { Style = comboBoxStyle };
foreach (var ianaId in _ianaTimeZoneIds)
timezone.AddItem(ianaId);
timezone.SelectedItem = _settings.Timezone;
timezone.SelectedItemChanged += _ =>
{
_settings.Timezone = timezone.SelectedItem;
SaveSettings();
};
grid.AddChild(WrapWithTitle(timezone, "Timezone"), intervalRow.Index, 2);
grid.AddRow().SetHeightInPixels(5);
var languageRow = grid.AddRow().Auto();
var languages = new ComboBox { Style = comboBoxStyle };
foreach (var language in _languages.Keys)
languages.AddItem(language);
languages.SelectedItem = _settings.Locale;
languages.SelectedItemChanged += _ =>
{
_settings.Locale = languages.SelectedItem;
SaveSettings();
};
grid.AddChild(WrapWithTitle(languages, "Language"), languageRow.Index, 0);
var barStyle = new ComboBox { Style = comboBoxStyle };
barStyle.SelectedItemChanged += _ =>
{
_settings.Style = barStyle.SelectedItem;
SaveSettings();
};
foreach (var barTypesValue in _barTypes.Keys)
barStyle.AddItem(barTypesValue);
barStyle.SelectedItem = _settings.Style;
grid.AddChild(WrapWithTitle(barStyle, "Bar's Style"), languageRow.Index, 2);
var openChartButtonStyle = new Style();
openChartButtonStyle.Set(ControlProperty.BackgroundColor, "#009345");
openChartButtonStyle.Set(ControlProperty.BackgroundColor, "#10A651", ControlState.Hover);
var openChartButton = new Button
{
Text = "Open Chart",
Margin = new Thickness(0, 20, 0, 0),
Style = openChartButtonStyle,
};
rootPanel.AddChild(openChartButton);
openChartButton.Click += _ => AddChart((ChartContainer) args.Context, _settings);
return new CommandResult(rootPanel);
}
private void UpdateIntervalSetting(string displayValue)
{
_settings.Interval = TvTimeFrames.GetValue(displayValue);
SaveSettings();
}
private void FillIntervals(ComboBox comboBox)
{
foreach (var displayValue in TvTimeFrames.GetAllDisplayValues()) comboBox.AddItem(displayValue);
}
private void UpdateSymbolSetting(string text)
{
_settings.Symbol = text;
SaveSettings();
}
private void UpdateTitleSetting(string title)
{
_settings.Title = title;
SaveSettings();
}
private void SaveSettings()
{
LocalStorage.SetObject("settings", _settings);
}
private ControlBase WrapWithTitle(ControlBase control, string title)
{
var stackPanel = new StackPanel();
stackPanel.AddChild(new TextBlock {Text = title, Margin = new Thickness(0, 0, 0, 3)});
stackPanel.AddChild(control);
return stackPanel;
}
private void AddChart(ChartContainer chartContainer, WidgetSettings settings)
{
var symbolName = settings.Symbol;
var customFrame = ChartManager.AddCustomFrame(settings.Title, chartContainer);
var webView = new WebView();
customFrame.Child = webView;
var html = Html
.Replace("{SYMBOL_NAME}", symbolName)
.Replace("{FRAME_ID}", customFrame.Id)
.Replace("{TIMEZONE}", settings.Timezone)
.Replace("{TIMEFRAME}", settings.Interval)
.Replace("{STYLE}", _barTypes[settings.Style].ToString())
.Replace("{LANGUAGE}", _languages[settings.Locale]);
#if DEBUG
File.WriteAllText(@"C:\Temp\tv.html", html);
#endif
webView.NavigateToStringAsync(html);
}
}
internal class TvTimeFrame
{
public TvTimeFrame(string displayValue, string tv)
{
DisplayValue = displayValue;
TV = tv;
}
public string DisplayValue { get; }
public string TV { get; }
}
internal static class TvTimeFrames
{
private static readonly TvTimeFrame[] _all =
{
new("1 minute", "1"),
new("2 minutes", "2"),
new("3 minutes", "3"),
new("5 minutes", "5"),
new("10 minutes", "10"),
new("15 minutes", "15"),
new("30 minutes", "30"),
new("45 minutes", "45"),
new("1 hour", "60"),
new("2 hours", "120"),
new("3 hours", "180"),
new("4 hours", "240"),
new("1 day", "D"),
new("1 week", "W"),
new("1 month", "M"),
new("3 months", "3M"),
new("6 months", "6M"),
new("12 months", "12M"),
new("1 range", "1R"),
new("10 ranges", "10R"),
new("100 ranges", "100R"),
new("1000 ranges", "1000R"),
};
public static IEnumerable<string> GetAllDisplayValues()
{
return _all.Select(i => i.DisplayValue);
}
public static string GetValue(string displayValue)
{
foreach (var tf in _all)
if (tf.DisplayValue == displayValue)
return tf.TV;
return "h1";
}
public static string GetDisplayValue(string value)
{
foreach (var tf in _all)
if (tf.TV == value)
return tf.DisplayValue;
return "1 hour";
}
}
internal record WidgetSettings
{
public string Title { get; set; }
public string Symbol { get; set; }
public string Interval { get; set; }
public string Timezone { get; set; }
public string Locale { get; set; }
public string Style { get; set; }
}
internal static class StaticExtensions
{
public static GridRow Auto(this GridRow row)
{
row.SetHeightToAuto();
return row;
}
public static ControlBase SetMargin(this ControlBase control, double left, double top, double right,
double bottom)
{
control.Margin = new Thickness(left, top, right, bottom);
return control;
}
}
}
Quant
Joined on 12.01.2014
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: TradingView.algo
- Rating: 5
- Installs: 2353
- Modified: 27/06/2024 14:40
Comments
if we can not get live data we canot place trade we can not add more then two indicator no custom timeframe then what is the benefit of using this plugin can you explain this is totally disgusting
This is great, but if you can't operate within the Tradingview implemented charts in cTrader, and the technically analyzed charts are not saved either, then what's the point? That's why I have Tradingview open, as is my case. It must continue to improve, until you can operate within the graphs, enter the indicators of your set up with the possibility of having your Tradingview payment user within cTrader and being able to save graphs. Nice try, keep working, thanks.
thx for the share!bro!u r the best!
really excellent addition and can see many other uses for this (such as pulling json feeds from the TV page etc).
nicely implemented
Looks amazing, thank you!