Taker (RFQ Submitter) Tutorial

This tutorial relies on context established in Prerequisites and Setup.

Pick Target Companies

Find the company ID of a specific counterparty you want to send an RFQ to:

companies = list(paginate(client.get_companies))
print(json.dumps(target_companies, indent=2))
# ...
target_companies = [
    "1ebc7ece-093e-42aa-8cdf-028ccb1fc68a",
    "ef7ec17a-c585-4239-b6bb-cd66c4022aee",
    "0158cdf0-8b8d-4047-943f-74f6ae3cb45d",
]

Or trade with all the companies at once:

my_company_id = client.get_status().result["auth"]["company_id"]
target_companies = [c["id"] for c in paginate(client.get_companies)
                    if c["id"] != my_company_id]

Create an RFQ

Let's trade a BTC future expiring a week from now. The RFQ itself will expire in an hour.

from datetime import datetime, timezone, timedelta
new_rfq = client.create_rfq(
    structure={"legs": [
      {
        "instrument": {
          "instrument_type": InstrumentType.DATED_FUTURE,
          "underlying": "BTC",
          "settlement_asset": "USDC",
          "expiry": (date.today() + timedelta(days=7)).isoformat()
        },
        "ratio": 1,
        "side": TradeSide.BUY
      }
    ]},
    qty="1",
    expires_at=(datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
    target_companies=target_companies
).result

Wait For Quotes

Poll your RFQ until you see some quote responses from the companies you targeted:

rfq = client.get_rfqs_sent(id=new_rfq["rfq_id"]).result[0]
print("BIDS:")
print(json.dumps(rfq["bids"], indent=2))
print("ASKS:")
print(json.dumps(rfq["asks"], indent=2))
Example output
BIDS:
[
  {
    "additional_margin_requirements": {
      "initial_margin": "1374.54360366191158",
      "maintenance_margin": "713.55106796599558"
    },
    "margin_requirements": {
      "initial_margin": "1325.29003407031158",
      "maintenance_margin": "664.29749837439558"
    },
    "parent_quote_id": "dd898543-0752-421d-b321-6556873322a5",
    "quote_price": "66050"
  },
  {
    "additional_margin_requirements": {
      "initial_margin": "1424.54360366191158",
      "maintenance_margin": "763.55106796599558"
    },
    "margin_requirements": {
      "initial_margin": "1325.29003407031158",
      "maintenance_margin": "664.29749837439558"
    },
    "parent_quote_id": "2c0e9b12-8aaa-4061-b8d7-455afe465f1f",
    "quote_price": "66000"
  }
]
ASKS:
[]

Select the ID of the quote you wish to accept:

parent_quote_id = "dd898543-0752-421d-b321-6556873322a5"

Accept the Quote

accepted = client.accept_quote(
    rfq_id=new_rfq["rfq_id"],
    parent_quote_id=parent_quote_id,
    side=TradeSide.SELL,  # or BUY if you want to target a quote with the best ask
).result
print(json.dumps(accepted, indent=2))
Example output
{
  "new_clearing_status": "pending_pool_creation",
  "pending_deposits_sum_qty": "1562.845507",
  "pending_settlement_pool": {
    "company_id": "f58b3d2f-b5a6-488d-b8a9-dc2b1f782be8",
    "data": {
      "creator_address": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
      "creator_company_margin_usage": {
        "balance": "0",
        "company": "f58b3d2f-b5a6-488d-b8a9-dc2b1f782be8",
        "margin_params": {
          "margin_mode": "simple",
          "params": {
            "asset_params": {},
            "auto_liquidation": true,
            "default_asset_param": {
              "futures_initial_margin": "0.02",
              "futures_leverage": "20000",
              "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"
          }
        },
        "margin_usage": {
          "initial_margin": "0",
          "maintenance_margin": "0"
        },
        "max_withdrawable_amount": "0"
      },
      "other_address": "0xa0ee7a142d267c1f36714e4a8f75612f20a79720",
      "other_company_margin_usage": {
        "balance": "0",
        "company": "1ebc7ece-093e-42aa-8cdf-028ccb1fc68a",
        "margin_params": {
          "margin_mode": "simple",
          "params": {
            "asset_params": {},
            "auto_liquidation": true,
            "default_asset_param": {
              "futures_initial_margin": "0.02",
              "futures_leverage": "20000",
              "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"
          }
        },
        "margin_usage": {
          "initial_margin": "0",
          "maintenance_margin": "0"
        },
        "max_withdrawable_amount": "0"
      },
      "pool_address": null,
      "pool_name": "Tutorial pool",
      "positions": [],
      "status": "pending"
    },
    "pool_id": "ae380917-075a-4d4b-87ab-c7bdf820815c"
  }
}

Wait for the Clearing Status

Because pending_settlement_pool is not null and new_clearing_status is pending_pool_creation in the above response, the maker chose to create a new settlement pool for this trade. Note that the pool already exists and has an ID but is in pending state.

The pool and the quote will progress automatically, but it will take a few seconds. Use the helper to wait until then:

quote = poller.wait_for_clearing_status(
    parent_quote_id=parent_quote_id,
    status=ClearingStatus.PENDING_TAKER_DEPOSIT_APPROVAL,
)
print(quote['clearing_status'])
Example output
pending_taker_deposit_approval

Note that if is poller returns successfully but quote['clearing_status'] is not pending_taker_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 no further action is required from you.

Approve USDC Spending for the Deposit

If the current clearing status is pending_taker_deposit_approval, you need to sign and submit a ERC-2612 spending approval for the deposit:

pool = client.get_settlement_pools(id=quote['pool_location']).result[0]

permits.sign_and_submit_decimal(
    pool_address=pool['data']['pool_address'],
    allowance=accepted['pending_deposits_sum_qty']
)

Wait for Clearing Status to Progress

Use the helper to wait until your deposit is approved:

quote = poller.wait_for_clearing_status(
    parent_quote_id=parent_quote_id,
    status=ClearingStatus.PENDING_MAKER_LAST_LOOK,
)

The maker of the trade (the company that responded to your quote) is responsible for the remaining steps.

You can continue polling the quote until it reaches one of the final clearing statuses: success_trades_booked_into_pool, or one starting with rejected_.

Last updated