Come iscriversi a un evento .NET in un listener python usando pythonnet?

Nov 15 2020

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

3 not2qubit Nov 23 2020 at 21:56

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 .NET

  • QuoteUpdatedche 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
1 LOST Nov 20 2020 at 05:24

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