فراخوانیهایی که حالت نگه میدارند
بعضی فراخوانیها کندلهای قبلی را به یاد دارند. هر جایی که چنین فراخوانیای نوشته شده حالت خودش را دارد و حالت فقط وقتی جلو میرود که اسکریپت به آن جا برسد.
بیشتر فراخوانیها فراموش میکنند: math.abs(x) برای همان x همان جواب را میدهد، هر چه قبلاً اتفاق افتاده باشد. چند فراخوانی به یاد میآورند. tech.sma(close, 20) بیست بستهشدن آخر را لازم دارد، tech.crosses_above(a, b) کندل قبلی را لازم دارد، و math.random() باید بداند عدد بعدی کدام است. این صفحه به آنها فراخوانیهای حالتدار میگوید (فراخوانیهایی که حالت نگه میدارند). مرجع هر کدام را در خط Effects علامت میزند: «keeps a state for each place it is written (a call site) and advances it when execution reaches the call».
کل قاعده در یک جمله جا میشود. فراخوانیِ حالتدار برای هر جایی که نوشته شده یک حالت دارد، و حالت دقیقاً وقتی جلو میرود که اسکریپت روی یک کندل به آن جا برسد. اگر اسکریپت روی یک کندل به آن جا نرسد، حالت همانطور که بود میماند.
چه چیزی حالت نگه میدارد#
| حالت نگه میدارد | نگه نمیدارد |
|---|---|
همهٔ تابعهای tech بهجز tech.tr: میانگینها، باندها، اسیلاتورها، بالاترین و پایینترین در یک پنجره، تقاطعها، پیوتها (60 تا از 61 تابع) | قیمتها و مقدارهای کندل: close، bar.index، bar.time |
rises(condition) و falls(condition) | close[1] و تاریخچهٔ هر نام ساده: موتور خودش سری را نگه میدارد، پس خواندنش چیزی را جلو نمیبرد |
tf.changed(tf)، math.random(...) | حساب، math.* بهجز math.random، text.*، color.* |
data.series(...) و data.intrabars(...) که تایمفریم دیگری را میخوانند | متدهای آرایه، نگاشت (map) یا ماتریس، و شیءهای نمودار |
تاریخچهٔ هر چیزی که نام ساده نیست: (high - low)[1] | عضوهای یک سشن اعلامشده و prior(...) و current(...) (پایینتر را ببینید) |
تابعی از خودتان که یک state دارد، یا یکی از ستون چپ را صدا میزند | تابعی از خودتان که هیچکدام را ندارد |
یک نام state از خودتان فرق دارد: مقداری است که خودتان، با دستورهای ساده، میخوانید و مینویسید. صفحهٔ let، mut و state let، mut و state را نشان میدهد.
برای هر جا یک حالت#
حالت مال جایی از متن است که فراخوانی آنجا نوشته شده، نه مال تابعی که صدا زده میشود. دو خط که هر دو tech.ema را صدا میزنند، دو میانگین متحرک با دو حافظهاند. تابعهای خودتان هم همینطورند: حافظه در خود تابع نیست، در جایی است که آن را صدا میزند.
// Every place where a call that keeps a state is written has a state of its own. smooth() is one function called twice, with two different series,
// and each call keeps its own running average: the difference to the same average written out directly is zero on every bar.
indicator "One state for each call"
edition = 1
display = pane
fn smooth(source: float, length: int): float = tech.ema(source, length)
let from_close = smooth(close, 10)
let from_open = smooth(open, 10)
plot(from_close - tech.ema(close, 10), name = "Close, function minus direct", color = color.green)
plot(from_open - tech.ema(open, 10), name = "Open, function minus direct", color = color.red)
plot(from_close - from_open, name = "Close average minus open average", color = color.gray)دو خط اول صفرند، چون smooth(close, 10) و tech.ema(close, 10) هر کدام روی هر کندل، با همان ورودی، حالت خودشان را جلو میبرند. اگر دو فراخوانی smooth یک حافظه را با هم شریک بودند، میانگین بازشدنها و میانگین بستهشدنها قاطی میشد.
چون حالت به یک جا بسته است، آن جا باید جایی باشد که روی هر کندل تعداد ثابتی بار اجرا میشود. به همین دلیل فراخوانیِ حالتدار نمیتواند در حلقه بنشیند. همهٔ دورهای حلقه همان یک جا هستند و یک حافظه را شریک میشوند، پس موتور آن را با E3301 رد میکند:
// Mistake: a request for another timeframe keeps a state per call site, so it may not stand in a loop (E3301).
indicator "Request in a loop"
edition = 1
display = pane
mut total = 0.0
for i in 0 to 2
total += data.series("60", close, symbol = symbol.id)
plot(total, name = "Total")همین برای تابعی از خودتان که حالت نگه میدارد و داخل حلقه صدا زده میشود هم صادق است. فراخوانی را یک بار پیش از حلقه انجام دهید و از مقدارش داخل حلقه استفاده کنید، همانطور که برنامهٔ اصلاحشده میکند:
// The fix for E3301: make the request once, outside the loop, and use its value inside.
indicator "Request outside the loop"
edition = 1
display = pane
let hourly = data.series("60", close, symbol = symbol.id)
mut total = 0.0
for i in 0 to 2
total += hourly
plot(total, name = "Total")فقط وقتی به آن برسد جلو میرود#
جایی که اسکریپت روی هر کندل به آن میرسد، هر کندل را به حالت میدهد، و فراخوانی در بالاترین سطح اسکریپت همین کار را میکند. جایی داخل بلوکی که روی بعضی کندلها اجرا میشود، فقط همان کندلها را به حالت میدهد. حالت روی کندلهایی که بلوک رد میشود جلو نمیرود و به صفر هم برنمیگردد: همان جا که بود منتظر میماند.
بررسیکننده وقتی این را ببیند به شما میگوید. فراخوانیِ حالتدار داخل یک بلوک if، else یا match مجاز است و یک اخطار است، W3302:
// Warning W3302: a call that keeps a state stands in an `if` block. It is allowed, but its state moves only on the bars where the block runs.
// Here the average of the up bars is smoothed over the up bars only, which is rarely what the line says.
indicator "Stateful call in a block"
edition = 1
display = price
mut smoothed: float = na
if close > open
smoothed = tech.ema(close, 5)
plot(smoothed, name = "Smoothed")گاهی دقیقاً همین را میخواهید: میانگینِ فقط کندلهای صعودی. اغلب نه، و اصلاح معمول این است که فراخوانی را روی هر کندل بالای بلوک اجرا کنید و بگذارید بلوک انتخاب کند چه چیزی نگه داشته شود:
// The fix for W3302: run the call on every bar above the block, and let the block only choose what to keep.
indicator "Stateful call above the block"
edition = 1
display = price
let smoothed_all = tech.ema(close, 5)
mut smoothed: float = na
if close > open
smoothed = smoothed_all
plot(smoothed, name = "Smoothed")فراخوانی کجا میتواند بنشیند#
| فراخوانی کجا نوشته شده | زبان چه میکند |
|---|---|
در بالاترین سطح اسکریپت، یا سمت راست یک let | روی هر کندل به آن میرسد. پیامی نیست. |
در بدنهٔ یک بلوک if یا else، یا در یک بازوی match | مجاز است. اخطار W3302 میگوید حالت فقط جایی جلو میرود که آن کد اجرا شود. |
مستقیم در سمت راست and، or یا ??، یا در یک بازوی if ... then ... else | رد میشود: E3302. ممکن است آن سمت اجرا نشود، پس حالت فقط روی بعضی کندلها جلو میرفت. عملگرهای تنبل را ببینید. |
در یک حلقهٔ for یا while، مستقیم یا از راه تابعی از خودتان | رد میشود: E3301. دورها یک حالت را شریک میشدند. |
| در تابعی از خودتان | بستگی به جایی دارد که تابع را صدا میزند: هر فراخوانی تابع، جایی با حالتهای خودش است. |
بررسیکننده W3302 و E3302 را گزارش میکند. E3301 را موتور وقتی اسکریپت را برای اجرا کامپایل میکند میدهد، نه بررسیکننده در گذر خودش.
خواندنهایی که شبیه فراخوانیاند و حالتی نگه نمیدارند#
بعضی مقدارها را موتور برای هر کندل یک بار حساب میکند، چه اسکریپت به خطی که آنها را میخواند برسد چه نرسد. حالتی مخصوص آن خط ندارند، پس میتوانند هر جایی بنشینند، از جمله سمت راست and:
- عضوهای یک سشن اعلامشده (
london.active،london.highو بقیه): روز معاملاتی و سشنها؛ prior(day).high،current(week).openو بقیهٔ فیلدهای دوره؛clock.reached(...)که به زمان کندل و کندل قبل از آن بستگی دارد، وbar.time_in(...)که به زمان کندل بستگی دارد.
// A trading session is two times and a zone. `state session` keeps a value that restarts at the start of each session,
// so this draws the high of the London session so far, and the high of the New York session so far.
indicator "Session highs"
edition = 1
display = price
session london = 08:00 .. 16:30 @ "Europe/London"
session newyork = 09:30 .. 16:00 @ "America/New_York"
state session london london_high = high
state session newyork newyork_high = high
if london.active
london_high = math.max(london_high, high)
if newyork.active
newyork_high = math.max(newyork_high, high)
plot(london_high, name = "London high so far", color = color.green)
plot(newyork_high, name = "New York high so far", color = color.orange)هزینهاش چقدر است#
فراخوانیِ حالتدار تا وقتی اسکریپت اجرا میشود حافظه نگه میدارد، اما فقط به اندازهٔ چیزی که به عقب میخواند. حافظهاش تابع تعداد کندلهایی است که میخواند (حداکثر 8,192) و هرگز تابع طول نمودار نیست. این حافظه در دفتر حافظهٔ سقفها و بودجهٔ اجرا شمرده میشود، و هر فراخوانی از بودجهٔ 500,000 گام برای هر کندل خرج میکند.
ببینید هم: عملگرهای تنبل، E3302 و W3302، انتخابها و حلقهها، تابعها، نوعها و enumها، مثالها: فراخوانیهایی که حالت نگه میدارند.