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])
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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