pyaver.orderbook

  1from solana.publickey import PublicKey
  2from .enums import Side
  3from .utils import load_bytes_data, load_multiple_bytes_data
  4from .data_classes import Price, SlabOrder
  5from .slab import Slab
  6from solana.rpc.async_api import AsyncClient
  7
  8
  9class Orderbook:
 10    """
 11    Orderbook object
 12
 13    Contains information on open orders on both the bids and asks of a particular outcome in a market
 14    """
 15
 16    pubkey: PublicKey
 17    """
 18    Orderbook public key
 19    """
 20    slab_bids: Slab
 21    """
 22    Slab object for bids
 23    """
 24    slab_asks: Slab
 25    """
 26    Slab object for asks
 27    """
 28    slab_bids_pubkey: PublicKey
 29    """
 30    Public key of the account containing the bids
 31    """
 32    slab_asks_pubkey: PublicKey
 33    """
 34    Public key of the account containing the asks
 35    """
 36    decimals: int
 37    """
 38    Decimal precision for orderbook
 39    """
 40    is_inverted: bool
 41    """
 42    Whether the bids and asks should be interpretted as inverted when parsing the data. (Used in the case of the second outcome in a two-outcome market.)
 43    """
 44
 45    def __init__(
 46        self, 
 47        pubkey: PublicKey, 
 48        slab_bids: Slab,
 49        slab_asks: Slab,
 50        slab_bids_pubkey: PublicKey,
 51        slab_asks_pubkey: PublicKey,
 52        decimals: int,
 53        is_inverted: bool = False
 54        ):
 55        """
 56        Initialise an Orderbook object. Do not use this function; use Orderbook.load() instead
 57
 58        Args:
 59            pubkey (PublicKey): Orderbook public key
 60            slab_bids (Slab): Slab object for bids
 61            slab_asks (Slab): Slab object for asks
 62            slab_bids_pubkey (PublicKey): Slab bids public key
 63            slab_asks_pubkey (PublicKey): Slab asks public key
 64            decimals (int): Decimal precision for orderbook
 65            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
 66        """
 67        self.decimals = decimals
 68        self.pubkey = pubkey
 69        self.slab_bids = slab_bids
 70        self.slab_asks = slab_asks
 71        self.slab_bids_pubkey = slab_bids_pubkey
 72        self.slab_asks_pubkey = slab_asks_pubkey
 73        self.is_inverted = is_inverted
 74    
 75    @staticmethod
 76    async def load(
 77        conn: AsyncClient, 
 78        slab_bids_pubkey: PublicKey, 
 79        slab_asks_pubkey: PublicKey, 
 80        orderbook_pubkey: PublicKey, 
 81        decimals: int, 
 82        is_inverted: bool = False
 83        ):
 84        """
 85        Initialise an Orderbook object
 86
 87        Parameters are found in MarketStoreStates' --> OrderbookAccounts
 88
 89        Args:
 90            conn (AsyncClient): Solana AsyncClient object
 91            slab_bids_pubkey (PublicKey): Slab bids public key
 92            slab_asks_pubkey (PublicKey): Slab asks public key
 93            orderbook_pubkey (PublicKey): Orderbook public key
 94            decimals (int): Decimal precision for orderbook
 95            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
 96
 97        Returns:
 98            Orderbook: Orderbook object
 99        """
100
101        slab_bids = await Orderbook.load_slab(conn, slab_bids_pubkey)
102        slab_asks  = await Orderbook.load_slab(conn, slab_asks_pubkey)
103
104        return Orderbook(
105            pubkey=orderbook_pubkey, 
106            slab_bids=slab_bids, 
107            slab_asks=slab_asks, 
108            slab_asks_pubkey=slab_asks_pubkey, 
109            slab_bids_pubkey=slab_bids_pubkey, 
110            decimals=decimals,
111            is_inverted=is_inverted
112            )
113
114    @staticmethod
115    async def load_slab(conn: AsyncClient, slab_address: PublicKey):
116        """
117        Loads onchain data for a Slab (contains orders for a particular side of the orderbook)
118
119        Args:
120            conn (AsyncClient): Solana AsyncClient object
121            slab_address (PublicKey): Slab public key
122
123        Returns:
124            Slab: Slab object
125        """
126        data = await load_bytes_data(conn, slab_address)
127        return Slab.from_bytes(data)
128
129    @staticmethod
130    async def load_multiple_slabs(conn: AsyncClient, slab_addresses: list[PublicKey]):
131        """
132        Loads onchain data for multiple Slabs (contains orders for a particular side of the orderbook)
133
134        Args:
135            conn (AsyncClient): Solana AsyncClient object
136            slab_addresses (list[PublicKey]): List of slab public keys
137
138        Returns:
139            list[Slab]: List of Slab objects
140        """
141        data = await load_multiple_bytes_data(conn, slab_addresses)
142        slabs = []
143        for d in data:
144            slabs.append(Slab.from_bytes(d))
145        return slabs
146
147    def invert(self):
148        """
149        Returns a version of the orderbook which has been parsed with bids and asks swtiched and
150        prices inverted. (Used for the second outcome in a two-outcome market)
151
152        Returns:
153            Orderbook: Orderbook object
154        """
155        return Orderbook(
156            self.pubkey,
157            self.slab_asks,
158            self.slab_bids,
159            self.slab_asks_pubkey,
160            self.slab_bids_pubkey,
161            self.decimals,
162            True
163        )
164    
165    
166    @staticmethod
167    def convert_price(p: Price, decimals: int):
168        """
169        Converts price to correct order of magnitude based on decimal precision
170
171        Args:
172            p (Price): Unconverted Price object
173            decimals (int): Decimal precision for orderbook
174
175        Returns:
176            Price: Price object
177        """
178        exp = 10 ** decimals
179        return Price(
180            price=p.price / exp,
181            #price=round((p.price / 2 ** 32) * exp) / exp,
182            size=p.size / exp
183        )
184    
185    @staticmethod
186    def get_L2_for_slab(
187        slab: Slab, 
188        depth: int, 
189        increasing : bool, 
190        decimals: int, 
191        ui_amount=False, 
192        is_inverted=False
193        ):
194        """
195        Get Level 2 market information for a particular slab
196
197        This contains information on orders on the slab aggregated by price tick.
198
199        Args:
200            slab (Slab): Slab object
201            depth (int): Number of orders to return
202            increasing (bool): Sort orders increasing
203            decimals (int): Decimal precision for orderbook
204            ui_amount (bool, optional): Converts prices based on decimal precision if true. Defaults to False.
205            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
206
207        Returns:
208            list[Price]: List of Price objects (size and price) corresponding to orders on the slab
209        """
210
211        l2_depth = Orderbook.__get_L2(slab, depth, decimals, increasing)
212        if(ui_amount):
213            l2_depth = [Orderbook.convert_price(p, decimals) for p in l2_depth]
214
215        if(is_inverted):
216            l2_depth = [Orderbook.invert_price(p) for p in l2_depth]
217
218        return l2_depth
219
220    @staticmethod
221    def __get_L2(slab: Slab, depth: int, decimals: int, increasing: bool):
222        """Get the Level 2 market information."""
223        # The first element of the inner list is price, the second is quantity.
224        levels: list[list[int]] = []
225        for node in slab.items(descending=not increasing):
226            price = Orderbook.__get_price_from_slab(node, decimals)
227            if len(levels) > 0 and levels[len(levels) - 1][0] == price:
228                levels[len(levels) - 1][1] += node.base_quantity
229            elif len(levels) == depth:
230                break
231            else:
232                levels.append([price, node.base_quantity])
233        return [
234            Price(
235                price=price_lots, 
236                size=size_lots
237            )
238            for price_lots, size_lots in levels
239        ]
240    
241    @staticmethod
242    def __get_L3(slab: Slab, decimals: int, increasing: bool, is_inverted: bool):
243        """Get the Level 1 market information."""
244        # The first element of the inner list is price, the second is quantity.
245        orders: list[SlabOrder] = []
246        for node in slab.items(descending = not increasing):
247            orders += [SlabOrder(
248                id = node.key,
249                price = (10**decimals) - Orderbook.__get_price_from_slab(node, decimals) if is_inverted else Orderbook.__get_price_from_slab(node, decimals),
250                price_ui = 1 - Orderbook.__get_price_from_slab(node, decimals) * (10**-decimals) if is_inverted else Orderbook.__get_price_from_slab(node, decimals) * (10**-decimals),
251                base_quantity = node.base_quantity,
252                base_quantity_ui = node.base_quantity * (10**-decimals),
253                user_market = node.user_market,
254                fee_tier = node.fee_tier,
255            )]
256
257        return orders
258
259    @staticmethod
260    def __get_price_from_slab(node, decimals: int):
261        return float(round( ((node.key >> 64)/(2**32)) * 10**decimals))
262
263    @staticmethod
264    def invert_price(p: Price):
265        """
266        Inverts prices
267
268        This is used when inverting the second outcome in a two-outcome market.
269
270        When switching prices between bids and asks, the price is `1-p`. 
271
272        Example, a BUY on A at a (probability) price of 0.4 is equivelant to a SELL on B at a price of 0.6 (1-0.4) and vice versa.
273
274        Args:
275            p (Price): Price object
276
277        Returns:
278            Price: Price object
279        """
280        return Price(
281            price=1-p.price, 
282            size=p.size
283            )
284
285    def get_bids_L3(self):
286        """
287        Gets level 1 market information for bids.
288
289        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
290
291        Returns:
292            list[SlabOrder]: List of slab orders for bids
293        """
294        is_increasing = False
295        if(self.is_inverted):
296            is_increasing = True
297        
298        return Orderbook.__get_L3(
299            self.slab_bids,
300            self.decimals,
301            is_increasing,
302            self.is_inverted
303        )
304
305    def get_asks_L3(self):
306        """
307        Gets level 1 market information for asks
308
309        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
310
311        Returns:
312            list[SlabOrder]: List of slab orders for asks
313        """
314        is_increasing = True
315        if(self.is_inverted):
316            is_increasing = False
317        
318        return Orderbook.__get_L3(
319            self.slab_asks,
320            self.decimals,
321            is_increasing,
322            self.is_inverted
323        )
324
325
326    def get_bids_l2(self, depth: int, ui_amount: bool):
327        """
328        Gets level 1 market information for bids
329
330        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
331
332        Args:
333            depth (int): Number of orders to return
334            ui_amount (bool): Converts prices based on decimal precision if true.
335
336        Returns:
337            list[Price]: List of Price objects (size and price) corresponding to orders on the slab
338        """
339        is_increasing = False
340        if(self.is_inverted):
341            is_increasing = True
342        
343        return Orderbook.get_L2_for_slab(
344            self.slab_bids,
345            depth,
346            is_increasing,
347            self.decimals,
348            ui_amount,
349            self.is_inverted,
350        )
351
352    def get_asks_l2(self, depth: int, ui_amount: bool):
353        """
354        Gets level 1 market information for asks
355
356        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
357
358        Args:
359            depth (int): Number of orders to return
360            ui_amount (bool): Converts prices based on decimal precision if true.
361
362        Returns:
363            list[Price]: List of Price objects (size and probability price) corresponding to orders on the slab
364        """
365        is_increasing = True
366        if(self.is_inverted):
367            is_increasing = False
368        
369        return Orderbook.get_L2_for_slab(
370            self.slab_asks,
371            depth,
372            is_increasing,
373            self.decimals,
374            ui_amount,
375            self.is_inverted,
376        )
377    
378    def get_best_bid_price(self, ui_amount: bool):
379        """
380        Gets the best bid price
381
382        Args:
383            ui_amount (bool):  Converts prices based on decimal precision if true.
384
385        Returns:
386            Price: Price object (size and price)
387        """
388        bids = self.get_bids_l2(1, ui_amount)
389        if(bids is not None and len(bids) > 0):
390            return bids[0]
391        return None
392
393    def get_best_ask_price(self, ui_amount: bool):
394        """
395        Gets the best ask price
396
397        Args:
398            ui_amount (bool):  Converts prices based on decimal precision if true.
399
400        Returns:
401            Price: Price object (size and price)
402        """
403        asks = self.get_asks_l2(1, ui_amount)
404        if(asks is not None and len(asks) > 0):
405            return asks[0]
406        return None
407    
408    def get_bid_price_by_order_id(self, order_id: int):
409        """
410        Gets bid Price object by order_id
411
412        Args:
413            order_id (int): Order ID
414
415        Returns:
416            Price: Price object (size and price)
417        """
418        bid = self.slab_bids.get(order_id)
419        if(bid is None):
420            return None
421        
422        bid_price = Price(price=bid.key >> 64, size=bid.base_quantity)
423        bid_price = Orderbook.convert_price(bid_price, self.decimals)
424
425        if(self.is_inverted):
426            bid_price = Orderbook.invert_price(bid_price)
427        
428        return bid_price
429
430    def get_ask_price_by_order_id(self, order_id: int):
431        """
432        Gets ask Price object by order_id
433
434        Args:
435            order_id (int): Order ID
436
437        Returns:
438            Price: Price object (size and price)
439        """
440        ask = self.slab_asks.get(order_id)
441        if(ask is None):
442            return None
443        
444        ask_price = Price(price=ask.key >> 64, size=ask.base_quantity)
445        ask_price = Orderbook.convert_price(ask_price, self.decimals)
446
447        if(self.is_inverted):
448            ask_price = Orderbook.invert_price(ask_price)
449        
450        return ask_price
451    
452    def estimate_avg_fill_for_base_qty(self, base_qty: int, side: Side, ui_amount: bool):
453        """
454        Gets estimate of average fill price (probability format) given a base/payout quantity
455
456        Args:
457            base_qty (int): Base quantity
458            side (Side): Side object (bid or ask)
459            ui_amount (bool): Converts prices based on decimal precision if true.
460
461        Returns:
462            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
463        """
464        return self.__estimate_fill_for_qty(base_qty, side, False, ui_amount)
465
466    def estimate_avg_fill_for_quote_qty(self, quote_qty: int, side: Side, ui_amount: bool):
467        """
468        Gets estimate of average fill price (probability format) given a stake/quote quantity
469
470        Args:
471            quote_qty (int): Base quantity
472            side (Side): Side object (bid or ask)
473            ui_amount (bool): Converts prices based on decimal precision if true.
474
475        Returns:
476            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
477        """
478        return self.__estimate_fill_for_qty(quote_qty, side, True, ui_amount)
479
480    def __estimate_fill_for_qty(self, qty: int, side: Side, quote: bool, ui_amount: bool):
481        """
482        _summary_
483
484        Args:
485            qty (int): Quanity
486            side (Side): Side object (bid or ask)
487            quote (bool): Quote quantity if true. Base quantity if false.
488            ui_amount (bool): Converts prices based on decimal precision if true.
489
490        Returns:
491            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
492        """
493        if(side == Side.BUY):
494            prices = self.get_bids_l2(100, ui_amount)
495        elif(side == Side.SELL):
496            prices = self.get_asks_l2(100, ui_amount)
497        
498        if(quote):
499            accumulator = lambda p: p.size
500        else:
501            accumulator = lambda p: p.size * p.price
502        
503        new_prices: list[Price] = []
504        cumulative_qty = 0
505        for price in prices:
506            remaining_qty = qty - cumulative_qty
507            if(remaining_qty <= accumulator(price)):
508                cumulative_qty += remaining_qty
509                new_size = remaining_qty if quote else remaining_qty/price.price
510                new_prices.append(Price(price=price.price, size=new_size))
511                break
512            else:
513                cumulative_qty += accumulator(price)
514                new_prices.append(price)
515        
516        return {
517            'avg_price': Orderbook.weighted_average(
518                nums=[p.price for p in new_prices],
519                weights=[p.size for p in new_prices]
520            ),
521            'worst_price': new_prices[-1].price,
522            'filled': cumulative_qty
523        }
524
525    @staticmethod
526    def weighted_average(nums, weights):
527        """
528        Calculates weighted average
529
530        Args:
531            nums (list[float]): List of values
532            weights (list[float]): List of weights
533
534        Returns:
535            float: Weighted average
536        """
537        sum = 0
538        weight_sum = 0
539
540        assert len(nums) == len(weights), 'Number of weights and nums do not correspond'
541
542        for i, num in enumerate(nums):
543            weight = weights[i]
544            sum += num * weight
545            weight_sum += weight
546        
547        return sum / weight_sum
class Orderbook:
 10class Orderbook:
 11    """
 12    Orderbook object
 13
 14    Contains information on open orders on both the bids and asks of a particular outcome in a market
 15    """
 16
 17    pubkey: PublicKey
 18    """
 19    Orderbook public key
 20    """
 21    slab_bids: Slab
 22    """
 23    Slab object for bids
 24    """
 25    slab_asks: Slab
 26    """
 27    Slab object for asks
 28    """
 29    slab_bids_pubkey: PublicKey
 30    """
 31    Public key of the account containing the bids
 32    """
 33    slab_asks_pubkey: PublicKey
 34    """
 35    Public key of the account containing the asks
 36    """
 37    decimals: int
 38    """
 39    Decimal precision for orderbook
 40    """
 41    is_inverted: bool
 42    """
 43    Whether the bids and asks should be interpretted as inverted when parsing the data. (Used in the case of the second outcome in a two-outcome market.)
 44    """
 45
 46    def __init__(
 47        self, 
 48        pubkey: PublicKey, 
 49        slab_bids: Slab,
 50        slab_asks: Slab,
 51        slab_bids_pubkey: PublicKey,
 52        slab_asks_pubkey: PublicKey,
 53        decimals: int,
 54        is_inverted: bool = False
 55        ):
 56        """
 57        Initialise an Orderbook object. Do not use this function; use Orderbook.load() instead
 58
 59        Args:
 60            pubkey (PublicKey): Orderbook public key
 61            slab_bids (Slab): Slab object for bids
 62            slab_asks (Slab): Slab object for asks
 63            slab_bids_pubkey (PublicKey): Slab bids public key
 64            slab_asks_pubkey (PublicKey): Slab asks public key
 65            decimals (int): Decimal precision for orderbook
 66            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
 67        """
 68        self.decimals = decimals
 69        self.pubkey = pubkey
 70        self.slab_bids = slab_bids
 71        self.slab_asks = slab_asks
 72        self.slab_bids_pubkey = slab_bids_pubkey
 73        self.slab_asks_pubkey = slab_asks_pubkey
 74        self.is_inverted = is_inverted
 75    
 76    @staticmethod
 77    async def load(
 78        conn: AsyncClient, 
 79        slab_bids_pubkey: PublicKey, 
 80        slab_asks_pubkey: PublicKey, 
 81        orderbook_pubkey: PublicKey, 
 82        decimals: int, 
 83        is_inverted: bool = False
 84        ):
 85        """
 86        Initialise an Orderbook object
 87
 88        Parameters are found in MarketStoreStates' --> OrderbookAccounts
 89
 90        Args:
 91            conn (AsyncClient): Solana AsyncClient object
 92            slab_bids_pubkey (PublicKey): Slab bids public key
 93            slab_asks_pubkey (PublicKey): Slab asks public key
 94            orderbook_pubkey (PublicKey): Orderbook public key
 95            decimals (int): Decimal precision for orderbook
 96            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
 97
 98        Returns:
 99            Orderbook: Orderbook object
100        """
101
102        slab_bids = await Orderbook.load_slab(conn, slab_bids_pubkey)
103        slab_asks  = await Orderbook.load_slab(conn, slab_asks_pubkey)
104
105        return Orderbook(
106            pubkey=orderbook_pubkey, 
107            slab_bids=slab_bids, 
108            slab_asks=slab_asks, 
109            slab_asks_pubkey=slab_asks_pubkey, 
110            slab_bids_pubkey=slab_bids_pubkey, 
111            decimals=decimals,
112            is_inverted=is_inverted
113            )
114
115    @staticmethod
116    async def load_slab(conn: AsyncClient, slab_address: PublicKey):
117        """
118        Loads onchain data for a Slab (contains orders for a particular side of the orderbook)
119
120        Args:
121            conn (AsyncClient): Solana AsyncClient object
122            slab_address (PublicKey): Slab public key
123
124        Returns:
125            Slab: Slab object
126        """
127        data = await load_bytes_data(conn, slab_address)
128        return Slab.from_bytes(data)
129
130    @staticmethod
131    async def load_multiple_slabs(conn: AsyncClient, slab_addresses: list[PublicKey]):
132        """
133        Loads onchain data for multiple Slabs (contains orders for a particular side of the orderbook)
134
135        Args:
136            conn (AsyncClient): Solana AsyncClient object
137            slab_addresses (list[PublicKey]): List of slab public keys
138
139        Returns:
140            list[Slab]: List of Slab objects
141        """
142        data = await load_multiple_bytes_data(conn, slab_addresses)
143        slabs = []
144        for d in data:
145            slabs.append(Slab.from_bytes(d))
146        return slabs
147
148    def invert(self):
149        """
150        Returns a version of the orderbook which has been parsed with bids and asks swtiched and
151        prices inverted. (Used for the second outcome in a two-outcome market)
152
153        Returns:
154            Orderbook: Orderbook object
155        """
156        return Orderbook(
157            self.pubkey,
158            self.slab_asks,
159            self.slab_bids,
160            self.slab_asks_pubkey,
161            self.slab_bids_pubkey,
162            self.decimals,
163            True
164        )
165    
166    
167    @staticmethod
168    def convert_price(p: Price, decimals: int):
169        """
170        Converts price to correct order of magnitude based on decimal precision
171
172        Args:
173            p (Price): Unconverted Price object
174            decimals (int): Decimal precision for orderbook
175
176        Returns:
177            Price: Price object
178        """
179        exp = 10 ** decimals
180        return Price(
181            price=p.price / exp,
182            #price=round((p.price / 2 ** 32) * exp) / exp,
183            size=p.size / exp
184        )
185    
186    @staticmethod
187    def get_L2_for_slab(
188        slab: Slab, 
189        depth: int, 
190        increasing : bool, 
191        decimals: int, 
192        ui_amount=False, 
193        is_inverted=False
194        ):
195        """
196        Get Level 2 market information for a particular slab
197
198        This contains information on orders on the slab aggregated by price tick.
199
200        Args:
201            slab (Slab): Slab object
202            depth (int): Number of orders to return
203            increasing (bool): Sort orders increasing
204            decimals (int): Decimal precision for orderbook
205            ui_amount (bool, optional): Converts prices based on decimal precision if true. Defaults to False.
206            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
207
208        Returns:
209            list[Price]: List of Price objects (size and price) corresponding to orders on the slab
210        """
211
212        l2_depth = Orderbook.__get_L2(slab, depth, decimals, increasing)
213        if(ui_amount):
214            l2_depth = [Orderbook.convert_price(p, decimals) for p in l2_depth]
215
216        if(is_inverted):
217            l2_depth = [Orderbook.invert_price(p) for p in l2_depth]
218
219        return l2_depth
220
221    @staticmethod
222    def __get_L2(slab: Slab, depth: int, decimals: int, increasing: bool):
223        """Get the Level 2 market information."""
224        # The first element of the inner list is price, the second is quantity.
225        levels: list[list[int]] = []
226        for node in slab.items(descending=not increasing):
227            price = Orderbook.__get_price_from_slab(node, decimals)
228            if len(levels) > 0 and levels[len(levels) - 1][0] == price:
229                levels[len(levels) - 1][1] += node.base_quantity
230            elif len(levels) == depth:
231                break
232            else:
233                levels.append([price, node.base_quantity])
234        return [
235            Price(
236                price=price_lots, 
237                size=size_lots
238            )
239            for price_lots, size_lots in levels
240        ]
241    
242    @staticmethod
243    def __get_L3(slab: Slab, decimals: int, increasing: bool, is_inverted: bool):
244        """Get the Level 1 market information."""
245        # The first element of the inner list is price, the second is quantity.
246        orders: list[SlabOrder] = []
247        for node in slab.items(descending = not increasing):
248            orders += [SlabOrder(
249                id = node.key,
250                price = (10**decimals) - Orderbook.__get_price_from_slab(node, decimals) if is_inverted else Orderbook.__get_price_from_slab(node, decimals),
251                price_ui = 1 - Orderbook.__get_price_from_slab(node, decimals) * (10**-decimals) if is_inverted else Orderbook.__get_price_from_slab(node, decimals) * (10**-decimals),
252                base_quantity = node.base_quantity,
253                base_quantity_ui = node.base_quantity * (10**-decimals),
254                user_market = node.user_market,
255                fee_tier = node.fee_tier,
256            )]
257
258        return orders
259
260    @staticmethod
261    def __get_price_from_slab(node, decimals: int):
262        return float(round( ((node.key >> 64)/(2**32)) * 10**decimals))
263
264    @staticmethod
265    def invert_price(p: Price):
266        """
267        Inverts prices
268
269        This is used when inverting the second outcome in a two-outcome market.
270
271        When switching prices between bids and asks, the price is `1-p`. 
272
273        Example, a BUY on A at a (probability) price of 0.4 is equivelant to a SELL on B at a price of 0.6 (1-0.4) and vice versa.
274
275        Args:
276            p (Price): Price object
277
278        Returns:
279            Price: Price object
280        """
281        return Price(
282            price=1-p.price, 
283            size=p.size
284            )
285
286    def get_bids_L3(self):
287        """
288        Gets level 1 market information for bids.
289
290        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
291
292        Returns:
293            list[SlabOrder]: List of slab orders for bids
294        """
295        is_increasing = False
296        if(self.is_inverted):
297            is_increasing = True
298        
299        return Orderbook.__get_L3(
300            self.slab_bids,
301            self.decimals,
302            is_increasing,
303            self.is_inverted
304        )
305
306    def get_asks_L3(self):
307        """
308        Gets level 1 market information for asks
309
310        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
311
312        Returns:
313            list[SlabOrder]: List of slab orders for asks
314        """
315        is_increasing = True
316        if(self.is_inverted):
317            is_increasing = False
318        
319        return Orderbook.__get_L3(
320            self.slab_asks,
321            self.decimals,
322            is_increasing,
323            self.is_inverted
324        )
325
326
327    def get_bids_l2(self, depth: int, ui_amount: bool):
328        """
329        Gets level 1 market information for bids
330
331        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
332
333        Args:
334            depth (int): Number of orders to return
335            ui_amount (bool): Converts prices based on decimal precision if true.
336
337        Returns:
338            list[Price]: List of Price objects (size and price) corresponding to orders on the slab
339        """
340        is_increasing = False
341        if(self.is_inverted):
342            is_increasing = True
343        
344        return Orderbook.get_L2_for_slab(
345            self.slab_bids,
346            depth,
347            is_increasing,
348            self.decimals,
349            ui_amount,
350            self.is_inverted,
351        )
352
353    def get_asks_l2(self, depth: int, ui_amount: bool):
354        """
355        Gets level 1 market information for asks
356
357        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
358
359        Args:
360            depth (int): Number of orders to return
361            ui_amount (bool): Converts prices based on decimal precision if true.
362
363        Returns:
364            list[Price]: List of Price objects (size and probability price) corresponding to orders on the slab
365        """
366        is_increasing = True
367        if(self.is_inverted):
368            is_increasing = False
369        
370        return Orderbook.get_L2_for_slab(
371            self.slab_asks,
372            depth,
373            is_increasing,
374            self.decimals,
375            ui_amount,
376            self.is_inverted,
377        )
378    
379    def get_best_bid_price(self, ui_amount: bool):
380        """
381        Gets the best bid price
382
383        Args:
384            ui_amount (bool):  Converts prices based on decimal precision if true.
385
386        Returns:
387            Price: Price object (size and price)
388        """
389        bids = self.get_bids_l2(1, ui_amount)
390        if(bids is not None and len(bids) > 0):
391            return bids[0]
392        return None
393
394    def get_best_ask_price(self, ui_amount: bool):
395        """
396        Gets the best ask price
397
398        Args:
399            ui_amount (bool):  Converts prices based on decimal precision if true.
400
401        Returns:
402            Price: Price object (size and price)
403        """
404        asks = self.get_asks_l2(1, ui_amount)
405        if(asks is not None and len(asks) > 0):
406            return asks[0]
407        return None
408    
409    def get_bid_price_by_order_id(self, order_id: int):
410        """
411        Gets bid Price object by order_id
412
413        Args:
414            order_id (int): Order ID
415
416        Returns:
417            Price: Price object (size and price)
418        """
419        bid = self.slab_bids.get(order_id)
420        if(bid is None):
421            return None
422        
423        bid_price = Price(price=bid.key >> 64, size=bid.base_quantity)
424        bid_price = Orderbook.convert_price(bid_price, self.decimals)
425
426        if(self.is_inverted):
427            bid_price = Orderbook.invert_price(bid_price)
428        
429        return bid_price
430
431    def get_ask_price_by_order_id(self, order_id: int):
432        """
433        Gets ask Price object by order_id
434
435        Args:
436            order_id (int): Order ID
437
438        Returns:
439            Price: Price object (size and price)
440        """
441        ask = self.slab_asks.get(order_id)
442        if(ask is None):
443            return None
444        
445        ask_price = Price(price=ask.key >> 64, size=ask.base_quantity)
446        ask_price = Orderbook.convert_price(ask_price, self.decimals)
447
448        if(self.is_inverted):
449            ask_price = Orderbook.invert_price(ask_price)
450        
451        return ask_price
452    
453    def estimate_avg_fill_for_base_qty(self, base_qty: int, side: Side, ui_amount: bool):
454        """
455        Gets estimate of average fill price (probability format) given a base/payout quantity
456
457        Args:
458            base_qty (int): Base quantity
459            side (Side): Side object (bid or ask)
460            ui_amount (bool): Converts prices based on decimal precision if true.
461
462        Returns:
463            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
464        """
465        return self.__estimate_fill_for_qty(base_qty, side, False, ui_amount)
466
467    def estimate_avg_fill_for_quote_qty(self, quote_qty: int, side: Side, ui_amount: bool):
468        """
469        Gets estimate of average fill price (probability format) given a stake/quote quantity
470
471        Args:
472            quote_qty (int): Base quantity
473            side (Side): Side object (bid or ask)
474            ui_amount (bool): Converts prices based on decimal precision if true.
475
476        Returns:
477            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
478        """
479        return self.__estimate_fill_for_qty(quote_qty, side, True, ui_amount)
480
481    def __estimate_fill_for_qty(self, qty: int, side: Side, quote: bool, ui_amount: bool):
482        """
483        _summary_
484
485        Args:
486            qty (int): Quanity
487            side (Side): Side object (bid or ask)
488            quote (bool): Quote quantity if true. Base quantity if false.
489            ui_amount (bool): Converts prices based on decimal precision if true.
490
491        Returns:
492            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
493        """
494        if(side == Side.BUY):
495            prices = self.get_bids_l2(100, ui_amount)
496        elif(side == Side.SELL):
497            prices = self.get_asks_l2(100, ui_amount)
498        
499        if(quote):
500            accumulator = lambda p: p.size
501        else:
502            accumulator = lambda p: p.size * p.price
503        
504        new_prices: list[Price] = []
505        cumulative_qty = 0
506        for price in prices:
507            remaining_qty = qty - cumulative_qty
508            if(remaining_qty <= accumulator(price)):
509                cumulative_qty += remaining_qty
510                new_size = remaining_qty if quote else remaining_qty/price.price
511                new_prices.append(Price(price=price.price, size=new_size))
512                break
513            else:
514                cumulative_qty += accumulator(price)
515                new_prices.append(price)
516        
517        return {
518            'avg_price': Orderbook.weighted_average(
519                nums=[p.price for p in new_prices],
520                weights=[p.size for p in new_prices]
521            ),
522            'worst_price': new_prices[-1].price,
523            'filled': cumulative_qty
524        }
525
526    @staticmethod
527    def weighted_average(nums, weights):
528        """
529        Calculates weighted average
530
531        Args:
532            nums (list[float]): List of values
533            weights (list[float]): List of weights
534
535        Returns:
536            float: Weighted average
537        """
538        sum = 0
539        weight_sum = 0
540
541        assert len(nums) == len(weights), 'Number of weights and nums do not correspond'
542
543        for i, num in enumerate(nums):
544            weight = weights[i]
545            sum += num * weight
546            weight_sum += weight
547        
548        return sum / weight_sum

Orderbook object

Contains information on open orders on both the bids and asks of a particular outcome in a market

Orderbook( pubkey: solana.publickey.PublicKey, slab_bids: pyaver.slab.Slab, slab_asks: pyaver.slab.Slab, slab_bids_pubkey: solana.publickey.PublicKey, slab_asks_pubkey: solana.publickey.PublicKey, decimals: int, is_inverted: bool = False)
46    def __init__(
47        self, 
48        pubkey: PublicKey, 
49        slab_bids: Slab,
50        slab_asks: Slab,
51        slab_bids_pubkey: PublicKey,
52        slab_asks_pubkey: PublicKey,
53        decimals: int,
54        is_inverted: bool = False
55        ):
56        """
57        Initialise an Orderbook object. Do not use this function; use Orderbook.load() instead
58
59        Args:
60            pubkey (PublicKey): Orderbook public key
61            slab_bids (Slab): Slab object for bids
62            slab_asks (Slab): Slab object for asks
63            slab_bids_pubkey (PublicKey): Slab bids public key
64            slab_asks_pubkey (PublicKey): Slab asks public key
65            decimals (int): Decimal precision for orderbook
66            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
67        """
68        self.decimals = decimals
69        self.pubkey = pubkey
70        self.slab_bids = slab_bids
71        self.slab_asks = slab_asks
72        self.slab_bids_pubkey = slab_bids_pubkey
73        self.slab_asks_pubkey = slab_asks_pubkey
74        self.is_inverted = is_inverted

Initialise an Orderbook object. Do not use this function; use Orderbook.load() instead

Args
  • pubkey (PublicKey): Orderbook public key
  • slab_bids (Slab): Slab object for bids
  • slab_asks (Slab): Slab object for asks
  • slab_bids_pubkey (PublicKey): Slab bids public key
  • slab_asks_pubkey (PublicKey): Slab asks public key
  • decimals (int): Decimal precision for orderbook
  • is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
pubkey: solana.publickey.PublicKey

Orderbook public key

slab_bids: pyaver.slab.Slab

Slab object for bids

slab_asks: pyaver.slab.Slab

Slab object for asks

slab_bids_pubkey: solana.publickey.PublicKey

Public key of the account containing the bids

slab_asks_pubkey: solana.publickey.PublicKey

Public key of the account containing the asks

decimals: int

Decimal precision for orderbook

is_inverted: bool

Whether the bids and asks should be interpretted as inverted when parsing the data. (Used in the case of the second outcome in a two-outcome market.)

@staticmethod
async def load( conn: solana.rpc.async_api.AsyncClient, slab_bids_pubkey: solana.publickey.PublicKey, slab_asks_pubkey: solana.publickey.PublicKey, orderbook_pubkey: solana.publickey.PublicKey, decimals: int, is_inverted: bool = False)
 76    @staticmethod
 77    async def load(
 78        conn: AsyncClient, 
 79        slab_bids_pubkey: PublicKey, 
 80        slab_asks_pubkey: PublicKey, 
 81        orderbook_pubkey: PublicKey, 
 82        decimals: int, 
 83        is_inverted: bool = False
 84        ):
 85        """
 86        Initialise an Orderbook object
 87
 88        Parameters are found in MarketStoreStates' --> OrderbookAccounts
 89
 90        Args:
 91            conn (AsyncClient): Solana AsyncClient object
 92            slab_bids_pubkey (PublicKey): Slab bids public key
 93            slab_asks_pubkey (PublicKey): Slab asks public key
 94            orderbook_pubkey (PublicKey): Orderbook public key
 95            decimals (int): Decimal precision for orderbook
 96            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
 97
 98        Returns:
 99            Orderbook: Orderbook object
100        """
101
102        slab_bids = await Orderbook.load_slab(conn, slab_bids_pubkey)
103        slab_asks  = await Orderbook.load_slab(conn, slab_asks_pubkey)
104
105        return Orderbook(
106            pubkey=orderbook_pubkey, 
107            slab_bids=slab_bids, 
108            slab_asks=slab_asks, 
109            slab_asks_pubkey=slab_asks_pubkey, 
110            slab_bids_pubkey=slab_bids_pubkey, 
111            decimals=decimals,
112            is_inverted=is_inverted
113            )

Initialise an Orderbook object

Parameters are found in MarketStoreStates' --> OrderbookAccounts

Args
  • conn (AsyncClient): Solana AsyncClient object
  • slab_bids_pubkey (PublicKey): Slab bids public key
  • slab_asks_pubkey (PublicKey): Slab asks public key
  • orderbook_pubkey (PublicKey): Orderbook public key
  • decimals (int): Decimal precision for orderbook
  • is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
Returns

Orderbook: Orderbook object

@staticmethod
async def load_slab( conn: solana.rpc.async_api.AsyncClient, slab_address: solana.publickey.PublicKey)
115    @staticmethod
116    async def load_slab(conn: AsyncClient, slab_address: PublicKey):
117        """
118        Loads onchain data for a Slab (contains orders for a particular side of the orderbook)
119
120        Args:
121            conn (AsyncClient): Solana AsyncClient object
122            slab_address (PublicKey): Slab public key
123
124        Returns:
125            Slab: Slab object
126        """
127        data = await load_bytes_data(conn, slab_address)
128        return Slab.from_bytes(data)

Loads onchain data for a Slab (contains orders for a particular side of the orderbook)

Args
  • conn (AsyncClient): Solana AsyncClient object
  • slab_address (PublicKey): Slab public key
Returns

Slab: Slab object

@staticmethod
async def load_multiple_slabs( conn: solana.rpc.async_api.AsyncClient, slab_addresses: list[solana.publickey.PublicKey])
130    @staticmethod
131    async def load_multiple_slabs(conn: AsyncClient, slab_addresses: list[PublicKey]):
132        """
133        Loads onchain data for multiple Slabs (contains orders for a particular side of the orderbook)
134
135        Args:
136            conn (AsyncClient): Solana AsyncClient object
137            slab_addresses (list[PublicKey]): List of slab public keys
138
139        Returns:
140            list[Slab]: List of Slab objects
141        """
142        data = await load_multiple_bytes_data(conn, slab_addresses)
143        slabs = []
144        for d in data:
145            slabs.append(Slab.from_bytes(d))
146        return slabs

Loads onchain data for multiple Slabs (contains orders for a particular side of the orderbook)

Args
  • conn (AsyncClient): Solana AsyncClient object
  • slab_addresses (list[PublicKey]): List of slab public keys
Returns

list[Slab]: List of Slab objects

def invert(self)
148    def invert(self):
149        """
150        Returns a version of the orderbook which has been parsed with bids and asks swtiched and
151        prices inverted. (Used for the second outcome in a two-outcome market)
152
153        Returns:
154            Orderbook: Orderbook object
155        """
156        return Orderbook(
157            self.pubkey,
158            self.slab_asks,
159            self.slab_bids,
160            self.slab_asks_pubkey,
161            self.slab_bids_pubkey,
162            self.decimals,
163            True
164        )

Returns a version of the orderbook which has been parsed with bids and asks swtiched and prices inverted. (Used for the second outcome in a two-outcome market)

Returns

Orderbook: Orderbook object

@staticmethod
def convert_price(p: pyaver.data_classes.Price, decimals: int)
167    @staticmethod
168    def convert_price(p: Price, decimals: int):
169        """
170        Converts price to correct order of magnitude based on decimal precision
171
172        Args:
173            p (Price): Unconverted Price object
174            decimals (int): Decimal precision for orderbook
175
176        Returns:
177            Price: Price object
178        """
179        exp = 10 ** decimals
180        return Price(
181            price=p.price / exp,
182            #price=round((p.price / 2 ** 32) * exp) / exp,
183            size=p.size / exp
184        )

Converts price to correct order of magnitude based on decimal precision

Args
  • p (Price): Unconverted Price object
  • decimals (int): Decimal precision for orderbook
Returns

Price: Price object

@staticmethod
def get_L2_for_slab( slab: pyaver.slab.Slab, depth: int, increasing: bool, decimals: int, ui_amount=False, is_inverted=False)
186    @staticmethod
187    def get_L2_for_slab(
188        slab: Slab, 
189        depth: int, 
190        increasing : bool, 
191        decimals: int, 
192        ui_amount=False, 
193        is_inverted=False
194        ):
195        """
196        Get Level 2 market information for a particular slab
197
198        This contains information on orders on the slab aggregated by price tick.
199
200        Args:
201            slab (Slab): Slab object
202            depth (int): Number of orders to return
203            increasing (bool): Sort orders increasing
204            decimals (int): Decimal precision for orderbook
205            ui_amount (bool, optional): Converts prices based on decimal precision if true. Defaults to False.
206            is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
207
208        Returns:
209            list[Price]: List of Price objects (size and price) corresponding to orders on the slab
210        """
211
212        l2_depth = Orderbook.__get_L2(slab, depth, decimals, increasing)
213        if(ui_amount):
214            l2_depth = [Orderbook.convert_price(p, decimals) for p in l2_depth]
215
216        if(is_inverted):
217            l2_depth = [Orderbook.invert_price(p) for p in l2_depth]
218
219        return l2_depth

Get Level 2 market information for a particular slab

This contains information on orders on the slab aggregated by price tick.

Args
  • slab (Slab): Slab object
  • depth (int): Number of orders to return
  • increasing (bool): Sort orders increasing
  • decimals (int): Decimal precision for orderbook
  • ui_amount (bool, optional): Converts prices based on decimal precision if true. Defaults to False.
  • is_inverted (bool, optional): Whether the bids and asks have been switched with each other. Defaults to False.
Returns

list[Price]: List of Price objects (size and price) corresponding to orders on the slab

@staticmethod
def invert_price(p: pyaver.data_classes.Price)
264    @staticmethod
265    def invert_price(p: Price):
266        """
267        Inverts prices
268
269        This is used when inverting the second outcome in a two-outcome market.
270
271        When switching prices between bids and asks, the price is `1-p`. 
272
273        Example, a BUY on A at a (probability) price of 0.4 is equivelant to a SELL on B at a price of 0.6 (1-0.4) and vice versa.
274
275        Args:
276            p (Price): Price object
277
278        Returns:
279            Price: Price object
280        """
281        return Price(
282            price=1-p.price, 
283            size=p.size
284            )

Inverts prices

This is used when inverting the second outcome in a two-outcome market.

When switching prices between bids and asks, the price is 1-p.

Example, a BUY on A at a (probability) price of 0.4 is equivelant to a SELL on B at a price of 0.6 (1-0.4) and vice versa.

Args
  • p (Price): Price object
Returns

Price: Price object

def get_bids_L3(self)
286    def get_bids_L3(self):
287        """
288        Gets level 1 market information for bids.
289
290        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
291
292        Returns:
293            list[SlabOrder]: List of slab orders for bids
294        """
295        is_increasing = False
296        if(self.is_inverted):
297            is_increasing = True
298        
299        return Orderbook.__get_L3(
300            self.slab_bids,
301            self.decimals,
302            is_increasing,
303            self.is_inverted
304        )

Gets level 1 market information for bids.

See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information

Returns

list[SlabOrder]: List of slab orders for bids

def get_asks_L3(self)
306    def get_asks_L3(self):
307        """
308        Gets level 1 market information for asks
309
310        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
311
312        Returns:
313            list[SlabOrder]: List of slab orders for asks
314        """
315        is_increasing = True
316        if(self.is_inverted):
317            is_increasing = False
318        
319        return Orderbook.__get_L3(
320            self.slab_asks,
321            self.decimals,
322            is_increasing,
323            self.is_inverted
324        )

Gets level 1 market information for asks

See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information

Returns

list[SlabOrder]: List of slab orders for asks

def get_bids_l2(self, depth: int, ui_amount: bool)
327    def get_bids_l2(self, depth: int, ui_amount: bool):
328        """
329        Gets level 1 market information for bids
330
331        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
332
333        Args:
334            depth (int): Number of orders to return
335            ui_amount (bool): Converts prices based on decimal precision if true.
336
337        Returns:
338            list[Price]: List of Price objects (size and price) corresponding to orders on the slab
339        """
340        is_increasing = False
341        if(self.is_inverted):
342            is_increasing = True
343        
344        return Orderbook.get_L2_for_slab(
345            self.slab_bids,
346            depth,
347            is_increasing,
348            self.decimals,
349            ui_amount,
350            self.is_inverted,
351        )

Gets level 1 market information for bids

See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information

Args
  • depth (int): Number of orders to return
  • ui_amount (bool): Converts prices based on decimal precision if true.
Returns

list[Price]: List of Price objects (size and price) corresponding to orders on the slab

def get_asks_l2(self, depth: int, ui_amount: bool)
353    def get_asks_l2(self, depth: int, ui_amount: bool):
354        """
355        Gets level 1 market information for asks
356
357        See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information
358
359        Args:
360            depth (int): Number of orders to return
361            ui_amount (bool): Converts prices based on decimal precision if true.
362
363        Returns:
364            list[Price]: List of Price objects (size and probability price) corresponding to orders on the slab
365        """
366        is_increasing = True
367        if(self.is_inverted):
368            is_increasing = False
369        
370        return Orderbook.get_L2_for_slab(
371            self.slab_asks,
372            depth,
373            is_increasing,
374            self.decimals,
375            ui_amount,
376            self.is_inverted,
377        )

Gets level 1 market information for asks

See https://www.thebalance.com/order-book-level-2-market-data-and-depth-of-market-1031118 for more information

Args
  • depth (int): Number of orders to return
  • ui_amount (bool): Converts prices based on decimal precision if true.
Returns

list[Price]: List of Price objects (size and probability price) corresponding to orders on the slab

def get_best_bid_price(self, ui_amount: bool)
379    def get_best_bid_price(self, ui_amount: bool):
380        """
381        Gets the best bid price
382
383        Args:
384            ui_amount (bool):  Converts prices based on decimal precision if true.
385
386        Returns:
387            Price: Price object (size and price)
388        """
389        bids = self.get_bids_l2(1, ui_amount)
390        if(bids is not None and len(bids) > 0):
391            return bids[0]
392        return None

Gets the best bid price

Args
  • ui_amount (bool): Converts prices based on decimal precision if true.
Returns

Price: Price object (size and price)

def get_best_ask_price(self, ui_amount: bool)
394    def get_best_ask_price(self, ui_amount: bool):
395        """
396        Gets the best ask price
397
398        Args:
399            ui_amount (bool):  Converts prices based on decimal precision if true.
400
401        Returns:
402            Price: Price object (size and price)
403        """
404        asks = self.get_asks_l2(1, ui_amount)
405        if(asks is not None and len(asks) > 0):
406            return asks[0]
407        return None

Gets the best ask price

Args
  • ui_amount (bool): Converts prices based on decimal precision if true.
Returns

Price: Price object (size and price)

def get_bid_price_by_order_id(self, order_id: int)
409    def get_bid_price_by_order_id(self, order_id: int):
410        """
411        Gets bid Price object by order_id
412
413        Args:
414            order_id (int): Order ID
415
416        Returns:
417            Price: Price object (size and price)
418        """
419        bid = self.slab_bids.get(order_id)
420        if(bid is None):
421            return None
422        
423        bid_price = Price(price=bid.key >> 64, size=bid.base_quantity)
424        bid_price = Orderbook.convert_price(bid_price, self.decimals)
425
426        if(self.is_inverted):
427            bid_price = Orderbook.invert_price(bid_price)
428        
429        return bid_price

Gets bid Price object by order_id

Args
  • order_id (int): Order ID
Returns

Price: Price object (size and price)

def get_ask_price_by_order_id(self, order_id: int)
431    def get_ask_price_by_order_id(self, order_id: int):
432        """
433        Gets ask Price object by order_id
434
435        Args:
436            order_id (int): Order ID
437
438        Returns:
439            Price: Price object (size and price)
440        """
441        ask = self.slab_asks.get(order_id)
442        if(ask is None):
443            return None
444        
445        ask_price = Price(price=ask.key >> 64, size=ask.base_quantity)
446        ask_price = Orderbook.convert_price(ask_price, self.decimals)
447
448        if(self.is_inverted):
449            ask_price = Orderbook.invert_price(ask_price)
450        
451        return ask_price

Gets ask Price object by order_id

Args
  • order_id (int): Order ID
Returns

Price: Price object (size and price)

def estimate_avg_fill_for_base_qty(self, base_qty: int, side: pyaver.enums.Side, ui_amount: bool)
453    def estimate_avg_fill_for_base_qty(self, base_qty: int, side: Side, ui_amount: bool):
454        """
455        Gets estimate of average fill price (probability format) given a base/payout quantity
456
457        Args:
458            base_qty (int): Base quantity
459            side (Side): Side object (bid or ask)
460            ui_amount (bool): Converts prices based on decimal precision if true.
461
462        Returns:
463            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
464        """
465        return self.__estimate_fill_for_qty(base_qty, side, False, ui_amount)

Gets estimate of average fill price (probability format) given a base/payout quantity

Args
  • base_qty (int): Base quantity
  • side (Side): Side object (bid or ask)
  • ui_amount (bool): Converts prices based on decimal precision if true.
Returns

dict[str, float]: Dictionary containing avg_price, worst_price, filled

def estimate_avg_fill_for_quote_qty(self, quote_qty: int, side: pyaver.enums.Side, ui_amount: bool)
467    def estimate_avg_fill_for_quote_qty(self, quote_qty: int, side: Side, ui_amount: bool):
468        """
469        Gets estimate of average fill price (probability format) given a stake/quote quantity
470
471        Args:
472            quote_qty (int): Base quantity
473            side (Side): Side object (bid or ask)
474            ui_amount (bool): Converts prices based on decimal precision if true.
475
476        Returns:
477            dict[str, float]: Dictionary containing `avg_price`, `worst_price`, `filled`
478        """
479        return self.__estimate_fill_for_qty(quote_qty, side, True, ui_amount)

Gets estimate of average fill price (probability format) given a stake/quote quantity

Args
  • quote_qty (int): Base quantity
  • side (Side): Side object (bid or ask)
  • ui_amount (bool): Converts prices based on decimal precision if true.
Returns

dict[str, float]: Dictionary containing avg_price, worst_price, filled

@staticmethod
def weighted_average(nums, weights)
526    @staticmethod
527    def weighted_average(nums, weights):
528        """
529        Calculates weighted average
530
531        Args:
532            nums (list[float]): List of values
533            weights (list[float]): List of weights
534
535        Returns:
536            float: Weighted average
537        """
538        sum = 0
539        weight_sum = 0
540
541        assert len(nums) == len(weights), 'Number of weights and nums do not correspond'
542
543        for i, num in enumerate(nums):
544            weight = weights[i]
545            sum += num * weight
546            weight_sum += weight
547        
548        return sum / weight_sum

Calculates weighted average

Args
  • nums (list[float]): List of values
  • weights (list[float]): List of weights
Returns

float: Weighted average