pyaver.user_market

  1from copy import deepcopy
  2from pydash import chunk
  3from .market import AverMarket
  4from solana.publickey import PublicKey
  5from asyncio import gather
  6from .checks import *
  7from solana.transaction import AccountMeta
  8from solana.keypair import Keypair
  9from solana.system_program import SYS_PROGRAM_ID
 10from spl.token.constants import TOKEN_PROGRAM_ID
 11from anchorpy import Context
 12from .user_host_lifetime import UserHostLifetime
 13from spl.token.instructions import get_associated_token_address
 14from .aver_client import AverClient
 15from .utils import sign_and_send_transaction_instructions, load_multiple_account_states, parse_user_market_state
 16from solana.rpc.types import TxOpts
 17from solana.rpc.commitment import Finalized
 18from .data_classes import UserHostLifetimeState, UserMarketState, UserBalanceState
 19from .constants import AVER_PROGRAM_ID, AVER_HOST_ACCOUNT, CANCEL_ALL_ORDERS_INSTRUCTION_CHUNK_SIZE
 20from .enums import OrderType, SelfTradeBehavior, Side, SizeFormat
 21import math
 22
 23class UserMarket():
 24    """
 25    Contains data on a user's orders on a particular market (for a particular host)
 26    """
 27
 28    aver_client: AverClient
 29    """
 30    AverClient object
 31    """
 32    pubkey: PublicKey
 33    """
 34    UserMarket public key
 35    """
 36    market: AverMarket
 37    """
 38    Corresponding Market object
 39    """
 40    user_market_state: UserMarketState
 41    """
 42    UserMarketState object
 43    """
 44    user_balance_state: UserBalanceState
 45    """
 46    UserBalanceState object
 47    """
 48    user_host_lifetime: UserHostLifetime
 49    """
 50    UserHostLifetime object
 51    """
 52
 53
 54    def __init__(self, aver_client: AverClient, pubkey: PublicKey, user_market_state: UserMarketState, market: AverMarket, user_balance_state: UserBalanceState, user_host_lifetime: UserHostLifetime):
 55        """
 56         Initialise an UserMarket object. Do not use this function; use UserMarket.load() instead
 57
 58        Args:
 59            aver_client (AverClient): AverClient object
 60            pubkey (PublicKey): UserMarket public key
 61            user_market_state (UserMarketState): UserMarketState object
 62            market (AverMarket): Market object
 63            user_balance_state (UserBalanceState): UserBalanceState object
 64            user_host_lifetime (UserHostLifetime): UserHostLifetime object
 65        """
 66        self.user_market_state = user_market_state
 67        self.pubkey = pubkey
 68        self.aver_client = aver_client
 69        self.market = market
 70        self.user_balance_state = user_balance_state
 71        self.user_host_lifetime = user_host_lifetime
 72
 73    @staticmethod
 74    async def load(
 75            aver_client: AverClient, 
 76            market: AverMarket, 
 77            owner: PublicKey, 
 78            host: PublicKey = AVER_HOST_ACCOUNT,
 79            program_id: PublicKey = AVER_PROGRAM_ID
 80        ):
 81            """
 82            Initialises an UserMarket object from Market, Host and Owner public keys
 83
 84            To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()
 85
 86            Args:
 87                aver_client (AverClient): AverClient object
 88                market (AverMarket): Corresponding Market object
 89                owner (PublicKey): Owner of UserMarket account
 90                host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
 91                program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
 92
 93            Returns:
 94                UserMarket: UserMarket object
 95            """
 96            uma, bump = UserMarket.derive_pubkey_and_bump(owner, market.market_pubkey, host, program_id)
 97            uhl = UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)[0]
 98            return await UserMarket.load_by_uma(aver_client, uma, market, uhl)
 99
100    @staticmethod
101    async def load_multiple(
102            aver_client: AverClient, 
103            markets: list[AverMarket], 
104            owners: list[PublicKey], 
105            host: PublicKey = AVER_HOST_ACCOUNT,
106            program_id: PublicKey = AVER_PROGRAM_ID,
107        ):
108            """
109            Initialises multiple UserMarket objects from Market, Host and Owner public keys
110
111            This method is more highly optimized and faster than using UserMarket.load() multiple times.
112
113            To refresh data on already loaded UserMarkets use src.refresh.refresh_multiple_user_markets()
114
115            Args:
116                aver_client (AverClient): AverClient object
117                markets (list[AverMarket]): List of corresponding AverMarket objects (in correct order)
118                owner (PublicKey): List of owners of UserMarket account
119                host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
120                program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
121
122            Returns:
123                list[UserMarket]: List of UserMarket objects
124            """
125            umas = []
126            for i, m in enumerate(markets):
127                umas.append(UserMarket.derive_pubkey_and_bump(owners[i], m.market_pubkey, host, program_id)[0])
128            uhls = [UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)[0] for owner in owners]
129            return await UserMarket.load_multiple_by_uma(aver_client, umas, markets, uhls)
130
131    @staticmethod
132    async def load_by_uma(
133            aver_client: AverClient,
134            pubkey: PublicKey,
135            market: (AverMarket or PublicKey),
136            uhl: PublicKey
137        ):
138        """
139        Initialises an UserMarket object from UserMarket account public key
140
141        To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()
142
143        Args:
144            aver_client (AverClient): AverClient object
145            pubkey (PublicKey): UserMarket account public key
146            market (AverMarket or PublicKey): AverMarket object or AverMarket public key
147            uhl (PublicKey): UserHostLifetime account
148
149        Returns:
150            UserMarket: UserMarket object
151        """
152        res: UserMarketState = await aver_client.program.account['UserMarket'].fetch(pubkey)
153        uhl = await UserHostLifetime.load(aver_client, uhl)
154
155        lamport_balance = await aver_client.request_lamport_balance(res.user)
156        token_balance = await aver_client.request_token_balance(aver_client.quote_token, res.user)
157        user_balance_state = UserBalanceState(lamport_balance, token_balance)
158
159        if(isinstance(market, PublicKey)):
160            market = await AverMarket.load(aver_client, market)
161
162        if(res.market.to_base58() != market.market_pubkey.to_base58()):
163            raise Exception('UserMarket and Market do not match')
164
165        return UserMarket(aver_client, pubkey, res, market, user_balance_state, uhl)   
166    
167    @staticmethod
168    async def load_multiple_by_uma(
169            aver_client: AverClient,
170            pubkeys: list[PublicKey],
171            markets: list[AverMarket],
172            uhls: list[PublicKey]
173        ):
174        """
175        Initialises an multiple UserMarket objects from a list of UserMarket account public keys
176
177        To refresh data on an already loaded UserMarket use src.refresh.refresh_user_markets()
178
179        Args:
180            aver_client (AverClient): AverClient object 
181            pubkeys (list[PublicKey]): List of UserMarket account public keys
182            markets (list[AverMarket]): List of AverMarket objects
183            uhls (list[PublicKey]): List of UserHostLifetime  account public keys
184
185        Raises:
186            Exception: UserMarket and market do not match
187
188        Returns:
189            list[UserMarket]: List of UserMarket objects
190        """
191
192        res: list[UserMarketState] = await aver_client.program.account['UserMarket'].fetch_multiple(pubkeys)
193        uhls = await UserHostLifetime.load_multiple(aver_client, uhls)
194
195        user_pubkeys = [u.user for u in res]
196        user_balances = (await load_multiple_account_states(aver_client, [], [], [], [], user_pubkeys))['user_balance_states']
197
198        umas: list[UserMarket] = []
199        for i, pubkey in enumerate(pubkeys):
200            if(res[i].market.to_base58() != markets[i].market_pubkey.to_base58()):
201                raise Exception('UserMarket and Market do not match')
202            umas.append(UserMarket(aver_client, pubkey, res[i], markets[i], user_balances[i], uhls[i]))
203        return umas
204    
205    @staticmethod
206    def get_user_markets_from_account_state(
207            aver_client: AverClient, 
208            pubkeys: list[PublicKey], 
209            user_market_states: list[UserMarketState],
210            aver_markets: list[AverMarket],
211            user_balance_states: list[UserBalanceState],
212            user_host_lifetime_states: list[UserHostLifetimeState],
213            user_host_lifetime_pubkeys: list[PublicKey]
214        ):
215        """
216        Returns multiple UserMarket objects from their respective MarketStates, stores and orderbook objects
217
218        Used in refresh.py
219
220        Args:
221            aver_client (AverClient): AverClient object
222            pubkeys (list[PublicKey]): List of UserMarket account pubkeys
223            user_market_states (list[UserMarketState]): List of UserMarketState objects
224            aver_markets (list[AverMarket]): List of AverMarket objects
225            user_balance_states (list[UserBalanceState]): List of UserBalanceState objects
226            user_host_lifetime_states: (List[UserHostLifetimeState]): List of UserHostLifetimeState objects
227            user_host_lifetime_pubkeys (list[PublicKey]): List of UserHostLifetime public keys
228
229        Returns:
230            list[UserMarket]: List of UserMarket objects
231        """
232        user_markets: list[UserMarket] = []
233        for i, pubkey in enumerate(pubkeys):
234            user_market = UserMarket(aver_client, pubkey, user_market_states[i], aver_markets[i], user_balance_states[i], UserHostLifetime(user_host_lifetime_pubkeys[i],user_host_lifetime_states[i]))
235            user_markets.append(user_market)
236        return user_markets
237    
238    
239    @staticmethod
240    def parse_multiple_user_market_state(
241            buffer: list[bytes],
242            aver_client: AverMarket
243        ):
244        """
245        Parses raw onchain data to UserMarketState objects    
246
247        Args:
248            buffer (list[bytes]): List of raw bytes coming from onchain
249            aver_client (AverMarket): AverClient object
250
251        Returns:
252            list[UserMarketState]: List of UserMarketState objects
253        """
254        return [parse_user_market_state(b, aver_client) for b in buffer]
255
256    @staticmethod
257    def derive_pubkey_and_bump(
258            owner: PublicKey,
259            market: PublicKey,
260            host: PublicKey,
261            program_id: PublicKey = AVER_PROGRAM_ID
262        ) -> PublicKey:
263        """
264        Derives PDA (Program Derived Account) for UserMarket public key given a user, host and market
265
266        Args:
267            owner (PublicKey): Owner of UserMarket account
268            market (PublicKey): Corresponding Market account public key
269            host (PublicKey): Host public key
270            program_id (PublicKey, optional): _description_. Defaults to AVER_PROGRAM_ID.
271
272        Returns:
273            PublicKey: UserMarket account public key
274        """
275
276        return PublicKey.find_program_address(
277            [bytes('user-market', 'utf-8'), bytes(owner), bytes(market), bytes(host)],
278            program_id
279        )    
280
281    @staticmethod
282    def make_create_user_market_account_instruction(
283            aver_client: AverClient,
284            market: AverMarket,
285            owner: PublicKey,
286            host: PublicKey = AVER_HOST_ACCOUNT, 
287            number_of_orders: int = None,
288            program_id: PublicKey = AVER_PROGRAM_ID
289        ):
290        """
291        Creates instruction for UserMarket account creation
292
293        Returns TransactionInstruction object only. Does not send transaction.
294
295        Args:
296            aver_client (AverClient): AverClient object
297            market (AverMarket): Corresponding Market object
298            owner (PublicKey): Owner of UserMarket account
299            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
300            number_of_orders (int, optional): _description_. Defaults to 5*number of market outcomes.
301            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
302
303        Returns:
304            TransactionInstruction: TransactionInstruction object
305        """
306        if(number_of_orders is None):
307            number_of_orders = market.market_state.number_of_outcomes * 5
308        
309        uma, uma_bump = UserMarket.derive_pubkey_and_bump(owner, market.market_pubkey, host, program_id)
310        user_host_lifetime, uhl_bump = UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)
311
312        return aver_client.program.instruction['init_user_market'](
313            number_of_orders,
314            uma_bump, 
315            ctx=Context(
316                accounts={
317                    "user": owner,
318                    "user_host_lifetime": user_host_lifetime,
319                    "user_market": uma,
320                    "market": market.market_pubkey,
321                    "host": host,
322                    "system_program": SYS_PROGRAM_ID,
323                },
324            )
325        )
326
327    @staticmethod
328    async def create_user_market_account(
329            aver_client: AverClient,
330            market: AverMarket,
331            owner: Keypair = None,
332            send_options: TxOpts = None,
333            host: PublicKey = AVER_HOST_ACCOUNT,
334            number_of_orders: int = None,
335            program_id: PublicKey = AVER_PROGRAM_ID
336        ):
337        """
338        Creates UserMarket account
339
340        Sends instructions on chain
341
342        Args:
343            aver_client (AverClient): AverClient object
344            market (AverMarket): Correspondign Market object
345            owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
346            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
347            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
348            number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
349            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
350
351        Returns:
352            RPCResponse: Response
353        """
354        if(number_of_orders is None):
355            number_of_orders = 5 * market.market_state.number_of_outcomes
356
357        if(owner is None):
358            owner = aver_client.owner
359
360        ix = UserMarket.make_create_user_market_account_instruction(
361            aver_client,
362            market,
363            owner.public_key,
364            host,
365            number_of_orders,
366            program_id
367        )
368
369        if(send_options is None):
370            send_options = TxOpts()
371        else:
372            send_options = TxOpts(
373                skip_confirmation=send_options.skip_confirmation,
374                skip_preflight=send_options.skip_confirmation,
375                preflight_commitment=Finalized,
376                max_retries=send_options.max_retries)
377
378        return await sign_and_send_transaction_instructions(
379            aver_client,
380            [],
381            owner,
382            [ix],
383            send_options,
384        )
385    
386    @staticmethod
387    async def get_or_create_user_market_account(
388            client: AverClient,
389            market: AverMarket,
390            owner: Keypair = None,
391            send_options: TxOpts = None,
392            quote_token_mint: PublicKey = None,
393            host: PublicKey = AVER_HOST_ACCOUNT,
394            number_of_orders: int = None,
395            referrer: PublicKey = SYS_PROGRAM_ID,
396            discount_token: PublicKey = SYS_PROGRAM_ID,
397            program_id: PublicKey = AVER_PROGRAM_ID 
398        ):
399        """
400        Attempts to load UserMarket object or creates one if not one is not found
401
402        Args:
403            client (AverClient): AverClient object
404            market (AverMarket): Corresponding AverMarket object
405            owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
406            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
407            quote_token_mint (PublicKey, optional): ATA token mint public key. Defaults to USDC token according to chosen solana network.
408            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
409            number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
410            referrer (PublicKey, optional): Referrer account public key. Defaults to SYS_PROGRAM_ID.
411            discount_token (PublicKey, optional): _description_. Defaults to SYS_PROGRAM_ID.
412            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
413
414        Returns:
415            UserMarket: UserMarket object
416        """
417        quote_token_mint = quote_token_mint if quote_token_mint is not None else client.quote_token
418        if(number_of_orders is None):
419            number_of_orders = market.market_state.number_of_outcomes * 5
420        
421        if(owner is None):
422            owner = client.owner
423        
424        user_market_pubkey = UserMarket.derive_pubkey_and_bump(owner.public_key, market.market_pubkey, host, program_id)[0]
425        try:
426            uma = await UserMarket.load(client, market, owner.public_key, host, program_id)
427            return uma
428        except:
429            uhl = await UserHostLifetime.get_or_create_user_host_lifetime(
430                client,
431                owner,
432                send_options,
433                quote_token_mint,
434                host,
435                referrer,
436                discount_token,
437                program_id
438            )
439
440            sig = await UserMarket.create_user_market_account(
441                client,
442                market,
443                owner, 
444                send_options,
445                host,
446                number_of_orders,
447                program_id,
448            )
449
450            await client.provider.connection.confirm_transaction(
451                sig['result'],
452                commitment=Finalized
453            )
454
455            return await UserMarket.load(
456                client, 
457                market,  
458                owner.public_key,
459                host,
460                program_id)
461
462
463
464    def make_place_order_instruction(
465            self,
466            outcome_id: int,
467            side: Side,
468            limit_price: float,
469            size: float,
470            size_format: SizeFormat,
471            user_quote_token_ata: PublicKey,
472            order_type: OrderType = OrderType.LIMIT,
473            self_trade_behavior: SelfTradeBehavior = SelfTradeBehavior.CANCEL_PROVIDE,
474            active_pre_flight_check: bool = False,
475        ):
476        """
477        Creates instruction to place order.
478
479        Returns TransactionInstruction object only. Does not send transaction.
480
481        Args:
482            outcome_id (int): ID of outcome
483            side (Side): Side object (bid or ask)
484            limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
485            size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
486            size_format (SizeFormat): SizeFormat object (Stake or Payout)
487            user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
488            order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT.
489            self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE.
490            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
491
492        Raises:
493            Exception: Cannot place error on closed market
494
495        Returns:
496            TransactionInstruction: TransactionInstruction object
497        """
498        if(self.market.orderbooks is None):
499            raise Exception('Cannot place error on closed market')
500
501        if(active_pre_flight_check):
502            check_sufficient_lamport_balance(self.user_balance_state)
503            check_correct_uma_market_match(self.user_market_state, self.market)
504            check_market_active_pre_event(self.market.market_state.market_status)
505            check_uhl_self_excluded(self.user_host_lifetime)
506            check_user_market_full(self.user_market_state)
507            check_limit_price_error(limit_price, self.market)
508            check_outcome_outside_space(outcome_id, self.market)
509            check_incorrect_order_type_for_market_order(limit_price, order_type, side, self.market)
510            check_stake_noop(size_format, limit_price, side)
511            tokens_available_to_buy = self.calculate_tokens_available_to_buy(outcome_id, limit_price)
512            tokens_available_to_sell = self.calculate_tokens_available_to_sell(outcome_id, limit_price)
513            check_is_order_valid(outcome_id, side, limit_price, size, size_format, tokens_available_to_sell, tokens_available_to_buy)
514            check_quote_and_base_size_too_small(self.market, side, size_format, outcome_id, limit_price, size)
515            check_user_permission_and_quote_token_limit_exceeded(self.market, self.user_market_state, size, limit_price, size_format)
516        
517        max_base_qty = math.floor(size * (10 ** self.market.market_state.decimals))
518        limit_price_u64 = math.ceil(limit_price * (10 ** self.market.market_state.decimals))
519
520        is_binary_market_second_outcome = self.market.market_state.number_of_outcomes == 2 and outcome_id == 1
521        orderbook_account_index = outcome_id if not is_binary_market_second_outcome else 0
522        orderbook_account = self.market.market_store_state.orderbook_accounts[orderbook_account_index]
523
524        return self.aver_client.program.instruction['place_order'](
525            {
526                "limit_price": limit_price_u64,
527                "size": max_base_qty,
528                "size_format": size_format,
529                "side": side,
530                "order_type": order_type,
531                "self_trade_behavior": self_trade_behavior,
532                "outcome_id": outcome_id,
533            },
534            ctx=Context(
535                accounts={
536                    "user": self.user_market_state.user,
537                    "user_host_lifetime": self.user_market_state.user_host_lifetime,
538                    "market": self.market.market_pubkey,
539                    "market_store": self.market.market_state.market_store,
540                    "user_market": self.pubkey,
541                    "user": self.user_market_state.user,
542                    "user_quote_token_ata": user_quote_token_ata,
543                    "quote_vault": self.market.market_state.quote_vault,
544                    "orderbook": orderbook_account.orderbook,
545                    "bids": orderbook_account.bids,
546                    "asks": orderbook_account.asks,
547                    "event_queue": orderbook_account.event_queue,
548                    "spl_token_program": TOKEN_PROGRAM_ID,
549                    "system_program": SYS_PROGRAM_ID,
550               },)
551        )
552
553    async def place_order(
554            self,
555            owner: Keypair,
556            outcome_id: int,
557            side: Side,
558            limit_price: float,
559            size: float,
560            size_format: SizeFormat,
561            send_options: TxOpts = None,
562            order_type: OrderType = OrderType.LIMIT,
563            self_trade_behavior: SelfTradeBehavior = SelfTradeBehavior.CANCEL_PROVIDE,
564            active_pre_flight_check: bool = True,  
565        ):
566        """
567        Places a new order
568
569        Sends instructions on chain
570
571        Args:
572            owner (Keypair): Owner of UserMarket account. Pays transaction fees.
573            outcome_id (int): index of the outcome intended to be traded
574            side (Side): Side object (bid/back/buy or ask/lay/sell)
575            limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
576            size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
577            size_format (SizeFormat): SizeFormat object (Stake or Payout formats supported)
578            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
579            order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT. Other options include OrderType.IOC, OrderType.KILL_OR_FILL, OrderType.POST_ONLY.
580            self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE. Other options include SelfTradeBehavior.DECREMENT_TAKE and SelfTradeBehavior.ABORT_TRANSACTION.
581            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True. 
582
583        Raises:
584            Exception: Owner must be same as user market owner
585
586        Returns:
587            RPCResponse: Response
588        """
589        if(not owner.public_key == self.user_market_state.user):
590            raise Exception('Owner must be same as user market owner')
591
592        user_quote_token_ata = await self.market.aver_client.get_or_create_associated_token_account(
593            self.user_market_state.user,
594            self.market.aver_client.owner,
595            self.market.market_state.quote_token_mint
596        )
597
598        ix = self.make_place_order_instruction(
599            outcome_id,
600            side, 
601            limit_price,
602            size,
603            size_format,
604            user_quote_token_ata,
605            order_type,
606            self_trade_behavior,
607            active_pre_flight_check
608        )
609        return await sign_and_send_transaction_instructions(
610            self.aver_client,
611            [],
612            owner,
613            [ix],
614            send_options
615        )
616
617    def make_cancel_order_instruction(
618            self,
619            order_id: int,
620            outcome_id: int,
621            active_pre_flight_check: bool = False,
622        ):
623        """
624        Creates instruction for to cancel order.
625
626        Returns TransactionInstruction object only. Does not send transaction.
627
628        Args:
629            order_id (int): ID of order to cancel
630            outcome_id (int): ID of outcome
631            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
632
633        Raises:
634            Exception: Cannot cancel orders on closed market
635            Exception: Insufficient lamport balance
636            Exception: Cannot cancel orders in current market status
637            Exception: Order ID does not exist in list of open orders
638
639        Returns:
640            TransactionInstruction: TransactionInstruction object
641        """
642        if(self.market.orderbooks is None):
643            raise Exception('Cannot cancel orders on closed market')
644
645        if(active_pre_flight_check):
646            check_sufficient_lamport_balance(self.user_balance_state)
647            check_cancel_order_market_status(self.market.market_state.market_status)
648            check_order_exists(self.user_market_state, order_id)
649      
650        is_binary_market_second_outcome = self.market.market_state.number_of_outcomes == 2 and outcome_id == 1
651        orderbook_account_index = outcome_id if not is_binary_market_second_outcome else 0
652        orderbook_account = self.market.market_store_state.orderbook_accounts[orderbook_account_index]
653
654        return self.aver_client.program.instruction['cancel_order'](
655            order_id, 
656            orderbook_account_index, 
657            ctx=Context(accounts={
658                "orderbook": orderbook_account.orderbook,
659                "event_queue": orderbook_account.event_queue,
660                "bids": orderbook_account.bids,
661                "asks": orderbook_account.asks,
662                "market": self.market.market_pubkey,
663                "user_market": self.pubkey,
664                "user": self.user_market_state.user,
665                "market_store": self.market.market_state.market_store,
666            })
667        )
668    
669    async def cancel_order(
670        self,
671        order_id: int,
672        outcome_id: int,
673        fee_payer: Keypair = None,
674        send_options: TxOpts = None,
675        active_pre_flight_check: bool = True,
676    ):
677        """
678        Cancels order
679
680        Sends instructions on chain
681
682        Args:
683            fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
684            order_id (int): ID of order to cancel
685            outcome_id (int): ID of outcome
686            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
687            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
688
689        Returns:
690            RPCResponse: Response
691        """
692
693        if(fee_payer is None):
694            fee_payer = self.aver_client.owner
695
696        ix = self.make_cancel_order_instruction(
697            order_id,
698            outcome_id,
699            active_pre_flight_check
700        )
701
702        return await sign_and_send_transaction_instructions(
703            self.aver_client,
704            [],
705            fee_payer,
706            [ix],
707            send_options
708        )
709
710    def make_cancel_all_orders_instruction(
711        self, 
712        outcome_ids_to_cancel: list[int],
713        active_pre_flight_check: bool = False,
714        ):
715        """
716        Creates instruction for to cancelling all orders
717
718        Cancels all orders on particular outcome_ids (not by order_id)
719
720        Returns TransactionInstruction object only. Does not send transaction.
721
722        Args:
723            outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
724            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
725
726        Raises:
727            Exception: Cannot cancel orders on closed market
728            Exception: Insufficient lamport balance
729            Exception: Cannot cancel orders in current market status
730
731        Returns:
732            TransactionInstruction: TransactionInstruction object
733        """
734        if(self.market.orderbooks is None):
735            raise Exception('Cannot cancel orders on closed market')
736
737        if(active_pre_flight_check):
738            check_sufficient_lamport_balance(self.user_balance_state)
739            check_cancel_order_market_status(self.market.market_state.market_status)
740            for outcome_id in outcome_ids_to_cancel:
741                check_outcome_has_orders(outcome_id, self.user_market_state)
742
743        remaining_accounts: list[AccountMeta] = []
744        for i, accounts in enumerate(self.market.market_store_state.orderbook_accounts):
745            if(not outcome_ids_to_cancel.__contains__(i)):
746                continue
747            remaining_accounts += [AccountMeta(
748                pubkey=accounts.orderbook,
749                is_signer=False,
750                is_writable=True,
751            )]
752            remaining_accounts += [AccountMeta(
753                pubkey=accounts.event_queue,
754                is_signer=False,
755                is_writable=True,
756            )]
757            remaining_accounts += [AccountMeta(
758                pubkey=accounts.bids,
759                is_signer=False,
760                is_writable=True,
761            )]
762            remaining_accounts += [AccountMeta(
763                pubkey=accounts.asks,
764                is_signer=False,
765                is_writable=True,
766            )]
767        
768        chunk_size = CANCEL_ALL_ORDERS_INSTRUCTION_CHUNK_SIZE
769        chunked_outcome_ids = chunk(outcome_ids_to_cancel, chunk_size)
770        chunked_remaining_accounts = chunk(remaining_accounts, chunk_size * 4)
771
772        ixs = []
773
774        for i, outcome_ids in enumerate(chunked_outcome_ids):
775            ixs.append(
776                self.aver_client.program.instruction['cancel_all_orders'](
777                    outcome_ids,
778                    ctx=Context(
779                        accounts={
780                                "user_market": self.pubkey,
781                                "market": self.market.market_pubkey,
782                                "user": self.user_market_state.user,
783                                "market_store": self.market.market_state.market_store,
784                            },
785                        remaining_accounts = chunked_remaining_accounts[i],
786                        )
787                    )
788            )
789
790        return ixs
791    
792    async def cancel_all_orders(
793        self,
794        outcome_ids_to_cancel: list[int], 
795        fee_payer: Keypair = None, 
796        send_options: TxOpts = None,
797        active_pre_flight_check: bool = True,
798    ):
799        """
800        Cancels all orders on particular outcome_ids (not by order_id)
801
802        Sends instructions on chain
803
804        Args:
805            fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
806            outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
807            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
808            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
809
810        Returns:
811            RPCResponse: Response
812        """
813        if(fee_payer is None):
814            fee_payer = self.aver_client.owner
815        
816        ixs = self.make_cancel_all_orders_instruction(outcome_ids_to_cancel, active_pre_flight_check)
817
818        sigs = await gather(
819            *[sign_and_send_transaction_instructions(
820                self.aver_client,
821                [],
822                fee_payer,
823                [ix],
824                send_options
825            ) for ix in ixs]
826            )
827        return sigs
828
829    def make_withdraw_idle_funds_instruction(
830        self,
831        user_quote_token_ata: PublicKey,
832        amount: float = None,
833    ):
834        """
835        Creates instruction for withdrawing funds in ATA 
836
837        Returns TransactionInstruction object only. Does not send transaction.
838
839        Args:
840            user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
841            amount (float, optional): amount. Defaults to maximum available funds.
842
843        Returns:
844            TransactionInstruction: TransactionInstruction object
845        """
846        if(amount is None):
847            amount = self.calculate_funds_available_to_withdraw()
848        
849        return self.aver_client.program.instruction['withdraw_tokens'](
850            amount,
851            ctx=Context(
852                accounts={
853                    "market": self.market.market_pubkey,
854                    "user_market": self.pubkey,
855                    "user": self.user_market_state.user,
856                    "user_quote_token_ata": user_quote_token_ata,
857                    "quote_vault": self.market.market_state.quote_vault,
858                    "vault_authority": self.market.market_state.vault_authority,
859                    "spl_token_program": TOKEN_PROGRAM_ID,
860                },
861            )
862        )
863    
864    async def withdraw_idle_funds(self, owner: Keypair, send_options: TxOpts = None, amount: float = None):
865        """
866        Withdraws idle funds in ATA
867
868        Sends instructions on chain
869
870        Args:
871            owner (Keypair): Owner of UserMarket account
872            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
873            amount (float, optional): amount. Defaults to None.
874
875        Raises:
876            Exception: Owner must be same as UMA owner
877
878        Returns:
879            TransactionInstruction: TransactionInstruction object
880        """
881        user_quote_token_ata = await self.market.aver_client.get_or_create_associated_token_account(
882            self.user_market_state.user,
883            self.market.aver_client.owner,
884            self.market.market_state.quote_token_mint
885        )
886        
887        ix = self.make_withdraw_idle_funds_instruction(user_quote_token_ata, amount)
888
889        if(not owner.public_key == self.user_market_state.user):
890            raise Exception('Owner must be same as UMA owner')
891
892        return await sign_and_send_transaction_instructions(
893            self.aver_client,
894            [],
895            owner,
896            [ix],
897            send_options
898        )
899
900
901    def calculate_funds_available_to_withdraw(self):
902        """
903        Calculates idle funds available to withdraw
904
905        Returns:
906            int: Tokens available to withdraw
907        """
908        return min([o.free for o in self.user_market_state.outcome_positions] + [self.user_market_state.net_quote_tokens_in])
909
910    def calculate_funds_available_to_collect(self, winning_outcome: int):
911        """
912        Calculate funds won if a particular outcome wins
913
914        Args:
915            winning_outcome (int): Winning outcome ID
916
917        Returns:
918            int: Tokens won
919        """
920        winning_outcome_position = self.user_market_state.outcome_positions[winning_outcome]
921        return winning_outcome_position.free + winning_outcome_position.locked
922
923    def calculate_exposures(self):
924        """
925        Calcualtes exposures for every possible outcome
926
927        The exposure on a particular outcome is the profit/loss if that outcome wins
928
929        Returns:
930            list[int]: List of exposures
931        """
932        net_quote_tokens_in = self.user_market_state.net_quote_tokens_in
933        return [o.free + o.locked - net_quote_tokens_in for o in self.user_market_state.outcome_positions]
934
935
936
937    def calculate_tokens_available_to_sell(self, outcome_index: int, price: float):
938        """
939        Calculates tokens available to sell on a particular outcome
940
941        Args:
942            outcome_index (int): Outcome ID
943            price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
944
945        Returns:
946            float: Token amount
947        """
948        return self.user_market_state.outcome_positions[outcome_index].free + price * self.user_balance_state.token_balance
949    
950    def calculate_tokens_available_to_buy(self, outcome_index: int, price: float):
951        """
952         Calculates tokens available to buy on a particular outcome
953
954        Args:
955            outcome_index (int): Outcome ID
956            price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
957
958        Returns:
959            float: Token amount
960        """
961        filtered_outcomes = deepcopy(self.user_market_state.outcome_positions)
962        del filtered_outcomes[outcome_index]
963        min_free_tokens_except_outcome_index  = min([op.free for op in filtered_outcomes])
964
965        return min_free_tokens_except_outcome_index + price * self.user_balance_state.token_balance
966    
967    def calculate_min_free_outcome_positions(self):
968        return min([o.free for o in self.user_market_state.outcome_positions])
class UserMarket:
 24class UserMarket():
 25    """
 26    Contains data on a user's orders on a particular market (for a particular host)
 27    """
 28
 29    aver_client: AverClient
 30    """
 31    AverClient object
 32    """
 33    pubkey: PublicKey
 34    """
 35    UserMarket public key
 36    """
 37    market: AverMarket
 38    """
 39    Corresponding Market object
 40    """
 41    user_market_state: UserMarketState
 42    """
 43    UserMarketState object
 44    """
 45    user_balance_state: UserBalanceState
 46    """
 47    UserBalanceState object
 48    """
 49    user_host_lifetime: UserHostLifetime
 50    """
 51    UserHostLifetime object
 52    """
 53
 54
 55    def __init__(self, aver_client: AverClient, pubkey: PublicKey, user_market_state: UserMarketState, market: AverMarket, user_balance_state: UserBalanceState, user_host_lifetime: UserHostLifetime):
 56        """
 57         Initialise an UserMarket object. Do not use this function; use UserMarket.load() instead
 58
 59        Args:
 60            aver_client (AverClient): AverClient object
 61            pubkey (PublicKey): UserMarket public key
 62            user_market_state (UserMarketState): UserMarketState object
 63            market (AverMarket): Market object
 64            user_balance_state (UserBalanceState): UserBalanceState object
 65            user_host_lifetime (UserHostLifetime): UserHostLifetime object
 66        """
 67        self.user_market_state = user_market_state
 68        self.pubkey = pubkey
 69        self.aver_client = aver_client
 70        self.market = market
 71        self.user_balance_state = user_balance_state
 72        self.user_host_lifetime = user_host_lifetime
 73
 74    @staticmethod
 75    async def load(
 76            aver_client: AverClient, 
 77            market: AverMarket, 
 78            owner: PublicKey, 
 79            host: PublicKey = AVER_HOST_ACCOUNT,
 80            program_id: PublicKey = AVER_PROGRAM_ID
 81        ):
 82            """
 83            Initialises an UserMarket object from Market, Host and Owner public keys
 84
 85            To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()
 86
 87            Args:
 88                aver_client (AverClient): AverClient object
 89                market (AverMarket): Corresponding Market object
 90                owner (PublicKey): Owner of UserMarket account
 91                host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
 92                program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
 93
 94            Returns:
 95                UserMarket: UserMarket object
 96            """
 97            uma, bump = UserMarket.derive_pubkey_and_bump(owner, market.market_pubkey, host, program_id)
 98            uhl = UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)[0]
 99            return await UserMarket.load_by_uma(aver_client, uma, market, uhl)
100
101    @staticmethod
102    async def load_multiple(
103            aver_client: AverClient, 
104            markets: list[AverMarket], 
105            owners: list[PublicKey], 
106            host: PublicKey = AVER_HOST_ACCOUNT,
107            program_id: PublicKey = AVER_PROGRAM_ID,
108        ):
109            """
110            Initialises multiple UserMarket objects from Market, Host and Owner public keys
111
112            This method is more highly optimized and faster than using UserMarket.load() multiple times.
113
114            To refresh data on already loaded UserMarkets use src.refresh.refresh_multiple_user_markets()
115
116            Args:
117                aver_client (AverClient): AverClient object
118                markets (list[AverMarket]): List of corresponding AverMarket objects (in correct order)
119                owner (PublicKey): List of owners of UserMarket account
120                host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
121                program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
122
123            Returns:
124                list[UserMarket]: List of UserMarket objects
125            """
126            umas = []
127            for i, m in enumerate(markets):
128                umas.append(UserMarket.derive_pubkey_and_bump(owners[i], m.market_pubkey, host, program_id)[0])
129            uhls = [UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)[0] for owner in owners]
130            return await UserMarket.load_multiple_by_uma(aver_client, umas, markets, uhls)
131
132    @staticmethod
133    async def load_by_uma(
134            aver_client: AverClient,
135            pubkey: PublicKey,
136            market: (AverMarket or PublicKey),
137            uhl: PublicKey
138        ):
139        """
140        Initialises an UserMarket object from UserMarket account public key
141
142        To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()
143
144        Args:
145            aver_client (AverClient): AverClient object
146            pubkey (PublicKey): UserMarket account public key
147            market (AverMarket or PublicKey): AverMarket object or AverMarket public key
148            uhl (PublicKey): UserHostLifetime account
149
150        Returns:
151            UserMarket: UserMarket object
152        """
153        res: UserMarketState = await aver_client.program.account['UserMarket'].fetch(pubkey)
154        uhl = await UserHostLifetime.load(aver_client, uhl)
155
156        lamport_balance = await aver_client.request_lamport_balance(res.user)
157        token_balance = await aver_client.request_token_balance(aver_client.quote_token, res.user)
158        user_balance_state = UserBalanceState(lamport_balance, token_balance)
159
160        if(isinstance(market, PublicKey)):
161            market = await AverMarket.load(aver_client, market)
162
163        if(res.market.to_base58() != market.market_pubkey.to_base58()):
164            raise Exception('UserMarket and Market do not match')
165
166        return UserMarket(aver_client, pubkey, res, market, user_balance_state, uhl)   
167    
168    @staticmethod
169    async def load_multiple_by_uma(
170            aver_client: AverClient,
171            pubkeys: list[PublicKey],
172            markets: list[AverMarket],
173            uhls: list[PublicKey]
174        ):
175        """
176        Initialises an multiple UserMarket objects from a list of UserMarket account public keys
177
178        To refresh data on an already loaded UserMarket use src.refresh.refresh_user_markets()
179
180        Args:
181            aver_client (AverClient): AverClient object 
182            pubkeys (list[PublicKey]): List of UserMarket account public keys
183            markets (list[AverMarket]): List of AverMarket objects
184            uhls (list[PublicKey]): List of UserHostLifetime  account public keys
185
186        Raises:
187            Exception: UserMarket and market do not match
188
189        Returns:
190            list[UserMarket]: List of UserMarket objects
191        """
192
193        res: list[UserMarketState] = await aver_client.program.account['UserMarket'].fetch_multiple(pubkeys)
194        uhls = await UserHostLifetime.load_multiple(aver_client, uhls)
195
196        user_pubkeys = [u.user for u in res]
197        user_balances = (await load_multiple_account_states(aver_client, [], [], [], [], user_pubkeys))['user_balance_states']
198
199        umas: list[UserMarket] = []
200        for i, pubkey in enumerate(pubkeys):
201            if(res[i].market.to_base58() != markets[i].market_pubkey.to_base58()):
202                raise Exception('UserMarket and Market do not match')
203            umas.append(UserMarket(aver_client, pubkey, res[i], markets[i], user_balances[i], uhls[i]))
204        return umas
205    
206    @staticmethod
207    def get_user_markets_from_account_state(
208            aver_client: AverClient, 
209            pubkeys: list[PublicKey], 
210            user_market_states: list[UserMarketState],
211            aver_markets: list[AverMarket],
212            user_balance_states: list[UserBalanceState],
213            user_host_lifetime_states: list[UserHostLifetimeState],
214            user_host_lifetime_pubkeys: list[PublicKey]
215        ):
216        """
217        Returns multiple UserMarket objects from their respective MarketStates, stores and orderbook objects
218
219        Used in refresh.py
220
221        Args:
222            aver_client (AverClient): AverClient object
223            pubkeys (list[PublicKey]): List of UserMarket account pubkeys
224            user_market_states (list[UserMarketState]): List of UserMarketState objects
225            aver_markets (list[AverMarket]): List of AverMarket objects
226            user_balance_states (list[UserBalanceState]): List of UserBalanceState objects
227            user_host_lifetime_states: (List[UserHostLifetimeState]): List of UserHostLifetimeState objects
228            user_host_lifetime_pubkeys (list[PublicKey]): List of UserHostLifetime public keys
229
230        Returns:
231            list[UserMarket]: List of UserMarket objects
232        """
233        user_markets: list[UserMarket] = []
234        for i, pubkey in enumerate(pubkeys):
235            user_market = UserMarket(aver_client, pubkey, user_market_states[i], aver_markets[i], user_balance_states[i], UserHostLifetime(user_host_lifetime_pubkeys[i],user_host_lifetime_states[i]))
236            user_markets.append(user_market)
237        return user_markets
238    
239    
240    @staticmethod
241    def parse_multiple_user_market_state(
242            buffer: list[bytes],
243            aver_client: AverMarket
244        ):
245        """
246        Parses raw onchain data to UserMarketState objects    
247
248        Args:
249            buffer (list[bytes]): List of raw bytes coming from onchain
250            aver_client (AverMarket): AverClient object
251
252        Returns:
253            list[UserMarketState]: List of UserMarketState objects
254        """
255        return [parse_user_market_state(b, aver_client) for b in buffer]
256
257    @staticmethod
258    def derive_pubkey_and_bump(
259            owner: PublicKey,
260            market: PublicKey,
261            host: PublicKey,
262            program_id: PublicKey = AVER_PROGRAM_ID
263        ) -> PublicKey:
264        """
265        Derives PDA (Program Derived Account) for UserMarket public key given a user, host and market
266
267        Args:
268            owner (PublicKey): Owner of UserMarket account
269            market (PublicKey): Corresponding Market account public key
270            host (PublicKey): Host public key
271            program_id (PublicKey, optional): _description_. Defaults to AVER_PROGRAM_ID.
272
273        Returns:
274            PublicKey: UserMarket account public key
275        """
276
277        return PublicKey.find_program_address(
278            [bytes('user-market', 'utf-8'), bytes(owner), bytes(market), bytes(host)],
279            program_id
280        )    
281
282    @staticmethod
283    def make_create_user_market_account_instruction(
284            aver_client: AverClient,
285            market: AverMarket,
286            owner: PublicKey,
287            host: PublicKey = AVER_HOST_ACCOUNT, 
288            number_of_orders: int = None,
289            program_id: PublicKey = AVER_PROGRAM_ID
290        ):
291        """
292        Creates instruction for UserMarket account creation
293
294        Returns TransactionInstruction object only. Does not send transaction.
295
296        Args:
297            aver_client (AverClient): AverClient object
298            market (AverMarket): Corresponding Market object
299            owner (PublicKey): Owner of UserMarket account
300            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
301            number_of_orders (int, optional): _description_. Defaults to 5*number of market outcomes.
302            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
303
304        Returns:
305            TransactionInstruction: TransactionInstruction object
306        """
307        if(number_of_orders is None):
308            number_of_orders = market.market_state.number_of_outcomes * 5
309        
310        uma, uma_bump = UserMarket.derive_pubkey_and_bump(owner, market.market_pubkey, host, program_id)
311        user_host_lifetime, uhl_bump = UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)
312
313        return aver_client.program.instruction['init_user_market'](
314            number_of_orders,
315            uma_bump, 
316            ctx=Context(
317                accounts={
318                    "user": owner,
319                    "user_host_lifetime": user_host_lifetime,
320                    "user_market": uma,
321                    "market": market.market_pubkey,
322                    "host": host,
323                    "system_program": SYS_PROGRAM_ID,
324                },
325            )
326        )
327
328    @staticmethod
329    async def create_user_market_account(
330            aver_client: AverClient,
331            market: AverMarket,
332            owner: Keypair = None,
333            send_options: TxOpts = None,
334            host: PublicKey = AVER_HOST_ACCOUNT,
335            number_of_orders: int = None,
336            program_id: PublicKey = AVER_PROGRAM_ID
337        ):
338        """
339        Creates UserMarket account
340
341        Sends instructions on chain
342
343        Args:
344            aver_client (AverClient): AverClient object
345            market (AverMarket): Correspondign Market object
346            owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
347            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
348            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
349            number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
350            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
351
352        Returns:
353            RPCResponse: Response
354        """
355        if(number_of_orders is None):
356            number_of_orders = 5 * market.market_state.number_of_outcomes
357
358        if(owner is None):
359            owner = aver_client.owner
360
361        ix = UserMarket.make_create_user_market_account_instruction(
362            aver_client,
363            market,
364            owner.public_key,
365            host,
366            number_of_orders,
367            program_id
368        )
369
370        if(send_options is None):
371            send_options = TxOpts()
372        else:
373            send_options = TxOpts(
374                skip_confirmation=send_options.skip_confirmation,
375                skip_preflight=send_options.skip_confirmation,
376                preflight_commitment=Finalized,
377                max_retries=send_options.max_retries)
378
379        return await sign_and_send_transaction_instructions(
380            aver_client,
381            [],
382            owner,
383            [ix],
384            send_options,
385        )
386    
387    @staticmethod
388    async def get_or_create_user_market_account(
389            client: AverClient,
390            market: AverMarket,
391            owner: Keypair = None,
392            send_options: TxOpts = None,
393            quote_token_mint: PublicKey = None,
394            host: PublicKey = AVER_HOST_ACCOUNT,
395            number_of_orders: int = None,
396            referrer: PublicKey = SYS_PROGRAM_ID,
397            discount_token: PublicKey = SYS_PROGRAM_ID,
398            program_id: PublicKey = AVER_PROGRAM_ID 
399        ):
400        """
401        Attempts to load UserMarket object or creates one if not one is not found
402
403        Args:
404            client (AverClient): AverClient object
405            market (AverMarket): Corresponding AverMarket object
406            owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
407            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
408            quote_token_mint (PublicKey, optional): ATA token mint public key. Defaults to USDC token according to chosen solana network.
409            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
410            number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
411            referrer (PublicKey, optional): Referrer account public key. Defaults to SYS_PROGRAM_ID.
412            discount_token (PublicKey, optional): _description_. Defaults to SYS_PROGRAM_ID.
413            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
414
415        Returns:
416            UserMarket: UserMarket object
417        """
418        quote_token_mint = quote_token_mint if quote_token_mint is not None else client.quote_token
419        if(number_of_orders is None):
420            number_of_orders = market.market_state.number_of_outcomes * 5
421        
422        if(owner is None):
423            owner = client.owner
424        
425        user_market_pubkey = UserMarket.derive_pubkey_and_bump(owner.public_key, market.market_pubkey, host, program_id)[0]
426        try:
427            uma = await UserMarket.load(client, market, owner.public_key, host, program_id)
428            return uma
429        except:
430            uhl = await UserHostLifetime.get_or_create_user_host_lifetime(
431                client,
432                owner,
433                send_options,
434                quote_token_mint,
435                host,
436                referrer,
437                discount_token,
438                program_id
439            )
440
441            sig = await UserMarket.create_user_market_account(
442                client,
443                market,
444                owner, 
445                send_options,
446                host,
447                number_of_orders,
448                program_id,
449            )
450
451            await client.provider.connection.confirm_transaction(
452                sig['result'],
453                commitment=Finalized
454            )
455
456            return await UserMarket.load(
457                client, 
458                market,  
459                owner.public_key,
460                host,
461                program_id)
462
463
464
465    def make_place_order_instruction(
466            self,
467            outcome_id: int,
468            side: Side,
469            limit_price: float,
470            size: float,
471            size_format: SizeFormat,
472            user_quote_token_ata: PublicKey,
473            order_type: OrderType = OrderType.LIMIT,
474            self_trade_behavior: SelfTradeBehavior = SelfTradeBehavior.CANCEL_PROVIDE,
475            active_pre_flight_check: bool = False,
476        ):
477        """
478        Creates instruction to place order.
479
480        Returns TransactionInstruction object only. Does not send transaction.
481
482        Args:
483            outcome_id (int): ID of outcome
484            side (Side): Side object (bid or ask)
485            limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
486            size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
487            size_format (SizeFormat): SizeFormat object (Stake or Payout)
488            user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
489            order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT.
490            self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE.
491            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
492
493        Raises:
494            Exception: Cannot place error on closed market
495
496        Returns:
497            TransactionInstruction: TransactionInstruction object
498        """
499        if(self.market.orderbooks is None):
500            raise Exception('Cannot place error on closed market')
501
502        if(active_pre_flight_check):
503            check_sufficient_lamport_balance(self.user_balance_state)
504            check_correct_uma_market_match(self.user_market_state, self.market)
505            check_market_active_pre_event(self.market.market_state.market_status)
506            check_uhl_self_excluded(self.user_host_lifetime)
507            check_user_market_full(self.user_market_state)
508            check_limit_price_error(limit_price, self.market)
509            check_outcome_outside_space(outcome_id, self.market)
510            check_incorrect_order_type_for_market_order(limit_price, order_type, side, self.market)
511            check_stake_noop(size_format, limit_price, side)
512            tokens_available_to_buy = self.calculate_tokens_available_to_buy(outcome_id, limit_price)
513            tokens_available_to_sell = self.calculate_tokens_available_to_sell(outcome_id, limit_price)
514            check_is_order_valid(outcome_id, side, limit_price, size, size_format, tokens_available_to_sell, tokens_available_to_buy)
515            check_quote_and_base_size_too_small(self.market, side, size_format, outcome_id, limit_price, size)
516            check_user_permission_and_quote_token_limit_exceeded(self.market, self.user_market_state, size, limit_price, size_format)
517        
518        max_base_qty = math.floor(size * (10 ** self.market.market_state.decimals))
519        limit_price_u64 = math.ceil(limit_price * (10 ** self.market.market_state.decimals))
520
521        is_binary_market_second_outcome = self.market.market_state.number_of_outcomes == 2 and outcome_id == 1
522        orderbook_account_index = outcome_id if not is_binary_market_second_outcome else 0
523        orderbook_account = self.market.market_store_state.orderbook_accounts[orderbook_account_index]
524
525        return self.aver_client.program.instruction['place_order'](
526            {
527                "limit_price": limit_price_u64,
528                "size": max_base_qty,
529                "size_format": size_format,
530                "side": side,
531                "order_type": order_type,
532                "self_trade_behavior": self_trade_behavior,
533                "outcome_id": outcome_id,
534            },
535            ctx=Context(
536                accounts={
537                    "user": self.user_market_state.user,
538                    "user_host_lifetime": self.user_market_state.user_host_lifetime,
539                    "market": self.market.market_pubkey,
540                    "market_store": self.market.market_state.market_store,
541                    "user_market": self.pubkey,
542                    "user": self.user_market_state.user,
543                    "user_quote_token_ata": user_quote_token_ata,
544                    "quote_vault": self.market.market_state.quote_vault,
545                    "orderbook": orderbook_account.orderbook,
546                    "bids": orderbook_account.bids,
547                    "asks": orderbook_account.asks,
548                    "event_queue": orderbook_account.event_queue,
549                    "spl_token_program": TOKEN_PROGRAM_ID,
550                    "system_program": SYS_PROGRAM_ID,
551               },)
552        )
553
554    async def place_order(
555            self,
556            owner: Keypair,
557            outcome_id: int,
558            side: Side,
559            limit_price: float,
560            size: float,
561            size_format: SizeFormat,
562            send_options: TxOpts = None,
563            order_type: OrderType = OrderType.LIMIT,
564            self_trade_behavior: SelfTradeBehavior = SelfTradeBehavior.CANCEL_PROVIDE,
565            active_pre_flight_check: bool = True,  
566        ):
567        """
568        Places a new order
569
570        Sends instructions on chain
571
572        Args:
573            owner (Keypair): Owner of UserMarket account. Pays transaction fees.
574            outcome_id (int): index of the outcome intended to be traded
575            side (Side): Side object (bid/back/buy or ask/lay/sell)
576            limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
577            size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
578            size_format (SizeFormat): SizeFormat object (Stake or Payout formats supported)
579            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
580            order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT. Other options include OrderType.IOC, OrderType.KILL_OR_FILL, OrderType.POST_ONLY.
581            self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE. Other options include SelfTradeBehavior.DECREMENT_TAKE and SelfTradeBehavior.ABORT_TRANSACTION.
582            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True. 
583
584        Raises:
585            Exception: Owner must be same as user market owner
586
587        Returns:
588            RPCResponse: Response
589        """
590        if(not owner.public_key == self.user_market_state.user):
591            raise Exception('Owner must be same as user market owner')
592
593        user_quote_token_ata = await self.market.aver_client.get_or_create_associated_token_account(
594            self.user_market_state.user,
595            self.market.aver_client.owner,
596            self.market.market_state.quote_token_mint
597        )
598
599        ix = self.make_place_order_instruction(
600            outcome_id,
601            side, 
602            limit_price,
603            size,
604            size_format,
605            user_quote_token_ata,
606            order_type,
607            self_trade_behavior,
608            active_pre_flight_check
609        )
610        return await sign_and_send_transaction_instructions(
611            self.aver_client,
612            [],
613            owner,
614            [ix],
615            send_options
616        )
617
618    def make_cancel_order_instruction(
619            self,
620            order_id: int,
621            outcome_id: int,
622            active_pre_flight_check: bool = False,
623        ):
624        """
625        Creates instruction for to cancel order.
626
627        Returns TransactionInstruction object only. Does not send transaction.
628
629        Args:
630            order_id (int): ID of order to cancel
631            outcome_id (int): ID of outcome
632            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
633
634        Raises:
635            Exception: Cannot cancel orders on closed market
636            Exception: Insufficient lamport balance
637            Exception: Cannot cancel orders in current market status
638            Exception: Order ID does not exist in list of open orders
639
640        Returns:
641            TransactionInstruction: TransactionInstruction object
642        """
643        if(self.market.orderbooks is None):
644            raise Exception('Cannot cancel orders on closed market')
645
646        if(active_pre_flight_check):
647            check_sufficient_lamport_balance(self.user_balance_state)
648            check_cancel_order_market_status(self.market.market_state.market_status)
649            check_order_exists(self.user_market_state, order_id)
650      
651        is_binary_market_second_outcome = self.market.market_state.number_of_outcomes == 2 and outcome_id == 1
652        orderbook_account_index = outcome_id if not is_binary_market_second_outcome else 0
653        orderbook_account = self.market.market_store_state.orderbook_accounts[orderbook_account_index]
654
655        return self.aver_client.program.instruction['cancel_order'](
656            order_id, 
657            orderbook_account_index, 
658            ctx=Context(accounts={
659                "orderbook": orderbook_account.orderbook,
660                "event_queue": orderbook_account.event_queue,
661                "bids": orderbook_account.bids,
662                "asks": orderbook_account.asks,
663                "market": self.market.market_pubkey,
664                "user_market": self.pubkey,
665                "user": self.user_market_state.user,
666                "market_store": self.market.market_state.market_store,
667            })
668        )
669    
670    async def cancel_order(
671        self,
672        order_id: int,
673        outcome_id: int,
674        fee_payer: Keypair = None,
675        send_options: TxOpts = None,
676        active_pre_flight_check: bool = True,
677    ):
678        """
679        Cancels order
680
681        Sends instructions on chain
682
683        Args:
684            fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
685            order_id (int): ID of order to cancel
686            outcome_id (int): ID of outcome
687            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
688            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
689
690        Returns:
691            RPCResponse: Response
692        """
693
694        if(fee_payer is None):
695            fee_payer = self.aver_client.owner
696
697        ix = self.make_cancel_order_instruction(
698            order_id,
699            outcome_id,
700            active_pre_flight_check
701        )
702
703        return await sign_and_send_transaction_instructions(
704            self.aver_client,
705            [],
706            fee_payer,
707            [ix],
708            send_options
709        )
710
711    def make_cancel_all_orders_instruction(
712        self, 
713        outcome_ids_to_cancel: list[int],
714        active_pre_flight_check: bool = False,
715        ):
716        """
717        Creates instruction for to cancelling all orders
718
719        Cancels all orders on particular outcome_ids (not by order_id)
720
721        Returns TransactionInstruction object only. Does not send transaction.
722
723        Args:
724            outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
725            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
726
727        Raises:
728            Exception: Cannot cancel orders on closed market
729            Exception: Insufficient lamport balance
730            Exception: Cannot cancel orders in current market status
731
732        Returns:
733            TransactionInstruction: TransactionInstruction object
734        """
735        if(self.market.orderbooks is None):
736            raise Exception('Cannot cancel orders on closed market')
737
738        if(active_pre_flight_check):
739            check_sufficient_lamport_balance(self.user_balance_state)
740            check_cancel_order_market_status(self.market.market_state.market_status)
741            for outcome_id in outcome_ids_to_cancel:
742                check_outcome_has_orders(outcome_id, self.user_market_state)
743
744        remaining_accounts: list[AccountMeta] = []
745        for i, accounts in enumerate(self.market.market_store_state.orderbook_accounts):
746            if(not outcome_ids_to_cancel.__contains__(i)):
747                continue
748            remaining_accounts += [AccountMeta(
749                pubkey=accounts.orderbook,
750                is_signer=False,
751                is_writable=True,
752            )]
753            remaining_accounts += [AccountMeta(
754                pubkey=accounts.event_queue,
755                is_signer=False,
756                is_writable=True,
757            )]
758            remaining_accounts += [AccountMeta(
759                pubkey=accounts.bids,
760                is_signer=False,
761                is_writable=True,
762            )]
763            remaining_accounts += [AccountMeta(
764                pubkey=accounts.asks,
765                is_signer=False,
766                is_writable=True,
767            )]
768        
769        chunk_size = CANCEL_ALL_ORDERS_INSTRUCTION_CHUNK_SIZE
770        chunked_outcome_ids = chunk(outcome_ids_to_cancel, chunk_size)
771        chunked_remaining_accounts = chunk(remaining_accounts, chunk_size * 4)
772
773        ixs = []
774
775        for i, outcome_ids in enumerate(chunked_outcome_ids):
776            ixs.append(
777                self.aver_client.program.instruction['cancel_all_orders'](
778                    outcome_ids,
779                    ctx=Context(
780                        accounts={
781                                "user_market": self.pubkey,
782                                "market": self.market.market_pubkey,
783                                "user": self.user_market_state.user,
784                                "market_store": self.market.market_state.market_store,
785                            },
786                        remaining_accounts = chunked_remaining_accounts[i],
787                        )
788                    )
789            )
790
791        return ixs
792    
793    async def cancel_all_orders(
794        self,
795        outcome_ids_to_cancel: list[int], 
796        fee_payer: Keypair = None, 
797        send_options: TxOpts = None,
798        active_pre_flight_check: bool = True,
799    ):
800        """
801        Cancels all orders on particular outcome_ids (not by order_id)
802
803        Sends instructions on chain
804
805        Args:
806            fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
807            outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
808            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
809            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
810
811        Returns:
812            RPCResponse: Response
813        """
814        if(fee_payer is None):
815            fee_payer = self.aver_client.owner
816        
817        ixs = self.make_cancel_all_orders_instruction(outcome_ids_to_cancel, active_pre_flight_check)
818
819        sigs = await gather(
820            *[sign_and_send_transaction_instructions(
821                self.aver_client,
822                [],
823                fee_payer,
824                [ix],
825                send_options
826            ) for ix in ixs]
827            )
828        return sigs
829
830    def make_withdraw_idle_funds_instruction(
831        self,
832        user_quote_token_ata: PublicKey,
833        amount: float = None,
834    ):
835        """
836        Creates instruction for withdrawing funds in ATA 
837
838        Returns TransactionInstruction object only. Does not send transaction.
839
840        Args:
841            user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
842            amount (float, optional): amount. Defaults to maximum available funds.
843
844        Returns:
845            TransactionInstruction: TransactionInstruction object
846        """
847        if(amount is None):
848            amount = self.calculate_funds_available_to_withdraw()
849        
850        return self.aver_client.program.instruction['withdraw_tokens'](
851            amount,
852            ctx=Context(
853                accounts={
854                    "market": self.market.market_pubkey,
855                    "user_market": self.pubkey,
856                    "user": self.user_market_state.user,
857                    "user_quote_token_ata": user_quote_token_ata,
858                    "quote_vault": self.market.market_state.quote_vault,
859                    "vault_authority": self.market.market_state.vault_authority,
860                    "spl_token_program": TOKEN_PROGRAM_ID,
861                },
862            )
863        )
864    
865    async def withdraw_idle_funds(self, owner: Keypair, send_options: TxOpts = None, amount: float = None):
866        """
867        Withdraws idle funds in ATA
868
869        Sends instructions on chain
870
871        Args:
872            owner (Keypair): Owner of UserMarket account
873            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
874            amount (float, optional): amount. Defaults to None.
875
876        Raises:
877            Exception: Owner must be same as UMA owner
878
879        Returns:
880            TransactionInstruction: TransactionInstruction object
881        """
882        user_quote_token_ata = await self.market.aver_client.get_or_create_associated_token_account(
883            self.user_market_state.user,
884            self.market.aver_client.owner,
885            self.market.market_state.quote_token_mint
886        )
887        
888        ix = self.make_withdraw_idle_funds_instruction(user_quote_token_ata, amount)
889
890        if(not owner.public_key == self.user_market_state.user):
891            raise Exception('Owner must be same as UMA owner')
892
893        return await sign_and_send_transaction_instructions(
894            self.aver_client,
895            [],
896            owner,
897            [ix],
898            send_options
899        )
900
901
902    def calculate_funds_available_to_withdraw(self):
903        """
904        Calculates idle funds available to withdraw
905
906        Returns:
907            int: Tokens available to withdraw
908        """
909        return min([o.free for o in self.user_market_state.outcome_positions] + [self.user_market_state.net_quote_tokens_in])
910
911    def calculate_funds_available_to_collect(self, winning_outcome: int):
912        """
913        Calculate funds won if a particular outcome wins
914
915        Args:
916            winning_outcome (int): Winning outcome ID
917
918        Returns:
919            int: Tokens won
920        """
921        winning_outcome_position = self.user_market_state.outcome_positions[winning_outcome]
922        return winning_outcome_position.free + winning_outcome_position.locked
923
924    def calculate_exposures(self):
925        """
926        Calcualtes exposures for every possible outcome
927
928        The exposure on a particular outcome is the profit/loss if that outcome wins
929
930        Returns:
931            list[int]: List of exposures
932        """
933        net_quote_tokens_in = self.user_market_state.net_quote_tokens_in
934        return [o.free + o.locked - net_quote_tokens_in for o in self.user_market_state.outcome_positions]
935
936
937
938    def calculate_tokens_available_to_sell(self, outcome_index: int, price: float):
939        """
940        Calculates tokens available to sell on a particular outcome
941
942        Args:
943            outcome_index (int): Outcome ID
944            price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
945
946        Returns:
947            float: Token amount
948        """
949        return self.user_market_state.outcome_positions[outcome_index].free + price * self.user_balance_state.token_balance
950    
951    def calculate_tokens_available_to_buy(self, outcome_index: int, price: float):
952        """
953         Calculates tokens available to buy on a particular outcome
954
955        Args:
956            outcome_index (int): Outcome ID
957            price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
958
959        Returns:
960            float: Token amount
961        """
962        filtered_outcomes = deepcopy(self.user_market_state.outcome_positions)
963        del filtered_outcomes[outcome_index]
964        min_free_tokens_except_outcome_index  = min([op.free for op in filtered_outcomes])
965
966        return min_free_tokens_except_outcome_index + price * self.user_balance_state.token_balance
967    
968    def calculate_min_free_outcome_positions(self):
969        return min([o.free for o in self.user_market_state.outcome_positions])

Contains data on a user's orders on a particular market (for a particular host)

UserMarket( aver_client: pyaver.aver_client.AverClient, pubkey: solana.publickey.PublicKey, user_market_state: pyaver.data_classes.UserMarketState, market: pyaver.market.AverMarket, user_balance_state: pyaver.data_classes.UserBalanceState, user_host_lifetime: pyaver.user_host_lifetime.UserHostLifetime)
55    def __init__(self, aver_client: AverClient, pubkey: PublicKey, user_market_state: UserMarketState, market: AverMarket, user_balance_state: UserBalanceState, user_host_lifetime: UserHostLifetime):
56        """
57         Initialise an UserMarket object. Do not use this function; use UserMarket.load() instead
58
59        Args:
60            aver_client (AverClient): AverClient object
61            pubkey (PublicKey): UserMarket public key
62            user_market_state (UserMarketState): UserMarketState object
63            market (AverMarket): Market object
64            user_balance_state (UserBalanceState): UserBalanceState object
65            user_host_lifetime (UserHostLifetime): UserHostLifetime object
66        """
67        self.user_market_state = user_market_state
68        self.pubkey = pubkey
69        self.aver_client = aver_client
70        self.market = market
71        self.user_balance_state = user_balance_state
72        self.user_host_lifetime = user_host_lifetime

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

Args
  • aver_client (AverClient): AverClient object
  • pubkey (PublicKey): UserMarket public key
  • user_market_state (UserMarketState): UserMarketState object
  • market (AverMarket): Market object
  • user_balance_state (UserBalanceState): UserBalanceState object
  • user_host_lifetime (UserHostLifetime): UserHostLifetime object

AverClient object

pubkey: solana.publickey.PublicKey

UserMarket public key

Corresponding Market object

UserMarketState object

UserBalanceState object

UserHostLifetime object

@staticmethod
async def load( aver_client: pyaver.aver_client.AverClient, market: pyaver.market.AverMarket, owner: solana.publickey.PublicKey, host: solana.publickey.PublicKey = 5xhmqK1Dh48TiqvHxoZi6WWWKL6THtsUjh3GoiVEbbR8, program_id: solana.publickey.PublicKey = 6q5ZGhEj6kkmEjuyCXuH4x8493bpi9fNzvy9L8hX83HQ)
74    @staticmethod
75    async def load(
76            aver_client: AverClient, 
77            market: AverMarket, 
78            owner: PublicKey, 
79            host: PublicKey = AVER_HOST_ACCOUNT,
80            program_id: PublicKey = AVER_PROGRAM_ID
81        ):
82            """
83            Initialises an UserMarket object from Market, Host and Owner public keys
84
85            To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()
86
87            Args:
88                aver_client (AverClient): AverClient object
89                market (AverMarket): Corresponding Market object
90                owner (PublicKey): Owner of UserMarket account
91                host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
92                program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
93
94            Returns:
95                UserMarket: UserMarket object
96            """
97            uma, bump = UserMarket.derive_pubkey_and_bump(owner, market.market_pubkey, host, program_id)
98            uhl = UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)[0]
99            return await UserMarket.load_by_uma(aver_client, uma, market, uhl)

Initialises an UserMarket object from Market, Host and Owner public keys

To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()

Args
  • aver_client (AverClient): AverClient object
  • market (AverMarket): Corresponding Market object
  • owner (PublicKey): Owner of UserMarket account
  • host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
  • program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
Returns

UserMarket: UserMarket object

@staticmethod
async def load_multiple( aver_client: pyaver.aver_client.AverClient, markets: list[pyaver.market.AverMarket], owners: list[solana.publickey.PublicKey], host: solana.publickey.PublicKey = 5xhmqK1Dh48TiqvHxoZi6WWWKL6THtsUjh3GoiVEbbR8, program_id: solana.publickey.PublicKey = 6q5ZGhEj6kkmEjuyCXuH4x8493bpi9fNzvy9L8hX83HQ)
101    @staticmethod
102    async def load_multiple(
103            aver_client: AverClient, 
104            markets: list[AverMarket], 
105            owners: list[PublicKey], 
106            host: PublicKey = AVER_HOST_ACCOUNT,
107            program_id: PublicKey = AVER_PROGRAM_ID,
108        ):
109            """
110            Initialises multiple UserMarket objects from Market, Host and Owner public keys
111
112            This method is more highly optimized and faster than using UserMarket.load() multiple times.
113
114            To refresh data on already loaded UserMarkets use src.refresh.refresh_multiple_user_markets()
115
116            Args:
117                aver_client (AverClient): AverClient object
118                markets (list[AverMarket]): List of corresponding AverMarket objects (in correct order)
119                owner (PublicKey): List of owners of UserMarket account
120                host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
121                program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
122
123            Returns:
124                list[UserMarket]: List of UserMarket objects
125            """
126            umas = []
127            for i, m in enumerate(markets):
128                umas.append(UserMarket.derive_pubkey_and_bump(owners[i], m.market_pubkey, host, program_id)[0])
129            uhls = [UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)[0] for owner in owners]
130            return await UserMarket.load_multiple_by_uma(aver_client, umas, markets, uhls)

Initialises multiple UserMarket objects from Market, Host and Owner public keys

This method is more highly optimized and faster than using UserMarket.load() multiple times.

To refresh data on already loaded UserMarkets use src.refresh.refresh_multiple_user_markets()

Args
  • aver_client (AverClient): AverClient object
  • markets (list[AverMarket]): List of corresponding AverMarket objects (in correct order)
  • owner (PublicKey): List of owners of UserMarket account
  • host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
  • program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
Returns

list[UserMarket]: List of UserMarket objects

@staticmethod
async def load_by_uma( aver_client: pyaver.aver_client.AverClient, pubkey: solana.publickey.PublicKey, market: pyaver.market.AverMarket, uhl: solana.publickey.PublicKey)
132    @staticmethod
133    async def load_by_uma(
134            aver_client: AverClient,
135            pubkey: PublicKey,
136            market: (AverMarket or PublicKey),
137            uhl: PublicKey
138        ):
139        """
140        Initialises an UserMarket object from UserMarket account public key
141
142        To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()
143
144        Args:
145            aver_client (AverClient): AverClient object
146            pubkey (PublicKey): UserMarket account public key
147            market (AverMarket or PublicKey): AverMarket object or AverMarket public key
148            uhl (PublicKey): UserHostLifetime account
149
150        Returns:
151            UserMarket: UserMarket object
152        """
153        res: UserMarketState = await aver_client.program.account['UserMarket'].fetch(pubkey)
154        uhl = await UserHostLifetime.load(aver_client, uhl)
155
156        lamport_balance = await aver_client.request_lamport_balance(res.user)
157        token_balance = await aver_client.request_token_balance(aver_client.quote_token, res.user)
158        user_balance_state = UserBalanceState(lamport_balance, token_balance)
159
160        if(isinstance(market, PublicKey)):
161            market = await AverMarket.load(aver_client, market)
162
163        if(res.market.to_base58() != market.market_pubkey.to_base58()):
164            raise Exception('UserMarket and Market do not match')
165
166        return UserMarket(aver_client, pubkey, res, market, user_balance_state, uhl)   

Initialises an UserMarket object from UserMarket account public key

To refresh data on an already loaded UserMarket use src.refresh.refresh_user_market()

Args
  • aver_client (AverClient): AverClient object
  • pubkey (PublicKey): UserMarket account public key
  • market (AverMarket or PublicKey): AverMarket object or AverMarket public key
  • uhl (PublicKey): UserHostLifetime account
Returns

UserMarket: UserMarket object

@staticmethod
async def load_multiple_by_uma( aver_client: pyaver.aver_client.AverClient, pubkeys: list[solana.publickey.PublicKey], markets: list[pyaver.market.AverMarket], uhls: list[solana.publickey.PublicKey])
168    @staticmethod
169    async def load_multiple_by_uma(
170            aver_client: AverClient,
171            pubkeys: list[PublicKey],
172            markets: list[AverMarket],
173            uhls: list[PublicKey]
174        ):
175        """
176        Initialises an multiple UserMarket objects from a list of UserMarket account public keys
177
178        To refresh data on an already loaded UserMarket use src.refresh.refresh_user_markets()
179
180        Args:
181            aver_client (AverClient): AverClient object 
182            pubkeys (list[PublicKey]): List of UserMarket account public keys
183            markets (list[AverMarket]): List of AverMarket objects
184            uhls (list[PublicKey]): List of UserHostLifetime  account public keys
185
186        Raises:
187            Exception: UserMarket and market do not match
188
189        Returns:
190            list[UserMarket]: List of UserMarket objects
191        """
192
193        res: list[UserMarketState] = await aver_client.program.account['UserMarket'].fetch_multiple(pubkeys)
194        uhls = await UserHostLifetime.load_multiple(aver_client, uhls)
195
196        user_pubkeys = [u.user for u in res]
197        user_balances = (await load_multiple_account_states(aver_client, [], [], [], [], user_pubkeys))['user_balance_states']
198
199        umas: list[UserMarket] = []
200        for i, pubkey in enumerate(pubkeys):
201            if(res[i].market.to_base58() != markets[i].market_pubkey.to_base58()):
202                raise Exception('UserMarket and Market do not match')
203            umas.append(UserMarket(aver_client, pubkey, res[i], markets[i], user_balances[i], uhls[i]))
204        return umas

Initialises an multiple UserMarket objects from a list of UserMarket account public keys

To refresh data on an already loaded UserMarket use src.refresh.refresh_user_markets()

Args
  • aver_client (AverClient): AverClient object
  • pubkeys (list[PublicKey]): List of UserMarket account public keys
  • markets (list[AverMarket]): List of AverMarket objects
  • uhls (list[PublicKey]): List of UserHostLifetime account public keys
Raises
  • Exception: UserMarket and market do not match
Returns

list[UserMarket]: List of UserMarket objects

@staticmethod
def get_user_markets_from_account_state( aver_client: pyaver.aver_client.AverClient, pubkeys: list[solana.publickey.PublicKey], user_market_states: list[pyaver.data_classes.UserMarketState], aver_markets: list[pyaver.market.AverMarket], user_balance_states: list[pyaver.data_classes.UserBalanceState], user_host_lifetime_states: list[pyaver.data_classes.UserHostLifetimeState], user_host_lifetime_pubkeys: list[solana.publickey.PublicKey])
206    @staticmethod
207    def get_user_markets_from_account_state(
208            aver_client: AverClient, 
209            pubkeys: list[PublicKey], 
210            user_market_states: list[UserMarketState],
211            aver_markets: list[AverMarket],
212            user_balance_states: list[UserBalanceState],
213            user_host_lifetime_states: list[UserHostLifetimeState],
214            user_host_lifetime_pubkeys: list[PublicKey]
215        ):
216        """
217        Returns multiple UserMarket objects from their respective MarketStates, stores and orderbook objects
218
219        Used in refresh.py
220
221        Args:
222            aver_client (AverClient): AverClient object
223            pubkeys (list[PublicKey]): List of UserMarket account pubkeys
224            user_market_states (list[UserMarketState]): List of UserMarketState objects
225            aver_markets (list[AverMarket]): List of AverMarket objects
226            user_balance_states (list[UserBalanceState]): List of UserBalanceState objects
227            user_host_lifetime_states: (List[UserHostLifetimeState]): List of UserHostLifetimeState objects
228            user_host_lifetime_pubkeys (list[PublicKey]): List of UserHostLifetime public keys
229
230        Returns:
231            list[UserMarket]: List of UserMarket objects
232        """
233        user_markets: list[UserMarket] = []
234        for i, pubkey in enumerate(pubkeys):
235            user_market = UserMarket(aver_client, pubkey, user_market_states[i], aver_markets[i], user_balance_states[i], UserHostLifetime(user_host_lifetime_pubkeys[i],user_host_lifetime_states[i]))
236            user_markets.append(user_market)
237        return user_markets

Returns multiple UserMarket objects from their respective MarketStates, stores and orderbook objects

Used in refresh.py

Args
  • aver_client (AverClient): AverClient object
  • pubkeys (list[PublicKey]): List of UserMarket account pubkeys
  • user_market_states (list[UserMarketState]): List of UserMarketState objects
  • aver_markets (list[AverMarket]): List of AverMarket objects
  • user_balance_states (list[UserBalanceState]): List of UserBalanceState objects
  • user_host_lifetime_states: (List[UserHostLifetimeState]): List of UserHostLifetimeState objects
  • user_host_lifetime_pubkeys (list[PublicKey]): List of UserHostLifetime public keys
Returns

list[UserMarket]: List of UserMarket objects

@staticmethod
def parse_multiple_user_market_state(buffer: list[bytes], aver_client: pyaver.market.AverMarket)
240    @staticmethod
241    def parse_multiple_user_market_state(
242            buffer: list[bytes],
243            aver_client: AverMarket
244        ):
245        """
246        Parses raw onchain data to UserMarketState objects    
247
248        Args:
249            buffer (list[bytes]): List of raw bytes coming from onchain
250            aver_client (AverMarket): AverClient object
251
252        Returns:
253            list[UserMarketState]: List of UserMarketState objects
254        """
255        return [parse_user_market_state(b, aver_client) for b in buffer]

Parses raw onchain data to UserMarketState objects

Args
  • buffer (list[bytes]): List of raw bytes coming from onchain
  • aver_client (AverMarket): AverClient object
Returns

list[UserMarketState]: List of UserMarketState objects

@staticmethod
def derive_pubkey_and_bump( owner: solana.publickey.PublicKey, market: solana.publickey.PublicKey, host: solana.publickey.PublicKey, program_id: solana.publickey.PublicKey = 6q5ZGhEj6kkmEjuyCXuH4x8493bpi9fNzvy9L8hX83HQ) -> solana.publickey.PublicKey:
257    @staticmethod
258    def derive_pubkey_and_bump(
259            owner: PublicKey,
260            market: PublicKey,
261            host: PublicKey,
262            program_id: PublicKey = AVER_PROGRAM_ID
263        ) -> PublicKey:
264        """
265        Derives PDA (Program Derived Account) for UserMarket public key given a user, host and market
266
267        Args:
268            owner (PublicKey): Owner of UserMarket account
269            market (PublicKey): Corresponding Market account public key
270            host (PublicKey): Host public key
271            program_id (PublicKey, optional): _description_. Defaults to AVER_PROGRAM_ID.
272
273        Returns:
274            PublicKey: UserMarket account public key
275        """
276
277        return PublicKey.find_program_address(
278            [bytes('user-market', 'utf-8'), bytes(owner), bytes(market), bytes(host)],
279            program_id
280        )    

Derives PDA (Program Derived Account) for UserMarket public key given a user, host and market

Args
  • owner (PublicKey): Owner of UserMarket account
  • market (PublicKey): Corresponding Market account public key
  • host (PublicKey): Host public key
  • program_id (PublicKey, optional): _description_. Defaults to AVER_PROGRAM_ID.
Returns

PublicKey: UserMarket account public key

@staticmethod
def make_create_user_market_account_instruction( aver_client: pyaver.aver_client.AverClient, market: pyaver.market.AverMarket, owner: solana.publickey.PublicKey, host: solana.publickey.PublicKey = 5xhmqK1Dh48TiqvHxoZi6WWWKL6THtsUjh3GoiVEbbR8, number_of_orders: int = None, program_id: solana.publickey.PublicKey = 6q5ZGhEj6kkmEjuyCXuH4x8493bpi9fNzvy9L8hX83HQ)
282    @staticmethod
283    def make_create_user_market_account_instruction(
284            aver_client: AverClient,
285            market: AverMarket,
286            owner: PublicKey,
287            host: PublicKey = AVER_HOST_ACCOUNT, 
288            number_of_orders: int = None,
289            program_id: PublicKey = AVER_PROGRAM_ID
290        ):
291        """
292        Creates instruction for UserMarket account creation
293
294        Returns TransactionInstruction object only. Does not send transaction.
295
296        Args:
297            aver_client (AverClient): AverClient object
298            market (AverMarket): Corresponding Market object
299            owner (PublicKey): Owner of UserMarket account
300            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
301            number_of_orders (int, optional): _description_. Defaults to 5*number of market outcomes.
302            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
303
304        Returns:
305            TransactionInstruction: TransactionInstruction object
306        """
307        if(number_of_orders is None):
308            number_of_orders = market.market_state.number_of_outcomes * 5
309        
310        uma, uma_bump = UserMarket.derive_pubkey_and_bump(owner, market.market_pubkey, host, program_id)
311        user_host_lifetime, uhl_bump = UserHostLifetime.derive_pubkey_and_bump(owner, host, program_id)
312
313        return aver_client.program.instruction['init_user_market'](
314            number_of_orders,
315            uma_bump, 
316            ctx=Context(
317                accounts={
318                    "user": owner,
319                    "user_host_lifetime": user_host_lifetime,
320                    "user_market": uma,
321                    "market": market.market_pubkey,
322                    "host": host,
323                    "system_program": SYS_PROGRAM_ID,
324                },
325            )
326        )

Creates instruction for UserMarket account creation

Returns TransactionInstruction object only. Does not send transaction.

Args
  • aver_client (AverClient): AverClient object
  • market (AverMarket): Corresponding Market object
  • owner (PublicKey): Owner of UserMarket account
  • host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
  • number_of_orders (int, optional): _description_. Defaults to 5*number of market outcomes.
  • program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
Returns

TransactionInstruction: TransactionInstruction object

@staticmethod
async def create_user_market_account( aver_client: pyaver.aver_client.AverClient, market: pyaver.market.AverMarket, owner: solana.keypair.Keypair = None, send_options: solana.rpc.types.TxOpts = None, host: solana.publickey.PublicKey = 5xhmqK1Dh48TiqvHxoZi6WWWKL6THtsUjh3GoiVEbbR8, number_of_orders: int = None, program_id: solana.publickey.PublicKey = 6q5ZGhEj6kkmEjuyCXuH4x8493bpi9fNzvy9L8hX83HQ)
328    @staticmethod
329    async def create_user_market_account(
330            aver_client: AverClient,
331            market: AverMarket,
332            owner: Keypair = None,
333            send_options: TxOpts = None,
334            host: PublicKey = AVER_HOST_ACCOUNT,
335            number_of_orders: int = None,
336            program_id: PublicKey = AVER_PROGRAM_ID
337        ):
338        """
339        Creates UserMarket account
340
341        Sends instructions on chain
342
343        Args:
344            aver_client (AverClient): AverClient object
345            market (AverMarket): Correspondign Market object
346            owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
347            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
348            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
349            number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
350            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
351
352        Returns:
353            RPCResponse: Response
354        """
355        if(number_of_orders is None):
356            number_of_orders = 5 * market.market_state.number_of_outcomes
357
358        if(owner is None):
359            owner = aver_client.owner
360
361        ix = UserMarket.make_create_user_market_account_instruction(
362            aver_client,
363            market,
364            owner.public_key,
365            host,
366            number_of_orders,
367            program_id
368        )
369
370        if(send_options is None):
371            send_options = TxOpts()
372        else:
373            send_options = TxOpts(
374                skip_confirmation=send_options.skip_confirmation,
375                skip_preflight=send_options.skip_confirmation,
376                preflight_commitment=Finalized,
377                max_retries=send_options.max_retries)
378
379        return await sign_and_send_transaction_instructions(
380            aver_client,
381            [],
382            owner,
383            [ix],
384            send_options,
385        )

Creates UserMarket account

Sends instructions on chain

Args
  • aver_client (AverClient): AverClient object
  • market (AverMarket): Correspondign Market object
  • owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
  • send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
  • host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
  • number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
  • program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
Returns

RPCResponse: Response

@staticmethod
async def get_or_create_user_market_account( client: pyaver.aver_client.AverClient, market: pyaver.market.AverMarket, owner: solana.keypair.Keypair = None, send_options: solana.rpc.types.TxOpts = None, quote_token_mint: solana.publickey.PublicKey = None, host: solana.publickey.PublicKey = 5xhmqK1Dh48TiqvHxoZi6WWWKL6THtsUjh3GoiVEbbR8, number_of_orders: int = None, referrer: solana.publickey.PublicKey = 11111111111111111111111111111111, discount_token: solana.publickey.PublicKey = 11111111111111111111111111111111, program_id: solana.publickey.PublicKey = 6q5ZGhEj6kkmEjuyCXuH4x8493bpi9fNzvy9L8hX83HQ)
387    @staticmethod
388    async def get_or_create_user_market_account(
389            client: AverClient,
390            market: AverMarket,
391            owner: Keypair = None,
392            send_options: TxOpts = None,
393            quote_token_mint: PublicKey = None,
394            host: PublicKey = AVER_HOST_ACCOUNT,
395            number_of_orders: int = None,
396            referrer: PublicKey = SYS_PROGRAM_ID,
397            discount_token: PublicKey = SYS_PROGRAM_ID,
398            program_id: PublicKey = AVER_PROGRAM_ID 
399        ):
400        """
401        Attempts to load UserMarket object or creates one if not one is not found
402
403        Args:
404            client (AverClient): AverClient object
405            market (AverMarket): Corresponding AverMarket object
406            owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
407            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
408            quote_token_mint (PublicKey, optional): ATA token mint public key. Defaults to USDC token according to chosen solana network.
409            host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
410            number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
411            referrer (PublicKey, optional): Referrer account public key. Defaults to SYS_PROGRAM_ID.
412            discount_token (PublicKey, optional): _description_. Defaults to SYS_PROGRAM_ID.
413            program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
414
415        Returns:
416            UserMarket: UserMarket object
417        """
418        quote_token_mint = quote_token_mint if quote_token_mint is not None else client.quote_token
419        if(number_of_orders is None):
420            number_of_orders = market.market_state.number_of_outcomes * 5
421        
422        if(owner is None):
423            owner = client.owner
424        
425        user_market_pubkey = UserMarket.derive_pubkey_and_bump(owner.public_key, market.market_pubkey, host, program_id)[0]
426        try:
427            uma = await UserMarket.load(client, market, owner.public_key, host, program_id)
428            return uma
429        except:
430            uhl = await UserHostLifetime.get_or_create_user_host_lifetime(
431                client,
432                owner,
433                send_options,
434                quote_token_mint,
435                host,
436                referrer,
437                discount_token,
438                program_id
439            )
440
441            sig = await UserMarket.create_user_market_account(
442                client,
443                market,
444                owner, 
445                send_options,
446                host,
447                number_of_orders,
448                program_id,
449            )
450
451            await client.provider.connection.confirm_transaction(
452                sig['result'],
453                commitment=Finalized
454            )
455
456            return await UserMarket.load(
457                client, 
458                market,  
459                owner.public_key,
460                host,
461                program_id)

Attempts to load UserMarket object or creates one if not one is not found

Args
  • client (AverClient): AverClient object
  • market (AverMarket): Corresponding AverMarket object
  • owner (Keypair): Owner of UserMarket account. Defaults to AverClient wallet
  • send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
  • quote_token_mint (PublicKey, optional): ATA token mint public key. Defaults to USDC token according to chosen solana network.
  • host (PublicKey, optional): Host account public key. Defaults to AVER_HOST_ACCOUNT.
  • number_of_orders (int, optional): _description_. Defaults to 5 * number of market outcomes.
  • referrer (PublicKey, optional): Referrer account public key. Defaults to SYS_PROGRAM_ID.
  • discount_token (PublicKey, optional): _description_. Defaults to SYS_PROGRAM_ID.
  • program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
Returns

UserMarket: UserMarket object

def make_place_order_instruction( self, outcome_id: int, side: pyaver.enums.Side, limit_price: float, size: float, size_format: pyaver.enums.SizeFormat, user_quote_token_ata: solana.publickey.PublicKey, order_type: pyaver.enums.OrderType = <OrderType.LIMIT: 0>, self_trade_behavior: pyaver.enums.SelfTradeBehavior = <SelfTradeBehavior.CANCEL_PROVIDE: 1>, active_pre_flight_check: bool = False)
465    def make_place_order_instruction(
466            self,
467            outcome_id: int,
468            side: Side,
469            limit_price: float,
470            size: float,
471            size_format: SizeFormat,
472            user_quote_token_ata: PublicKey,
473            order_type: OrderType = OrderType.LIMIT,
474            self_trade_behavior: SelfTradeBehavior = SelfTradeBehavior.CANCEL_PROVIDE,
475            active_pre_flight_check: bool = False,
476        ):
477        """
478        Creates instruction to place order.
479
480        Returns TransactionInstruction object only. Does not send transaction.
481
482        Args:
483            outcome_id (int): ID of outcome
484            side (Side): Side object (bid or ask)
485            limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
486            size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
487            size_format (SizeFormat): SizeFormat object (Stake or Payout)
488            user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
489            order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT.
490            self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE.
491            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
492
493        Raises:
494            Exception: Cannot place error on closed market
495
496        Returns:
497            TransactionInstruction: TransactionInstruction object
498        """
499        if(self.market.orderbooks is None):
500            raise Exception('Cannot place error on closed market')
501
502        if(active_pre_flight_check):
503            check_sufficient_lamport_balance(self.user_balance_state)
504            check_correct_uma_market_match(self.user_market_state, self.market)
505            check_market_active_pre_event(self.market.market_state.market_status)
506            check_uhl_self_excluded(self.user_host_lifetime)
507            check_user_market_full(self.user_market_state)
508            check_limit_price_error(limit_price, self.market)
509            check_outcome_outside_space(outcome_id, self.market)
510            check_incorrect_order_type_for_market_order(limit_price, order_type, side, self.market)
511            check_stake_noop(size_format, limit_price, side)
512            tokens_available_to_buy = self.calculate_tokens_available_to_buy(outcome_id, limit_price)
513            tokens_available_to_sell = self.calculate_tokens_available_to_sell(outcome_id, limit_price)
514            check_is_order_valid(outcome_id, side, limit_price, size, size_format, tokens_available_to_sell, tokens_available_to_buy)
515            check_quote_and_base_size_too_small(self.market, side, size_format, outcome_id, limit_price, size)
516            check_user_permission_and_quote_token_limit_exceeded(self.market, self.user_market_state, size, limit_price, size_format)
517        
518        max_base_qty = math.floor(size * (10 ** self.market.market_state.decimals))
519        limit_price_u64 = math.ceil(limit_price * (10 ** self.market.market_state.decimals))
520
521        is_binary_market_second_outcome = self.market.market_state.number_of_outcomes == 2 and outcome_id == 1
522        orderbook_account_index = outcome_id if not is_binary_market_second_outcome else 0
523        orderbook_account = self.market.market_store_state.orderbook_accounts[orderbook_account_index]
524
525        return self.aver_client.program.instruction['place_order'](
526            {
527                "limit_price": limit_price_u64,
528                "size": max_base_qty,
529                "size_format": size_format,
530                "side": side,
531                "order_type": order_type,
532                "self_trade_behavior": self_trade_behavior,
533                "outcome_id": outcome_id,
534            },
535            ctx=Context(
536                accounts={
537                    "user": self.user_market_state.user,
538                    "user_host_lifetime": self.user_market_state.user_host_lifetime,
539                    "market": self.market.market_pubkey,
540                    "market_store": self.market.market_state.market_store,
541                    "user_market": self.pubkey,
542                    "user": self.user_market_state.user,
543                    "user_quote_token_ata": user_quote_token_ata,
544                    "quote_vault": self.market.market_state.quote_vault,
545                    "orderbook": orderbook_account.orderbook,
546                    "bids": orderbook_account.bids,
547                    "asks": orderbook_account.asks,
548                    "event_queue": orderbook_account.event_queue,
549                    "spl_token_program": TOKEN_PROGRAM_ID,
550                    "system_program": SYS_PROGRAM_ID,
551               },)
552        )

Creates instruction to place order.

Returns TransactionInstruction object only. Does not send transaction.

Args
  • outcome_id (int): ID of outcome
  • side (Side): Side object (bid or ask)
  • limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
  • size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
  • size_format (SizeFormat): SizeFormat object (Stake or Payout)
  • user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
  • order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT.
  • self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE.
  • active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
Raises
  • Exception: Cannot place error on closed market
Returns

TransactionInstruction: TransactionInstruction object

async def place_order( self, owner: solana.keypair.Keypair, outcome_id: int, side: pyaver.enums.Side, limit_price: float, size: float, size_format: pyaver.enums.SizeFormat, send_options: solana.rpc.types.TxOpts = None, order_type: pyaver.enums.OrderType = <OrderType.LIMIT: 0>, self_trade_behavior: pyaver.enums.SelfTradeBehavior = <SelfTradeBehavior.CANCEL_PROVIDE: 1>, active_pre_flight_check: bool = True)
554    async def place_order(
555            self,
556            owner: Keypair,
557            outcome_id: int,
558            side: Side,
559            limit_price: float,
560            size: float,
561            size_format: SizeFormat,
562            send_options: TxOpts = None,
563            order_type: OrderType = OrderType.LIMIT,
564            self_trade_behavior: SelfTradeBehavior = SelfTradeBehavior.CANCEL_PROVIDE,
565            active_pre_flight_check: bool = True,  
566        ):
567        """
568        Places a new order
569
570        Sends instructions on chain
571
572        Args:
573            owner (Keypair): Owner of UserMarket account. Pays transaction fees.
574            outcome_id (int): index of the outcome intended to be traded
575            side (Side): Side object (bid/back/buy or ask/lay/sell)
576            limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
577            size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
578            size_format (SizeFormat): SizeFormat object (Stake or Payout formats supported)
579            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
580            order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT. Other options include OrderType.IOC, OrderType.KILL_OR_FILL, OrderType.POST_ONLY.
581            self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE. Other options include SelfTradeBehavior.DECREMENT_TAKE and SelfTradeBehavior.ABORT_TRANSACTION.
582            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True. 
583
584        Raises:
585            Exception: Owner must be same as user market owner
586
587        Returns:
588            RPCResponse: Response
589        """
590        if(not owner.public_key == self.user_market_state.user):
591            raise Exception('Owner must be same as user market owner')
592
593        user_quote_token_ata = await self.market.aver_client.get_or_create_associated_token_account(
594            self.user_market_state.user,
595            self.market.aver_client.owner,
596            self.market.market_state.quote_token_mint
597        )
598
599        ix = self.make_place_order_instruction(
600            outcome_id,
601            side, 
602            limit_price,
603            size,
604            size_format,
605            user_quote_token_ata,
606            order_type,
607            self_trade_behavior,
608            active_pre_flight_check
609        )
610        return await sign_and_send_transaction_instructions(
611            self.aver_client,
612            [],
613            owner,
614            [ix],
615            send_options
616        )

Places a new order

Sends instructions on chain

Args
  • owner (Keypair): Owner of UserMarket account. Pays transaction fees.
  • outcome_id (int): index of the outcome intended to be traded
  • side (Side): Side object (bid/back/buy or ask/lay/sell)
  • limit_price (float): Limit price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
  • size (float): Size - in the format specified in size_format. This value is in number of 'tokens' - i.e. 20.45 => 20.45 USDC, the SDK handles the conversion to u64 token units (e.g. to 20,450,000 as USDC is a 6 decimal place token)
  • size_format (SizeFormat): SizeFormat object (Stake or Payout formats supported)
  • send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
  • order_type (OrderType, optional): OrderType object. Defaults to OrderType.LIMIT. Other options include OrderType.IOC, OrderType.KILL_OR_FILL, OrderType.POST_ONLY.
  • self_trade_behavior (SelfTradeBehavior, optional): Behavior when a user's trade is matched with themselves. Defaults to SelfTradeBehavior.CANCEL_PROVIDE. Other options include SelfTradeBehavior.DECREMENT_TAKE and SelfTradeBehavior.ABORT_TRANSACTION.
  • active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
Raises
  • Exception: Owner must be same as user market owner
Returns

RPCResponse: Response

def make_cancel_order_instruction( self, order_id: int, outcome_id: int, active_pre_flight_check: bool = False)
618    def make_cancel_order_instruction(
619            self,
620            order_id: int,
621            outcome_id: int,
622            active_pre_flight_check: bool = False,
623        ):
624        """
625        Creates instruction for to cancel order.
626
627        Returns TransactionInstruction object only. Does not send transaction.
628
629        Args:
630            order_id (int): ID of order to cancel
631            outcome_id (int): ID of outcome
632            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
633
634        Raises:
635            Exception: Cannot cancel orders on closed market
636            Exception: Insufficient lamport balance
637            Exception: Cannot cancel orders in current market status
638            Exception: Order ID does not exist in list of open orders
639
640        Returns:
641            TransactionInstruction: TransactionInstruction object
642        """
643        if(self.market.orderbooks is None):
644            raise Exception('Cannot cancel orders on closed market')
645
646        if(active_pre_flight_check):
647            check_sufficient_lamport_balance(self.user_balance_state)
648            check_cancel_order_market_status(self.market.market_state.market_status)
649            check_order_exists(self.user_market_state, order_id)
650      
651        is_binary_market_second_outcome = self.market.market_state.number_of_outcomes == 2 and outcome_id == 1
652        orderbook_account_index = outcome_id if not is_binary_market_second_outcome else 0
653        orderbook_account = self.market.market_store_state.orderbook_accounts[orderbook_account_index]
654
655        return self.aver_client.program.instruction['cancel_order'](
656            order_id, 
657            orderbook_account_index, 
658            ctx=Context(accounts={
659                "orderbook": orderbook_account.orderbook,
660                "event_queue": orderbook_account.event_queue,
661                "bids": orderbook_account.bids,
662                "asks": orderbook_account.asks,
663                "market": self.market.market_pubkey,
664                "user_market": self.pubkey,
665                "user": self.user_market_state.user,
666                "market_store": self.market.market_state.market_store,
667            })
668        )

Creates instruction for to cancel order.

Returns TransactionInstruction object only. Does not send transaction.

Args
  • order_id (int): ID of order to cancel
  • outcome_id (int): ID of outcome
  • active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
Raises
  • Exception: Cannot cancel orders on closed market
  • Exception: Insufficient lamport balance
  • Exception: Cannot cancel orders in current market status
  • Exception: Order ID does not exist in list of open orders
Returns

TransactionInstruction: TransactionInstruction object

async def cancel_order( self, order_id: int, outcome_id: int, fee_payer: solana.keypair.Keypair = None, send_options: solana.rpc.types.TxOpts = None, active_pre_flight_check: bool = True)
670    async def cancel_order(
671        self,
672        order_id: int,
673        outcome_id: int,
674        fee_payer: Keypair = None,
675        send_options: TxOpts = None,
676        active_pre_flight_check: bool = True,
677    ):
678        """
679        Cancels order
680
681        Sends instructions on chain
682
683        Args:
684            fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
685            order_id (int): ID of order to cancel
686            outcome_id (int): ID of outcome
687            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
688            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
689
690        Returns:
691            RPCResponse: Response
692        """
693
694        if(fee_payer is None):
695            fee_payer = self.aver_client.owner
696
697        ix = self.make_cancel_order_instruction(
698            order_id,
699            outcome_id,
700            active_pre_flight_check
701        )
702
703        return await sign_and_send_transaction_instructions(
704            self.aver_client,
705            [],
706            fee_payer,
707            [ix],
708            send_options
709        )

Cancels order

Sends instructions on chain

Args
  • fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
  • order_id (int): ID of order to cancel
  • outcome_id (int): ID of outcome
  • send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
  • active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
Returns

RPCResponse: Response

def make_cancel_all_orders_instruction( self, outcome_ids_to_cancel: list[int], active_pre_flight_check: bool = False)
711    def make_cancel_all_orders_instruction(
712        self, 
713        outcome_ids_to_cancel: list[int],
714        active_pre_flight_check: bool = False,
715        ):
716        """
717        Creates instruction for to cancelling all orders
718
719        Cancels all orders on particular outcome_ids (not by order_id)
720
721        Returns TransactionInstruction object only. Does not send transaction.
722
723        Args:
724            outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
725            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
726
727        Raises:
728            Exception: Cannot cancel orders on closed market
729            Exception: Insufficient lamport balance
730            Exception: Cannot cancel orders in current market status
731
732        Returns:
733            TransactionInstruction: TransactionInstruction object
734        """
735        if(self.market.orderbooks is None):
736            raise Exception('Cannot cancel orders on closed market')
737
738        if(active_pre_flight_check):
739            check_sufficient_lamport_balance(self.user_balance_state)
740            check_cancel_order_market_status(self.market.market_state.market_status)
741            for outcome_id in outcome_ids_to_cancel:
742                check_outcome_has_orders(outcome_id, self.user_market_state)
743
744        remaining_accounts: list[AccountMeta] = []
745        for i, accounts in enumerate(self.market.market_store_state.orderbook_accounts):
746            if(not outcome_ids_to_cancel.__contains__(i)):
747                continue
748            remaining_accounts += [AccountMeta(
749                pubkey=accounts.orderbook,
750                is_signer=False,
751                is_writable=True,
752            )]
753            remaining_accounts += [AccountMeta(
754                pubkey=accounts.event_queue,
755                is_signer=False,
756                is_writable=True,
757            )]
758            remaining_accounts += [AccountMeta(
759                pubkey=accounts.bids,
760                is_signer=False,
761                is_writable=True,
762            )]
763            remaining_accounts += [AccountMeta(
764                pubkey=accounts.asks,
765                is_signer=False,
766                is_writable=True,
767            )]
768        
769        chunk_size = CANCEL_ALL_ORDERS_INSTRUCTION_CHUNK_SIZE
770        chunked_outcome_ids = chunk(outcome_ids_to_cancel, chunk_size)
771        chunked_remaining_accounts = chunk(remaining_accounts, chunk_size * 4)
772
773        ixs = []
774
775        for i, outcome_ids in enumerate(chunked_outcome_ids):
776            ixs.append(
777                self.aver_client.program.instruction['cancel_all_orders'](
778                    outcome_ids,
779                    ctx=Context(
780                        accounts={
781                                "user_market": self.pubkey,
782                                "market": self.market.market_pubkey,
783                                "user": self.user_market_state.user,
784                                "market_store": self.market.market_state.market_store,
785                            },
786                        remaining_accounts = chunked_remaining_accounts[i],
787                        )
788                    )
789            )
790
791        return ixs

Creates instruction for to cancelling all orders

Cancels all orders on particular outcome_ids (not by order_id)

Returns TransactionInstruction object only. Does not send transaction.

Args
  • outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
  • active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to False.
Raises
  • Exception: Cannot cancel orders on closed market
  • Exception: Insufficient lamport balance
  • Exception: Cannot cancel orders in current market status
Returns

TransactionInstruction: TransactionInstruction object

async def cancel_all_orders( self, outcome_ids_to_cancel: list[int], fee_payer: solana.keypair.Keypair = None, send_options: solana.rpc.types.TxOpts = None, active_pre_flight_check: bool = True)
793    async def cancel_all_orders(
794        self,
795        outcome_ids_to_cancel: list[int], 
796        fee_payer: Keypair = None, 
797        send_options: TxOpts = None,
798        active_pre_flight_check: bool = True,
799    ):
800        """
801        Cancels all orders on particular outcome_ids (not by order_id)
802
803        Sends instructions on chain
804
805        Args:
806            fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
807            outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
808            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
809            active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
810
811        Returns:
812            RPCResponse: Response
813        """
814        if(fee_payer is None):
815            fee_payer = self.aver_client.owner
816        
817        ixs = self.make_cancel_all_orders_instruction(outcome_ids_to_cancel, active_pre_flight_check)
818
819        sigs = await gather(
820            *[sign_and_send_transaction_instructions(
821                self.aver_client,
822                [],
823                fee_payer,
824                [ix],
825                send_options
826            ) for ix in ixs]
827            )
828        return sigs

Cancels all orders on particular outcome_ids (not by order_id)

Sends instructions on chain

Args
  • fee_payer (Keypair): Keypair to pay fee for transaction. Defaults to AverClient wallet
  • outcome_ids_to_cancel (list[int]): List of outcome ids to cancel orders on
  • send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
  • active_pre_flight_check (bool, optional): Clientside check if order will success or fail. Defaults to True.
Returns

RPCResponse: Response

def make_withdraw_idle_funds_instruction( self, user_quote_token_ata: solana.publickey.PublicKey, amount: float = None)
830    def make_withdraw_idle_funds_instruction(
831        self,
832        user_quote_token_ata: PublicKey,
833        amount: float = None,
834    ):
835        """
836        Creates instruction for withdrawing funds in ATA 
837
838        Returns TransactionInstruction object only. Does not send transaction.
839
840        Args:
841            user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
842            amount (float, optional): amount. Defaults to maximum available funds.
843
844        Returns:
845            TransactionInstruction: TransactionInstruction object
846        """
847        if(amount is None):
848            amount = self.calculate_funds_available_to_withdraw()
849        
850        return self.aver_client.program.instruction['withdraw_tokens'](
851            amount,
852            ctx=Context(
853                accounts={
854                    "market": self.market.market_pubkey,
855                    "user_market": self.pubkey,
856                    "user": self.user_market_state.user,
857                    "user_quote_token_ata": user_quote_token_ata,
858                    "quote_vault": self.market.market_state.quote_vault,
859                    "vault_authority": self.market.market_state.vault_authority,
860                    "spl_token_program": TOKEN_PROGRAM_ID,
861                },
862            )
863        )

Creates instruction for withdrawing funds in ATA

Returns TransactionInstruction object only. Does not send transaction.

Args
  • user_quote_token_ata (PublicKey): Quote token ATA public key (holds funds for this user)
  • amount (float, optional): amount. Defaults to maximum available funds.
Returns

TransactionInstruction: TransactionInstruction object

async def withdraw_idle_funds( self, owner: solana.keypair.Keypair, send_options: solana.rpc.types.TxOpts = None, amount: float = None)
865    async def withdraw_idle_funds(self, owner: Keypair, send_options: TxOpts = None, amount: float = None):
866        """
867        Withdraws idle funds in ATA
868
869        Sends instructions on chain
870
871        Args:
872            owner (Keypair): Owner of UserMarket account
873            send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
874            amount (float, optional): amount. Defaults to None.
875
876        Raises:
877            Exception: Owner must be same as UMA owner
878
879        Returns:
880            TransactionInstruction: TransactionInstruction object
881        """
882        user_quote_token_ata = await self.market.aver_client.get_or_create_associated_token_account(
883            self.user_market_state.user,
884            self.market.aver_client.owner,
885            self.market.market_state.quote_token_mint
886        )
887        
888        ix = self.make_withdraw_idle_funds_instruction(user_quote_token_ata, amount)
889
890        if(not owner.public_key == self.user_market_state.user):
891            raise Exception('Owner must be same as UMA owner')
892
893        return await sign_and_send_transaction_instructions(
894            self.aver_client,
895            [],
896            owner,
897            [ix],
898            send_options
899        )

Withdraws idle funds in ATA

Sends instructions on chain

Args
  • owner (Keypair): Owner of UserMarket account
  • send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
  • amount (float, optional): amount. Defaults to None.
Raises
  • Exception: Owner must be same as UMA owner
Returns

TransactionInstruction: TransactionInstruction object

def calculate_funds_available_to_withdraw(self)
902    def calculate_funds_available_to_withdraw(self):
903        """
904        Calculates idle funds available to withdraw
905
906        Returns:
907            int: Tokens available to withdraw
908        """
909        return min([o.free for o in self.user_market_state.outcome_positions] + [self.user_market_state.net_quote_tokens_in])

Calculates idle funds available to withdraw

Returns

int: Tokens available to withdraw

def calculate_funds_available_to_collect(self, winning_outcome: int)
911    def calculate_funds_available_to_collect(self, winning_outcome: int):
912        """
913        Calculate funds won if a particular outcome wins
914
915        Args:
916            winning_outcome (int): Winning outcome ID
917
918        Returns:
919            int: Tokens won
920        """
921        winning_outcome_position = self.user_market_state.outcome_positions[winning_outcome]
922        return winning_outcome_position.free + winning_outcome_position.locked

Calculate funds won if a particular outcome wins

Args
  • winning_outcome (int): Winning outcome ID
Returns

int: Tokens won

def calculate_exposures(self)
924    def calculate_exposures(self):
925        """
926        Calcualtes exposures for every possible outcome
927
928        The exposure on a particular outcome is the profit/loss if that outcome wins
929
930        Returns:
931            list[int]: List of exposures
932        """
933        net_quote_tokens_in = self.user_market_state.net_quote_tokens_in
934        return [o.free + o.locked - net_quote_tokens_in for o in self.user_market_state.outcome_positions]

Calcualtes exposures for every possible outcome

The exposure on a particular outcome is the profit/loss if that outcome wins

Returns

list[int]: List of exposures

def calculate_tokens_available_to_sell(self, outcome_index: int, price: float)
938    def calculate_tokens_available_to_sell(self, outcome_index: int, price: float):
939        """
940        Calculates tokens available to sell on a particular outcome
941
942        Args:
943            outcome_index (int): Outcome ID
944            price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
945
946        Returns:
947            float: Token amount
948        """
949        return self.user_market_state.outcome_positions[outcome_index].free + price * self.user_balance_state.token_balance

Calculates tokens available to sell on a particular outcome

Args
  • outcome_index (int): Outcome ID
  • price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
Returns

float: Token amount

def calculate_tokens_available_to_buy(self, outcome_index: int, price: float)
951    def calculate_tokens_available_to_buy(self, outcome_index: int, price: float):
952        """
953         Calculates tokens available to buy on a particular outcome
954
955        Args:
956            outcome_index (int): Outcome ID
957            price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
958
959        Returns:
960            float: Token amount
961        """
962        filtered_outcomes = deepcopy(self.user_market_state.outcome_positions)
963        del filtered_outcomes[outcome_index]
964        min_free_tokens_except_outcome_index  = min([op.free for op in filtered_outcomes])
965
966        return min_free_tokens_except_outcome_index + price * self.user_balance_state.token_balance

Calculates tokens available to buy on a particular outcome

Args
  • outcome_index (int): Outcome ID
  • price (float): Price - in probability format i.e. in the range (0, 1). If you are using Decimal or other odds formats you will need to convert these prior to passing as an argument
Returns

float: Token amount

def calculate_min_free_outcome_positions(self)
968    def calculate_min_free_outcome_positions(self):
969        return min([o.free for o in self.user_market_state.outcome_positions])