-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfetchQuotes.ts
More file actions
133 lines (113 loc) · 4.3 KB
/
Copy pathfetchQuotes.ts
File metadata and controls
133 lines (113 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { writeFileSync } from "fs";
const BASE_URL = "https://api.jup.ag/swap/v1";
const API_KEY = process.env.JUP_API_KEY!;
const SOL_MINT = "So11111111111111111111111111111111111111112";
const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const SOL_DECIMALS = 9;
const USDC_DECIMALS = 6;
const TRADE_SIZES_USD = [100, 1_000, 10_000, 50_000, 100_000];
const VENUES = { orderbook: "Phoenix", amm: "Raydium" } as const;
const SLEEP_MS = 400;
const RATE_LIMIT_SLEEP_MS = 1000;
const MAX_RETRIES = 3;
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getQuote(params: Record<string, string>): Promise<any> {
const url = `${BASE_URL}/quote?${new URLSearchParams(params).toString()}`;
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
const res = await fetch(url, { headers: { "x-api-key": API_KEY } });
if (res.status === 429) {
console.warn(` 429 rate limited, sleeping ${RATE_LIMIT_SLEEP_MS}ms and retrying...`);
await sleep(RATE_LIMIT_SLEEP_MS);
continue;
}
if (!res.ok) {
const body = await res.text();
throw new Error(`Quote request failed (${res.status}): ${body}`);
}
return res.json();
}
throw new Error(`Quote request failed after ${MAX_RETRIES} retries (persistent 429)`);
}
async function resolveVenueLabels(): Promise<Set<string>> {
const res = await fetch(`${BASE_URL}/program-id-to-label`, {
headers: { "x-api-key": API_KEY },
});
if (!res.ok) throw new Error(`program-id-to-label failed: ${res.status}`);
const map = (await res.json()) as Record<string, string>;
return new Set(Object.values(map));
}
interface Row {
venue: string;
venueType: "orderbook" | "amm";
sizeUsd: number;
inAmountUsdc: number;
outAmountSol: number;
effectivePrice: number;
priceImpactPct: number;
}
async function main() {
console.log("Resolving venue labels from /program-id-to-label...");
const labels = await resolveVenueLabels();
console.log(`Found ${labels.size} unique labels.`);
for (const [type, venue] of Object.entries(VENUES)) {
if (!labels.has(venue)) {
console.error(`GATE A: venue "${venue}" (${type}) not found in label list. Stopping — pivot required.`);
process.exit(1);
}
}
console.log(`Confirmed venues: order book = ${VENUES.orderbook}, AMM = ${VENUES.amm}`);
console.log("\nFetching baseline SOL/USDC price (unrestricted 1 SOL quote)...");
const baseline = await getQuote({
inputMint: SOL_MINT,
outputMint: USDC_MINT,
amount: String(1 * 10 ** SOL_DECIMALS),
slippageBps: "50",
});
const baselinePrice = Number(baseline.outAmount) / 10 ** USDC_DECIMALS;
console.log(`Baseline SOL price: $${baselinePrice.toFixed(4)}`);
const rows: Row[] = [];
for (const sizeUsd of TRADE_SIZES_USD) {
for (const [type, venue] of Object.entries(VENUES) as [Row["venueType"], string][]) {
const inAmount = Math.round(sizeUsd * 10 ** USDC_DECIMALS);
console.log(`Quoting $${sizeUsd} USDC -> SOL via ${venue}...`);
const quote = await getQuote({
inputMint: USDC_MINT,
outputMint: SOL_MINT,
amount: String(inAmount),
slippageBps: "50",
dexes: venue,
});
const outAmountSol = Number(quote.outAmount) / 10 ** SOL_DECIMALS;
const priceImpactPct = Number(quote.priceImpactPct) * 100;
const effectivePrice = sizeUsd / outAmountSol;
rows.push({
venue,
venueType: type,
sizeUsd,
inAmountUsdc: sizeUsd,
outAmountSol,
effectivePrice,
priceImpactPct,
});
console.log(
` -> ${outAmountSol.toFixed(6)} SOL, effective price $${effectivePrice.toFixed(4)}, priceImpactPct ${priceImpactPct.toFixed(4)}%`
);
await sleep(SLEEP_MS);
}
}
const header = "venue,venueType,sizeUsd,inAmountUsdc,outAmountSol,effectivePrice,priceImpactPct,baselinePrice";
const csvRows = rows.map(
(r) =>
`${r.venue},${r.venueType},${r.sizeUsd},${r.inAmountUsdc},${r.outAmountSol},${r.effectivePrice},${r.priceImpactPct},${baselinePrice}`
);
writeFileSync("data/results.csv", [header, ...csvRows].join("\n") + "\n");
console.log(`\nWrote ${rows.length} rows to data/results.csv`);
console.log("\nSummary:");
console.table(rows);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});