C# for algorithmic trading
What is C#
C# is an object-oriented programming language used to build a wide range of services and applications. Its clear, structured syntax makes it easy to learn and read, even for beginners, while providing a structured approach to writing compact and reusable code.
C# algorithms in one minute!
- Even if you have never worked with C# before, creating and deploying your first bot or indicator takes only a couple of minutes.
- As C# is easy to use, you can quickly rewrite any code snippets from this documentation to suit your needs.
- When using C#, 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.
- In C#, you can write code that do not block server threads upon execution. In other words, you can start certain tasks simultaneously with other tasks.
What is .NET?
For C# programs to run, their source code must be compiled into Intermediate Language (IL). This IL code follows the Common Language Infrastructure (CLI) specification and is later compiled into native machine instructions at runtime. The .NET framework provides a virtual execution environment built on extensive class libraries and the Common Language Runtime (CLR), Microsoft's implementation 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: When adding new data types or collections to your code, you will often find that .NET already offers suitable classes and methods for the task.
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 | |
Alternatively, you can use the var keyword to avoid specifying a data type.
1 | |
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 | |
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 | |
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 enum's as special classes that contain various constants.
1 2 3 4 5 6 7 8 9 | |
To access a constant contained in an enum, use the following syntax.
1 | |
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 | |
Lastly, the bool type typically represents a yes or no input. The values yes and no correspond to true and false, respectively.
1 2 | |
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 | |
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 | |
Class libraries
Class libraries (and, subsequently, class methods) can be accessed by their namespace as shown in the below example.
1 | |
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 | |
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 | |
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 | |
Create cTrader extensions
In the below snippet, we create a basic trading bot 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 | |
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 trading robot 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 bot 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 trading bot 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 trading robots.
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.
