Saturday 21 November 2020

How to subscribe to a .NET event in a python listener using pythonnet?

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:

...
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; 
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:



from How to subscribe to a .NET event in a python listener using pythonnet?

No comments:

Post a Comment