Como se inscrever em um evento .NET em um ouvinte python usando pythonnet?

Nov 15 2020

Estou tentando fazer um ouvinte de evento se inscrever em um evento tick (preço) de um aplicativo de negociação FX, usando Python. O aplicativo original é um aplicativo nativo do Windows de 32 bits chamado MetaTrader4 . Ele não tem API, então a ponte mtapi foi projetada em .NET para permitir que outras linguagens de programação interajam com ela. A aplicação possui alguns eventos definidos, dois dos quais são: QuoteUpdatee QuoteUpdated.

Então, eu gostaria de escrever um ouvinte ( delegado ?) Usando python.net para se inscrever neste evento. Mas, como não consigo entender como o código .NET está produzindo esses eventos e nem como usar o pythonnet corretamente , não consegui fazer isso funcionar. Também continuo encontrando o erro:

TypeError: 'EventBinding' object is not callable

Googling isto não retorna nada útil, exceto este comentário "FIXME".

Este é o meu código:

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

#...

A intenção do meu código deve ser clara.

P: Como posso fazer meu listener disparar ao receber o QuoteUpdateevento .NET?


Para referência:

  • O código .NET em C # (de MtApiClient.cs é semelhante a este:
...
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 um pequeno aplicativo de teste VB se parece com isto:
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

ATUALIZAR

Para referência, temos as seguintes chamadas DLL relevantes, fornecidas pelos atributos, tipos 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)


Problemas semelhantes:

Existem literalmente centenas de problemas relacionados ao SO, e provavelmente examinei mais de 60% deles, mas com quase nenhum sucesso de aplicabilidade por caso de uso. Alguns dos quais são:

  • As classes Python oferecem suporte a eventos como outras linguagens?
  • Qual é a maneira correta de converter esse registro de manipulador de eventos de C # para VB.net?
  • Como faço para converter um delegado VB em um manipulador de eventos Python?
  • https://ironpython.net/documentation/dotnet/dotnet.html (também pode ser relevante)
  • https://openbookproject.net/thinkcs/python/english3e/events.html
  • https://code.activestate.com/recipes/410686-c-style-events-in-python/

Respostas

3 not2qubit Nov 23 2020 at 21:56

Depois de alguns dias e algumas horas longas, encontrei uma combinação de erro (s). Como sempre, uma combinação de 2 ou 3 erros simples pode tornar sua vida miserável por muito tempo. Mas o maior erro de todos foi ser enganado ao pensar que um listner (também conhecido como delegado ) tinha que ser complicado com um monte de funções __init__e __iadd__. Errado! O Python.NET funciona na maioria das vezes, mas os erros que você obtém (se houver) são inúteis se você cometer algum pequeno erro.

Existem 1.5 problemas com meu código original.

  • Havia 2 funções de Citação diferentes , a saber:

  • QuoteUpdateque retorna o remetente e um objeto chamado "MtQuoteEventArgs" no .NET

  • QuoteUpdatedque retorna remetente e 3 argumentos .

  • Portanto, precisamos corrigir a função printTick () e a linha do ouvinte.

O código corrigido é:

def printTick(source, symbol, ask, bid):
    print('Tick: Symbol: {}  Ask: {:.5f}  Bid: {:.5f}'.format(symbol, ask, bid))

...

mtc.QuoteUpdates += printTick

Para obter informações adicionais sobre eventos e delegados C # :

  • https://www.tutorialsteacher.com/csharp/csharp-event
1 LOST Nov 20 2020 at 05:24

De acordo com a documentação mtapi que você vinculou, o código deve ser:

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