Abrufen eines Preisstroms über WebSocket in C#.

 

Guten Tag, meine Damen und Herren. Ich bin daran interessiert, Angebote aus verschiedenen Quellen zu erhalten (einschließlich der LMAX-Börse). Da verschiedene Broker mit verschiedenen ECNs, Liquiditätsanbietern, verbunden sind, ist es besser, die Kurse direkt von den ECNs selbst zu erhalten. Allerdings gibt es hier eine Einschränkung: Die meisten Anbieter geben ihre Angebote nur dann an uns weiter, wenn wir uns direkt mit ihnen in Verbindung setzen. Aber es gibt eine Reihe von Stellen, bei denen wir Angebote einholen können, darunter die "Markttiefe". Zum Beispiel streamt LMAX seine Liquidität in Widgets wie https://s3-eu-west-1.amazonaws.com/lmax-widget3/website-widget-vwap.html (an Wochenenden ist es inaktiv, weil es keine Kurse gibt, an Wochentagen verschwindet das Logo und ein Marktplatz erscheint). Es gibt auch eine Reihe von ECNs, die ihre Kurse mit Markttiefe streamen.

Die Zitate im Widget werden über einen Websocket gestreamt, d.h. sie können nicht direkt über WebRequest() abgerufen werden, sondern es ist notwendig, Websocket-Ereignisse zu abonnieren. Und hier ist, wo ich im dunklen Wald bin, da ich nicht (fast) vertraut mit C# und darüber hinaus mit Web-Technologien :) gefunden 2 Beispiele für das Erhalten von Daten über Web-Sockets, aber keiner von Ihnen funktioniert richtig.

Beispiel: Client erstellen und mit Socket verbinden, sieht so aus. Ich habe es erhalten (nachdem ich in den Widget-Quellen gegraben und versucht habe, etwas zu verstehen), ich kann ein Angebot an das Widget senden, aber nicht empfangen :) d.h. es ist nicht klar, welche Anfrage an den Server gesendet werden soll, um ein Angebot zu erhalten. Wenn wir die Zitate selbst senden, antwortet der Server positiv und sie werden im Widget angezeigt. In den Screenshots können Sie die Antwort des Servers sehen. Wenn Sie den Thread des Sockets mit Ihrem Browser durchsehen, werden Informationen über die Verbindung angezeigt. Und wenn der Markt aktiv ist, werden die gesendeten Notierungen heruntergeladen und im Widget angezeigt :) Ich habe ein anderes Beispiel, Verbindung über Socket, aber es funktioniert nicht so weit.

Wenn jemand eine Idee hat, wie man ein Angebot von Widget (kann direkt von MT und winapi, oder C#) erhalten, wäre ich dankbar :)

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

Dieser Rahmen zeigt einen Strom von Zitaten und anderen Informationen an, z. B. meine Verbindung zur aktiven Zeit:

 

Ich verstehe nicht alles. Aber warten wir den Montag ab und zitieren die Aktivität. Ich werde auch versuchen, danach zu suchen. Interessant sind auch andere direkte Quellen für Zitate. Keine Verspätungen, nebenbei bemerkt?

Vor etwa 5-6 Jahren habe ich über VBA-Excel Echtzeitinformationen (nicht den Markt) von einer Website abgerufen. Jetzt erinnere ich mich nicht mehr an die Details, wie es gemacht wurde. Ich habe die Verbindung über mein Browserfenster mit einem Skript hergestellt, das mit der Informationsseite interagierte. Das Skript kommunizierte mit der Seite, und dann wurde es an Excel gesendet.

Eigentlich ist das eine interessante Frage.

 
Yuriy Asaulenko:

Ich verstehe das alles nicht. Aber warten wir den Montag ab und zitieren die Aktivität. Ich werde versuchen, es zu sehen. Interessant sind auch andere direkte Quellen für Zitate. Sind sie übrigens ohne Verzögerungen?

Vor etwa 5-6 Jahren habe ich über VBA-Excel Echtzeitinformationen (nicht über den Markt) von einer Website abgerufen. Jetzt erinnere ich mich nicht mehr an die Details, wie es gemacht wurde. Ich habe die Verbindung über mein Browserfenster mit einem Skript hergestellt, das mit der Informationsseite interagierte. Das Skript arbeitete bereits mit der Seite und gab sie an Excel weiter.

Eigentlich ist das eine interessante Frage.

Ja, ich muss bis Montag warten. Dort werden Sie sofort sehen, was Sie durch die vorgeschlagene Abfrage in die Widget-Nullen stopfen können :) Scheint ohne Verzögerungen zu sein, im Vergleich zu den Kursen in mt4, kommen schnell...

Interessante Frage, denn es ist möglich, nicht nur von Widgets, sondern auch von Webterminals zu ziehen...

 
Maxim Dmitrievsky:

Die Frage ist interessant, da es möglich ist, nicht nur von Widgets, sondern auch von Webterminals zu ziehen...

Wenn möglich, senden Sie mir bitte weitere Links. Nicht jede Seite kann zum Abrufen von Informationen verwendet werden. Connect - Sie stellen eine Verbindung her, aber das Format der Daten wird z. B. in Flash geschrieben. Und Absatz.
 
LMAX hat eine API - Sie brauchen keine Widgets.
 
Dmitriy Skub:
LMAX hat eine API - Sie brauchen keine Widgets.
Es ist also alles für Geld. Jeder Markttermin von einem Lieferanten kostet Knete, oder eine Rechnung von 10k.
 
Yuriy Asaulenko:
Wenn möglich, senden Sie mir bitte weitere Links. Nicht jede Seite kann zum Abrufen von Informationen verwendet werden. Verbinden - Sie stellen eine Verbindung her, aber das Datenformat wird z. B. in Flash gespeichert. Und Absatz.
Ich habe es getan. Nun, hier überprüfen wir es... Grundsätzlich ist in diesem Fall ein Socket sichtbar, ein Thread ist sichtbar... Natürlich bin ich kein Experte, aber ich glaube, dass alles, was man mit den Augen sehen kann, auch von der Software erfasst werden kann :) Flash wird auch Daten von irgendwoher beziehen...
 
Dmitriy Skub:
LMAX hat eine API - Sie brauchen keine Widgets.
Ich vermute, Sie benötigen ein Konto für die API.
 
Maxim Dmitrievsky:
Ich habe sie geworfen. Nun, hier überprüfen wir es... im Prinzip ist in diesem Fall ein Socket sichtbar, ein Thread ist sichtbar... Frage, wie man es richtig analysiert, natürlich bin ich kein Experte, aber ich bin der Meinung, dass alles, was für die Augen sichtbar ist, durch Software erhalten werden kann :) Flash wird auch Daten von irgendwoher beziehen...
kann mit einem bekannten Austauschprotokoll erhalten werden. Bei Flash ist das nicht der Fall, sonst ist es wie bei der Kryptographie. :)
 

Beispiel für die Verbindung Nr. 2. Nach den Regeln, um eine Verbindung zu einem Socket, müssen Sie Header senden, um von http zu wss wechseln, und dann bereits mit dem Socket über tcp kommunizieren... Ich verstehe nicht, warum eine solche Verbindung über Socket hergestellt wird, wenn es über WebSocket viel einfacher ist. Der Fehler ist hier eine falsche Anfrage, d.h. es findet kein Handshake statt, ich denke, ich habe alle Header korrekt ausgefüllt. Vielleicht ist der Fehler, dass die Verbindung mit ssl ist, und es ist nicht GetStream, aber GetSslstream... Da es sich bei ws um eine ungesicherte Verbindung handelt und wss gesichert ist und es noch viele andere "Vielleichts" gibt, ist es schwer zu verstehen, ohne eine Flasche oder Ihre Hilfe :) Am Anfang des Codes befindet sich ein Link zum Originalbeispiel, das ich bereits etwas verändert habe.

//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:
Es ist also alles für Geld. Jeder Markttermin eines Verkäufers kostet Knete oder eine Rechnung von 10.000 oder mehr.
Nun ja, sie sind gierig, aber was soll man machen)). Aber wenn Sie schlichten wollen, ist api die einzige verfügbare Option.