cTrader 中的 WebView
WebView 是一个控件,可用于在图表或指标输出窗口中显示网页。
WebView 基于 Microsoft Edge WebView2。 因此,它与所有现代网络标准完全兼容。
您还可以在页面上执行 JavaScript 代码并通过 WebView 获取结果。
注意
WebView 仅在 .NET 6 或更高版本的指标和 cBot 中有效。
在 cTrader 中使用 WebView
作为示例,我们将在窗口和主图表中显示 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 事件。 您可以使用它们创建访问您选择的网页(例如,外汇新闻源或任何包含自定义 HTML 的页面)的算法,在该页面上执行自定义 JavaScript,并正确处理从 WebView 发送回算法的任何消息。
执行脚本
要在 WebView 中执行自定义脚本,您只需调用 WebView.ExecuteScript(String javaScript) 方法,如下所示。
| private _webView = new WebView();
_webView.ExecuteScript("document.getElementById('nextButton').style.display = 'none';");
|
需要在 WebView 中执行的脚本可以简单地作为字符串传递给 ExecuteString() 方法。
请注意,此字符串必须包含完全准确的 JavaScript。 例如,它必须在每个可执行行后包含分号。
多行 JavaScript
您可以自由地将多行 JavaScript 传递给 ExecuteScript() 方法,如下例所示(注意字符串文字的使用)。
| _webView.ExecuteScript(@"
const element = document.getElementById('nextButton');
element.style.display = 'none';
");
|
处理消息事件
当 WebView 中显示的内容发送消息时,会引发 WebMessageReceived 事件。 您可以通过订阅任何合适的事件处理程序来响应此事件。
| private _webView = new WebView();
_webView.WebMessageReceived += OnWebMessageReceived;
private void OnWebMessageReceived(WebViewWebMessageReceivedEventArgs args)
{
Print($"Source: {args.Source} | Message: {args.Message}");
}
|
要从 WebView 发送消息,您只需从 HTML 页面内的 JavaScript 代码中调用 window.postMessage()。 例如,您可以传递以下示例中的脚本来计算两个数字之间的百分比差异(例如可能的建仓价和平仓价)。
| function percentChange(oldVal, newVal) {
let change = ((newVal - oldVal) / oldVal) * 100;
window.postMessage('Change: ${change}%');
}
percentChange(1.0784, 1.0975);
|
变量和字符串插值
当您调用 ExecuteScript() 方法时,C# 和 JavaScript 之间不共享上下文。 如果您想将变量从 C# 作用域传递到 JavaScript 作用域,则必须使用字符串插值。
| 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 中的控件进行交互。