جاوااسکریپت
در Node.js 18 یا بالاتر، fetch داخلی برای کار با روشا کافی است و کتابخانهٔ دیگری لازم نیست. یک تابع get کلید را از ROSHA_KEY میخواند و برای 429 به اندازهٔ Retry-After صبر میکند. مهمترین نکتهٔ جاوااسکریپت این است که ins_code را رشته نگه دارید.
پیشنیاز
Section titled “پیشنیاز”Node.js 18 یا بالاتر و کلید در متغیر محیطی. نمونهها await سطح بالا دارند؛ آنها را در فایل .mjs بگذارید.
export ROSHA_KEY="rosha_xxxxxxxx_your-secret"node example.mjsتابع کمکی get چیست؟
Section titled “تابع کمکی get چیست؟”get یک درخواست GET میفرستد، اگر 429 گرفت به اندازهٔ Retry-After صبر میکند و یک بار دیگر میفرستد، و برای هر خطای دیگر استثنا میدهد.
const BASE = "https://poolick.ir";
async function get(path) { for (let attempt = 0; attempt < 2; attempt++) { const res = await fetch(BASE + path, { headers: { Authorization: `Bearer ${process.env.ROSHA_KEY}` }, }); if (res.status === 429) { await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get("Retry-After") ?? 1))); continue; } if (!res.ok) throw new Error(`${res.status} ${(await res.json()).detail}`); return res.json(); } throw new Error("rate limited");}
const results = await get("/v1/instruments?q=" + encodeURIComponent("فولاد") + "&limit=3");console.log(results.map((i) => [i.ins_code, i.ticker, i.name]));مقدار پارامترهای فارسی را با encodeURIComponent یا URLSearchParams کدگذاری کنید. معنای هر کد خطا در خطاها است.
چطور سابقهٔ روزانه را بگیرم؟
Section titled “چطور سابقهٔ روزانه را بگیرم؟”/v1/instruments/{ref}/daily را صدا بزنید و rows را بخوانید. این برنامه ۳۰ روز معاملاتی اخیر فولاد را با قیمت تعدیلشده میگیرد و بازده بازه را حساب میکند:
const BASE = "https://poolick.ir";
async function get(path) { for (let attempt = 0; attempt < 2; attempt++) { const res = await fetch(BASE + path, { headers: { Authorization: `Bearer ${process.env.ROSHA_KEY}` }, }); if (res.status === 429) { await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get("Retry-After") ?? 1))); continue; } if (!res.ok) throw new Error(`${res.status} ${(await res.json()).detail}`); return res.json(); } throw new Error("rate limited");}
const query = new URLSearchParams({ adjusted: "true", limit: "30" });const data = await get(`/v1/instruments/46348559193224090/daily?${query}`); // فولادconst rows = data.rows; // oldest first
const first = rows[0];const last = rows.at(-1);console.log(data.ticker, data.ins_code);console.log(`${first.d} close ${first.close} -> ${last.d} close ${last.close}`);console.log(`return: ${((last.close / first.close - 1) * 100).toFixed(2)}%`);قیمتها به ریال است. در روزهای بدون معامله open، high و low برابر null است؛ پیش از محاسبه آن را بررسی کنید. شرح فیلدها در سابقهٔ روزانه است.
چرا ins_code نباید عدد شود؟
Section titled “چرا ins_code نباید عدد شود؟”ins_code از Number.MAX_SAFE_INTEGER (یعنی 9007199254740991) بزرگتر است و Number رقمهای آخر آن را عوض میکند. روشا آن را عمداً به شکل رشته برمیگرداند؛ همان رشته را نگه دارید.
const insCode = "32097828799138957"; // شاخص کل, as returned by the API
console.log(String(Number(insCode))); // "32097828799138956" (wrong: the last digit changed)console.log(Number.isSafeInteger(Number(insCode))); // false
// Correct: keep it as a string everywhere (URLs, Map keys, database columns).const url = `/v1/indices/${insCode}/daily?limit=1`;console.log(url);
// Only if you really need numeric ordering, use BigInt, never Number.console.log(BigInt(insCode) > BigInt("9007199254740991")); // trueاین خطا بیصدا است: برای بعضی کدها عدد درست از آب درمیآید و برای بعضی نه. پس آزمودن با یک نماد کافی نیست.
قاعدهها:
ins_codeرا باNumber()،parseInt()یا+تبدیل نکنید.- اگر JSON را با کتابخانهای میخوانید که رشتههای عددی را خودکار عدد میکند، این رفتار را برای
ins_codeخاموش کنید. BigIntدرJSON.stringifyخطا میدهد؛ پیش از سریالکردن آن را به رشته برگردانید.- در پایگاه داده، ستون را متنی بگیرید.
چطور کل سابقهٔ یک نماد را بگیرم؟
Section titled “چطور کل سابقهٔ یک نماد را بگیرم؟”هر پاسخ حداکثر ۵٬۰۰۰ ردیف دارد؛ برای سابقهٔ بلندتر با تاریخ صفحهبندی کنید. هر بار end را روزِ قبل از قدیمیترین ردیف گرفتهشده بگذارید و وقتی تعداد ردیفها از اندازهٔ صفحه کمتر شد بایستید.
const BASE = "https://poolick.ir";const PAGE = 5000; // the REST cap on `limit`
async function get(path) { for (let attempt = 0; attempt < 2; attempt++) { const res = await fetch(BASE + path, { headers: { Authorization: `Bearer ${process.env.ROSHA_KEY}` }, }); if (res.status === 429) { await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get("Retry-After") ?? 1))); continue; } if (!res.ok) throw new Error(`${res.status} ${(await res.json()).detail}`); return res.json(); } throw new Error("rate limited");}
function dayBefore(isoDate) { const d = new Date(isoDate + "T00:00:00Z"); d.setUTCDate(d.getUTCDate() - 1); return d.toISOString().slice(0, 10);}
async function fullDaily(ref, adjusted = true) { let rows = []; let end = null; for (;;) { const query = new URLSearchParams({ limit: String(PAGE), adjusted: String(adjusted) }); if (end) query.set("end", end); const batch = (await get(`/v1/instruments/${ref}/daily?${query}`)).rows; rows = batch.concat(rows); // each page is older than the one before if (batch.length < PAGE) return rows; end = dayBefore(batch[0].d); }}
const rows = await fullDaily("46348559193224090"); // فولادconsole.log(`${rows.length} rows from ${rows[0].d} to ${rows.at(-1).d}`);همین الگو برای /client-type، /v1/indices/{ref}/daily و /v1/funds/{ref}/history هم کار میکند. شرح روش در قراردادها و قالبها است.
نکتههای دیگر
Section titled “نکتههای دیگر”- زمانها: فیلدهای زمان مثل
tsبا اختلاف ساعت تهران (+03:30) میآیند وnew Date(ts)آنها را درست میخواند. فیلدهای تاریخ مثلdفقط روز هستند؛ برای مقایسه همان رشتهٔ ISO کافی است. - اعداد بزرگ دیگر:
valueوnet_assetعدد اعشاریاند و ممکن است از بازهٔ عدد صحیح امن بزرگتر شوند. برای نمایش و محاسبهٔ معمول مشکلی نیست؛ برای حسابداری دقیق تا آخرین ریال، این را در نظر بگیرید. - سهمیه: هر فراخوانی
getیک درخواست از سقف دقیقهای و سهمیهٔ روزانه کم میکند؛ محدودیتها و پلنها. - TypeScript: نوع
ins_codeراstringتعریف کنید.
صفحهٔ اصلی · قیمتها · وضعیت · پنل · پشتیبانی
دادهها توصیهٔ سرمایهگذاری نیست. سلب مسئولیت