راهنمای انتخاب VPS مناسب برای رباتهای معاملهگر
برای اجرای ربات معاملهگر به جای سرعت خام، روی پایداری تمرکز کنید. یاد بگیرید چگونه با تنظیم systemd، دقت ساعت سیستم و مدیریت API keys، از توقف ناگهانی ربات جلوگیری کنید.
نیازهای یک ربات معاملهگر از VPS
یک VPS برای رباتهای معاملهگر بر اساس چهار معیار ارزیابی میشود: آیا فرآیند پس از توقف بهطور خودکار بازیابی میشود، آیا ساعت سیستم دقیق است، آیا سرقت کلیدهای API (رابط برنامهنویسی کاربردی) دشوار است، و آیا در صورت توقف ربات، از آن مطلع میشوید. سرعت خام در این فهرست برای یک ربات خردهفروشی در اولویتهای پایین قرار دارد، زیرا بخش کند در مسیر سفارش شما، کارگزار و فاصله جغرافیایی تا آن است، نه میزبانی که کد Python شما را اجرا میکند.
این یک راهنمای مهندسی است. هیچکدام از مطالب اینجا توصیه مالی نیست و هیچ استراتژی معاملاتی مورد بحث قرار نگرفته است.
آپتایم یک نظم در راهاندازی مجدد است، نه عددی در صفحه فروش
تعهدات آپتایم تبلیغاتی متفاوت هستند. AWS حداقل 99.5 درصد برای یک instance تکی EC2 و 99.99 درصد برای استقرار در چندین availability zone را مشخص میکند. این تعهدات در دسترس بودن زیرساخت را پوشش میدهند، نه سلامت بات شما را. بات ممکن است به دلیل یک exception مدیریتنشده، یک websocket که هرگز دوباره متصل نمیشود، یا OOM (خارج از حافظه) killer از کار بیفتد، در حالی که سرور تمام مدت روشن باقی میماند. بنابراین پرسش مفید این است که در ده ثانیهای که پس از خروج پروسه شما میگذرد، چه اتفاقی میافتد.
بات را به عنوان یک سرویس systemd اجرا کنید و اجازه دهید سیستم init مسئولیت راهاندازی مجدد را بر عهده بگیرد. یک فایل unit این کار را انجام میدهد.
[Unit]
Description=Trading bot
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0
[Service]
Type=simple
User=bot
WorkingDirectory=/opt/tradingbot
EnvironmentFile=/etc/tradingbot/api.env
ExecStart=/opt/tradingbot/venv/bin/python -m tradingbot
Restart=always
RestartSec=10
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/tradingbot
[Install]
WantedBy=multi-user.targetStartLimitIntervalSec=0 خطی است که افراد از آن غافل میشوند. تنظیمات پیشفرض systemd اجازه 5 شروع در 10 ثانیه را میدهد، که شامل شروع اولیه و شروعهای دستی است؛ پیکربندی مدیر سیستم میتواند این پیشفرضها را بازنویسی کند. رسیدن به این محدودیت، تلاشهای خودکار برای راهاندازی مجدد را متوقف میکند، اما unit میتواند پس از گذشت بازه زمانی، بهصورت دستی یا توسط یک timer یا socket شروع شود. تنظیم آن روی 0 محدودیت نرخ را غیرفعال میکند، بنابراین باتی که در حلقه crash گیر کرده است، به جای سکوت، به تلاش ادامه میدهد. RestartSec=10 از ضربه زدن این حلقه به صرافی با درخواستهای اتصال مجدد جلوگیری میکند.
پیش از اعتماد به فایل، آن را بررسی کنید و سپس اجرا نمایید:
sudo systemd-analyze verify /etc/systemd/system/tradingbot.service
sudo systemctl daemon-reload
sudo systemctl enable --now tradingbot
systemctl status tradingbotenable بخشی است که پس از reboot باقی میماند و بهروزرسانیهای هسته به معنای reboot هستند. برای اینکه ببینید آیا بات بیسروصدا در حال مرگ بوده است، از systemd شمارنده راهاندازی مجدد را بخواهید:
systemctl show tradingbot -p NRestarts
journalctl -u tradingbot --since "24 hours ago" | tail -50NRestarts=0 هیچ کار راهاندازی مجدد خودکاری را از زمان آخرین بازنشانی شمارنده محاسبه نمیکند؛ این عدد ثابتکننده یک هفته سالم یا بدون crash نیست. راهاندازیهای دستی و systemctl reset-failed شمارنده را بازنشانی میکنند و یک بات هنگکرده همچنان میتواند عدد صفر را نشان دهد. NRestarts=812 تعداد 812 کار راهاندازی مجدد را میشمارد، نه اتصالهای موفق به صرافی یا معاملات را، و هیچ چیزی درباره زمان وقوع آنها نمیگوید. برای زمانبندی و علت، از جمله شکستها پیش از اتصال بات، journal را بررسی کنید. کالبدشکافی کامل فایل unit، شامل تایمرها برای کارهای زمانبندیشده مانند گزارش روزانه، در اجرای یک برنامه به عنوان سرویس systemd پوشش داده شده است.
تنظیم ساعت روی UTC و اطمینان از همگامسازی
APIهای صرافیها از برچسبهای زمانی (timestamps) برای اعتبارسنجی درخواستهای امضاشده استفاده میکنند؛ Binance Spot بهطور پیشفرض یک پنجره دریافت 5 ثانیهای دارد و برچسبهای زمانی که حداقل 1 ثانیه جلوتر از زمان سرور باشند را رد میکند. انحراف ساعت باعث بروز خطاهایی میشود که شبیه به شکست در احراز هویت هستند، بنابراین کاربران ساعتها وقت خود را صرف چرخش کلیدها میکنند، بدون اینکه زمان را بررسی کنند. در APIهای مشابه با Binance، پیام خطا دقیقاً به این صورت است: Timestamp for this request was 1000ms ahead of the server's time.
سرور را روی UTC تنظیم کنید. مناطق زمانی محلی باعث پرشهای ناشی از تغییر ساعت تابستانی در زمانهای نمایش داده شده میشوند؛ برچسبهای زمانی Unix که توسط APIها استفاده میشوند، با تغییر منطقه زمانی نمایش، تغییر نمیکنند.
sudo timedatectl set-timezone UTC
timedatectlاوبونتو از نسخه 25.10 به بعد بهطور پیشفرض از chrony استفاده میکند؛ در نصبهای قدیمیتر میتوان از systemd-timesyncd استفاده کرد که یک کلاینت SNTP (پروتکل ساده زمان شبکه) است. systemd-timesyncd ساعت را تنظیم میکند، اما نمیتواند همزمان از چندین سرور پرسوجو کند تا منبع زمانی نادرست را تشخیص دهد. بهجای آن از chrony استفاده کنید:
sudo apt update && sudo apt install -y chrony
sudo systemctl enable --now chrony
chronyc tracking
chronyc sources -vخطی که باید از chronyc tracking خوانده شود System time است، برای مثال System time : 0.000031415 seconds fast of NTP time. مقدار کوچک نشاندهنده اصلاح باقیمانده ناچیز برای ساعت NTP در chrony است؛ پیش از قضاوت در مورد دقت، همگامسازی، Root delay و Root dispersion را نیز بررسی کنید. اگر مقدار Leap status : Not synchronised را نشان میدهد، chrony همگامسازی نشده است، حتی اگر سرورها پاسخ داده باشند؛ اندازهگیریها ممکن است به دلیل عدم دقت کافی رد شوند. یک دقیقه صبر کنید و پیش از دستکاری قوانین فایروال، دوباره بررسی کنید.
کلیدهای API را در مکانهایی که کپی میکنید قرار ندهید
نشت یک کلید صرافی (exchange key) از نشت یک کلید SSH خطرناکتر است، زیرا دسترسی برداشت (withdrawal) آن را بلافاصله به پول نقد تبدیل میکند. دو عادت، بخش بزرگی از این ریسک را پوشش میدهند.
نخست، هرگز به یک کلید ربات دسترسی برداشت ندهید و در صورتی که صرافی پشتیبانی میکند، کلید را به آدرس IP سرور خود محدود کنید. این تنها کنترلی است که یک کلید سرقتشده را تقریباً بیفایده میکند.
دوم، secret را خارج از دایرکتوری کد نگه دارید. هر چیزی که داخل /opt/tradingbot باشد، دیر یا زود سر از مخزن git یا آرشیو پشتیبان در میآورد. آن را در فایلی قرار دهید که مالکیت آن با root است و توسط systemd خوانده میشود:
sudo install -d -m 750 -o root -g bot /etc/tradingbot
sudo install -m 640 -o root -g bot /dev/null /etc/tradingbot/api.env
sudo nano /etc/tradingbot/api.envاین فایل شامل خطوط ساده KEY=value بدون کوتیشن و بدون export است. حالت 640 به مالک root دسترسی خواندن/نوشتن میدهد و اعضای گروه bot دسترسی خواندن دارند، در حالی که سایر کاربران هیچ مجوزی ندارند. با sudo -u bot cat /etc/tradingbot/api.env بررسی کنید و سپس با یک کاربر بدون امتیاز خارج از گروه bot تست کنید؛ جایی که باید با خطای Permission denied مواجه شوید.
خود ربات نباید به عنوان root یا کاربر لاگین شما اجرا شود. پیش از شروع سرویس یا ایجاد فایلهایی که مالکیت آنها با گروه bot است، یک حساب سیستمی با shell از نوع nologin و دایرکتوری home به صورت /var/lib/tradingbot ایجاد کنید:
sudo useradd --system --shell /usr/sbin/nologin --home-dir /var/lib/tradingbot --create-home botدلیل استفاده از هر یک از این فلگها و اینکه ProtectSystem=strict دقیقاً تا چه حد پیش میرود، در اجرای سرویسها به عنوان کاربر بدون امتیاز آمده است. مابقی موارد پایه سرور، کلیدهای SSH و فایروال، در ده دقیقه نخست روی یک VPS جدید قرار دارد.
پیش از آنکه کارگزار متوجه شود، از خرابی مطلع شوید
systemctl status نشان میدهد که پردازش در حال اجراست، اما به این معنی نیست که ربات واقعاً کاری انجام میدهد. پردازشی که در یک حلقه تلاش مجدد (retry loop) روی یک وبسوکت قطعشده گیر کرده باشد، همچنان از بررسیهای پردازشی که در بخش قبل ذکر شد عبور میکند.
بهجای آن از یک heartbeat استفاده کنید. Uptime Kuma دارای مانیتورهای push است: این ابزار انتظار دارد ربات شما در زمانبندی مشخصی یک URL را فراخوانی کند و در صورت متوقف شدن این فراخوانیها، هشدار میدهد. این فراخوانی را در انتهای حلقه اصلی (main loop) خود قرار دهید، یعنی پس از بخشی که سلامت ربات را تأیید میکند، مانند خواندن موفقیتآمیز دادههای بازار.
curl -fsS "http://monitor.example.com:3001/api/push/YOUR_TOKEN?status=up&msg=loop_ok"بازه زمانی مانیتور را تقریباً دو برابر زمان حلقه خود تنظیم کنید تا نوسانات (jitter) عادی باعث ارسال هشدار کاذب نشود. مانیتور را روی سروری متفاوت از سرور ربات اجرا کنید، زیرا مانیتوری که همزمان با سرویس تحت نظارت از کار بیفتد، هیچ گزارشی ارسال نخواهد کرد. نحوه راهاندازی در مانیتورینگ وضعیت به صورت self-hosted با Uptime Kuma توضیح داده شده است.
یک هشدار برای دیسک نیز اضافه کنید. رباتی که لاگهای پرحجم مینویسد، سیستم فایل root را در عرض چند هفته پر میکند؛ پر شدن دیسک باعث توقف نوشتن در دیتابیس میشود، نه لزوماً تماسهای شبکه، بنابراین علائم آن ممکن است عجیب به نظر برسد. journalctl --vacuum-time=14d و یک خط SystemMaxUse= در /etc/systemd/journald.conf باعث محدود ماندن حجم journal میشود.
بخش صادقانه: تأخیر (Latency) عمدتاً به میزبان شما مربوط نیست
اینجاست که بازار محصولات VPS برای ترید، از جنبه فنی خارج میشود. صفحات تبلیغاتی ارقام زیر یک میلیثانیه را ذکر میکنند و القا میکنند که میزبان همان چیزی است که میان شما و اجرای سفارش فاصله انداخته است. برای تقریباً تمام رباتهای خردهفروشی، اینطور نیست.
سفارش شما از ربات به سمت endpoint صرافی یا کارگزار از طریق اینترنت عمومی حرکت میکند. آن مسیر تحت سلطه فاصله فیزیکی و peering بین ارائهدهنده شما و آنهاست. سپس سیستمهای خود کارگزار، صف، بررسیهای ریسک و محدودیتهای نرخ (rate limits) خود را به آن اضافه میکنند.
بهجای حدس زدن، آن را اندازهگیری کنید. curl زمان اتصال و زمان دریافت اولین بایت را برای یک endpoint واقعی گزارش میدهد:
curl -s -o /dev/null -w 'dns=%{time_namelookup}s connect=%{time_connect}s ttfb=%{time_starttransfer}s\n' \
https://api.kraken.com/0/public/Time
mtr -rwzc 100 api.kraken.comپیش از آنکه متعهد شوید، این دستور را از روی سرور کاندید اجرا کنید. اگر connect برابر با 0.180 ثانیه باشد، این شامل حل DNS و اتصال TCP است؛ این مقدار مشخص نمیکند که شما در کدام قاره هستید. اگر connect برابر با 0.004 ثانیه و ttfb برابر با 0.140 ثانیه باشد، تفاوت شامل راهاندازی TLS، انتقال درخواست، تأخیرهای شبکه و پردازش سرور است. تغییر میزبان میتواند بر برخی از این مؤلفهها تأثیر بگذارد، بنابراین اندازهگیریها را از مکانهای کاندید با هم مقایسه کنید.
پس میزبان چه زمانی اهمیت پیدا میکند؟ زمانی که شما در محل (colocated) هستید یا به محل برگزاری متصل (cross-connected) هستید و بر سر جایگاه در صف رقابت میکنید، که این یک کسبوکار متفاوت با بودجهای متفاوت است. و زمانی که کد خودتان گلوگاه (bottleneck) است: رباتی که در هر تیک، شاخصها را روی کل تاریخچه بازمحاسبه میکند، میتواند 200 میلیثانیه از CPU را در هر حلقه بسوزاند، که این تأخیر واقعی است و شما بهصورت رایگان میتوانید آن را کنترل کنید. پیش از خرید سرور سریعتر، حلقه کد خود را پروفایل کنید.
آنچه در میزبانی که انتخاب میکنید اهمیت دارد، جغرافیا، شبکه پایدار و حافظه کافی است تا OOM killer هرگز فرصت دخالت پیدا نکند. اگر تاریخچه تیکها را در یک دیتابیس محلی نگه میدارید، حافظه را افزایش دهید.
چکلیست کوتاه پیش از عملیاتیسازی
systemctl is-enabled tradingbotدستورenabledرا چاپ میکند و سرویس پس ازsudo rebootبه کار خود ادامه میدهد.chronyc trackingوضعیت همگامسازی، اختلاف زمانی سیستم کمتر از چند میلیثانیه، و تأخیر و پراکندگی root را در محدودهٔ مجاز زمانی شما گزارش میدهد.- کلید API دارای مجوز معامله است، مجوز برداشت ندارد و در صورت پشتیبانی صرافی، دارای لیست سفید IP است.
- متوقف کردن پردازش با
sudo systemctl kill -s SIGKILL tradingbotباعث میشود سرویس پس از تأخیرRestartSecو زمان راهاندازی، دوباره بالا بیاید. - مانیتور heartbeat پس از بازهٔ تنظیمشده به شما هشدار میدهد و زمانی که ربات را عمداً متوقف میکنید، تلاش مجدد انجام میدهد.
- لاگها محدود شدهاند و فایلسیستم root در
df -hفضای کافی دارد.
کل فرآیند را به مدت یک هفته در محیط sandbox صرافی یا در حالت paper mode پیش از استفاده از سرمایهٔ واقعی اجرا کنید. هدف از این یک هفته این است که هر یک از موارد بالا حداقل یک بار با شکست مواجه شود.
FAQ
آیا یک ربات معاملهگر به سرور با تأخیر کم (low-latency) یا bare metal نیاز دارد؟
تنها در صورتی که در حال رقابت بر سر سرعت اجرا با سایر شرکتکنندگان خودکار در همان صرافی باشید؛ که این معمولاً به معنای استفاده از colocation است، نه یک VPS عمومی. برای یک ربات خردهفروشی، زمان رفت و برگشت (round trip) عمدتاً تحت تأثیر موقعیت جغرافیایی و پردازشهای خودِ کارگزار است. بنابراین سروری نزدیک به API endpoint انتخاب کنید و پیش از پرداخت هزینه برای گزینههای سریعتر، با استفاده از curl و mtr اندازهگیریهای لازم را انجام دهید.
یک ربات معاملهگر به چه میزان RAM و CPU نیاز دارد؟
بیشتر رباتهای تکاستراتژی محدود به شبکه (network-bound) هستند و در فواصل بین رویدادها بیکار میمانند. حافظه زمانی به محدودیت تبدیل میشود که تاریخچه تیکها را در حافظه نگه دارید یا یک دیتابیس محلی اجرا کنید. بنابراین بهجای حدس زدن، free -h و لاگهای سیستم را برای مشاهده پیامهای OOM kill بررسی کنید.
چرا API صرافی درخواستهای من را با خطای timestamp رد میکند؟
timestamp درخواست خارج از بازه زمانی مجاز صرافی است؛ هم انحراف ساعت (clock drift) و هم تأخیر در ارسال درخواست اهمیت دارند. chrony را نصب کنید، تأیید کنید که chronyc tracking یک System time کوچک و وضعیت leap هماهنگ را نشان میدهد، و همچنین root delay و dispersion را بررسی کنید. تنظیم ساعت ماشین روی UTC باعث میشود زمانهای نمایش داده شده یکپارچه باشند، اما انحراف ساعت را اصلاح نمیکند. چرخاندن (rotate) کلید API نیز مشکل ساعت را حل نمیکند.
چگونه از متوقف شدن ربات در طول شب بدون اطلاع خودم جلوگیری کنم؟
آن را تحت مدیریت systemd با Restart=always و StartLimitIntervalSec=0 اجرا کنید تا در صورت بروز crash loop، بهجای رسیدن به محدودیت نرخ شروع (start rate limit)، عملیات retry ادامه یابد. سپس یک heartbeat اضافه کنید که ربات در پایان هر حلقه موفق ارسال میکند. قابلیت restart، وضعیت پردازش را مدیریت میکند و heartbeat حالتی را پوشش میدهد که پردازش زنده است اما در یک وضعیت گیر کرده (stuck) قرار دارد.
آیا میتوانم ربات و ابزار مانیتورینگ را روی یک VPS اجرا کنم؟
میتوانید، اما ابزار مانیتورینگ در روزی که واقعاً به آن نیاز دارید به شما دروغ خواهد گفت؛ زیرا قطعی که باعث از کار افتادن ربات شود، مانیتورینگ را نیز از دسترس خارج میکند. سیستم هشداردهی را روی ماشین جداگانهای نگه دارید، ترجیحاً با یک ارائهدهنده یا در منطقه جغرافیایی متفاوت، و سرور ربات را فقط برای خودِ ربات و لاگهای آن اختصاص دهید.