ข้ามไปที่เนื้อหา

WebView ใน cTrader

WebView เป็นตัวควบคุมที่สามารถใช้เพื่อแสดงหน้าเว็บบนแผนภูมิหรือหน้าต่างแสดงผลของอินดิเคเตอร์

WebView อิงตาม Microsoft Edge WebView2 ดังนั้น จึงเข้ากันได้อย่างสมบูรณ์กับมาตรฐานเว็บสมัยใหม่ทั้งหมด

คุณยังสามารถเรียกใช้โค้ด JavaScript บนหน้าและรับผลลัพธ์ผ่าน WebView ได้อีกด้วย

หมายเหตุ

WebView ทำงานได้เฉพาะบนอินดิเคเตอร์และ cBot ที่ใช้ .NET 6 หรือใหม่กว่าเท่านั้น

การใช้ WebView ใน cTrader

ตัวอย่างเช่น เราจะแสดงเว็บไซต์ cTrader.com ภายในหน้าต่างและแผนภูมิหลัก

 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/");
        }
    }
}

หลังจากสร้างอินสแตนซ์ของอินดิเคเตอร์ด้านล่าง คุณจะสามารถนำทางระหว่างหน้าเว็บต่างๆ บน cTrader.com ได้ หน้าต่างแสดงผลที่คุณเลือกจะทำงานคล้ายกับเว็บเบราว์เซอร์

  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;
        }
    }
}

การใช้สคริปต์และการจัดการเหตุการณ์ข้อความ

คลาส WebView มีสมาชิกที่ทรงพลังสองตัว ได้แก่ เมธอด ExecuteScript() และเหตุการณ์ WebMessageReceived คุณสามารถใช้สิ่งเหล่านี้เพื่อสร้างอัลกอที่เข้าถึงหน้าเว็บที่คุณเลือก (เช่น ฟีดข่าว Forex หรือหน้าใดๆ ที่มี HTML แบบกำหนดเอง) เรียกใช้ JavaScript ที่กำหนดเองบนหน้านี้ และจัดการข้อความที่ส่งจาก WebView กลับไปยังอัลกอได้อย่างถูกต้อง

เรียกใช้สคริปต์

ในการเรียกใช้สคริปต์ที่กำหนดเองภายใน WebView ของคุณ สิ่งที่คุณต้องทำคือเรียกเมธอด WebView.ExecuteScript(String javaScript) ดังที่แสดงด้านล่าง

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

สคริปต์ที่ต้องเรียกใช้ภายใน WebView สามารถส่งผ่านเป็นสตริงไปยังเมธอด ExecuteString() ได้อย่างง่ายดาย

โปรดทราบว่าสตริงนี้ต้องมี JavaScript ที่ถูกต้องอย่างสมบูรณ์ ตัวอย่างเช่น ต้องมีเครื่องหมายอัฒภาคหลังทุกบรรทัดที่สามารถเรียกใช้ได้

JavaScript หลายบรรทัด

คุณสามารถส่ง JavaScript หลายบรรทัดไปยังเมธอด ExecuteScript() ได้อย่างอิสระตามที่แสดงในตัวอย่างด้านล่าง (สังเกตการใช้ลิเทอรัลสตริง)

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

จัดการเหตุการณ์ข้อความ

เหตุการณ์ WebMessageReceived จะเกิดขึ้นเมื่อเนื้อหาที่แสดงใน WebView ส่งข้อความ คุณสามารถตอบสนองต่อเหตุการณ์นี้โดยการสมัครสมาชิกตัวจัดการเหตุการณ์ที่เหมาะสม

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}");
}

ในการส่งข้อความจาก WebView สิ่งที่คุณต้องทำคือเรียก window.postMessage() จากโค้ด JavaScript ภายในหน้า HTML ตัวอย่างเช่น คุณสามารถส่งสคริปต์ในตัวอย่างด้านล่างเพื่อคำนวณความแตกต่างเปอร์เซ็นไทล์ระหว่างตัวเลขสองตัว (เช่น ราคาเข้าและออกที่เป็นไปได้สำหรับตำแหน่ง)

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

ตัวแปรและการแทรกสตริง

บริบทไม่ได้ถูกแชร์ระหว่าง C# และ JavaScript เมื่อคุณเรียกเมธอด ExecuteScript() หากคุณต้องการส่งตัวแปรจากขอบเขต C# ไปยังขอบเขต JavaScript คุณจะต้องใช้การแทรกสตริง

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

วางการควบคุมภายใน WebView

เป็นไปได้ที่จะรวม ExecuteScript() และ OnWebMessageReceived เพื่อวางการควบคุมภายในหน้าที่เปิดผ่าน WebView ซึ่งเมื่อมีการโต้ตอบจะส่งข้อความกลับไปยัง cBot ในตัวอย่างด้านล่าง เราใช้ฟังก์ชันนี้เพื่ออนุญาตให้มีการเทรดโดยตรงภายใน WebView โปรดทราบว่า WebView เปิดหน้า HTML ที่กำหนดเองแทนที่จะเป็นทรัพยากรที่มีอยู่

  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; }
    }
}

ผ่าน WebView, cBot เปิดหน้า HTML ที่กำหนดเองซึ่งมีการควบคุมที่อนุญาตให้เราวางคำสั่งใหม่ โดยใช้วิธี SendSymbolsToHtml(), cBot เติมเมนูแบบเลื่อนลงภายใน HTML ด้วยรายการสัญลักษณ์ทั้งหมดที่บัญชีสามารถเทรดได้ ในขณะที่ cBot กำลังทำงาน ผู้ใช้สามารถเลือกสัญลักษณ์ ระบุปริมาณคำสั่ง และจากนั้นวางคำสั่งใหม่โดยตรงภายในหน้าเว็บโดยไม่ต้องโต้ตอบกับการควบคุมภายใน cTrader เลย