راهنما · آزمونگر استراتژی

کنسول بک‌تست: آزمودن استراتژی‌های معاملاتی با کد

هر نمودار یک دکمه استراتژی دارد. کنسولی باز می‌کند که یک استراتژی را روی شمع‌های گذشته بازپخش می‌کند و هر معامله روی نمودار، سود، افت سرمایه، نرخ برد و منحنی سرمایه در برابر خرید و نگهداری را نشان می‌دهد. با یک استراتژی داخلی شروع کنید، سپس استراتژی خودتان را در چند خط JavaScript بنویسید.

باز کردن کنسول روی EURUSD رفتن به مثال‌ها

امن از پایه. اسکریپت شما فقط در مرورگر خودتان، در محیطی ایزوله بدون دسترسی به اینترنت اجرا می‌شود: می‌تواند شمع‌هایی را که به آن داده می‌شود بخواند و نه چیز دیگری (نه حساب شما، نه این سایت، نه سایت‌های دیگر). هیچ چیزی از نوشته‌های شما روی سرورهای ما اجرا نمی‌شود.

شروع در یک دقیقه

  1. هر نموداری را باز کنید، مثلاً EURUSD، و تایم‌فریمی را که می‌خواهید بیازمایید انتخاب کنید (H1، D1…).
  2. در نوار ابزار نمودار استراتژی را بزنید. کنسول زیر نمودار باز می‌شود.
  3. یک استراتژی داخلی انتخاب کنید، مثلاً تقاطع میانگین‌های متحرک. بلافاصله اجرا می‌شود: پیکان‌ها روی نمودار هر ورود و خروج را نشان می‌دهند.
  4. یک ورودی را تغییر دهید (مثلاً میانگین کند را از ۳۰ به ۵۰): آزمون خودبه‌خود دوباره اجرا می‌شود.
  5. نمای کلی را برای جمع‌ها، معاملات را برای هر معامله و منحنی سرمایه را برای حساب در طول زمان بخوانید.
  6. ویرایش یک نسخه را بزنید تا به اسکریپت خودتان تبدیل شود، تغییرش دهید و اجرای آزمون (یا Ctrl+Enter) را بزنید.

کنسول، بخش به بخش

بخشچه می‌کند
فهرست استراتژی‌هاداخلی: شش استراتژی آماده (تقاطع میانگین‌های متحرک، برگشت RSI، بازگشت به میانگین باندهای بولینگر، تقاطع سیگنال MACD، شکست دونچیان، SuperTrend). استراتژی‌های من: آن‌هایی که ذخیره کرده‌اید (وارد شوید). اسکریپت جدید… یک اسکریپت خالی شروع می‌کند.
دیدن کد / ویرایش یک نسخهدیدن کد دقیقاً نشان می‌دهد یک استراتژی داخلی چه می‌کند. ویرایش یک نسخه آن را به‌عنوان اسکریپت خودتان در ویرایشگر کپی می‌کند، آماده تغییر.
ورودی‌هاهر input(…) در اسکریپت اینجا به یک فیلد تبدیل می‌شود (عدد یا فهرست). تغییر هر کدام آزمون را دوباره اجرا می‌کند. پیش‌فرض‌ها آن‌ها را برمی‌گرداند.
تنظیماتسرمایه، اندازه سفارش، هزینه‌ها، تعداد شمع‌ها و اینکه معاملات فروش مجازند یا نه (پایین‌تر).
ویرایشگرکد JavaScript استراتژی. Tab تورفتگی می‌دهد، Ctrl/⌘+Enter اجرا می‌کند. خطا پیام و شماره خط را نشان می‌دهد.
شناسه مرجعفهرستی کوتاه از هر آنچه اسکریپت می‌تواند به کار ببرد، درست درون کنسول. این صفحه نسخه کامل آن است.
ذخیره / ذخیره با نام…اسکریپت، نام، ورودی‌ها و تنظیماتش را در حساب شما نگه می‌دارد (نقطه روی ذخیره • یعنی تغییرات ذخیره‌نشده). استراتژی‌هایتان روی هر نموداری فهرست می‌شوند.
زبانه‌های نتایجنمای کلی، معاملات (با دانلود CSV)، منحنی سرمایه و Log. معاملات و خطوط رسم‌شده روی نمودار هم کشیده می‌شوند؛ برای رفتن به یک معامله روی آن کلیک کنید.

تنظیمات

تنظیممعنامقدار شروع مناسب
سرمایه اولیهحسابی که آزمون با آن شروع می‌شود، به ارز قیمت (برای EURUSD: دلار).10,000
اندازه سفارشدرصد سرمایه: هر معامله این سهم از حساب را در آن لحظه به کار می‌برد (۱۰۰٪ = کل حساب، بدون اهرم). واحد: مقداری ثابت (برای فارکس، واحدهای ارز پایه: ۱۰۰٬۰۰۰ = یک لات استاندارد). اسکریپت می‌تواند با { qty } اندازه هر سفارش را خودش تعیین کند.۱۰۰٪ سرمایه، یا یک لات ثابت
کارمزد ٪ هر طرفکارمزدی به‌صورت درصدی از ارزش معامله‌شده، که در ورود و دوباره در خروج گرفته می‌شود.۰٫۰۰۲–۰٫۰۱ برای فارکس، ۰٫۰۵–۰٫۱ برای سهام و رمزارز
لغزش / اسپرد (قیمت)در هر اجرا به زیان شما افزوده می‌شود، به واحد قیمت. شمع‌ها قیمت‌های bid هستند، پس اسپرد معمول را اینجا وارد کنید تا خریدها واقع‌بینانه باشند.EURUSD ۰٫۰۰۰۱۰ (۱ پیپ)، طلا ۰٫۳۰
کندلروی نمودار: شمع‌های بارگذاری‌شده روی صفحه. با ورود به حساب، تا ۵٬۰۰۰ … ۱۰۰٬۰۰۰ را انتخاب کنید تا تاریخچه بیشتری از همان تایم‌فریم بارگذاری شود (این سایت تا 20,000 اجازه می‌دهد).هر چه بیشتر: شمع‌های بیشتر، معاملات بیشتر، اعداد قابل‌اعتمادتر
اجازه معاملات فروشخاموش: short() فقط موقعیت خرید را می‌بندد و هرگز فروش باز نمی‌کند. برای آزمودن فقط سمت خرید بدون ویرایش اسکریپت مفید است.برای فارکس روشن، برای سرمایه‌گذاری فقط خرید خاموش

خواندن نتایج

عددچه چیزی به شما می‌گوید
سود خالصسرمایه نهایی منهای سرمایه اولیه، پس از کارمزد و لغزش؛ همچنین به درصد سرمایه.
خرید و نگهداریآنچه صرفاً خرید در اولین شمع و نگه داشتن تا آخرین شمع به دست می‌داد. استراتژی‌ای که از آن بهتر نباشد، یا فقط با افت سرمایه بسیار عمیق‌تر بهتر باشد، ارزش افزوده کمی دارد.
حداکثر افت سرمایهبزرگ‌ترین افت سرمایه از یک قله، به پول و درصد. دردی که باید تحمل می‌کردید. بسیاری از معامله‌گران به سود خالص ÷ حداکثر افت سرمایه نگاه می‌کنند.
معاملات، ٪ بردچند معامله بسته شده و چه سهمی سودآور بوده. نرخ برد پایین اشکالی ندارد اگر برنده‌ها خیلی بزرگ‌تر از بازنده‌ها باشند.
فاکتور سودسود ناخالص ÷ زیان ناخالص. بالای ۱ یعنی سودآور؛ ۱٫۳–۲ برای استراتژی‌های سالم معمول است؛ مقادیر خیلی بالا با معاملات کم معمولاً شانس است.
میانگین معامله، میانگین سود، میانگین زیانمیانگین سود هر معامله و اندازه معمول برنده‌ها و بازنده‌ها. میانگین معامله را با هزینه‌هایتان مقایسه کنید.
بزرگ‌ترین سود / زیان، معاملات زیان‌ده پیاپیموارد افراطی. یک برنده بسیار بزرگ که کل نتیجه را بر دوش دارد نشانه هشدار است.
نسبت شارپبازده به ازای هر واحد نوسان، سالانه‌شده از تغییرات سرمایه شمع به شمع. بالای ۱ خوب و بالای ۲ خیلی خوب (و نادر) است.
زمان در بازارسهم شمع‌هایی که موقعیت باز دارند. زمان کمتر برای همان سود یعنی ریسک کمتر.
خرید / فروشمعاملات، سود و نرخ برد هر طرف جداگانه: اغلب یک طرف همه کار را انجام می‌دهد.

زبانه معاملات هر معامله را با جهت، زمان و قیمت ورود و خروج، اندازه، سود به پول و درصد، تعداد شمع‌هایی که طول کشید، دلیل بسته شدن (سیگنال، حد ضرر، حد سود، برگردانده، در پایان هنوز باز) و نظر شما فهرست می‌کند. دانلود CSV آن را برای صفحه‌گسترده صادر می‌کند. منحنی سرمایه حساب را در برابر خرید و نگهداری نشان می‌دهد. Log نشان می‌دهد اسکریپت شما با log() چه نوشته است.

سفارش‌ها چگونه اجرا می‌شوند

  • بدون نگاه به آینده. سفارشی که در onBar(i) گذاشته شود در قیمت باز شدن شمع بعدی (i + 1) اجرا می‌شود. استراتژی هرگز نمی‌تواند در قیمتی معامله کند که نمی‌توانست بداند.
  • حد ضرر درون شمع. حد ضرر و حد سود با سقف و کف هر شمع بررسی می‌شوند. اگر هر دو می‌توانستند در یک شمع لمس شوند، حد ضرر حساب می‌شود (فرض محتاطانه). شکاف از روی حد ضرر در قیمت باز شدن اجرا می‌شود، مثل بازارهای واقعی.
  • یک موقعیت در هر زمان. long() هنگام موقعیت فروش، فروش را می‌بندد و خرید باز می‌کند («برگردانده»)؛ long() وقتی از قبل در خرید هستید فقط حد ضرر و حد سودهای داده‌شده را به‌روز می‌کند.
  • هزینه در هر اجرا. لغزش هر قیمت اجرا را به زیان شما جابه‌جا می‌کند؛ کارمزد در ورود و خروج گرفته می‌شود.
  • پایان. معامله‌ای که پس از آخرین شمع هنوز باز است در آخرین قیمت بسته شدن بسته و با «در پایان هنوز باز» علامت‌گذاری می‌شود.
onBar(i): long() filled at this open candle i + 1

نوشتن استراتژی خودتان

استراتژی یک برنامه کوتاه JavaScript در دو بخش است:

  1. بالای اسکریپت یک‌بار اجرا می‌شود: ورودی‌ها را با input() بخوانید، سری‌های کامل را با ta.* یا ind() محاسبه و با plot() رسم کنید.
  2. function onBar(i) { … } برای هر شمع، از قدیمی‌ترین، اجرا می‌شود. i شماره شمع است (۰ = قدیمی‌ترین). اینجا تصمیم بگیرید: 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 ۱٫۰۸۵۰، برای طلا ۲۶۵۰٫۴۰)؛ زمان به ثانیه 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 نگه داشته می‌شود). مقدار واردشده یا ۱۴ را برمی‌گرداند.
input("Average", "EMA", { options: ["SMA", "EMA"] })یک فهرست. متن انتخاب‌شده را برمی‌گرداند.

حداکثر ۳۰ ورودی. مقادیرشان همراه استراتژی ذخیره می‌شوند و هنگام انتشار در یک ایده همراهش می‌روند.

محاسبات (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()Average True Range (میانگین ساده، مانند نمودار) و True Range هر شمع.
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 در روند صعودی ۱ و در روند نزولی -۱ است.

src هر سری‌ای است: close، high، نتیجه‌ای دیگر یا آرایه خودتان. هر تابع یک آرایه جدید برمی‌گرداند؛ هیچ چیز شمع‌ها را تغییر نمی‌دهد.

سیگنال‌ها

تابعدرست وقتی…
ta.crossover(a, b, i)a روی شمع i از b به بالا عبور می‌کند (روی 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")یک خط روی نمودار قیمت می‌کشد (تا ۱۲).
log("text", value, …)یک خط با زمان شمع در زبانه Log می‌نویسد (تا ۲۰۰ خط).

همه ۷۱ اندیکاتور نمودار در کد: 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اندیکاتورورودی‌ها (پیش‌فرض)خطوط
maMoving Averageperiod: 20, method: SMA, price: close, shift: 0MA
amaAdaptive Moving Averageperiod: 9, fast: 2, slow: 30, price: closeAMA
adxAverage Directional Movement Indexperiod: 14ADX, +DI, −DI
adxwADX Wilderperiod: 14ADX, +DI, −DI
bbBollinger Bandsperiod: 20, dev: 2, price: close, shift: 0Middle, Upper, Lower
demaDouble Exponential Moving Averageperiod: 14, price: closeDEMA
envEnvelopesperiod: 14, method: SMA, dev: 0.1, price: closeUpper, Lower
framaFractal Adaptive Moving Averageperiod: 14, price: closeFrAMA
ichimokuIchimoku Kinko Hyotenkan: 9, kijun: 26, senkou: 52Tenkan, Kijun, Span A, Span B, Chikou
sarParabolic SARstep: 0.02, max: 0.2SAR
stddevStandard Deviationperiod: 20, method: SMA, price: closeStdDev
temaTriple Exponential Moving Averageperiod: 14, price: closeTEMA
vidyaVariable Index Dynamic Averagecmo: 9, ema: 12, price: closeVIDYA
atrAverage True Rangeperiod: 14ATR
bearsBears Powerperiod: 13Bears
bullsBulls Powerperiod: 13Bulls
cciCommodity Channel Indexperiod: 14, price: typicalCCI
chaikinChaikin Oscillatorfast: 3, slow: 10, method: EMACHO
demarkerDeMarkerperiod: 14DeMarker
forceForce Indexperiod: 13, method: SMAForce
macdMACDfast: 12, slow: 26, signal: 9, price: closeMACD, Signal
momentumMomentumperiod: 14, price: closeMomentum
osmaMoving Average of Oscillatorfast: 12, slow: 26, signal: 9, price: closeOsMA
rsiRelative Strength Indexperiod: 14, price: closeRSI
rviRelative Vigor Indexperiod: 10RVI, Signal
stochStochastic Oscillatork: 5, d: 3, slowing: 3, method: SMA%K, %D
trixTriple Exponential Averageperiod: 14, price: closeTRIX
wprWilliams' Percent Rangeperiod: 14%R
adAccumulation/DistributionهیچA/D
mfiMoney Flow Indexperiod: 14MFI
obvOn Balance VolumeهیچOBV
volumesVolumesهیچVolume
acAccelerator OscillatorهیچAC
alligatorAlligatorjaw: 13, jawShift: 8, teeth: 8, teethShift: 5, lips: 5, lipsShift: 3Jaw, Teeth, Lips
aoAwesome OscillatorهیچAO
fractalsFractalsهیچUp, Down
gatorGator Oscillatorjaw: 13, teeth: 8, lips: 5Upper, Lower
bwmfiMarket Facilitation IndexهیچBW MFI
zigzagZigZagdepth: 12, dev: 0.3ZigZag
vwapVWAP (volume-weighted average price)هیچVWAP
supertrendSuperTrendperiod: 10, mult: 3Up trend, Down trend
keltnerKeltner Channelsperiod: 20, atr: 10, mult: 2Middle, Upper, Lower
donchianDonchian Channelsperiod: 20Upper, Lower, Middle
hmaHull Moving Averageperiod: 20, price: closeHMA
pivotsPivot Points (classic)هیچP, R1, S1, R2, S2, R3, S3
stochrsiStochastic RSIrsi: 14, stoch: 14, k: 3, d: 3%K, %D
aroonAroonperiod: 25Aroon up, Aroon down
cmfChaikin Money Flowperiod: 20CMF
uoUltimate Oscillatorp1: 7, p2: 14, p3: 28UO
chopChoppiness Indexperiod: 14CHOP
vwmaVolume-Weighted Moving Averageperiod: 20VWMA
zlemaZero-Lag Exponential Moving Averageperiod: 21, price: closeZLEMA
almaArnaud Legoux Moving Averageperiod: 9, offset: 0.85, sigma: 6, price: closeALMA
mcginleyMcGinley Dynamicperiod: 14McGinley
lsmaLeast Squares Moving Averageperiod: 25, price: closeLSMA
chandelierChandelier Exitperiod: 22, mult: 3Long exit, Short exit
rocRate of Changeperiod: 12ROC
cmoChande Momentum Oscillatorperiod: 9CMO
tsiTrue Strength Indexlong: 25, short: 13, signal: 13TSI, Signal
ppoPercentage Price Oscillatorfast: 12, slow: 26, signal: 9Histogram, PPO, Signal
dpoDetrended Price Oscillatorperiod: 20DPO
kstKnow Sure Thingsignal: 9KST, Signal
vortexVortex Indicatorperiod: 14VI+, VI−
eomEase of Movementperiod: 14EOM
massMass Indexema: 9, sum: 25Mass Index
coppockCoppock Curvewma: 10, long: 14, short: 11Coppock
hvHistorical Volatilityperiod: 20HV %
fisherFisher Transformperiod: 9Fisher, Trigger
squeezeSqueeze Momentumbb: 20, bbk: 2, kc: 20, kck: 1.5Momentum, Squeeze on, Squeeze off
crsiConnors RSIrsi: 3, streak: 2, rank: 100CRSI
pvtPrice 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]) را اضافه کنید، شرطی را آسان‌تر کنید یا شمع‌های بیشتری بارگذاری کنید.
استراتژی بیش از ۱۵ ثانیه طول کشید (حلقه بی‌پایان؟)حلقه‌ای که هرگز تمام نمی‌شود، یا حلقه‌ای سنگین درون onBar. به‌جای حلقه زدن روی همه شمع‌ها در هر شمع، سری‌ها را یک‌بار در بالا محاسبه کنید.
Unexpected token / SyntaxError (خط N)یک اشتباه تایپی در آن خط: پرانتز، نقل‌قول یا ویرگولی جا افتاده است.

آزمونی که بتوان به آن اعتماد کرد

  • هزینه‌هایتان را اضافه کنید. استراتژی‌ای که زیاد معامله می‌کند ممکن است با هزینه صفر عالی به نظر برسد و با اسپرد واقعی پول از دست بدهد. همیشه لغزش (اسپرد) و کارمزد را وارد کنید.
  • به تعداد کافی معامله داشته باشید. زیر حدود ۳۰ معامله، اعداد بیشتر شانسی‌اند. شمع‌های بیشتری بارگذاری کنید یا تایم‌فریم کوچک‌تری به کار ببرید.
  • استراتژی را با گذشته جفت‌وجور نکنید. امتحان کردن ورودی‌های زیاد تا رسیدن به نتیجه بی‌نقص، نویز آن دوره را پیدا می‌کند نه قاعده‌ای که کار کند. روی بخشی از تاریخچه تنظیم کنید، سپس همان ورودی‌ها را روی دوره‌ای دیگر و چند نماد دیگر بررسی کنید.
  • با خرید و نگهداری مقایسه کنید و به افت سرمایه نگاه کنید، نه فقط سود.
  • مراقب موارد افراطی باشید. اگر یکی دو معامله همه سود را بسازند، استراتژی به شانس وابسته است.
  • پیش از ریسک کردن پول تمرین کنید. استراتژی را در معاملات آزمایشی یا با بازپخش شمع‌ها رو به جلو اجرا کنید و برای سیگنال‌هایش هشدار بگذارید.

بک‌تست‌ها از شمع‌های bid گذشته استفاده می‌کنند و سوآپ، هزینه تأمین مالی و اسپردهایی را که با بازار تغییر می‌کنند نادیده می‌گیرند. نتایج گذشته تضمینی برای نتایج آینده نیستند. این توصیه سرمایه‌گذاری نیست.

ذخیره، اشتراک، انتشار

  • ذخیره (با ورود به حساب) حداکثر 50 استراتژی را با ورودی‌ها و تنظیماتشان نگه می‌دارد؛ در فهرست روی هر نموداری ظاهر می‌شوند.
  • از نمودار یک ایده منتشر کنید و استراتژی پیوست کنید: خوانندگان می‌توانند نمودار شما را باز و استراتژی را با ورودی‌های شما خودشان اجرا کنند. ببینید ایده‌ها.
  • تحلیل خود را در یک باشگاه با تیم به اشتراک بگذارید.
  • دانلود CSV در زبانه معاملات، معاملات را به یک صفحه‌گسترده صادر می‌کند.
  • برای پژوهش سنگین‌تر، همین شمع‌ها را با API قیمت (GET /bars) به Python، R یا Excel بیاورید.

محدودیت‌ها

شمع در هر آزمونبازدیدکنندگان: شمع‌های روی نمودار. واردشده‌ها: تا 20,000 (طرح‌های Premium می‌توانند این را بالا ببرند؛ ببینید Premium).
اسکریپت۵۰٬۰۰۰ نویسه، ۳۰ ورودی، ۱۲ خط رسم‌شده، ۲۰۰ خط گزارش، ۵٬۰۰۰ معامله فهرست‌شده، ۱۵ ثانیه زمان اجرا.
استراتژی‌های ذخیره‌شده50 برای هر حساب.
پوزیشن‌هایک موقعیت در هر زمان برای هر آزمون (خرید، فروش یا بدون موقعیت)؛ یک نماد و یک تایم‌فریم در هر آزمون.

پرسش‌ها

آیا باید برنامه‌نویسی بلد باشم؟

نه. استراتژی‌های داخلی بدون کد کار می‌کنند؛ فقط ورودی‌هایشان را تغییر می‌دهید. اگر بیشتر خواستید، ویرایش یک نسخه یک اسکریپت کارا به شما می‌دهد تا خط به خط تغییرش دهید و مثال‌های بالا اجزای معمول را پوشش می‌دهند.

آیا مثل Pine Script است؟

ایده همان است (سری‌ها، ورودی‌ها، رسم‌ها، سفارش‌ها) و زبان JavaScript ساده است. بیشتر استراتژی‌های ساده Pine خط به خط ترجمه می‌شوند: ta.sma(close, 20) همان ta.sma(close, 20) می‌ماند، strategy.entry("L", strategy.long) به long() تبدیل می‌شود و بدنه اسکریپت با [i] پس از هر سری درون onBar(i) می‌رود.

آیا استراتژی می‌تواند برای من معامله کند؟

نه. کنسول فقط روی شمع‌های گذشته آزمایش می‌کند. برای دنبال کردن زنده یک استراتژی، از معاملات آزمایشی و هشدارها استفاده کنید یا API قیمت را از برنامه خودتان فراخوانی کنید.

چرا نتیجه من با پلتفرم دیگری فرق دارد؟

شمع‌های متفاوت (قیمت bid در برابر میانی، ساعات جلسه کارگزار)، اجرا در باز شدن بعدی به‌جای بسته شدن، هزینه‌ها و قاعده حد ضرر پیش از هدف همگی نتایج را تغییر می‌دهند. موارد همسان را مقایسه کنید.

آیا کد من خصوصی است؟

بله. در مرورگر شما اجرا می‌شود؛ فقط وقتی ذخیره را بزنید در حسابتان نگه داشته می‌شود و دیگران فقط اگر آن را در یک ایده منتشر کنید می‌بینندش.