Maker (RFQ Responder) Tutorial

This tutorial relies on context established in Prerequisites and Setup.

Pick a Received RFQ

Get a list of received RFQs in open status, evaluate them and pick one:

open_rfqs = [r for r in client.get_rfqs_received().result if r["rfq_status"] == "open"]
rfq = open_rfqs[0]
print(json.dumps(rfq, indent=2))
Output
{
  "asks": [],
  "bids": [],
  "clearing_status": null,
  "created_at": "2024-04-18T19:13:48.405834Z",
  "qty": "10000",
  "quotes_common_metadata": {},
  "rfq_expires_at": "2024-04-18T20:13:48.390Z",
  "rfq_id": "38ce49bf-9be9-4fd8-bc9a-5a90529c931f",
  "rfq_legs": [
    {
      "instrument": {
        "funding_interval_s": 3600,
        "instrument_type": "perpetual_future",
        "settlement_asset": "USDC",
        "underlying": "DOGE"
      },
      "qty": "10000",
      "rfq_id": "38ce49bf-9be9-4fd8-bc9a-5a90529c931f",
      "rfq_leg_id": "b55b6240-c245-4de9-a077-39f0edd66b86",
      "side": "buy"
    }
  ],
  "rfq_status": "open",
  "structure": {
    "legs": [
      {
        "instrument": {
          "funding_interval_s": 3600,
          "instrument_type": "perpetual_future",
          "settlement_asset": "USDC",
          "underlying": "DOGE"
        },
        "ratio": 1,
        "side": "buy"
      }
    ]
  },
  "structure_price": {
    "delta": "1",
    "gamma": "0",
    "native_price": "1",
    "price": "0.1499674821151633",
    "rho": "0",
    "theta": "0",
    "timestamp": "2024-04-18T19:14:09.870949585Z",
    "vega": "0"
  },
  "taker_company": "f58b3d2f-b5a6-488d-b8a9-dc2b1f782be8"
}

Create a Quote

from datetime import datetime, timezone, timedelta
from variational import PoolStrategyType, MarginMode

margin_params = {
    "margin_mode": MarginMode.SIMPLE,
    "params": {
        "liquidation_penalty": "0.1",
        "auto_liquidation": True,
        "asset_params": {},
        "default_asset_param": {
            "futures_initial_margin": "0.02",
            "futures_maintenance_margin": "0.01",
            "futures_leverage": "1000000000",
            "option_initial_margin": "0.15",
            "option_initial_margin_min": "0.1",
            "option_maintenance_margin": "0.075",
        },
    }
}
quote = client.create_quote(
    rfq_id=rfq["rfq_id"],
    expires_at=(datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
    leg_quotes=[
        {
            "target_rfq_leg_id": "b55b6240-c245-4de9-a077-39f0edd66b86",
            "bid": "0.148",
            "ask": None,  # both "bid" and "ask" could be included
        }
    ],
    pool_strategy={
        "strategy": PoolStrategyType.CREATE_NEW,
        "name": "Maker tutorial pool",
        "creator_params": margin_params,
        "other_params": margin_params,
    }
).result
print(json.dumps(quote, indent=2))
Output
{
  "aggregated_ask": null,
  "aggregated_bid": "0.148000000000",
  "clearing_events": [],
  "clearing_status": null,
  "client_quote_id": null,
  "creator_params": {
    "margin_mode": "simple",
    "params": {
      "asset_params": {},
      "auto_liquidation": true,
      "default_asset_param": {
        "futures_initial_margin": "0.02",
        "futures_leverage": "1000000000",
        "futures_maintenance_margin": "0.01",
        "option_initial_margin": "0.15",
        "option_initial_margin_min": "0.1",
        "option_maintenance_margin": "0.075"
      },
      "liquidation_penalty": "0.1"
    }
  },
  "expires_at": "2024-04-18T20:16:13.647117Z",
  "maker_company": "1ebc7ece-093e-42aa-8cdf-028ccb1fc68a",
  "new_pool_name": "Maker tutorial pool",
  "other_params": {
    "margin_mode": "simple",
    "params": {
      "asset_params": {},
      "auto_liquidation": true,
      "default_asset_param": {
        "futures_initial_margin": "0.02",
        "futures_leverage": "1000000000",
        "futures_maintenance_margin": "0.01",
        "option_initial_margin": "0.15",
        "option_initial_margin_min": "0.1",
        "option_maintenance_margin": "0.075"
      },
      "liquidation_penalty": "0.1"
    }
  },
  "parent_quote_id": "be295917-ac71-4417-bc4a-550de0fb4f3a",
  "per_leg_quotes": [
    {
      "ask": null,
      "bid": "0.148000000000",
      "target_rfq_leg_id": "b55b6240-c245-4de9-a077-39f0edd66b86"
    }
  ],
  "pool_location": null,
  "target_rfq_id": "38ce49bf-9be9-4fd8-bc9a-5a90529c931f"
}

Wait until Your Quote Is Accepted

Before you can take the next step, the creator of the RFQ needs to accept your quote and approve their deposit if needed. Then the quote clearing status will change to pending_maker_last_look.

You can use the helper to wait for a single quote:

from variational import ClearingStatus
long_poller = PollingHelper(client=client, interval=10, attempts=100)
quote = long_poller.wait_for_clearing_status(
    parent_quote_id=quote["parent_quote_id"],
    status=ClearingStatus.PENDING_MAKER_LAST_LOOK
)

Or check all outstanding quotes:

accepted_quotes = list(
    [q for q in paginate(client.get_quotes_sent)
     if q["clearing_status"] == ClearingStatus.PENDING_MAKER_LAST_LOOK])

Before your quote is accepted, you can update it by replacing it with a new one:

quote = client.replace_quote(
    rfq_id=rfq["rfq_id"],
    parent_quote_id=quote["parent_quote_id"],
    expires_at=(datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
    leg_quotes=[
        {
            "target_rfq_leg_id": "b55b6240-c245-4de9-a077-39f0edd66b86",
            "bid": "0.147",
            "ask": None,  # both "bid" and "ask" could be included
        }
    ],
    pool_strategy={
        "strategy": PoolStrategyType.CREATE_NEW,
        "name": "Maker tutorial pool",
        "creator_params": margin_params,
        "other_params": margin_params,
    }
).result

Perform a Last Look

When your quote is pending_maker_last_look and is still looking good, approve it:

from variational import RequestAction

last_look = client.maker_last_look(
    rfq_id=rfq["rfq_id"],
    parent_quote_id=quote["parent_quote_id"],
    action=RequestAction.ACCEPT,
).result
print(json.dumps(last_look, indent=2))
Output
{
  "new_clearing_status": "pending_maker_deposit_approval",
  "pending_deposits_sum_qty": "9.519183",
  "settlement_pool_address": "0x24b044aa809712867f6332aa465a302705013b92"
}

Note that if new_clearing_status is not pending_maker_deposit_approval, it means that the deposit for this trade is covered by a previously issued approval or there is enough funds in the pool, so you can skip the next part.

Approve USDC Spending for the Deposit

If new_clearing_status is pending_maker_deposit_approval, you need to sign and submit a ERC-2612 spending approval for the deposit:

permits.sign_and_submit_decimal(
    pool_address=last_look['settlement_pool_address'],
    allowance=last_look['pending_deposits_sum_qty']
)

Wait for Clearing Status to Progress

At this point, the trade should automatically progress to the final state success_trades_booked_into_pool, but it can take a few seconds.

Use the helper to wait for the trade to clear:

quote = poller.wait_for_clearing_status(
    parent_quote_id=quote["parent_quote_id"],
    status=ClearingStatus.SUCCESS_TRADES_BOOKED_INTO_POOL,
)

Last updated