Come iscriversi a un evento .NET in un listener python usando pythonnet?
Sto cercando di fare in modo che un ascoltatore di eventi si iscriva a un evento tick (prezzo) da un'applicazione di trading FX, utilizzando Python. L'applicazione originale è un'app Windows nativa a 32 bit chiamata MetaTrader4 . Questo non ha alcuna API, quindi il bridge mtapi è stato progettato in .NET per consentire ad altri linguaggi di programmazione di interagire con esso. L'applicazione ha alcuni eventi definiti, due dei quali sono: QuoteUpdatee QuoteUpdated.
Quindi vorrei scrivere un ascoltatore ( delegato ?) Usando python.net per iscriversi a questo evento. Ma dal momento che non sono in grado di capire come il codice .NET stia producendo questi eventi, e nemmeno come usare correttamente pythonnet , non sono stato in grado di farlo funzionare. Continuo anche a incorrere nell'errore:
TypeError: 'EventBinding' object is not callable
Googling questo non restituisce nulla di utile, a parte questo commento "FIXME".
Ecco il mio codice:
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'intenzione per il mio codice dovrebbe essere chiara.
D: Come posso fare in modo che il mio listener si attivi quando ricevo l' QuoteUpdateevento .NET?
Per riferimento:
- Il codice .NET in C # (da MtApiClient.cs assomiglia a questo:
...
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;
- E una piccola app VB Test assomiglia a questa:
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
AGGIORNARE
Per riferimento, abbiamo le seguenti chiamate DLL rilevanti, fornite dagli attributi, tipi e __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)
Problemi simili:
Ci sono letteralmente centinaia di problemi SO correlati e probabilmente ne ho esaminato oltre il 60%, ma con quasi zero successo nell'applicabilità per caso d'uso. Alcuni dei quali sono:
- Le classi Python supportano eventi come altri linguaggi?
- Qual è il modo corretto per convertire questa registrazione del gestore eventi da C # a VB.net?
- Come si converte un delegato VB in un gestore di eventi Python?
- https://ironpython.net/documentation/dotnet/dotnet.html (può anche essere rilevante)
- https://openbookproject.net/thinkcs/python/english3e/events.html
- https://code.activestate.com/recipes/410686-c-style-events-in-python/
Risposte
Dopo alcuni giorni e alcune lunghe ore ho trovato una combinazione di errori. Come sempre, una combinazione di 2-3 semplici errori può rendere la tua vita infelice per molto tempo. Ma l'errore più grande di tutte, era stato ingannato a pensare che un listner (aka. Delegato ) ha dovuto essere complicato con un gruppo di __init__e __iadd__funzioni. Sbagliato! Python.NET funziona la maggior parte delle volte ma gli errori che ottieni (se ce ne sono) sono abbastanza inutili se fai qualche piccolo errore.
Ci sono 1.5 problemi con il mio codice originale.
C'erano 2 diverse funzioni di citazione , vale a dire:
QuoteUpdateche restituisce il mittente e un oggetto denominato "MtQuoteEventArgs" in .NETQuoteUpdatedche restituisce mittente e 3 argomenti .Pertanto dobbiamo correggere sia la funzione printTick () che la riga del listener.
Il codice corretto è:
def printTick(source, symbol, ask, bid):
print('Tick: Symbol: {} Ask: {:.5f} Bid: {:.5f}'.format(symbol, ask, bid))
...
mtc.QuoteUpdates += printTick
Per ulteriori informazioni su eventi e delegati C # :
- https://www.tutorialsteacher.com/csharp/csharp-event
Secondo la documentazione mtapi che hai collegato, il codice dovrebbe essere:
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