راهنما · آزمونگر استراتژی
کنسول بکتست: آزمودن استراتژیهای معاملاتی با کد
هر نمودار یک دکمه استراتژی دارد. کنسولی باز میکند که یک استراتژی را روی شمعهای گذشته بازپخش میکند و هر معامله روی نمودار، سود، افت سرمایه، نرخ برد و منحنی سرمایه در برابر خرید و نگهداری را نشان میدهد. با یک استراتژی داخلی شروع کنید، سپس استراتژی خودتان را در چند خط JavaScript بنویسید.
امن از پایه. اسکریپت شما فقط در مرورگر خودتان، در محیطی ایزوله بدون دسترسی به اینترنت اجرا میشود: میتواند شمعهایی را که به آن داده میشود بخواند و نه چیز دیگری (نه حساب شما، نه این سایت، نه سایتهای دیگر). هیچ چیزی از نوشتههای شما روی سرورهای ما اجرا نمیشود.
شروع در یک دقیقه
- هر نموداری را باز کنید، مثلاً EURUSD، و تایمفریمی را که میخواهید بیازمایید انتخاب کنید (H1، D1…).
- در نوار ابزار نمودار استراتژی را بزنید. کنسول زیر نمودار باز میشود.
- یک استراتژی داخلی انتخاب کنید، مثلاً تقاطع میانگینهای متحرک. بلافاصله اجرا میشود: پیکانها روی نمودار هر ورود و خروج را نشان میدهند.
- یک ورودی را تغییر دهید (مثلاً میانگین کند را از ۳۰ به ۵۰): آزمون خودبهخود دوباره اجرا میشود.
- نمای کلی را برای جمعها، معاملات را برای هر معامله و منحنی سرمایه را برای حساب در طول زمان بخوانید.
- ویرایش یک نسخه را بزنید تا به اسکریپت خودتان تبدیل شود، تغییرش دهید و اجرای آزمون (یا 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()وقتی از قبل در خرید هستید فقط حد ضرر و حد سودهای دادهشده را بهروز میکند. - هزینه در هر اجرا. لغزش هر قیمت اجرا را به زیان شما جابهجا میکند؛ کارمزد در ورود و خروج گرفته میشود.
- پایان. معاملهای که پس از آخرین شمع هنوز باز است در آخرین قیمت بسته شدن بسته و با «در پایان هنوز باز» علامتگذاری میشود.
نوشتن استراتژی خودتان
استراتژی یک برنامه کوتاه JavaScript در دو بخش است:
- بالای اسکریپت یکبار اجرا میشود: ورودیها را با
input()بخوانید، سریهای کامل را باta.*یاind()محاسبه و باplot()رسم کنید. 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 | اندیکاتور | ورودیها (پیشفرض) | خطوط |
|---|---|---|---|
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]) را اضافه کنید، شرطی را آسانتر کنید یا شمعهای بیشتری بارگذاری کنید. |
| استراتژی بیش از ۱۵ ثانیه طول کشید (حلقه بیپایان؟) | حلقهای که هرگز تمام نمیشود، یا حلقهای سنگین درون 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 در برابر میانی، ساعات جلسه کارگزار)، اجرا در باز شدن بعدی بهجای بسته شدن، هزینهها و قاعده حد ضرر پیش از هدف همگی نتایج را تغییر میدهند. موارد همسان را مقایسه کنید.
آیا کد من خصوصی است؟
بله. در مرورگر شما اجرا میشود؛ فقط وقتی ذخیره را بزنید در حسابتان نگه داشته میشود و دیگران فقط اگر آن را در یک ایده منتشر کنید میبینندش.