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
SYM = 'EURUSD'
rA = []
res = 0
def printTick(symbol, ask, bid):
print('Tick: Symbol: {} Ask: {:.5f} Bid: {:.5f}'.format(symbol, ask, bid))
class OnTick:
def __init__(self):
self.listeners = []
def __iadd__(self, listener):
# Shortcut for using += to add a listener
self.listeners.append(listener)
return self
def notify(self, *args, **kwargs):
for listener in self.listeners:
listener(*args, **kwargs)
# --- MAIN -------------------------------
# Setup .NET API bridge connection
mtc = mt.MtApiClient()
res = mtc.BeginConnect('127.0.0.1', 8222);
#--------------------------------------
# Register and use the listener
#--------------------------------------
# This WORKS!
newTick = OnTick()
newTick += printTick
newTick.notify('EURUSD', 1.12400, 1.12300)
# 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 available DLL attributes/calls:
>>> c.
c.AccountBalance( c.ChartXOnDropped( c.NULL c.OrderStopLoss( c.TimeYear( c.iCustom(
c.AccountCompany( c.ChartXYToTimePrice( c.ObjectCreate( c.OrderSwap( c.ToString( c.iDeMarker(
c.AccountCredit( c.ChartYOnDropped( c.ObjectDelete( c.OrderSymbol( c.UninitializeReason( c.iEnvelopes(
c.AccountCurrency( c.Comment( c.ObjectDescription( c.OrderTakeProfit( c.UnlockTicks( c.iEnvelopesOnArray(
c.AccountEquity( c.ConnectionState c.ObjectFind( c.OrderTicket( c.WindowBarsPerChart( c.iForce(
c.AccountFreeMargin( c.ConnectionStateChanged c.ObjectGetDouble( c.OrderType( c.WindowExpertName( c.iFractals(
c.AccountFreeMarginCheck( c.CopyRates( c.ObjectGetFiboDescription( c.OrdersHistoryTotal( c.WindowFind( c.iGator(
c.AccountFreeMarginMode( c.Day( c.ObjectGetInteger( c.OrdersTotal( c.WindowFirstVisibleBar( c.iHigh(
c.AccountLeverage( c.DayOfWeek( c.ObjectGetShiftByValue( c.Overloads c.WindowHandle( c.iHighArray(
c.AccountMargin( c.DayOfYear( c.ObjectGetString( c.PlaySound( c.WindowIsVisible( c.iHighest(
c.AccountName( c.EMPTY c.ObjectGetTimeByValue( c.Print( c.WindowOnDropped( c.iIchimoku(
c.AccountNumber( c.Equals( c.ObjectGetValueByShift( c.QuoteAdded c.WindowPriceMax( c.iLow(
c.AccountProfit( c.ErrorDescription( c.ObjectGetValueByTime( c.QuoteRemoved c.WindowPriceMin( c.iLowArray(
c.AccountServer( c.ExecutorHandle c.ObjectMove( c.QuoteUpdate c.WindowPriceOnDropped( c.iLowest(
c.AccountStopoutLevel( c.Finalize( c.ObjectName( c.QuoteUpdated c.WindowRedraw( c.iMA(
c.AccountStopoutMode( c.GetHashCode( c.ObjectSet( c.ReferenceEquals( c.WindowScreenShot( c.iMACD(
c.Alert( c.GetLastError( c.ObjectSetDouble( c.RefreshRates( c.WindowTimeOnDropped( c.iMAOnArray(
c.BeginConnect( c.GetOrder( c.ObjectSetFiboDescription( c.Seconds( c.WindowXOnDropped( c.iMFI(
c.BeginDisconnect( c.GetOrders( c.ObjectSetInteger( c.SendFTP( c.WindowYOnDropped( c.iMomentum(
c.ChangeAccount( c.GetQuotes( c.ObjectSetString( c.SendMail( c.WindowsTotal( c.iMomentumOnArray(
c.ChartApplyTemplate( c.GetTickCount( c.ObjectSetText( c.SeriesInfoInteger( c.Year( c.iOBV(
c.ChartClose( c.GetType( c.ObjectType( c.Sleep( c.add_ConnectionStateChanged( c.iOpen(
c.ChartFirst( c.GlobalVariableCheck( c.ObjectsDeleteAll( c.SymbolInfoDouble( c.add_OnChartEvent( c.iOpenArray(
c.ChartGetDouble( c.GlobalVariableDel( c.ObjectsTotal( c.SymbolInfoInteger( c.add_OnLastTimeBar( c.iOsMA(
c.ChartGetInteger( c.GlobalVariableGet( c.OnChartEvent c.SymbolInfoSession( c.add_QuoteAdded( c.iRSI(
c.ChartGetString( c.GlobalVariableName( c.OnLastTimeBar c.SymbolInfoString( c.add_QuoteRemoved( c.iRSIOnArray(
c.ChartId( c.GlobalVariableSet( c.OrderClose( c.SymbolInfoTick( c.add_QuoteUpdate( c.iRVI(
c.ChartIndicatorDelete( c.GlobalVariableSetOnCondition( c.OrderCloseAll( c.SymbolName( c.add_QuoteUpdated( c.iSAR(
c.ChartIndicatorName( c.GlobalVariablesDeleteAll( c.OrderCloseBy( c.SymbolSelect( c.get_ConnectionState( c.iStdDev(
c.ChartIndicatorsTotal( c.GlobalVariablesTotal( c.OrderCloseByCurrentPrice( c.SymbolsTotal( c.get_ExecutorHandle( c.iStdDevOnArray(
c.ChartNavigate( c.Hour( c.OrderClosePrice( c.TerminalCompany( c.iAC( c.iStochastic(
c.ChartNext( c.IsConnected( c.OrderCloseTime( c.TerminalInfoDouble( c.iAD( c.iTime(
c.ChartOpen( c.IsDemo( c.OrderComment( c.TerminalInfoInteger( c.iADX( c.iTimeArray(
c.ChartPeriod( c.IsDllsAllowed( c.OrderCommission( c.TerminalInfoString( c.iAO( c.iVolume(
c.ChartPriceOnDropped( c.IsExpertEnabled( c.OrderDelete( c.TerminalName( c.iATR( c.iVolumeArray(
c.ChartRedraw( c.IsLibrariesAllowed( c.OrderExpiration( c.TerminalPath( c.iAlligator( c.iWPR(
c.ChartSaveTemplate( c.IsOptimization( c.OrderLots( c.TextSetFont( c.iBWMFI( c.remove_ConnectionStateChanged(
c.ChartScreenShot( c.IsStopped( c.OrderMagicNumber( c.TimeCurrent( c.iBands( c.remove_OnChartEvent(
c.ChartSetDouble( c.IsTesting( c.OrderModify( c.TimeDay( c.iBandsOnArray( c.remove_OnLastTimeBar(
c.ChartSetInteger( c.IsTradeAllowed( c.OrderOpenPrice( c.TimeDayOfWeek( c.iBarShift( c.remove_QuoteAdded(
c.ChartSetString( c.IsTradeContextBusy( c.OrderOpenTime( c.TimeDayOfYear( c.iBars( c.remove_QuoteRemoved(
c.ChartSetSymbolPeriod( c.IsVisualMode( c.OrderPrint( c.TimeGMT( c.iBearsPower( c.remove_QuoteUpdate(
c.ChartSymbol( c.MarketInfo( c.OrderProfit( c.TimeHour( c.iBullsPower( c.remove_QuoteUpdated(
c.ChartTimeOnDropped( c.MemberwiseClone( c.OrderSelect( c.TimeLocal( c.iCCI( c.set_ExecutorHandle(
c.ChartTimePriceToXY( c.MessageBox( c.OrderSend( c.TimeMinute( c.iCCIOnArray(
c.ChartWindowFind( c.Minute( c.OrderSendBuy( c.TimeMonth( c.iClose(
c.ChartWindowOnDropped( c.Month( c.OrderSendSell( c.TimeSeconds( c.iCloseArray(
where c=MtApi.MtApiClient()
.
The relevant attributes, types and __doc__
are:
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/
from How to subscribe to a .NET event in a python listener using pythonnet?
No comments:
Post a Comment