Wie abonniere ich ein .NET-Ereignis in einem Python-Listener mit Pythonnet?
Ich versuche, einen Ereignis-Listener dazu zu bringen, ein Tick- Ereignis (Preisereignis) aus einer FX-Handelsanwendung mit Python zu abonnieren . Die ursprüngliche Anwendung ist eine native 32-Bit-Windows-App namens MetaTrader4 . Da dies keine API hat, wurde die mtapi- Brücke in .NET entwickelt, damit andere Programmiersprachen mit ihr interagieren können. In der Anwendung sind einige Ereignisse definiert, von denen zwei: QuoteUpdateund sind QuoteUpdated.
Daher möchte ich einen Listener ( Delegat ?) Mit python.net schreiben , um dieses Ereignis zu abonnieren. Da ich jedoch nicht verstehen kann, wie der .NET-Code diese Ereignisse erzeugt und wie Pythonnet nicht ordnungsgemäß verwendet wird , konnte ich dies nicht zum Laufen bringen. Ich stoße auch immer wieder auf den Fehler:
TypeError: 'EventBinding' object is not callable
Wenn Sie dies googeln, wird nichts Nützliches zurückgegeben, abgesehen von diesem "FIXME" -Kommentar.
Hier ist mein 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
#...
Die Absicht für meinen Code sollte klar sein.
F: Wie kann ich meinen Listener beim Empfang des QuoteUpdate.NET-Ereignisses auslösen?
Als Referenz:
- Der .NET-Code in C # (aus MtApiClient.cs) sieht folgendermaßen aus:
...
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;
- Und eine kleine VB Test App sieht so aus:
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
AKTUALISIEREN
Als Referenz haben wir die folgenden relevanten DLL-Aufrufe, die durch die Attribute, Typen und angegeben werden __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)
Ähnliche Probleme:
Es gibt buchstäblich Hunderte verwandter SO-Probleme, und ich habe mir wahrscheinlich über 60% davon angesehen, aber mit fast null Erfolg bei der Anwendbarkeit auf Anwendungsfälle. Einige davon sind:
- Unterstützt Python-Klassen Ereignisse wie andere Sprachen?
- Was ist der richtige Weg, um diese Event-Handler-Registrierung von C # in VB.net zu konvertieren?
- Wie konvertiere ich einen VB-Delegaten in einen Python-Ereignishandler?
- https://ironpython.net/documentation/dotnet/dotnet.html (kann auch relevant sein)
- https://openbookproject.net/thinkcs/python/english3e/events.html
- https://code.activestate.com/recipes/410686-c-style-events-in-python/
Antworten
Nach ein paar Tagen und einigen langen Stunden fand ich eine Kombination von Fehlern. Wie immer kann eine Kombination von 2-3 einfachen Fehlern Ihr Leben für lange Zeit unglücklich machen. Aber der größte Fehler von allen, wurde zu denken täuschen , dass ein listner (aka. Delegierter ) hatte mit einem Bündel von kompliziert sein __init__und __iadd__Funktionen. Falsch! Python.NET funktioniert nur die meiste Zeit, aber die Fehler, die Sie erhalten (wenn überhaupt), sind ziemlich nutzlos, wenn Sie einen kleinen Fehler machen.
Es gibt 1,5 Probleme mit meinem Originalcode.
Es gab 2 verschiedene Angebotsfunktionen , nämlich:
QuoteUpdateDies gibt den Absender und ein Objekt mit dem Namen "MtQuoteEventArgs" in .NET zurückQuoteUpdatedDies gibt den Absender und 3 Argumente zurück .Daher müssen wir sowohl die Funktion printTick () als auch die Listener-Zeile korrigieren .
Der korrigierte Code lautet:
def printTick(source, symbol, ask, bid):
print('Tick: Symbol: {} Ask: {:.5f} Bid: {:.5f}'.format(symbol, ask, bid))
...
mtc.QuoteUpdates += printTick
Weitere Informationen zu C # -Ereignissen und -Delegierten :
- https://www.tutorialsteacher.com/csharp/csharp-event
Gemäß der von Ihnen verlinkten mtapi-Dokumentation sollte der Code wie folgt lauten:
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