C#でWebSocket経由で価格ストリームを取得する。

 

こんにちは、親愛なる皆様。さまざまなソース(LMAX取引所を含む)から見積もりを受け取ることに興味がある。ブローカーによって、流動性プロバイダーであるECNが異なるため、ECN自身から直接見積もりを取る方がよいでしょう。しかし、そこには限界があります。ほとんどのプロバイダーは、私たちが直接接続しない限り、見積もりを共有してくれません。でも、「マーケットデプス」をはじめ、見積もりを取れるところはいくつかあります。例えば、LMAXは https://s3-eu-west-1.amazonaws.com/lmax-widget3/website-widget-vwap.html のようなウィジェットで流動性を配信している(週末は相場がないため非アクティブ、平日はロゴが消えてマーケットプレイスが表示される)。また、マーケットデプスで相場を配信しているECNもいくつかあります。

つまり、WebRequest()を介して直接取得することはできないので、WebSocketイベントを購読する必要があります。そして、ここで私は暗い森にいる、私は(ほぼ)C#に精通していないため、さらにWeb技術 :) Webソケットを介してデータを取得する2つの例を見つけましたが、それらのいずれもが適切に動作しません。

例:クライアントを作成し、ソケットに接続する場合、以下のようになります。ウィジェットのソースを調べて、何かを理解しようとしたところ、ウィジェットに見積もりを送ることはできますが、受け取ることはできません :) つまり、見積もりを受け取るために、どのリクエストをサーバーに送るべきかが明確ではありません。引用文そのものを送れば、サーバーは積極的に返信し、ウィジェットに表示されます。スクリーンショットでは、サーバーからの応答を見ることができます。ブラウザでソケットのスレッドを調べると、接続に関する情報も表示されます。そして、マーケットがアクティブになると、送信された相場がダウンロードされ、ウィジェットに表示されます :)別の例として、ソケット経由の接続もありますが、今のところうまくいきません。

ウィジェットから見積もりを受け取る方法をご存知の方がいらっしゃいましたら(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に送られていたのです。

実は、これは面白い質問なんです。

 
Yuriy Asaulenko:

全部はわからないけど。しかし、月曜日を待って、活動を引用してみましょう。見てみようと思います。その他、直接的な引用元も興味深い。ちなみに、遅延はないのでしょうか?

5~6年ほど前、VBAのエクセルで、どこかのサイトからリアルタイムの情報(相場ではない)を取得していた。今となっては、どのような経緯でそうなったのか、詳しくは覚えていません。情報ページと連動するスクリプトで、ブラウザーのウィンドウ経由で接続したのです。そして、そのスクリプトはすでにExcelに送り込まれていた。

実は、これは面白い質問なんです。

そう、月曜日を待たないといけないのです。そこで、提案されたクエリによってウィジェットのゼロに何を詰め込むことができるかがすぐにわかるでしょう :)mt4の相場と比べると、遅れがなく、すぐに来るようです。

ウィジェットだけでなく、ウェブ端末からの引き込みも可能なので、興味深い質問ですね...。

 
Maxim Dmitrievsky:

ウィジェットだけでなく、Web端末からも引っ張ってこられるので、質問が面白いですね...。

可能であれば、他のリンクも送ってください。すべてのページで情報を引き出せるわけではありません。Connect - 接続はするが、データのフォーマットは例えばFlashで書き込む。そして段落。
 
LMAXはAPIを持っているので、ウィジェットは必要ありません。
 
Dmitriy Skub:
LMAXはAPIを持っているので、ウィジェットは必要ありません。
ということで、すべて生地のためです。業者からの市場デートは何でもドーでもいいし、10kからのインボイスでもいい。
 
Yuriy Asaulenko:
可能であれば、他のリンクも送ってください。すべてのページで情報を引き出せるわけではありません。接続 - 接続はするが、データの形式は例えばFlashに保存される。そして段落。
やりましたね。さて、ここで確認ですが、基本的に今回はソケットが見えている、スレッドが見えている...という状態です。もちろん、私は専門家ではありませんが、目で見えるものはすべてソフトウェアで得ることができると信じています :)フラッシュもどこかからデータを引っ張ってくる...。
 
Dmitriy Skub:
LMAXはAPIを持っているので、ウィジェットは必要ありません。
APIを利用するためにはアカウントが必要なのでは?
 
Maxim Dmitrievsky:
投げました。さて、ここで確認ですが、原則的にソケットは見えるのですが、この場合、スレッドは見えるのです...。正しく解析する方法を質問します。もちろん私は専門家ではありませんが、目に見えるものはすべてソフトウェアで得ることができるという意見です :)フラッシュもどこかからデータを引っ張ってくる...。
は、既知の交換プロトコルで得られるかもしれない。Flashでは得られないというか、暗号の ようなものですね。:)
 

接続例その2。ルールによると、ソケットに接続するためには、httpからwssに切り替えるためのヘッダを送信する必要があり、その後、すでにtcpでソケットと通信している...とあります。WebSocket 経由の方がはるかに簡単なのに、なぜこのような接続がSocket経由で行われるのか理解できない。このエラーは不正確なリクエスト、つまりハンドシェイクが行われなかったということです。もしかしたら、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が唯一の選択肢となります。
理由: