قطعیت و سقفها
چه چیزی باعث میشود اسکریپت روی کندلهای یکسان خروجی یکسان بدهد: عددهای تصادفی و seed، تاریخچه، بودجهها، شبکهٔ قیمت، و چیزهایی که تابع کندلها نیستند.
اسکریپت تابعی از متنش، ورودیهایش و کندلهایی است که رویشان اجرا میشود. آن را دو بار روی همان کندلها و با همان ورودیها اجرا کنید، همان خطها، علامتها و ترسیمها را رسم میکند و یک استراتژی همان معاملهها را انجام میدهد. این صفحه قطعههایی را فهرست میکند که این را درست نگه میدارند، و چند مقداری را که تابع کندلها نیستند.
مستندات این وعده را برای مثالهای خودش بررسی میکند. هر برنامه در مثالها، با هر بار ساخته شدن مستندات، دو بار روی همان دادهٔ بازار مصنوعیِ seedدار کامپایل و اجرا میشود، و اثر انگشت کوتاهی از آنچه رسم کرده با اثر انگشت ذخیرهشده مقایسه میشود. اگر اثر انگشت یک مثال جابهجا شود، ساخت شکست میخورد تا کسی دلیلش را بررسی کند.
چه چیزی تابع کندلها نیست#
clock.nowزمان فعلی است، آنطور که پلتفرم میداند. اسکریپتی که آن را میخواند در زمان دیگری جواب دیگری میدهد. فقط جایی از آن استفاده کنید که هدف همین است.- خود کندلها. نمودار همان تاریخچهای را بارگذاری میکند که بارگذاری میکند؛ اسکریپتی که بیشتر از آنچه نمودار دارد به عقب نگاه کند
naمیگیرد، که واقعیتی دربارهٔ داده است، نه شکست اسکریپت. - ورودیها: مقدار دیگری برای یک ورودی، اجرای دیگری است.
- کندل در حال شکلگیری. تا وقتی جدیدترین کندل هنوز در حال شکلگیری است، اسکریپت با هر قیمت تازه دوباره روی آن اجرا میشود؛ کندلهای بستهشدهٔ قبل از آن تغییر نمیکنند. صفحهٔ نتیجههایی که زیادی خوب به نظر میرسند میگوید اسکریپت آنجا چه چیزی را میتواند بخواند.
بقیهٔ چیزها از همینها و از متن اسکریپت حساب میشوند.
عددهای تصادفی و seed آنها#
math.random(min, max) به معنای سکه تصادفی نیست: فهرست ثابتی از عددهاست، و اسکریپت در جایی که میخواهد عدد بعدی را میخواند. این فهرست به چهار چیز بستگی دارد و به هیچ چیز دیگر:
- هویت اسکریپت: شناسهای که پلتفرم اسکریپت را با آن ذخیره میکند؛ برای اسکریپتی که هنوز ذخیره نشده، عنوان سرآیندش؛ و اگر آن هم نباشد، یک واژهٔ ثابت؛
- مقدار ورودیها، به ترتیب جایشان در اسکریپت؛
- seedی که میزبان به اجرا میدهد، اگر بدهد؛
- مجموعهٔ دادهای که اجرا روی آن است.
خود فراخوانی هم یک شماره دارد: جایش در میان فراخوانیهای math.random اسکریپت. هر عدد بر اساس کندل و جایی که آن را میخواهد کشیده میشود، پس عددهای یک کندل بستهشده وقتی کندلی اضافه شود یا نمودار تاریخچهٔ بیشتری بارگذاری کند تغییر نمیکنند، و یک فراخوانی هرگز عددهای فراخوانی دیگر را نمیگیرد.
معنای عملی این، که روی اسکریپت کوچکی که math.random(0, 1) را رسم میکند اندازهگیری شده:
| چه چیزی را تغییر میدهید | عددها |
|---|---|
| قالببندی، کامنتها، نام یک متغیر، ترتیب خطهای بیربط | همان |
| عنوان یک اسکریپت ذخیرهشده، یا ویرایش آن (شناسهٔ ذخیرهشدهاش میماند) | همان |
| عنوان اسکریپتی که شناسهٔ ذخیرهشده ندارد (هویت همان عنوان است) | عددهای دیگر |
| یک ورودی، حتی ورودیای که فراخوانی نمیخواند | عددهای دیگر |
| مجموعهٔ داده یا seed میزبان | عددهای دیگر |
اضافه یا حذف کردن یک فراخوانی math.random بالای فراخوانی دیگر | فراخوانیهای بعد از آن جای دیگری میگیرند |
اگر یک عدد باید از هر کدام از اینها جان سالم به در ببرد، به فراخوانی seed خودش را بدهید. فراخوانیای با seed = n از مولدی استفاده میکند که فقط به n و به جایی که عدد را میخواهد بستگی دارد، پس تغییر عنوان، ورودیها یا مجموعهٔ داده به آن دست نمیزند:
// math.random is deterministic: the same seed and the same call site give the same numbers on every run and every machine.
// The seed is part of the call, so changing it changes the numbers and nothing else does.
indicator "Seeded noise"
edition = 1
display = pane
input seed: int = 7, name = "Seed", min = 0
let noise = math.random(-1, 1, seed = seed)
plot(noise, name = "Noise", style = histogram, color = color.gray)
plot(tech.sma(noise, 20), name = "Average of the noise", color = color.orange)تاریخچهای که موتور نگه میدارد#
یک سری فقط کندلهایی را نگه میدارد که متن میگوید میخواند و نه بیشتر، پس اسکریپت روی نموداری با هزار کندل و با صد هزار کندل همان حافظه را مصرف میکند. یک خواندن حداکثر 8,192 کندل به عقب میرود. خواندنی که بیشتر برود خطاست، هرگز یک na بیصدا نیست:
- عددی که در اسکریپت نوشته شده پیش از اجرا پیدا میشود (
E-LIMIT-HISTORY-STATIC)؛ - عددی که هنگام اجرا حساب میشود، اسکریپت را روی همان کندل متوقف میکند (
E-LIMIT-HISTORY)؛ - خواندنی که هیچ چیز در متن برایش سقف نمیگذارد، مثل ورودیای بدون
max، اجرا میشود و تا 8,192 کندل نگه میدارد؛ ویرایشگر این را باW-ENGINE-HISTORY-OPENمیگوید.
// Reading earlier bars: `close[n]` is the close n bars ago, and the engine keeps a bounded history for every series.
// This compares the close with the close 50 bars ago, 100 bars ago, and the highest close of the last 200.
indicator "Look back"
edition = 1
display = pane
let change_50 = close - close[50]
let change_100 = close - close[100]
let from_peak = close - tech.highest(close, 200)
plot(change_50, name = "Change over 50 bars", color = color.blue)
plot(change_100, name = "Change over 100 bars", color = color.purple)
plot(from_peak, name = "From the 200-bar high", color = color.red)// Mistake: a window longer than the history the engine keeps cannot be filled, so the run is refused before it starts (E-LIMIT-HISTORY-STATIC).
indicator "Window beyond the history"
edition = 1
display = price
plot(tech.sma(close, 100000), name = "Too long")بودجهها#
سه بودجه یک اجرا را محدود میکنند و اسکریپت نمیتواند آنها را بالا ببرد: گامهایی که یک کندل میتواند خرج کند (500,000)، گامهایی که کل یک اجرا میتواند خرج کند (600,000,000)، و بایتهایی که اسکریپت نگه میدارد و میسازد (دفتر حافظه: 134,217,728 نگهداشته در یک لحظه، 2,147,483,648 ساختهشده در طول یک اجرا). اینها کاری را میشمارند که موتور انجام میدهد، نه ثانیهها را: یک دور حلقه، پیمودن یک پنجره، یک ترسیم، یک نویسهٔ متنی که ساخته میشود. شمارش روی هر دستگاهی یکسان است، پس اسکریپتی که از یک بودجه بیرون بزند همهجا روی همان کندل و همان خط متوقف میشود، و پیام میگوید کدام. صفحهٔ سقفها و بودجهٔ اجرا عددها را دارد، و خطای حلقهای که تمام نمیشود هم در همان صفحه است.
سقف زمان ساعت تنها استثناست: اجرا روی نمودار پس از 5,000 میلیثانیه زمان واقعی هم متوقف میشود، و این به دستگاه بستگی دارد. اسکریپتی که روی یک دستگاه کند به سقف گام نزدیک است، ممکن است اول به سقف زمان برسد (E-LIMIT-TIME).
شبکهٔ قیمت#
قیمت روی نمودار روی شبکهای است که گامش تیک نماد است (symbol.tick). قیمتهایی که از داده میآیند روی شبکهاند. قیمتی که خودتان حساب میکنید روی شبکه نیست: close - 2 * tech.atr(14) تقریباً هیچوقت مضربی از تیک نیست. تستر سفارش را با همان قیمتی که به آن داده میشود پر میکند. آن را به شبکه گرد نمیکند، پس حد ضرری که از دامنه حساب شده با همان عدد دقیق پر میشود، و معامله ممکن است با قیمتی بسته شود که بازار هرگز نمیتوانست داشته باشد.
symbol.snap(price) قیمت را به نزدیکترین تیک گرد میکند؛ symbol.snap_down و symbol.snap_up به تیک پایینتر و بالاتر گرد میکنند؛ symbol.ticks(distance) یک فاصله را به تعداد تیک تبدیل میکند و symbol.price_of(ticks) برعکس. برای حد ضرر یا هدف، قیمت را روی شبکه بیاورید:
// Prices live on a grid of ticks. symbol.snap rounds a price to the nearest tick, so a stop or a target is a price the market can have.
strategy "Stops on the price grid"
edition = 1
display = price
setup:
capital = 10000
size = sizing.lots(0.1)
let range = tech.atr(14)
let fast = tech.ema(close, 8)
let slow = tech.ema(close, 21)
if tech.crosses_above(fast, slow)
orders.enter("Long", side.long)
orders.exit(
"Long exit",
"Long",
stop = symbol.snap(close - 2 * range),
limit = symbol.snap(close + 3 * range),
)
plot(fast, name = "Fast", color = color.green)
plot(slow, name = "Slow", color = color.red)عددها#
تابعهایی که دو دستگاه مجازند کمی متفاوت حساب کنند (math.log، math.exp، math.pow و تابعهای مثلثاتی) در خود موتور نوشته شدهاند و تابعهای خود دستگاه را صدا نمیزنند. جمع، تفریق، ضرب، تقسیم، جذر و گرد کردن را قالب عدد تعیین میکند. این جمله و حد اندازهگیریشدهاش در عددها آمده است.
مثالهای این صفحه#
مثالهای قطعیت برنامههای بالا و چند برنامهٔ دیگر را روی دادهٔ مصنوعی مستندات اجرا میکنند: seed، تاریخچه، شبکهٔ قیمت و بودجهٔ گامها.
ببینید هم: سقفها و بودجهٔ اجرا، فراخوانیهایی که حالت نگه میدارند، پیامهای موتور.