Program sebelumnya menyimpan harga, penghitung, dan pengaturan dalam variabel terpisah. Cara ini cukup untuk membuat banyak indikator dan strategi. Namun, dalam program yang lebih besar, beberapa data terkait mungkin lebih mudah ditangani sebagai satu kesatuan, misalnya dua batas rentang harga atau harga dan waktu sebuah sinyal.

Untuk itu, tersedia tipe buatan pengguna, atau user-defined type, disingkat UDT. Metode adalah operasi yang terkait dengan suatu tipe. Keduanya merupakan bagian Pyne code, tetapi tidak diperlukan untuk contoh selanjutnya dalam buku. Jika ingin mempelajari strategi terlebih dahulu, kamu bisa melanjutkan ke debugging dan kembali ke sini nanti.

Data terkait dalam satu tipe

Rentang harga dijelaskan oleh dua angka: batas bawah dan batas atas. Kita bisa memberi nama pada pasangan data ini melalui tipe sendiri:

@udt
class PriceRange:
    bottom: float = 0.0
    top: float = 0.0

class mendefinisikan tipe baru bernama PriceRange. bottom dan top adalah field tipe tersebut. Keduanya bisa menyimpan pecahan dan memiliki nilai bawaan nol.

Penanda @udt menjadikannya tipe buatan pengguna Pyne. Nama udt diimpor dari modul pynecore.core.pine_udt. Dalam Edge, definisi tipe berisi field; metode terkait didefinisikan secara terpisah. Dalam batas bahasa ini, kita tidak menggunakan pewarisan.

Tipe dan objek yang dibuat darinya

Tipe menjelaskan data apa yang akan disimpan bersama. Pasangan data tertentu disimpan dalam objek, juga disebut instance, yang dibuat dari tipe tersebut:

current_range = PriceRange()
current_range.bottom = low
current_range.top = high

PriceRange() membuat instance baru dengan nilai bawaan field. Dua baris berikutnya mengatur batas bawah ke harga terendah candle saat ini dan batas atas ke harga tertinggi. Field dapat dibaca sebagai current_range.bottom dan current_range.top. Titik di sini menandakan akses field.

Kamu bisa membuat beberapa objek dari tipe yang sama. Misalnya, PriceRange lain menyimpan rentang harga berbeda; nama field-nya sama, tetapi nilainya independen.

Metode: operasi pada tipe tertentu

Titik tengah rentang harga adalah rata-rata kedua batasnya. Kita juga bisa menuliskannya sebagai metode untuk tipe tersebut:

@method
def midpoint(self: PriceRange) -> float:
    return (self.bottom + self.top) / 2

@method menandai fungsi sebagai metode. Tipe parameter pertama menentukan data yang dikerjakan: di sini sebuah objek PriceRange. self adalah nama objek yang titik tengahnya dihitung di dalam metode. Ini merupakan nama parameter; anotasi PriceRange menentukan tipenya.

Definisi berada di luar blok class, pada tingkat terluar file. Nama method diimpor dari modul pynecore.core.pine_method.

Pemanggilannya:

center = midpoint(current_range)

Metode yang diketahui dipanggil melalui namanya, dengan objek yang dikerjakan sebagai argumen pertama. Jadi, parameter self menerima objek current_range. Angka yang dikembalikan disimpan dalam variabel center.

Perhitungan ini juga bisa ditulis sebagai fungsi biasa. Metode membantu menyusun program saat ada beberapa operasi untuk tipe tertentu dan kamu ingin memperjelas data yang dikerjakan masing-masing.

Program lengkap

Buat file udt_ranges.py dalam kelompok Scripts:

"""
@pyne edge
"""
from pynecore.core.pine_method import method
from pynecore.core.pine_udt import udt
from pynecore.lib import color, high, low, plot, script


@udt
class PriceRange:
    bottom: float = 0.0
    top: float = 0.0


@method
def midpoint(self: PriceRange) -> float:
    return (self.bottom + self.top) / 2


@script.indicator("Price Range Objects", overlay=True)
def main():
    current_range = PriceRange()
    current_range.bottom = low
    current_range.top = high
    center = midpoint(current_range)
    plot(current_range.bottom, "Lower", color=color.blue)
    plot(current_range.top, "Upper", color=color.orange)
    plot(center, "Midpoint", color=color.green, linewidth=2)

Jalankan pada data demo. Garis biru menunjukkan harga terendah candle, garis oranye harga tertinggi, dan garis hijau titik tengah keduanya. Pada setiap candle, titik hijau berada di antara dua nilai lainnya.

PyneIDE memperlihatkan kode tipe buatan PriceRange dan metode midpoint; grafik menampilkan tiga garis batas harga bawah, atas, dan titik tengah.
Dua field PriceRange menyimpan batas candle, dan metode midpoint menghitung titik tengahnya.

Program membuat objek PriceRange baru pada setiap candle. Dalam contoh ini, field tidak mempertahankan nilai candle sebelumnya; field selalu menyimpan kedua batas candle saat ini.

Penugasan dan penyalinan

Memberikan objek kepada variabel lain tidak sama dengan menyalinnya:

other_range = current_range
other_range.top = other_range.top + 1.0

Kedua variabel merujuk objek yang sama. Karena itu, baris kedua juga mengubah nilai current_range.top.

Jika ingin instance terpisah dengan nilai field yang sama, buat objek baru:

other_range = PriceRange()
other_range.bottom = current_range.bottom
other_range.top = current_range.top
other_range.top = other_range.top + 1.0

Untuk tipe yang terdiri dari dua angka ini, mengubah batas atas instance baru tidak mengubah objek asli. Kedua angka disimpan dalam objek terpisah. Jika nantinya field menyimpan objek atau array lain, memberikannya kepada instance baru juga memberikan referensi, tanpa menyalin isinya secara terpisah.

Mempertahankan objek antarcandle

Jika ingin terus menggunakan objek yang sama, kamu juga bisa memakai penanda Persistent yang sudah dipelajari. Impor nama Persistent dari modul pynecore.

current_range: Persistent[PriceRange] = PriceRange()
current_range.bottom = low
current_range.top = high

Objek dibuat sekali, lalu kedua field diperbarui pada setiap candle. Kamu bisa mengganti pembuatan objek dan dua penugasan field di awal main() dengan tiga baris ini. Perhitungan titik tengah dan gambar tetap sama.

Dalam contoh ini grafik tidak berubah karena nilai field yang diberikan sama. Pada tugas lain, kamu bisa mengubah field hanya saat suatu peristiwa terjadi, misalnya untuk mempertahankan rentang harga yang dipilih sebelumnya. Tipe saja tidak menjadikan objek persisten.

Coba sendiri

Buat metode bernama width yang mengembalikan lebar rentang harga. Seperti midpoint, gunakan parameter self: PriceRange dan hitung hasil dari kedua field.

Solusi latihan
@method
def width(self: PriceRange) -> float:
    return self.top - self.bottom

Pemanggilan di dalam main():

range_width = width(current_range)

Jika harga tertinggi sebuah candle adalah 108 dan terendahnya 101, lebarnya 7. Metode hanya membaca field tanpa mengubah kedua batas.