millionchart
English

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

مقدارهای زیادی را در آرایه یا نگاشت (map) نگه دارید: پنجره‌ای از کندل‌های اخیر، حلقه روی عضوها، میانه و صدک، و شمارش بر اساس کلید.

درس 7 از 18. تاریخچه با [n] هر بار یک مقدار می‌دهد. وقتی به چند مقدار با هم نیاز دارید، مثلاً دامنهٔ بیست کندل آخر برای پیدا کردن وسط آن‌ها، آن‌ها را در یک آرایه (فهرستی مرتب) یا یک نگاشت (map) (مقدارهایی که زیر کلیدها نگه داشته می‌شوند) بگذارید.

پنجره‌ای از مقدارهای اخیر#

Cuprel
indicator "Last closes"
    edition = 1
    display = price

input size: int = 20, name = "Window size", min = 2, max = 500
state window: array<float> = array<float>()
window.push(close)
if window.size() > size
    window.shift()
plot(window.avg(), name = "Average of the window", color = color.orange)
plot(window.max(), name = "Highest close", color = color.green)
plot(window.min(), name = "Lowest close", color = color.red)
  • state window: array<float> = array<float>() یک بار آرایه‌ای خالی از عدد می‌سازد و آن را از کندلی به کندل بعد نگه می‌دارد.
  • window.push(close) قیمت بسته‌شدن همین کندل را به انتهای آرایه اضافه می‌کند. وقتی پنجره بیش از size عضو داشته باشد، window.shift() قدیمی‌ترین عضو را بیرون می‌اندازد.
  • .size()، .avg()، .max() و .min() کل آرایه را می‌خوانند. .at(0) قدیمی‌ترین عضو است و .last() جدیدترین؛ شماره‌ها از 0 شمرده می‌شوند.

گشتن در عضوها#

for x in window به ترتیب به همهٔ عضوها سر می‌زند. این اسکریپت می‌شمارد چند قیمت بسته‌شدن در پنجره بالای میانگینشان قرار دارند:

Cuprel
indicator "Share above the average"
    edition = 1
    display = pane

input size: int = 20, name = "Window size", min = 2, max = 500
state window: array<float> = array<float>()
window.push(close)
if window.size() > size
    window.shift()
let average = window.avg()
mut above = 0
for x in window
    if x > average
        above += 1
plot(above / window.size() * 100, name = "Closes above the average, %", color = color.purple)
level(50, name = "Half", color = color.gray, style = dashed)

mut above = 0 در هر کندل از نو شروع می‌شود، پس شمارش هر بار تازه انجام می‌شود. for (i, x) in window شمارهٔ i را هم می‌دهد.

وسط یک پنجره#

آرایه می‌تواند عضوهایش را مرتب کند تا میانه یا صدک را پیدا کند. اینجا پنجره دامنهٔ کندل‌ها را نگه می‌دارد و اسکریپت دامنهٔ معمول و یک دامنهٔ بزرگ را نشان می‌دهد:

Cuprel
indicator "Typical range"
    edition = 1
    display = pane

input size: int = 50, name = "Window size", min = 5, max = 500
state ranges: array<float> = array<float>()
ranges.push(high - low)
if ranges.size() > size
    ranges.shift()
plot(high - low, name = "Range", style = columns, color = color.gray)
plot(ranges.median(), name = "Median range", color = color.blue)
plot(ranges.percentile(80), name = "80th percentile", color = color.orange)

ranges.median() مقدار وسط پنجره است و ranges.percentile(80) مقداری که 80 درصد عضوها پایین‌تر از آن‌اند. ستونی که بالای خط نارنجی برود، کندلی است که از چهار کندل از هر پنج کندل پنجره پهن‌تر است.

مقدارها زیر کلیدها#

نگاشت (map) یک مقدار را زیر یک کلید نگه می‌دارد. این یکی برای هر ساعت روز، مجموع دامنهٔ کندل‌های آن ساعت و تعدادشان را نگه می‌دارد و میانگین دامنهٔ ساعت فعلی را رسم می‌کند:

Cuprel
indicator "Range by hour"
    edition = 1
    display = pane

state totals: map<int, float> = map<int, float>()
state counts: map<int, int> = map<int, int>()
let hour = clock.hour_of(bar.time, zone = "UTC")
totals.set(hour, (totals.at(hour) ?? 0) + (high - low))
counts.set(hour, (counts.at(hour) ?? 0) + 1)
mut widest_hour = 0
mut widest = 0.0
for key in totals.keys()
    let average = totals.at(key) / counts.at(key)
    if average > widest
        widest = average
        widest_hour = key
plot(totals.at(hour) / counts.at(hour), name = "Average range of this hour", color = color.blue)
plot(widest_hour, name = "Hour with the widest bars (UTC)", display = none)
  • totals.set(key, value) مقداری را نگه می‌دارد؛ totals.at(key) آن را می‌خواند و تا وقتی کلید وجود ندارد na می‌دهد، پس ?? 0 ساعت تازه را از 0 شروع می‌کند.
  • totals.keys() آرایه‌ای از کلیدهاست، پس یک حلقهٔ for می‌تواند در آن‌ها بگردد.
  • display = none خط آخر را از روی نمودار برمی‌دارد: اسکریپت مقدار را باز هم حساب می‌کند، اما رسمش نمی‌کند.

آن را روی نمودار درون‌روزی به کار ببرید: روی نمودار روزانه همهٔ کندل‌ها یک ساعت دارند.

اشتباهی که باید از آن دوری کرد#

کروشه بعد از یک نام همیشه یعنی تاریخچه، هیچ‌وقت «عضوِ شمارهٔ فلان» نیست: window[1] کل آرایه است، همان‌طور که یک کندل قبل بود. بررسی‌کننده آن را نمی‌پذیرد و می‌گوید window.at(1) بنویسید:

Cuprel · خطا E3173
indicator "Brackets on an array"
    edition = 1
    display = price

state window: array<float> = array<float>()
window.push(close)
if window.size() > 10
    window.shift()
plot(window[1], name = "Second item")

آرایه یا نگاشت حداکثر 100,000 عضو نگه می‌دارد؛ بیشتر از آن، اسکریپت با E-LIMIT-ITEMS متوقف می‌شود. پنجره‌ای که قدیمی‌ترین عضوش را با shift() بیرون می‌اندازد هیچ‌وقت به این سقف نزدیک نمی‌شود. خواندن .at(i) بیرون از آرایه هم اسکریپت را متوقف می‌کند، پس اول .size() را بررسی کنید.

امتحان کنید#

  • در «Last closes»، در window.push(...) به جای close بنویسید high - low: حالا خط‌ها دامنهٔ کندل‌ها را نشان می‌دهند، نه قیمت را (با display = pane اسکریپت را به پنل جدا ببرید).
  • در «Share above the average»، x > average را به x > close تغییر دهید: خط سهمی از پنجره را نشان می‌دهد که بالای قیمت بسته‌شدن فعلی است.
  • در «Typical range»، ranges.percentile(95) را امتحان کنید: خط نارنجی بالا می‌رود و ستون‌های کمتری از آن رد می‌شوند.
  • در «Range by hour»، zone = "Europe/London" را به کار ببرید: ساعت‌ها به وقت لندن شمرده می‌شوند.

نقطهٔ بررسی#

  • می‌توانید پنجره‌ای غلتان از مقدارها را در یک آرایهٔ state با push و shift نگه دارید.
  • می‌توانید با for x in ... در آرایه بگردید و عضوهای تکی را با .at(i) بخوانید.
  • می‌توانید میانه و صدک یک پنجره را پیدا کنید.
  • می‌توانید در یک نگاشت مقدارها را بر اساس کلید بشمارید و جمع بزنید، و می‌دانید که .at(key) برای کلید ناموجود na می‌دهد.

بعدی: درس 8: برچسب‌ها، خط‌ها و باکس‌ها