在C#中通过WebSocket检索一个价格流。

 

你好,亲爱的先生们。对接收不同来源的报价感兴趣(包括LMAX交易所)。由于不同的经纪商与不同的ECN、流动性提供商相连,因此最好直接从ECN本身获得报价。但那里有一个限制 - 大多数供应商不会与我们分享他们的报价,除非我们直接与他们联系。但有几个地方我们可以获得报价,包括 "市场深度"。例如,LMAX在小工具中流传其流动性,如https://s3-eu-west-1.amazonaws.com/lmax-widget3/website-widget-vwap.html(周末不活动,因为没有报价,在工作日,标志会消失,会出现一个市场)。也有一些ECN将其报价与市场深度结合起来。

小工具中的报价是通过websocket流式传输的,也就是说,它们不能直接通过WebRequest()获得,必须订阅websocket事件。这里是我陷入黑暗森林的地方,因为我不(几乎)熟悉C#,更不熟悉网络技术:)找到了2个通过网络套接字获取数据的例子,但都不能正常工作。

示例:创建客户端并连接到套接字,看起来像这样。我收到了(在挖掘了widget的来源并试图理解一些东西之后),我可以向widget发送一个报价,但不能接收:)也就是说,不清楚应该向服务器发送哪个请求来接收一个报价。如果我们自己发送报价,服务器会积极回复,它们会出现在小部件中。在截图中,你可以看到服务器的回复,如果你在Chrome中查看套接字的线程,你会看到关于连接的信息。而当市场活跃时,发送的报价将被下载并显示在小部件中 :)我有另一个例子,通过套接字连接,但到目前为止还没有工作。

如果有人知道如何从widget接收报价(可能是直接从MT和winapi,或C#),我会很感激:)

using System;
using System.Threading.Tasks;
namespace SenseConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            GetDocList();
            Console.ReadLine();
        }
        static async Task<string> GetDocList()
        {
            var client = new SenseWebSocketClient(new Uri("wss://data-fix.smt-data.com/lmax"));
            Console.WriteLine("Connecting to Qlik Sense...");
            Console.WriteLine("Getting document list...");
            var docs = await client.GetDocList();
            Console.WriteLine(docs);
            return docs;
        }
    }
}

using System;   
 using System.Net.WebSockets;  
 using System.Text;  
 using System.Threading;  
 using System.Threading.Tasks;

 namespace SenseConsoleApp  
 {
   
   public class SenseWebSocketClient  
   {  
     private ClientWebSocket _client;
    
     public Uri _senseServerURI;  
     public SenseWebSocketClient(Uri senseServerURI)  
     {  
       _client = new ClientWebSocket();
       _senseServerURI = senseServerURI;  
     }  
     public async Task<string> GetDocList()  
     {
            
       string cmd = "{\"channel\":\"/fixprof/depthmax/EURUSD\",\"data\":[\"EURUSD\",0,0,0,0,0,0,0,0,0,0,[[0,0],[0.0,0.0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0],[0.0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]],\"id\":\"56kcb\"}";  // это строка с запросом к серверу, т.е. по факту мы отправляем на него котировки
       await _client.ConnectAsync(_senseServerURI, CancellationToken.None);  
       await SendCommand(cmd);  
       var docList = await Receive();  
       return docList;  // ответ сервера
     }  
     private async Task ConnectToSenseServer()  
     {  
       await _client.ConnectAsync(_senseServerURI, CancellationToken.None);  
     }  
     private async Task SendCommand(string jsonCmd)  
     {  
       ArraySegment<byte> outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(jsonCmd));  
       await _client.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None);  
     }  
     private async Task<string> Receive()  
     {  
       var receiveBufferSize = 1536;  
       byte[] buffer = new byte[receiveBufferSize];  
       var result = await _client.ReceiveAsync (new ArraySegment<byte>(buffer), CancellationToken.None);  
       var resultJson = (new UTF8Encoding()).GetString(buffer);  
       return resultJson;  
     }  
   }  
 }  

这个框架显示一个报价流和其他信息,如我在活动时间的连接。

 

我并不是什么都明白。但让我们等待星期一,并引用活动。我也会试着去看。其他直接来源的引文也很有意思。顺便说一句,没有延误吗?

大约5-6年前,我通过VBA Excel从一些网站获得实时信息(不是市场)。现在我已经不记得是怎么做的细节了。我通过我的浏览器窗口与一个与信息页面互动的脚本连接。脚本与页面进行了交流,然后被发送到Excel。

实际上,这是个有趣的问题。

 
Yuriy Asaulenko:

我不明白这一切。但让我们等待星期一,并引用活动。我试着看看。其他直接来源的引文也很有意思。顺便说一下,他们没有延误吗?

大约5-6年前,我通过VBA Excel从一些网站获得实时信息(不是市场)。现在我已经不记得是怎么做的细节了。我通过我的浏览器窗口与一个与信息页面互动的脚本连接。脚本已经把它输入Excel。

实际上,这是个有趣的问题。

是的,我必须等待星期一。在那里,你会立即看到你可以通过提议的查询,把什么东西塞进小部件的零点:)似乎没有延迟,与mt4的报价相比,来得很快...

有趣的问题是,不仅可以从小工具中提取,还可以从网络终端中提取...

 
Maxim Dmitrievsky:

这个问题很有意思,因为你不仅可以从widget中提取,还可以从网络终端中提取...

如果可能的话,请发给我一些其他的链接。不是每个页面都可以用来拉动信息。连接--你连接了,但数据的格式是用,比如说,Flash写的。还有一段。
 
LMAX有一个API - 你不需要任何小工具。
 
Dmitriy Skub:
LMAX有一个API - 你不需要任何小工具。
所以这都是为了面团。任何来自供应商的市场日期都要花费面团,或者10千的发票。
 
Yuriy Asaulenko:
如果可能的话,请发给我一些其他的链接。不是每一个页面都可以用来提取信息。连接--你连接了,但数据的格式被储存在,比如说,Flash。还有一段。
我已经做到了。好吧,我们在这里检查一下......基本上,在这种情况下,一个插座是可见的,一个线程是可见的......问题是如何正确地解析它。 当然,我不是专家,但我相信所有能用眼睛看到的东西都可以通过软件得到:)Flash也会从某个地方提取数据...
 
Dmitriy Skub:
LMAX有一个API - 你不需要任何小工具。
我怀疑你需要一个API的账户。
 
Maxim Dmitrievsky:
我已经把它扔了。好了,我们在这里检查一下......原则上,在这种情况下,一个插座是可见的,一个线程是可见的......问题是如何正确地解析它,当然我不是专家,但我有一个看法,所有眼睛能看到的东西都可以通过软件得到:)Flash也会从某个地方提取数据...
也许可以通过已知的交换协议获得。你不会从Flash中得到它,否则它就类似于密码学。:)
 

2号连接实例。根据规则,为了连接到一个套接字,你需要发送头信息,从http切换到wss,然后已经通过tcp与套接字进行通信...我不明白为什么这样的连接要通过Socket进行,而通过WebSocket 则更容易。这里的错误是请求不正确,即没有发生握手,我认为我正确填写了所有的头信息。也许错误的原因是连接是用ssl的,而且不是GetStream,而是GetSslstream...因为ws是不安全的连接,而wss是安全的。 还有许多其他的 "可能",没有瓶子,或者没有你的帮助,很难理解。)在代码的顶部有一个原始例子的链接,我已经对它进行了一些修改。

//stackoverflow.com/questions/2064641/is-there-a-websocket-client-implemented-for-net
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace ssoc
{
    class Program
    {
        static void Main(string[] args)
        {
            Uri LMAXuri = new Uri("wss://data-fix.smt-data.com/lmax"); // путь к сокету
            Dictionary<string, string> headers = new Dictionary<string, string>(); //список хедеров для передачи серверу

            headers.Add("Host", "data-fix.smt-data.com");
            headers.Add("Connection", "Upgrade");
            headers.Add("Pragma", "no-cache");
            headers.Add("Cache-Control", "no-cache");
            headers.Add("Upgrade", "websocket");
            headers.Add("Origin", "https://s3-eu-west-1.amazonaws.com");
            headers.Add("Sec-WebSocket-Version", "13");
            headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36");
            headers.Add("Accept-Encoding", "gzip, deflate, sdch");
            headers.Add("Accept-Language", "ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4");
            headers.Add("Cookie", "connect.sid=s%3ALwAlO60b6DkqtFUZldvHp-kR.K3qfytDjKCo4cz0%2FxxHpZk1qGCyS9AWFPDv3ro2Yu%2BU");
            headers.Add("Sec-WebSocket-Key", "Kd6XNibByshdJKZA8qWmDA==");
            headers.Add("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits");

            WebSocket ws = new WebSocket(LMAXuri);
            ws.SetHeaders(headers); // установим свои хедерыб иначе будут исп. умолчания
            ws.Connect();
            string result = ws.Recv();
            Console.WriteLine(result);
            Console.ReadLine();

        }
    }

    public class WebSocket
    {
        private Uri mUrl;
        private TcpClient mClient;
        private NetworkStream mStream;
        private bool mHandshakeComplete;
        private Dictionary<string, string> mHeaders;

        public WebSocket(Uri url)
        {
            mUrl = url;

            string protocol = mUrl.Scheme;
            if (!protocol.Equals("ws") && !protocol.Equals("wss"))
                throw new ArgumentException("Unsupported protocol: " + protocol);
        }

        public void SetHeaders(Dictionary<string, string> headers)
        {
            mHeaders = headers;
        }

        public void Connect()
        {
            string host = mUrl.DnsSafeHost;
            string path = mUrl.PathAndQuery;
            string origin = "http://" + host;
            
            mClient = CreateSocket(mUrl);
            mStream = mClient.GetStream();

            int port = ((IPEndPoint)mClient.Client.RemoteEndPoint).Port;
            
            if (port != 443)
                host = host + ":" + port;

            StringBuilder extraHeaders = new StringBuilder();
            if (mHeaders != null)
            {
                foreach (KeyValuePair<string, string> header in mHeaders)
                    extraHeaders.Append(header.Key + ": " + header.Value + "\r\n");
            }

            string request = "GET " + path + " HTTP/1.1\r\n" + extraHeaders.ToString() + "\r\n";
            byte[] sendBuffer = Encoding.UTF8.GetBytes(request);

            mStream.Write(sendBuffer, 0, sendBuffer.Length);

            StreamReader reader = new StreamReader(mStream);
            {
                string header = reader.ReadLine();
                Console.WriteLine(header);
                if (!header.Equals("HTTP/1.1 101 Web Socket Protocol Handshake"))
                    throw new IOException("Invalid handshake response");

                header = reader.ReadLine();
                if (!header.Equals("Upgrade: WebSocket"))
                    throw new IOException("Invalid handshake response");

                header = reader.ReadLine();
                if (!header.Equals("Connection: Upgrade"))
                    throw new IOException("Invalid handshake response");
            }

            mHandshakeComplete = true;
            Console.WriteLine("Сервер пожал нам руку: ", mHandshakeComplete);
        }

        public void Send(string str)
        {
            if (!mHandshakeComplete)
                throw new InvalidOperationException("Handshake not complete");

            byte[] sendBuffer = Encoding.UTF8.GetBytes(str);

            mStream.WriteByte(0x00);
            mStream.Write(sendBuffer, 0, sendBuffer.Length);
            mStream.WriteByte(0xff);
            mStream.Flush();
        }

        public string Recv()
        {
            if (!mHandshakeComplete)
                throw new InvalidOperationException("Handshake not complete");

            StringBuilder recvBuffer = new StringBuilder();

            BinaryReader reader = new BinaryReader(mStream);
            byte b = reader.ReadByte();
            if ((b & 0x80) == 0x80)
            {
                // Skip data frame
                int len = 0;
                do
                {
                    b = (byte)(reader.ReadByte() & 0x7f);
                    len += b * 128;
                } while ((b & 0x80) != 0x80);

                for (int i = 0; i < len; i++)
                    reader.ReadByte();
            }

            while (true)
            {
                b = reader.ReadByte();
                if (b == 0xff)
                    break;

                recvBuffer.Append(b);
            }

            return recvBuffer.ToString();
        }

        public void Close()
        {
            mStream.Dispose();
            mClient.Close();
            mStream = null;
            mClient = null;
        }

        private static TcpClient CreateSocket(Uri url)
        {
            string scheme = url.Scheme;
            string host = url.DnsSafeHost;

            int port = url.Port;
            if (port <= 0)
            {
                if (scheme.Equals("wss"))
                    port = 443;
                else if (scheme.Equals("ws"))
                    port = 80;
                else
                    throw new ArgumentException("Unsupported scheme");
            }
            if (scheme.Equals("ws"))
            {
                throw new NotImplementedException("SSL support not implemented yet");
            }
            else
                return new TcpClient(host, port);
        }
    }
}
 
Maxim Dmitrievsky:
所以这都是为了面团。任何来自供应商的市场日期都要花费面团,或1万以上的发票。
嗯,是的,他们很贪婪,你能做什么呢))。但如果你想进行仲裁,api是唯一可用的选择。