ข้ามไปที่เนื้อหา

สร้างอินดิเคเตอร์ทางเทคนิคใน cTrader

บทความนี้อธิบายขั้นตอนในการสร้างอินดิเคเตอร์ทางเทคนิคที่กำหนดเอง รวมถึงเครื่องมือสำหรับการเทรดด้วยอัลกอริทึมหรือการดำเนินการด้วยตนเองใน cTrader

ในแท็บ อินดิเคเตอร์ ของแอป Algo ให้คลิกปุ่ม ใหม่ เพื่อเปิดตัวช่วยสร้างอัลกอริทึม

ป้อนชื่อสำหรับอินดิเคเตอร์ของคุณ จากนั้นเลือกภาษาโปรแกรมระหว่าง C# และ Python

เลือกวิธีการสร้างจาก:

  • จากเริ่มต้น – อินดิเคเตอร์ใหม่จะมีเฉพาะเทมเพลตพื้นฐานเท่านั้น

  • ใช้เทมเพลต – คุณจะสามารถเลือกอัลกอริทึมที่สร้างไว้ล่วงหน้าจากรายการเทมเพลต Python# หรือ C# ซึ่งครอบคลุมประเภทอินดิเคเตอร์ เครื่องมือวิเคราะห์ทางเทคนิค และอื่นๆ อีกมากมาย

หมายเหตุ

อัลกอริทึมที่สร้างไว้ล่วงหน้ามีตรรกะสำหรับการคำนวณและพารามิเตอร์ที่สามารถปรับแต่งได้อยู่แล้ว อินดิเคเตอร์ดังกล่าวพร้อมที่จะแสดงบนกราฟทันทีที่คุณบันทึกและสร้าง

หลังจากที่คุณคลิก สร้าง โค้ดเอดิเตอร์จะเปิดขึ้นและคุณสามารถเริ่มแก้ไขโค้ดอินดิเคเตอร์ได้

แก้ไขโค้ด

ขึ้นอยู่กับวิธีการสร้างของคุณ ตัวอย่างโค้ดอินดิเคเตอร์ควรมีองค์ประกอบอย่างน้อยหนึ่งรายการต่อไปนี้:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[Indicator(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class SimpleIndicator : Indicator
{
    [Parameter(DefaultValue = "Hello world!")]
    public string Message { get; set; }

    [Output("Main")]
    public IndicatorDataSeries Result { get; set; }

    protected override void Initialize()
    {
    // Called once when the indicator is launched
    // Initialise variables, nested indicators or series

        Print(Message);
    }

    public override void Calculate(int index)
    {
    // Called automatically on each historical bar
    // Also called in real-time on each tick
    // Place the indicator formula and logic here
    }

    // === Custom Methods ===
    // Add helper methods below to structure your indicator logic

}

แอตทริบิวต์ Indicator พร้อมด้วยคุณสมบัติเสริม เช่น TimeZone และ AccessRights จะอยู่ก่อนการประกาศคลาสอินดิเคเตอร์ (SimpleIndicator)

เมธอดเหล่านี้รวมอยู่ในอินดิเคเตอร์ทุกตัวโดยค่าเริ่มต้น:

  • เมธอด Initialize() จะถูกเรียกใช้หนึ่งครั้งเมื่ออินดิเคเตอร์ของคุณเริ่มทำงาน โดยทั่วไปจะใช้เพื่อตั้งค่าตัวแปร อินดิเคเตอร์ที่ซ้อนกัน และชุดข้อมูลที่จำเป็นต้องพร้อมใช้งานตั้งแต่เริ่มต้น
  • เมธอด Calculate() จะถูกเรียกใช้โดยอัตโนมัติในแต่ละแท่งเทียนในประวัติและในเวลาจริงสำหรับทุก tick ใหม่ นี่คือที่ที่คุณใช้สูตรของอินดิเคเตอร์และกำหนดวิธีการคำนวณและแสดงผลลัพธ์

แอตทริบิวต์ [Output()] ระบุค่าที่อินดิเคเตอร์ของคุณสร้างขึ้นและวิธีที่ค่าเหล่านั้นปรากฏบนกราฟ คุณสามารถปรับแต่งชื่อและสไตล์ของแต่ละเอาต์พุตได้ เช่น การตั้งค่าสีของเส้น

เมธอด Initialize() และ Calculate() เป็นพื้นฐานของอินดิเคเตอร์ที่กำหนดเองทุกตัวและไม่ควรละเว้น คุณสามารถเพิ่มเมธอดของคุณเองเพื่อขยายฟังก์ชันการทำงาน เช่นเดียวกับที่คุณทำเมื่อเขียนโค้ด cBot หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับอัลกอริทึม ให้เริ่มต้นด้วยคู่มือ พื้นฐาน C# และสำรวจ ตัวอย่างโค้ดอินดิเคเตอร์

หมายเหตุ

การอ้างอิง รวมถึงคลาส อีเวนต์ เมธอด ตัวแปร ฯลฯ ทั้งหมดสำหรับการสร้างอัลกอริทึมใน cTrader ในขณะที่ตัวอย่างอัลกอริทึมและเทมเพลตแบบเต็มมีอยู่ใน ที่เก็บ GitHub

นำความรู้ใหม่ของคุณมาใช้ในการแก้ไขโค้ดอินดิเคเตอร์และปรับให้เหมาะกับความต้องการของคุณ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class SimpleIndicator:
    def initialize(self):
        # Called once when the indicator is launched
        # Initialise variables, nested indicators or series
        api.Print(api.Message)

    def calculate(self, index):
        # Called automatically on each historical bar
        # Also called in real-time on each tick
        # Place the indicator formula and logic here
        api.Result[index] = 100 # Example calculation

    # === Custom Methods ===
    # Add helper methods below to structure your indicator logic

คลาส SimpleIndicator กำหนดอินดิเคเตอร์ที่กำหนดเองใน Python

เมธอดเหล่านี้รวมอยู่ในอินดิเคเตอร์ทุกตัวโดยค่าเริ่มต้น:

  • เมธอด initialize(self) จะถูกเรียกใช้หนึ่งครั้งเมื่ออินดิเคเตอร์ของคุณเริ่มทำงาน โดยทั่วไปจะใช้เพื่อเริ่มต้นตัวแปร อินดิเคเตอร์ที่ซ้อนกัน และชุดข้อมูลที่จำเป็นต้องพร้อมใช้งานตั้งแต่เริ่มต้น
  • เมธอด calculate(self, index) จะถูกเรียกใช้โดยอัตโนมัติในแต่ละแท่งเทียนในประวัติและในเวลาจริงสำหรับทุก tick ใหม่ นี่คือที่ที่คุณใช้สูตรของอินดิเคเตอร์และกำหนดวิธีการคำนวณและแสดงผลลัพธ์

เมธอด initialize(self) และ calculate(self, index) เป็นพื้นฐานของอินดิเคเตอร์ที่กำหนดเองทุกตัวและไม่ควรละเว้น คุณยังสามารถเพิ่มเมธอดตัวช่วยของคุณเองภายในคลาสเพื่อขยายฟังก์ชันการทำงาน เช่นเดียวกับที่คุณทำเมื่อเขียนโค้ด cBot หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับอัลกอริทึม ให้เริ่มต้นด้วย พื้นฐาน Python และสำรวจ ตัวอย่างโค้ดอินดิเคเตอร์

นำความรู้ใหม่ของคุณมาใช้ในการแก้ไขโค้ดอินดิเคเตอร์และปรับให้เหมาะกับความต้องการของคุณ

บันทึกและสร้าง

บันทึกโค้ดของคุณโดยคลิกปุ่ม บันทึก ที่ด้านบนของโปรแกรมแก้ไขโค้ด หรือใช้ทางลัด Ctrl+S

ก่อนที่คุณจะสามารถใช้อินดิเคเตอร์ของคุณได้ คุณต้องตรวจสอบความถูกต้องของโค้ดโดยการสร้างโปรเจกต์อินดิเคเตอร์ คลิกปุ่ม สร้าง ที่ด้านบนของโปรแกรมแก้ไขโค้ด หรือกด Ctrl+B

บันทึกโค้ดของคุณโดยคลิกไอคอน บันทึก ที่ด้านบนของโปรแกรมแก้ไขโค้ด หรือใช้ทางลัด Cmd+S

ก่อนที่คุณจะสามารถใช้อินดิเคเตอร์ของคุณได้ คุณต้องตรวจสอบความถูกต้องของโค้ดโดยการสร้างโปรเจกต์อินดิเคเตอร์ คลิกไอคอน สร้าง ที่ด้านบนของโปรแกรมแก้ไขโค้ด หรือกด Cmd+B

เมื่อการสร้างสำเร็จ คุณจะเห็นข้อความยืนยันใน ผลลัพธ์การสร้าง หากการสร้างล้มเหลว จะปรากฏสรุปข้อผิดพลาดทั้งหมดที่พบแทน

หากมีการเปลี่ยนแปลงในโค้ดตั้งแต่การสร้างครั้งล่าสุด จะมีเครื่องหมายดอกจันปรากฏข้างไอคอน สร้าง ในกรณีนี้ คุณควรสร้างอินดิเคเตอร์อีกครั้งก่อนที่จะเพิ่มอินสแตนซ์ลงในกราฟใดๆ

หากต้องการใช้หรือแสดงอินดิเคเตอร์บนกราฟ ให้เพิ่มอินสแตนซ์ของอินดิเคเตอร์นั้น หรือคุณสามารถดำเนินการแก้ไขโค้ดอินดิเคเตอร์ต่อไปตามที่อธิบายไว้ในส่วนต่างๆ

ใช้การคำนวณ NaN

อินดิเคเตอร์ทำการคำนวณเพื่อแสดงผลลัพธ์ทางภาพที่เฉพาะเจาะจง และเป็นสิ่งสำคัญที่โค้ดของคุณต้องจัดการกับกรณีพิเศษและเงื่อนไขขอบเขตระหว่างการคำนวณเหล่านี้อย่างถูกต้อง

พิจารณาส่วนของโค้ดต่อไปนี้จากอินดิเคเตอร์ที่แสดงผลลัพธ์ oscillator ราคาที่ถูกลบเทรนด์ออก:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private MovingAverage movingAverage;

protected override void Initialize()
{
    movingAverage = Indicators.SimpleMovingAverage(Source, Periods);
}

public override void Calculate(int index)
{
    Result[index] = Source[index] - movingAverage.Result[index - Periods / 2 - 1];
}

โค้ดด้านบนกำหนดกฎการคำนวณทั่วไปแต่ละเว้นเงื่อนไขขอบเขต ตัวอย่างเช่น หาก Periods ถูกตั้งค่าเป็น 10 และ index อยู่ในช่วง 0–9 การคำนวณจะส่งคืน NaN (Not a Number) ปัญหานี้เกิดขึ้นเนื่องจากอินดิเคเตอร์ไม่สามารถคำนวณค่าสำหรับแผนภูมิแท่งที่มีดัชนีเป็นลบได้

เพื่อแก้ไขปัญหา คุณสามารถเพิ่มเงื่อนไขดังที่แสดงด้านล่าง:

1
2
3
4
5
6
7
public override void Calculate(int index)
{
    if (index >= Periods + Periods / 2 + 1)
    {
        Result[index] = Source[index] - movingAverage.Result[index - Periods / 2 - 1];
    }
}

โค้ดสำหรับค่าเฉลี่ยเคลื่อนที่อย่างง่ายเองก็ต้องตรวจสอบว่า index >= Periods ด้วย หากคุณใช้อินดิเคเตอร์ที่ซ้อนกัน เช่น ค่าเฉลี่ยเคลื่อนที่ประเภทอื่น เงื่อนไขจะแตกต่างออกไปเพื่อรองรับการคำนวณเพิ่มเติม

แม้ว่าคุณสามารถกำหนดเงื่อนไขขอบเขตอย่างชัดเจนได้ แต่ C# มีวิธีที่สะดวกในการหลีกเลี่ยงปัญหาเหล่านี้ผ่านการคำนวณ NaN โดยเฉพาะอย่างยิ่ง ประเภทข้อมูล double รองรับการคำนวณ NaN อย่างเต็มที่ ตัวอย่างเช่น:

1
2
3
4
5
double result = double.NaN + 1.4; 
// result will be NaN

bool isNaN = double.IsNaN(result); 
// isNaN will be true

อย่างไรก็ตาม เมื่อตัวดำเนินการใดๆ ในการคำนวณเป็น NaN ผลลัพธ์ก็จะเป็น NaN ด้วย เนื่องจากอินดิเคเตอร์ที่กำหนดเองของ cTrader ทำงานบนประเภท DataSeries หากคุณพยายามเข้าถึงองค์ประกอบ DataSeries ที่มีดัชนีเป็นลบ มันจะส่งคืน NaN และค่าจะไม่ถูกวาดบนแผนภูมิการเทรด พฤติกรรมนี้ช่วยให้อินดิเคเตอร์สามารถทำงานต่อไปได้โดยไม่เกิดข้อยกเว้น ในกรณีส่วนใหญ่ คุณสามารถละเว้นเงื่อนไขขอบเขตอย่างปลอดภัยและมุ่งเน้นเฉพาะตรรกะการคำนวณทั่วไปเท่านั้น

เพิ่มการตรวจสอบอย่างชัดเจน

แม้ว่าการคำนวณ NaN จะทำให้การจัดการเงื่อนไขขอบเขตง่ายขึ้น แต่บางกรณียังคงต้องมีการตรวจสอบ NaN อย่างชัดเจน อินดิเคเตอร์แบบเรียกซ้ำเป็นตัวอย่างที่พบบ่อย อินดิเคเตอร์แบบเรียกซ้ำคำนวณค่าปัจจุบันโดยอิงจากค่าที่คำนวณไว้ก่อนหน้า

พิจารณาการใช้งานค่าเฉลี่ยเคลื่อนที่แบบเอ็กซ์โพเนนเชียลต่อไปนี้:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public override void Calculate(int index)
{
    var previousValue = Result[index - 1];

    if (double.IsNaN(previousValue))
    {
        Result[index] = Source[index];
    }
    else
    {
        Result[index] = Source[index] * _exp + previousValue * (1 - _exp);
    }
}

เมื่ออินดิเคเตอร์คำนวณช่วงแรก ค่าเริ่มต้นต้องถูกกำหนดอย่างชัดเจนให้เป็นประเภทที่ไม่ใช่ NaN มิฉะนั้น การคำนวณทั้งหมดที่ตามมาจะส่งคืน NaN ทำให้อินดิเคเตอร์ไร้ประโยชน์

ในโค้ดด้านบน สิ่งนี้ถูกจัดการด้วยเมธอด double.IsNaN(previousValue) ไม่สามารถใช้ตัวดำเนินการ == สำหรับการตรวจสอบนี้ได้ (previousValue == double.NaN) เนื่องจากการเปรียบเทียบใดๆ ของ NaN กับค่าอื่น รวมถึงตัวมันเอง จะประเมินเป็น false เสมอ

เมื่อคุณพอใจกับโค้ดอินดิเคเตอร์แล้ว สร้าง มัน จากนั้นใช้งานโดยเพิ่มอินสแตนซ์ลงในแผนภูมิ

Image title