kieran-mackle / AutoTrader

A Python-based development platform for automated trading systems - from backtesting to optimisation to livetrading.

Home Page:https://kieran-mackle.github.io/AutoTrader/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Broker interface should be programmatically enforced

jmcph4 opened this issue · comments

Is your feature request related to a problem? Please describe.
A common desire is to extend AutoTrader's functionality to new exchanges (either specific instances of exchanges or entire classes, such as DEXes). In order to do this, new implementations must conform to the broker interface. Unfortunately, this interface, despite having very real expectations on compliant implementations, is not enforced anywhere in the current codebase.

Python purists will argue this is not Pythonic as it attempts to sidestep ducktyping; however, there is clearly confusion surrounding exactly what a broker must look like.

Describe the solution you'd like
At a high level, programmatic enforcement of the broker interface (which exists regardless of enforcement method). Actually achieving this in Python is sadly a best-effort endeavour though.

The least insane solution is very likely an abstract base class. Specifically, by defining abstract methods. Something like the following should suffice:

class Broker(metaclass=abc.ABCMeta):
    @classmethod
    def __subclasshook__(cls, subclass):
        return (hasattr(subclass, 'get_NAV') and 
                callable(subclass.get_NAV) and 
                hasattr(subclass, 'get_balance') and 
                callable(subclass.get_balance) and
                hasattr(subclass, 'place_order') and 
                callable(subclass.place_order) and
                hasattr(subclass, 'get_orders') and
                callable(subclass.get_orders) and
                hasattr(subclass, 'cancel_order') and
                callable(subclass.cancel_order) and
                hasattr(subclass, 'get_trades') and 
                callable(subclass.get_trades) and
                hasattr(subclass, 'get_positions') and
                callable(subclass.get_positions) or
                NotImplemented)

    @abc.abstractmethod
    def get_NAV(self) -> float:
       pass
       
    # etc. ...

Describe alternatives you've considered
Other methods like subclass hooks but these seem ugly and complicated (admittedly my Python knowledge may be lacking here and these might be The One True Way(TM)).

Additional context
None.

In hindsight, Broker is an unfortunate name for that snippet as it collides with existing conventions:

class Broker:
def __init__(self, config: dict, utils: BrokerUtils = None) -> None:
"""AutoTrader Broker Class constructor."""
self._utils = utils if utils is not None else Utils()

Great suggestion, it has been an issue on my radar for a while and is definitely something which should be improved. Will bump it up the priority list.

Related: #7

Implemented with #60