Skip to content

Plugin code samples

This page provides several Python and C# code examples for creating native plugins, including tools for manual or algorithmic trading in cTrader.

Plugin sample repositories

Comprehensive plugin code samples, including ready-to-run templates for various UI areas and functionalities, are available in separate Python and C# repositories in GitHub.

Tip

Use customisable parameters in both C# and Python plugins to achieve greater flexibility. Customisable parameters for C# plugins are declared in the regular C# code, while Python plugins require customisable parameters declared in their .cs files.

Display a website in a chart frame

The following plugin displays the cTrader Store website inside a separate chart frame.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 ForumExample : Plugin
    {
        private WebView _cTraderWebView;

        protected override void OnStart()
        {
            _cTraderWebView = new WebView();
            _cTraderWebView.Loaded += DisplayForum;

            var webViewFrame = ChartManager.AddCustomFrame("Forum");
            webViewFrame.Child = _cTraderWebView;
            webViewFrame.ChartContainer.Mode = ChartMode.Multi;
            webViewFrame.Attach();
        }


        private void DisplayForum(WebViewLoadedEventArgs args)
        {
            _cTraderWebView.NavigateAsync("https://ctrader.com/forum");
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import clr
clr.AddReference("cAlgo.API")

from cAlgo.API import *

class ForumExample():
    def on_start(self):
        self.cTraderWebView = WebView()
        self.cTraderWebView.Loaded += self.display_forum

        webViewFrame = api.ChartManager.AddCustomFrame("Forum")
        webViewFrame.Child = self.cTraderWebView
        webViewFrame.ChartContainer.Mode = ChartMode.Multi
        webViewFrame.Attach()

    def display_forum(self, args):
        self.cTraderWebView.NavigateAsync("https://ctrader.com/forum")

Write information to local storage

Once per minute, the plugin below saves the total account profit and loss (P&L) into a file using the local storage feature and the current timestamp as the file name. It also displays the same information in a separate section in Active Symbol Panel (ASP).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 GrossPnL : Plugin
    {
        private TextBlock _textBlock = new TextBlock
        {
            Text = "Starting...",
            FontSize = 15,
            FontWeight = FontWeight.ExtraBold,
            TextAlignment = TextAlignment.Center,
            Padding = new Thickness(5, 5, 5, 5),
        };

        protected override void OnStart()
        {
            var aspBlock = Asp.SymbolTab.AddBlock("Gross P&L");
            aspBlock.Child = _textBlock;
            Timer.Start(TimeSpan.FromMinutes(1));
        }

        protected override void OnTimer()
        {
            var timestamp = Server.TimeInUtc;

            string result = timestamp.ToString("HH mm ss");

            LocalStorage.SetString($"{result}", $"{Account.UnrealizedGrossProfit}", LocalStorageScope.Device);

            _textBlock.Text = $"{result}: {Account.UnrealizedGrossProfit}";
        }

    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import clr
clr.AddReference("cAlgo.API")

from cAlgo.API import *

class GrossPnL():
    def on_start(self):
        self.textBlock = TextBlock()
        self.textBlock.Text = "Starting..."
        self.textBlock.FontSize = 15
        self.textBlock.FontWeight = FontWeight.ExtraBold
        self.textBlock.TextAlignment = TextAlignment.Center
        self.textBlock.Padding = Thickness(5, 5, 5, 5)

        aspBlock = api.Asp.SymbolTab.AddBlock("Gross P&L")
        aspBlock.Child = self.textBlock

        api.Timer.Start(60)

    def on_timer(self):
        timestamp = api.Server.TimeInUtc

        result = timestamp.ToString("HH mm ss")

        api.LocalStorage.SetString(result, f"{api.Account.UnrealizedGrossProfit}", LocalStorageScope.Device)

        self.textBlock.Text = f"{result}: {api.Account.UnrealizedGrossProfit}"

Display a separate window with a custom control

The plugin below adds a custom button to a detached window. On click, the control adds a take-profit level for all open positions but only if a position does not have a previously established take profit.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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 ProtectionPlugin : Plugin
    {

        private Button _buttonAddTakeProfit;
        private Window _window;

        protected override void OnStart()
        {
            _buttonAddTakeProfit = new Button
            {
                BackgroundColor = Color.SeaGreen,
                Height = 50,
                Text = "Add Take Profit"
            };

            _buttonAddTakeProfit.Click += AddTakeProfit;
            _window = new Window
            {
                Height = 150,
                Width = 150,
                Padding = new Thickness(5, 10, 10, 5),
            };

            _window.Child = _buttonAddTakeProfit;
            _window.Show();
        }

        private void AddTakeProfit(ButtonClickEventArgs args)
        {
            foreach (var position in Positions)
            {
                if (position.TakeProfit is null)
                {
                    position.ModifyTakeProfitPips(20);
                }
            }
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import clr
clr.AddReference("cAlgo.API")

from cAlgo.API import *

class ProtectionPlugin():
    def on_start(self):
        self.buttonAddTakeProfit = Button()
        self.buttonAddTakeProfit.BackgroundColor = Color.SeaGreen
        self.buttonAddTakeProfit.Height = 50
        self.buttonAddTakeProfit.Text = "Add Take Profit"

        self.buttonAddTakeProfit.Click += self.On_add_take_profit_click

        self.window = Window()
        self.window.Height = 150
        self.window.Width = 150
        self.window.Padding = Thickness(5, 10, 10, 5)

        self.window.Child = self.buttonAddTakeProfit
        self.window.Show()

    def On_add_take_profit_click(self, args):
        for position in api.Positions:
            if position.TakeProfit is None:
                position.ModifyTakeProfitPips(20)

Display information about bar prices in Trade Watch display

When built, this plugin adds a new tab to Trade Watch panel. This tab contains a two-by-two grid that displays information about the last known bar prices for the m1 timeframe and the "USDJPY" symbol.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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 BarInfo : Plugin
    {
        private Grid _grid;
        private TextBlock _lowBlock;
        private TextBlock _highBlock;
        private TextBlock _closeBlock;
        private TextBlock _openBlock;
        private Bars _bars;

        protected override void OnStart()
        {
            _bars = MarketData.GetBars(TimeFrame.Minute, "USDJPY");
            _grid = new Grid(2, 2)
            {
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
                ShowGridLines = true,
                Height = 150,
                Width = 150,
            };

            _lowBlock = new TextBlock
            {
                Text = $"Low: {_bars.LowPrices.LastValue}",
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
            };

            _highBlock = new TextBlock
            {
                Text = $"High: {_bars.HighPrices.LastValue}",
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
            };

            _closeBlock = new TextBlock
            {
                Text = $"Low: {_bars.ClosePrices.LastValue}",
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
            };

            _openBlock = new TextBlock
            {
                Text = $"Open: {_bars.OpenPrices.LastValue}",
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
            };

            _grid.AddChild(_lowBlock, 0, 0);
            _grid.AddChild(_highBlock, 0, 1);
            _grid.AddChild(_openBlock, 1, 0);
            _grid.AddChild(_closeBlock, 1, 1);

            var TradeWatchTab = TradeWatch.AddTab("Bar Info");
            TradeWatchTab.Child = _grid;
            TradeWatchTab.IsSelected = true;
            _bars.Tick += OnBarsTick;
        }

        private void OnBarsTick(BarsTickEventArgs args)
        {
            _lowBlock.Text = _bars.LowPrices.LastValue.ToString();
            _highBlock.Text = _bars.HighPrices.LastValue.ToString();
            _openBlock.Text = _bars.HighPrices.LastValue.ToString();
            _closeBlock.Text = _bars.HighPrices.LastValue.ToString();
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import clr
clr.AddReference("cAlgo.API")

from cAlgo.API import *

class BarInfo():
    def on_start(self):
        tradeWatchTab = api.TradeWatch.AddTab("Bar Info")
        tradeWatchTab.IsSelected = True

        grid = Grid(2, 2)
        grid.HorizontalAlignment = HorizontalAlignment.Center
        grid.VerticalAlignment = VerticalAlignment.Center
        grid.ShowGridLines = True
        grid.Height = 150
        grid.Width = 150

        self.bars = api.MarketData.GetBars(TimeFrame.Minute, "USDJPY")

        self.lowBlock = self.get_text_block(f"Low: {self.bars.LowPrices.LastValue}")
        self.highBlock = self.get_text_block(f"High: {self.bars.HighPrices.LastValue}")
        self.openBlock = self.get_text_block(f"Open: {self.bars.OpenPrices.LastValue}")
        self.closeBlock = self.get_text_block(f"Close: {self.bars.ClosePrices.LastValue}")

        grid.AddChild(self.lowBlock, 0, 0)
        grid.AddChild(self.highBlock, 0, 1)
        grid.AddChild(self.openBlock, 1, 0)
        grid.AddChild(self.closeBlock, 1, 1)

        tradeWatchTab.Child = grid

        self.bars.Tick += self.on_bars_Tick

    def on_bars_Tick(self, args):
        self.lowBlock.Text = f"Low: {self.bars.LowPrices.LastValue}"
        self.highBlock.Text = f"High: {self.bars.HighPrices.LastValue}"
        self.openBlock.Text = f"Open: {self.bars.OpenPrices.LastValue}"
        self.closeBlock.Text = f"Close: {self.bars.ClosePrices.LastValue}"

    def get_text_block(self, text):
        textblock = TextBlock()
        textblock.Text = text
        textblock.HorizontalAlignment = HorizontalAlignment.Center
        textblock.VerticalAlignment = VerticalAlignment.Center
        return textblock

React to changing the active frame

The following plugin detects which ChartFrame is currently active. Inside a custom block in the ASP, the plugin shows the percentage difference between the current price of the symbol for which this ChartFrame is opened and the price of this symbol approximately one month ago.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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 ActiveFrameChangedSample : Plugin
    {

        // Declaring the necessary UI elements
        private Grid _grid;
        private TextBlock _percentageTextBlock;
        private Frame _activeFrame;

        protected override void OnStart()
        {
            // Initialising the grid and the TextBlock
            // displaying the percentage difference
            _grid = new Grid(1, 1);
            _percentageTextBlock = new TextBlock
            {
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
                Text = "Monthly change: ",
            };

            _grid.AddChild(_percentageTextBlock, 0, 0);

            // Initialising a new block inside the ASP
            // and adding the grid as a child
            var block = Asp.SymbolTab.AddBlock("Monthly Change Plugin");

            block.Child = _grid;

            // Attaching a custom handler to the
            // ActiveFrameChanged event
            ChartManager.ActiveFrameChanged += ChartManager_ActiveFrameChanged;
        }

        private void ChartManager_ActiveFrameChanged(ActiveFrameChangedEventArgs obj)
        {
            if (obj.NewFrame is ChartFrame)
            {
                // Casting the Frame into a ChartFrame
                var newChartFrame = obj.NewFrame as ChartFrame;

                // Attaining market data for the symbol for which
                // the currently active ChartFrame is opened
                var dailySeries = MarketData.GetBars(TimeFrame.Daily, newChartFrame.Symbol.Name);

                // Calculating the monthly change and displaying it
                // inside the TextBlock
                double monthlyChange = (newChartFrame.Symbol.Bid - dailySeries.ClosePrices[dailySeries.ClosePrices.Count - 30]) / 100;
                _percentageTextBlock.Text = $"Monthly change: {monthlyChange}";
            }
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import clr
clr.AddReference("cAlgo.API")

from cAlgo.API import *

class ActiveFrameChangedSample():
    def on_start(self):
        # Initialising the grid and the TextBlock displaying the percentage difference
        grid = Grid(1, 1)
        self.percentageTextBlock = TextBlock()
        self.percentageTextBlock.HorizontalAlignment = HorizontalAlignment.Center
        self.percentageTextBlock.VerticalAlignment = VerticalAlignment.Center
        self.percentageTextBlock.Text = "Monthly change: "

        grid.AddChild(self.percentageTextBlock, 0, 0)

        # Initialising a new block inside the ASP and adding the grid as a child
        block = api.Asp.SymbolTab.AddBlock("Monthly Change Plugin")
        block.Child = grid

        # Attaching an event handler to the ActiveFrameChanged event
        api.ChartManager.ActiveFrameChanged += self.on_chart_manager_active_frame_changed

    def on_chart_manager_active_frame_changed(self, args):
        if args.NewFrame is None or isinstance(args.NewFrame.__implementation__, ChartFrame) == False:
            return

        newChartFrame = ChartFrame(args.NewFrame)

        # Attaining market data for the symbol for which the currently active ChartFrame is opened
        dailySeries = api.MarketData.GetBars(TimeFrame.Daily, newChartFrame.Symbol.Name)

        # Calculating the monthly change and displaying it inside the TextBlock
        monthlyChange = (newChartFrame.Symbol.Bid - dailySeries.ClosePrices[dailySeries.ClosePrices.Count - 30]) / 100
        self.percentageTextBlock.Text = f"Monthly change: {monthlyChange}"

Image title