2021-07-15 13:03:22 -07:00
|
|
|
#!/usr/bin/env pyston3
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import rx
|
|
|
|
import sys
|
2021-07-19 07:14:00 -07:00
|
|
|
import threading
|
2021-08-21 14:06:58 -07:00
|
|
|
import typing
|
2021-07-15 13:03:22 -07:00
|
|
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
sys.path.insert(0, os.path.abspath(
|
|
|
|
os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
import mango # nopep8
|
2021-07-26 07:47:57 -07:00
|
|
|
import mango.marketmaking # nopep8
|
2021-08-22 11:48:20 -07:00
|
|
|
from mango.marketmaking.orderchain import chain # nopep8
|
|
|
|
from mango.marketmaking.orderchain import chainbuilder # nopep8
|
2021-07-15 13:03:22 -07:00
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Shows the on-chain data of a particular account.")
|
2021-07-23 02:20:44 -07:00
|
|
|
mango.ContextBuilder.add_command_line_parameters(parser)
|
2021-07-15 13:03:22 -07:00
|
|
|
mango.Wallet.add_command_line_parameters(parser)
|
2021-08-22 11:48:20 -07:00
|
|
|
chainbuilder.ChainBuilder.add_command_line_parameters(parser)
|
2021-07-15 13:03:22 -07:00
|
|
|
parser.add_argument("--market", type=str, required=True, help="market symbol to make market upon (e.g. ETH/USDC)")
|
|
|
|
parser.add_argument("--oracle-provider", type=str, required=True,
|
|
|
|
help="name of the price provider to use (e.g. pyth)")
|
|
|
|
parser.add_argument("--existing-order-tolerance", type=Decimal, default=Decimal("0.001"),
|
|
|
|
help="tolerance in price and quantity when matching existing orders or cancelling/replacing")
|
2021-07-16 06:26:42 -07:00
|
|
|
parser.add_argument("--pulse-interval", type=int, default=10,
|
|
|
|
help="number of seconds between each 'pulse' of the market maker")
|
2021-07-31 06:43:39 -07:00
|
|
|
parser.add_argument("--account-index", type=int, default=0,
|
|
|
|
help="index of the account to use, if more than one available")
|
2021-07-31 09:37:51 -07:00
|
|
|
parser.add_argument("--notify-errors", type=mango.parse_subscription_target, action="append", default=[],
|
|
|
|
help="The notification target for error events")
|
2021-07-15 13:03:22 -07:00
|
|
|
parser.add_argument("--dry-run", action="store_true", default=False,
|
|
|
|
help="runs as read-only and does not perform any transactions")
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
logging.getLogger().setLevel(args.log_level)
|
|
|
|
logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
2021-07-31 09:37:51 -07:00
|
|
|
for notify in args.notify_errors:
|
|
|
|
handler = mango.NotificationHandler(notify)
|
|
|
|
handler.setLevel(logging.ERROR)
|
|
|
|
logging.getLogger().addHandler(handler)
|
2021-07-15 13:03:22 -07:00
|
|
|
|
|
|
|
|
2021-07-31 06:43:39 -07:00
|
|
|
def cleanup(context: mango.Context, wallet: mango.Wallet, account: mango.Account, market: mango.Market, dry_run: bool):
|
2021-07-15 13:03:22 -07:00
|
|
|
logging.info("Cleaning up.")
|
2021-07-31 06:43:39 -07:00
|
|
|
market_operations: mango.MarketOperations = mango.create_market_operations(
|
|
|
|
context, wallet, account, market, dry_run)
|
2021-07-15 13:03:22 -07:00
|
|
|
orders = market_operations.load_my_orders()
|
|
|
|
for order in orders:
|
|
|
|
market_operations.cancel_order(order)
|
2021-07-20 02:24:05 -07:00
|
|
|
logging.info("Settling.")
|
|
|
|
market_operations.crank()
|
|
|
|
market_operations.settle()
|
2021-07-15 13:03:22 -07:00
|
|
|
|
|
|
|
|
2021-07-23 02:20:44 -07:00
|
|
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
2021-08-19 15:02:53 -07:00
|
|
|
logging.info(f"{context}")
|
2021-07-15 13:03:22 -07:00
|
|
|
|
|
|
|
disposer = mango.DisposePropagator()
|
2021-08-21 08:03:13 -07:00
|
|
|
manager = mango.IndividualWebSocketSubscriptionManager(context)
|
2021-07-15 13:03:22 -07:00
|
|
|
disposer.add_disposable(manager)
|
2021-08-17 04:17:49 -07:00
|
|
|
health_check = mango.HealthCheck()
|
|
|
|
disposer.add_disposable(health_check)
|
2021-07-15 13:03:22 -07:00
|
|
|
|
|
|
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
|
|
|
group = mango.Group.load(context, context.group_id)
|
2021-07-31 06:43:39 -07:00
|
|
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
2021-07-15 13:03:22 -07:00
|
|
|
|
|
|
|
market_symbol = args.market.upper()
|
|
|
|
market = context.market_lookup.find_by_symbol(market_symbol)
|
|
|
|
if market is None:
|
|
|
|
raise Exception(f"Could not find market {market_symbol}")
|
|
|
|
|
|
|
|
# The market index is also the index of the base token in the group's token list.
|
|
|
|
if market.quote != group.shared_quote_token.token:
|
|
|
|
raise Exception(
|
2021-07-16 09:24:55 -07:00
|
|
|
f"Group {group.name} uses shared quote token {group.shared_quote_token.token.symbol}/{group.shared_quote_token.token.mint}, but market {market.symbol} uses quote token {market.quote.symbol}/{market.quote.mint}.")
|
2021-07-15 13:03:22 -07:00
|
|
|
|
2021-07-31 06:43:39 -07:00
|
|
|
cleanup(context, wallet, account, market, args.dry_run)
|
2021-07-15 13:03:22 -07:00
|
|
|
|
2021-08-17 04:17:49 -07:00
|
|
|
latest_group_observer = mango.build_group_watcher(context, manager, health_check, group)
|
|
|
|
account_subscription, latest_account_observer = mango.build_account_watcher(
|
|
|
|
context, manager, health_check, account, latest_group_observer)
|
|
|
|
latest_price_observer = mango.build_price_watcher(
|
|
|
|
context, manager, health_check, disposer, args.oracle_provider, market)
|
2021-07-15 13:03:22 -07:00
|
|
|
|
2021-07-23 06:18:26 -07:00
|
|
|
market = mango.ensure_market_loaded(context, market)
|
|
|
|
market_instruction_builder: mango.MarketInstructionBuilder = mango.create_market_instruction_builder(
|
2021-07-31 06:43:39 -07:00
|
|
|
context, wallet, account, market, args.dry_run)
|
2021-07-15 13:03:22 -07:00
|
|
|
if isinstance(market, mango.SerumMarket):
|
2021-08-17 04:17:49 -07:00
|
|
|
inventory_watcher: mango.Watcher[mango.Inventory] = mango.build_serum_inventory_watcher(
|
|
|
|
context, manager, health_check, disposer, wallet, market)
|
2021-08-21 14:06:58 -07:00
|
|
|
latest_open_orders_observer: mango.Watcher[mango.PlacedOrdersContainer] = mango.build_serum_open_orders_watcher(
|
|
|
|
context, manager, health_check, market, wallet)
|
|
|
|
latest_bids_watcher: mango.Watcher[typing.Sequence[mango.Order]] = mango.build_serum_orderbook_side_watcher(
|
|
|
|
context, manager, health_check, market.underlying_serum_market, mango.OrderBookSideType.BIDS)
|
|
|
|
latest_asks_watcher: mango.Watcher[typing.Sequence[mango.Order]] = mango.build_serum_orderbook_side_watcher(
|
|
|
|
context, manager, health_check, market.underlying_serum_market, mango.OrderBookSideType.ASKS)
|
2021-07-15 13:03:22 -07:00
|
|
|
elif isinstance(market, mango.SpotMarket):
|
2021-08-20 16:00:34 -07:00
|
|
|
inventory_watcher = mango.SpotInventoryAccountWatcher(market, latest_account_observer)
|
2021-08-17 04:17:49 -07:00
|
|
|
latest_open_orders_observer = mango.build_spot_open_orders_watcher(
|
|
|
|
context, manager, health_check, wallet, account, group, market)
|
2021-08-21 14:06:58 -07:00
|
|
|
latest_bids_watcher = mango.build_serum_orderbook_side_watcher(
|
|
|
|
context, manager, health_check, market.underlying_serum_market, mango.OrderBookSideType.BIDS)
|
|
|
|
latest_asks_watcher = mango.build_serum_orderbook_side_watcher(
|
|
|
|
context, manager, health_check, market.underlying_serum_market, mango.OrderBookSideType.ASKS)
|
2021-07-23 06:18:26 -07:00
|
|
|
elif isinstance(market, mango.PerpMarket):
|
2021-08-20 16:00:34 -07:00
|
|
|
inventory_watcher = mango.PerpInventoryAccountWatcher(market, latest_account_observer, group)
|
2021-08-17 04:17:49 -07:00
|
|
|
latest_open_orders_observer = mango.build_perp_open_orders_watcher(
|
|
|
|
context, manager, health_check, market, account, group, account_subscription)
|
2021-08-21 14:06:58 -07:00
|
|
|
latest_bids_watcher = mango.build_perp_orderbook_side_watcher(
|
|
|
|
context, manager, health_check, market, mango.OrderBookSideType.BIDS)
|
|
|
|
latest_asks_watcher = mango.build_perp_orderbook_side_watcher(
|
|
|
|
context, manager, health_check, market, mango.OrderBookSideType.ASKS)
|
2021-07-15 13:03:22 -07:00
|
|
|
else:
|
|
|
|
raise Exception(f"Could not determine type of market {market.symbol}")
|
|
|
|
|
2021-08-21 05:25:28 -07:00
|
|
|
manager.open()
|
2021-07-15 13:03:22 -07:00
|
|
|
|
2021-07-26 07:47:57 -07:00
|
|
|
order_reconciler = mango.marketmaking.ToleranceOrderReconciler(
|
2021-07-15 13:03:22 -07:00
|
|
|
args.existing_order_tolerance, args.existing_order_tolerance)
|
2021-08-22 11:48:20 -07:00
|
|
|
|
|
|
|
desired_orders_chain: chain.Chain = chainbuilder.ChainBuilder.from_command_line_parameters(args)
|
2021-07-26 07:47:57 -07:00
|
|
|
market_maker = mango.marketmaking.MarketMaker(
|
2021-08-22 11:48:20 -07:00
|
|
|
wallet, market, market_instruction_builder, desired_orders_chain, order_reconciler)
|
2021-07-26 07:47:57 -07:00
|
|
|
model_state = mango.marketmaking.ModelState(market, latest_account_observer,
|
|
|
|
latest_group_observer, latest_price_observer,
|
2021-08-21 14:06:58 -07:00
|
|
|
latest_open_orders_observer, inventory_watcher,
|
|
|
|
latest_bids_watcher, latest_asks_watcher)
|
2021-07-16 06:26:42 -07:00
|
|
|
|
2021-08-17 04:17:49 -07:00
|
|
|
health_check.add("marketmaker_pulse", market_maker.pulse_complete)
|
2021-07-16 06:26:42 -07:00
|
|
|
|
2021-08-07 10:42:39 -07:00
|
|
|
logging.info(f"Current assets in account {account.address} (owner: {account.owner}):")
|
2021-07-21 10:25:44 -07:00
|
|
|
mango.TokenValue.report([asset for asset in account.net_assets if asset is not None], logging.info)
|
|
|
|
|
2021-07-16 06:26:42 -07:00
|
|
|
pulse_disposable = rx.interval(args.pulse_interval).subscribe(
|
|
|
|
on_next=lambda _: market_maker.pulse(context, model_state))
|
2021-07-15 13:03:22 -07:00
|
|
|
disposer.add_disposable(pulse_disposable)
|
|
|
|
|
2021-07-19 07:14:00 -07:00
|
|
|
# Wait - don't exit. Exiting will be handled by signals/interrupts.
|
|
|
|
waiter = threading.Event()
|
|
|
|
try:
|
|
|
|
waiter.wait()
|
|
|
|
except:
|
|
|
|
pass
|
2021-07-15 13:03:22 -07:00
|
|
|
|
2021-07-20 02:24:05 -07:00
|
|
|
logging.info("Shutting down...")
|
2021-07-15 13:03:22 -07:00
|
|
|
disposer.dispose()
|
2021-07-31 06:43:39 -07:00
|
|
|
cleanup(context, wallet, account, market, args.dry_run)
|
2021-07-20 02:24:05 -07:00
|
|
|
logging.info("Shutdown complete.")
|