Comment s'abonner à un événement .NET dans un écouteur python en utilisant pythonnet?
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
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 .NETQuoteUpdatedqui 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
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