Added order placement API. (#31)
Added order placement API including finding the open order account logic.
This commit is contained in:
parent
969c14f3e1
commit
e2351f5134
112
src/market.py
112
src/market.py
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
import math
|
||||
from typing import Any, Iterable, List, NamedTuple, Tuple
|
||||
from typing import Any, Iterable, List, NamedTuple
|
||||
|
||||
from solana.account import Account
|
||||
from solana.publickey import PublicKey
|
||||
|
@ -12,11 +12,14 @@ from solana.transaction import Transaction, TransactionInstruction
|
|||
|
||||
from ._layouts.account_flags import ACCOUNT_FLAGS_LAYOUT
|
||||
from ._layouts.market import MARKET_LAYOUT, MINT_LAYOUT
|
||||
from ._layouts.open_orders import OPEN_ORDERS_LAYOUT
|
||||
from ._layouts.slab import Slab
|
||||
from .enums import Side
|
||||
from .enums import OrderType, Side
|
||||
from .instructions import DEFAULT_DEX_PROGRAM_ID, CancelOrderParams, MatchOrdersParams, NewOrderParams
|
||||
from .instructions import cancel_order as cancel_order_inst
|
||||
from .instructions import match_orders as match_order_inst
|
||||
from .instructions import new_order as new_order_inst
|
||||
from .open_order_account import OpenOrderAccount, make_create_account_instruction
|
||||
from .queue_ import decode_event_queue, decode_request_queue
|
||||
from .utils import load_bytes_data
|
||||
|
||||
|
@ -91,6 +94,22 @@ class Market:
|
|||
"""Returns quote mint address."""
|
||||
return PublicKey(self._decode.quote_mint)
|
||||
|
||||
def base_vault_address(self) -> PublicKey:
|
||||
"""Returns base vault address."""
|
||||
return PublicKey(self._decode.base_vault)
|
||||
|
||||
def quote_vault_address(self) -> PublicKey:
|
||||
"""Returns quote vault address."""
|
||||
return PublicKey(self._decode.quote_vault)
|
||||
|
||||
def request_queue(self) -> PublicKey:
|
||||
"""Returns quote vault address."""
|
||||
return PublicKey(self._decode.request_queue)
|
||||
|
||||
def event_queue(self) -> PublicKey:
|
||||
"""Returns quote vault address."""
|
||||
return PublicKey(self._decode.event_queue)
|
||||
|
||||
def __base_spl_token_multiplier(self) -> int:
|
||||
return 10 ** self._base_spl_token_decimals
|
||||
|
||||
|
@ -109,7 +128,12 @@ class Market:
|
|||
)
|
||||
|
||||
def price_number_to_lots(self, price: float) -> int:
|
||||
raise NotImplementedError("price_number_to_lots is not implemented")
|
||||
return int(
|
||||
round(
|
||||
(price * 10 ** self.__quote_spl_token_multiplier() * self._decode.base_lot_size)
|
||||
/ (10 ** self.__base_spl_token_multiplier() * self._decode.quote_lot_size)
|
||||
)
|
||||
)
|
||||
|
||||
def base_size_lots_to_number(self, size: int) -> float:
|
||||
return float(size * self._decode.base_lot_size) / self.__base_spl_token_multiplier()
|
||||
|
@ -183,14 +207,84 @@ class Market:
|
|||
fee_cost=event.native_fee_or_rebate * (1 if event.event_flags.maker else -1),
|
||||
)
|
||||
|
||||
def place_order(self, order_params: NewOrderParams):
|
||||
pass
|
||||
def place_order(
|
||||
self,
|
||||
payer: PublicKey,
|
||||
owner: Account,
|
||||
order_type: OrderType,
|
||||
side: Side,
|
||||
limit_price: int,
|
||||
max_quantity: int,
|
||||
client_id: int = 0,
|
||||
):
|
||||
transaction = Transaction()
|
||||
signers: List[Account] = [owner]
|
||||
open_order_accounts = self.find_open_orders_accounts_for_owner(owner.public_key())
|
||||
if not open_order_accounts:
|
||||
new_open_order_account = Account()
|
||||
transaction.add(
|
||||
make_create_account_instruction(
|
||||
owner.public_key(),
|
||||
new_open_order_account.public_key(),
|
||||
Client(self._endpoint).get_minimum_balance_for_rent_exemption(OPEN_ORDERS_LAYOUT.sizeof())[
|
||||
"result"
|
||||
],
|
||||
self._program_id,
|
||||
)
|
||||
)
|
||||
signers.append(new_open_order_account)
|
||||
|
||||
def make_place_order_transaction(self, order_params: NewOrderParams) -> Tuple[Transaction, List[PublicKey]]:
|
||||
pass
|
||||
transaction.add(
|
||||
self.make_place_order_instruction(
|
||||
payer,
|
||||
owner,
|
||||
order_type,
|
||||
side,
|
||||
limit_price,
|
||||
max_quantity,
|
||||
client_id,
|
||||
open_order_accounts[0].address if open_order_accounts else new_open_order_account.public_key(),
|
||||
)
|
||||
)
|
||||
return self._send_transaction(transaction, *signers)
|
||||
|
||||
def find_open_orders_accounts_for_owner(self, owner_address: PublicKey):
|
||||
pass
|
||||
def make_place_order_instruction(
|
||||
self,
|
||||
payer: PublicKey,
|
||||
owner: Account,
|
||||
order_type: OrderType,
|
||||
side: Side,
|
||||
limit_price: int,
|
||||
max_quantity: int,
|
||||
client_id: int,
|
||||
open_order_account: PublicKey,
|
||||
) -> TransactionInstruction:
|
||||
if self.base_size_number_to_lots(max_quantity) < 0:
|
||||
raise Exception("Size lot %d is too small." % max_quantity)
|
||||
if self.price_number_to_lots(limit_price) < 0:
|
||||
raise Exception("Price lot %d is too small." % limit_price)
|
||||
return new_order_inst(
|
||||
NewOrderParams(
|
||||
market=self.address(),
|
||||
open_orders=open_order_account,
|
||||
payer=payer,
|
||||
owner=owner.public_key(),
|
||||
request_queue=self.request_queue(),
|
||||
base_vault=self.base_vault_address(),
|
||||
quote_vault=self.quote_vault_address(),
|
||||
side=side,
|
||||
limit_price=limit_price,
|
||||
max_quantity=max_quantity,
|
||||
order_type=order_type,
|
||||
client_id=client_id,
|
||||
program_id=self._program_id,
|
||||
)
|
||||
)
|
||||
|
||||
def find_open_orders_accounts_for_owner(self, owner_address: PublicKey) -> List[OpenOrderAccount]:
|
||||
return OpenOrderAccount.find_for_market_and_owner(
|
||||
self._endpoint, self.address(), owner_address, self._program_id
|
||||
)
|
||||
|
||||
def cancel_order_by_client_id(self, owner: str) -> str:
|
||||
pass
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
import base64
|
||||
from typing import List, NamedTuple
|
||||
|
||||
from solana.publickey import PublicKey
|
||||
from solana.rpc.api import Client
|
||||
from solana.rpc.api import Client, MemcmpOpt
|
||||
from solana.system_program import CreateAccountParams, create_account
|
||||
from solana.transaction import TransactionInstruction
|
||||
|
||||
from ._layouts.open_orders import OPEN_ORDERS_LAYOUT
|
||||
from .instructions import DEFAULT_DEX_PROGRAM_ID
|
||||
from .utils import load_bytes_data
|
||||
|
||||
|
||||
class ProgramAccount(NamedTuple):
|
||||
public_key: PublicKey
|
||||
data: bytes
|
||||
is_executablable: bool
|
||||
lamports: int
|
||||
owner: PublicKey
|
||||
|
||||
|
||||
class OpenOrderAccount:
|
||||
# pylint: disable=too-many-arguments
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
@ -41,6 +53,9 @@ class OpenOrderAccount:
|
|||
@staticmethod
|
||||
def from_bytes(address: PublicKey, data_bytes: bytes) -> OpenOrderAccount:
|
||||
open_order_decoded = OPEN_ORDERS_LAYOUT.parse(data_bytes)
|
||||
if not open_order_decoded.account_flags.open_orders or not open_order_decoded.account_flags.initialized:
|
||||
raise Exception("Not an open order account or not initialized.")
|
||||
|
||||
return OpenOrderAccount(
|
||||
address=address,
|
||||
market=PublicKey(open_order_decoded.market),
|
||||
|
@ -56,11 +71,56 @@ class OpenOrderAccount:
|
|||
)
|
||||
|
||||
@staticmethod
|
||||
def find_for_market_and_owner(connection: Client, market: PublicKey, owner: PublicKey):
|
||||
pass
|
||||
def find_for_market_and_owner(
|
||||
endpoint: str, market: PublicKey, owner: PublicKey, program_id: PublicKey
|
||||
) -> List[OpenOrderAccount]:
|
||||
filters = [
|
||||
MemcmpOpt(
|
||||
offset=5 + 8, # 5 bytes of padding, 8 bytes of account flag
|
||||
bytes=str(market),
|
||||
),
|
||||
MemcmpOpt(
|
||||
offset=5 + 8 + 32, # 5 bytes of padding, 8 bytes of account flag, 32 bytes of market public key
|
||||
bytes=str(owner),
|
||||
),
|
||||
]
|
||||
|
||||
resp = Client(endpoint).get_program_accounts(
|
||||
program_id, encoding="base64", memcmp_opts=filters, data_size=OPEN_ORDERS_LAYOUT.sizeof()
|
||||
)
|
||||
accounts = []
|
||||
for account in resp["result"]:
|
||||
account_details = account["account"]
|
||||
accounts.append(
|
||||
ProgramAccount(
|
||||
public_key=PublicKey(account["pubkey"]),
|
||||
data=base64.decodebytes(account_details["data"][0].encode("ascii")),
|
||||
is_executablable=bool(account_details["executable"]),
|
||||
owner=PublicKey(account_details["owner"]),
|
||||
lamports=int(account_details["lamports"]),
|
||||
)
|
||||
)
|
||||
return [OpenOrderAccount.from_bytes(account.public_key, account.data) for account in accounts]
|
||||
|
||||
@staticmethod
|
||||
def load(endpoint: str, address: str) -> OpenOrderAccount:
|
||||
addr_pub_key = PublicKey(address)
|
||||
bytes_data = load_bytes_data(addr_pub_key, endpoint)
|
||||
return OpenOrderAccount.from_bytes(addr_pub_key, bytes_data)
|
||||
|
||||
|
||||
def make_create_account_instruction(
|
||||
owner_address: PublicKey,
|
||||
new_account_address: PublicKey,
|
||||
lamports: int,
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID,
|
||||
) -> TransactionInstruction:
|
||||
return create_account(
|
||||
CreateAccountParams(
|
||||
from_pubkey=owner_address,
|
||||
new_account_pubkey=new_account_address,
|
||||
lamports=lamports,
|
||||
space=OPEN_ORDERS_LAYOUT.sizeof(),
|
||||
program_id=program_id,
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,12 +13,12 @@ def _decode_queue(header_layout: Any, node_layout: Any, buffer: bytes, history:
|
|||
for i in range(min(history, alloc_len)):
|
||||
node_index = (header.head + header.count + alloc_len - 1 - i) % alloc_len
|
||||
offset = header_layout.sizeof() + node_index * node_layout.sizeof()
|
||||
nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203 # noqa: E203
|
||||
nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203
|
||||
else:
|
||||
for i in range(header.count):
|
||||
node_index = (header.head + i) % alloc_len
|
||||
offset = header_layout.sizeof() + node_index * node_layout.sizeof()
|
||||
nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203 # noqa: E203
|
||||
nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203
|
||||
return header, nodes
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
c2VydW0FAAAAAAAAADklQaAz6ADd3zS9MiJs/L7mmunSOSZtNjcUVqTd25yTY3mmdwHMScQ6cpqR7agJJnqKc3TW62zaypJkFqLsl+cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs27bCDgAAAPj///////////////////8HAAAAAAAAAAAAAAAAAAAA///////////0AQAAAAAAAP3/////////COIBAAAAAAD8/////////9IEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGFkZGluZw==
|
|
@ -1,3 +1,4 @@
|
|||
_BINARY_DIR_PATH = "tests/binary/"
|
||||
ASK_ORDER_BIN_PATH = _BINARY_DIR_PATH + "ask_order_binary.bin"
|
||||
EVENT_QUEUE_BIN_PATH = _BINARY_DIR_PATH + "event_queue_binary.bin"
|
||||
OPEN_ORDER_ACCOUNT_BIN_PATH = _BINARY_DIR_PATH + "open_order_account_binary.bin"
|
||||
|
|
|
@ -4,6 +4,7 @@ from solana.account import Account
|
|||
from solana.publickey import PublicKey
|
||||
from solana.rpc.api import Client
|
||||
|
||||
from src.enums import OrderType, Side
|
||||
from src.market import Market
|
||||
|
||||
from .utils import confirm_transaction
|
||||
|
@ -77,3 +78,57 @@ def test_match_order(bootstrapped_market: Market, stubbed_payer: Account, http_c
|
|||
# There should be no ask order.
|
||||
asks = bootstrapped_market.load_asks()
|
||||
assert sum(1 for _ in asks) == 0
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_new_order(
|
||||
bootstrapped_market: Market,
|
||||
stubbed_payer: Account,
|
||||
http_client: Client,
|
||||
stubbed_quote_wallet: Account,
|
||||
stubbed_base_wallet: Account,
|
||||
):
|
||||
initial_request_len = len(bootstrapped_market.load_request_queue())
|
||||
sig = bootstrapped_market.place_order(
|
||||
payer=stubbed_quote_wallet.public_key(),
|
||||
owner=stubbed_payer,
|
||||
side=Side.Buy,
|
||||
order_type=OrderType.Limit,
|
||||
limit_price=1000,
|
||||
max_quantity=3000,
|
||||
)
|
||||
confirm_transaction(http_client, sig)
|
||||
|
||||
request_queue = bootstrapped_market.load_request_queue()
|
||||
# 0 request after matching.
|
||||
assert len(request_queue) == initial_request_len + 1
|
||||
|
||||
# There should be no bid order.
|
||||
bids = bootstrapped_market.load_bids()
|
||||
assert sum(1 for _ in bids) == 0
|
||||
|
||||
# There should be no ask order.
|
||||
asks = bootstrapped_market.load_asks()
|
||||
assert sum(1 for _ in asks) == 0
|
||||
|
||||
sig = bootstrapped_market.place_order(
|
||||
payer=stubbed_base_wallet.public_key(),
|
||||
owner=stubbed_payer,
|
||||
side=Side.Sell,
|
||||
order_type=OrderType.Limit,
|
||||
limit_price=1500,
|
||||
max_quantity=3000,
|
||||
)
|
||||
confirm_transaction(http_client, sig)
|
||||
|
||||
# The two order shouldn't get executed since there is a price difference of 1
|
||||
sig = bootstrapped_market.match_orders(stubbed_payer, 2)
|
||||
confirm_transaction(http_client, sig)
|
||||
|
||||
# There should be 1 bid order that we sent earlier.
|
||||
bids = bootstrapped_market.load_bids()
|
||||
assert sum(1 for _ in bids) == 1
|
||||
|
||||
# There should be 1 ask order that we sent earlier.
|
||||
asks = bootstrapped_market.load_asks()
|
||||
assert sum(1 for _ in asks) == 1
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import base64
|
||||
|
||||
from solana.publickey import PublicKey
|
||||
|
||||
from src.open_order_account import OPEN_ORDERS_LAYOUT, OpenOrderAccount
|
||||
|
||||
from .binary_file_path import OPEN_ORDER_ACCOUNT_BIN_PATH
|
||||
|
||||
|
||||
def test_decode_open_order_account_layout():
|
||||
"""Test decode event queue."""
|
||||
with open(OPEN_ORDER_ACCOUNT_BIN_PATH, "r") as input_file:
|
||||
base64_res = input_file.read()
|
||||
data = base64.decodebytes(base64_res.encode("ascii"))
|
||||
open_order_account = OPEN_ORDERS_LAYOUT.parse(data)
|
||||
assert open_order_account.account_flags.open_orders
|
||||
assert open_order_account.account_flags.initialized
|
||||
assert PublicKey(open_order_account.market) == PublicKey("4r5Bw3HxmxAzPQ2ATUvgF2nFe3B6G1Z2Nq2Nwu77wWc2")
|
||||
assert PublicKey(open_order_account.owner) == PublicKey("7hJx7QMiVfjZSSADQ18oNKzqifJPMu18djYLkh4aYh5Q")
|
||||
# if there is no order the byte returned here will be all 0. In this case we have three orders.
|
||||
assert len([order for order in open_order_account.orders if int.from_bytes(order, "little") != 0]) == 3
|
||||
# the first three order are bid order
|
||||
assert int.from_bytes(open_order_account.is_bid_bits, "little") == 0b111
|
||||
|
||||
|
||||
def test_decode_open_order_account():
|
||||
"""Test decode event queue."""
|
||||
with open(OPEN_ORDER_ACCOUNT_BIN_PATH, "r") as input_file:
|
||||
base64_res = input_file.read()
|
||||
data = base64.decodebytes(base64_res.encode("ascii"))
|
||||
open_order_account = OpenOrderAccount.from_bytes(PublicKey(1), data)
|
||||
assert open_order_account.market == PublicKey("4r5Bw3HxmxAzPQ2ATUvgF2nFe3B6G1Z2Nq2Nwu77wWc2")
|
||||
assert open_order_account.owner == PublicKey("7hJx7QMiVfjZSSADQ18oNKzqifJPMu18djYLkh4aYh5Q")
|
||||
assert len([order for order in open_order_account.orders if order != 0]) == 3
|
||||
# the first three order are bid order
|
||||
assert open_order_account.is_bid_bits == 0b111
|
Loading…
Reference in New Issue