Как подписаться на событие .NET в прослушивателе python с помощью pythonnet?

Nov 15 2020

Я пытаюсь настроить прослушиватель событий для подписки на событие тика (цены) из торгового приложения FX, используя Python. Исходное приложение - это собственное 32-разрядное приложение для Windows под названием MetaTrader4 . У него нет API, поэтому мост mtapi был разработан в .NET, чтобы другие языки программирования могли взаимодействовать с ним. В приложении определены некоторые события, два из которых: QuoteUpdateи QuoteUpdated.

Итак, я хотел бы написать слушателя ( делегата ?), Используя python.net, чтобы подписаться на это событие. Но поскольку я не могу понять, как код .NET создает эти события, и как правильно использовать pythonnet , мне не удалось заставить это работать. Я тоже постоянно сталкиваюсь с ошибкой:

TypeError: 'EventBinding' object is not callable

Поиск в Google не вернул ничего полезного, кроме этого комментария "FIXME".

Вот мой код:

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

#...

Намерение моего кода должно быть ясным.

Q: Как я могу заставить мой слушатель активироваться при получении QuoteUpdateсобытия .NET?


Для справки:

  • Код .NET на C # (из MtApiClient.cs выглядит так:
...
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; 
  • А небольшое приложение VB Test выглядит так:
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

ОБНОВИТЬ

Для справки, у нас есть следующие соответствующие вызовы DLL, заданные атрибутами, типами и __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)


Похожие вопросы:

Существует буквально сотня связанных проблем SO, и я, вероятно, рассмотрел более 60% из них, но практически безуспешно с точки зрения применимости к конкретному варианту использования. Некоторые из них:

  • Поддерживают ли классы Python события, как и другие языки?
  • Как правильно преобразовать регистрацию обработчика событий с C # на VB.net?
  • Как преобразовать делегата VB в обработчик событий Python?
  • https://ironpython.net/documentation/dotnet/dotnet.html (также может быть актуально)
  • https://openbookproject.net/thinkcs/python/english3e/events.html
  • https://code.activestate.com/recipes/410686-c-style-events-in-python/

Ответы

3 not2qubit Nov 23 2020 at 21:56

Через несколько дней и несколько долгих часов я обнаружил комбинацию ошибок. Как всегда, сочетание 2-3 простых ошибок может надолго сделать вашу жизнь несчастной. Но самая большая ошибка все, была дурачит думать , что у слушателя (иначе. Делегат ) должен был быть сложным с кучей __init__и __iadd__функциями. Неправильно! Python.NET просто работает большую часть времени, но ошибки, которые вы получаете (если они вообще есть), бесполезны, если вы сделаете небольшую ошибку.

В моем исходном коде есть 1.5 проблемы.

  • Было 2 разных функции Quote , а именно:

  • QuoteUpdateкоторый возвращает отправителя и объект с именем «MtQuoteEventArgs» в .NET.

  • QuoteUpdatedкоторый возвращает отправителя и 3 аргумента .

  • Поэтому нам нужно исправить как функцию printTick (), так и строку прослушивателя.

Исправленный код:

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

...

mtc.QuoteUpdates += printTick

Для получения дополнительной информации о событиях и делегатах C # :

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

Согласно документации mtapi, которую вы связали, код должен быть:

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