Pythonnetを使用してPythonリスナーで.NETイベントをサブスクライブする方法は?

Nov 15 2020

Pythonを使用して、FX取引アプリケーションからティック(価格)イベントをサブスクライブするイベントリスナーを作成しようとしています。元のアプリケーションは、MetaTrader4と呼ばれるネイティブの32ビットWindowsアプリです。これにはAPIがないため、mtapiブリッジは他のプログラミング言語が相互作用できるように.NETで設計されています。アプリケーションにはいくつかのイベントが定義されており、そのうちの2つは:QuoteUpdateQuoteUpdatedです。

そこで、python.netを使用してこのイベントをサブスクライブするリスナー(デリゲート?)を作成したいと思います。しかし、.NETコードがこれらのイベントをどのように生成しているか、またpythonnetを適切に使用する方法も理解できないため、これを機能させることができませんでした。私もエラーに遭遇し続けます:

TypeError: 'EventBinding' object is not callable

これをグーグルで検索しても、この「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イベントを受信したときにリスナーを起動させるにはどうすればよいですか?


参考のため:

  • C#の.NETコード(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テストアプリは次のようになります。
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

更新

参考までに、属性、タイプ、および__doc__:によって指定された次の関連するDLL呼び出しがあります

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の単純な間違いの組み合わせは、あなたの人生を長い間悲惨にする可能性があります。しかし、すべての最大の過ちは、LISTNER(別名。と思うようにだまされていたデリゲートが)の束を合併しなければならなかった__init____iadd__機能。違う!Python.NETはほとんどの場合機能しますが、小さな間違いをした場合に発生するエラー(あるとしても)はほとんど役に立ちません。

私の元のコードには1.5の問題があります。

  • 2つの異なる見積もり関数がありました。

  • QuoteUpdate.NETで送信者と「MtQuoteEventArgs」という名前のオブジェクトを返します

  • 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