Bỏ qua

Tạo plugin trong cTrader

Bài viết này phác thảo các bước để tạo plugin gốc, bao gồm các công cụ cho giao dịch thuật toán hoặc thao tác thủ công trong hệ sinh thái cTrader.

Bạn cũng có thể tìm hiểu cách tạo plugin khung tùy chỉnhcửa sổ tùy chỉnh, hoặc phát triển plugin xuất hiện trong các khu vực giao diện người dùng, chẳng hạn như Bảng ký hiệu đang hoạt động (ASP), Màn hình theo dõi giao dịch (TW) hoặc thanh công cụ biểu đồ.

Trong tab Plugins của ứng dụng Algo, nhấp vào nút Mới để mở trình hướng dẫn tạo thuật toán.

Nhập tên cho plugin của bạn, sau đó chọn một ngôn ngữ lập trình giữa C#Python.

Chọn một phương thức tạo từ:

  • Từ đầu – plugin mới sẽ chỉ chứa một mẫu cơ bản.

  • Sử dụng mẫu – bạn sẽ có thể chọn một thuật toán có sẵn từ danh sách các mẫu Python# hoặc C#, bao gồm nhiều vị trí đặt plugin và chức năng khác nhau.

Ghi chú

Các plugin có sẵn đã chứa logic về vị trí đặt và chức năng. Các plugin như vậy sẵn sàng hoạt động ngay khi bạn lưu và xây dựng chúng.

Sau khi bạn nhấp vào Tạo, trình soạn thảo mã sẽ mở ra và bạn có thể bắt đầu chỉnh sửa mã plugin.

Chỉnh sửa mã

Giới thiệu về plugin đã cho thấy plugin có thể thêm các phần tử mới vào giao diện người dùng cTrader, bao gồm Bảng ký hiệu đang hoạt động (ASP), Màn hình theo dõi giao dịch, khu vực biểu đồ và cửa sổ tùy chỉnh. Hướng dẫn đặt vị trí plugin giải thích cách đặt chúng vào các khu vực này. Quyết định vị trí đặt thông tin hoặc các phần tử điều khiển của plugin trước khi viết bất kỳ mã nào.

Ghi chú

Khi quyết định vị trí đặt, hãy căn chỉnh đầu ra của plugin với khu vực giao diện người dùng phù hợp nhất. Khung biểu đồ phù hợp với WebView lớn, Bảng ký hiệu đang hoạt động phù hợp hơn với các bảng nhỏ gọn và Màn hình theo dõi giao dịch là một lựa chọn tốt cho các thông báo lặp lại.

Tùy thuộc vào phương thức tạo của bạn, mã plugin chứa một hoặc nhiều phần sau:

  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
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
[Plugin(AccessRights = AccessRights.None)]
public class SimplePlugin : Plugin
{
    const string WebViewUrl = "https://ctrader.com";

    protected override void OnStart()
    {
        var icon = new SvgIcon(@"
<svg class='w-6 h-6 text-gray-800 dark:text-white' aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='15' height='15' fill='none' viewBox='0 0 24 24'>
<path stroke='#BFBFBF' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M11 6.5h2M11 18h2m-7-5v-2m12 2v-2M5 8h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Zm0 12h2a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Zm12 0h2a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Zm0-12h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Z'/>
</svg>
");
        Commands.Add(CommandType.ChartContainerToolbar, OnIconClicked, icon);
    }

    private CommandResult OnIconClicked(CommandArgs args)
    {
        var buttonStyle = new Style();
        buttonStyle.Set(ControlProperty.Margin, new Thickness(0, 5, 0, 0));
        buttonStyle.Set(ControlProperty.Width, 150);
        var stackPanel = new StackPanel();

        var showMessageBoxButton = new Button { Text = "show MessageBox", Style = buttonStyle };
        showMessageBoxButton.Click += args =>
        {
            MessageBox.Show("Some message", "Caption", MessageBoxButton.YesNo);
        };
        stackPanel.AddChild(showMessageBoxButton);

        var showCustomWindowButton = new Button { Text = "show Custom Window", Style = buttonStyle };
        showCustomWindowButton.Click += args =>
        {
            var window = new Window();
            var webView = new WebView();
            window.Child = webView;

            window.Show();
            webView.NavigateAsync(WebViewUrl);
        };
        stackPanel.AddChild(showCustomWindowButton);

        var blockCounter = 1;
        var addAspBlockButton = new Button { Text = "add ASP Block", Style = buttonStyle };
        addAspBlockButton.Click += args =>
        {
            var newBlock = Asp.SymbolTab.AddBlock("One more block " + blockCounter);
            newBlock.IsExpanded = true;
            newBlock.Height = 600;
            blockCounter++;

            var webView = new WebView();
            newBlock.Child = webView;
            webView.NavigateAsync(WebViewUrl);
        };
        stackPanel.AddChild(addAspBlockButton);

        var aspTabCounter = 1;
        var addAspTabButton = new Button { Text = "add ASP Tab", Style = buttonStyle };
        addAspTabButton.Click += args =>
        {
            var tab = Asp.AddTab("ASP tab " + aspTabCounter);
            tab.Index = aspTabCounter;
            aspTabCounter++;

            var webView = new WebView();
            tab.Child = webView;
            webView.NavigateAsync(WebViewUrl);
        };
        stackPanel.AddChild(addAspTabButton);

        var tradewatchTabCounter = 1;
        var addTradeWatchTabButton = new Button { Text = "add TradeWatch Tab", Style = buttonStyle };
        addTradeWatchTabButton.Click += args =>
        {
            var tab = TradeWatch.AddTab("Tab " + tradewatchTabCounter);
            tab.Index = tradewatchTabCounter;
            tradewatchTabCounter++;
            tab.IsSelected = true;

            var webView = new WebView();
            tab.Child = webView;
            webView.NavigateAsync(WebViewUrl);
        };
        stackPanel.AddChild(addTradeWatchTabButton);

        var addCustomFrameButton = new Button { Text = "add Custom Frame", Style = buttonStyle };
        var customFrameCounter = 1;
        addCustomFrameButton.Click += args =>
        {
            var frame = ChartManager.AddCustomFrame("Custom Frame " + customFrameCounter);
            customFrameCounter++;

            var webView = new WebView();
            frame.Child = webView;
            webView.NavigateAsync(WebViewUrl);
        };
        stackPanel.AddChild(addCustomFrameButton);

        var customizeActiveChartButton = new Button { Text = "customize Active Chart", Style = buttonStyle };
        customizeActiveChartButton.Click += args =>
        {
            var activeFrame = ChartManager.ActiveFrame;
            if (activeFrame is ChartFrame chartFrame)
            {
                var chart = chartFrame.Chart;
                chart.ColorSettings.BackgroundColor = Color.DarkBlue;
                chart.DisplaySettings.TickVolume = false;
                chart.ZoomLevel = 10;
            }
        };
        stackPanel.AddChild(customizeActiveChartButton);

        var border = new Border();
        border.Padding = 5;
        border.BackgroundColor = "#1A1A1A";
        border.CornerRadius = 3;
        border.BorderThickness = 1;
        border.BorderColor = "525252";
        border.Child = stackPanel;
        border.Width = 170;
        border.Height = 190;
        return new CommandResult(border);
    }
}

Thuộc tính Plugin với các thuộc tính tùy chọn như AccessRights xuất hiện phía trên khai báo lớp plugin (SimplePlugin).

Plugin này sử dụng phương thức OnStart(), được gọi khi plugin được tải. Biểu tượng này thêm một biểu tượng tùy chỉnh vào thanh công cụ biểu đồ bằng cách sử dụng Commands.Add.

When clicked, the icon triggers a user-defined handler (OnIconClicked) that opens a panel with several UI controls.

These are main actions available in the UI:

  • Show message box - opens a standard message box with Yes/No buttons.
  • Hiển thị cửa sổ tùy chỉnh - mở một cửa sổ nổi mới chứa WebView tải một trang bên ngoài.
  • Thêm khối ASP - thêm một khối có thể mở rộng mới vào Bảng ký hiệu đang hoạt động (ASP), nhúng một WebView.
  • Thêm tab ASP - chèn một tab mới trong ASP, tải một WebView vào đó.
  • Thêm tab Màn hình theo dõi giao dịch - thêm một tab mới trong Màn hình theo dõi giao dịch và tải một WebView vào đó.
  • Thêm khung tùy chỉnh - tạo một khung biểu đồ tùy chỉnh chồng lên biểu đồ và điền vào đó một WebView.
  • Tùy chỉnh biểu đồ đang hoạt động - thay đổi giao diện và cài đặt của biểu đồ đang hoạt động (ví dụ: màu nền, mức độ phóng to, hiển thị khối lượng tick).

Mỗi nút trong bảng được tạo kiểu bằng đối tượng Style và được thêm vào một StackPanel dọc. Bản thân bảng được bọc bên trong một Border đã được tạo kiểu, được trả về như là kết quả của lệnh thanh công cụ.

Bằng cách nghiên cứu Cơ bản về C# hoặc Cơ bản về Python và xem xét các ví dụ về mã plugin, bạn có thể hiểu sâu hơn về phát triển thuật toán trong cTrader.

Ghi chú

Tài liệu tham khảo bao gồm tất cả các lớp, sự kiện, phương thức, biến, v.v. để xây dựng plugin trong cTrader, trong khi các ví dụ và mẫu thuật toán đầy đủ có sẵn trong kho lưu trữ GitHub."

  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
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
class SimplePlugin():
    def on_start(self):
        api.Commands.Add(CommandType.ChartContainerToolbar, Func[CommandArgs, CommandResult](self.command_handler), COMMANDSVGICON);

    def command_handler(self, args):
        buttonStyle = Style()
        buttonStyle.Set(ControlProperty.Margin, Thickness(0, 5, 0, 0))
        buttonStyle.Set(ControlProperty.Width, 150)

        stackPanel = StackPanel()

        showMessageBoxButton = Button()
        showMessageBoxButton.Text = "show MessageBox"
        showMessageBoxButton.Style = buttonStyle
        showMessageBoxButton.Click += lambda _ : MessageBox.Show("Some message", "Caption", MessageBoxButton.YesNo)

        stackPanel.AddChild(showMessageBoxButton)

        showCustomWindowButton = Button()
        showCustomWindowButton.Text = "show Custom Window"
        showCustomWindowButton.Style = buttonStyle
        showCustomWindowButton.Click += self.on_show_window_button_clicked

        stackPanel.AddChild(showCustomWindowButton)

        self.blockCounter = 1

        addAspBlockButton = Button()
        addAspBlockButton.Text = "add ASP Block"
        addAspBlockButton.Style = buttonStyle
        addAspBlockButton.Click += self.on_add_asp_block_button_clicked

        stackPanel.AddChild(addAspBlockButton)

        self.aspTabCounter = 1

        addAspTabButton = Button()
        addAspTabButton.Text = "add ASP Tab"
        addAspTabButton.Style = buttonStyle
        addAspTabButton.Click += self.on_add_asp_tab_button_clicked

        stackPanel.AddChild(addAspTabButton)

        self.tradewatchTabCounter = 1

        addTradeWatchTabButton = Button()
        addTradeWatchTabButton.Text = "add TradeWatch Tab"
        addTradeWatchTabButton.Style = buttonStyle
        addTradeWatchTabButton.Click += self.on_add_trade_watch_tab_button

        stackPanel.AddChild(addTradeWatchTabButton)

        self.customFrameCounter = 1;

        addCustomFrameButton = Button()
        addCustomFrameButton.Text = "add Custom Frame"
        addCustomFrameButton.Style = buttonStyle
        addCustomFrameButton.Click += self.on_add_custom_frame_button_clicked

        stackPanel.AddChild(addCustomFrameButton);

        self.mainMenuItemCounter = 1;

        addMainMenuItemButton = Button()
        addMainMenuItemButton.Text = "add Main Menu Item"
        addMainMenuItemButton.Style = buttonStyle
        addMainMenuItemButton.Click += self.on_add_main_menu_item_button_clicked

        stackPanel.AddChild(addMainMenuItemButton);

        self.mainMenuBottomItemCounter = 1;

        addMainMenuBottomItemButton = Button()
        addMainMenuBottomItemButton.Text = "add Main Menu Bottom Item"
        addMainMenuBottomItemButton.Style = buttonStyle
        addMainMenuBottomItemButton.Click += self.on_add_main_menu_bottom_item_button_clicked

        stackPanel.AddChild(addMainMenuBottomItemButton);

        customizeActiveChartButton = Button()
        customizeActiveChartButton.Text = "customize Active Chart"
        customizeActiveChartButton.Style = buttonStyle
        customizeActiveChartButton.Click += self.on_customize_active_chart_button_clicked

        stackPanel.AddChild(customizeActiveChartButton);

        border = Border()
        border.Padding = Thickness(5)
        border.BackgroundColor = Color.FromHex("#1A1A1A")
        border.CornerRadius = CornerRadius(3)
        border.BorderThickness = Thickness(1)
        border.BorderColor = Color.FromHex("#525252")
        border.Child = stackPanel
        border.Width = 170
        border.Height = 250

        return CommandResult(border);

    def on_show_window_button_clicked(self, args):
        window = Window()
        webView = WebView()
        window.Child = webView

        window.Show()
        webView.NavigateAsync(WEBVIEWURL)

    def on_add_asp_block_button_clicked(self, args):
        newBlock = api.Asp.SymbolTab.AddBlock(f"One more block {self.blockCounter}")
        newBlock.IsExpanded = True
        newBlock.Height = 600
        self.blockCounter += 1
        webView = WebView()
        newBlock.Child = webView
        webView.NavigateAsync(WEBVIEWURL)

    def on_add_asp_tab_button_clicked(self, args):
        tab = api.Asp.AddTab(f"ASP tab {self.aspTabCounter}")
        tab.Index = self.aspTabCounter
        self.aspTabCounter += 1
        webView = WebView()
        tab.Child = webView
        webView.NavigateAsync(WEBVIEWURL)

    def on_add_trade_watch_tab_button(self, args):
        tab = api.TradeWatch.AddTab(f"Tab {self.tradewatchTabCounter}")
        tab.Index = self.tradewatchTabCounter
        self.tradewatchTabCounter += 1
        tab.IsSelected = True
        webView = WebView()
        tab.Child = webView
        webView.NavigateAsync(WEBVIEWURL)

    def on_add_custom_frame_button_clicked(self, args):
        frame = api.ChartManager.AddCustomFrame(f"Custom Frame {self.customFrameCounter}")
        self.customFrameCounter += 1
        webView = WebView()
        frame.Child = webView
        webView.NavigateAsync(WEBVIEWURL)

    def on_customize_active_chart_button_clicked(self, args):
        activeFrame = api.ChartManager.ActiveFrame

        if activeFrame is None or isinstance(activeFrame.__implementation__, ChartFrame) == False:
            return

        chartFrame = ChartFrame(activeFrame)
        chart = chartFrame.Chart
        chart.ColorSettings.BackgroundColor = Color.DarkBlue
        chart.DisplaySettings.TickVolume = False
        chart.ZoomLevel = 10

    def on_add_main_menu_item_button_clicked(self, args):
        item = api.MainMenu.AddItem(f"Item {self.mainMenuItemCounter}", MAINMENUITEMSVGICON)
        self.mainMenuItemCounter += 1
        webView = WebView()
        webView.Loaded += lambda _ : webView.NavigateAsync("https://help.ctrader.com/")
        item.Child = webView

    def main_menu_bottom_item_handler(self):
        api.Print("Main menu bottom item triggered!")

    def on_add_main_menu_bottom_item_button_clicked(self, args):
        item = api.MainMenu.AddBottomItem(f"Item {self.mainMenuBottomItemCounter}", MAINMENUBOTTOMITEMSVGICON)
        self.mainMenuBottomItemCounter += 1
        item.Handler = Action(self.main_menu_bottom_item_handler)



WEBVIEWURL = "https://ctrader.com";

COMMANDSVGICON = SvgIcon("<svg class='w-6 h-6 text-gray-800 dark:text-white' aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='15' height='15' fill='none' viewBox='0 0 24 24'><path stroke='#BFBFBF' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M11 6.5h2M11 18h2m-7-5v-2m12 2v-2M5 8h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Zm0 12h2a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Zm12 0h2a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Zm0-12h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1Z'/></svg>")
MAINMENUITEMSVGICON = SvgIcon("<svg width=\"800px\" height=\"800px\" viewBox=\"0 0 1024 1024\" class=\"icon\"  version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M819.9 747.6H204.1c-21.8 0-39.5-17.7-39.5-39.5V278.4c0-21.8 17.7-39.5 39.5-39.5h615.7c21.8 0 39.5 17.7 39.5 39.5v429.7c0.1 21.9-17.6 39.5-39.4 39.5z\" fill=\"#FFD632\" /><path d=\"M819.9 753.1H204.1c-24.8 0-45-20.2-45-45V278.4c0-24.8 20.2-45 45-45h615.7c24.8 0 45 20.2 45 45v429.7c0.1 24.9-20.1 45-44.9 45zM204.1 244.4c-18.7 0-34 15.3-34 34v429.7c0 18.7 15.3 34 34 34h615.7c18.7 0 34-15.3 34-34V278.4c0-18.7-15.3-34-34-34H204.1z\" fill=\"#333336\" /><path d=\"M296.5 766.6h58.3v39.9c0 10.1-8.2 18.4-18.4 18.4h-21.6c-10.1 0-18.4-8.2-18.4-18.4v-39.9z\" fill=\"#FFC86B\" /><path d=\"M336.4 830.4h-21.6c-13.2 0-23.9-10.7-23.9-23.9v-45.4h69.3v45.4c0.1 13.2-10.6 23.9-23.8 23.9zM302 772.1v34.4c0 7.1 5.8 12.9 12.9 12.9h21.6c7.1 0 12.9-5.8 12.9-12.9v-34.4H302z\" fill=\"#333336\" /><path d=\"M441 315.3v-62.1c0-29.4-23.9-53.3-53.3-53.3H263.6c-29.4 0-53.3 23.9-53.3 53.3v62.1c0 24.7 7.8 47.6 21 66.4 6.5 9.2 9.9 20.3 9.9 31.5v160.2c0 11.3-3.4 22.3-9.9 31.5-13.2 18.8-21 41.7-21 66.4v63.1c0 28.9 23.4 52.3 52.3 52.3h126.2c28.9 0 52.3-23.4 52.3-52.3v-63.1c0-24.7-7.7-47.5-20.9-66.2-6.5-9.3-10-20.4-10-31.8v-160c0-11.4 3.4-22.5 10-31.8 13-18.8 20.8-41.6 20.8-66.2z\" fill=\"#68A240\" /><path d=\"M388.7 792.1H262.6c-31.8 0-57.8-25.9-57.8-57.8v-63.1c0-25.1 7.6-49.1 22-69.5 5.8-8.2 8.9-18 8.9-28.4V413.2c0-10.3-3.1-20.1-8.9-28.4-14.4-20.4-22-44.5-22-69.5v-62.1c0-32.4 26.4-58.8 58.8-58.8h124.1c32.4 0 58.8 26.4 58.8 58.8v62.1c0 25-7.6 49-21.9 69.4-5.9 8.3-9 18.2-9 28.7v159.9c0 10.4 3.1 20.3 9 28.7 14.3 20.4 21.9 44.4 21.9 69.4v63.1c0 31.7-25.9 57.6-57.8 57.6zM263.6 205.4c-26.3 0-47.8 21.4-47.8 47.8v62.1c0 22.8 6.9 44.6 20 63.2 7.1 10.1 10.9 22.1 10.9 34.7v160.2c0 12.6-3.8 24.6-10.9 34.7-13.1 18.6-20 40.4-20 63.2v63.1c0 25.8 21 46.8 46.8 46.8h126.2c25.8 0 46.8-21 46.8-46.8v-63.1c0-22.7-6.9-44.5-19.9-63.1-7.2-10.2-11-22.3-11-35V413.3c0-12.7 3.8-24.8 11-35 13-18.5 19.9-40.3 19.9-63.1v-62.1c0-26.3-21.4-47.8-47.8-47.8H263.6z\" fill=\"#333336\" /><path d=\"M780.6 437.5H495.4c-8 0-14.5-6.5-14.5-14.5V317.6c0-8 6.5-14.5 14.5-14.5h285.2c8 0 14.5 6.5 14.5 14.5V423c0 8-6.5 14.5-14.5 14.5z\" fill=\"#D8A128\" /><path d=\"M780.6 443H495.4c-11 0-20-9-20-20V317.6c0-11 9-20 20-20h285.2c11 0 20 9 20 20V423c0 11-8.9 20-20 20zM495.4 308.6c-5 0-9 4-9 9V423c0 5 4 9 9 9h285.2c5 0 9-4 9-9V317.6c0-5-4-9-9-9H495.4zM476.9 889.6c-2.1 0-4-1.2-5-3.2-0.8-1.6-19.7-39-79-19.6-23.1 7.5-41.3 6.6-54.3-2.8-18.3-13.2-18.5-38-18.5-39.1 0-3 2.5-5.5 5.5-5.5s5.5 2.5 5.5 5.5c0 0.3 0.3 20.4 14 30.2 10 7.2 24.9 7.6 44.4 1.2 19.6-6.4 48.1-11 71.8 3.8 14.5 9 20.3 21 20.5 21.5 1.3 2.7 0.1 6-2.6 7.3-0.8 0.5-1.6 0.7-2.3 0.7z m-145.7-64.7z\" fill=\"#333336\" /><path d=\"M550 570.9h-57.3c-4.7 0-8.6-3.8-8.6-8.6V505c0-4.7 3.8-8.6 8.6-8.6H550c4.7 0 8.6 3.8 8.6 8.6v57.3c-0.1 4.7-3.9 8.6-8.6 8.6z\" fill=\"#D5D9CF\" /><path d=\"M550 576.4h-57.3c-7.8 0-14.1-6.3-14.1-14.1V505c0-7.8 6.3-14.1 14.1-14.1H550c7.8 0 14.1 6.3 14.1 14.1v57.3c-0.1 7.8-6.4 14.1-14.1 14.1z m-57.3-74.5c-1.7 0-3.1 1.4-3.1 3.1v57.3c0 1.7 1.4 3.1 3.1 3.1H550c1.7 0 3.1-1.4 3.1-3.1V505c0-1.7-1.4-3.1-3.1-3.1h-57.3z\" fill=\"#333336\" /><path d=\"M667.3 570.9H610c-4.7 0-8.6-3.8-8.6-8.6V505c0-4.7 3.8-8.6 8.6-8.6h57.3c4.7 0 8.6 3.8 8.6 8.6v57.3c0 4.7-3.8 8.6-8.6 8.6z\" fill=\"#D5D9CF\" /><path d=\"M667.3 576.4H610c-7.8 0-14.1-6.3-14.1-14.1V505c0-7.8 6.3-14.1 14.1-14.1h57.3c7.8 0 14.1 6.3 14.1 14.1v57.3c0 7.8-6.3 14.1-14.1 14.1zM610 501.9c-1.7 0-3.1 1.4-3.1 3.1v57.3c0 1.7 1.4 3.1 3.1 3.1h57.3c1.7 0 3.1-1.4 3.1-3.1V505c0-1.7-1.4-3.1-3.1-3.1H610z\" fill=\"#333336\" /><path d=\"M784.7 570.9h-57.3c-4.7 0-8.6-3.8-8.6-8.6V505c0-4.7 3.8-8.6 8.6-8.6h57.3c4.7 0 8.6 3.8 8.6 8.6v57.3c0 4.7-3.9 8.6-8.6 8.6z\" fill=\"#D5D9CF\" /><path d=\"M784.7 576.4h-57.3c-7.8 0-14.1-6.3-14.1-14.1V505c0-7.8 6.3-14.1 14.1-14.1h57.3c7.8 0 14.1 6.3 14.1 14.1v57.3c0 7.8-6.3 14.1-14.1 14.1z m-57.3-74.5c-1.7 0-3.1 1.4-3.1 3.1v57.3c0 1.7 1.4 3.1 3.1 3.1h57.3c1.7 0 3.1-1.4 3.1-3.1V505c0-1.7-1.4-3.1-3.1-3.1h-57.3z\" fill=\"#333336\" /><path d=\"M550 691.6h-57.3c-4.7 0-8.6-3.8-8.6-8.6v-57.3c0-4.7 3.8-8.6 8.6-8.6H550c4.7 0 8.6 3.8 8.6 8.6V683c-0.1 4.8-3.9 8.6-8.6 8.6z\" fill=\"#D5D9CF\" /><path d=\"M550 697.1h-57.3c-7.8 0-14.1-6.3-14.1-14.1v-57.3c0-7.8 6.3-14.1 14.1-14.1H550c7.8 0 14.1 6.3 14.1 14.1V683c-0.1 7.8-6.4 14.1-14.1 14.1z m-57.3-74.4c-1.7 0-3.1 1.4-3.1 3.1v57.3c0 1.7 1.4 3.1 3.1 3.1H550c1.7 0 3.1-1.4 3.1-3.1v-57.3c0-1.7-1.4-3.1-3.1-3.1h-57.3z\" fill=\"#333336\" /><path d=\"M667.3 691.6H610c-4.7 0-8.6-3.8-8.6-8.6v-57.3c0-4.7 3.8-8.6 8.6-8.6h57.3c4.7 0 8.6 3.8 8.6 8.6V683c0 4.8-3.8 8.6-8.6 8.6z\" fill=\"#D5D9CF\" /><path d=\"M667.3 697.1H610c-7.8 0-14.1-6.3-14.1-14.1v-57.3c0-7.8 6.3-14.1 14.1-14.1h57.3c7.8 0 14.1 6.3 14.1 14.1V683c0 7.8-6.3 14.1-14.1 14.1zM610 622.7c-1.7 0-3.1 1.4-3.1 3.1v57.3c0 1.7 1.4 3.1 3.1 3.1h57.3c1.7 0 3.1-1.4 3.1-3.1v-57.3c0-1.7-1.4-3.1-3.1-3.1H610z\" fill=\"#333336\" /><path d=\"M784.7 691.6h-57.3c-4.7 0-8.6-3.8-8.6-8.6v-57.3c0-4.7 3.8-8.6 8.6-8.6h57.3c4.7 0 8.6 3.8 8.6 8.6V683c0 4.8-3.9 8.6-8.6 8.6z\" fill=\"#68A240\" /><path d=\"M784.7 697.1h-57.3c-7.8 0-14.1-6.3-14.1-14.1v-57.3c0-7.8 6.3-14.1 14.1-14.1h57.3c7.8 0 14.1 6.3 14.1 14.1V683c0 7.8-6.3 14.1-14.1 14.1z m-57.3-74.4c-1.7 0-3.1 1.4-3.1 3.1v57.3c0 1.7 1.4 3.1 3.1 3.1h57.3c1.7 0 3.1-1.4 3.1-3.1v-57.3c0-1.7-1.4-3.1-3.1-3.1h-57.3z\" fill=\"#333336\" /></svg>")
MAINMENUBOTTOMITEMSVGICON = SvgIcon("<svg width=\"800px\" height=\"800px\" viewBox=\"0 0 1024 1024\" class=\"icon\"  version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M819 234l-48-13 48-13 13-48 13 48 48 13-48 13-13 48-13-48zM867 287l-19-5 19-4 4-19 4 19 19 4-19 5-4 19-4-19z\" fill=\"#FDCD60\" /><path d=\"M130 472l-21-5 21-5 5-21 5 21 21 5-21 5-5 20-5-20z\" fill=\"#FDCD60\" /><path d=\"M208 246m-9 0a9 9 0 1 0 18 0 9 9 0 1 0-18 0Z\" fill=\"#5546CB\" /><path d=\"M824 804a25 25 0 1 1 25-25 25 25 0 0 1-25 25z m0-36a10 10 0 1 0 10 10 10 10 0 0 0-10-9z\" fill=\"#5546CB\" /><path d=\"M166 728h78v77.34h-78z\" fill=\"#FDCD60\" /><path d=\"M452 509v43h128v-43a19 19 0 0 1 14-18H438a19 19 0 0 1 14 18z\" fill=\"#F97744\" /><path d=\"M661 489h1a19 19 0 0 1 19 19v43h32v114a74 74 0 0 1-74 74H387a73 73 0 0 1-73-73V552h38v-43a19 19 0 0 1 19-19h1a116 116 0 0 1-83-50v337a31 31 0 0 0 31 31h386a31 31 0 0 0 31-31V440a116 116 0 0 1-76 49z\" fill=\"#F97744\" /><path d=\"M334 666a53 53 0 0 0 53 53h251a54 54 0 0 0 54-54v-93h-32a30 30 0 0 1-30 25 30 30 0 0 1-30-25H432a30 30 0 0 1-30 25 30 30 0 0 1-30-25h-38z\" fill=\"#FFFFFF\" /><path d=\"M580 553H452a19 19 0 0 1-19 19h166a19 19 0 0 1-19-19z\" fill=\"#5546CB\" /><path d=\"M662 572h30v93a54 54 0 0 1-54 54H387a53 53 0 0 1-53-53v-94h37a19 19 0 0 1-19-19h-38v113a73 73 0 0 0 73 73h251a74 74 0 0 0 74-74V552h-32a19 19 0 0 1-18 20z\" fill=\"#5546CB\" /><path d=\"M289 375a96 96 0 0 0 83 95v-70a30 30 0 0 1 30-30 30 30 0 0 1 30 30v71h168v-71a30 30 0 0 1 30-30 30 30 0 0 1 30 30v69a96 96 0 0 0 76-94V266a31 31 0 0 0-31-31H320a31 31 0 0 0-31 31v109z\" fill=\"#FDCD60\" /><path d=\"M402 597a30 30 0 0 0 30-25h-59a30 30 0 0 0 29 25zM372 510h60v41.48h-60zM630 597a30 30 0 0 0 30-25h-59a30 30 0 0 0 29 25zM600 510h60v41.48h-60z\" fill=\"#5546CB\" /><path d=\"M438 491h-67a19 19 0 0 0-19 19v43a19 19 0 0 0 19 19h63a19 19 0 0 0 19-19v-44a19 19 0 0 0-15-18z m-6 61h-60v-42h60zM662 490h-67a19 19 0 0 0-14 18v43a19 19 0 0 0 19 19h63a19 19 0 0 0 19-19v-42a19 19 0 0 0-20-19z m-1 61h-61v-41h60z\" fill=\"#FDCD60\" /><path d=\"M786 524h-29v148h29a24 24 0 0 0 24-24V548a24 24 0 0 0-24-24z\" fill=\"#AFBCF3\" /><path d=\"M786 504h-29V266a51 51 0 0 0-51-51h-78a116 116 0 0 0-230 0h-78a51 51 0 0 0-51 51v240l-8-2h-30a44 44 0 0 0-44 44v100a44 44 0 0 0 44 44h30l8-2v87a51 51 0 0 0 51 51h386a51 51 0 0 0 51-51v-85h29a44 44 0 0 0 44-44V548a44 44 0 0 0-44-44zM260 672h-29a24 24 0 0 1-24-24V548a24 24 0 0 1 24-24h29z m253-536a96 96 0 0 1 95 79H418a96 96 0 0 1 95-79zM289 266a31 31 0 0 1 31-31h386a31 31 0 0 1 31 31v109a96 96 0 0 1-76 94v-69a30 30 0 0 0-30-30 30 30 0 0 0-30 30v71H432v-71a30 30 0 0 0-30-30 30 30 0 0 0-30 30v70a96 96 0 0 1-83-95V266z m417 542H320a31 31 0 0 1-31-31V440a116 116 0 0 0 83 50h289a116 116 0 0 0 76-49v336a31 31 0 0 1-31 31z m104-160a24 24 0 0 1-24 24h-29V524h29a24 24 0 0 1 24 24z\" fill=\"#5546CB\" /><path d=\"M208 548v100a24 24 0 0 0 24 24h29V524h-30a24 24 0 0 0-23 24z\" fill=\"#AFBCF3\" /></svg>")

Lớp SimplePlugin đại diện cho một plugin được viết bằng Python sử dụng Algo API để mở rộng giao diện nền tảng với các phần tử điều khiển và tương tác tùy chỉnh.

Phương thức on_start() là điểm khởi đầu, được gọi khi plugin được khởi tạo. Nó thêm một biểu tượng SVG tùy chỉnh vào thanh công cụ biểu đồ.

When clicked, the icon triggers the command_handler method, which returns a styled panel (CommandResult) containing multiple interactive buttons.

Each button performs a distinct platform interaction:

  • Show message box - opens a standard message box with yes and no buttons.
  • Hiển thị cửa sổ tùy chỉnh - mở một cửa sổ nổi mới chứa WebView tải một trang bên ngoài.
  • Thêm khối ASP - thêm một khối có thể mở rộng mới vào Bảng ký hiệu đang hoạt động (ASP), nhúng một WebView.
  • Thêm tab ASP - chèn một tab mới trong khu vực ASP, tải một WebView vào đó.
  • Thêm tab Màn hình theo dõi giao dịch - thêm một tab mới vào Màn hình theo dõi giao dịch và chọn nó theo mặc định.
  • Thêm khung tùy chỉnh - tạo một khung biểu đồ nổi và chèn một WebView vào bên trong.
  • Thêm mục menu chính - thêm một mục mới vào menu cấp cao nhất chính với một biểu tượng tùy chỉnh, mở một WebView trỏ đến trang Trợ giúp cTrader.
  • Thêm mục dưới cùng menu chính - thêm một mục vào phần dưới cùng của menu chính, in một thông báo khi được nhấp vào.
  • Tùy chỉnh biểu đồ đang hoạt động - thay đổi biểu đồ đang hoạt động bằng cách thay đổi màu nền, ẩn khối lượng tick và điều chỉnh mức độ phóng to.

Các nút này được thêm vào một bố cục xếp chồng theo chiều dọc (StackPanel) và được bọc trong một Border đã được tạo kiểu, xác định kích thước, khoảng đệm, nền, độ dày và màu viền của nó. Sau đó, bảng được trả về dưới dạng CommandResult, hiển thị nó trong giao diện người dùng biểu đồ.

Bằng cách nghiên cứu Cơ bản về Python và xem xét các ví dụ về mã plugin, bạn có thể hiểu sâu hơn về phát triển thuật toán trong cTrader.

Ghi chú

Tài liệu tham khảo bao gồm tất cả các lớp, sự kiện, phương thức, biến, v.v. để xây dựng plugin trong cTrader, trong khi các ví dụ và mẫu thuật toán đầy đủ có sẵn trong kho lưu trữ GitHub."

Áp dụng kiến thức mới của bạn để chỉnh sửa mã plugin và điều chỉnh nó theo nhu cầu của bạn.

Lưu và biên dịch

Lưu mã của bạn bằng cách nhấp vào nút Lưu ở đầu trình soạn thảo mã hoặc sử dụng phím tắt Ctrl+S.

Trước khi có thể sử dụng plugin của mình, bạn cần xác thực mã của nó bằng cách xây dựng dự án plugin. Nhấp vào nút Biên dịch ở đầu trình soạn thảo mã hoặc nhấn Ctrl+B.

Lưu mã của bạn bằng cách nhấp vào biểu tượng Lưu ở đầu trình soạn thảo mã hoặc sử dụng phím tắt Cmd+S.

Trước khi bạn có thể sử dụng chỉ báo của mình, bạn cần xác thực mã của nó bằng cách xây dựng dự án chỉ báo. Nhấp vào biểu tượng Biên dịch ở đầu trình soạn thảo mã hoặc nhấn Cmd+B.

Khi việc xây dựng thành công, bạn sẽ thấy một thông báo xác nhận trong Kết quả xây dựng. Nếu quá trình biên dịch thất bại, một bản tóm tắt tất cả các lỗi gặp phải sẽ xuất hiện thay vào đó.

Nếu có thay đổi trong mã kể từ lần biên dịch cuối cùng, một dấu sao sẽ xuất hiện bên cạnh biểu tượng Biên dịch. Trong trường hợp này, bạn nên xây dựng lại plugin để các thay đổi có hiệu lực.

Khi xây dựng thành công, plugin sẽ ngay lập tức thêm các phần tử tùy chỉnh vào khu vực của giao diện người dùng cTrader được chỉ định trong mã của nó.

Sử dụng các phương thức đặc biệt (tùy chọn)

Tương tự như cBot, plugin có thể sử dụng phương thức OnStart(), phương thức OnStop() và đối tượng Timer:

  • Trình xử lý OnStart() được gọi một lần khi một plugin mới được khởi tạo.
  • Trình xử lý OnStop() được gọi khi phiên bản plugin bị vô hiệu hóa, gỡ cài đặt hoặc kết thúc do cTrader đóng.
  • Đối tượng Timer được sử dụng để chạy mã theo các khoảng thời gian đều đặn. Sau khi khởi tạo và khởi động bộ hẹn giờ tích hợp của lớp Timer, phương thức được chỉ định sẽ được thực thi mỗi khi khoảng thời gian đã định trôi qua.

Xử lý ngoại lệ

Giống như các thuật toán khác, plugin có thể truy cập phương thức OnException, thường được sử dụng để kiểm soát cách một thuật toán xử lý lỗi.

Để minh họa việc sử dụng các phương thức đã đề cập ở trên, hãy xem xét một plugin đơn giản hiển thị tổng số vị thế có lãi hiện đang mở. Được gắn vào ASP, plugin này cập nhật hiển thị của nó mỗi giây bằng cách sử dụng bộ hẹn giờ tích hợp.

 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
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 ProfitablePositions : Plugin
    {       
        private TextBlock _textBlock = new TextBlock 
        {
            Text = "Starting...",
            FontSize = 30,
            FontWeight = FontWeight.ExtraBold,
            TextAlignment = TextAlignment.Center,
            Padding = new Thickness(5, 5, 5, 5),
        };

        protected override void OnStart()
        {
            var aspBlock = Asp.SymbolTab.AddBlock("Profitable Positions");
            aspBlock.IsExpanded = true;
            aspBlock.Height = 50;

            aspBlock.Child = _textBlock;
            _textBlock.Text = GetProfitablePositions();

            Timer.Start(TimeSpan.FromSeconds(1));

        }

        protected override void OnTimer() 
        {
            _textBlock.Text = GetProfitablePositionsCount().toString();
        }


        protected int GetProfitablePositionsCount() 
        {
            int count = 0;
            foreach (var position in Positions) 
            {
                if (position.GrossProfit > 0) 
                {
                    count++;
                }
            }
            return count;
        }
    }        
}

Image title