Futures Contracts Expiry Date Handling in Algorum.

When your algo is dealing with Futures, you need to specify the correct futures contract name as the symbol ticker to trade. This introduces challenges in backtesting, as the algo has to have the logic of changing the futures contract name per each month of backtesting.

Algorum allows you to specify the futures contract in a more generic way, based on the fact that at any point of time there are only three futures contracts trading on NSE. Current Month, Mid Month and Far Month. And since the futures are based on a underlying asset or index, Algorum allows you to specify a futures contract using the underlying asset/index ticker symbol, along with the number of the month of the contract that your strategy want to deal with. For instance below code snippet shows how to subscribe for NIFTY futures for various monthly contracts.

The value of 0 for FNOMonth property of Symbol object indicates current month contract, value of 1 indicates mid month contract and value of 2 indicates far month contract. For instance in the above code snippet, if the current month is Jan 2022, the current month symbol indicates NIFTY futures for Jan 2022, mid month symbol indicates NIFTY futures for Feb 2022 and far month symbol indicates NIFTY futures for Mar 2022.

Auto handling of monthly expiry dates during Backtesting

The monthly futures contracts expire every Thursday of the month, and if Thursday is a holiday, a day before that (and goes back in time based on holidays). For instance, Jan 2022 NIFTY futures expire on Jan 27, 2022 (Thursday). So on Jan 28, the current month futures will become Feb 2022 futures. Algorum backtesting, paper trading and live trading engines takes care of this changes and will stream the correct tick data from correct futures contract to your strategy when the expiry happens every month.

Slippage Modelling during Backtesting and Paper Trading in Algorum.

Slippage is the most important factor that effects the profit & loss of your strategy, if your strategy depends on Market orders. When you place a Market order, there are two factors that affect slippage, Time (Latency) and Market Depth. Take an example scenario where your strategy determines that it is time to place an order, where the stock that you are monitoring is at a price 20. Now when you place an order, the order might reach the stock exchange (NSE for India, and others like NASDAQ, NYSE, Market Venues for USA) within a second. Within this second, the price might have changed from to 21 or 19. If you are on BUY side, and if the price has changed to 21, this means you would have bought the stock at a higher price than desired. And if the price has changed to 19, this means you would have bought the stock at lesser price than desired. But in all probability you would end up buying stock at 21 price (greater price), as the ASK side of the market (sellers) always quotes prices higher than the current stock price. And in Market order scenario your BUY order will be filled from the ASK side of the Market.

Algorum provides two slippage models to try and simulate the Slippage in your backtesting, so you can understand the effect of slippage in your strategy.

Basis Points Slippage (BPS) Model

In BPS model, you can specify number of basis points your order price would be moving away from when you are placing a Market order. When you are placing a BUY order, the current tick price of your stock symbol will be increased by the specified basis points. When you are placing a SELL order, the current tick price of your stock symbol will be decreased by the specified basis points. This will reduce your profit, but is helpful to understand how your strategy P&L is effected in a slippage scenario.

Time Slippage Model

In Time slippage model, you can specify a delay in milliseconds, where this delay is used to get the next closest tick for your symbol. So if you placed order at 9.15 AM 30 Seconds, and specify a Time slippage of 1000 milliseconds, Algorum will fill your order with stock price at 9.15 AM 31 Seconds, from the historic data. This will essentially simulate the time delay between your strategy identifying the price to place an order and the actual time at which the order is sent to the stock exchange for filling. This may result in increase or decrease of profit based on whether the price has increased or decreased after the slippage time.

Specifying Slippage while placing an order

You can specify the slippage type (SlippageType (Enumeration)) and slippage value while placing the order in the PlaceOrderRequest object, as shown below.


var placeOrderRequest = new PlaceOrderRequest()
{
   OrderType = OrderType.Market,
   Price = tickData.LTP,
   Quantity = qty,
   Symbol = _symbol,
   Timestamp = tickData.Timestamp,
   TradeExchange = ( LaunchMode == StrategyLaunchMode.Backtesting || LaunchMode == StrategyLaunchMode.PaperTrading ) ? TradeExchange.PAPER : TradeExchange.NSE,
   TriggerPrice = tickData.LTP,
   OrderDirection = OrderDirection.Sell,
   Tag = _state.CurrentOrderId,
   SlippageType = SlippageType.TIME,
   Slippage = 1000,
   OrderProductType = OrderProductType.Intraday
};

await PlaceOrderAsync( placeOrderRequest );

Algorum – A Modern Approach to Algo Trading for Quant Developers.

Algorum is an Algo Trading SaaS platform, which allows Quant/Algo Developers to Develop Test, Deploy and Monitor Algo Trading Strategies without managing any Software, Hardware and Data infrastructure.

Though Algo Trading is prevalent in India (~50% of the stock trading volume is algorithmic), today Algo/Quant Developers have several challenges in implementing the Algo Strategies. They have to purchase costly historical and real-time stock datasets, build their own backtesting engine and analytical libraries or rely on very limited opens source and not so well-maintained libraries, develop different code paths in their algo code to work in backtesting, paper trading and real trading scenarios, manage dedicated machines/VMs for trading and build monitoring tools to manage and monitor their algo trading strategies. These steps are complex and add up to months of development and testing for Algo/Quant Developers. At Algorum we built a SaaS platform that removes these pain points and provides all these capabilities through its cloud platform, and makes Algo Trading less time consuming, easy and affordable for the Algo/Quant Developers.

Algorum supports India Capital Markets (Cash, F&O) through our integrated brokerage, Northeast Ltd, and USA Capital Markets (Equities) through Alpaca Brokerage.

Algorum comes with comprehensive documentation (https://docs.algorumsoftware.com/), which guides Quant/Algo Developers in every step of their Algo Strategy development, testing, deployment and monitoring. Algorum has a growing set of sample algo strategies that demonstrates different trading techniques and Algorum capabilities.

Let us see how Algorum can help Quant Developers in their journey of developing, testing, deploying and monitoring their Algo Trading strategies.

Algorum CLI

Algorum CLI (Command Line Interface) is a multi-platform command line tool that helps Quant/Algo Developers develop, test, monitor and manage algo strategies in Algorum Platform. This tool runs on Windows, Linux and Mac OSX, and is extremely simple to use command line tool with full help and interactive commands to guide you through input steps in each command.

Local Development & Cloud Deployment

With Algorum, Quant/Algo Developers can develop, debug and test their Algo Strategy locally on their system, right from within their favourite IDEs, like VS Code, Visual Studio, PyCharm, etc. Once they are ready with a working Algo Strategy, they can deploy it onto Algorum Cloud infrastructure for reliable and efficient execution and monitoring. This eliminates the inefficiencies of relying on logs from remote executions during development phases, to the most extent possible.

Fully Managed Deployment of Strategies with Dedicated Containers

Algorum takes care of complete deployment and running of algo strategies within our AWS Cloud in dedicated containers. Quant/Algo Developers just have to use our Algorum CLI (Command Line Interface) tool to upload their algo strategy docker image and Algorum takes care of running it on our cloud infrastructure This provides highest security and reliability for their strategies. This also allows the developers to build their algo strategies in whatever framework and language they want and choose the type of the strategies they want to build like Technical Analysis based, Machine Learning based, etc.,

Technical Analysis Libraries

Algorum comes with built in Technical Analysis libraries with over 50+ Indicators, 25+ Candlestick Patterns. We are working to add more to this library to help Quant/Algo Developers with Chart Patterns and AI models as well. This will help them focus on building strategies rather than building and testing indicator logic. These indicators are available to algo strategies being developed in any programming language supported by Algorum.

Managed Logging

Algorum lets Quant/Algo Developers log important information related to their quant strategy while it is running, to our cloud storage using a simple method call. This helps them analyze the strategy for various aspects in real time using Algorum CLI log streaming, and off line analysis using the downloadable log messages.

Strategy State Store

Algorum lets Quant/Algo Developers save the state of their strategy at any point in time of its execution to a fully backed up cloud store. This helps them reload their strategy with same state in case of any unforeseen failure of the cloud infrastructure. Any existing state is passed on to the strategy when it starts running again. The state can also be used to store the trades and other information in their running strategy, which can then be viewed and analysed from our web and mobile apps.

Fully Integrated Brokerage Solution

We are tightly integrated into our partner Broker infrastructure, which provides end-to-end Algo Trading experience and solution. Quant/Algo Developers can develop, test, deploy and monitor their Algo Trading strategies while utilizing our partner Broker for their Brokerage account. This also allows us to provide unparalleled low-latency infrastructure for data streamed to Algo and for the orders generated by the Algos. We look forward to the Quant/Algo Developers to explore Algorum Platform. We have webinars being conducted twice every week to help Quant/Algo Developers get started with developing their algo strategies using Algorum Platform. Register for our webinars here (https://app.livestorm.co/algorum-software-private-limited/algorum-quant-platform-launch?type=detailed) and get started.

.NET 5 and C#. Glimpse of creating a quant strategy in Algorum

No crap! Just a piece of code to create a simple RSI based quant strategy in Algorum using .NET 5 and C#. You get technical indicator evaluation, logging, state management, real time data ticks, order status handling, all in just few lines of code. And your strategy code is abstracted from underlying broker API, and runs both in paper trading (virtual money) mode and actual broker live mode. Enjoy this for now!


using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Algorum.Quant.Types;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Algorum.Strategy.SupportResistance
{
   /// 
   /// Strategy classes should derive from the QuantEngineClient, 
   /// and implement the abstract methods to receive events like 
   /// tick data, order update, etc.,
   /// 
   public class RSIStrategy : QuantEngineClient
   {
      private class State
      {
         public bool Bought;
         public TickData LastTick;
         public TickData CurrentTick;
         public List Orders;
         public string CurrentOrderId;
         public Order CurrentOrder;
         public CrossBelow CrossBelowObj;
         public CrossAbove CrossAboveObj;
         public bool DayChanged;
      }

      public const double Capital = 1000000;
      private const double Leverage = 8; // 8x Leverage on margin by Brokerage

      private Symbol _symbol;
      private IIndicatorEvaluator _indicatorEvaluator;
      private State _state;

      /// 
      /// Helps create strategy class object class and initialize asynchornously
      /// 
      /// URL of the Quant Engine Server
      /// User Algorum API Key
      /// Launch mode of this strategy
      /// Unique Strategy Id
      /// User unique id
      /// Instance of RSIStrategy class
      public static async Task GetInstanceAsync(
         string url, string apiKey, StrategyLaunchMode launchMode, string sid, string userId )
      {
         var strategy = new RSIStrategy( url, apiKey, launchMode, sid, userId );
         await strategy.InitializeAsync();
         return strategy;
      }

      private RSIStrategy( string url, string apiKey, StrategyLaunchMode launchMode, string sid, string userId )
         : base( url, apiKey, launchMode, sid, userId )
      {
         // No-Op
      }

      private async Task InitializeAsync()
      {
         // Load any saved state
         _state = await GetDataAsync( "state" );

         if ( ( _state == null ) || ( LaunchMode == StrategyLaunchMode.Backtesting ) )
         {
            _state = new State();
            _state.Orders = new List();
            _state.CrossBelowObj = new CrossBelow();
            _state.CrossAboveObj = new CrossAbove();
            _state.DayChanged = false;
         }

         // Create our stock symbol object
         // For India users
         _symbol = new Symbol() { SymbolType = SymbolType.FuturesIndex, Ticker = "NIFTY" };

         // For USA users
         //_symbol = new Symbol() { SymbolType = SymbolType.Stock, Ticker = "AAPL" };

         // Create the technical indicator evaluator that can work with minute candles of the stock
         // This will auto sync with the new tick data that would be coming in for this symbol
         _indicatorEvaluator = await CreateIndicatorEvaluatorAsync( new CreateIndicatorRequest()
         {
            Symbol = _symbol,
            CandlePeriod = CandlePeriod.Minute,
            PeriodSpan = 60
         } );

         // Subscribe to the symbols we want (one second tick data)
         await SubscribeSymbolsAsync( new List
         {
            _symbol
         } );
      }

      /// 
      /// Called when there is an update on a order placed by this strategy
      /// 
      /// Order object
      /// Async Task
      public override async Task OnOrderUpdateAsync( Order order )
      {
         // Process only orders initiated by this strategy
         if ( string.Compare( order.Tag, _state.CurrentOrderId ) == 0 )
         {
            switch ( order.Status )
            {
            case OrderStatus.Completed:

               lock ( _state )
                  _state.Orders.Add( order );

               if ( order.OrderDirection == OrderDirection.Buy )
               {
                  _state.Bought = true;
                  _state.CurrentOrder = order;

                  // Log the buy
                  var log = $"{order.OrderTimestamp}, Order Id {order.OrderId}, Bought {order.FilledQuantity} units of {order.Symbol.Ticker} at price {order.AveragePrice}";
                  await LogAsync( LogLevel.Information, log );

                  // DIAG::
                  Console.WriteLine( log );
               }
               else
               {
                  _state.Bought = false;
                  _state.CurrentOrder = null;

                  // Log the sell
                  var log = $"{order.OrderTimestamp}, Order Id {order.OrderId}, Sold {order.FilledQuantity} units of {order.Symbol.Ticker} at price {order.AveragePrice}";
                  await LogAsync( LogLevel.Information, log );

                  // DIAG::
                  Console.WriteLine( log );
               }

               _state.CurrentOrderId = string.Empty;

               var stats = GetStats( _state.CurrentTick );
               await SendAsync( "publish_stats", stats );

               foreach ( var kvp in stats )
                  Console.WriteLine( $"{kvp.Key}: {kvp.Value}" );

               break;
            default:
               // Log the order status
               {
                  var log = $"{order.OrderTimestamp}, Order Id {order.OrderId}, Status {order.Status}, Message {order.StatusMessage}";
                  await LogAsync( LogLevel.Information, log );

                  // DIAG::
                  Console.WriteLine( log );
               }

               break;
            }

            // Store our state
            await SetDataAsync( "state", _state );
         }
      }

      /// 
      /// Called on every tick of the data
      /// 
      /// TickData object
      /// Async Task
      public override async Task OnTickAsync( TickData tickData )
      {
         try
         {
            var prevTick = _state.CurrentTick;
            _state.CurrentTick = tickData;

            if ( prevTick == null || ( !_state.DayChanged && tickData.Timestamp.Day > prevTick.Timestamp.Day && !_state.Bought ) )
            {
               _state.DayChanged = true;
            }

            // Get the RSI value
            var rsi = await _indicatorEvaluator.RSIAsync( 14 );
            var (direction, strength) = await _indicatorEvaluator.TRENDAsync( 14 );

            if ( ( _state.LastTick == null ) || ( tickData.Timestamp - _state.LastTick.Timestamp ).TotalMinutes >= 1 )
            {
               await LogAsync( LogLevel.Debug, $"{tickData.Timestamp}, {tickData.LTP}, rsi {rsi}, direction {direction}, strength {strength}" );
               _state.LastTick = tickData;
            }

            // We BUY the stock when the RSI value crosses below 30 (oversold condition)
            if (
               rsi > 0 && _state.CrossBelowObj.Evaluate( rsi, 15 ) &&
               direction == 2 && strength >= 10 &&
               ( !_state.Bought ) && ( string.IsNullOrWhiteSpace( _state.CurrentOrderId ) ) )
            {
               _state.DayChanged = false;

               // Place buy order
               _state.CurrentOrderId = Guid.NewGuid().ToString();
               var qty = Math.Floor( Capital / tickData.LTP ) * Leverage;

               await PlaceOrderAsync( new PlaceOrderRequest()
               {
                  OrderType = OrderType.Market,
                  Price = tickData.LTP,
                  Quantity = qty,
                  Symbol = _symbol,
                  Timestamp = tickData.Timestamp,
                  TradeExchange = ( LaunchMode == StrategyLaunchMode.Backtesting || LaunchMode == StrategyLaunchMode.PaperTrading ) ? TradeExchange.PAPER : TradeExchange.NSE,
                  TriggerPrice = tickData.LTP,
                  OrderDirection = OrderDirection.Buy,
                  SlippageType = SlippageType.TIME,
                  Slippage = 1000,
                  Tag = _state.CurrentOrderId
               } );

               // Store our state
               await SetDataAsync( "state", _state );

               // Log the buy initiation
               var log = $"{tickData.Timestamp}, Placed buy order for {qty} units of {_symbol.Ticker} at price (approx) {tickData.LTP}, {tickData.Timestamp}";
               await LogAsync( LogLevel.Information, log );

               // DIAG::
               Console.WriteLine( log );
            }
            else if ( _state.CurrentOrder != null )
            {
               if ( (
                     ( tickData.LTP - _state.CurrentOrder.AveragePrice >= _state.CurrentOrder.AveragePrice * 0.1 / 100 ) ||
                     tickData.Timestamp.Hour >= 15 )
                     &&
                  ( _state.Bought ) )
               {
                  await LogAsync( LogLevel.Information, $"OAP {_state.CurrentOrder.AveragePrice}, LTP {tickData.LTP}" );

                  // Place sell order
                  _state.CurrentOrderId = Guid.NewGuid().ToString();
                  var qty = _state.CurrentOrder.FilledQuantity;

                  await PlaceOrderAsync( new PlaceOrderRequest()
                  {
                     OrderType = OrderType.Market,
                     Price = tickData.LTP,
                     Quantity = qty,
                     Symbol = _symbol,
                     Timestamp = tickData.Timestamp,
                     TradeExchange = ( LaunchMode == StrategyLaunchMode.Backtesting || LaunchMode == StrategyLaunchMode.PaperTrading ) ? TradeExchange.PAPER : TradeExchange.NSE,
                     TriggerPrice = tickData.LTP,
                     OrderDirection = OrderDirection.Sell,
                     SlippageType = SlippageType.TIME,
                     Slippage = 1000,
                     Tag = _state.CurrentOrderId
                  } );

                  _state.CurrentOrder = null;

                  // Store our state
                  await SetDataAsync( "state", _state );

                  // Log the sell initiation
                  var log = $"{tickData.Timestamp}, Placed sell order for {qty} units of {_symbol.Ticker} at price (approx) {tickData.LTP}, {tickData.Timestamp}";
                  await LogAsync( LogLevel.Information, log );

                  // DIAG::
                  Console.WriteLine( log );
               }
            }
         }
         catch ( Exception ex )
         {
            await LogAsync( LogLevel.Error, ex.ToString() );

            // DIAG::
            Console.WriteLine( ex );
         }
         finally
         {
            await SendProgressAsync( tickData );
         }
      }

      /// 
      /// Called on custom events passed on by users to the strategy
      /// 
      /// Custom event data string
      /// Async Task
      public override async Task OnCustomEventAsync( string eventData )
      {
         await LogAsync( LogLevel.Information, eventData );
         return string.Empty;
      }

      /// 
      /// Start trading
      /// 
      /// TradingRequest object
      /// 
      public override async Task StartTradingAsync( TradingRequest tradingRequest )
      {
         // Preload candles
         await _indicatorEvaluator.PreloadCandlesAsync( 210, DateTime.UtcNow, tradingRequest.ApiKey, tradingRequest.ApiSecretKey );

         await base.StartTradingAsync( tradingRequest );
      }

      /// 
      /// Backtest this strategy
      /// 
      /// BacktestRequest object
      /// Backtest id
      public override async Task BacktestAsync( BacktestRequest backtestRequest )
      {
         // Preload candles
         await _indicatorEvaluator.PreloadCandlesAsync( 20, backtestRequest.StartDate.AddDays( 1 ), backtestRequest.ApiKey, backtestRequest.ApiSecretKey );

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

      public override Dictionary GetStats( TickData tickData )
      {
         var statsMap = new Dictionary();

         statsMap["Capital"] = Capital;
         statsMap["Order Count"] = _state.Orders.Count;

         double buyVal = 0;
         double sellVal = 0;
         double buyQty = 0;
         double sellQty = 0;

         foreach ( var order in _state.Orders )
         {
            if ( ( order.Status == OrderStatus.Completed ) && ( order.OrderDirection == OrderDirection.Buy ) && order.Symbol.IsMatch( tickData ) )
            {
               buyVal += order.FilledQuantity * order.AveragePrice;
               buyQty += order.FilledQuantity;
            }

            if ( ( order.Status == OrderStatus.Completed ) && ( order.OrderDirection == OrderDirection.Sell ) && order.Symbol.IsMatch( tickData ) )
            {
               sellVal += order.FilledQuantity * order.AveragePrice;
               sellQty += order.FilledQuantity;
            }
         }

         if ( sellQty < buyQty )
            sellVal += ( buyQty - sellQty ) * tickData.LTP;

         double pl = sellVal - buyVal;
         statsMap["PL"] = pl;
         statsMap["Portfolio Value"] = Capital + pl;

         return statsMap;
      }
   }
}

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

namespace Algorum.Strategy.SupportResistance
{
   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( "url" );
         string apiKey = config.GetValue( "apiKey" );
         string userId = config.GetValue( "userId" );
         string sid = config.GetValue( "sid" );
         string bkApiKey = config.GetValue( "bkApiKey" );
         string bkApiSecretKey = config.GetValue( "bkApiSecretKey" );
         string clientCode = config.GetValue( "clientCode" );
         string password = config.GetValue( "password" );
         string twoFactorAuth = config.GetValue( "twoFactorAuth" );
         BrokeragePlatform brokeragePlatform = config.GetValue( "brokeragePlatform" );
         int samplingTime = config.GetValue( "samplingTime" );
         StrategyLaunchMode launchMode = config.GetValue( "launchMode" );

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

         // Create our strategy object
         var strategy = await RSIStrategy.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( "startDate" ), "dd-MM-yyyy", null );
            DateTime endDate = DateTime.ParseExact( config.GetValue( "endDate" ), "dd-MM-yyyy", null );

            await strategy.BacktestAsync( new BacktestRequest()
            {
               StartDate = startDate,
               EndDate = endDate,
               ApiKey = bkApiKey,
               ApiSecretKey = bkApiSecretKey,
               SamplingTimeInSeconds = samplingTime,
               BrokeragePlatform = brokeragePlatform,
               Capital = RSIStrategy.Capital
            } );
         }
         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,
               Capital = RSIStrategy.Capital
            } );
         }

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

Python (3.9). Glimpse of creating a quant strategy in Algorum

No crap! Just a piece of code to create a simple Golden Crossover (EMA 50 > EM 200) based quant strategy in Algorum using Python (3.9). You get technical indicator evaluation, logging, state management, real time data ticks, order status handling, all in just few lines of code. And your strategy code is abstracted from underlying broker API, and runs both in paper trading (virtual money) mode and actual broker live mode. Enjoy this for now!

golden_crossover_strategy.py


import datetime
import threading
import traceback
import uuid

import AlgorumQuantClient.quant_client
import AlgorumQuantClient.algorum_types
import jsonpickle


class GoldenCrossoverQuantStrategy(AlgorumQuantClient.quant_client.QuantEngineClient):
    Capital = 100000
    Leverage = 3  # 3x Leverage on Capital

    class State(object):
        def __init__(self):
            self.Bought = False
            self.LastTick = None
            self.CurrentTick = None
            self.Orders = []
            self.CurrentOrderId = None
            self.CurrentOrder = None
            self.CrossAboveObj = None

    def __init__(self, url, apikey, launchmode, sid, user_id, trace_ws=False):
        try:
            # Pass constructor arguments to base class
            super(GoldenCrossoverQuantStrategy, self).__init__(url, apikey, launchmode, sid, user_id, trace_ws)

            # Load any saved state
            state_json_str = self.get_data("state")

            if state_json_str is not None:
                self.State = jsonpickle.decode(state_json_str)

            if self.State is None or launchmode == AlgorumQuantClient.algorum_types.StrategyLaunchMode.Backtesting:
                self.State = GoldenCrossoverQuantStrategy.State()
                self.State.CrossAboveObj = AlgorumQuantClient.algorum_types.CrossAbove()

            self.StateLock = threading.RLock()

            # Subscribe for our symbol data
            # For India users
            self.symbol = AlgorumQuantClient.algorum_types.TradeSymbol(
                AlgorumQuantClient.algorum_types.SymbolType.FuturesIndex,
                'NIFTY',
                AlgorumQuantClient.algorum_types.FNOPeriodType.Monthly,
                0, 0,
                AlgorumQuantClient.algorum_types.OptionType.Unspecified,
                0, 0)

            # For USA users
            # self.symbol = AlgorumQuantClient.algorum_types.TradeSymbol(
            #     AlgorumQuantClient.algorum_types.SymbolType.Stock,
            #     'MSFT',
            #     AlgorumQuantClient.algorum_types.FNOPeriodType.Monthly,
            #     0, 0,
            #     AlgorumQuantClient.algorum_types.OptionType.Unspecified,
            #     0, 0)

            symbols = [self.symbol]
            self.subscribe_symbols(symbols)

            # Create indicator evaluator, which will be automatically synchronized with the real time or backtesting
            # data that is streaming into this algo
            self.evaluator = self.create_indicator_evaluator(
                AlgorumQuantClient.algorum_types.CreateIndicatorRequest(
                    self.symbol,
                    AlgorumQuantClient.algorum_types.CandlePeriod.Minute,
                    1))
        except Exception:
            print(traceback.format_exc())
            self.log(AlgorumQuantClient.algorum_types.LogLevel.Error, traceback.format_exc())

    # This method is called on each tick for the subscribed symbols
    def on_tick(self, tick_data):
        try:
            self.State.CurrentTick = tick_data

            ema50 = self.Evaluator.ema(50)
            ema200 = self.Evaluator.ema(200)

            if self.State.LastTick is not None and (
                    datetime.datetime.strptime(tick_data.Timestamp,
                                               AlgorumQuantClient.quant_client.QuantEngineClient.get_date_format(
                                                   tick_data.Timestamp)) -
                    datetime.datetime.strptime(self.State.LastTick.Timestamp,
                                               AlgorumQuantClient.quant_client.QuantEngineClient.get_date_format(
                                                   self.State.LastTick.Timestamp))).total_seconds() < 60:
                pass
            else:
                msg = str(tick_data.Timestamp) + ',' + str(tick_data.LTP) + ', ema50 ' \
                      + str(ema50) + ', ema200 ' + str(ema200)
                print(msg)
                self.log(AlgorumQuantClient.algorum_types.LogLevel.Information, msg)
                self.State.LastTick = tick_data

            if ema50 > 0 and ema200 > 0 and \
                    self.State.CrossAboveObj.evaluate(ema50, ema200) and \
                    not self.State.Bought and \
                    self.State.CurrentOrderId is None:
                self.State.CurrentOrderId = uuid.uuid4().hex
                place_order_request = AlgorumQuantClient.algorum_types.PlaceOrderRequest()
                place_order_request.OrderType = AlgorumQuantClient.algorum_types.OrderType.Market
                place_order_request.Price = tick_data.LTP
                place_order_request.Quantity = \
                    (GoldenCrossoverQuantStrategy.Capital / tick_data.LTP) * GoldenCrossoverQuantStrategy.Leverage
                place_order_request.Symbol = self.symbol
                place_order_request.Timestamp = tick_data.Timestamp

                if self.LaunchMode == AlgorumQuantClient.algorum_types.StrategyLaunchMode.Backtesting:
                    place_order_request.TradeExchange = AlgorumQuantClient.algorum_types.TradeExchange.PAPER
                else:
                    place_order_request.TradeExchange = AlgorumQuantClient.algorum_types.TradeExchange.NSE

                place_order_request.OrderDirection = AlgorumQuantClient.algorum_types.OrderDirection.Buy
                place_order_request.Tag = self.State.CurrentOrderId
                place_order_request.SlippageType = AlgorumQuantClient.algorum_types.SlippageType.TIME
                place_order_request.Slippage = 1000

                self.place_order(place_order_request)
                self.set_data("state", self.State)

                msg = 'Placed buy order for ' + str(place_order_request.Quantity) + ' units of ' + self.symbol.Ticker + \
                      ' at price (approx) ' + str(tick_data.LTP) + ', ' + str(tick_data.Timestamp)
                print(msg)
                self.log(AlgorumQuantClient.algorum_types.LogLevel.Information, msg)
            else:
                if self.State.CurrentOrder is not None and \
                        ((tick_data.LTP - self.State.CurrentOrder.AveragePrice >= (
                                self.State.CurrentOrder.AveragePrice * (0.1 / 100))) or
                         (self.State.CurrentOrder.AveragePrice - tick_data.LTP >= (
                                 self.State.CurrentOrder.AveragePrice * (0.25 / 100)))) and self.State.Bought:
                    qty = self.State.CurrentOrder.FilledQuantity

                    self.State.CurrentOrderId = uuid.uuid4().hex
                    place_order_request = AlgorumQuantClient.algorum_types.PlaceOrderRequest()
                    place_order_request.OrderType = AlgorumQuantClient.algorum_types.OrderType.Market
                    place_order_request.Price = tick_data.LTP
                    place_order_request.Quantity = qty
                    place_order_request.Symbol = self.symbol
                    place_order_request.Timestamp = tick_data.Timestamp

                    if self.LaunchMode == AlgorumQuantClient.algorum_types.StrategyLaunchMode.Backtesting:
                        place_order_request.TradeExchange = AlgorumQuantClient.algorum_types.TradeExchange.PAPER
                    else:
                        place_order_request.TradeExchange = AlgorumQuantClient.algorum_types.TradeExchange.NSE

                    place_order_request.TriggerPrice = tick_data.LTP
                    place_order_request.OrderDirection = AlgorumQuantClient.algorum_types.OrderDirection.Sell
                    place_order_request.Tag = self.State.CurrentOrderId
                    place_order_request.SlippageType = AlgorumQuantClient.algorum_types.SlippageType.TIME
                    place_order_request.Slippage = 1000

                    self.place_order(place_order_request)
                    self.set_data("state", self.State)

                    msg = 'Placed sell order for ' + str(qty) + ' units of ' + self.symbol.Ticker + \
                          ' at price (approx) ' + str(tick_data.LTP) + ', ' + str(tick_data.Timestamp)
                    print(msg)
                    self.log(AlgorumQuantClient.algorum_types.LogLevel.Information, msg)

            if self.LaunchMode == AlgorumQuantClient.algorum_types.StrategyLaunchMode.Backtesting:
                self.send_progress_async(tick_data)
        except Exception:
            self.log(AlgorumQuantClient.algorum_types.LogLevel.Error, traceback.format_exc())

    # This method is called on order updates, once the place_order method is called
    def on_order_update(self, order: AlgorumQuantClient.algorum_types.Order):
        try:
            if order.Status == AlgorumQuantClient.algorum_types.OrderStatus.Completed:
                self.StateLock.acquire()
                self.State.Orders.append(order)
                self.StateLock.release()

                if order.OrderDirection == AlgorumQuantClient.algorum_types.OrderDirection.Buy:
                    self.State.Bought = True
                    self.State.CurrentOrder = order
                    msg = 'Order Id ' + order.OrderId + ' Bought ' + \
                          str(order.FilledQuantity) + ' units of ' + order.Symbol.Ticker + ' at price ' + \
                          str(order.AveragePrice)
                    print(msg)
                    self.log(AlgorumQuantClient.algorum_types.LogLevel.Information, msg)
                else:
                    self.State.Bought = False
                    self.State.CurrentOrder = None
                    msg = 'Order Id ' + order.OrderId + ' Sold ' + \
                          str(order.FilledQuantity) + ' units of ' + order.Symbol.Ticker + ' at price ' + \
                          str(order.AveragePrice)
                    print(msg)
                    self.log(AlgorumQuantClient.algorum_types.LogLevel.Information, msg)

                self.State.CurrentOrderId = None
                stats = self.get_stats(self.State.CurrentTick)
                self.publish_stats(stats)

                for k, v in stats.items():
                    print('Key: ' + str(k) + ', Value: ' + str(v))

            self.set_data("state", self.State)
        except Exception:
            self.log(AlgorumQuantClient.algorum_types.LogLevel.Error, traceback.format_exc())

    def backtest(self, backtest_request: AlgorumQuantClient.algorum_types.BacktestRequest):
        # Preload the indicator evaluator with 200 candles
        self.evaluator.preload_candles(200, backtest_request.StartDate, backtest_request.ApiKey,
                                       backtest_request.ApiSecretKey)

        AlgorumQuantClient.quant_client.QuantEngineClient.backtest(self, backtest_request)

    def get_stats(self, tick_date: AlgorumQuantClient.algorum_types.TickData):
        stats_map = None

        try:
            stats_map = {"Capital": GoldenCrossoverQuantStrategy.Capital, "Order Count": len(self.State.Orders)}

            buy_val = 0.0
            sell_val = 0.0
            buy_qty = 0.0
            sell_qty = 0.0

            for order in self.State.Orders:
                if (order.Status == AlgorumQuantClient.algorum_types.OrderStatus.Completed) and \
                        (order.OrderDirection == AlgorumQuantClient.algorum_types.OrderDirection.Buy) and \
                        order.Symbol.Ticker == tick_date.Symbol.Ticker:
                    buy_val += order.FilledQuantity * order.AveragePrice
                    buy_qty += order.FilledQuantity

                if (order.Status == AlgorumQuantClient.algorum_types.OrderStatus.Completed) and \
                        (order.OrderDirection == AlgorumQuantClient.algorum_types.OrderDirection.Sell) and \
                        order.Symbol.Ticker == tick_date.Symbol.Ticker:
                    sell_val += order.FilledQuantity * order.AveragePrice
                    sell_qty += order.FilledQuantity

            if sell_qty < buy_qty:
                sell_val += (buy_qty - sell_qty) * tick_date.LTP

            pl = sell_val - buy_val
            stats_map['PL'] = pl
            stats_map['Portfolio Value'] = GoldenCrossoverQuantStrategy.Capital + pl

            self.log(AlgorumQuantClient.algorum_types.LogLevel.Information, "PL: " + str(pl))
            self.log(AlgorumQuantClient.algorum_types.LogLevel.Information,
                     "Portfolio Value: " + str(stats_map['Portfolio Value']))

        except Exception:
            self.log(AlgorumQuantClient.algorum_types.LogLevel.Error, traceback.format_exc())

        return stats_map

main.py


import datetime
import os
import traceback
import uuid

import AlgorumQuantClient.algorum_types
import golden_crossover_quant_strategy
import trend_reversal_quant_strategy
import support_resistance_quant_strategy
import rsi_quant_strategy
import gapup_quant_strategy
import index_futures_trend_quant_strategy

if __name__ == '__main__':
    client = None

    try:
        if 'url' in os.environ:
            url = os.environ['url']
        else:
            url = None

        if url is None or url == '':
            url = 'wss://india-qe-api.algorum.net/quant/engine/api/v1'

        if 'apiKey' in os.environ:
            apikey = os.environ['apiKey']
        else:
            apikey = None

        if apikey is None or apikey == '':
            apikey = ''

        if 'launchMode' in os.environ:
            launchmode = os.environ['launchMode']
        else:
            launchmode = None

        if launchmode is None or launchmode == '':
            launchmode = AlgorumQuantClient.algorum_types.StrategyLaunchMode.Backtesting

        if 'sid' in os.environ:
            sid = os.environ['sid']
        else:
            sid = None

        if sid is None or sid == '':
            sid = uuid.uuid4().hex

        if 'userId' in os.environ:
            user_id = os.environ['userId']
        else:
            user_id = None

        if user_id is None or user_id == '':
            user_id = ''

        if 'bkApiKey' in os.environ:
            bk_api_key = os.environ['bkApiKey']
        else:
            bk_api_key = None

        if bk_api_key is None or bk_api_key == '':
            bk_api_key = ''

        if 'bkApiSecretKey' in os.environ:
            bk_api_secret_key = os.environ['bkApiSecretKey']
        else:
            bk_api_secret_key = None

        if bk_api_secret_key is None or bk_api_secret_key == '':
            bk_api_secret_key = 'Algorum User's Alpaca Api Secret Key'

        if 'clientCode' in os.environ:
            client_code = os.environ['clientCode']
        else:
            client_code = None

        if 'password' in os.environ:
            password = os.environ['password']
        else:
            password = None

        if 'twoFactorAuth' in os.environ:
            two_factor_auth = os.environ['twoFactorAuth']
        else:
            two_factor_auth = None

        if 'samplingTime' in os.environ:
            sampling_time = os.environ['samplingTime']
        else:
            sampling_time = 15

        url += '?sid=' + sid + '&apiKey=' + apikey + '&launchMode=' + launchmode

        print('URL: ' + url)
        print('User Id: ' + user_id)

        # Golden crossover quant strategy
        client = golden_crossover_quant_strategy.GoldenCrossoverQuantStrategy(
            url,
            apikey,
            launchmode,
            sid,
            user_id
        )

        if 'brokeragePlatform' in os.environ:
            brokerage_platform = os.environ['brokeragePlatform']
        else:
            brokerage_platform = None

        if brokerage_platform is None or brokerage_platform == '':
            brokerage_platform = AlgorumQuantClient.algorum_types.BrokeragePlatform.Alpaca

        # Backtesting mode
        if launchmode == AlgorumQuantClient.algorum_types.StrategyLaunchMode.Backtesting:
            if 'startDate' in os.environ:
                startDate = datetime.datetime.strptime(os.environ['startDate'], '%d-%m-%Y')
            else:
                startDate = None

            if startDate is None or startDate == '':
                startDate = datetime.datetime.strptime('01-03-2021', '%d-%m-%Y')

            if 'endDate' in os.environ:
                endDate = datetime.datetime.strptime(os.environ['endDate'], '%d-%m-%Y')
            else:
                endDate = None

            if endDate is None or endDate == '':
                endDate = datetime.datetime.strptime('01-04-2021', '%d-%m-%Y')

            backtestRequest = AlgorumQuantClient.algorum_types.BacktestRequest(
                startDate, endDate, sid, bk_api_key, bk_api_secret_key,
                client_code, password, two_factor_auth, sampling_time, brokerage_platform,
                golden_crossover_quant_strategy.GoldenCrossoverQuantStrategy.Capital)
            client.backtest(backtestRequest)
        else:
            tradingRequest = AlgorumQuantClient.algorum_types.TradingRequest(
                bk_api_key, bk_api_secret_key,
                client_code, password, two_factor_auth, sampling_time, brokerage_platform,
                golden_crossover_quant_strategy.GoldenCrossoverQuantStrategy.Capital)
            client.start_trading(tradingRequest)

        client.wait()
        print('Main strategy thread exited')
    except Exception:
        print(traceback.format_exc())

        if client is not None:
            client.log(AlgorumQuantClient.algorum_types.LogLevel.Error, traceback.format_exc())