OPTIBOOK
optibook.common_types API documentation

Module optibook.common_types

Classes

class Instrument (instrument_id: str, tick_size: float, instrument_type: Optional[InstrumentType] = None, price_change_limit: Optional[PriceChangeLimit] = None, *, expiry: Optional[datetime.datetime] = None, option_kind: Optional[OptionKind] = None, strike: Optional[float] = None, base_instrument_id: Optional[str] = None, interest_rate: Optional[float] = None, index_id: Optional[float] = None, index_constituents: Optional[Dict[str, float]] = None, index_divisor: Optional[float] = None, index_volatility: Optional[float] = None, etf_cash_comp: Optional[float] = None, etf_multiplier: Optional[float] = None, instrument_group: Optional[str] = None)
Expand source code
class Instrument:
    @staticmethod
    def from_dict(instrument_id: str, tick_size: float, price_change_limit: Optional[Union[Dict, PriceChangeLimit]],
                  dict_data: Dict) -> "Instrument":
        if price_change_limit and not isinstance(price_change_limit, PriceChangeLimit):
            price_change_limit = PriceChangeLimit(price_change_limit['absolute_change'],
                                                  price_change_limit['relative_change'])

        instrument = Instrument(instrument_id, tick_size=tick_size, price_change_limit=price_change_limit)

        # TODO: Access via getters instead of attributes?
        for k, v in dict_data.items():
            setattr(instrument, k, v)

        try:
            if instrument.instrument_type:
                instrument.instrument_type = InstrumentType[instrument.instrument_type]
            if instrument.expiry:
                instrument.expiry = datetime.strptime(instrument.expiry, '%Y-%m-%d %H:%M:%S')
            if instrument.option_kind:
                instrument.option_kind = OptionKind[instrument.option_kind]
        except:
            logger.exception(f'Error while parsing instrument {instrument_id} {tick_size} {dict_data}')

        return instrument

    @staticmethod
    def from_extra_info_json(instrument_id: str, tick_size: float, price_change_limit: Optional[PriceChangeLimit],
                             json_data: str) -> "Instrument":
        return Instrument.from_dict(instrument_id, tick_size, price_change_limit, json.loads(json_data))

    @staticmethod
    def to_extra_info_json(instrument) -> str:
        instr_copy = copy.deepcopy(instrument.__dict__)

        if instr_copy['instrument_type']:
            instr_copy['instrument_type'] = instr_copy['instrument_type'].name
        if instr_copy['expiry']:
            instr_copy['expiry'] = datetime.strftime(instr_copy['expiry'], '%Y-%m-%d %H:%M:%S')
        if instr_copy['option_kind']:
            instr_copy['option_kind'] = instr_copy['option_kind'].name

        # some fields are not part of the extra_info json
        # but are fixed fields in the protocol
        del instr_copy['instrument_id']
        del instr_copy['tick_size']
        del instr_copy['price_change_limit']
        del instr_copy['parameters']
        del instr_copy['paused']
        del instr_copy['expired']

        return json.dumps(instr_copy)

    def __init__(self,
                 instrument_id: str,
                 tick_size: float,
                 instrument_type: Optional[InstrumentType] = None,
                 price_change_limit: Optional[PriceChangeLimit] = None,
                 *,
                 expiry: Optional[datetime] = None,
                 option_kind: Optional[OptionKind] = None,
                 strike: Optional[float] = None,
                 base_instrument_id: Optional[str] = None,
                 interest_rate: Optional[float] = None,
                 index_id: Optional[float] = None,
                 index_constituents: Optional[Dict[str, float]] = None,
                 index_divisor: Optional[float] = None,
                 index_volatility: Optional[float] = None,
                 etf_cash_comp: Optional[float] = None,
                 etf_multiplier: Optional[float] = None,
                 instrument_group: Optional[str] = None):
        # All instruments
        self.instrument_id: str = instrument_id
        self.tick_size: float = tick_size
        self.instrument_type: Optional[InstrumentType] = instrument_type
        self.price_change_limit: Optional[PriceChangeLimit] = price_change_limit

        # options
        self.expiry: Optional[datetime] = expiry
        self.option_kind: Optional[OptionKind] = option_kind
        self.strike: Optional[float] = strike

        # stock options
        self.base_instrument_id: Optional[str] = base_instrument_id

        # futures and index options (stock interest rates are set by the generator)
        self.interest_rate = interest_rate

        # index etfs, futures, options
        self.index_id = index_id
        self.index_constituents = index_constituents
        self.index_divisor = index_divisor

        # index options
        self.index_volatility = index_volatility

        # etfs
        self.etf_cash_comp = etf_cash_comp
        self.etf_multiplier = etf_multiplier

        # miscellaneous
        self.instrument_group: Optional[str] = instrument_group
        self.paused: bool = False
        self.expired: bool = False
        self.parameters: Optional[Dict] = None

    def __repr__(self):
        included_attributes = ['instrument_id', 'tick_size', 'price_change_limit', 'instrument_type',
                               'base_instrument_id', 'expiry', 'option_kind', 'strike', 'interest_rate', 'index_id',
                               'instrument_group', 'paused', 'expired']
        included_attributes = filter(lambda attr: getattr(self, attr) is not None, included_attributes)
        repr = f'''Instrument({", ".join(f"{attr}={getattr(self, attr)}" for attr in included_attributes)})'''
        return repr

Static methods

def from_dict(instrument_id: str, tick_size: float, price_change_limit: Union[Dict[~KT, ~VT], PriceChangeLimit, None], dict_data: Dict[~KT, ~VT]) ‑> Instrument
def from_extra_info_json(instrument_id: str, tick_size: float, price_change_limit: Optional[PriceChangeLimit], json_data: str) ‑> Instrument
def to_extra_info_json(instrument) ‑> str
class InstrumentType (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code
class InstrumentType(Enum):
    STOCK = 1
    STOCK_OPTION = 2
    STOCK_FUTURE = 3

    INDEX_TRACKING_ETF = 4

    INDEX_OPTION = 5
    INDEX_FUTURE = 6

Ancestors

  • enum.Enum

Class variables

var INDEX_FUTURE
var INDEX_OPTION
var INDEX_TRACKING_ETF
var STOCK
var STOCK_FUTURE
var STOCK_OPTION
class OptionKind (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code
class OptionKind(Enum):
    PUT = 1
    CALL = 2

Ancestors

  • enum.Enum

Class variables

var CALL
var PUT
class OrderStatus

Summary of an order.

Attributes

order_id : int
The id of the order.
instrument_id : str
The id of the traded instrument.
price : float
The price at which the instrument traded.
volume : int
The volume that was traded.
side : 'bid' or 'ask'
If 'bid' this is a bid order. If 'ask' this is an ask order.
Expand source code
class OrderStatus:
    """
    Summary of an order.

    Attributes
    ----------
    order_id: int
        The id of the order.

    instrument_id: str
        The id of the traded instrument.

    price: float
        The price at which the instrument traded.

    volume: int
        The volume that was traded.

    side: 'bid' or 'ask'
        If 'bid' this is a bid order.
        If 'ask' this is an ask order.
    """
    def __init__(self):
        self.order_id: int = 0
        self.instrument_id: str = ''
        self.price: float = 0.0
        self.volume: int = 0
        self.side: str = ''

    def __repr__(self):
        return f'OrderStatus(order_id={self.order_id}, instrument_id={self.instrument_id}, price={self.price}, ' \
               f'volume={self.volume}, side={self.side})'
class PriceBook (*, timestamp=None, instrument_id=None, bids=None, asks=None)

An order book at a specific point in time.

Attributes

timestamp : datetime.datetime
The time of the snapshot.
instrument_id : str
The id of the instrument the book is on.
bids : List[PriceVolume]
List of price points and volumes representing all bid orders. Sorted from highest price to lowest price (i.e. from best to worst).
asks : List[PriceVolume]
List of price points and volumes representing all ask orders. Sorted from lowest price to highest price (i.e. from best to worst).
Expand source code
class PriceBook:
    """
    An order book at a specific point in time.

    Attributes
    ----------
    timestamp: datetime.datetime
        The time of the snapshot.

    instrument_id: str
        The id of the instrument the book is on.

    bids: List[PriceVolume]
        List of price points and volumes representing all bid orders.
        Sorted from highest price to lowest price (i.e. from best to worst).

    asks: List[PriceVolume]
        List of price points and volumes representing all ask orders.
        Sorted from lowest price to highest price (i.e. from best to worst).
    """
    def __init__(self, *, timestamp=None, instrument_id=None, bids=None, asks=None):
        self.timestamp: datetime = datetime(1970, 1, 1) if not timestamp else timestamp
        self.instrument_id: str = '' if not instrument_id else instrument_id
        self.bids: List[PriceVolume] = [] if not bids else bids
        self.asks: List[PriceVolume] = [] if not asks else asks

    def __repr__(self):
        book_desc = f"PriceBook({self.instrument_id} {self.timestamp})"
        book_header = ['#bids', 'price', '#asks']

        bid_width = max([bid.volume_width for bid in self.bids] + [len(book_header[0])]) + 2
        ask_width = max([ask.volume_width for ask in self.asks] + [len(book_header[2])]) + 2
        price_width = max([order.price_width for order in self.asks + self.bids] + [len(book_header[1])]) + 2

        book_repr = [book_desc, self._format_level(book_header, bid_width, price_width, ask_width)]
        for ask in self.asks[::-1]: 
            ask_level = ['', str(round(ask.price, 3)), str(ask.volume)]
            ask_level = self._format_level(ask_level, bid_width, price_width, ask_width)
            book_repr.append(ask_level)
        for bid in self.bids: 
            bid_level = [str(bid.volume), str(round(bid.price, 3)), '']
            bid_level = self._format_level(bid_level, bid_width, price_width, ask_width)
            book_repr.append(bid_level)
        return '\n'.join(book_repr)

    def __eq__(self, other):
        if not isinstance(other, PriceBook):
            return NotImplemented
        return self.instrument_id == other.instrument_id and self.bids == other.bids and self.asks == other.asks
    
    @staticmethod
    def _format_level(level: List[str], bid_width: int, price_width: int, ask_width: int):
        assert len(level) == 3, "_format_level expects 3 elements in level (#bid, price, #ask)"
        return f"{level[0].center(bid_width, ' ')}|{level[1].center(price_width, ' ')}|{level[2].center(ask_width, ' ')}"
class PriceChangeLimit (absolute_change: float, relative_change: float)
Expand source code
class PriceChangeLimit:
    def __init__(self, absolute_change: float, relative_change: float):
        self.absolute_change: float = absolute_change
        self.relative_change: float = relative_change

    def __repr__(self):
        return f'PriceChangeLimit(absolute_change={self.absolute_change:.4f}, ' \
               f'relative_change={self.relative_change * 100:.2f}%)'
class PriceVolume (price, volume)

Bundles a price and a volume

Attributes

price : float
 
volume : int
 
Expand source code
class PriceVolume:
    """
    Bundles a price and a volume

    Attributes
    ----------
    price: float

    volume: int
    """
    def __init__(self, price, volume):
        self.price = price
        self.volume = volume

    def __repr__(self):
        return f"PriceVolume(price={str(self.price)}, volume={str(self.volume)})"

    def __eq__(self, other):
        if not isinstance(other, PriceVolume):
            return NotImplemented
        return self.price == other.price and self.volume == other.volume

    # Used for formatting
    @property
    def price_width(self):
        return len(str(round(self.price, 3))) # Currently limit price granularity to 3 d.p.

    @property
    def volume_width(self):
        return len(str(self.volume))

Instance variables

prop price_width
Expand source code
@property
def price_width(self):
    return len(str(round(self.price, 3))) # Currently limit price granularity to 3 d.p.
prop volume_width
Expand source code
@property
def volume_width(self):
    return len(str(self.volume))
class SingleSidedBooking
Expand source code
class SingleSidedBooking:
    def __init__(self):
        self.username: str = ''
        self.instrument_id: str = ''
        self.price: float = 0.0
        self.volume: int = 0
        self.action: str = ''

    def __repr__(self):
        return f'SingleSidedBooking(username={self.username}, instrument_id={self.instrument_id}, ' \
               f'price={self.price}, volume={self.volume}, action={self.action})'
class SocialMediaFeed (*, timestamp=None, post=None, meta_data=None)
Expand source code
class SocialMediaFeed:
    def __init__(self, *, timestamp=None, post=None, meta_data=None):
        self.timestamp: datetime = datetime(1970, 1, 1) if not timestamp else timestamp
        self.post: str = '' if not post else post
        self.meta_data: str = '' if not meta_data else meta_data

    def __repr__(self):
        return f'SocialMediaFeed(timestamp={self.timestamp}, post={self.post}, meta_data={self.meta_data})'
class Trade

A private trade.

A private trade is a trade in which you were involved, i.e. a trade in which you were either a buyer or a seller.

Attributes

timestamp : datetime.datetime
The time of the trade.
order_id : int
The id of the order that traded.
trade_id : int
Id of the trade
instrument_id : str
The id of the traded instrument.
price : float
The price at which the instrument traded.
volume : int
The volume that was traded.
side : 'bid' or 'ask'
If 'bid' you bought. If 'ask' you sold.
Expand source code
class Trade:
    """
    A private trade.

    A private trade is a trade in which you were involved,
    i.e. a trade in which you were either a buyer or a seller.

    Attributes
    ----------
    timestamp: datetime.datetime
        The time of the trade.

    order_id: int
        The id of the order that traded.

    trade_id: int
        Id of the trade

    instrument_id: str
        The id of the traded instrument.

    price: float
        The price at which the instrument traded.

    volume: int
        The volume that was traded.

    side: 'bid' or 'ask'
        If 'bid' you bought.
        If 'ask' you sold.
    """
    def __init__(self):
        self.timestamp: datetime = datetime(1970, 1, 1)
        self.order_id: int = 0
        self.trade_id: int = -1
        self.instrument_id: str = ''
        self.price: float = 0.0
        self.volume: int = 0
        self.side: str = ''

    def __repr__(self):
        return f'Trade(timestamp={self.timestamp}, order_id={self.order_id}, trade_id={self.trade_id}, ' \
               f'instrument_id={self.instrument_id}, price={self.price}, volume={self.volume}, side={self.side})'
class TradeTick (*, timestamp=None, instrument_id=None, price=None, volume=None, aggressor_side=None, buyer=None, seller=None, trade_id=None)

A public trade.

A public trade is a trade between any two parties, i.e. a trade in which you might not have been involved.

Attributes

timestamp : datetime.datetime
The time of the trade.
instrument_id : str
The id of the traded instrument.
price : float
The price at which the instrument traded.
volume : int
The volume that was traded.
aggressor_side : 'bid' or 'ask'
The side of the aggressive party. If 'bid' then the initiator (aggressor) of the trade bought. If 'ask' then the initiator (aggressor) of the trade sold.
buyer : str
Name of buyer.
seller : str
Name of seller.
trade_id : int
Id of the trade
Expand source code
class TradeTick:
    """
    A public trade.

    A public trade is a trade between any two parties,
    i.e. a trade in which you might not have been involved.

    Attributes
    ----------
    timestamp: datetime.datetime
        The time of the trade.

    instrument_id: str
        The id of the traded instrument.

    price: float
        The price at which the instrument traded.

    volume: int
        The volume that was traded.

    aggressor_side: 'bid' or 'ask'
        The side of the aggressive party.
        If 'bid' then the initiator (aggressor) of the trade bought.
        If 'ask' then the initiator (aggressor) of the trade sold.

    buyer: str
        Name of buyer.

    seller: str
        Name of seller.

    trade_id: int
        Id of the trade
    """
    def __init__(self, *, timestamp=None, instrument_id=None, price=None, volume=None, aggressor_side=None, buyer=None, seller=None, trade_id=None):
        self.timestamp: datetime = datetime(1970, 1, 1) if not timestamp else timestamp
        self.instrument_id: str = '' if not instrument_id else instrument_id
        self.price: float = 0.0 if not price else price
        self.volume: int = 0 if not volume else volume
        self.aggressor_side: str = '' if not aggressor_side else aggressor_side
        self.buyer: str = '' if not buyer else buyer
        self.seller: str = '' if not seller else seller
        self.trade_id: int = -1 if not trade_id else trade_id

    def __repr__(self):
        return f'TradeTick(timestamp={self.timestamp}, instrument_id={self.instrument_id}, price={self.price}, ' \
               f'volume={self.volume}, aggressor_side={self.aggressor_side}, buyer={self.buyer}, ' \
               f'seller={self.seller}, trade_id={self.trade_id})'