Comment s'abonner à un événement .NET dans un écouteur python en utilisant pythonnet?

Nov 15 2020

J'essaie de créer un écouteur d'événements pour m'abonner à un événement tick (prix) à partir d'une application de trading FX, en utilisant Python. L'application d'origine est une application Windows 32 bits native appelée MetaTrader4 . Cela n'a pas d'API, donc le pont mtapi a été conçu dans .NET pour permettre à d'autres langages de programmation d'interagir avec lui. L'application a des événements définis, dont deux sont: QuoteUpdateet QuoteUpdated.

Je voudrais donc écrire un auditeur ( délégué ?) Utilisant python.net pour m'abonner à cet événement. Mais comme je ne suis pas en mesure de comprendre comment le code .NET produit ces événements, ni comment utiliser correctement pythonnet , je n'ai pas été en mesure de faire fonctionner cela. Je continue également à rencontrer l'erreur:

TypeError: 'EventBinding' object is not callable

Googler cela ne renvoie rien d'utile, à part ce commentaire "FIXME".

Voici mon code:

import os, sys, clr
sys.path.append(r"C:\Program Files\MtApi")
asm = clr.AddReference('MtApi')
import MtApi as mt

res = 0

def printTick(symbol, ask, bid):
    print('Tick: Symbol: {}  Ask: {:.5f}  Bid: {:.5f}'.format(symbol, ask, bid))

# Setup .NET API bridge connection
mtc = mt.MtApiClient()
res = mtc.BeginConnect('127.0.0.1', 8222);

#--------------------------------------
# Register and use the listener
#--------------------------------------
# This does NOT work!
mtc.QuoteUpdate += printTick

#...

L'intention de mon code doit être claire.

Q: Comment puis-je déclencher mon écouteur lors de la réception de l' QuoteUpdateévénement .NET?


Pour référence:

  • Le code .NET en C # (de MtApiClient.cs ressemble à ceci:
...
private void _client_QuoteUpdated(MTApiService.MtQuote quote) { 
    if (quote != null) { 
        QuoteUpdate?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote))); 
        QuoteUpdated?.Invoke(this, quote.Instrument, quote.Bid, quote.Ask); 
    } 
} 
...
public event MtApiQuoteHandler QuoteUpdated; 
public event EventHandler<MtQuoteEventArgs> QuoteUpdate; 
public event EventHandler<MtQuoteEventArgs> QuoteAdded; 
public event EventHandler<MtQuoteEventArgs> QuoteRemoved; 
  • Et une petite application de test VB ressemble à ceci:
Imports MtApi
Public Class Form1
    Private apiClient As MtApiClient
    Public Sub New()
        InitializeComponent()
        apiClient = New MtApiClient
        AddHandler apiClient.QuoteUpdated, AddressOf QuoteUpdatedHandler
    End Sub

    Sub QuoteUpdatedHandler(sender As Object, symbol As String, bid As Double, ask As Double)
        Dim quoteSrt As String
        quoteSrt = symbol + ": Bid = " + bid.ToString() + "; Ask = " + ask.ToString()
        ListBoxQuotesUpdate.Invoke(Sub()
                                       ListBoxQuotesUpdate.Items.Add(quoteSrt)
                                   End Sub)
        Console.WriteLine(quoteSrt)
    End Sub
    Private Sub btnConnect_Click(sender As System.Object, e As System.EventArgs) Handles btnConnect.Click
        apiClient.BeginConnect(8222)
    End Sub
    Private Sub btnDisconnect_Click(sender As System.Object, e As System.EventArgs) Handles btnDisconnect.Click
        apiClient.BeginDisconnect()
    End Sub
End Class

METTRE À JOUR

Pour référence, nous avons les appels DLL pertinents suivants, donnés par les attributs, les types et __doc__:

attr: QuoteAdded             type: <class 'CLR.EventBinding'>    doc: <n/a>
attr: QuoteRemoved           type: <class 'CLR.EventBinding'>    doc: <n/a>
attr: QuoteUpdate            type: <class 'CLR.EventBinding'>    doc: <n/a>
attr: QuoteUpdated           type: <class 'CLR.EventBinding'>    doc: <n/a>

attr: add_QuoteAdded         type: <class 'CLR.MethodBinding'>   doc: Void add_QuoteAdded(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: add_QuoteRemoved       type: <class 'CLR.MethodBinding'>   doc: Void add_QuoteRemoved(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: add_QuoteUpdate        type: <class 'CLR.MethodBinding'>   doc: Void add_QuoteUpdate(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: add_QuoteUpdated       type: <class 'CLR.MethodBinding'>   doc: Void add_QuoteUpdated(MtApi.MtApiQuoteHandler)

attr: remove_QuoteAdded      type: <class 'CLR.MethodBinding'>   doc: Void remove_QuoteAdded(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: remove_QuoteRemoved    type: <class 'CLR.MethodBinding'>   doc: Void remove_QuoteRemoved(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: remove_QuoteUpdate     type: <class 'CLR.MethodBinding'>   doc: Void remove_QuoteUpdate(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: remove_QuoteUpdated    type: <class 'CLR.MethodBinding'>   doc: Void remove_QuoteUpdated(MtApi.MtApiQuoteHandler)


Problèmes similaires:

Il y a littéralement des centaines de problèmes liés au SO, et j'en ai probablement examiné plus de 60%, mais avec un succès presque nul quant à l'applicabilité par cas d'utilisation. Certains d'entre eux sont:

  • Les classes Python prennent-elles en charge les événements comme les autres langages?
  • Quelle est la bonne façon de convertir cette inscription de gestionnaire d'événements de C # à VB.net?
  • Comment convertir un délégué VB en un gestionnaire d'événements python?
  • https://ironpython.net/documentation/dotnet/dotnet.html (peut également être pertinent)
  • https://openbookproject.net/thinkcs/python/english3e/events.html
  • https://code.activestate.com/recipes/410686-c-style-events-in-python/

Réponses

3 not2qubit Nov 23 2020 at 21:56

Après quelques jours et de longues heures, j'ai trouvé une combinaison d'erreur (s). Comme toujours, une combinaison de 2 ou 3 erreurs simples peut vous rendre la vie misérable pendant longtemps. Mais la plus grande erreur de tous, a été dupé à penser qu'un listner (alias. Délégué ) a dû être compliqué avec un tas de __init__et __iadd__fonctions. Faux! Python.NET fonctionne la plupart du temps, mais les erreurs que vous obtenez (le cas échéant) sont assez inutiles si vous faites une petite erreur.

Il y a 1.5 problèmes avec mon code d'origine.

  • Il y avait 2 fonctions de devis différentes , à savoir:

  • QuoteUpdatequi renvoie l' expéditeur et un objet nommé "MtQuoteEventArgs" dans .NET

  • QuoteUpdatedqui renvoie l' expéditeur et 3 arguments .

  • Par conséquent, nous devons corriger à la fois la fonction printTick () et la ligne d'écoute.

Le code corrigé est:

def printTick(source, symbol, ask, bid):
    print('Tick: Symbol: {}  Ask: {:.5f}  Bid: {:.5f}'.format(symbol, ask, bid))

...

mtc.QuoteUpdates += printTick

Pour plus d'informations sur les événements et les délégués C # :

  • https://www.tutorialsteacher.com/csharp/csharp-event
1 LOST Nov 20 2020 at 05:24

Selon la documentation mtapi que vous avez liée, le code doit être:

def printTick(sender, args):
  print(str(args.Quote.Instrument))


mtc = mt.MtApiClient()
res = mtc.BeginConnect('127.0.0.1', 8222)

mtc.QuoteUpdate += printTick  # Subscribe & handle new repeated events

rA  = mtc.SymbolInfoTick(SYM) # Make a request to get a one-time tick data

# Need wait loop here