الاختبار التاريخي (Backtesting) هو أن تسأل التاريخ: ماذا كانت ستفعل هذه الاستراتيجية لو طبّقتَها؟ — قبل أن تخاطر بريال واحد. أغلب الشروحات بلغة بايثون تفترض أسهماً أمريكية ومصدر بيانات تربطه بنفسك. هذا الدليل يفعلها على السوق المالية السعودية (تداول، المؤشر TASI) عبر تاسي لاب، بحيث تكون العمولة والضريبة والأسعار هي الحقيقية للسوق السعودي — لا تقريباً عاماً.
سنشغّل استراتيجية الارتداد بمؤشر RSI الكلاسيكية — الشراء حين ينزل RSI تحت 30 (تشبّع بيعي)، والبيع حين يتجاوز 70 (تشبّع شرائي) — على سهم أرامكو السعودية (2222)، بطريقتين:
- الطريقة الأولى — الاختبار المُستضاف. استدعاء HTTP واحد. تشغّل تاسي لاب الاستراتيجية على بيانات تاريخية مخزّنة وتعيد لك النتيجة، ومعها مقارنة بالشراء والاحتفاظ.
- الطريقة الثانية — اكتب منطقك بنفسك. اجلب البيانات، واحسب الإشارة في بايثون، وحاكِ التنفيذ بالعمولة الحقيقية، ثم سجّل التشغيل كتجربة متتبَّعة.
قبل أن تبدأ
تحتاج حساباً مجانياً في تاسي لاب ومفتاح API. سجّل الآن — يستغرق دقيقة، ويأتي بمحفظة ورقية رصيدها 100,000 ريال افتراضي. انسخ مفتاحك من اللوحة، ثم ثبّت الحزمة وصدّر المفتاح:
pip install tasilab
export TASILAB_API_KEY="your-api-key"
الطريقة الأولى — الاختبار المُستضاف (استدعاء واحد)
أسرع طريق هو POST /v1/backtest/run. تصف الاستراتيجية كبيانات — المؤشر، والعتبات، والسهم، والفترة — وتتولّى تاسي لاب الباقي. هذا تشغيل RSI على أرامكو لآخر سنة (1Y ≈ 250 يوم تداول):
curl -X POST https://api.tasilab.com/v1/backtest/run \
-H "X-API-Key: $TASILAB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"symbol": "2222",
"period": "1Y",
"strategy_type": "threshold",
"indicator": "RSI",
"params": {"period": 14},
"buy_op": "<", "buy_threshold": 30,
"sell_op": ">", "sell_threshold": 70
}'
الطلب نفسه من بايثون، مع قراءة الرقمين اللذين يهمّان فعلاً:
import os, requests
resp = requests.post(
"https://api.tasilab.com/v1/backtest/run",
headers={"X-API-Key": os.environ["TASILAB_API_KEY"]},
json={
"symbol": "2222", # Saudi Aramco
"period": "1Y", # ~250 trading days
"strategy_type": "threshold",
"indicator": "RSI",
"params": {"period": 14},
"buy_op": "<", "buy_threshold": 30, # oversold -> buy
"sell_op": ">", "sell_threshold": 70, # overbought -> sell
},
timeout=60,
)
resp.raise_for_status()
r = resp.json()
print(f"Strategy : {r['return_pct']:+.2f}% ({r['num_trades']} trades)")
print(f"Buy & hold: {r['buy_hold_return_pct']:+.2f}%")
print(f"Final : {r['final_value']} SAR vs {r['buy_hold_final_value']} SAR held")
الاستجابة سجلّ كامل للتشغيل — لا مجرّد رقم. أكثر الحقول استخداماً:
return_pct/return_sar— كيف كان أداء الاستراتيجية.buy_hold_return_pct/buy_hold_final_value— كيف كان أداء مجرّد الاحتفاظ بالسهم للفترة نفسها. هذا هو المعيار.num_tradesوnum_bars— كم كانت الاستراتيجية نشطة، وعلى كم يوم.trades— كل عملية شراء/بيع بتاريخها وسعرها وكميتها وقيمتها.equity_curve— قيمة المحفظة يومياً، جاهزة للرسم.run_id— مُعرّف التشغيل المحفوظ.
اقرأ حُكم التاريخ
أهم سطر في هذا الناتج هو المقارنة بين return_pct وbuy_hold_return_pct. الاستراتيجية لا تستحق الاهتمام إلا إذا تفوّقت على البديل المُمِلّ: أن تشتري السهم ولا تفعل شيئاً.
وهنا الجزء غير المريح، وهو سبب عرضنا للرقمين افتراضياً: القاعدة الذكية ظاهرياً كثيراً ما تخسر أمام الشراء والاحتفاظ. ارتداد RSI الكلاسيكي على سهم قوي الاتجاه يبيع رابحيه مبكراً وغالباً يتأخّر عن المؤشر. تعرض تاسي لاب ذلك بصراحة — لا تجمّل التاريخ، ويجب ألّا يفعل تحليلك ذلك أيضاً. جملة صادقة مثل «هذه ميزة ضعيفة» أثمن من اختبار عُدّل حتى بدا جميلاً.
return_pct لاستراتيجيتك أقل من الشراء والاحتفاظ، فتلك نتيجة حقيقية لا فشل في الأداة. إنّ انضباط المقارنة بالمعيار — في كل مرة — هو معظم ما يفصل الميزة القابلة للتداول عن مجرّد قصة.الطريقة الثانية — اكتب منطقك، وتتبّعه
يغطّي الاختبار المُستضاف استراتيجيات المؤشرات الشائعة. وحين تريد تحكّماً كاملاً — إشارات مخصّصة، وتحجيم مراكز خاصّ، ومقاييسك أنت — اجلب البيانات وشغّل الحلقة بنفسك، ثم سجّل التشغيل كـتجربة في تاسي لاب لتكون قابلة للتكرار والمقارنة لاحقاً. هذا الشكل العام، بتقاطع متوسّطين متحرّكين بسيط:
import os
from decimal import Decimal
from tasilab import Tasilab
COMMISSION, VAT = Decimal("0.00155"), Decimal("0.15") # real TASI fees
def fees(price, qty):
commission = price * qty * COMMISSION
return commission + commission * VAT
tasi = Tasilab(api_key=os.environ["TASILAB_API_KEY"])
# 1) real historical bars for Saudi Aramco
bars = tasi.get_historical("2222", "2024-01-01", "2025-12-31")["bars"]
closes = [float(b["close"]) for b in bars]
# 2) your own signal - a 10/30 SMA crossover here
def sma(v, p): return [None]*(p-1) + [sum(v[i-p+1:i+1])/p for i in range(p-1, len(v))]
fast, slow = sma(closes, 10), sma(closes, 30)
# 3) simulate fills with real fees, collect trades ... (loop omitted)
# 4) log the run as a tracked experiment
with tasi.create_experiment(
name="SMA 10/30 on Saudi Aramco",
symbol="2222", start_date="2024-01-01", end_date="2025-12-31",
parameters={"fast": 10, "slow": 30, "starting_capital_sar": 100000},
) as exp:
exp.log_trades(trades)
exp.log_metrics({"total_return_pct": total_return_pct, "num_trades": len(trades)})
print("Logged experiment:", exp.id)
كل تشغيل تسجّله يظهر تحت التجارب في لوحتك بمعاملاته ومقاييسه وقائمة صفقاته ومنحنى رأس ماله — فتصفّ «SMA 10/30» أمام «RSI 30/70» أمام «MACD 12/26/9» على السهم والفترة نفسيهما، وتدع الأرقام تقرّر. هذا هو الفرق بين تشغيل اختبار وبناء سجلّ أداء.
لماذا تفعلها على تاسي تحديداً
يمكنك تقريب كل هذا بمكتبة اختبار عامة ومصدر بيانات مجاني. لكنك ستفقد بالضبط ما يحدّد نجاة الميزة عند احتكاكها بالسوق السعودي الحقيقي:
- عمولة حقيقية.
0.155%عمولة زائد15%ضريبة قيمة مضافة على العمولة، على كل تنفيذ — نفس حساب السوق. القاعدة عالية التردّد التي تبدو رابحة قبل الرسوم غالباً ليست كذلك بعدها. - أسعار وقواعد تداول حقيقية. أسعار تاسي التاريخية الفعلية، لا مؤشّراً بديلاً.
- ساعات تداول حقيقية. الأحد–الخميس، 10:00–15:00 بتوقيت السعودية — الرزنامة التي ستتداول عليها استراتيجيتك فعلاً.
حاكِ السوق الذي ستتداوله، لا بديلاً مريحاً عنه.
التالي: اقرأ توثيق الـ API الكامل، أو جرّب الاستراتيجيات المُستضافة الأخرى — ma_crossover وprice_ma_cross وmacd_signal_cross — بتبديل strategy_type في الطلب أعلاه.
