Skip to content

C# and .NET Basics

What Is C#

C# is an object-oriented programming language that can be used to build various types of extensions and applications. As an object-oriented language, C# is easy to learn and read even if you have no prior programming experience. It also provides a structured means for writing compact and reusable code.

C# is our language of choice for cBots and indicators because of the following reasons.

  • Shallow learning curve. Even if you have never worked with C# before, creating and deploying your first bot or an indicator should only take a couple of minutes.
  • Language efficiency. As C# is easy to use, you can quickly rewrite any code snippets from this documentation to suit your needs.
  • Extensive class library. When using C# and .NET, you can access a large number of libraries containing predefined classes and methods. These methods can efficiently handle common tasks, leaving you free to solve complex trading problems.
  • Support of concurrent operations. In C#, you can write code that does not block server threads upon execution. In other words, you can start certain tasks simultaneously with other tasks.

To be executed, C# source code has to be compiled into an Intermediary Language (IL). IL code, in turn, is necessary to ensure compliance with the international Common Language Infrastructure (CLI) specification. During execution, IL code is compiled into a set of native machine instructions.

What Is .NET

.NET is a virtual execution system based on a number of class libraries and the Common Language Runtime, Microsoft's interpretation of the CLI. Without going into technical intricacies, .NET fulfills the following functions.

  • Facilitating the development and execution of applications. The .NET SDK already contains several built-in compilers and build engines, eliminating the need for creating any custom solutions.
  • Providing runtime libraries. If you would like to add certain data types or collections to your code, there is a high chance that .NET already has the corresponding set of classes and methods.

In our opinion, .NET is the perfect environment for creating and running trading bots or indicators.

The Basics of C#

Data Types and Variable Declarations

Data types are a means of categorising data so that C# knows exactly how to treat variables and properties. In variable/property declarations, data types always precede the variable/property name.

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

Alternatively, you can use the var keyword to avoid specifying a data type.

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

Objects and Classes

Think of objects as abstractions of tangible or intangible entities. These entities can have certain characteristics (properties) and can perform various operations (methods). In turn, classes serve as templates for object creation.

As C# is an object-oriented language, classes can also inherit properties and methods from other classes. Consider the following example in which we declare a new NewBot class that inherits from the Robot class. We also define some new class properties.

 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; }
}

Data Types

As C# is a strongly-typed language, it is necessary to specify data types when declaring variables and class properties. In brief, data types constitute unique classes with different sets of behaviors. Consider the following examples in which various data types are specified for different class properties and related parameters.

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

In the above code, the DataSeries data type represents a read-only list of values and is, therefore, typically used to represent market prices.

When there is a need to create multi-option parameters, you can use the built-in enum data type as detailed below. You can think of enums as special classes that contain various constants.

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; }

To access a constant contained in an enum, use the following syntax.

1
var newOption = Option.First;

If a parameter or a property needs to be a number, you will most often use the int (integer) or double (decimal) data types. While the double type has less precision than the decimal type, it is also less resource-intensive.

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

Last but not least, the bool type typically represents a 'Yes/No' input. The values 'Yes' and 'No' correspond to true and false, respectively.

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

Namespaces

Namespaces act as designated collections of classes. Once you assign a class to a namespace, it can later be accessed by using the Namespace.ClassName notation. We will assign our NewBot to the CoolTradingBots namespace.

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

Class Methods

Class methods are defined following class declaration. All objects of our NewBot class will be able to call the CustomTradeOperation() method. It takes two arguments, namely a Symbol object, and a double object. Our method is supposed to take a symbol and execute some sort of trading action for the specified volume

 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. 
        }
    }
}

Class Libraries

Class libraries (and, subsequently, class methods) can be accessed by their namespace as shown in the below example.

1
CoolTradingBots.NewBot.CustomTradeOperation(symbolName, 10000)

Alternatively, you can type in the using keyword at the beginning of your code to designate certain namespaces and avoid redundancies. Consider the following code snippet.

1
2
3
using CoolTradingBots;

NewBot.CustomTradeOperation(symbolName, 10000)

Conditional Statements

To use conditional statements, use a keyword followed by an expression in round brackets. The below example uses the if keyword to finalize our CustomTradingOperation() method. To do so, we use the EvaluateMarket() method which is defined in the Analytics.Actions namespace.

 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!")
            }
        }
    }
}

Collections

Collections are defined as 'containers' that can store one or more objects of a particular class.

Collections are fully indexable, meaning that their members can be accessed by passing a certain integer value in square bracers. Consider the following example in which the Calculate() method prints the properties of a bar accessed via its index.

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

Creating cTrader Extensions

In the below snippet, we create a basic cBot using only the knowledge covered in the above sections.

 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);
            } 
        }
    }
}

Synchronous and Asynchronous Operations

As stated previously, C# fully supports asynchronous operations. The diagram below defines a basic example of how trading activities are performed in synchronous execution.

graph TD
    A(Encounter a Technical Signal) ==> B(Execute a Market Order)
    B ==> C(Take Profit/Stop Loss Reached)
    C ==> D(Close the Position)
    D ==> A

Synchronous execution carries one important drawback. In the above example, the action of executing a market order fully occupies all server threads, meaning that your cBot is unable to do anything else before this operation is completed.

This is less than ideal when you want to quickly react to market events and volatility. Ideally, your cBot should be able to 'store' various actions or put certain tasks 'on hold' to engage in other, more pressing activities. We will expand our example to better reflect how asynchronous operations are performed.

graph TD
    A([Encounter a Technical Signal]) ==> B([Place a 'Buy' Order]) & C([Place a 'Sell' Order for Hedging]) ==> D([An Order Hits Its Take Profit/Stop Loss])
    D ==> E([Close the Position])
    E ==> A

In the above example, our cBot simultaneously placed orders in both directions for hedging its positions. In contrast to synchronous operations, there is no need to wait for one operation to complete before proceeding with another one. This significantly expands developers' opportunities for creating efficient and reliable cBots.

Note that asynchronous execution is different from multi-threading.

  • In asynchronous execution, all tasks are started on the same thread. When they are 'stored' or 'put on hold', they free this thread and, when their execution continues later, it occurs on a different thread chosen from the thread pool.
  • When multi-threading, all tasks start on different threads and continue their execution on their initial threads. There is no thread 'shuffling'.

cTrader never invokes your methods in parallel so you do not have to worry about multi-threading issues.