واجهة WebSocket لأسواق التنبؤ في الوقت الفعلي
الدفع في الوقت الفعلي. أصدر تذكرة، اتصل، واستقبل إطارات سعر وحالة Market التي تستحقها خطتك، مع نبضة قلب واستعادة عند إعادة الاتصال.
بث WebSocket هو النصف المباشر من الـ API. يدفع إطارات سعر وحالة Market عند تغيّرها. عندما تصبح signal الخاصة بـ Market هي arb، يحمل إطارها أيضًا دفاتر العمق لكل نتيجة وmax_stake؛ لا يرسل البث كائن Opportunity الخاص بـ REST. يعمل البث الكامل على Basic وPremium وPro. يمكن لمفتاح Free أيضًا إصدار تذكرة بث، مقتصرة على قناة sample منسَّقة بدلًا من بث السوق الكامل.
يتحدث البث بروتوكول Centrifugo، فتتصل بعميل centrifuge الخاص بلغتك بدلًا من مقبس خام. الاتصال مصافحة من خطوتين: أصدر تذكرة قصيرة الأجل عبر REST، ثم افتح المقبس بها.
1. أصدر تذكرة
استدعِ POST /v1/stream/token بمفتاح API الخاص بك. التذكرة صالحة لنحو 120 ثانية وللاستخدام مرة واحدة لاتصال واحد.
curl -s -X POST "https://api.dino.markets/v1/stream/token" \
-H "Authorization: Bearer sk_live_..."
يحصل مفتاح Basic (أو أعلى) على البث الكامل:
{
"ticket": "eyJhbGciOi...",
"ws_url": "wss://stream.dino.markets/connection/websocket",
"expires_in": 120,
"allowed": {
"channels": ["markets:all", "org:{id}", "status"],
"max_conns": 3
}
}
يحصل مفتاح Free على الشكل نفسه، مقتصرًا على التذوق العيني:
{
"ticket": "eyJhbGciOi...",
"ws_url": "wss://stream.dino.markets/connection/websocket",
"expires_in": 120,
"allowed": {
"channels": ["sample", "org:{id}", "status"],
"max_conns": 1
}
}
تعكس allowed ما تمنحه خطتك: القنوات التي سيشترك الخادم فيها لك، وحد اتصالاتك المتزامنة. كل خطة نشطة، بما فيها Free، تحصل على تذكرة حقيقية؛ فقط مفتاح منتهي الصلاحية أو من خطة غير معروفة يحصل هنا على 402 مع رابط للترقية، لا تذكرة فارغة صامتة أبدًا.
2. اتصل
تنفذ مساعدات SDK الرسمية أدناه خطوتَي المصافحة كلتيهما: تصدر تذكرة جديدة لكل محاولة اتصال وتمررها إلى ws_url في بيانات الاتصال ضمن t. لست بحاجة إلى الاشتراك في القنوات يدويًا. عند الاتصال، يعيد الخادم قراءة حالة الحساب الحالية ويشترك لك بالضبط في القنوات التي تمنحها الخطة النشطة، فلا يمكن لتذكرة صدرت سابقًا الحفاظ على وصول قديم.
ينتهي القبول الأولي بعد نحو 15 ثانية. تعيد أول عملية تجديد فحص خطة الاتصال مقابل حالة الحساب الحالية؛ فإن تطابقت حصلت على التجديد المعتاد لمدة 15 دقيقة، بينما تُغلق الخطة المفقودة أو المتغيرة الخط. لذا يمكن لتغيير خطة يتسابق مع استجابة الاتصال أن يترك وصولًا قديمًا لمدة 15 ثانية على الأكثر.
import { Dino, watch } from "dino-markets";
const client = new Dino({ apiKey: process.env.DINO_API_KEY });
const stream = await watch(client, {
onFrame: (frame, channel) => console.log(channel, frame),
onRecoveryFailed: (channel) => bootstrapFromRest(channel),
});يصدر SDK الرسمي تذكرة جديدة لكل محاولة اتصال، بما في ذلك إعادة الاتصال التلقائية. إن استخدمت Centrifuge مباشرة، يجب أن يفعل استدعاء بيانات الاتصال لديك الشيء نفسه.
القنوات
يختار الخادم مجموعة قنواتك من خطتك. تحصل على مجموعة فرعية من هذه:
| القناة | الخطة | تحمل |
|---|---|---|
sample | Free | نحو 3 أسواق منسَّقة يختارها الخادم، في الوقت الفعلي: تذوق خطة Free. حبيبة القناة: الخادم يختار الأسواق التي تتدفق هنا، لا المشترك. |
markets:all | Basic وPremium وPro | التغذية المطابَقة الكاملة عبر الرياضة والطقس والعملات الرقمية والاقتصاد، كإطارات حالة وسعر. هذه القناة المرجعية في v2. تحصل عليها عندما يكون نطاقك التغذية الكاملة. |
markets:{sport} | Basic وPremium وPro | بث التغذية المطابَقة لرياضة عامة واحدة، مثل markets:baseball. تحصل على هذه بدلًا من markets:all عندما يكون نطاقك مجموعة فرعية. |
status | كل خطة، بما فيها Free | نبضة القلب. راجع نبضة القلب. |
quote:{sport} | Pro | تغيرات سعر خام لأعلى دفتر الأوامر لكل رياضة. |
candidates:{sport} | Pro | مرشحة نطاق المراجعة لكل رياضة، المطابقات المؤقتة قبل تأكيدها. راجع المطابقة والثقة. |
org:{id} | كل خطة، بما فيها Free | قناة حضور مؤسستك. تنضم إليها كل اتصالاتك لتُحسب مقابل الحد. لن تنشر أو تقرأ بيانات عليها. |
تستخدم الرياضات مفاتيح عامة مثل baseball وsoccer وtennis؛ يمكن لرياضة واحدة أن تمتد عبر عدة بطولات. تبقى القناة لكل رياضة، لا لكل بطولة. اسأل /v2/categories عن السياقات الصالحة.
Messages
ما يصل يعتمد على عائلة القناة. تحمل قنوات markets:* إطارات market وprice المسطَّحة؛ وتعكس قناة sample الخاصة بـ Free أشكال الإطارات نفسها، مقتصرة على أسواقها المنسَّقة. تحمل قناة status نبضة القلب. كل رسالة هي JSON بحقل type.
على قنوات markets
قنوات markets:* هي البث المرجعي في v2، وتحمل كائن السوق. الإطارات مسطَّحة: يميّزها حقل type، ولا يوجد مغلَّف data، ولا مفتاح channel، ولا مفتاح schema_version. يصل نوعان من الإطارات.
يُرسَل إطار market عند تغيّر حالة الزوج: زوج جديد يصبح نشطًا، أو زوج يُسحَب، أو تنقلب signal الخاصة به. هو كائن السوق الموحَّد الكامل مع إضافة مميّز type؛ وعندما تكون signal هي "arb" يحمل أيضًا دفاتر العمق لكل نتيجة. في إطار market، تكون priced_at هي لحظة بناء الإطار. على صف REST مسعَّر تكون هي وقت التقاط الإطار المتأخر بالضبط؛ فقط صف الهوية غير المسعَّر يعود إلى updated_at الخاصة بالزوج.
{
"type": "market",
"id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2",
"...": "the rest of the market object"
}
يُرسَل إطار price عند تغيّر السعر فقط. هو فرق مضغوط بمفتاح نتيجة key الثابت، يحمل كل عددي على المستوى الأعلى مشتق من السعر بحيث لا يصبح كائن market المخزَّن مؤقتًا قديمًا أبدًا:
{
"type": "price",
"id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2",
"priced_at": "2026-07-01T22:55:02+00:00",
"spread_pts": 2.0,
"potential_arb_pct": 1.83,
"outcomes": [
{ "key": "NYY", "kalshi": 0.52, "polymarket": 0.54 },
{ "key": "BOS", "kalshi": 0.50, "polymarket": 0.46 }
]
}
عندما تكون signal الزوج هي arb، تحمل كل نتيجة في إطار price أيضًا كائنَي عمق kalshi_book وpolymarket_book، ويحمل الإطار على المستوى الأعلى max_stake: أصغر عمق عند سعر الطلب الأرخص المستهلَك عبر النتائج، بحيث لا تكون arb المتدفقة عمياء عن الحجم أبدًا. تكون max_stake قيمتها null عندما لا تملك أي ساق مستهلَكة قراءة عمق.
تُنشَر الإطارات مزدوجة إلى markets:{sport} وmarkets:all، والتسليم بمعدل مرة واحدة على الأقل: بعد نشر جزئي تُعاد الجولة التالية إرساله، فإطار مكرر نادر ممكن. الإطارات موصوفة ذاتيًا ومتكافئة القوة، فتطبيق نسخة مكررة غير ضار.
رسالة status هي نبضة القلب، وسأتناولها بعد ذلك.
نبضة القلب
التغذية هادئة معظم الوقت، لأن arb حدث نادر. مقبس صامت غامض: قد يعني أنه لا توجد arb مفتوحة، أو قد يعني أن التغذية معطَّلة. قناة status تزيل هذا الغموض. ينشر brain نبضة قلب على مؤقّت، سواء كانت أي arb مفتوحة أم لا.
{
"type": "status",
"schema_version": 1,
"channel": "status",
"ts": "2026-06-25T21:00:00+00:00",
"data": {
"state": "ok",
"active_pairs": 312,
"open_opportunities": 0
}
}
| الحقل | المعنى |
|---|---|
state | ok عندما تكون التغذية مباشرة وتُطلق. degraded أثناء إحماء بدء بارد أو نافذة استعادة حين تُحجَز arb. |
active_pairs | عدد المباريات المطابَقة التي يراقبها brain حاليًا. |
open_opportunities | عدد arb المفتوحة الآن. نبضة قلب بـ ok و0 تعني أن التغذية سليمة ولا يوجد ببساطة ما تتصرف بشأنه. |
عامل فجوة في نبضات القلب، لا تغذية arb هادئة، على أنها إشارة وجود خلل.
حدود الاتصال
لكل خطة حد على الاتصالات المتزامنة: تسمح Free بواحد، وBasic بثلاثة، وPremium وPro بعشرة. يُحسب الحد من جهة الخادم من قناة حضور مؤسستك، فيصمد عبر العمليات والأجهزة. يُرفَض اتصال يتجاوز حدك. الحد هو المقياس القيمي، فإن كنت تشغّل استراتيجيات متوازية أو تريد خطًا للتجهيز إلى جانب الإنتاج، فهذا ما تدفع مقابله عند الترقية إلى خطة أعلى.
الانطلاق من لقطة
للبدء بـ Opportunities مؤكدة ثم البقاء مباشرًا، اقرأ GET /v2/arbitrage، ثم اتصل بالبث. تحمل استجابة REST as_of، لا إزاحة بث، فلا توجد نقطة استئناف دقيقة لتسليمها. عامل قراءة REST واتصال البث كرأيين مستقلين. توقّع نافذة تداخل صغيرة، وأزل التكرار بمطابقة opportunities[].market_id مقابل id الخاص بـ Market في البث.
الاستعادة بعد انقطاع
تحتفظ قنوات البث القابلة للاستعادة بسجل محدود مدته 5 دقائق. يسجّل عميل centrifuge offset آخر رسالة رآها، فعند إعادة الاتصال يطلب من الخادم إعادة بث ما فاته من تلك النقطة. لانقطاع قصير هذا تلقائي وبلا فجوة.
إن انقطع اتصالك لفترة أطول من نافذة السجل، لا تستطيع الاستعادة سد الفجوة. يستدعي SDK دالة onRecoveryFailed في JavaScript أو on_recovery_failed في Python مع القناة المتأثرة. أعد تهيئة تلك القناة من لقطة REST جديدة بدلًا من الوثوق ببث جزئي.