问题
I'm trying to make an event listener to subscribe to a tick (price) event from a FX trading application, using Python. The original application is a native 32-bit Windows app called MetaTrader4. This does not have any API, so the mtapi bridge has been designed in .NET to allow other programming languages to interact with it. The application has some events defined, two of which are: QuoteUpdate
and QuoteUpdated
.
So I would like to write a listener (delegate?) using python.net to subscribe to this event. But since I am not able to understand how the .NET code is producing these events, and neither how to properly use pythonnet, I have not been able to get this to work. I also keep running into the error:
TypeError: 'EventBinding' object is not callable
Googling this doesn't return anything useful, apart this "FIXME" comment.
Here's is my 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
#...
The intention for my code should be clear.
Q: How can I make my listener fire when receiving the QuoteUpdate
.NET event?
For Reference:
- The .NET code in C# (from MtApiClient.cs look like this:
...
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;
- And a small VB Test app look like this:
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
UPDATE
For reference, we have the following relevant DLL calls, given by the attributes, types and __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)
Similar Issues:
There are literally 100's of related SO issue, and I've probably looked at over 60% of them but with nearly zero success to applicability to by use case. Some of which are:
- Does Python classes support events like other languages?
- What's the correct way to convert this event handler registration from C# to VB.net?
- How do I convert a VB delegate into a python event handler?
- https://ironpython.net/documentation/dotnet/dotnet.html (may also be relevant)
- https://openbookproject.net/thinkcs/python/english3e/events.html
- https://code.activestate.com/recipes/410686-c-style-events-in-python/
回答1:
After a few days and some long hours I found a combination of mistake(s). As always, a combination of 2-3 simple mistakes can make your life miserable for a long time. But the biggest mistake of all, was being fooled to think that a listner (aka. delegate) had to be complicated with a bunch of __init__
and __iadd__
functions. Wrong! Python.NET just works most of the time but the errors you get (if any at all) are pretty useless if you make any small mistake.
There are 1.5 problems with my original code.
There were 2 different Quote functions, namely:
QuoteUpdate
which returns sender and an object named "MtQuoteEventArgs" in .NETQuoteUpdated
which returns sender and 3 arguments.Therefore we need to fix both the printTick() function and the listener line.
The corrected code is:
def printTick(source, symbol, ask, bid):
print('Tick: Symbol: {} Ask: {:.5f} Bid: {:.5f}'.format(symbol, ask, bid))
...
mtc.QuoteUpdates += printTick
回答2:
According to the mtapi documentation you linked, the code should be:
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
来源:https://stackoverflow.com/questions/64848373/how-to-subscribe-to-a-net-event-in-a-python-listener-using-pythonnet