-
Notifications
You must be signed in to change notification settings - Fork 2
feat: RFQ Workflow #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| require_once __DIR__.'/../vendor/autoload.php'; | ||
|
|
||
| use Danielgnh\PolymarketPhp\Client; | ||
| use Danielgnh\PolymarketPhp\Enums\OrderSide; | ||
| use Danielgnh\PolymarketPhp\Enums\RfqSortBy; | ||
| use Danielgnh\PolymarketPhp\Enums\RfqSortDir; | ||
| use Danielgnh\PolymarketPhp\Enums\RfqState; | ||
|
|
||
| /** | ||
| * Example: RFQ (Request for Quote) Workflow. | ||
| * | ||
| * This example demonstrates the complete RFQ workflow for institutional trading: | ||
| * 1. Requester creates an RFQ request | ||
| * 2. Market maker creates a quote in response | ||
| * 3. Requester accepts the quote | ||
| * 4. Market maker approves the order (last look) | ||
| * | ||
| * RFQ is designed for large orders and negotiated pricing. | ||
| */ | ||
|
|
||
| // Initialize client with authentication | ||
| $client = new Client(); | ||
|
|
||
| // Setup authentication (required for RFQ operations) | ||
| try { | ||
| $client->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"; | ||
|
Comment on lines
+133
to
+143
|
||
| } | ||
|
|
||
| // 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"; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
| } | ||
|
Comment on lines
+127
to
+130
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Danielgnh\PolymarketPhp\Enums; | ||
|
|
||
| enum RfqSortBy: string | ||
| { | ||
| case PRICE = 'price'; | ||
| case EXPIRY = 'expiry'; | ||
| case SIZE = 'size'; | ||
| case CREATED = 'created'; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Danielgnh\PolymarketPhp\Enums; | ||
|
|
||
| enum RfqSortDir: string | ||
| { | ||
| case ASC = 'asc'; | ||
| case DESC = 'desc'; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Danielgnh\PolymarketPhp\Enums; | ||
|
|
||
| enum RfqState: string | ||
| { | ||
| case ACTIVE = 'active'; | ||
| case INACTIVE = 'inactive'; | ||
| case UNKNOWN = 'unknown'; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Danielgnh\PolymarketPhp\Resources\Clob; | ||
|
|
||
| use Danielgnh\PolymarketPhp\Exceptions\PolymarketException; | ||
| use Danielgnh\PolymarketPhp\Resources\Resource; | ||
|
|
||
| /** | ||
| * RFQ (Request for Quote) Resource. | ||
| * | ||
| * Handles institutional/large order workflow for negotiated pricing. | ||
| * Enables users to request quotes from market makers and vice versa. | ||
| */ | ||
| class Rfq extends Resource | ||
| { | ||
| /** | ||
| * Create an RFQ request for buying/selling outcome tokens. | ||
| * | ||
| * @param array<string, mixed> $requestData RFQ request data (tokenId, side, size, price, etc.) | ||
| * | ||
|
Comment on lines
+19
to
+22
|
||
| * @return array<string, mixed> | ||
| * | ||
| * @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<string, mixed> | ||
| * | ||
| * @throws PolymarketException | ||
| */ | ||
| public function cancelRequest(string $requestId): array | ||
| { | ||
| $response = $this->httpClient->delete("/rfq/requests/$requestId"); | ||
|
|
||
|
Comment on lines
+45
to
+46
|
||
| return $response->json(); | ||
| } | ||
|
|
||
| /** | ||
| * List RFQ requests with pagination and filters. | ||
| * | ||
| * @param array<string, mixed> $filters Filters (state, tokenId, limit, offset, sortBy, sortDir, etc.) | ||
| * | ||
| * @return array<int, array<string, mixed>> | ||
| * | ||
| * @throws PolymarketException | ||
| */ | ||
| public function listRequests(array $filters = []): array | ||
| { | ||
| $response = $this->httpClient->get('/rfq/requests', $filters); | ||
|
|
||
| /** @var array<int, array<string, mixed>> */ | ||
| return $response->json(); | ||
| } | ||
|
|
||
| /** | ||
| * Create a quote responding to an RFQ request. | ||
| * | ||
| * @param array<string, mixed> $quoteData Quote data (requestId, price, size, expiry, etc.) | ||
| * | ||
| * @return array<string, mixed> | ||
| * | ||
| * @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<string, mixed> | ||
| * | ||
| * @throws PolymarketException | ||
| */ | ||
| public function cancelQuote(string $quoteId): array | ||
| { | ||
| $response = $this->httpClient->delete("/rfq/quotes/$quoteId"); | ||
|
|
||
|
Comment on lines
+94
to
+95
|
||
| return $response->json(); | ||
| } | ||
|
|
||
| /** | ||
| * List RFQ quotes with pagination and filters. | ||
| * | ||
| * @param array<string, mixed> $filters Filters (state, requestId, limit, offset, sortBy, sortDir, etc.) | ||
| * | ||
| * @return array<int, array<string, mixed>> | ||
| * | ||
| * @throws PolymarketException | ||
| */ | ||
| public function listQuotes(array $filters = []): array | ||
| { | ||
| $response = $this->httpClient->get('/rfq/quotes', $filters); | ||
|
|
||
| /** @var array<int, array<string, mixed>> */ | ||
| return $response->json(); | ||
| } | ||
|
|
||
| /** | ||
| * Accept a quote and create orders (requester action). | ||
| * | ||
| * @param array<string, mixed> $acceptData Acceptance data (quoteId, etc.) | ||
| * | ||
| * @return array<string, mixed> | ||
| * | ||
| * @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<string, mixed> $approveData Approval data (orderId, etc.) | ||
| * | ||
| * @return array<string, mixed> | ||
| * | ||
| * @throws PolymarketException | ||
| */ | ||
| public function approveOrder(array $approveData): array | ||
| { | ||
| $response = $this->httpClient->post('/rfq/approve', $approveData); | ||
|
|
||
| return $response->json(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor consistency: other example scripts use spaced concatenation for
__DIR__(e.g.,examples/bridge-deposit.php:5uses__DIR__ . '/../vendor/autoload.php'). Consider matching that formatting here for consistency.