این نسخه ارتقایافته از زبان برنامهنویسی مخصوص تریدرها کلی بهینهسازی و ویژگیهایی که مدتها درخواست شده بودن رو اضافه کرده. حالا کار با این زبان راحتتر و کاربردیتر شده و قراره اون رو به سطح جدیدی برسونه.
تو این مطلب، به چندتا از مهمترین تغییرات نسخه 6 اشاره میکنیم.
مثل تغییرات نسخههای قبلی، بهروزرسانیهای Pine v6 هیچ تأثیری روی اسکریپتهای شخصی یا منتشرشده که با نسخههای قبلی نوشته شدن، نداره. اما از این به بعد، همه ویژگیهای جدید فقط تو نسخه آخر اضافه میشن. برای همین پیشنهاد میکنیم اگه میخواید به امکانات جدید دسترسی داشته باشید، اسکریپتهاتون رو به نسخه 6 تبدیل کنید.
راحتترین راه، استفاده از ابزار تبدیل به نسخه 6 هست. کافیه تو Pine Editor وارد منوی "Manage script" بشید و گزینه "Convert code to v6" رو انتخاب کنید.
حواستون باشه که همه اسکریپتها بهصورت خودکار به نسخه 6 تبدیل نمیشن و بعضی وقتها باید دستی تغییراتی بدید تا اسکریپت به درستی کار کنه.
درخواستهای داینامیک
"درخواستهای داینامیک" تو Pine Script یعنی میتونید از کلی نماد معاملاتی، شاخصهای مالی و اقتصادی که تو TradingView هست، داده بگیرید. با استفاده از توابع request.*()، اسکریپتها میتونن اطلاعات هر نماد موجود رو تو تایمفریمهای مختلف، جدا از نماد یا تایمفریمی که تو چارت تنظیم شده، دریافت کنن.
این توابع خیلی کاربردی و قدرتمند هستن و کلی استفاده میتونن داشته باشن. اما قبلاً یه محدودیت بزرگ داشتن: باید برای مشخص کردن نماد و تایمفریم از مقدارهای "رشتهای ساده" استفاده میکردید. این یعنی باید از همون اول (یعنی اولین کندل) مشخص میکردید که چه چیزی میخواید و بعدش دیگه نمیتونستید تغییرش بدید.
علاوه بر این، همه درخواستهای request.() فقط تو محدوده کلی اسکریپت (global scope) اجرا میشدن. به عبارت دیگه، هر درخواست request.() فقط میتونست اطلاعات یک نماد و یک تایمفریم رو بگیره و اجازه نداشتید این درخواستها رو توی حلقهها، شرطها یا توابع کتابخونهای استفاده کنید.
تو Pine Script® v6، این محدودیتها رو برداشتن. حالا اسکریپتها میتونن از مقدارهای "رشتهای سری" استفاده کنن تا زمینه هر درخواست request.() رو مشخص کنن. همچنین، حالا میتونید درخواستهای request.() رو توی محدودههای محلی (local scopes) هم استفاده کنید.
با این تغییرات، میتونید نمادها رو توی هر کندل تاریخی محاسبه یا تغییر بدید و دادههاشون رو به صورت داینامیک درخواست کنید. همچنین میتونید مجموعهای از نمادها بسازید و دادههاشون رو توی حلقهها دریافت کنید و کلی کار دیگه با توابع request.*() انجام بدید که قبلاً ممکن نبود.
برای یه نمونه از کارهایی که میتونید با درخواستهای داینامیک انجام بدید، به عملکرد داخلی اندیکاتور Performance موجود در TradingView نگاه کنید. این اندیکاتور لیستهای نماد و تایمفریم که با کاما جدا شدن رو به آرایهها تقسیم میکنه و بعد به صورت داینامیک داخل حلقهها از request.security() استفاده میکنه تا مقادیر هر مجموعه داده رو بگیره.
قبلاً برای اینکه چنین اسکریپتی بنویسید، مجبور بودید چندین بار از input.symbol() و input.timeframe() استفاده کنید و هر ترکیب نماد و تایمفریم هم نیاز به یک درخواست جداگانه request.security() توی کد داشت.
برای اطلاعات بیشتر، به این اسکریپتهایی که توسط حساب TradingView منتشر شده و از درخواستهای داینامیک برای دریافت داده از زمینههای دیگه استفاده میکنن، نگاهی بندازید.
Forex Heatmap: این اندیکاتور شناسههای تیکر برای ترکیبهای جفت ارزها بر اساس لیست کدهای ارزی که کاربر مشخص کرده، ایجاد میکنه. بعد به صورت داینامیک دادهها رو برای هر ترکیب جفت ارز توی حلقهها درخواست میکنه و از این دادهها برای پر کردن یه جدول رنگی استفاده میکنه.
Ticker Tape: این اندیکاتور یه آرایه از شناسههای تیکر بر اساس لیست نمادهایی که کاربر مشخص کرده، ایجاد میکنه. بعد به صورت داینامیک توی یک حلقه، اطلاعات قیمت و تغییر روزانه رو برای هر شناسه تیکر از آرایه درخواست میکنه و از این دادهها برای بهروزرسانی نمایش چرخشی "تیپ" استفاده میکنه.
LibraryCOT: قبلاً این کتابخونه فقط ابزارهایی برای ایجاد شناسههای تیکر جهت درخواست دادههای CFTC Commitment of Traders (COT) ارائه میکرد، چون کتابخونهها نمیتوانستند توابعی که شامل درخواستهای request.*() بودند رو صادر کنند. اما حالا با درخواستهای داینامیک، این محدودیت دیگه وجود نداره. حالا این کتابخونه تابع requestCommitmentOfTraders() رو صادر میکنه که بهطور داخلی از request.security() استفاده میکنه تا دادههای COT رو بهطور مستقیم دریافت کنه و این کار راحتی و انعطافپذیری بیشتری به برنامهنویسان میده.
علاوه بر این، قبلاً تمام کدهای گزارش CFTC داخل جملات switch نگهداری میشدند تا مقادیر "ساده" برگردونده بشن، که این موضوع به شدت تعداد تیکرهای قابل درخواست رو محدود میکرد. اما حالا با درخواستهای داینامیک، دیگه نیازی به شناسههای تیکر "ساده" نیست و این کتابخونه میتونه کدهای گزارش رو تو یک نقشه (map) ذخیره کنه و از نمادهای بیشتری پشتیبانی کنه.
بهینهسازی بولی
یکی از بهبودهای قابل توجه در Pine Script® v6 شاید در نگاه اول به چشم نیاد، اما احتمالاً تفاوتی در کارایی کدها حس خواهید کرد. نحوه داخلی مدیریت مقادیر "bool" تو Pine تغییر کرده و ارزیابی کوتاهمدت (یا "تنبل") معرفی شده. این یعنی وقتی که برای تعیین نتیجه به بررسی ادامه عبارات نیاز نیست، عملیات و و یا (and و or) دیگه ادامه نمیدن.
این تغییرات باعث بهبود عملکرد اکثر اسکریپتها تو TradingView میشه. تفاوت در کارایی بهخصوص تو اسکریپتهای نسبتاً بزرگ که به شدت به شرطها و مقادیر "bool" وابسته هستن، به وضوح حس میشه. ما تو تستهایی که روی بعضی از محبوبترین اسکریپتهای متنباز جامعه انجام دادیم، این موضوع رو تأیید کردیم.
به عنوان یه مزیت، ارزیابی "bool" تنبل معمولاً باعث میشه کدها تمیزتر و مختصرتر بشن. برای مثال، اگه شرطی داشته باشید که به آیتمهایی از یک آرایه وابسته است، باید اندازه آرایه رو چک کنید تا مطمئن بشید که ایندکس آیتم وجود داره، چون اگه ایندکس خارج از محدوده باشه، اسکریپت متوقف میشه و خطا میده. ولی با ارزیابی تنبل در Pine v6، میتونید یک عبارت شرطی واحد بسازید که قبل از تلاش برای دسترسی به یک عنصر، آرایه رو چک کنه، چیزی که در v5 ممکن نبود.
//@version=6
indicator("Lazy evaluation demo")
// آرایه "bool" بدون اندازه ثابت.
array<bool> myArray = array.new<bool>()
// وقتی که `close` بالای `open` هست، یک مقدار جدید به `myArray` اضافه کن.
if close > open
myArray.push(true)
// در v5، خط 13 باعث بروز خطا میشد چون `myArray.first()` همیشه اجرا میشد، حتی اگر عبارت اول `false` بود.
// ولی در v6، `myArray.first()` فقط زمانی اجرا میشه که عبارت اول به `true` ارزیابی بشه.
if myArray.size() != 0 and myArray.first()
label.new(bar_index, high, "Test")
اندازه و فرمت متن
حالا همه نوع اشکالی که متن رو نشون میدن (مثل باکسها، برچسبها و جدولها) میتونن از اندازههای متنی که به واحد "نقطه" تایپوگرافیک مشخص شده، استفاده کنن — همون نقاطی که در ویرایشگرهای متن استاندارد استفاده میشه. قبلاً شما مجبور بودید بین اندازههای دلخواه مثل size.large (۲۴) و size.huge (۳۶) انتخاب کنید. اما حالا با مشخصات جدید اندازه نقطهای، میتونید اندازه متن رو دقیقاً به شکلی که میخواید تنظیم کنید. حتی میتونید اندازههای بسیار بزرگی بسازید که در نسخههای قبلی Pine ممکن نبود.
علاوه بر این، یک پارامتر جدید به نام text_formatting
برای این نوع اشکال معرفی شده، که میتونید با استفاده از اون متن رو کج (italics)، بولد (bold) یا هر دو حالت کنید.
//@version=6
indicator("Text size showcase", overlay = true)
// یک جدول جدید با پسزمینه زرد و قاب مشکی درست میکنیم.
var t = table.new(position.bottom_center, 1, 2, bgcolor = color.yellow, frame_color = color.black, frame_width = 1)
// اگر آخرین تاریخ تایید شده است، دو سلول به جدول اضافه میکنیم.
if barstate.islastconfirmedhistory
t.cell(0, 0, "text_size = size.huge", text_size = size.huge) // اندازه متن خیلی بزرگ
t.cell(0, 1, "text_size = 60, bold & italicized", text_size = 60, text_formatting = text.format_bold + text.format_italic) // اندازه متن ۶۰ و هم بولد و هم کج
کاهش سفارش استراتژی
کاربرهای فعال اسکریپت استراتژی ممکنه بدونن که در Pine v5، یک استراتژی میتونه تا ۹۰۰۰ معامله رو شبیهسازی کنه و بعد از اون محاسباتش متوقف میشه و خطا میگیره، مگر این که از حالت Deep Backtesting استفاده کنید. این محدودیت به خصوص برای استراتژیهایی که معاملات مکرر شبیهسازی میکنن و زنگ هشدار ایجاد میکنن، خیلی دردسرسازه.
در Pine Script® v6، استراتژیها دیگه بعد از رسیدن به محدودیت ۹۰۰۰ معامله متوقف نمیشن یا خطا نمیگیرن. بلکه، استراتژی قدیمیترین سفارشها رو کاهش میده تا برای سفارشهای جدید جا باز کنه. سفارشهای کاهشیافته در تستر استراتژی نمایش داده نمیشن، اما استراتژی به محاسباتش ادامه میده. برای بررسی ایندکس معامله قدیمیترین سفارش غیرکاهشیافته، میتونید از متغیر جدید strategy.closedtrades.first_index
استفاده کنید. این ایندکس رو میتونید به عنوان آرگومان trade_num
در فراخوانیهای تابع strategy.closedtrades.*()
استفاده کنید.
ایندکسهای منفی آرایه
در Pine v6، توابع array.get()
، array.set()
، array.insert()
و array.remove()
حالا میتونن ایندکسهای منفی رو قبول کنن تا به عناصر آرایهها از انتها دسترسی پیدا کنن. این ویژگی یک راه راحتتر و خلاصهتر برای دسترسی به عناصر آرایه به صورت معکوس ارائه میده. برای مثال، فراخوانی array.get(myArray, -2)
دومین عنصر از آخر آرایه myArray
رو برمیگردونه که معادل array.get(myArray, array.size(myArray) – 2)
هست.