diff --git a/examples/rfq-workflow.php b/examples/rfq-workflow.php new file mode 100644 index 0000000..ffa20d2 --- /dev/null +++ b/examples/rfq-workflow.php @@ -0,0 +1,170 @@ +auth(); // Uses private key from .env +} catch (Exception $e) { + echo "Authentication required for RFQ operations\n"; + echo "Set POLYMARKET_PRIVATE_KEY in your .env file\n"; + exit(1); +} + +echo "RFQ Workflow Examples\n"; +echo "====================\n\n"; + +// Example token ID +$tokenId = '21742633143463906290569050155826241533067272736897614950488156847949938836455'; + +// 1. Create an RFQ Request (Requester wants to buy/sell) +echo "1. Creating RFQ Request...\n"; +try { + $request = $client->clob()->rfq()->createRequest([ + 'token_id' => $tokenId, + 'side' => OrderSide::BUY->value, + 'size' => '100', + 'price' => '0.55', + 'expiry' => time() + 3600, // Expires in 1 hour + ]); + + $requestId = $request['id']; + echo "Created RFQ request: $requestId\n"; + echo "Token: $tokenId\n"; + echo "Side: BUY\n"; + echo "Size: 100 shares\n"; + echo "Target Price: \$0.55\n\n"; +} catch (Exception $e) { + echo "Error creating request: {$e->getMessage()}\n\n"; + $requestId = null; +} + +// 2. List all RFQ Requests +echo "2. Listing Active RFQ Requests...\n"; +try { + $requests = $client->clob()->rfq()->listRequests([ + 'state' => RfqState::ACTIVE->value, + 'limit' => 10, + 'sort_by' => RfqSortBy::CREATED->value, + 'sort_dir' => RfqSortDir::DESC->value, + ]); + + echo 'Found '.count($requests)." active RFQ requests\n\n"; +} catch (Exception $e) { + echo "Error listing requests: {$e->getMessage()}\n\n"; +} + +// 3. Create a Quote (Market Maker responds to request) +if ($requestId !== null) { + echo "3. Creating Quote for Request...\n"; + try { + $quote = $client->clob()->rfq()->createQuote([ + 'request_id' => $requestId, + 'price' => '0.54', + 'size' => '100', + 'expiry' => time() + 1800, // 30 minutes + ]); + + $quoteId = $quote['id']; + echo "Created quote: $quoteId\n"; + echo "Offered Price: \$0.54\n"; + echo "Size: 100 shares\n\n"; + } catch (Exception $e) { + echo "Error creating quote: {$e->getMessage()}\n\n"; + $quoteId = null; + } +} else { + $quoteId = null; +} + +// 4. List Quotes for a Request +if ($requestId !== null) { + echo "4. Listing Quotes for Request...\n"; + try { + $quotes = $client->clob()->rfq()->listQuotes([ + 'request_id' => $requestId, + 'sort_by' => RfqSortBy::PRICE->value, + ]); + + echo 'Found '.count($quotes)." quotes\n\n"; + } catch (Exception $e) { + echo "Error listing quotes: {$e->getMessage()}\n\n"; + } +} + +// 5. Accept a Quote (Requester accepts market maker's quote) +if ($quoteId !== null) { + echo "5. Accepting Quote...\n"; + try { + $acceptance = $client->clob()->rfq()->acceptQuote([ + 'quote_id' => $quoteId, + ]); + + echo "Quote accepted\n"; + echo "Orders created\n\n"; + } catch (Exception $e) { + echo "Error accepting quote: {$e->getMessage()}\n\n"; + } +} + +// 6. Approve Order (Market Maker's last look) +echo "6. Approving Order (Last Look)...\n"; +try { + $approval = $client->clob()->rfq()->approveOrder([ + 'order_id' => 'example-order-id', + ]); + + echo "Order approved\n"; + echo "Trade will execute\n\n"; +} catch (Exception $e) { + echo "Error approving order: {$e->getMessage()}\n\n"; +} + +// 7. Cancel a Quote (if needed) +if ($quoteId !== null) { + echo "7. Canceling Quote (Demo)...\n"; + try { + $cancellation = $client->clob()->rfq()->cancelQuote($quoteId); + echo "Quote canceled: $quoteId\n\n"; + } catch (Exception $e) { + echo "Error canceling quote: {$e->getMessage()}\n\n"; + } +} + +// 8. Cancel a Request (if needed) +if ($requestId !== null) { + echo "8. Canceling Request (Demo)...\n"; + try { + $cancellation = $client->clob()->rfq()->cancelRequest($requestId); + echo "Request canceled: $requestId\n"; + } catch (Exception $e) { + echo "Error canceling request: {$e->getMessage()}\n"; + } +} + +echo "\nRFQ Workflow Complete!\n"; +echo "\nNote: This example demonstrates the full workflow.\n"; +echo "In production, different parties (requesters and quoters) would perform different steps.\n"; diff --git a/src/Clob.php b/src/Clob.php index d9a792e..74381e7 100644 --- a/src/Clob.php +++ b/src/Clob.php @@ -15,6 +15,7 @@ use Danielgnh\PolymarketPhp\Resources\Clob\OrderScoring; use Danielgnh\PolymarketPhp\Resources\Clob\Pricing; use Danielgnh\PolymarketPhp\Resources\Clob\Rewards; +use Danielgnh\PolymarketPhp\Resources\Clob\Rfq; use Danielgnh\PolymarketPhp\Resources\Clob\Server; use Danielgnh\PolymarketPhp\Resources\Clob\Spreads; use Danielgnh\PolymarketPhp\Resources\Clob\Trades; @@ -35,6 +36,7 @@ * - Authentication: API key management * - Account: Balance, allowances, and notifications * - Rewards: Earnings and reward percentages + * - Rfq: Request for Quote (institutional trading) * - OrderScoring: Order scoring checks * - Server: Health checks and server info * @@ -121,4 +123,9 @@ public function server(): Server { return new Server($this->httpClient); } + + public function rfq(): Rfq + { + return new Rfq($this->httpClient); + } } diff --git a/src/Enums/RfqSortBy.php b/src/Enums/RfqSortBy.php new file mode 100644 index 0000000..62c3603 --- /dev/null +++ b/src/Enums/RfqSortBy.php @@ -0,0 +1,13 @@ + $requestData RFQ request data (tokenId, side, size, price, etc.) + * + * @return array + * + * @throws PolymarketException + */ + public function createRequest(array $requestData): array + { + $response = $this->httpClient->post('/rfq/requests', $requestData); + + return $response->json(); + } + + /** + * Cancel an RFQ request. + * + * @param string $requestId RFQ request ID + * + * @return array + * + * @throws PolymarketException + */ + public function cancelRequest(string $requestId): array + { + $response = $this->httpClient->delete("/rfq/requests/$requestId"); + + return $response->json(); + } + + /** + * List RFQ requests with pagination and filters. + * + * @param array $filters Filters (state, tokenId, limit, offset, sortBy, sortDir, etc.) + * + * @return array> + * + * @throws PolymarketException + */ + public function listRequests(array $filters = []): array + { + $response = $this->httpClient->get('/rfq/requests', $filters); + + /** @var array> */ + return $response->json(); + } + + /** + * Create a quote responding to an RFQ request. + * + * @param array $quoteData Quote data (requestId, price, size, expiry, etc.) + * + * @return array + * + * @throws PolymarketException + */ + public function createQuote(array $quoteData): array + { + $response = $this->httpClient->post('/rfq/quotes', $quoteData); + + return $response->json(); + } + + /** + * Cancel an RFQ quote. + * + * @param string $quoteId RFQ quote ID + * + * @return array + * + * @throws PolymarketException + */ + public function cancelQuote(string $quoteId): array + { + $response = $this->httpClient->delete("/rfq/quotes/$quoteId"); + + return $response->json(); + } + + /** + * List RFQ quotes with pagination and filters. + * + * @param array $filters Filters (state, requestId, limit, offset, sortBy, sortDir, etc.) + * + * @return array> + * + * @throws PolymarketException + */ + public function listQuotes(array $filters = []): array + { + $response = $this->httpClient->get('/rfq/quotes', $filters); + + /** @var array> */ + return $response->json(); + } + + /** + * Accept a quote and create orders (requester action). + * + * @param array $acceptData Acceptance data (quoteId, etc.) + * + * @return array + * + * @throws PolymarketException + */ + public function acceptQuote(array $acceptData): array + { + $response = $this->httpClient->post('/rfq/accept', $acceptData); + + return $response->json(); + } + + /** + * Approve an order during last look window (quoter action). + * + * @param array $approveData Approval data (orderId, etc.) + * + * @return array + * + * @throws PolymarketException + */ + public function approveOrder(array $approveData): array + { + $response = $this->httpClient->post('/rfq/approve', $approveData); + + return $response->json(); + } +}