Category Technical analysis  Published on 27/06/2024

TradingView

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:

  1. Download the plugin
  2. Double click on the downloaded file to install it
  3. 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's avatar
Quant

Joined on 12.01.2014

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: TradingView.algo
  • Rating: 5
  • Installs: 109
Comments
Log in to add a comment.
JI
jim.tollan · 3 days ago

really excellent addition and can see many other uses for this (such as pulling json feeds from the TV page etc).

nicely implemented