millionchart
English

درس 6: تابع‌های خودتان

یک محاسبه را با fn زیر یک نام بگذارید: پارامترها و مقدار پیش‌فرض، نگهبان با return، چند نتیجه با هم، و تابع‌هایی که به یاد می‌سپارند.

درس 6 از 18. وقتی یک محاسبه را دو بار می‌نویسید، یا محاسبه‌ای ارزش یک نام دارد، آن را در یک تابع بگذارید. تابع را با fn و بالای کدی که از آن استفاده می‌کند می‌نویسید، و مثل یک تابع داخلی صدایش می‌زنید.

تابعی در یک خط#

Cuprel
indicator "Percent change"
    edition = 1
    display = pane

fn percent_change(now: float, before: float): float = (now - before) / before * 100
input back: int = 10, name = "Bars back", min = 1, max = 200
plot(percent_change(close, close[back]), name = "Change %", style = histogram)
level(0, name = "Zero", color = color.gray)
  • fn percent_change(now: float, before: float): float نام تابع و دو پارامتر آن را می‌گوید. داخل تابع، now و before همان مقدارهایی‌اند که فراخوانی داده است.
  • بعد از = نتیجه می‌آید. تابعی که در یک خط نوشته شود، یک عبارت است.
  • نوع‌ها (float) به خواننده کمک می‌کنند. در edition 1 موتور آن‌ها را بررسی نمی‌کند.

تابعی با یک بلوک#

تابع بلندتر یک بلوک تورفته دارد. return نتیجه را می‌دهد و تابع را همان لحظه تمام می‌کند؛ به همین دلیل نگهبان خوبی است:

Cuprel
indicator "Small bodies"
    edition = 1
    display = price

fn body_share(o: float, c: float, h: float, l: float): float
    let span = h - l
    if span == 0
        return na
    return math.abs(c - o) / span
input limit: float = 0.1, name = "Largest share", min = 0.01, max = 1.0, step = 0.01
let share = body_share(open, close, high, low)
mark.char(share < limit, name = "Small body", char = "s", place = above, color = color.gray)
plot(close, name = "Close", color = color.gray)

body_share بدنهٔ کندل را به صورت سهمی از کل دامنهٔ آن می‌دهد: 0 برای کندلی که در همان قیمتی بسته شد که باز شده بود، و 1 برای کندلی که سایه ندارد. کندلی که سقف و کفش برابر است، تقسیم بر صفر می‌شد؛ پس نگهبان اول na برمی‌گرداند.

مقدار پیش‌فرض و نام آرگومان#

پارامتر می‌تواند مقدار پیش‌فرض داشته باشد، و فراخوانی می‌تواند آرگومانی را که می‌دهد با نام بیاورد:

Cuprel
indicator "Envelope"
    edition = 1
    display = price

fn envelope(source: float, length: int, percent: float = 1.0): (float, float, float)
    let middle = tech.sma(source, length)
    let offset = middle * percent / 100
    return (middle + offset, middle, middle - offset)
let (upper, middle, lower) = envelope(close, 20, percent = 0.5)
plot(upper, name = "Upper", color = color.red)
plot(middle, name = "Middle", color = color.gray)
plot(lower, name = "Lower", color = color.green)

این تابع سه مقدار را با هم برمی‌گرداند، یک تاپل؛ (float, float, float) همین را می‌گوید. فراخوان آن‌ها را با let (upper, middle, lower) = ... از هم جدا می‌کند؛ تعداد نام‌ها باید با تعداد مقدارها برابر باشد. برای مقداری که لازم ندارید _ بنویسید: let (upper, _, lower) = envelope(close, 20).

تابعی که به یاد می‌سپارد#

تابع می‌تواند state یا تابعی مثل tech.ema به کار ببرد. آن وقت هر جایی که تابع از آن صدا زده می‌شود، حافظهٔ خودش را دارد:

Cuprel
indicator "Rising in a row"
    edition = 1
    display = pane

fn rising_count(x: float): int
    state count = 0
    if x > x[1]
        count += 1
    else
        count = 0
    return count
plot(rising_count(close), name = "Closes rising in a row", color = color.blue)
plot(rising_count(volume), name = "Volume rising in a row", color = color.orange)

دو فراخوانی count را با هم شریک نیستند: یکی بسته‌شدن‌های رو به بالا را می‌شمارد و دیگری حجم رو به بالا را. تابع نمی‌تواند mut یا stateی را که بیرون از آن اعلام شده تغییر دهد؛ مقدار را به صورت پارامتر به آن بدهید و مقدار تازه را به صورت نتیجه بگیرید.

تابعی که به یاد می‌سپارد کجا می‌تواند بیاید#

چون چنین تابعی کندل‌هایی را که دیده به یاد دارد، باید در هر کندل اجرا شود. اگر در سمت راست and نوشته شود، فقط وقتی اجرا می‌شود که سمت چپ درست باشد، و بررسی‌کننده آن را نمی‌پذیرد:

Cuprel · خطا E3302
indicator "Called only sometimes"
    edition = 1
    display = pane

fn rising_count(x: float): int
    state count = 0
    if x > x[1]
        count += 1
    else
        count = 0
    return count
let strong = close > open and rising_count(close) > 2
plot(if strong then 1 else 0, name = "Strong")

اول آن را در یک خط جدا صدا بزنید، بعد نتیجه را به کار ببرید:

Cuprel
indicator "Called on every bar"
    edition = 1
    display = pane

fn rising_count(x: float): int
    state count = 0
    if x > x[1]
        count += 1
    else
        count = 0
    return count
let rising = rising_count(close)
let strong = close > open and rising > 2
plot(if strong then 1 else 0, name = "Strong")

تابع نمی‌تواند خودش را صدا بزند (E3310)؛ به جای آن از حلقه استفاده کنید. بقیهٔ قاعده‌ها در تابع‌ها، نوع‌ها و enum‌ها آمده است.

امتحان کنید#

  • در «Percent change»، percent_change(close, open) را صدا بزنید: هیستوگرام تغییر از بازشدن تا بسته‌شدن هر کندل می‌شود.
  • در «Small bodies»، Largest share را روی 0.05 بگذارید: کندل‌های کمتری علامت می‌خورند.
  • در «Envelope»، percent = 0.5 را حذف کنید: پیش‌فرض 1.0 به کار می‌رود و باندها دورتر می‌روند.
  • در «Rising in a row»، خط سومی اضافه کنید: plot(rising_count(high), name = "Highs rising in a row")؛ این فراخوانی هم حافظهٔ خودش را می‌گیرد.

نقطهٔ بررسی#

  • می‌توانید تابعی در یک خط و تابعی با بلوک بنویسید و از return به عنوان نگهبان استفاده کنید.
  • می‌توانید به پارامتر مقدار پیش‌فرض بدهید و آرگومان را با نامش بفرستید.
  • می‌توانید چند مقدار برگردانید و آن‌ها را با let (a, b, c) = ... از هم جدا کنید.
  • می‌دانید که هر فراخوانیِ تابعی که به یاد می‌سپارد حافظهٔ خودش را دارد و باید در هر کندل صدا زده شود.

بعدی: درس 7: فهرست‌ها و نگاشت‌ها