Haladó: saját típusok és metódusok
Összetartozó adatokat saját típusba szervezel, és hozzájuk tartozó műveletet készítesz.
Az eddigi programok külön változókban tárolták az árakat, számlálókat és beállításokat. Ezzel sokféle indikátort és stratégiát el tudsz készíteni. Nagyobb programban azonban érdemes lehet egy egységként kezelni több összetartozó adatot: például egy ársáv két határát vagy egy jelzés árát és időpontját.
Erre szolgál a felhasználói típus, angolul user-defined type, röviden UDT. A metódus pedig egy adott típushoz tartozó művelet. Mindkettő a Pyne code része, de a könyv további példáihoz nem szükséges használnod őket. Ha először a stratégiákig szeretnél eljutni, továbbléphetsz a hibakereséshez, és később visszatérhetsz ide.
Összetartozó adatok egy típusban
Egy ársávot két szám ír le: az alsó és a felső határa. Saját típusban nevet adhatunk ennek az adatpárnak:
@udt
class PriceRange:
bottom: float = 0.0
top: float = 0.0A class új típust definiál, a neve PriceRange. A bottom és a top a típus mezői. Mindkettő tört értéket is tárolhat, és az alapértékük nulla.
Az @udt jelölés teszi ezt Pyne felhasználói típussá. A udt nevet a pynecore.core.pine_udt modulból importáljuk. Az Edge-ben a típus definíciójába a mezőket írjuk; a hozzá tartozó metódusokat külön definiáljuk. Öröklődést ebben a nyelvi keretben nem használunk.
A típus és a belőle létrehozott objektum
A típus leírja, milyen adatokat fogunk együtt tárolni. Egy konkrét adatpárt a típusból létrehozott objektum, más néven példány tárol:
current_range = PriceRange()
current_range.bottom = low
current_range.top = highA PriceRange() új példányt készít a mezők alapértékeivel. A következő két sor az alsó határát a mostani gyertya minimumára, a felsőt a maximumára állítja. A current_range.bottom és a current_range.top alakban olvashatod ki a mezőit. A pont itt egy mező elérését jelöli.
Ugyanabból a típusból több objektumot is létrehozhatsz. Például egy másik PriceRange más ársávot tárolhat; a mezőneveik azonosak, az értékeik egymástól függetlenek.
Metódus: művelet egy adott típuson
Az ársáv közepét a két határ átlagával kapjuk meg. Ezt a típushoz tartozó metódusként is megírhatjuk:
@method
def midpoint(self: PriceRange) -> float:
return (self.bottom + self.top) / 2Az @method metódusként jelöli meg a függvényt. Az első paraméter típusa mondja meg, milyen adaton dolgozik: itt egy PriceRange objektumon. A self annak az objektumnak a neve a metóduson belül, amelynek a közepét ki akarjuk számítani. Ez egy paraméternév; a típusát a PriceRange jelölés adja meg.
A definíció a class blokkon kívül, a fájl legkülső szintjén áll. A method nevet a pynecore.core.pine_method modulból importáljuk.
A hívás:
center = midpoint(current_range)Az ismert metódust a nevével hívjuk, és első argumentumként átadjuk az objektumot, amelyen dolgozik. A self paraméter így a current_range objektumot kapja. A visszaadott szám a center változóba kerül.
Ezt a számítást egyszerű függvénnyel is megírhatnád. A metódus akkor segít a program szervezésében, ha több, egy adott típushoz tartozó műveleted van, és egyértelművé szeretnéd tenni, milyen adaton dolgoznak.
A teljes program
Hozd létre az udt_ranges.py fájlt a Scripts csoportban:
"""
@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)Futtasd a demóadaton. A kék vonal a gyertyák minimumát, a narancssárga a maximumát, a zöld a kettő közepét mutatja. A zöld pont minden gyertyán a másik két érték között van.

A program minden gyertyán új PriceRange objektumot készít. Ebben a példában a mezők nem őrzik az előző gyertya értékeit; mindig a mostani gyertya két határát tárolják.
Értékadás és másolás
Egy objektumot egy másik változóhoz rendelni nem ugyanaz, mint lemásolni:
other_range = current_range
other_range.top = other_range.top + 1.0A két változó ugyanarra az objektumra hivatkozik. A második sor ezért a current_range.top értékét is megváltoztatja.
Ha külön példányt szeretnél ugyanazokkal a mezőértékekkel, hozz létre egy új objektumot:
other_range = PriceRange()
other_range.bottom = current_range.bottom
other_range.top = current_range.top
other_range.top = other_range.top + 1.0Ennél a két számból álló típusnál az új példány felső határának módosítása nem változtatja meg az eredetiét. A két számot külön objektumban tároljuk. Ha később egy mezőben másik objektumot vagy tömböt tárolsz, annak átadása egy új példánynak ugyanúgy hivatkozást ad át, és nem másolja le külön a tartalmát.
Objektum megőrzése a gyertyák között
Ha ugyanazt az objektumot szeretnéd tovább használni, a korábban tanult Persistent jelölést is alkalmazhatod. Ehhez importáld a Persistent nevet a pynecore modulból.
current_range: Persistent[PriceRange] = PriceRange()
current_range.bottom = low
current_range.top = highAz objektum egyszer jön létre, a két mezőjét pedig minden gyertyán frissítjük. A main() elején lévő objektumlétrehozást és a két mező értékadását cserélheted erre a három sorra. A közép számítása és a rajzolás változatlan marad.
Ebben a példában ettől a grafikon nem változik: ugyanazokat a mezőértékeket adjuk meg. Más feladatban a mezőket csak egy eseménynél módosíthatod, így például egy korábban kijelölt ársávot is megőrizhetsz. A típus önmagában nem teszi perzisztenssé az objektumot.
Próbáld ki önállóan
Készíts width nevű metódust, amely visszaadja az ársáv szélességét. A midpoint mintájára írj egy self: PriceRange paramétert, és a két mezőből számold ki az eredményt.
A feladat megoldása
@method
def width(self: PriceRange) -> float:
return self.top - self.bottomA hívása a main() belsejében:
range_width = width(current_range)Ha egy gyertyán a maximum 108, a minimum 101, akkor a szélesség 7. A metódus csak kiolvassa a mezőket; egyik határt sem módosítja.