Wie abonniere ich ein .NET-Ereignis in einem Python-Listener mit Pythonnet?

Nov 15 2020

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

3 not2qubit Nov 23 2020 at 21:56

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ück

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

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