콘텐츠로 이동

cBot 고급 작업

이 가이드는 주로 cBot 코드 샘플의 광범위한 목록을 보완하기 위한 것입니다.

다른 심볼 거래

새 인스턴스를 생성할 때 지정한 심볼 외에도 여러 심볼을 거래하는 cBot을 개발할 수 있습니다.

이를 가능하게 하기 위해, Symbols는 거래 계정에서 사용 가능한 모든 심볼의 컬렉션입니다. 루프를 사용하여 이를 반복할 수 있으며, 특정 심볼을 찾기 위해 검색할 수도 있습니다.

여러 심볼과 올바르게 작업하려면 cBot은 다음 정보를 알아야 합니다:

  • 허용되는 최소 및 최대 거래량
  • 거래량 단계
  • 핍 틱 크기
  • 핍 틱 값 (계정 입금 통화로 표시된 한 핍 또는 틱의 금전적 가치)

이 데이터는 Symbol 객체에서 얻을 수 있습니다. 아래 코드에서는 "GBPUSD"라는 이름의 심볼을 찾은 후 이를 위한 시장 주문을 생성합니다. 또한 최소 허용 거래량을 주문 거래량으로 사용합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        var symbol = Symbols.GetSymbol("GBPUSD");

        if (symbol is not null)
        {
            ExecuteMarketOrder(TradeType.Sell, symbol.Name, symbol.VolumeInUnitsMin);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class Sample_cBot():
    def on_start(self):
        symbol = api.Symbols.GetSymbol("GBPUSD")

        if symbol is not None:
            api.ExecuteMarketOrder(TradeType.Sell, symbol.Name, symbol.VolumeInUnitsMin)

핍을 틱으로 변환

cBot 코딩 시, 일부 변수 값은 단위로 계산되고 다른 값은 핍으로 계산되어 버그가 발생하기 쉽습니다.

아래 SymbolExtensions 클래스는 핍을 틱으로 변환하는 방법을 보여줍니다. 또한 절대 가격 값에 핍을 추가하는 방법도 보여줍니다.

  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
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
using System;
using cAlgo.API;
using cAlgo.API.Internals;

namespace Samples
{
    [Robot(AccessRights = AccessRights.None)]
    public class Sample : Robot
    {
        protected override void OnStart()
        {
            var spreadInPips = Symbol.NormalizePips(Symbol.ToPips(Symbol.Spread));

            Print(spreadInPips);

            var spreadInTicks = Math.Round(Symbol.ToTicks(Symbol.Spread));

            Print(spreadInTicks);

            /* Calculating a long position stop loss using the
            absolute price value. */
            var stopLossInPips = 60;

            /* We use 'NormalizePips' to avoid the 'invalid decimal places'
            error in case our stop loss value has too many decimals. */
            var stopLossInPrice = Symbol.Ask - (Symbol.NormalizePips(stopLossInPips) * Symbol.PipSize);

            Print(stopLossInPrice);
        }
    }

    public static class SymbolExtensions
    {
        /// <summary>
        /// Returns a symbol pip value
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns>double</returns>
        public static double GetPip(this Symbol symbol)
        {
            return symbol.TickSize / symbol.PipSize * Math.Pow(10, symbol.Digits);
        }

        /// <summary>
        /// Returns a price value in terms of pips
        /// </summary>
        /// <param name="symbol"></param>
        /// <param name="price">The price level</param>
        /// <returns>double</returns>
        public static double ToPips(this Symbol symbol, double price)
        {
            return price * symbol.GetPip();
        }

        /// <summary>
        /// Returns a price value in terms of ticks
        /// </summary>
        /// <param name="symbol"></param>
        /// <param name="price">The price level</param>
        /// <returns>double</returns>
        public static double ToTicks(this Symbol symbol, double price)
        {
            return price * Math.Pow(10, symbol.Digits);
        }

        /// <summary>
        /// Rounds a price level to the number of symbol digits
        /// </summary>
        /// <param name="symbol">The symbol</param>
        /// <param name="price">The price level</param>
        /// <returns>double</returns>
        public static double Round(this Symbol symbol, double price)
        {
            return Math.Round(price, symbol.Digits);
        }

        /// <summary>
        /// Normalize x Pips amount decimal places to something that can be used as a stop loss or take profit for an order.
        /// For example if symbol is EURUSD and you pass to this method 10.456775 it will return back 10.5
        /// </summary>
        /// <param name="symbol">The symbol</param>
        /// <param name="pips">The amount of Pips</param>
        /// <returns>double</returns>
        public static double NormalizePips(this Symbol symbol, double pips)
        {
            var currentPrice = Convert.ToDecimal(symbol.Bid);

            var pipSize = Convert.ToDecimal(symbol.PipSize);

            var pipsDecimal = Convert.ToDecimal(pips);

            var pipsAddedToCurrentPrice = Math.Round((pipsDecimal * pipSize) + currentPrice, symbol.Digits);

            var tickSize = Convert.ToDecimal(symbol.TickSize);

            var result = (pipsAddedToCurrentPrice - currentPrice) * tickSize / pipSize * Convert.ToDecimal(Math.Pow(10, symbol.Digits));

            return decimal.ToDouble(result);
        }
    }
}
 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import clr
import math

clr.AddReference("cAlgo.API")

from cAlgo.API import *
from robot_wrapper import *


class Sample():
    def on_start(self):
        spreadInPips = SymbolExtensions.normalize_pips(api.Symbol, SymbolExtensions.to_pips(api.Symbol, api.Symbol.Spread))
        print(spreadInPips)

        spreadInTicks = round(SymbolExtensions.to_ticks(api.Symbol, api.Symbol.Spread))
        print(spreadInTicks)

        stopLossInPips = 60

        stopLossInPrice = api.Symbol.Ask - (SymbolExtensions.normalize_pips(api.Symbol, stopLossInPips) * api.Symbol.PipSize)
        print(stopLossInPrice)


class SymbolExtensions:
    @staticmethod
    def get_pip(symbol):
        return symbol.TickSize / symbol.PipSize * math.pow(10, symbol.Digits)

    @staticmethod
    def to_pips(symbol, price):
        return price * SymbolExtensions.get_pip(symbol)

    @staticmethod
    def to_ticks(symbol, price):
        return price * math.pow(10, symbol.Digits)

    @staticmethod
    def round(symbol, price):
        return round(price, symbol.Digits)

    @staticmethod
    def normalize_pips(symbol, pips):
        currentPrice = float(symbol.Bid)
        pipSize = float(symbol.PipSize)
        pipsDecimal = float(pips)

        pipsAddedToCurrentPrice = round((pipsDecimal * pipSize) + currentPrice, symbol.Digits)
        tickSize = float(symbol.TickSize)

        result = (pipsAddedToCurrentPrice - currentPrice) * tickSize / pipSize * math.pow(10, symbol.Digits)
        return round(result, 12)

거래 세부 정보 가져오기

Transactions는 계정의 입금 및 출금에 대한 데이터를 제공하는 유형을 포함합니다. 이러한 유형을 통해 계정의 거래 세부 정보를 프로그래밍 방식으로 액세스하고 관리할 수 있습니다.

거래 유형(입금 또는 출금), 금액, 타임스탬프, 거래 ID, 잔고 세부 정보, 자산 속성 등에 대한 데이터를 검색할 수 있습니다.

이 cBot은 거래 세부 정보를 검색하고 출력합니다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot]
    public class TransactionsAPITEST : Robot
    {
        protected override void OnStart()
        {           
            foreach (var transaction in Transactions)
            {
                Print($"Transaction ID: {transaction.Id}, Amount: {transaction.Amount}, Type: {transaction.Type}, Date: {transaction.Time}");
            }
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class TransactionsAPITEST():
    def on_start(self):
        for transaction in api.Transactions:
            api.Print(
                f"Transaction ID: {transaction.Id}, "
                f"Amount: {transaction.Amount}, "
                f"Type: {transaction.Type}, "
                f"Date: {transaction.Time}"
            )

과거 주문 데이터 가져오기

cTrader는 완료된 시장 주문 및 실행되거나 취소된 예약 주문을 포함한 주문 정보를 검색할 수 있도록 HistoricalOrder 컬렉션을 제공합니다. 검색 가능한 세부 정보에는 주문 ID, 유형, 라벨, 목표 가격, 만료 등이 포함됩니다.

아래 cBot 코드는 과거 주문의 세부 정보를 가져오는 방법을 보여줍니다:

 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
29
30
31
32
33
34
35
using cAlgo.API;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class HistoricalOrdersExample : Robot
    {
        protected override void OnStart()
        {
            var historicalOrders = HistoricalOrders;
            foreach (var order in historicalOrders)
            {
                Print($"Order ID: {order.Id}");
                Print($"Type: {order.OrderType}");
                Print($"Volume: {order.VolumeInUnits}");
                Print($"Price: {order.TargetPrice}");
                Print($"Expiration: {order.ExpirationTime}");
                Print($"Comment: {order.Comment}");
                Print($"Label: {order.Label}");
                Print($"Stop Loss: {order.StopLoss}");
                Print($"Take Profit: {order.TakeProfit}");
                Print($"TradeType: {order.TradeType}");
                Print($"Quantity: {order.Quantity}");
                Print($"Label: {order.Label}");
            }
        }
    }
}

과거 거래 데이터 사용

cBot 코딩 시, 계정 기록에 액세스할 수 있습니다. 이는 과거 거래를 반복하고 그 데이터를 원하는 목적으로 사용할 수 있음을 의미합니다.

아래 코드 스니펫은 과거 모든 거래와 그 거래 데이터를 포함하는 .CSV 파일을 생성합니다.

 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
29
30
31
32
33
using System;
using cAlgo.API;
using System.Text;
using System.IO;

namespace Samples
{
    /* We provide the access rights required for
    reading and writing in locally stored files. */
    [Robot(AccessRights = AccessRights.FullAccess)]
    public class Sample : Robot
    {
        protected override void OnStart()
        {
            var stringBuilder = new StringBuilder();

            _ = stringBuilder.AppendLine($"PositionId,TradeType,SymbolName,VolumeInUnits,EntryTime,EntryPrice,ClosingTime,ClosingPrice,NetProfit,Balance");

            // All trades are inside the 'History' collection
            foreach (var trade in History)
            {
                _ = stringBuilder.AppendLine($"{trade.PositionId},{trade.TradeType},{trade.SymbolName},{trade.VolumeInUnits},{trade.EntryTime:o},{trade.EntryPrice},{trade.ClosingTime:o},{trade.ClosingPrice},{trade.NetProfit},{trade.Balance}");
            }

            // We will save the CSV file on our desktop
            var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            var filePath = Path.Combine(desktopPath, $"{Account.Number}_History.csv");

            File.WriteAllText(filePath, stringBuilder.ToString());
        }
    }
}

거래 정보 가져오기

cTrader에서 거래는 주문을 실행하고 포지션을 열거나 닫게 합니다. 시장 유동성에 따라 주문은 단일 거래로 완전히 체결되거나 여러 거래로 부분적으로 체결될 수 있습니다. 자세한 내용은 포지션과 거래를 참조하세요.

아래 cBot 코드는 단일 포지션에 존재하는 여러 거래에 대한 정보를 검색하는 방법을 보여줍니다:

 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
29
30
31
32
33
34
35
using System;
using cAlgo.API;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class DealsExample : Robot
    {
        protected override void OnStart()
        {
            var position = ExecuteMarketOrder(TradeType.Buy, "EURUSD", 10000);

            var deals = position.Position.Deals;

            foreach (var deal in deals)
            {
                Print(deal.ExecutionTime + " " + deal.Status + " " + deal.Id);
            }

            ClosePosition(position.Position, 3000);
            ClosePosition(position.Position, 7000);

            var closing_deals = History.FindByPositionId(position.Position.Id);

            foreach (var closing_deal in closing_deals)
            {
                Print(closing_deal.ClosingDeal.ExecutionTime + " " + closing_deal.ClosingDeal.VolumeInUnits);
            }
        }

        protected override void OnStop()
        {
        }
    }
}
 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
29
30
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class DealsExample():
    def on_start(self):
        position = api.ExecuteMarketOrder(TradeType.Buy, "EURUSD", 10000)

        deals = position.Position.Deals

        for deal in deals:
            api.Print(f"{deal.ExecutionTime} {deal.Status} {deal.Id}")

        api.ClosePosition(position.Position, 3000)
        api.ClosePosition(position.Position, 7000)

        closing_deals = api.History.FindByPositionId(position.Position.Id)

        for closing_deal in closing_deals:
            api.Print(f"{closing_deal.ClosingDeal.ExecutionTime} {closing_deal.ClosingDeal.VolumeInUnits}")

    def on_stop(self):
        pass

랏을 단위로 변환

기본적으로 cTrader 알고는 거래량을 랏 대신 단위로 계산합니다. 그러나 경우에 따라 랏으로 작업하는 것이 더 편리하고 익숙할 수 있습니다.

Symbol.QuantityToVolumeUnits() 메서드를 사용하여 랏을 단위로 변환할 수 있습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System;
using cAlgo.API;
using System.Text;
using System.IO;

namespace Samples
{
    [Robot(AccessRights = AccessRights.None)]
    public class Sample : Robot
    {
        protected override void OnStart()
        {
            var lots = 2.5;

            var volumeInUnits = Symbol.QuantityToVolumeInUnits(lots);

            ExecuteMarketOrder(TradeType.Sell, SymbolName, volumeInUnits);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class Sample():
    def on_start(self):
        lots = 2.5
        volume_in_units = api.Symbol.QuantityToVolumeInUnits(lots)
        api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, volume_in_units)

또는 Symbol.VolumeInUnitsToQuantity() 메서드를 사용하여 단위를 랏으로 변환할 수 있습니다.

색상 작업

cBot은 다양한 작업을 수행할 때 사용자 정의 색상을 사용할 수 있습니다(예: 트레이딩 차트에 텍스트 표시). 색상을 사용자 정의 매개변수로 추가하려면 매개변수 선언에 다음 코드를 추가하세요.

1
2
[Parameter("Color", DefaultValue = "#f54242")]
public Color DrawingColor { get; set; }

참고

DefaultValue를 정의할 때 16진수 색상 코드와 색상 이름(예: red")을 모두 사용할 수 있습니다."

색상 매개변수가 작동하는 방식을 설명하기 위해, 시작 시 작동 중인 트레이딩 차트에 텍스트를 작성하는 간단한 cBot을 생성할 것입니다. 이 텍스트의 색상은 TextColor 매개변수를 통해 사용자 정의할 수 있습니다.

 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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class SampleColorcBot : Robot
    {
        [Parameter("Text Color", DefaultValue = "red")]
        public Color TextColor { get; set; }

        protected override void OnStart()
        {
            var staticText = Chart.DrawStaticText("static", "Sample text to demonstrate how color works", VerticalAlignment.Center, HorizontalAlignment.Center, TextColor);

        }

    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class SampleColorcBot():
    def on_start(self):
        api.Chart.DrawStaticText(
            "static",
            "Sample text to demonstrate how color works",
            VerticalAlignment.Center,
            HorizontalAlignment.Center,
            api.TextColor
        )