Package: mango-explorer
🥭 Mango Explorer⚠ WarningTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.🥭 Introductionmango-explorerprovides Python code to integrate withMango Markets. The canonical client is written in typescript and by far better maintaned, hence we recommend to use it instead. You can find ithere.It includes some useful command-line programs, such as:depositwithdrawplace-ordermarket-buymarket-sellsend-tokensThere are also more complex programs such as a runnablemarketmakerfor Serum, Spot and Perp markets.📦 Installationmango-exploreris available as aPython package on Pypiand can be installed as:pip install mango-explorerA simpleinstallation walkthroughis available, and of course other ways of installing it or adding it as a dependency are possible and will depend on the particular tools you are using.🌳 BranchesThe latest version of the code is in themain branch on Github.Code to integrate with Version 2 of Mango is in thev2 branch.📓 ExamplesHere are some example snippets to get you started.Many more examples are provided in a separateGithub repoand can berun in your browser (no installation required!) at Binder.🏃 Show OrderBookFull runnable code inmango-explorer-examplesrepoThis code will connect to thedevnetcluster, fetch the orderbook for SOL-PERP, and print out a summary of it:import mango

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    market = mango.market(context, "SOL-PERP")
    print(market.fetch_orderbook(context))🏃 Subscribe to OrderBookFull runnable code inmango-explorer-examplesrepoThis code will connect to thedevnetcluster and will print out the latest orderbook every time the orderbook changes, and will exit after 60 seconds.import datetime
import mango
import time

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    market = mango.market(context, "SOL-PERP")
    subscription = market.on_orderbook_change(context, lambda ob: print("\n", datetime.datetime.now(), "\n", ob))

    time.sleep(60)

    subscription.dispose()🏃 Show Perp FillsFull runnable code inmango-explorer-examplesrepoA 'fill' is when a maker order from the orderbook is matched with an incoming taker order. It can be useful to see these.This code will connect to thedevnetcluster and fetch all recent events. It will then show all the fill events.import mango

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    market = mango.market(context, "SOL-PERP")
    event_queue = mango.PerpEventQueue.load(context, market.event_queue_address, market.lot_size_converter)
    print(event_queue.fills)🏃 Subscribe to FillsFull runnable code inmango-explorer-examplesrepoThis code will connect to thedevnetcluster and print out every fill that happens. It will exit after 60 seconds.import datetime
import mango
import time

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    market = mango.market(context, "SOL-PERP")
    subscription = market.on_fill(context, lambda ob: print("\n", datetime.datetime.now(), "\n", ob))

    time.sleep(60)

    subscription.dispose()🏃 Place OrderFull runnable code inmango-explorer-examplesrepoThis code will load the 'example' wallet, connect to thedevnetcluster, place an order and wait for the order's transaction signature to be confirmed.The order is an IOC ('Immediate Or Cancel') order to buy 2 SOL-PERP at $1. This order is unlikely to be filled (SOL currently costs substantially more than $1), but that saves having to show order cancellation code here.Possibleorder_typevalues are:mango.OrderType.LIMITmango.OrderType.IOCmango.OrderType.POST_ONLYmango.OrderType.MARKET (it's nearly always better to use IOC and a price with acceptable slippage)mango.OrderType.POST_ONLY_SLIDE (only available on perp markets)import decimal
import mango

from solana.publickey import PublicKey

# Use our hard-coded devnet wallet for DeekipCw5jz7UgQbtUbHQckTYGKXWaPQV4xY93DaiM6h.
# For real-world use you'd load the bytes from the environment or a file. Later we use
# its Mango Account at HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL.
wallet = mango.Wallet(bytes([67,218,68,118,140,171,228,222,8,29,48,61,255,114,49,226,239,89,151,110,29,136,149,118,97,189,163,8,23,88,246,35,187,241,107,226,47,155,40,162,3,222,98,203,176,230,34,49,45,8,253,77,136,241,34,4,80,227,234,174,103,11,124,146]))

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    group = mango.Group.load(context)
    account = mango.Account.load(context, PublicKey("HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL"), group)
    market_operations = mango.operations(context, wallet, account, "SOL-PERP", dry_run=False)

    # Try to buy 2 SOL for $1.
    order = mango.Order.from_values(side=mango.Side.BUY,
                                    price=decimal.Decimal(1),
                                    quantity=decimal.Decimal(2),
                                    order_type=mango.OrderType.IOC)
    print("Placing order:", order)
    placed_order_signatures = market_operations.place_order(order)

    print("Waiting for place order transaction to confirm...\n", placed_order_signatures)
    mango.WebSocketTransactionMonitor.wait_for_all(
            context.client.cluster_ws_url, placed_order_signatures, commitment="processed"
        )🏃 Place and Cancel OrderFull runnable code inmango-explorer-examplesrepoThis code will load the 'example' wallet, connect to thedevnetcluster, place an order and then cancel it.This is a bit longer than the Place Order example but only because it performs most of the Place Order code as a setup to create the order so there's an order to cancel.The key point is thatcancel_order()takes anOrderas a parameter, and thatOrderneeds either theidorclient_idproperty to be set so the Mango program can find the equivalent order data on-chain. The sample code specified 1001 as theclient_idwhen the order was instantiated so it's fine for use here.import decimal
import mango

from solana.publickey import PublicKey

# Use our hard-coded devnet wallet for DeekipCw5jz7UgQbtUbHQckTYGKXWaPQV4xY93DaiM6h.
# For real-world use you'd load the bytes from the environment or a file. Later we use
# its Mango Account at HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL.
wallet = mango.Wallet(bytes([67,218,68,118,140,171,228,222,8,29,48,61,255,114,49,226,239,89,151,110,29,136,149,118,97,189,163,8,23,88,246,35,187,241,107,226,47,155,40,162,3,222,98,203,176,230,34,49,45,8,253,77,136,241,34,4,80,227,234,174,103,11,124,146]))

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    group = mango.Group.load(context)
    account = mango.Account.load(context, PublicKey("HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL"), group)
    market_operations = mango.operations(context, wallet, account, "SOL-PERP", dry_run=False)

    print("Orders (initial):")
    print(market_operations.load_orderbook())

    order = mango.Order.from_values(side=mango.Side.BUY,
                                    price=decimal.Decimal(10),
                                    quantity=decimal.Decimal(1),
                                    order_type=mango.OrderType.POST_ONLY)
    print("Placing order:", order)
    placed_order_signatures = market_operations.place_order(order)

    print("Waiting for place order transaction to confirm...\n", placed_order_signatures)
    mango.WebSocketTransactionMonitor.wait_for_all(
            context.client.cluster_ws_url, placed_order_signatures, commitment="processed"
        )

    print("\n\nOrders (including our new order):")
    orderbook = market_operations.load_orderbook()
    print(orderbook)

    # Order has the client ID 1001 so we can use that Order object as a parameter here.
    cancellaton_signatures = market_operations.cancel_order(order)

    print("Waiting for cancel order transaction to confirm...\n", cancellaton_signatures)
    mango.WebSocketTransactionMonitor.wait_for_all(
            context.client.cluster_ws_url, cancellaton_signatures, commitment="processed"
        )

    print("\n\nOrders (without our order):")
    print(market_operations.load_orderbook())🏃 Refresh OrdersSolana's transaction mechanism allows for atomic cancel-and-replace of orders - either the entire transaction succeeds (old orders are cancelled, new orders are placed), or the entire transaction fails (no orders are cancelled, no orders are placed).Neither Serum nor Mango supports 'editing' or changing an order - to change the price or quantity for an order you must cancel it and replace it with an order with updated values.This code will loop 3 times around:in one transaction: cancelling all perp orders and placing bid and ask perp orders on SOL-PERPwait for that transaction to confirmpause for 5 secondsYou can verify the transaction signatures inSolana Explorerto see there is a single transaction containing aCancelAllPerpOrdersinstruction followed by twoPlacePerpOrder2instructions. Since they're all in the same transaction, they will all succeed or all fail - if any instruction fails, the previous instructions are not committed to the chain, as if they never happened.import decimal
import mango
import time

from solana.publickey import PublicKey

# Use our hard-coded devnet wallet for DeekipCw5jz7UgQbtUbHQckTYGKXWaPQV4xY93DaiM6h.
# For real-world use you'd load the bytes from the environment or a file. Later we use
# its Mango Account at HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL.
wallet = mango.Wallet(bytes([67,218,68,118,140,171,228,222,8,29,48,61,255,114,49,226,239,89,151,110,29,136,149,118,97,189,163,8,23,88,246,35,187,241,107,226,47,155,40,162,3,222,98,203,176,230,34,49,45,8,253,77,136,241,34,4,80,227,234,174,103,11,124,146]))

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    group = mango.Group.load(context)
    account = mango.Account.load(context, PublicKey("HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL"), group)
    market_operations = mango.operations(context, wallet, account, "SOL-PERP", dry_run=False)
    market_instructions: mango.PerpMarketInstructionBuilder = mango.instruction_builder(context, wallet, account, "SOL-PERP", dry_run=False)

    signers: mango.CombinableInstructions = mango.CombinableInstructions.from_wallet(wallet)

    for counter in range(3):
        print("\n\nOrders:")
        orderbook = market_operations.load_orderbook()
        print(orderbook)

        instructions = signers
        instructions += market_instructions.build_cancel_all_orders_instructions()
        buy = mango.Order.from_values(side=mango.Side.BUY,
                                      price=orderbook.top_bid.price,
                                      quantity=decimal.Decimal(1),
                                      order_type=mango.OrderType.POST_ONLY,
                                      client_id=counter+10)
        print(buy)
        instructions += market_instructions.build_place_order_instructions(buy)
        sell = mango.Order.from_values(side=mango.Side.SELL,
                                       price=orderbook.top_ask.price,
                                       quantity=decimal.Decimal(1),
                                       order_type=mango.OrderType.POST_ONLY,
                                       client_id=counter+20)
        print(sell)
        instructions += market_instructions.build_place_order_instructions(sell)

        signatures = instructions.execute(context)

        print("Waiting for refresh order transaction to confirm...\n", signatures)
        mango.WebSocketTransactionMonitor.wait_for_all(
                context.client.cluster_ws_url, signatures, commitment="processed"
            )

        print("Sleeping for 5 seconds...")
        time.sleep(5)

print("Cleaning up...")
instructions = signers
instructions += market_instructions.build_cancel_all_orders_instructions()

cleanup_signatures = instructions.execute(context)

print("Waiting for cleanup transaction to confirm...\n", cleanup_signatures)
mango.WebSocketTransactionMonitor.wait_for_all(
        context.client.cluster_ws_url, cleanup_signatures, commitment="processed"
    )

print("Example complete.")🏃 Show Account DataFull runnable code inmango-explorer-examplesrepoThis code will connect to thedevnetcluster and show important data from a Mango Account.Data on deposits, borrows and perp positions are all available in apandasDataFramefor you to perform your own calculations upon. TheAccountclass also has some methods to take thisDataFrameand run common calculations on it, such as calculating the total value of theAccount(usingtotal_value()), the health of the account (usinginit_health()andmaint_health()), or the account's leverage (usingleverage()).import mango

from solana.publickey import PublicKey

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    group = mango.Group.load(context)
    cache: mango.Cache = mango.Cache.load(context, group.cache)

    account = mango.Account.load(context, PublicKey("HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL"), group)
    open_orders = account.load_all_spot_open_orders(context)
    frame = account.to_dataframe(group, open_orders, cache)
    print(frame)
    print(f"Init Health: {account.init_health(frame)}")
    print(f"Maint Health: {account.maint_health(frame)}")
    print(f"Total Value: {account.total_value(frame)}")
    print(f"Leverage: {account.leverage(frame):,.2f}x")🏃 Subscribe to Account changesFull runnable code inmango-explorer-examplesrepoThis code will connect to thedevnetcluster and print out the MangoAccountevery time it changes. It will exit after 60 seconds.Thesubscribe()method takes a lambda callback as a parameter - this lambda will be called with a newAccountobject when an update is received.This code will exit after 60 seconds.This pattern can also be used withGroup,CacheandEventQueueobjects.NOTE:This will send an updatedAccountobject, including whatever updated values triggered the update. However a proper valuation of anAccountinvolves several more Mango data accounts so it's important to fetch all data fresh (includingGroup,Cacheand allOpenOrders) to calculate values.import datetime
import mango
import time

from solana.publickey import PublicKey

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    # Load our devnet Random Taker Mango Account - it should have some activity
    group = mango.Group.load(context)
    account = mango.Account.load(context, PublicKey("5JDWiJGmnvs1DZomMV3s9Ev6DAgCQ9Svxd81EHCapjnD"), group)

    manager = mango.IndividualWebSocketSubscriptionManager(context)
    subscription = account.subscribe(context, manager, lambda acc: print(datetime.datetime.now(), "\n", acc))
    manager.open()

    time.sleep(60)

    manager.dispose()🏃 DepositFull runnable code inmango-explorer-examplesrepoThis code will connect to thedevnetcluster and deposit 0.1 SOL into a MangoAccount.import decimal
import mango

from solana.publickey import PublicKey


# Use our hard-coded devnet wallet for DeekipCw5jz7UgQbtUbHQckTYGKXWaPQV4xY93DaiM6h.
# For real-world use you'd load the bytes from the environment or a file. This wallet
# has a Mango Account at HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL.
wallet = mango.Wallet(bytes([67,218,68,118,140,171,228,222,8,29,48,61,255,114,49,226,239,89,151,110,29,136,149,118,97,189,163,8,23,88,246,35,187,241,107,226,47,155,40,162,3,222,98,203,176,230,34,49,45,8,253,77,136,241,34,4,80,227,234,174,103,11,124,146]))

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    group = mango.Group.load(context)
    account = mango.Account.load(context, PublicKey("HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL"), group)
    sol_token = mango.token(context, "SOL")

    print("Wrapped SOL in account", account.slot_by_instrument(sol_token).net_value)

    deposit_value = mango.InstrumentValue(sol_token, decimal.Decimal("0.1"))
    deposit_signatures = account.deposit(context, wallet, deposit_value)
    print("Waiting for deposit transaction to confirm...", deposit_signatures)
    mango.WebSocketTransactionMonitor.wait_for_all(
            context.client.cluster_ws_url, deposit_signatures, commitment="processed"
        )

    account = mango.Account.load(context, account.address, group)
    print("Wrapped SOL after deposit", account.slot_by_instrument(sol_token).net_value)🏃 WithdrawFull runnable code inmango-explorer-examplesrepoThis code will connect to thedevnetcluster and withdraw 0.1 SOL from a MangoAccount.Unlikedeposit(), you specify an address as the destination. You can withdraw from your MangoAccountto anyone's wallet (be careful!). Also, you can withdraw tokens even if you don't own them (as long as you have sufficient collateral) by borrowing them -withdraw()'sallow_borrowboolean allows or prohibits borrowing tokens theAccountdoesn't own.import decimal
import mango

from solana.publickey import PublicKey


# Use our hard-coded devnet wallet for DeekipCw5jz7UgQbtUbHQckTYGKXWaPQV4xY93DaiM6h.
# For real-world use you'd load the bytes from the environment or a file. This wallet
# has a Mango Account at HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL.
wallet = mango.Wallet(bytes([67,218,68,118,140,171,228,222,8,29,48,61,255,114,49,226,239,89,151,110,29,136,149,118,97,189,163,8,23,88,246,35,187,241,107,226,47,155,40,162,3,222,98,203,176,230,34,49,45,8,253,77,136,241,34,4,80,227,234,174,103,11,124,146]))

with mango.ContextBuilder.build(cluster_name="devnet") as context:
    group = mango.Group.load(context)
    account = mango.Account.load(context, PublicKey("HhepjyhSzvVP7kivdgJH9bj32tZFncqKUwWidS1ja4xL"), group)
    sol_token = mango.token(context, "SOL")

    print("Wrapped SOL in account", account.slot_by_instrument(sol_token).net_value)

    withdraw_value = mango.InstrumentValue(sol_token, decimal.Decimal("0.1"))
    withdrawal_signatures = account.withdraw(context, wallet, wallet.address, withdraw_value, False)
    print("Waiting for withdraw transaction to confirm...", withdrawal_signatures)
    mango.WebSocketTransactionMonitor.wait_for_all(
            context.client.cluster_ws_url, withdrawal_signatures, commitment="processed"
        )

    account = mango.Account.load(context, account.address, group)
    print("Wrapped SOL after withdrawal", account.slot_by_instrument(sol_token).net_value)🏛️ Running the marketmakerThere is aMarketmaking Quickstart- a walkthrough of setting up and running the marketmaker on devnet, from setting up the account, depositing tokens, to running the marketmaker itself.It can take around 30 minutes to run through.Requirements:A server with docker installedNoteThis walkthrough is devnet-only so no actual funds are used or at-risk.🔖 ReferencesSolanaPyPySerumPython Decimal ClassPython Construct LibraryPython ObservablesRxPy Backpressure🥭 Support🥭 Mango Marketssupport is available at:Docs|Discord|Twitter|Github|Email