Własne funkcje
Podzielisz kod na nazwane obliczenia i użyjesz ich w kilku miejscach.
Znasz już obliczenie zakresu świecy: high - low. Jeśli potrzebujesz go w wielu obliczeniach, możesz wydzielić funkcję. Nazwa wywołania może wyjaśniać cel, a wzór utrzymujesz w jednym miejscu.
Wejścia i wynik funkcji
def price_range(top: float, bottom: float) -> float:
return top - bottomdef definiuje funkcję price_range. Przyjmuje dwa wejścia, top i bottom, oba liczbowe i dopuszczające ułamki.
-> float określa typ wyniku. return oblicza i zwraca wynik, kończąc dane wywołanie.
price_range(105, 100) daje 5. Funkcja nie wie, czy liczby są cenami, i nie wybiera świecy. Wartości podaje wywołujący.
Ciało funkcji ma cztery spacje wcięcia. Definicja stoi obok main() na zewnętrznym poziomie pliku, więc potrzebne ceny przekazujemy parametrami. Funkcję można też zdefiniować wewnątrz main(); spróbujemy poniżej.
Zasięg nazwy
top i bottom są parametrami dostępnymi wyłącznie w price_range. main() nie może odwołać się do nich bezpośrednio; używa zwróconego wyniku.
Zasięg nazwy określa część kodu, w której jest dostępna. Dwie funkcje mogą używać identycznych nazw parametrów bez tworzenia jednej wspólnej zmiennej.
price_range zdefiniowane na poziomie pliku nie widzi automatycznie lokalnych zmiennych main(). Może jednak korzystać z importów na początku pliku, na przykład close. Widoczność zależy więc też od miejsca definicji.
Definicja i wywołanie to nie to samo. Definicja opisuje regułę, a wywołanie wykonuje ją z konkretnymi wartościami.
Pełny program
Utwórz price_functions.py. Ta sama funkcja obliczy pełny zakres świecy i zmianę od otwarcia do zamknięcia ze znakiem:
"""
@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)Uruchom na demonstracji. Pełny zakres nie jest ujemny, bo maksimum jest co najmniej równe minimum. Zmiana otwarcia do zamknięcia może być ujemna. Funkcja nie modyfikuje cen; zwraca ich różnicę.

Funkcja w przykładzie jest krótka i łatwa do ręcznego sprawdzenia. W większym programie możesz nazwać bardziej złożone podzadania, ale nie musisz wydzielać pojedynczego dodawania.
Kolejność parametrów
price_range(high, low) przekazuje high jako top, a low jako bottom. Zamiana odwraca znak odejmowania.
Możesz też podać nazwę parametru:
full_range = price_range(top=high, bottom=low)To dłuższy zapis, ale bywa czytelniejszy przy złożonych wywołaniach. Nazwa musi dokładnie odpowiadać parametrowi definicji.
Funkcja wewnątrz main()
Funkcja zdefiniowana wewnątrz innej ma dostęp także do jej zmiennych. Przydaje się to, gdy kilka obliczeń korzysta z tego samego ustawienia wejściowego.
Wygładźmy obie linie średnią kroczącą. Jej długość określimy jednym wejściem używanym w obu obliczeniach. Do funkcji wewnętrznej przekażemy tylko ceny, a długość odczyta z parametrów main().
W price_functions.py zachowaj zewnętrzne price_range i dekorator. Dodaj input i ta do importu z pynecore.lib, a definicję main() zastąp:
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 to poznane wejście całkowitoliczbowe. Wewnętrzne average_difference dostaje top i bottom od wywołania, a length odczytuje z otaczającego main(). Nie trzeba go ponownie przekazywać.
Definicja wewnętrzna ma cztery spacje, bo należy do main(). Własne ciało, wiersz return, ma osiem. average_difference jest dostępne wewnątrz main(), nie na zewnętrznym poziomie pliku.
price_range liczy różnicę, ta.sma zwraca jej średnią. Niebieska linia jest średnią pełnego zakresu, pomarańczowa średnią zmiany korpusu ze znakiem. Oba wywołania działają na każdej świecy, więc średnie liczą się nieprzerwanie.
Uruchom wersję, otwórz Inputs kołem zębatym, zmień Average length z 5 na 20 i kliknij Save. Otwarty wykres automatycznie przeliczy obie linie na dwudziestoświecowe. Nie trzeba było zmieniać wywołań. Ustawiając długość 1, odzyskasz linie pierwotnego programu.
Ten dostęp do zewnętrznych zmiennych nazywa się domknięciem, po angielsku closure. Możliwość odczytu nie oznacza, że przypisanie wewnątrz automatycznie nadpisze zmienną zewnętrzną. Tutaj tylko odczytujemy length.
Gdy czytasz kod przetłumaczony z Pine Script: PyneComp umieszcza funkcje wewnątrz main(). Zmienne głównego poziomu Pine Script też trafiają do main(), dzięki czemu funkcje wewnętrzne mają do nich dostęp. Stąd taki układ w wygenerowanym kodzie. Własny Pyne code może używać obu rozwiązań: oddzielnej funkcji z parametrami lub wewnętrznej z dostępem do otaczających zmiennych.
W wersji parametrycznej wszystkie przekazane wartości widać w wywołaniu i łatwo użyć innej pary cen. Wersja wewnętrzna bywa wygodna, gdy kilka obliczeń korzysta z tych samych zmiennych main().
Własna funkcja też może mieć stan
price_range oblicza tylko z otrzymanych wartości. Nie ma zachowywanej zmiennej ani historii, więc można ją dowolnie wywoływać z dwiema wartościami.
Powyższe average_difference już ma stan, bo jego ta.sma liczy z przeszłości. Własna funkcja może też bezpośrednio deklarować Persistent lub Series.
Funkcja zliczająca zdarzenia
Policzmy osobno świece wzrostowe i spadkowe. Funkcja otrzyma wartość logiczną i zapamięta, ile razy dotąd dostała prawdę.
Na początku pliku importuj Persistent:
from pynecore import PersistentPrzed main(), na poziomie pliku, dodaj:
def count_events(event: bool) -> int:
count: Persistent[int] = 0
if event:
count += 1
return countcount zaczyna od zera. Prawdziwe event zwiększa go o jeden, fałszywe zachowuje wartość. return zawsze zwraca dotychczasową sumę. Po zakończeniu wywołania Persistent zachowuje wynik na następny raz.
Zostaw overlay=False. Zastąp definicję i ciało main():
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)Wywołujemy tę samą funkcję z dwóch miejsc kodu. Każde ma osobny stan count: pierwsze liczy wzrosty, drugie spadki. Pierwsze nie zwiększa drugiego licznika.
Jeśli pierwsze cztery świece wystąpią w podanej kolejności, otrzymasz:
| Świeca | Relacja otwarcia i zamknięcia | bullish_count | bearish_count |
|---|---|---|---|
| Pierwsza | Zamknięcie wyżej | 1 | 0 |
| Druga | Zamknięcie niżej | 1 | 1 |
| Trzecia | Ceny równe | 1 | 1 |
| Czwarta | Zamknięcie wyżej | 2 | 1 |
Uruchom na demonstracji. Zielona linia zwiększa się tylko przy wzrostowej, czerwona przy spadkowej świecy. Nowy przebieg buduje oba liczniki od zera.
Oba wywołania działają na każdej świecy; warunek zwiększenia jest wewnątrz. Dla funkcji stanowych kolejność i częstość wywołań należą do zachowania. Wywoływana tylko na części świec historia przesuwa się wyłącznie przy tych wywołaniach.
Jeśli potrzebujesz jednego wspólnego licznika, wywołaj funkcję raz i używaj zmiennej wynikowej w wielu miejscach. Drugie miejsce wywołania oznacza nowy osobny licznik.
Kilka instrukcji w funkcji
Wynik pośredni możesz obliczyć przed return:
def midpoint(top: float, bottom: float) -> float:
total = top + bottom
return total / 2total jest lokalne i nie staje się zmienną main() po zakończeniu wywołania. Z funkcji przekazaliśmy tylko wynik.
Spróbuj samodzielnie
Dodaj funkcję midpoint, potem utwórz wskaźnik overlay rysujący środek świec. Parametry top i bottom mają otrzymywać maksimum i minimum.
Rozwiązanie zadania
Wpisz powyższe midpoint przed main(). W dekoratorze ustaw overlay=True, a w ciele:
center = midpoint(high, low)
plot(center, "Midpoint", color=color.blue, linewidth=2)Możesz usunąć obliczenia i rysowanie wcześniejszych linii z tej wersji. Usuń też zbędne importy.
Wyjaśnij wywołanie
Dla price_range(close, open) opowiedz, która wartość trafia do którego parametru, co robi return i gdzie main() otrzymuje wynik. Te trzy kroki można śledzić także przy dłuższych wywołaniach.