Kamu sudah mengenal perhitungan ukuran candle: high - low. Jika dibutuhkan dalam beberapa perhitungan, kamu bisa menempatkannya dalam fungsi sendiri. Pemanggilannya bisa menjelaskan tujuan perhitungan, sementara rumusnya tetap disimpan di satu tempat.

Input dan hasil sebuah fungsi

def price_range(top: float, bottom: float) -> float:
    return top - bottom

def mendefinisikan fungsi. Nama fungsinya price_range. Fungsi mengharapkan dua input bernama top dan bottom. Keduanya angka yang boleh memiliki pecahan.

-> float menandakan tipe hasil. return menghitung dan mengembalikan hasil, lalu mengakhiri pemanggilan fungsi tersebut.

Hasil price_range(105, 100) adalah 5. Fungsi tidak mengetahui apakah angka tersebut harga dan tidak memilih candle. Pemanggillah yang memberikan kedua nilai.

Isi fungsi di sini menjorok empat spasi. Dalam contoh ini, definisi berada di tingkat terluar file, sejajar dengan main(). Karena itu, kedua harga yang dibutuhkan diberikan sebagai parameter. Fungsi juga bisa dibuat di dalam main(); kita akan mencobanya di bawah.

Cakupan nama

top dan bottom adalah parameter yang hanya tersedia di dalam fungsi price_range. main() tidak bisa merujuknya secara langsung. Di sana, kamu menggunakan hasil yang dikembalikan.

Cakupan nama menentukan bagian kode tempat nama itu dapat diakses. Dua fungsi berbeda boleh menggunakan nama parameter yang sama tanpa membuat parameternya menjadi satu variabel bersama.

price_range yang didefinisikan pada tingkat terluar file tidak otomatis dapat mengakses variabel lokal main(). Namun, fungsi ini bisa menggunakan nama yang diimpor di awal file, seperti close. Jadi, tempat fungsi didefinisikan juga memengaruhi nama yang dapat dilihatnya.

Definisi dan pemanggilan fungsi juga berbeda. Definisi menentukan aturan; pemanggilan menjalankan aturan tersebut dengan nilai tertentu.

Program lengkap

Buat file price_functions.py. Fungsi yang sama menghitung seluruh rentang candle serta perubahan bertanda antara harga pembukaan dan penutupan:

"""
@pyne edge
"""
from pynecore.lib import close, color, high, low, open, plot, script


def price_range(top: float, bottom: float) -> float:
    return top - bottom


@script.indicator("Price Functions", overlay=False)
def main():
    full_range = price_range(high, low)
    body_change = price_range(close, open)
    plot(full_range, "Full range", color=color.blue)
    plot(body_change, "Body change", color=color.orange)

Jalankan pada data demo. Rentang penuh tidak negatif karena harga tertinggi candle setidaknya sama dengan harga terendah. Perubahan antara pembukaan dan penutupan bisa negatif. Fungsi tidak mengubah harga yang diberikan; fungsi mengembalikan selisih kedua angka.

Dua pemanggilan fungsi dalam price_functions.py menggambar rentang penuh candle dan perubahan bertanda pada badannya.
Fungsi buatan yang sama menghitung dua hasil berbeda dari input yang berbeda.

Fungsi contoh ini pendek dan mudah diperiksa secara manual. Dalam program yang lebih besar, kamu juga bisa memberi nama pada subperhitungan yang lebih rumit, tetapi satu penjumlahan tidak harus dijadikan fungsi tersendiri.

Urutan parameter

Pemanggilan price_range(high, low) memberikan nilai high kepada top dan nilai low kepada bottom. Jika urutannya ditukar, tanda hasil pengurangan juga berbalik.

Kamu juga bisa menyebutkan nama parameternya:

full_range = price_range(top=high, bottom=low)

Cara ini lebih panjang, tetapi bisa memudahkan pembacaan pemanggilan yang lebih rumit. Nama yang diberikan harus sama persis dengan nama parameter fungsi.

Fungsi di dalam main()

Fungsi yang didefinisikan di dalam fungsi lain juga bisa mengakses variabel fungsi yang mengelilinginya. Ini berguna ketika beberapa perhitungan menggunakan pengaturan input yang sama.

Kita akan menghaluskan kedua garis program sebelumnya dengan moving average. Panjang rata-rata ditentukan melalui satu input dan digunakan oleh kedua perhitungan. Fungsi di dalam hanya menerima dua harga; panjangnya dibaca dari parameter main().

Dalam price_functions.py, pertahankan fungsi luar price_range dan dekorator indikator. Tambahkan input dan ta pada impor dari pynecore.lib, lalu ganti definisi main() dengan:

def main(
    length: int = input.int(5, title="Average length", minval=1, maxval=200),
):
    def average_difference(top: float, bottom: float) -> float:
        return ta.sma(price_range(top, bottom), length)

    average_range = average_difference(high, low)
    average_body_change = average_difference(close, open)
    plot(average_range, "Average range", color=color.blue)
    plot(average_body_change, "Average body change", color=color.orange)

length adalah input bilangan bulat yang sudah kita kenal. Fungsi dalam average_difference menerima dua parameter: top dan bottom diberikan oleh pemanggilan. Nilai length dibaca dari fungsi main() yang mengelilinginya, sehingga tidak perlu diberikan lagi pada setiap pemanggilan.

Definisi fungsi dalam diawali empat spasi karena merupakan bagian main(). Isinya sendiri, baris return, diawali delapan spasi. Nama average_difference dapat digunakan di dalam main() dan tidak tersedia di tingkat terluar file.

price_range menghitung selisih kedua harga dan ta.sma mengembalikan moving average selisih itu. Garis biru menunjukkan rata-rata seluruh rentang candle, sedangkan garis oranye menunjukkan rata-rata perubahan bertanda antara harga pembukaan dan penutupan. Kedua pemanggilan berjalan pada setiap candle, sehingga kedua rata-rata dihitung terus-menerus.

Jalankan program yang diubah, lalu buka Inputs melalui ikon roda gigi. Ubah Average length dari 5 menjadi 20 dan klik Save. Grafik yang terbuka otomatis dihitung ulang dan kedua garis menggunakan rata-rata dua puluh candle. Kamu tidak perlu mengubah kedua pemanggilan fungsi dalam. Jika panjang diatur menjadi 1, kedua garis program asli muncul kembali.

Akses ke variabel luar ini disebut closure. Membaca nilai tidak berarti penugasan di dalam fungsi otomatis bisa menimpa variabel luar. Dalam contoh ini, kita hanya membaca nilai length.

Pada versi berparameter, semua nilai yang diberikan terlihat dari pemanggilan dan fungsi yang sama mudah digunakan dengan pasangan harga lain. Fungsi dalam dapat lebih nyaman ketika beberapa perhitungan bergantung pada variabel yang sama dalam main().

Fungsi buatan juga bisa memiliki status

price_range hanya menghitung dari nilai yang diterima. Fungsi ini tidak menyimpan variabel antarcandle dan tidak menggunakan riwayat. Karena itu, kamu bisa memanggilnya kapan saja dengan dua nilai.

Sebaliknya, average_difference di atas menggunakan status karena ta.sma di dalamnya menghitung berdasarkan nilai terdahulu. Kamu juga bisa membuat variabel Persistent atau Series langsung dalam fungsi sendiri.

Fungsi penghitung peristiwa

Kita akan menghitung candle naik dan turun secara terpisah. Untuk itu, kita menulis fungsi yang menerima nilai logika dan mengingat berapa kali nilai benar sudah diterimanya.

Impor nama Persistent di awal file:

from pynecore import Persistent

Tulis fungsi berikut sebelum main(), pada tingkat terluar file:

def count_events(event: bool) -> int:
    count: Persistent[int] = 0
    if event:
        count += 1
    return count

Nilai awal count adalah nol. Jika event benar, nilainya bertambah satu; jika salah, nilainya dipertahankan. Dalam kedua kasus, return mengembalikan total sejauh ini. Setelah pemanggilan fungsi berakhir, nilai variabel Persistent tetap tersimpan untuk pemanggilan berikutnya.

Pertahankan overlay=False. Ganti definisi dan isi main() sebelumnya dengan:

def main():
    bullish_count = count_events(close > open)
    bearish_count = count_events(close < open)
    plot(bullish_count, "Bullish count", color=color.green)
    plot(bearish_count, "Bearish count", color=color.red)

Kita memanggil fungsi yang sama dari dua tempat berbeda dalam kode. Setiap lokasi pemanggilan memiliki status count tersendiri: yang pertama menghitung candle naik, yang kedua menghitung candle turun. Pemanggilan pertama tidak menambah penghitung pemanggilan kedua.

Misalnya, jika empat candle pertama mengikuti urutan ini, hasilnya sebagai berikut:

CandleHubungan harga pembukaan dan penutupanbullish_countbearish_count
PertamaHarga penutupan lebih tinggi10
KeduaHarga penutupan lebih rendah11
KetigaKedua harga sama11
KeempatHarga penutupan lebih tinggi21

Jalankan pada data demo. Garis hijau hanya bertambah pada candle naik, sedangkan garis merah hanya pada candle turun. Eksekusi baru membangun kedua penghitung kembali dari nol.

Kedua pemanggilan fungsi berjalan pada setiap candle; kondisi penambahan ada di dalam fungsi. Untuk fungsi yang menggunakan status, urutan dan frekuensi pemanggilan merupakan bagian dari perilakunya. Jika fungsi hanya dipanggil pada sebagian candle, riwayat di dalamnya juga hanya bergerak pada pemanggilan tersebut.

Jika memerlukan satu penghitung bersama, panggil fungsi sekali lalu gunakan variabel hasilnya di beberapa tempat. Lokasi pemanggilan kedua berarti penghitung terpisah yang baru.

Beberapa pernyataan dalam satu fungsi

Jika hasil memerlukan nilai antara, kamu bisa menghitungnya sebelum return:

def midpoint(top: float, bottom: float) -> float:
    total = top + bottom
    return total / 2

total adalah variabel lokal. Variabel ini tidak menjadi variabel main() setelah pemanggilan selesai. Kita hanya mengembalikan hasil dari fungsi.

Coba sendiri

Tambahkan fungsi midpoint ke program, lalu buat indikator overlay yang menggambar titik tengah harga candle. Gunakan parameter top dan bottom untuk menerima harga tertinggi dan terendah.

Solusi latihan

Tulis definisi midpoint di atas sebelum main(). Gunakan overlay=True pada dekorator indikator, dengan isi:

center = midpoint(high, low)
plot(center, "Midpoint", color=color.blue, linewidth=2)

Perhitungan dan gambar dua garis sebelumnya bisa dihapus dari versi ini. Hapus juga impor yang tidak digunakan.

Menjelaskan sebuah pemanggilan

Untuk baris price_range(close, open), jelaskan nilai yang masuk ke setiap parameter, apa yang terjadi pada baris return, dan di mana main() menerima hasilnya. Ketiga langkah ini juga bisa kamu telusuri dalam pemanggilan fungsi yang lebih panjang.