Bỏ qua

WebView trong cTrader

WebView là một điều khiển có thể được sử dụng để hiển thị một trang web trên biểu đồ hoặc cửa sổ đầu ra của chỉ báo.

WebView dựa trên Microsoft Edge WebView2. Do đó, nó hoàn toàn tương thích với tất cả các tiêu chuẩn web hiện đại.

Bạn cũng có thể thực thi mã JavaScript trên một trang và nhận kết quả thông qua WebView.

Ghi chú

WebView chỉ hoạt động trên các chỉ báo và cBot .NET 6 trở lên.

Sử dụng WebView trong cTrader

Ví dụ, chúng ta sẽ hiển thị trang web cTrader.com bên trong một cửa sổ và biểu đồ chính.

 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
using cAlgo.API;

namespace cAlgo.Robots
{
    [Indicator(AccessRights = AccessRights.None, IsOverlay = true)]
    public class WebViewOnWindow : Indicator
    {
        private WebView _webView;

        [Parameter("On Window?", DefaultValue = false)]
        public bool OnWindow { get; set; }

        protected override void Initialize()
        {
            _webView = new WebView
            {
                DefaultBackgroundColor = Color.Red
            };

            _webView.Loaded += OnWebViewLoaded;

            if (OnWindow)
            {
                var window = new Window
                {
                    Child = _webView
                };

                window.Show();
            }
            else
            {
                Chart.AddControl(_webView);
            }           
        }

        public override void Calculate(int index)
        {
        }

        private void OnWebViewLoaded(WebViewLoadedEventArgs args)
        {
            _webView.NavigateAsync("https://ctrader.com/");
        }
    }
}

Sau khi tạo một phiên bản của chỉ báo dưới đây, bạn sẽ có thể điều hướng giữa các trang web khác nhau trên cTrader.com. Cửa sổ đầu ra bạn chọn sẽ hoạt động tương tự như một trình duyệt web.

  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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
using cAlgo.API;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class WebViewOnWindow : Robot
    {
        private WebView _webView;

        private TextBox _addressTextBox;

        private TextBox _scriptTextbox;

        private Button _executeScriptButton;

        protected override void OnStart()
        {
            var goBackButton = new Button
            {
                Text = "←",
                Margin = 3
            };

            goBackButton.Click += OnGoBackButtonClick;

            var goForwardButton = new Button
            {
                Text = "→",
                Margin = 3
            };

            goForwardButton.Click += OnGoForwardButtonClick;

            _addressTextBox = new TextBox
            {
                Text = "https://ctrader.com/",
                Margin = 3,
                Width = 150,
                MinWidth = 150,
                MaxWidth = 150
            };

            var goButton = new Button
            {
                Text = "→",
                Margin = 3
            };

            goButton.Click += OnGoButtonClick;

            var reloadButton = new Button
            {
                Text = "Reload",
                Margin = 3
            };

            reloadButton.Click += OnReloadButtonClick;

            var stopButton = new Button
            {
                Text = "x",
                Margin = 3
            };

            stopButton.Click += OnStopButtonClick;

            _scriptTextbox = new TextBox
            {
                Text = "alert('Hi');",
                Margin = 3,
                Width = 150,
                MinWidth = 150,
                MaxWidth = 150,
                IsEnabled = false
            };

            _executeScriptButton = new Button
            {
                Text = "Execute Script",
                Margin = 3,
                IsEnabled = false
            };

            _executeScriptButton.Click += OnExecuteScriptButtonClick;

            var addressBarPanel = new StackPanel
            {
                MaxHeight = 50,
                VerticalAlignment = VerticalAlignment.Top,
                BackgroundColor = Color.Black,
                Orientation = Orientation.Horizontal
            };

            addressBarPanel.AddChild(goBackButton);
            addressBarPanel.AddChild(goForwardButton);
            addressBarPanel.AddChild(_addressTextBox);
            addressBarPanel.AddChild(goButton);
            addressBarPanel.AddChild(reloadButton);
            addressBarPanel.AddChild(stopButton);
            addressBarPanel.AddChild(_scriptTextbox);
            addressBarPanel.AddChild(_executeScriptButton);

            _webView = new WebView
            {
                DefaultBackgroundColor = Color.Red
            };

            _webView.NavigationCompleted += OnWebViewNavigationCompleted;
            _webView.WebMessageReceived += OnWebViewWebMessageReceived;
            _webView.Loaded += OnWebViewLoaded;
            _webView.Unloaded += OnWebViewUnloaded;

            var mainGrid = new Grid(2, 1);

            mainGrid.Rows[0].SetHeightToAuto();
            mainGrid.Rows[1].SetHeightInStars(1);

            mainGrid.AddChild(addressBarPanel, 0, 0);
            mainGrid.AddChild(_webView, 1, 0);

            var window = new Window
            {
                Child = mainGrid
            };

            window.Show();
        }

        private void OnWebViewLoaded(WebViewLoadedEventArgs args)
        {
            Print($"Webview loaded, IsLoaded: {args.WebView.IsLoaded}");

            _webView.NavigateAsync(_addressTextBox.Text);
        }

        private void OnWebViewUnloaded(WebViewUnloadedEventArgs args)
        {
            Print($"Webview unloaded, IsLoaded: {args.WebView.IsLoaded}");
        }

        private void OnStopButtonClick(ButtonClickEventArgs args)
        {
            _webView.StopAsync();
        }

        private void OnExecuteScriptButtonClick(ButtonClickEventArgs args)
        {
            var result = _webView.ExecuteScript(_scriptTextbox.Text);

            Print($"IsSuccessful: {result.IsSuccessful} | Json: {result.Json}");
        }

        private void OnReloadButtonClick(ButtonClickEventArgs args)
        {
            _webView.ReloadAsync();
        }

        private void OnGoForwardButtonClick(ButtonClickEventArgs args)
        {
            _webView.GoForwardAsync();
        }

        private void OnGoBackButtonClick(ButtonClickEventArgs args)
        {
            _webView.GoBackAsync();
        }

        private void OnGoButtonClick(ButtonClickEventArgs args)
        {
            _webView.NavigateAsync(_addressTextBox.Text);
        }

        private void OnWebViewWebMessageReceived(WebViewWebMessageReceivedEventArgs args)
        {
            Print($"Source: {args.Source} | Message: {args.Message}");
        }

        private void OnWebViewNavigationCompleted(WebViewNavigationCompletedEventArgs args)
        {
            Print($"{args.HttpStatusCode} | {args.IsSuccessful} | {args.Url}");

            _addressTextBox.Text = args.Url;
            _scriptTextbox.IsEnabled = true;
            _executeScriptButton.IsEnabled = true;
        }
    }
}

Sử dụng script và xử lý sự kiện tin nhắn

Lớp WebView có hai thành viên mạnh mẽ, cụ thể là phương thức ExecuteScript() và sự kiện WebMessageReceived. Bạn có thể sử dụng chúng để tạo các thuật toán truy cập vào trang web bạn chọn (ví dụ: nguồn cấp tin tức Forex hoặc bất kỳ trang nào có HTML tùy chỉnh), thực thi JavaScript tùy chỉnh trên trang này và xử lý chính xác mọi tin nhắn được gửi từ WebView trở lại thuật toán.

Thực thi script

Để thực thi một script tùy chỉnh bên trong WebView của bạn, tất cả những gì bạn cần làm là gọi phương thức WebView.ExecuteScript(String javaScript) như được hiển thị bên dưới.

1
2
private _webView = new WebView();
_webView.ExecuteScript("document.getElementById('nextButton').style.display = 'none';");

Script cần được thực thi bên trong WebView có thể đơn giản được truyền dưới dạng chuỗi vào phương thức ExecuteString().

Lưu ý rằng chuỗi này phải chứa JavaScript hoàn toàn chính xác. Ví dụ, nó phải bao gồm dấu chấm phẩy sau mỗi dòng có thể thực thi.

JavaScript nhiều dòng

Bạn có thể tự do truyền JavaScript nhiều dòng vào phương thức ExecuteScript() như được hiển thị trong ví dụ dưới đây (lưu ý việc sử dụng chuỗi ký tự).

1
2
3
4
_webView.ExecuteScript(@"
const element = document.getElementById('nextButton');
element.style.display = 'none';
"); 

Xử lý sự kiện tin nhắn

Sự kiện WebMessageReceived được kích hoạt khi nội dung hiển thị trong WebView gửi một tin nhắn. Bạn có thể phản ứng với sự kiện này bằng cách đăng ký bất kỳ trình xử lý sự kiện phù hợp nào.

1
2
3
4
5
6
private _webView = new WebView();
_webView.WebMessageReceived += OnWebMessageReceived;
private void OnWebMessageReceived(WebViewWebMessageReceivedEventArgs args)
{
    Print($"Source: {args.Source} | Message: {args.Message}");
}

Để gửi một tin nhắn từ WebView, tất cả những gì bạn cần làm là gọi window.postMessage() từ mã JavaScript bên trong trang HTML. Ví dụ, bạn có thể truyền script trong ví dụ dưới đây để tính toán sự khác biệt phần trăm giữa hai số (chẳng hạn như giá vào lệnh và giá thoát lệnh có thể cho một vị thế).

1
2
3
4
5
function percentChange(oldVal, newVal) {
  let change = ((newVal - oldVal) / oldVal) * 100;
  window.postMessage('Change: ${change}%');
}
percentChange(1.0784, 1.0975);

Biến và nội suy chuỗi

Ngữ cảnh không được chia sẻ giữa C# và JavaScript khi bạn gọi phương thức ExecuteScript(). Nếu bạn muốn truyền một biến từ phạm vi C# sang phạm vi JavaScript, bạn sẽ phải sử dụng nội suy chuỗi.

1
2
var a = 2 + 2;
_webView.ExecuteScript($"window.postMessage({a});");

Đặt điều khiển bên trong WebView

Có thể kết hợp ExecuteScript()OnWebMessageReceived để đặt các điều khiển bên trong một trang được mở thông qua WebView mà khi tương tác, sẽ gửi tin nhắn trở lại cBot. Trong ví dụ dưới đây, chúng ta sử dụng chức năng này để cho phép giao dịch trực tiếp bên trong WebView. Lưu ý rằng WebView mở một trang HTML tùy chỉnh thay vì một tài nguyên hiện có.

  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
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;
using System.Text.Json;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class WebViewInteractionsExample : Robot
    {
        private WebView _webView;

        protected override void OnStart()
        {
            var window = new Window();           
            _webView = new WebView();

            _webView.Loaded += WebView_Loaded;
            _webView.NavigationCompleted += WebView_NavigationCompleted;
            _webView.WebMessageReceived += WebView_WebMessageReceived;

            window.Child = _webView;
            window.Width = 500;
            window.Height = 500;
            window.Show();
            window.Closed += args => Stop(); // stop cBot when window is closed
        }

        private void WebView_WebMessageReceived(WebViewWebMessageReceivedEventArgs args)
        {
            Print("Message received from WebView: {0}", args.Message);

            var tradeMessage = JsonSerializer.Deserialize<TradeMessage>(args.Message);;
            var tradeType = tradeMessage.side == "buy" ? TradeType.Buy : TradeType.Sell;

            ExecuteMarketOrder(tradeType, tradeMessage.symbolName, tradeMessage.volume);
        }

        private void WebView_Loaded(WebViewLoadedEventArgs args)
        {
            args.WebView.NavigateToStringAsync(@"
            <script>
                function setSymbols(symbols) // called from the cBot
                {
                const symbolSelector = document.getElementById('symbolHtml');
                symbolSelector.innerHTML = '';
                symbols.forEach((symbol) => {  
                    symbolSelector.add(new Option(symbol));
                });
                }

                function trade(operation)
                {
                const symbolSelector = document.getElementById('symbolHtml');
                const selectedSymbolName = symbolSelector.options[symbolSelector.selectedIndex].text;

                window.postMessage({
                    side: operation,
                    volume: Number(document.getElementById('volumeHtml').value),
                    symbolName: selectedSymbolName
                }); //send message to cBot
                }
            </script>

            <body bgcolor='white'>
            <div style='display:flex;flex-direction:column;width:300px'>
                <h1>This is HTML page</h1>
                <div>Symbol:</div>
                <select id='symbolHtml'></select>
                <span style='margin-top: 10px'>Volume:</span>
                <input id='volumeHtml' value='1000' />
                <div style='margin-top:10px'>
                    <input type='button' value='Buy' onclick='trade(`buy`)' />
                    <input type='button' value='Sell' onclick='trade(`sell`)' />
                </div>
            </div>
            </body>
        ");          
        }

        private void WebView_NavigationCompleted(WebViewNavigationCompletedEventArgs obj)
        {
            SendSymbolsToHtml();
        }

        private void SendSymbolsToHtml()
        {
            var allSymbols = "";
            foreach (var symbol in Symbols)
            {
                allSymbols = allSymbols + "'" + symbol + "',";
            }

            _webView.ExecuteScript("setSymbols([" + allSymbols + "]);"); // invokes the corresponding function inside HTML
        }        
    }

    class TradeMessage
    {
        public string side { get; set; }
        public double volume { get; set; }
        public string symbolName { get; set; }
    }
}

Thông qua WebView, cBot mở một trang HTML tùy chỉnh chứa các điều khiển cho phép chúng ta đặt lệnh mới. Sử dụng phương thức SendSymbolsToHtml(), cBot điền vào một menu thả xuống bên trong HTML với danh sách tất cả các cặp tiền tệ mà một tài khoản có thể giao dịch. Trong khi cBot đang chạy, người dùng có thể chọn một cặp tiền tệ, chỉ định khối lượng lệnh và sau đó đặt một lệnh mới trực tiếp bên trong trang web mà không cần tương tác với các điều khiển bên trong cTrader.