콘텐츠로 이동

알고리즘 트레이딩을 위한 C#

C#이란 무엇인가

C#은 다양한 서비스와 애플리케이션을 구축하기 위해 사용되는 객체 지향 프로그래밍 언어입니다. 명확하고 구조화된 구문으로 인해 초보자도 쉽게 배우고 읽을 수 있으며, 간결하고 재사용 가능한 코드를 작성하는 구조화된 접근 방식을 제공합니다.

1분 만에 C# 알고리즘 만들기!

  • 이전에 C#을 사용해 본 적이 없더라도 첫 번째 로봇 또는 지표를 만들고 배포하는 데 단 몇 분밖에 걸리지 않습니다.
  • C#은 사용하기 쉬워서 이 문서의 코드 스니펫을 빠르게 수정하여 필요에 맞게 사용할 수 있습니다.
  • C#을 사용하면 미리 정의된 클래스와 메서드가 포함된 다양한 라이브러리에 액세스할 수 있습니다. 이러한 메서드는 일반적인 작업을 효율적으로 처리하여 복잡한 트레이딩 문제를 해결할 수 있도록 해줍니다.
  • C#에서는 실행 시 서버 스레드를 차단하지 않는 코드를 작성할 수 있습니다. 즉, 다른 작업과 동시에 특정 작업을 시작할 수 있습니다.

.NET이란 무엇인가?

C# 프로그램을 실행하려면 소스 코드를 중간 언어(IL)로 컴파일해야 합니다. 이 IL 코드는 공용 언어 인프라(CLI) 사양을 따르며, 런타임에 네이티브 머신 명령어로 컴파일됩니다. .NET 프레임워크는 광범위한 클래스 라이브러리와 공용 언어 런타임(CLR), Microsoft의 CLI 구현을 기반으로 가상 실행 환경을 제공합니다.

기술적 복잡성을 생략하면 .NET은 다음과 같은 기능을 수행합니다:

  • 애플리케이션 개발 및 실행 용이화: .NET SDK에는 이미 여러 내장 컴파일러와 빌드 엔진이 포함되어 있어 사용자 정의 솔루션을 만들 필요가 없습니다.
  • 런타임 라이브러리 제공: 코드에 새로운 데이터 타입이나 컬렉션을 추가할 때, .NET이 이미 적합한 클래스와 메서드를 제공하는 경우가 많습니다.

C#의 기본

데이터 타입 및 변수 선언

데이터 타입은 C#이 변수와 속성을 정확히 어떻게 처리할지 알 수 있도록 데이터를 분류하는 수단입니다. 변수/속성 선언에서 데이터 타입은 항상 변수/속성 이름 앞에 옵니다.

1
string developer = "I am developing cBots, plugins, and indicators!";

또는 var 키워드를 사용하여 데이터 타입을 지정하지 않을 수도 있습니다.

1
var developer = "I am developing cBots, plugins, and indicators!";

객체와 클래스

객체를 유형이 있거나 없는 실체의 추상화로 생각하세요. 이러한 실체는 특정 특성(속성)을 가질 수 있으며 다양한 작업(메서드)을 수행할 수 있습니다. 클래스는 객체 생성을 위한 템플릿 역할을 합니다.

C#은 객체 지향 언어이므로 클래스는 다른 클래스의 속성과 메서드를 상속할 수 있습니다. 다음 예제를 고려해 보세요. 여기서는 Robot 클래스를 상속받는 새로운 NewBot 클래스를 선언합니다. 또한 몇 가지 새로운 클래스 속성을 정의합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* We use the colon sign to designate inheritance.
Additionally, we used expressions in square brackets to
specify the parameters that apply to the entire class */
[Robot(AccessRights = AccessRights.None)]
public class NewBot : Robot
{
    /* We declare a custom class property and make it
    read-only. */
    public string CustomProperty { get; }

    /* In this declaration, we define the default value of a
    custom parameter. */
    [Parameter("BotName", DefaultValue = "Traders First!")]

    /* We declare the BotName property which is changeable via
    the "BotName" parameter. */
    public string BotName { get; }

    /* We also declare the BotComment parameter.
    It can be both read and set. */
    [Parameter("BotComment", DefaultValue = "Our super-duper bot!")]
    public string BotComment { get; set; }
}

데이터 타입

C#은 강력한 타입 언어이므로 변수와 클래스 속성을 선언할 때 데이터 타입을 지정해야 합니다. 간단히 말해, 데이터 타입은 고유한 클래스로 다양한 행동 세트를 가지고 있습니다. 다음 예제에서는 다양한 클래스 속성과 관련 매개변수에 대해 다양한 데이터 타입이 지정됩니다.

1
2
[Parameter("Price")]
public DataSeries Source { get; }

위 코드에서 DataSeries 데이터 타입은 읽기 전용 값 목록을 나타내며, 일반적으로 시장 가격을 나타내는 데 사용됩니다.

다중 옵션 매개변수를 생성해야 할 때는 아래와 같이 내장된 enum 데이터 타입을 사용할 수 있습니다. enum은 다양한 상수를 포함하는 특수 클래스로 생각할 수 있습니다.

1
2
3
4
5
6
7
8
9
public enum Option
{
    First,
    Second,
    Third
}

[Parameter("Option", DefaultValue = Option.Third)]
public Option SelectedOption { get; set; }

enum에 포함된 상수에 액세스하려면 다음 구문을 사용하세요.

1
var newOption = Option.First;

매개변수나 속성이 숫자여야 할 경우, 가장 자주 int(정수) 또는 double(소수) 데이터 타입을 사용하게 됩니다. double 타입은 decimal 타입보다 정밀도가 낮지만 리소스 사용량도 적습니다.

1
2
[Parameter("MA Periods", DefaultValue = 14, MinValue = 1, MaxValue = 20)]
public int Periods { get; set; }

마지막으로 bool 타입은 일반적으로 예 또는 아니오 입력을 나타냅니다. 예와 아니오는 각각 truefalse에 해당합니다.

1
2
[Parameter("Message", DefaultValue = true)]
public bool DisplayChartMessage { get; set; }

네임스페이스

네임스페이스는 클래스의 지정된 컬렉션 역할을 합니다. 클래스를 네임스페이스에 할당하면 나중에 Namespace.ClassName 표기법을 사용하여 액세스할 수 있습니다. 우리는 NewBotCoolTradingBots 네임스페이스에 할당할 것입니다.

1
2
3
4
5
6
7
8
namespace CoolTradingBots
{
    [Robot(AccessRights = AccessRights.None)]
    public class NewBot : Robot
    {
        // ...
    }
}

클래스 메서드

클래스 메서드는 클래스 선언 후에 정의됩니다. NewBot 클래스의 모든 객체는 CustomTradeOperation() 메서드를 호출할 수 있습니다. 이 메서드는 Symbol 객체와 double 객체 두 가지 인수를 취합니다. 우리 메서드는 심볼을 취하고 지정된 볼륨에 대해 어떤 종류의 트레이딩 작업을 실행하도록 설계되었습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
namespace CoolTradingBots
{
    [Robot(AccessRights = AccessRights.None)]
    public class NewBot : Robot
    {
        // ... Defining the class parameters.

        // We declare the CustomTradeOperation method. 
        protected override void CustomTradeOperation(string symbolName, double volume)

        {
            // This space is for declaring the method logic. 
        }
    }
}

클래스 라이브러리

클래스 라이브러리(및 클래스 메서드)는 아래 예제와 같이 네임스페이스로 액세스할 수 있습니다.

1
CoolTradingBots.NewBot.CustomTradeOperation(symbolName, 10000)

또는 코드 시작 부분에 using 키워드를 입력하여 특정 네임스페이스를 지정하고 중복을 피할 수 있습니다. 다음 코드 스니펫을 고려하세요.

1
2
3
using CoolTradingBots;

NewBot.CustomTradeOperation(symbolName, 10000)

조건문

조건문을 사용하려면 키워드 뒤에 괄호 안에 표현식을 사용하세요. 아래 예제에서는 if 키워드를 사용하여 CustomTradingOperation() 메서드를 완료합니다. 이를 위해 Analytics.Actions 네임스페이스에 정의된 EvaluateMarket() 메서드를 사용합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
namespace CoolTradingBots
{
    [Robot(AccessRights = AccessRights.None)]
    public class NewBot : Robot
    {
        // ... Defining the class parameters.

        protected override void CustomTradeOperation(string symbolName, double volume)
        {
            // We declare and initialize the 'result' variable.
            var result = Analytics.Action.EvaluateMarket();

            // We use a conditional statement based on the IsSuccessful property.
            if (result.IsSuccessful)
            {
                Print("Operation successful!")
            }
        }
    }
}

컬렉션

컬렉션은 특정 클래스의 하나 이상의 객체를 저장할 수 있는 컨테이너로 정의됩니다.

컬렉션은 완전히 인덱싱 가능하므로, 특정 integer 값을 대괄호 안에 전달하여 멤버에 액세스할 수 있습니다. 다음 예제에서는 Calculate() 메서드가 인덱스를 통해 액세스한 바의 속성을 출력합니다.

1
2
3
4
5
public override void Calculate(int index)
{
       var bar = Bars[index];
       Print($"{bar.Open} | {bar.High} | {bar.Low} | {bar.Close}");
}

cTrader 확장 기능 만들기

아래 스니펫에서는 위 섹션에서 다룬 지식만을 사용하여 기본 트레이딩 봇을 만드는 방법을 보여줍니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System; 

namespace Spotware.cBots
{
    [Robot(AccessRights = AccessRights.None)]
    public class SuperAwesomeBot : Robot
    {

        /* In the below method, we define the operation(s) that our bot should
        perform when it is launched. */
        protected override void OnStart()
        {
            var result = ExecuteMarketOrder(TradeType.Buy, symbolName, 10000);

            /* We use a conditional statement using the IsSuccessful property of the
            TradeResult object. */
            if (result.IsSuccessful)
            {
                var position = result.Position;
                Print ("Position entry price is {0}", position.EntryPrice);
            } 
        }
    }
}

동기 및 비동기 작업

이전에 언급한 바와 같이, C#은 비동기 작업을 완전히 지원합니다. 아래 다이어그램은 동기 실행에서 트레이딩 활동이 수행되는 기본 예시를 정의합니다.

graph TD
    A(기술적 신호 발견) ==> B(시장가 주문 실행) 
    B ==> C(이익실현/손절매 도달) 
    C ==> D(포지션 청산)
    D ==> A

동기 실행은 중요한 단점을 가지고 있습니다. 위 예시에서, 시장가 주문 실행 작업은 모든 서버 스레드를 완전히 점유하므로, 이 작업이 완료되기 전에 트레이딩 로봇이 다른 작업을 수행할 수 없습니다.

이는 시장 이벤트와 변동성에 빠르게 대응하려는 경우에 이상적이지 않습니다. 이상적으로, 봇은 다양한 작업을 저장하거나 특정 작업을 보류하여 더 긴급한 활동에 참여할 수 있어야 합니다. 우리는 비동기 작업이 수행되는 방식을 더 잘 반영하기 위해 예시를 확장할 것입니다.

graph TD
    A([기술적 신호 발견]) ==> B([매수 주문 실행]) & C([헤징을 위한 매도 주문 실행]) ==> D([주문이 이익실현/손절매에 <br>도달]) 
    D ==> E([포지션 종료])
    E ==> A

위 예시에서, 우리의 트레이딩 봇은 포지션을 헤징하기 위해 양방향으로 동시에 주문을 냈습니다. 동기 작업과 달리, 한 작업이 완료될 때까지 기다릴 필요 없이 다른 작업을 진행할 수 있습니다. 이는 효율적이고 신뢰할 수 있는 트레이딩 로봇을 만들기 위한 개발자의 기회를 크게 확장합니다.

비동기 실행은 멀티스레딩과 다릅니다:

  • 비동기 실행에서는 모든 작업이 동일한 스레드에서 시작됩니다. 작업이 저장되거나 보류되면 이 스레드를 해제하고, 나중에 실행이 계속될 때 스레드 풀에서 선택된 다른 스레드에서 실행됩니다.
  • 멀티스레딩에서는 모든 작업이 다른 스레드에서 시작되고 초기 스레드에서 실행을 계속합니다. 스레드 셔플링은 없습니다.

cTrader는 여러분의 메서드를 병렬로 호출하지 않으므로 멀티스레딩 문제에 대해 걱정할 필요가 없습니다.

Image title