अब तक कीमत, काउंटर और सेटिंग अलग वेरिएबल में थीं। इससे बहुत तरह के इंडिकेटर और रणनीति बन सकते हैं। बड़े प्रोग्राम में संबंधित डेटा एक साथ रखना उपयोगी है: जैसे कीमत बैंड की दो सीमाएँ या संकेत की कीमत और समय।

इसके लिए यूज़र-डिफ़ाइंड टाइप, अंग्रेज़ी में user-defined type, संक्षेप UDT, है। मेथड किसी टाइप से जुड़ा काम है। दोनों Pyne code का हिस्सा हैं, लेकिन आगे के उदाहरणों में ज़रूरी नहीं। पहले रणनीतियाँ सीखना चाहें तो डिबगिंग पर जाकर बाद में लौट सकते हैं।

संबंधित डेटा एक टाइप में

कीमत बैंड को दो नंबर बताते हैं: नीचे और ऊपर की सीमा। अपने टाइप में इस जोड़े को नाम दें:

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

class नया टाइप PriceRange बनाता है। bottom और top उसके फ़ील्ड हैं। दोनों दशमलव रख सकते हैं और डिफ़ॉल्ट शून्य है।

@udt इसे Pyne यूज़र टाइप बनाता है। udt, pynecore.core.pine_udt से इंपोर्ट है। Edge में टाइप के भीतर फ़ील्ड लिखते हैं और मेथड अलग बनाते हैं। इस सीमा में इनहेरिटेंस नहीं लेते।

टाइप और उससे बना ऑब्जेक्ट

टाइप बताता है कि कौन-सा डेटा साथ रखेंगे। असली जोड़ा उससे बने ऑब्जेक्ट, जिसे इंस्टेंस भी कहते हैं, में रहता है:

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

PriceRange() डिफ़ॉल्ट फ़ील्ड वाला नया इंस्टेंस बनाता है। अगली दो लाइन नीचे की सीमा वर्तमान लो और ऊपर की हाई करती हैं। 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 में जाता है।

यही साधारण फ़ंक्शन से भी कर सकते हैं। कई काम एक टाइप से जुड़े हों और स्पष्ट करना हो कि किस डेटा पर हैं, तो मेथड व्यवस्थित रखने में मदद करता है।

पूरा प्रोग्राम

Scripts में udt_ranges.py बनाएँ:

"""
@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)

डेमो पर चलाएँ। नीली लाइन लो, नारंगी हाई और हरी दोनों का मध्य है। हर कैंडल पर हरा बिंदु बाकी दो के बीच है।

PyneIDE में PriceRange यूज़र टाइप और midpoint मेथड का कोड है; ग्राफ़ पर नीचे, ऊपर और मध्य कीमत की तीन लाइनें हैं।
PriceRange के दो फ़ील्ड कैंडल की सीमाएँ रखते हैं और midpoint उनका मध्य निकालता है।

हर कैंडल पर नया 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 लगा सकते हैं। pynecore से Persistent इंपोर्ट करें।

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 है। मेथड केवल फ़ील्ड पढ़ता है; कोई सीमा नहीं बदलता।