.NET 5 & C#

This page will guide you through the steps to backtest your strategy locally in .NET 5 & C#

Let us do a small backtest with our strategy locally, to ensure our algo logic is working and behaving as expected.

Preparing your Main class for testing and live deployment scenarios

Once your strategy class is ready, now is the time to use it to test and deploy your strategy with Algorum. As mentioned in the Code your first strategy section, your strategy class will receive some parameters, which you need to pass in if you are testing/debugging your strategy class locally. As you can see in the below Main method code of your Program.cs file, we are reading those parameters from the configuration settings. These settings will be filled in for you during the backtesting and live deployment of the strategy in Algorum Cloud. But for local testing and debugging you need to have these in the appsettings.json file.

using System;
using System.Threading.Tasks;
using Algorum.Quant.Types;
using Microsoft.Extensions.Configuration;

namespace Algorum.Strategy.GoldenCrossover
{
   class Program
   {
      static async Task Main( string[] args )
      {
         // Get the url to connect from the arguments.
         // This will be local url when deployed on Algorum cloud.
         // For local debugging and testing, you can connect to the remote Algorum cloud, without deploying.
         var configBuilder = new ConfigurationBuilder().AddJsonFile( "appsettings.json" ).AddEnvironmentVariables();
         var config = configBuilder.Build();

         string url = config.GetValue<string>( "url" );
         string apiKey = config.GetValue<string>( "apiKey" );
         string userId = config.GetValue<string>( "userId" );
         string sid = config.GetValue<string>( "sid" );
         string bkApiKey = config.GetValue<string>( "bkApiKey" );
         string bkApiSecretKey = config.GetValue<string>( "bkApiSecretKey" );
         string clientCode = config.GetValue<string>( "clientCode" );
         string password = config.GetValue<string>( "password" );
         string twoFactorAuth = config.GetValue<string>( "twoFactorAuth" );
         BrokeragePlatform brokeragePlatform = config.GetValue<BrokeragePlatform>( "brokeragePlatform" );
         int samplingTime = config.GetValue<int>( "samplingTime" );
         StrategyLaunchMode launchMode = config.GetValue<StrategyLaunchMode>( "launchMode" );

         url += $"?sid={sid}&apiKey={apiKey}&launchMode={launchMode}";

         // Create our strategy object
         var strategy = await GoldenCrossoverQuantStrategy.GetInstanceAsync( url, apiKey, launchMode, sid, userId );

         // If we are started in backtestign mode, start the backtest
         if ( launchMode == StrategyLaunchMode.Backtesting )
         {
            DateTime startDate = DateTime.ParseExact( config.GetValue<string>( "startDate" ), "dd-MM-yyyy", null );
            DateTime endDate = DateTime.ParseExact( config.GetValue<string>( "endDate" ), "dd-MM-yyyy", null );

            await strategy.BacktestAsync( new BacktestRequest()
            {
               StartDate = startDate,
               EndDate = endDate,
               ApiKey = bkApiKey,
               ApiSecretKey = bkApiSecretKey,
               SamplingTimeInSeconds = samplingTime,
               BrokeragePlatform = brokeragePlatform
            } );
         }
         else
         {
            // Else, start trading in live or paper trading mode as per the given launchMode
            await strategy.StartTradingAsync( new TradingRequest()
            {
               ApiKey = bkApiKey,
               ApiSecretKey = bkApiSecretKey,
               ClientCode = clientCode,
               Password = password,
               TwoFactorAuth = twoFactorAuth,
               SamplingTimeInSeconds = samplingTime,
               BrokeragePlatform = brokeragePlatform
            } );
         }

         // Wait until our strategy is stopped
         strategy.Wait();
      }
   }
}

appsettings.json file parameters

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "apiKey": "<Your Algorum API key>",
  "url": "wss://india-quant-engine-api.algorum.net/quant/engine/api/v1",
  "startDate": "10-03-2021",
  "endDate": "27-04-2021",
  "launchMode": "backtesting",
  "sid": "<Any unique string>",
  "bkApiKey": "<Your Alpaca Brokerage API Key>",
  "bkAPiSecretKey": "<Your Alpaca Brokerage API Secret Key>",
  "clientCode": "<Your brokerage client code or user id>",
  "password": "<Your brokerage account password>",
  "twoFactorAuth": "<Your brokerage account two factor authentication code>",
  "samplingTime": 15,
  "brokeragePlatform": "Alpaca"
}

Strategy Parameters

We have read about apiKey, url, launchMode and sid parameters in the Code your first strategy section. The other parameters that you need for your strategy are described below.

Parameter NameDescription
bkApiKeyThis parameter is your brokerage API key. Applies mostly to the brokerages that provide access to their API using an API Key and Secret Key mechanism. Typical example is Alpaca Brokerage users.
bkApiSecretKeyThis parameter is your brokerage API Secret key. Applies mostly to the brokerages that provide access to their API using an API Key and Secret Key mechanism. Typical example is Alpaca Brokerage users.
clientCodeThis parameter is your brokerage account user id or client code, where applicable. Typically used only for live trading for Indian users. Not applicable for Alpaca Brokerage users.
passwordThis parameter is you brokerage account password, where applicable. Typically used only for live trading for Indian users. Not applicable for Alpaca Brokerage users.
twoFactorAuthThis parameter is your brokerage account two factor authentication code, where applicable. Typically used only for live trading for Indian users. Not applicable for Alpaca Brokerage users.
samplingTimeThis parameter is the sampling time, which indicates a period in seconds. Your strategy will receive the tick data from the Algorum Cloud at the interval of the given period in seconds. So if the value of samplingTime is 1, your strategy will receive ticks accumulated for each second. If you want to receive every tick from Algorum Cloud, then set this parameter value to 0.
brokeragePlatformThis parameter indicates the brokerage your strategy would run on. For USA users this is always Alpaca for now. For India users, this will be brokerage of the user, from the list of brokerages supported by Algorum.

Security of your strategy parameters

We understand that your strategy parameters are sensitive and need to be protected when deploying onto the Algorum Cloud. While running your strategy locally, you can use whatever means that best suites you to secure these parameters. While deploying the strategy to Algorum cloud, you need not embed these parameters in your code or in appsettings.json file. You can pass them directly to the Algorum CLI commands (backtest or paper/live trading), which encrypts them and makes them available to your strategy while running in Algorum Cloud. None of these parameters are stored permanently in any of our databases.

Create an instance of your strategy

Once you understand about the strategy parameters, you have to now create an instance of your strategy and initialize it with required parameters, which include your Algorum Quant Engine IP, your Algorum API Key, Launch Mode and Strategy Identifier.

url += $"?sid={sid}&apiKey={apiKey}&launchMode={launchMode}";

// Create our strategy object
var strategy = await GoldenCrossoverQuantStrategy.GetInstanceAsync( url, apiKey, launchMode, sid );

Backtesting locally

Once you are ready with your parameters and instantiated your strategy class, as you can see in the main method, you can initiate the backtest using QuantEngineClient backtest method, by setting your launchMode parameter to a value of backtesting in your main method. If you want to preload the indicator data, you can override the backtest method in your strategy class, and use QuantEngineClient preload_candles method. Below is how you can override the backtest method.

/// <summary>
/// Backtest this strategy
/// </summary>
/// <param name="backtestRequest">BacktestRequest object</param>
/// <returns>Backtest id</returns>
public override async Task<string> BacktestAsync( BacktestRequest backtestRequest )
{
   // Preload candles
   await _indicatorEvaluator.PreloadCandlesAsync( 210, backtestRequest.StartDate.AddDays( 1 ), backtestRequest.ApiKey, backtestRequest.ApiSecretKey );

   // Run backtest
   return await base.BacktestAsync( backtestRequest );
}

To start the backtesting you need to pass in the BacktestRequest object with backtest date range, Api keys (for USA users), etc.,. As the backtesting starts, your strategy OnTickAsync method will start receiving the ticks from the backtest period and you can test and debug your strategy logic accordingly.

// If we are started in backtestign mode, start the backtest
if ( launchMode == StrategyLaunchMode.Backtesting )
{
   DateTime startDate = DateTime.ParseExact( config.GetValue<string>( "startDate" ), "dd-MM-yyyy", null );
   DateTime endDate = DateTime.ParseExact( config.GetValue<string>( "endDate" ), "dd-MM-yyyy", null );

   await strategy.BacktestAsync( new BacktestRequest()
   {
      StartDate = startDate,
      EndDate = endDate,
      ApiKey = bkApiKey,
      ApiSecretKey = bkApiSecretKey,
      SamplingTimeInSeconds = samplingTime,
      BrokeragePlatform = brokeragePlatform
   } );
}