دليل · مختبر الاستراتيجيات
وحدة الاختبار الرجعي: اختبر استراتيجيات التداول بالكود
لكل رسم بياني زر الاستراتيجية. يفتح وحدة تعيد تشغيل استراتيجية على الشموع السابقة وتعرض كل صفقة على الرسم والربح والتراجع ونسبة الربح ومنحنى رأس المال مقارنةً بالشراء والاحتفاظ. ابدأ باستراتيجية مدمجة، ثم اكتب استراتيجيتك في بضعة أسطر من JavaScript.
آمن بالتصميم. يعمل سكربتك في متصفحك فقط، داخل بيئة معزولة بلا إنترنت: يمكنه قراءة الشموع المعطاة له ولا شيء غيرها (لا حسابك ولا هذا الموقع ولا مواقع أخرى). لا يُشغَّل شيء مما تكتبه على خوادمنا.
ابدأ في دقيقة
- افتح أي رسم بياني، مثل EURUSD، واختر الإطار الزمني الذي تريد اختباره (H1، D1…).
- اضغط الاستراتيجية في شريط أدوات الرسم. تُفتح الوحدة أسفل الرسم.
- اختر استراتيجية مدمجة، مثل تقاطع المتوسطات المتحركة. تعمل فورًا: تُعلّم الأسهم على الرسم كل دخول وخروج.
- غيّر مدخلًا (مثلًا المتوسط البطيء من 30 إلى 50): يُعاد الاختبار تلقائيًا.
- اقرأ نظرة عامة للإجماليات، والصفقات لكل صفقة، ومنحنى رأس المال للحساب عبر الزمن.
- اضغط تعديل نسخة لتحويلها إلى سكربتك الخاص، وغيّرها، ثم اضغط تشغيل الاختبار (أو Ctrl+Enter).
الوحدة جزءًا جزءًا
| الجزء | ما الذي يفعله |
|---|---|
| قائمة الاستراتيجيات | مدمجة: ست استراتيجيات جاهزة (تقاطع المتوسطات المتحركة، وانعكاس RSI، والارتداد إلى المتوسط مع نطاقات بولينجر، وتقاطع إشارة MACD، واختراق دونشيان، وSuperTrend). استراتيجياتي: التي حفظتها (سجّل الدخول). ويبدأ سكربت جديد… سكربتًا فارغًا. |
| عرض الكود / تعديل نسخة | يعرض عرض الكود ما تفعله الاستراتيجية المدمجة بالضبط. وينسخها تعديل نسخة إلى المحرر كسكربتك الخاص، جاهزة للتغيير. |
| المدخلات | يصبح كل input(…) في السكربت حقلًا هنا (رقمًا أو قائمة). يعيد تغيير أيٍّ منها تشغيل الاختبار. ويعيدها الافتراضيات. |
| الإعدادات | رأس المال وحجم الأمر والتكاليف وعدد الشموع وهل يُسمح بصفقات البيع (أدناه). |
| المحرر | كود JavaScript للاستراتيجية. يضيف Tab مسافة بادئة، ويشغّل Ctrl/⌘+Enter. يعرض الخطأ رسالته ورقم السطر. |
| المرجع | قائمة قصيرة بكل ما يمكن للسكربت استخدامه، داخل الوحدة مباشرة. هذه الصفحة هي النسخة المطوّلة. |
| حفظ / حفظ باسم… | يحفظ السكربت واسمه ومدخلاته وإعداداته في حسابك (النقطة على حفظ • تعني تغييرات غير محفوظة). تُدرج استراتيجياتك على كل رسم بياني. |
| تبويبات النتائج | نظرة عامة والصفقات (مع تنزيل CSV) ومنحنى رأس المال وLog. تُرسم الصفقات والخطوط على الرسم البياني أيضًا؛ انقر صفقة للانتقال إليها. |
الإعدادات
| الإعداد | المعنى | قيمة بداية جيدة |
|---|---|---|
| رأس المال الابتدائي | الحساب الذي يبدأ به الاختبار، بعملة السعر (لـ EURUSD: دولارات). | 10,000 |
| حجم الأمر | % من رأس المال: تستخدم كل صفقة هذه الحصة من الحساب في حينها (100% = الحساب كله، بلا رافعة). الوحدات: كمية ثابتة (للفوركس بوحدات العملة الأساسية: 100,000 = عقد قياسي واحد). يمكن للسكربت تحديد حجمه لكل أمر عبر { qty }. | 100% من رأس المال، أو عقد ثابت |
| العمولة % لكل جانب | رسوم بنسبة مئوية من القيمة المتداولة، تُحتسب عند الدخول ومرة أخرى عند الخروج. | 0.002–0.01 للفوركس، و0.05–0.1 للأسهم والعملات المشفرة |
| الانزلاق / الفارق (السعر) | يُضاف ضدك في كل تنفيذ، بوحدات السعر. الشموع أسعار bid، فضع هنا الفارق المعتاد لتكون عمليات الشراء واقعية. | EURUSD 0.00010 (نقطة واحدة)، الذهب 0.30 |
| الشموع | على الرسم البياني: الشموع المحمّلة على الشاشة. بعد تسجيل الدخول اختر حتى 5,000 … 100,000 لتحميل مزيد من تاريخ الإطار الزمني نفسه (يسمح هذا الموقع بما يصل إلى 20,000). | أكبر عدد ممكن: شموع أكثر وصفقات أكثر وأرقام أوثق |
| السماح بصفقات البيع | معطّل: يغلق short() مركز الشراء فقط ولا يفتح بيعًا أبدًا. مفيد لاختبار جانب الشراء وحده دون تعديل السكربت. | مفعّل للفوركس، ومعطّل للاستثمار بالشراء فقط |
قراءة النتائج
| رقم | ما الذي يخبرك به |
|---|---|
| صافي الربح | رأس المال النهائي ناقص رأس المال الابتدائي، بعد العمولة والانزلاق؛ وأيضًا كنسبة من رأس المال. |
| الشراء والاحتفاظ | ما كان سيحققه مجرد الشراء عند الشمعة الأولى والاحتفاظ حتى الأخيرة. الاستراتيجية التي لا تتفوق عليه، أو تتفوق بتراجع أعمق بكثير، تضيف القليل. |
| أقصى تراجع | أكبر هبوط لرأس المال من قمة، بالمال والنسبة. الألم الذي كان عليك تحمّله. ينظر كثير من المتداولين إلى صافي الربح ÷ أقصى تراجع. |
| الصفقات، % الرابحة | عدد الصفقات المغلقة ونسبة الرابحة منها. لا بأس بنسبة ربح منخفضة إذا كانت الرابحة أكبر بكثير من الخاسرة. |
| عامل الربح | إجمالي الربح ÷ إجمالي الخسارة. ما فوق 1 يحقق ربحًا؛ و1.3–2 معتاد في الاستراتيجيات السليمة؛ والقيم المرتفعة جدًا مع صفقات قليلة غالبًا حظ. |
| متوسط الصفقة، ومتوسط الربح، ومتوسط الخسارة | متوسط الربح لكل صفقة، والحجم المعتاد للرابحة والخاسرة. قارن متوسط الصفقة بتكاليفك. |
| أكبر ربح / خسارة، والصفقات الخاسرة المتتالية | القيم المتطرفة. صفقة رابحة ضخمة واحدة تحمل النتيجة كلها علامة تحذير. |
| نسبة شارب | العائد لكل وحدة تقلب، سنويًا من تغيرات رأس المال من شمعة إلى أخرى. فوق 1 جيد، وفوق 2 جيد جدًا (ونادر). |
| الوقت في السوق | نسبة الشموع التي فيها مركز مفتوح. وقت أقل للربح نفسه يعني مخاطرة أقل. |
| شراء / بيع | الصفقات والربح ونسبة الربح لكل جانب على حدة: غالبًا ما يقوم جانب واحد بكل العمل. |
يعرض تبويب الصفقات كل صفقة باتجاهها ووقت وسعر الدخول والخروج والحجم والربح بالمال والنسبة وعدد الشموع التي استغرقتها وسبب إغلاقها (إشارة، وقف خسارة، جني أرباح، معكوسة، ما زالت مفتوحة في النهاية) وتعليقك. يصدّرها تنزيل CSV إلى جدول بيانات. ويُظهر منحنى رأس المال الحساب مقابل الشراء والاحتفاظ. ويُظهر Log ما كتبه سكربتك عبر log().
كيف تُنفَّذ الأوامر
- لا اطّلاع على المستقبل. يُنفَّذ الأمر الموضوع في
onBar(i)عند افتتاح الشمعة التالية (i + 1). لا يمكن للاستراتيجية أبدًا التداول بسعر لم يكن بإمكانها معرفته. - أوامر الوقف داخل الشمعة. يُفحص وقف الخسارة وجني الأرباح مقابل أعلى كل شمعة وأدناها. وإذا أمكن بلوغ كليهما في الشمعة نفسها يُحتسب الوقف (الافتراض الحذر). والفجوة عبر الوقف تُنفَّذ عند الافتتاح، كما في الأسواق الحقيقية.
- مركز واحد في كل مرة. يغلق
long()أثناء مركز البيع ذلك المركز ويفتح شراءً («معكوس»)؛ وlong()مع وجود مركز شراء يحدّث فقط أوامر الوقف التي تمررها. - تكاليف على كل تنفيذ. يحرّك الانزلاق كل سعر تنفيذ ضدك؛ وتُحتسب العمولة عند الدخول والخروج.
- النهاية. تُغلق الصفقة التي تبقى مفتوحة بعد آخر شمعة عند آخر إغلاق وتُوسم بـ «ما زالت مفتوحة في النهاية».
كتابة استراتيجيتك الخاصة
الاستراتيجية برنامج JavaScript قصير من جزأين:
- يعمل الجزء العلوي مرة واحدة: اقرأ مدخلاتك بـ
input()، واحسب سلاسل كاملة بـta.*أوind()، وارسمها بـplot(). - يعمل
function onBar(i) { … }لكل شمعة من الأقدم.iرقم الشمعة (0 = الأقدم). قرّر هنا:long()أوshort()أوexit()أو لا شيء.
// 1. once: inputs and series
const len = input("Average length", 50, { min: 5, max: 400 });
const avg = ta.sma(close, len); // one value per candle
plot(avg, "SMA " + len, "#2F80ED"); // drawn on the price chart
// 2. every candle
function onBar(i) {
if (avg[i] == null) return; // the first len - 1 values are null (warming up)
if (position === 0 && close[i] > avg[i]) long();
if (position > 0 && close[i] < avg[i]) exit("below the average");
}
أمور يجدر معرفتها من البداية:
- السلاسل مصفوفات، قيمة لكل شمعة من الأقدم:
close[i]إغلاق هذه الشمعة، وclose[i - 1]إغلاق التي قبلها. تكون السلاسل المحسوبةnullإلى أن تتوفر شموع كافية؛ تحقق قبل استخدامها. - داخل
onBar(i)لا يمكنك قراءة الشموع إلا حتىi. يوقفclose[i + 1]الاختبار بخطأ: فذلك اطّلاع على المستقبل. - استخدم
constوletكالمعتاد؛ تحتفظ متغيراتك بقيمها بين الشموع (مفيد للوقف المتحرك). - الأسعار بوحدات الرسم نفسه (لـ EURUSD 1.0850، وللذهب 2650.40)؛ والوقت بثوانٍ UTC.
المرجع
الشموع والحالة الحالية
| الاسم | ما هو |
|---|---|
open, high, low, close, volume | مصفوفات قيم الشموع من الأقدم (الحجم هو حجم التيك). |
time | وقت افتتاح كل شمعة بثوانٍ UTC. يعطي new Date(time[i] * 1000).getUTCHours() الساعة. |
bars, symbol, timeframe | عدد الشموع والرمز (مثل "EURUSD") والإطار الزمني (مثل "H1"). |
position | داخل onBar: 1 شراء، و-1 بيع، و0 بلا مركز. |
positionSize, entryPrice, entryBar | حجم الصفقة المفتوحة بالوحدات وسعر تنفيذها ورقم شمعتها (null بلا مركز). i - entryBar = الشموع المحتفظ بها. |
equity, cash | قيمة الحساب عند إغلاق هذه الشمعة (مع الصفقة المفتوحة)، والنقد بدونها. |
المدخلات
| الاستدعاء | النتيجة |
|---|---|
input("Period", 14, { min: 2, max: 100, step: 1 }) | حقل رقمي (يبقى بين min وmax). يعيد القيمة المكتوبة، أو 14. |
input("Average", "EMA", { options: ["SMA", "EMA"] }) | قائمة. تعيد النص المختار. |
حتى 30 مدخلًا. تُحفظ قيمها مع الاستراتيجية وتنتقل معها عند نشرها في فكرة.
الحسابات (ta.*)
| الدالة | يعيد |
|---|---|
ta.sma(src, n), ta.ema(src, n), ta.wma(src, n), ta.rma(src, n) | المتوسط المتحرك البسيط والأسي والمرجّح ومتوسط وايلدر لأي سلسلة. |
ta.rsi(src, 14) | RSI (0–100). |
ta.atr(14), ta.tr() | متوسط المدى الحقيقي (متوسط بسيط كما على الرسم) والمدى الحقيقي لكل شمعة. |
ta.stdev(src, n) | الانحراف المعياري على n شموع. |
ta.highest(src, n), ta.lowest(src, n) | أعلى / أدنى قيمة لآخر n شموع، بما فيها هذه. للاختراق قارن مع [i - 1]. |
ta.change(src, n = 1), ta.roc(src, n = 1) | الفرق عن n شموع مضت، والفرق نفسه بالنسبة المئوية. |
ta.macd(src, 12, 26, 9) | { macd, signal, hist }. |
ta.bb(src, 20, 2) | نطاقات بولينجر { upper, middle, lower }. |
ta.stoch(14, 3, 3) | العشوائي { k, d } (الفترة، وتنعيم %D، والإبطاء). |
ta.supertrend(10, 3) | { line, dir }: تكون dir مساوية 1 في الاتجاه الصاعد و-1 في الهابط. |
src أي سلسلة: close أو high أو نتيجة أخرى أو مصفوفتك الخاصة. تعيد كل دالة مصفوفة جديدة؛ ولا شيء يغيّر الشموع.
الإشارات
| الدالة | صحيح عندما… |
|---|---|
ta.crossover(a, b, i) | يعبر a فوق b على الشمعة i (كان عنده أو تحته على i - 1). ويمكن أن يكون b رقمًا: ta.crossover(rsi, 30, i). |
ta.crossunder(a, b, i) | يعبر a تحت b. |
ta.cross(a, b, i) | أيٌّ منهما. |
ta.rising(s, n, i), ta.falling(s, n, i) | ارتفع s (أو انخفض) في كل واحدة من آخر n شموع. |
الأوامر
| الاستدعاء | التأثير (عند افتتاح الشمعة التالية) |
|---|---|
long(options) (أو buy) | يفتح مركز شراء؛ ويغلق مركز البيع أولًا. |
short(options) (أو sell) | يفتح مركز بيع؛ ويغلق مركز الشراء أولًا. ومع تعطيل صفقات البيع يغلق الشراء فقط. |
exit("reason") | يغلق المركز المفتوح. ويظهر السبب في قائمة الصفقات. |
stopLoss(price), takeProfit(price) | يحرّك وقف / هدف الصفقة المفتوحة فورًا (null يزيله). استخدمها للوقف المتحرك. |
| الخيار | المعنى |
|---|---|
{ sl: 1.0820, tp: 1.0950 } | وقف الخسارة وجني الأرباح كأسعار. |
{ slPct: 2, tpPct: 4 } | الوقف والهدف بالنسبة المئوية من سعر التنفيذ. |
{ qty: 50000 } | الحجم بالوحدات لهذا الأمر (بدلًا من إعداد حجم الأمر). |
{ comment: "breakout" } | ملاحظة تُعرض مع الصفقة. |
المخرجات
| الاستدعاء | التأثير |
|---|---|
plot(series, "Name", "#2F80ED") | يرسم خطًا على رسم الأسعار (حتى 12). |
log("text", value, …) | يكتب سطرًا في تبويب Log مع وقت الشمعة (حتى 200 سطر). |
كل مؤشرات الرسم الـ 71 في الكود: ind()
يحسب ind(id, inputs) أي مؤشر من مؤشرات الرسم تمامًا كما يحسبه الرسم. ويعيد خطه الأول كسلسلة؛ وكل الخطوط في .lines وأسماؤها في .names. المدخلات التي تتركها تأخذ القيمة الافتراضية المعروضة. method واحد من SMA وEMA وSMMA وLWMA؛ وprice واحد من close وopen وhigh وlow وmedian وtypical وweighted.
const st = ind("stoch", { k: 14, d: 3, slowing: 3 }); // st = %K, st.lines[1] = %D
const ich = ind("ichimoku"); // ich.names: Tenkan, Kijun, Span A, Span B, Chikou
const kijun = ich.lines[1];
| id | مؤشر | المدخلات (الافتراضية) | الخطوط |
|---|---|---|---|
ma | Moving Average | period: 20, method: SMA, price: close, shift: 0 | MA |
ama | Adaptive Moving Average | period: 9, fast: 2, slow: 30, price: close | AMA |
adx | Average Directional Movement Index | period: 14 | ADX, +DI, −DI |
adxw | ADX Wilder | period: 14 | ADX, +DI, −DI |
bb | Bollinger Bands | period: 20, dev: 2, price: close, shift: 0 | Middle, Upper, Lower |
dema | Double Exponential Moving Average | period: 14, price: close | DEMA |
env | Envelopes | period: 14, method: SMA, dev: 0.1, price: close | Upper, Lower |
frama | Fractal Adaptive Moving Average | period: 14, price: close | FrAMA |
ichimoku | Ichimoku Kinko Hyo | tenkan: 9, kijun: 26, senkou: 52 | Tenkan, Kijun, Span A, Span B, Chikou |
sar | Parabolic SAR | step: 0.02, max: 0.2 | SAR |
stddev | Standard Deviation | period: 20, method: SMA, price: close | StdDev |
tema | Triple Exponential Moving Average | period: 14, price: close | TEMA |
vidya | Variable Index Dynamic Average | cmo: 9, ema: 12, price: close | VIDYA |
atr | Average True Range | period: 14 | ATR |
bears | Bears Power | period: 13 | Bears |
bulls | Bulls Power | period: 13 | Bulls |
cci | Commodity Channel Index | period: 14, price: typical | CCI |
chaikin | Chaikin Oscillator | fast: 3, slow: 10, method: EMA | CHO |
demarker | DeMarker | period: 14 | DeMarker |
force | Force Index | period: 13, method: SMA | Force |
macd | MACD | fast: 12, slow: 26, signal: 9, price: close | MACD, Signal |
momentum | Momentum | period: 14, price: close | Momentum |
osma | Moving Average of Oscillator | fast: 12, slow: 26, signal: 9, price: close | OsMA |
rsi | Relative Strength Index | period: 14, price: close | RSI |
rvi | Relative Vigor Index | period: 10 | RVI, Signal |
stoch | Stochastic Oscillator | k: 5, d: 3, slowing: 3, method: SMA | %K, %D |
trix | Triple Exponential Average | period: 14, price: close | TRIX |
wpr | Williams' Percent Range | period: 14 | %R |
ad | Accumulation/Distribution | لا شيء | A/D |
mfi | Money Flow Index | period: 14 | MFI |
obv | On Balance Volume | لا شيء | OBV |
volumes | Volumes | لا شيء | Volume |
ac | Accelerator Oscillator | لا شيء | AC |
alligator | Alligator | jaw: 13, jawShift: 8, teeth: 8, teethShift: 5, lips: 5, lipsShift: 3 | Jaw, Teeth, Lips |
ao | Awesome Oscillator | لا شيء | AO |
fractals | Fractals | لا شيء | Up, Down |
gator | Gator Oscillator | jaw: 13, teeth: 8, lips: 5 | Upper, Lower |
bwmfi | Market Facilitation Index | لا شيء | BW MFI |
zigzag | ZigZag | depth: 12, dev: 0.3 | ZigZag |
vwap | VWAP (volume-weighted average price) | لا شيء | VWAP |
supertrend | SuperTrend | period: 10, mult: 3 | Up trend, Down trend |
keltner | Keltner Channels | period: 20, atr: 10, mult: 2 | Middle, Upper, Lower |
donchian | Donchian Channels | period: 20 | Upper, Lower, Middle |
hma | Hull Moving Average | period: 20, price: close | HMA |
pivots | Pivot Points (classic) | لا شيء | P, R1, S1, R2, S2, R3, S3 |
stochrsi | Stochastic RSI | rsi: 14, stoch: 14, k: 3, d: 3 | %K, %D |
aroon | Aroon | period: 25 | Aroon up, Aroon down |
cmf | Chaikin Money Flow | period: 20 | CMF |
uo | Ultimate Oscillator | p1: 7, p2: 14, p3: 28 | UO |
chop | Choppiness Index | period: 14 | CHOP |
vwma | Volume-Weighted Moving Average | period: 20 | VWMA |
zlema | Zero-Lag Exponential Moving Average | period: 21, price: close | ZLEMA |
alma | Arnaud Legoux Moving Average | period: 9, offset: 0.85, sigma: 6, price: close | ALMA |
mcginley | McGinley Dynamic | period: 14 | McGinley |
lsma | Least Squares Moving Average | period: 25, price: close | LSMA |
chandelier | Chandelier Exit | period: 22, mult: 3 | Long exit, Short exit |
roc | Rate of Change | period: 12 | ROC |
cmo | Chande Momentum Oscillator | period: 9 | CMO |
tsi | True Strength Index | long: 25, short: 13, signal: 13 | TSI, Signal |
ppo | Percentage Price Oscillator | fast: 12, slow: 26, signal: 9 | Histogram, PPO, Signal |
dpo | Detrended Price Oscillator | period: 20 | DPO |
kst | Know Sure Thing | signal: 9 | KST, Signal |
vortex | Vortex Indicator | period: 14 | VI+, VI− |
eom | Ease of Movement | period: 14 | EOM |
mass | Mass Index | ema: 9, sum: 25 | Mass Index |
coppock | Coppock Curve | wma: 10, long: 14, short: 11 | Coppock |
hv | Historical Volatility | period: 20 | HV % |
fisher | Fisher Transform | period: 9 | Fisher, Trigger |
squeeze | Squeeze Momentum | bb: 20, bbk: 2, kc: 20, kck: 1.5 | Momentum, Squeeze on, Squeeze off |
crsi | Connors RSI | rsi: 3, streak: 2, rank: 100 | CRSI |
pvt | Price Volume Trend | لا شيء | PVT |
أمثلة
كل مثال يعمل كما هو: اضغط نسخ، وافتح الوحدة، واختر سكربت جديد…، والصق، ثم اضغط تشغيل الاختبار. تعتمد النتائج على الرمز والإطار الزمني والفترة؛ الأمثلة تعلّم الأسلوب وليست توصيات.
1. Your first strategy: buy above a moving average
The smallest useful script. It is long while the close is above its 50-candle average and flat otherwise. The top of the script runs once; onBar(i) runs for every candle.
const len = input("Average length", 50, { min: 5, max: 400 });
const avg = ta.sma(close, len);
plot(avg, "SMA " + len, "#2F80ED");
function onBar(i) {
if (avg[i] == null) return; // still warming up
if (position === 0 && close[i] > avg[i]) long({ comment: "close above the average" });
if (position > 0 && close[i] < avg[i]) exit("close below the average");
}
2. Moving average crossover, long and short
Always in the market: long() while short reverses the position (and short() while long). Untick Allow short trades in the settings to make it long-only without changing the code.
const fast = ta.ema(close, input("Fast EMA", 12, { min: 2, max: 200 }));
const slow = ta.ema(close, input("Slow EMA", 26, { min: 3, max: 400 }));
plot(fast, "Fast", "#2F80ED");
plot(slow, "Slow", "#E39B2D");
function onBar(i) {
if (ta.crossover(fast, slow, i)) long();
if (ta.crossunder(fast, slow, i)) short();
}
3. Fixed stop loss and take profit in percent
slPct and tpPct are measured from the fill price. The stop and the target are checked inside every candle with its high and low; if both could have been hit in one candle, the stop counts.
const r = ta.rsi(close, 14);
const sl = input("Stop loss %", 1.5, { min: 0.1, max: 20, step: 0.1 });
const tp = input("Take profit %", 3, { min: 0.1, max: 50, step: 0.1 });
function onBar(i) {
if (position !== 0) return; // one trade at a time
if (ta.crossover(r, 30, i)) long({ slPct: sl, tpPct: tp, comment: "RSI back above 30" });
if (ta.crossunder(r, 70, i)) short({ slPct: sl, tpPct: tp, comment: "RSI back below 70" });
}
4. ATR stop and a 2:1 target in prices
sl and tp take prices. Here the stop sits 2 ATR from the close and the target twice as far (risk/reward 1:2). The fill is at the next open, so the real distance can differ slightly.
const a = ta.atr(14);
const hi = ta.highest(high, 20);
const mult = input("Stop (ATR multiple)", 2, { min: 0.5, max: 6, step: 0.5 });
function onBar(i) {
if (i < 1 || a[i] == null || hi[i - 1] == null || position !== 0) return;
if (close[i] > hi[i - 1]) { // a new 20-candle high
const stop = close[i] - mult * a[i];
const target = close[i] + 2 * mult * a[i];
long({ sl: stop, tp: target, comment: "breakout, risk " + (mult * a[i]).toFixed(5) });
}
}
5. A trailing stop that follows the price
stopLoss(price) moves the stop of the open trade. This one trails 3 ATR under the highest close since the entry and never moves down.
const a = ta.atr(14);
const fast = ta.ema(close, 20), slow = ta.ema(close, 50);
let stop = null;
function onBar(i) {
if (a[i] == null || slow[i] == null) return;
if (position === 0) {
stop = null;
if (ta.crossover(fast, slow, i)) long({ comment: "trend up" });
return;
}
const candidate = close[i] - 3 * a[i];
if (stop === null || candidate > stop) { // only ever raise it
stop = candidate;
stopLoss(stop);
}
}
6. Any chart indicator with ind(): ADX trend filter
ind(id, inputs) gives the first line of any of the chart's 71 indicators; the other lines are in .lines (their names in .names). Here: trade the MACD cross only when ADX says the market is trending.
const adx = ind("adx", { period: 14 }); // .lines[0] ADX, [1] +DI, [2] -DI
const plusDI = adx.lines[1], minusDI = adx.lines[2];
const m = ta.macd(close, 12, 26, 9);
const minAdx = input("Minimum ADX", 25, { min: 10, max: 50 });
function onBar(i) {
if (adx[i] == null || adx[i] < minAdx) return;
if (ta.crossover(m.macd, m.signal, i) && plusDI[i] > minusDI[i]) long();
if (ta.crossunder(m.macd, m.signal, i) && minusDI[i] > plusDI[i]) short();
}
7. Mean reversion with Bollinger Bands
Buys a close under the lower band, sells a close over the upper band, and exits at the middle band. A classic for ranging markets; it suffers in strong trends.
const b = ta.bb(close, input("Period", 20, { min: 5, max: 200 }), input("Width", 2, { min: 0.5, max: 4, step: 0.1 }));
plot(b.upper, "Upper", "#8E44AD");
plot(b.lower, "Lower", "#8E44AD");
function onBar(i) {
if (b.middle[i] == null) return;
if (position === 0 && close[i] < b.lower[i]) long({ comment: "below the lower band" });
else if (position === 0 && close[i] > b.upper[i]) short({ comment: "above the upper band" });
else if (position > 0 && close[i] >= b.middle[i]) exit("middle band");
else if (position < 0 && close[i] <= b.middle[i]) exit("middle band");
}
8. Trade only at certain hours (time filter)
time[i] is the candle's open in UTC seconds. This example only opens trades between 07:00 and 16:00 UTC (the London session) and closes anything still open at 20:00. Use it on an intraday timeframe.
const from = input("Start hour (UTC)", 7, { min: 0, max: 23 });
const to = input("End hour (UTC)", 16, { min: 1, max: 24 });
const fast = ta.sma(close, 10), slow = ta.sma(close, 30);
function onBar(i) {
const hour = new Date(time[i] * 1000).getUTCHours();
if (position !== 0 && hour >= 20) { exit("end of the day"); return; }
if (hour < from || hour >= to) return;
if (ta.crossover(fast, slow, i)) long();
if (ta.crossunder(fast, slow, i)) short();
}
9. Your own calculation: a volatility breakout
Series are plain arrays, so you can compute your own. This builds yesterday's range for every candle and buys a break above the open plus half of it. log() writes to the Log tab.
const k = input("Range multiple", 0.5, { min: 0.1, max: 2, step: 0.1 });
const range = high.map((h, i) => (i ? high[i - 1] - low[i - 1] : null)); // the previous candle's range
const trigger = open.map((o, i) => (range[i] == null ? null : o + k * range[i]));
plot(trigger, "Trigger", "#16A085");
function onBar(i) {
if (trigger[i] == null) return;
if (position === 0 && close[i] > trigger[i]) {
long({ slPct: 1, comment: "close above open + " + k + " x range" });
log("signal at", close[i], "trigger", trigger[i]);
} else if (position > 0 && i - entryBar >= 3) {
exit("held 3 candles");
}
}
10. Position size from risk (1% of equity per trade)
{ qty } sets the size in units for one order, overriding the Order size setting. Here the size is chosen so that hitting the stop loses about 1% of the current equity.
const a = ta.atr(14);
const riskPct = input("Risk per trade %", 1, { min: 0.1, max: 5, step: 0.1 });
const fast = ta.ema(close, 20), slow = ta.ema(close, 50);
function onBar(i) {
if (a[i] == null || slow[i] == null || position !== 0) return;
if (ta.crossover(fast, slow, i)) {
const stopDistance = 2 * a[i];
const qty = (equity * riskPct / 100) / stopDistance;
long({ qty: qty, sl: close[i] - stopDistance, tp: close[i] + 3 * stopDistance,
comment: "risk " + riskPct + "%" });
}
}
11. Two conditions in a row: pullback in an uptrend
Combines a trend filter (price above the 200 average), a pullback (RSI under 40 within the last 5 candles) and a trigger (close back above the 20 average).
const trend = ta.sma(close, 200), fast = ta.ema(close, 20), r = ta.rsi(close, 14);
function onBar(i) {
if (trend[i] == null || i < 5) return;
let dipped = false;
for (let k = 0; k < 5; k++) if (r[i - k] != null && r[i - k] < 40) dipped = true;
if (position === 0 && close[i] > trend[i] && dipped && ta.crossover(close, fast, i)) {
long({ slPct: 2, tpPct: 4, comment: "pullback bought" });
}
}
الأخطاء وكيفية إصلاحها
| الرسالة | السبب والحل |
|---|---|
| the script needs a function onBar(i) { … } | لا يحتوي السكربت على onBar، أو أنه مكتوب خطأً (onbar). اكتب function onBar(i) { … } في المستوى الأعلى من السكربت، لا داخل دالة أخرى. |
| close[124] is in the future: inside onBar(123) only candles up to 123 are known | قرأت شمعة لاحقة. استخدم close[i] وما قبله (close[i - 1]). للاختراق، قارن إغلاق اليوم بأعلى قمة للأمس: close[i] > hi[i - 1]. |
| close cannot be changed | الشموع للقراءة فقط. أنشئ مصفوفتك الخاصة بدلًا من ذلك: const mid = close.map((c, i) => (c + open[i]) / 2); |
| Cannot read properties of null / ظهور NaN في النتائج | تكون السلسلة null في البداية (مرحلة الإحماء). ابدأ onBar بـ if (avg[i] == null) return;، وتحقق من i >= 1 قبل استخدام [i - 1]. |
| unknown indicator "…" | استخدم معرّفًا من جدول المؤشرات (بأحرف صغيرة)، مثل ind("adx") لا ind("ADX"). |
| لا صفقات: لم تضع الاستراتيجية أي أمر | لم تتحقق الشروط معًا قط. أضف log("rsi", r[i]) لترى القيم، أو خفّف شرطًا، أو حمّل مزيدًا من الشموع. |
| استغرقت الاستراتيجية أكثر من 15 ثانية (حلقة لا نهائية؟) | حلقة لا تنتهي، أو حلقة ثقيلة داخل onBar. احسب السلاسل مرة واحدة في الأعلى بدلًا من المرور على كل الشموع عند كل شمعة. |
| Unexpected token / SyntaxError (السطر N) | خطأ كتابي في ذلك السطر: قوس أو علامة اقتباس أو فاصلة ناقصة. |
اختبار يمكنك الوثوق به
- أضف تكاليفك. قد تبدو الاستراتيجية التي تتداول كثيرًا رائعة بلا تكاليف وتخسر المال بفارق واقعي. املأ دائمًا الانزلاق (الفارق) والعمولة.
- استخدم عددًا كافيًا من الصفقات. تحت نحو 30 صفقة تكون الأرقام في معظمها صدفة. حمّل مزيدًا من الشموع أو استخدم إطارًا زمنيًا أصغر.
- لا تُفصّلها على الماضي. تجربة مدخلات كثيرة حتى تصبح النتيجة مثالية تجد ضجيج تلك الفترة لا قاعدة تعمل. اضبط على جزء من التاريخ، ثم افحص المدخلات نفسها على فترة أخرى وعلى بضعة رموز أخرى.
- قارن بالشراء والاحتفاظ وانظر إلى التراجع، لا إلى الربح فقط.
- راقب القيم المتطرفة. إذا حققت صفقة أو اثنتان كل الربح، فالاستراتيجية تعتمد على الحظ.
- تدرّب قبل المخاطرة بالمال. شغّل الاستراتيجية للأمام في التداول التجريبي أو مع إعادة الشموع، واضبط تنبيهات لإشاراتها.
تستخدم الاختبارات الرجعية شموع bid السابقة وتتجاهل المقايضات والتمويل والفوارق التي تتغير مع السوق. النتائج السابقة لا تضمن النتائج المستقبلية. هذه ليست نصيحة استثمارية.
الحفظ والمشاركة والنشر
- يحتفظ حفظ (بعد تسجيل الدخول) بما يصل إلى 50 استراتيجية بمدخلاتها وإعداداتها؛ وتظهر في القائمة على كل رسم بياني.
- انشر فكرة من الرسم البياني وأرفق استراتيجية: يمكن للقراء فتح رسمك وتشغيل الاستراتيجية بأنفسهم بمدخلاتك. راجع الأفكار.
- شارك تحليلك مع فريق في نادٍ.
- يصدّر تنزيل CSV في تبويب الصفقات الصفقات إلى جدول بيانات.
- للأبحاث الأعمق، اجلب الشموع نفسها عبر API الأسعار (
GET /bars) إلى Python أو R أو Excel.
الحدود
| الشموع لكل اختبار | الزوار: الشموع على الرسم البياني. المسجّلون: حتى 20,000 (يمكن لخطط Premium رفع هذا الحد؛ راجع Premium). |
| النص البرمجي | 50,000 حرف، و30 مدخلًا، و12 خطًا مرسومًا، و200 سطر سجل، و5,000 صفقة مدرجة، و15 ثانية تشغيل. |
| الاستراتيجيات المحفوظة | 50 لكل حساب. |
| المراكز | مركز واحد في كل مرة لكل اختبار (شراء أو بيع أو بلا مركز)؛ ورمز واحد وإطار زمني واحد لكل اختبار. |
الأسئلة
هل أحتاج إلى معرفة البرمجة؟
لا. تعمل الاستراتيجيات المدمجة دون كود؛ تغيّر مدخلاتها فقط. وإذا أردت المزيد، يمنحك تعديل نسخة سكربتًا عاملًا تغيّره سطرًا سطرًا، وتغطي الأمثلة أعلاه اللبنات المعتادة.
هل هذا مثل Pine Script؟
الفكرة نفسها (السلاسل والمدخلات والرسوم والأوامر)، واللغة JavaScript عادية. تُترجم معظم استراتيجيات Pine البسيطة سطرًا بسطر: يبقى ta.sma(close, 20) كما هو ta.sma(close, 20)، ويصبح strategy.entry("L", strategy.long) long()، ويدخل متن السكربت في onBar(i) مع [i] بعد كل سلسلة.
هل يمكن للاستراتيجية التداول نيابةً عني؟
لا. لا تختبر الوحدة إلا على الشموع السابقة. لمتابعة استراتيجية مباشرة، استخدم التداول التجريبي والتنبيهات، أو استدعِ API الأسعار من برنامجك.
لماذا تختلف نتيجتي عن نتيجة منصة أخرى؟
الشموع المختلفة (أسعار bid مقابل المتوسطة، وأوقات جلسات الوسيط)، والتنفيذ عند الافتتاح التالي بدل الإغلاق، والتكاليف، وقاعدة الوقف قبل الهدف، كلها تغيّر النتائج. قارن المتماثل بالمتماثل.
هل الكود الخاص بي خاص؟
نعم. يعمل في متصفحك؛ ولا يُخزَّن في حسابك إلا عند الضغط على حفظ، ولا يراه الآخرون إلا إذا نشرته في فكرة.