Для опытных: свои типы и методы
Объедините связанные данные в собственный тип и создайте операцию для них.
Ранее цены, счётчики и настройки хранились в отдельных переменных. Этого достаточно для множества индикаторов и стратегий. Но в большой программе бывает удобно объединить связанные данные: например, границы диапазона или цену и время сигнала.
Для этого есть пользовательский тип, по-английски user-defined type, сокращённо UDT. Метод является операцией, связанной с определённым типом. Оба входят в Pyne code, но для следующих примеров необязательны. Если хотите сначала дойти до стратегий, переходите к отладке и вернитесь позже.
Связанные данные в одном типе
Диапазон задают два числа: нижняя и верхняя границы. Собственный тип даёт этой паре имя:
@udt
class PriceRange:
bottom: float = 0.0
top: float = 0.0class определяет новый тип PriceRange. bottom и top являются его полями. Оба допускают дробные значения и по умолчанию равны нулю.
@udt делает тип пользовательским типом Pyne. udt импортируется из pynecore.core.pine_udt. В Edge в определении типа записываются поля, методы отдельно. Наследование в этом наборе языка не используется.
Тип и созданный объект
Тип описывает совместно хранимые данные. Конкретную пару хранит объект, или экземпляр:
current_range = PriceRange()
current_range.bottom = low
current_range.top = highPriceRange() создаёт экземпляр с полями по умолчанию. Следующие строки устанавливают минимум текущей свечи нижней границей, максимум верхней. Поля читаются через current_range.bottom и current_range.top. Точка означает доступ к полю.
Из типа можно создать несколько объектов. Другой PriceRange может хранить иной диапазон. Имена полей совпадают, значения независимы.
Метод: операция над определённым типом
Середина диапазона равна среднему границ. Запишем это методом:
@method
def midpoint(self: PriceRange) -> float:
return (self.bottom + self.top) / 2@method помечает функцию методом. Тип первого параметра определяет данные: здесь объект PriceRange. self является именем этого объекта внутри метода. Это имя параметра, его тип задаёт PriceRange.
Определение находится вне class, на внешнем уровне файла. method импортируется из pynecore.core.pine_method.
Вызов:
center = midpoint(current_range)Известный метод вызывается по имени, первым аргументом получает объект. Поэтому self получает current_range. Возвращённое число присваивается center.
Это можно написать и простой функцией. Метод помогает организовать несколько операций одного типа и явно обозначить обрабатываемые данные.
Полная программа
Создайте udt_ranges.py в 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)Запустите на демонстрационных данных. Синяя линия показывает минимумы, оранжевая максимумы, зелёная середину. Зелёная точка каждой свечи лежит между двумя другими.

Каждая свеча создаёт новый PriceRange. Поля здесь не сохраняют прошлую свечу: они хранят границы текущей.
Присваивание и копирование
Присвоение объекта другой переменной не копирует его:
other_range = current_range
other_range.top = other_range.top + 1.0Обе переменные ссылаются на один объект. Вторая строка меняет и current_range.top.
Для отдельного экземпляра с теми же полями создайте новый объект:
other_range = PriceRange()
other_range.bottom = current_range.bottom
other_range.top = current_range.top
other_range.top = other_range.top + 1.0У этого типа из двух чисел изменение новой верхней границы не меняет исходную. Числа хранятся в разных объектах. Если поле позднее содержит другой объект или массив, его передача новому экземпляру тоже передаёт ссылку, не копируя содержимое.
Сохранение объекта между свечами
Для повторного использования объекта примените знакомый Persistent. Импортируйте Persistent из pynecore.
current_range: Persistent[PriceRange] = PriceRange()
current_range.bottom = low
current_range.top = highОбъект создаётся один раз, поля обновляются на каждой свече. Этими строками можно заменить создание и присваивания полей в начале main(). Середина и рисование прежние.
График здесь не меняется: поля получают те же значения. В другой задаче их можно менять только при событии, сохраняя ранее выбранный диапазон. Сам тип не делает объект постоянным между свечами.
Попробуйте самостоятельно
Создайте метод width, возвращающий ширину диапазона. По примеру midpoint используйте self: PriceRange и вычислите из полей.
Решение
@method
def width(self: PriceRange) -> float:
return self.top - self.bottomВызов внутри main():
range_width = width(current_range)Для максимума 108 и минимума 101 ширина 7. Метод только читает поля, не меняя границы.