ProRealTime
Zone de développement des applications API, des logiciels et utilitaires développés par les membres du forum

Re: L3 IG et la vitesse d'execution des ordres

par falex » 04 janv. 2020 22:55

A l’époque du dev de la L3 c’est le maintien de sessions https ouvertement qui m’avait permis de fortement accélérer le processus.

A l’inverse de toi, une fois la technique de « session » mise en place je ne voyais presque aucune différence entre un Petit et un gros cpu.


En fait ta remarque est juste : le temps d’ouverture d’un canal https est long surtout sur des petit cpu.

Faudrait retrouver dans le forum le avant après mais de mémoire je divisais le temps d’envoi d’un ordre par 10 au bas mot.

En Python avec la libraire requests, suffit d’appeler « session ». En .Net je n’ai aucune idée de si c’est possible et comment faire ...

Good luck Jim

Re: L3 IG et la vitesse d'execution des ordres

par Guipit » 04 janv. 2020 23:57

Pouah !!!! La vache mais vous venez de quelle planete ???? Lololol

Ya besoin de tout ça pour trader ?
Sérieux je suis largué lol

Re: L3 IG et la vitesse d'execution des ordres

par Robinhood » 05 janv. 2020 00:02

Merci je vais regarder ça !

Nb : "Jim" ?

Re: L3 IG et la vitesse d'execution des ordres

par takapoto » 05 janv. 2020 08:19

Oui, Guipit, on a besoin de tout ça (toi y compris :) )

Je ne me suis jamais posé la question de garder le client HTTP ouvert ou pas. Je l'ouvre au moment de la connexion et il reste tout le temps ouvert.

Voici le code de ma classe HTTP client :

Code : #

   public class Http
    {
        //---------------------------------------------------------------------------------------------------------------------------
        // Propriétés externes
        //---------------------------------------------------------------------------------------------------------------------------
        public string errorMessage = "";

        //---------------------------------------------------------------------------------------------------------------------------
        // Variables de travail
        //---------------------------------------------------------------------------------------------------------------------------
        public HttpClient http = null;
        private HttpClientHandler httpHandler = null;

        public HttpResponseMessage response = null;
        private JsonSerializerSettings configJson = null;

        //---------------------------------------------------------------------------------------------------------------------------
        // Constructeur
        //---------------------------------------------------------------------------------------------------------------------------
        public Http()
        {
            //Initialisation du configurateur JSON
            configJson = new JsonSerializerSettings 
            { 
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                MissingMemberHandling = MissingMemberHandling.Ignore,
                FloatFormatHandling = FloatFormatHandling.String,
                NullValueHandling = NullValueHandling.Ignore
            };
            configJson.Converters.Add(new StringEnumConverter());

            //Réponse aux requêtes HTTP
            response = new HttpResponseMessage();
        }

        //===========================================================================================================================
        // INITIALISATION DU HTTP
        //===========================================================================================================================

        //---------------------------------------------------------------------------------------------------------------------------
        // Liberation
        //---------------------------------------------------------------------------------------------------------------------------
        public void Dispose()
        {
            if (http != null)
            {
                http.Dispose();
                http = null;
            }

            if (httpHandler != null)
            {
                httpHandler.Dispose();
                httpHandler = null;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------
        // Initialisation de la requête HTTP simple
        //---------------------------------------------------------------------------------------------------------------------------
        public bool InitializeWithoutProxy(string version, string apiKey)
        {
            //On libère les objets déjà initialisés
            Dispose();

            //On créé le http
            http = new HttpClient();

            //On initialise le header
            http.DefaultRequestHeaders.Clear();
            http.DefaultRequestHeaders.Add("Version", version);
            http.DefaultRequestHeaders.Add("X-IG-API-KEY", apiKey);
            http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            //Retour ok
            return true;
        }

        //---------------------------------------------------------------------------------------------------------------------------
        // Initialisation de la requête HTTP en utilisant le proxy par défaut
        //---------------------------------------------------------------------------------------------------------------------------
        public bool InitializeWithDefaultProxy(string version, string apiKey)
        {
            //On libère les objets déjà initialisés
            Dispose();

            //On créé le http
            httpHandler = new HttpClientHandler();
            IWebProxy webProxy = WebRequest.DefaultWebProxy;
            webProxy.Credentials = CredentialCache.DefaultNetworkCredentials;
            httpHandler.Proxy = webProxy;
            http = new HttpClient(httpHandler);

            //On initialise le header
            http.DefaultRequestHeaders.Clear();
            http.DefaultRequestHeaders.Add("Version", version);
            http.DefaultRequestHeaders.Add("X-IG-API-KEY", apiKey);
            http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            //Retour ok
            return true;
        }

        //---------------------------------------------------------------------------------------------------------------------------
        // Initialisation de la requête HTTP en utilisant le proxy précisé par l'utilisateur
        //---------------------------------------------------------------------------------------------------------------------------
        public bool InitializeWithUserProxy(string version, string apiKey, string userProxyServer, string userProxyPort)
        {
            //On libère les objets déjà initialisés
            Dispose();

            //On créé le http
            string proxyUrl = userProxyServer.Trim() + ":" + userProxyPort.Trim();
            if (proxyUrl.ToLower().StartsWith("http") == false) proxyUrl = "http://" + proxyUrl;
            httpHandler = new HttpClientHandler();
            try
            {
                httpHandler.Proxy = (IWebProxy)new WebProxy(proxyUrl, false);
                httpHandler.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
                httpHandler.UseProxy = true;
            }
            catch (Exception e)
            {
                errorMessage = GetFullExceptionMessage(e);
                return false;
            }

            //On initialise le header
            http.DefaultRequestHeaders.Clear();
            http.DefaultRequestHeaders.Add("Version", version);
            http.DefaultRequestHeaders.Add("X-IG-API-KEY", apiKey);
            http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            //Retour ok
            return true;
        }

        //---------------------------------------------------------------------------------------------------------------------------
        // Récupération du message d'erreur d'une exception 
        //---------------------------------------------------------------------------------------------------------------------------
        public static string GetFullExceptionMessage(Exception e)
        {
            string message = e.Message;
            var innerException = e.InnerException;
            while (innerException != null)
            {
                message += "\r\n" + innerException.Message;
                innerException = innerException.InnerException;
            }
            return message;
        }

        //===========================================================================================================================
        // GESTION DU HEADER DU HTTP
        //===========================================================================================================================

        //---------------------------------------------------------------------------------------------------------------------------
        // Récupération d'une clé dans le HEADER
        //---------------------------------------------------------------------------------------------------------------------------
        public string GetHeaderKey(string key)
        {
            foreach (var header in this.response.Headers)
            {
                if (header.Key.Equals(key)) return header.Value.First();
            }
            return "";
        }

        //---------------------------------------------------------------------------------------------------------------------------
        // Modification d'une clé dans le HEADER
        //---------------------------------------------------------------------------------------------------------------------------
        public void SetHeaderKey(string key, string value)
        {
            this.http.DefaultRequestHeaders.Remove(key);
            this.http.DefaultRequestHeaders.Add(key, value);
        }

        //-----------------------------------------------------------------------------------------
        // Modification de la version dans le header
        //-----------------------------------------------------------------------------------------
        public void SetVersion(string version)
        {
            this.http.DefaultRequestHeaders.Remove("Version");
            this.http.DefaultRequestHeaders.Add("Version", version);
        }

        //-----------------------------------------------------------------------------------------
        // Modification de la method dans le header
        //-----------------------------------------------------------------------------------------
        public void SetMethod(string method)
        {
            this.http.DefaultRequestHeaders.Remove("_method");
            if (method != "")
            {
                this.http.DefaultRequestHeaders.Add("_method", method);
            }
        }

        //===========================================================================================================================
        // REQUETES
        //===========================================================================================================================

        //---------------------------------------------------------------------------------------------------------------------------
        // POST
        // Renvoie la réponse sous forme de chaine ou bien null si erreur
        // Si erreur, errorMessage contient le message d'erreur
        //---------------------------------------------------------------------------------------------------------------------------
        public async Task<string> Post(string url, object data = null)
        {
            StringContent body;
            if (data != null)
            {
                body = new StringContent(JsonConvert.SerializeObject(data));
                body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            }
            else
            {
                body = null;
            }

            try
            {
                var postTask = http.PostAsync(url, body);
                response = postTask.Result;
                response.EnsureSuccessStatusCode();
            }
            catch (Exception e)
            {
                errorMessage = url + "\r\n" + GetFullExceptionMessage(e);
                return null;
            }

            // Récupération de la réponse
            try
            {
                string content = await GetResponse();
                if (content == null)
                {
                    errorMessage = url + "\r\n" + errorMessage;
                }
                return content;
            }
            catch (Exception e)
            {
                errorMessage = url + "\r\n" + GetFullExceptionMessage(e);
                return null;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------
        // GET
        // Renvoie la réponse sous forme de chaine ou bien null si erreur
        // Si erreur, errorMessage contient le message d'erreur
        //---------------------------------------------------------------------------------------------------------------------------
        public async Task<string> Get(string url)
        {
            try
            {
                var getTask = http.GetAsync(url);
                response = getTask.Result;
                response.EnsureSuccessStatusCode();
            }
            catch (Exception e)
            {
                errorMessage = url + "\r\n" + GetFullExceptionMessage(e);
                return null;
            }

            // Récupération de la réponse
            try
            {
                string content = await GetResponse();
                if (content == null)
                {
                    errorMessage = url + "\r\n" + errorMessage;
                }
                return content;
            }
            catch (Exception e)
            {
                errorMessage = url + "\r\n" + GetFullExceptionMessage(e);
                return null;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------
        // PUT
        // Renvoie la réponse sous forme de chaine ou bien null si erreur
        // Si erreur, errorMessage contient le message d'erreur
        //---------------------------------------------------------------------------------------------------------------------------
        public async Task<string> Put(string url, object data = null)
        {
            StringContent body;
            if (data != null)
            {
                body = new StringContent(JsonConvert.SerializeObject(data));
                body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            }
            else
            {
                body = null;
            }

            try
            {
                var putTask = http.PutAsync(url, body);
                response = putTask.Result;
                response.EnsureSuccessStatusCode();
            }
            catch (Exception e)
            {
                errorMessage = url + "\r\n" + GetFullExceptionMessage(e);
                return null;
            }

            // Récupération de la réponse
            try
            {
                string content = await GetResponse();
                if (content == null)
                {
                    errorMessage = url + "\r\n" + errorMessage;
                }
                return content;
            }
            catch (Exception e)
            {
                errorMessage = url + "\r\n" + GetFullExceptionMessage(e);
                return null;
            }
        }

        //-----------------------------------------------------------------------------------------
        // Récupération de la réponse renvoyée par une requête
        // Renvoie le contenu de la réponse ou null si erreur
        //-----------------------------------------------------------------------------------------
        public async Task<string> GetResponse()
        {
            //Si on n'a pas obtenu de réponse
            if (response == null)
            {
                errorMessage = "No response";
                return null;
            }

            //Si la réponse est vide
            if (response.Content == null)
            {
                errorMessage = "Empty response";
                return null;
            }

            //Si le code retour n'est pas ok
            if (response.IsSuccessStatusCode == false)
            {
                errorMessage = response.StatusCode.ToString();
                return null;
            }

            //Récupération des informations renvoyées par la requête
            try
            {
                string content = await response.Content.ReadAsStringAsync();

                //Si rien n'a été renvoyé
                if (content == null)
                {
                    errorMessage = "Reponse vide";
                    return null;
                }

                //Ok
                return content;
            }
            catch (Exception e)
            {
                errorMessage = GetFullExceptionMessage(e);
                return null;
            }

        }

        //-----------------------------------------------------------------------------------------
        // Décodage d'une donnée JSON en un objet 
        //-----------------------------------------------------------------------------------------
        public T DecodeJSON<T>(string content)
        {
            try
            {
                var result = JsonConvert.DeserializeObject<T>(content, configJson);
                return result;
            }
            catch (Exception e)
            {
                errorMessage = "Erreur JSON : " + e.Message.ToString();
                return default(T);
            }
        }


    }
Et la fonction qui l'ouvre une fois pour toute :

Code : #

//-----------------------------------------------------------------------------------------
        // Connexions à IG avec un POST V2
        //-----------------------------------------------------------------------------------------
        private async Task xConnect()
        {
            //Initialisation du body permettant la connexion
            ConnexionBody body = new ConnexionBody();
            body.identifier = this.main.connexionElements.user;
            body.password = this.main.connexionElements.password;

            //Initialisation du résultat
            this.main.connexionElements.result = null;

            //Tentative 1 : connexion directe sans proxy => si ok, on sort
            if (this.main.connexionElements.http.InitializeWithoutProxy("2", this.main.connexionElements.apiKey) == true)
            {
                this.main.connexionElements.result = await this.main.connexionElements.http.Post(this.main.url, body);
                if (this.main.connexionElements.result != null)
                {
                    this.main.connexionElements.proxyMethod = ProxyMethod.WithoutProxy;
                    return;
                }
            }

            //Tentative 2 : connexion avec le proxy par défaut => si ok, on sort
            if (this.main.connexionElements.http.InitializeWithDefaultProxy("2", this.main.connexionElements.apiKey) == true)
            {
                this.main.connexionElements.result = await this.main.connexionElements.http.Post(this.main.url, body);
                if (this.main.connexionElements.result != null) 
                {
                    this.main.connexionElements.proxyMethod = ProxyMethod.DefaultProxy;
                    return;
                }
            }

            //Tentative 3 : connexion avec le proxy utilisateur => si ok, on sort
            if (this.main.connexionElements.useProxy == true)
            {
                if (this.main.connexionElements.http.InitializeWithUserProxy("2", this.main.connexionElements.apiKey, this.main.connexionElements.proxyServer, this.main.connexionElements.proxyPort) == true)
                {
                    this.main.connexionElements.result = await this.main.connexionElements.http.Post(this.main.url, body);
                    if (this.main.connexionElements.result != null)
                    {
                        this.main.connexionElements.proxyMethod = ProxyMethod.UserProxy;
                        return;
                    }
                }
            }
        }

Re: L3 IG et la vitesse d'execution des ordres

par Robinhood » 05 janv. 2020 14:31

Merci Taka je vais regarder.

D'après mes nouvelles estimations, le délai d’exécution est très volatile.

J'ai ainsi estimé qu'ig était parfois capable d’exécuter un ordre en moins de 20ms. Mais la distribution des délais m'inquiète fortement. Certains délais sont estimés à plus de 500ms.

Et encore, ces tests je les ai fait aujourd'hui, en demo, sur le Bitcoin. Vu le spread, je ne crois pas que le moteur d'exe soit saturé de demandes. C'est plutôt inquiétant pour la suite.

Bon de toute façon le seul vrai juge de paix sera les exe en réel. Je vais être vite fixé.

Par ailleurs, pour revenir aux requêtes Post passées par le client http, on avait évoqué le fait que la vitesse d’exécution de ces requêtes étaient dépendantes en partie de la vitesse du cpu. Partant du principe que cette vitesse est globalement stable dans le temps, j'en déduis que la quasi totalité de la part "volatile" du délai est l’œuvre des serveurs d'ig. Reste à savoir dans quelle mesure le moteur d'exe en est responsable. Là dessus impossible de savoir. Ce dernier élément à son importance dans la mesure où un ordre envoyé via REST ou FIX est en principe traité pareil. Donc toutes choses égales par ailleurs, le passage du REST au FIX n'aurait que peu d'impact puisque la vol du délai serait du quasi exclusivement côté serveur .

Dans l'absolu cela a du sens car il n'y a aucune raison que la vitesse d'exe soit stable dans le temps. Un nombre inconnu de variables ayant un impact +/- important comme l'état de l'offre/demande des clients d'ig, la capacité d'ig à se hedger en externe, etc...

On est là vraiment au cœur du bouzin :-)

Re: L3 IG et la vitesse d'execution des ordres

par Robinhood » 05 janv. 2020 14:47

@Taka : tout se joue là => "response = postTask.Result;"

Response attends le retour du serveur d'ig.

En partant du principe que le délai est bien fortement dépendant du moteur d'exe, mon objectif est de minimiser le délai d'envoi d'une requête POST, en intégrant toutes couches composant le trajet :

- Envoi de la requête (traitement cpu) => à optimiser...=> utiliser une autre bibliothèque??
- Voyage de la requête vers le serveur d'ig => ok sous contrôle (cf sujet VPS)

Re: L3 IG et la vitesse d'execution des ordres

par falex » 05 janv. 2020 19:02

Oui et non Robinhood sur les délais d’exécution côté ig.

Oui de temps en temps les délais d’exécution explose mais c’est surtout les moments où il y a de la volatilité et aussi si ton ordre passe d’un mode full auto à mode exécution par un opérateur. Dans ces moments la table (ou desk) d’exécution d’ig confirme ou rejette les ordres.
Faut pas oublier que l’a Contrepartie c’est ig donc ils s’assurent de pouvoir te servir.

Après je répète, si à chaque envoi d’ordre tu rouvres un canal de communication https avec les serveurs d’ig tu perds un temps de fou et ça c’est de ton côté à régler ce problème.

En résumé et en moyenne fallait compter un temps d’exécution global d’environ 100ms. Moyenne qu’avec des valeurs plus hautes et basses dans la série bien évidemment.

Re: L3 IG et la vitesse d'execution des ordres

par Robinhood » 05 janv. 2020 22:28

Peu importe la raison côté délai d'exe. Pour ma part si cette donnée n'est pas "contenue" ou à défaut inférieure en moyenne à un certain seuil, ça ne me convient pas.

Bien évidemment j'ai une seule session d'ouverte sur le client http mais cela ne change "quasi" rien. Le problème c'est bien l'envoi de la requête/attente retour serveur (malheureusement ces 2 éléments je ne peux pas les dissocier, en tout cas pour le moment). Je suis en train de tester d'autres librairies pour voir si je peux un peu gagner de côté là. Mais mon intuition me dit que la messe est dite.

100ms c'est grossomodo ce que j'ai, (un peu moins pour la médiane qui est plus révélatrice) mais ça reste très élevé, surtout comparé à ce qu'avance certains concurrents.

Maintenant le seul juge de paix ça va être les opés en réel. Je vais mettre ça en place rapidement et en fonction, je traiterais avec ig ou non.

Sujets similaires
Vitesse execution des ordres suivant l'indice
par Guillaume de Russie » 04 janv. 2019 18:36 (10 Réponses)
améliorer son trading par la vitesse d'exécution
Fichier(s) joint(s) par Edd » 09 sept. 2015 11:33 (34 Réponses)
Problème lenteur exécution des ordres
par Benoist Rousseau » 23 nov. 2014 13:54 (21 Réponses)
Exécution d ordres sur plusieurs comptes
par Benoist Rousseau » 17 mai 2016 13:49 (3 Réponses)
exécution des ordres hors zone de cotation sur ProRealTime
Fichier(s) joint(s) par Vinny » 18 sept. 2019 14:55 (7 Réponses)
Exécution des ordres stop sur PRT démo cfds à risque limité
Fichier(s) joint(s) par ground77600 » 29 mars 2022 12:17 (55 Réponses)
Vitesse du site
Fichier(s) joint(s) par FD707 » 30 sept. 2013 10:04 (8 Réponses)
Internet -> Débit et Vitesse différence ?
Fichier(s) joint(s) par DarkPoule » 18 janv. 2014 15:22 (65 Réponses)
Vitesse du forum
Fichier(s) joint(s) par Benoist Rousseau » 03 juin 2015 21:24 (9 Réponses)
Améliorer la vitesse de PRT
par libertarian » 01 août 2016 20:12 (2 Réponses)