Skip to main content Skip to footer

Basic concepts

To create a strategy user needs to create a class which should be inherited from VisualStrategy 

The developer should set parameter PlacementMode. This parameter defines where the strategy will dispay plots (if it has). This parameter can take one of the following values:

  • PricePanel - draw plots on the same panel as price chart;
  • SeparatePanel - draw plots on separate panel;

The strategy can use external money management models to calculate position sizing. To do it user user must set the parameter IsMoneyManagementUsed to True in the constructor and then to initialize MoneyManagement property using one of the existing models.Read more about Money Management models here

The strategy also can use external exit signal models, i.e. signals for closing opened positions (for example it can be models using Stop Loss, Take Profit, Trailing Stop etc.). To do it user must set parameter IsTradeExitSignalUsed to True in the constructor and then add prefered exit signals to TradeExitSignals collection. Read more about Trade Exit Signals here. The following snippet of code shows how to do it

public MovingAverageCrossDemo()
{
        PlacementMode = ChartPanelUsing.PricePanel;
        //We use external money management
        IsMoneyManagementUsed = true;
        MoneyManagement = new FixedContract(); //Default money management

        IsTradeExitSignalUsed = true; //We allow to add custom exit signals

        TradeExitSignals.Add(new ConstantStopLossExitSignal());
         TradeExitSignals.Add(new ChandelierStopExitSignal());
}

Strategy as well as indicator is able to plot different data series. The following snippet of code shows adding of 2 plot series to the strategy

private readonly Series<double> _signalDownSeries;
private readonly Series<double> _signalUpSeries;

public MovingAverageCrossDemo()
{
      PlacementMode = ChartPanelUsing.PricePanel;

       _signalDownSeries = AddPlot("Cross down", Colors.Red, PlotPointType.TriangleDown).Values;
         _signalUpSeries = AddPlot("Cross up", Colors.LimeGreen, PlotPointType.TriangleUp).Values;
}

Also there are useful functions helping in strategy development:

Strategy is a ScriptCalculationUnit as well as an Indicator. So the following functions are available for Strategy as well: OnMarketData, OnMarketDepth, OnTimer, OnCalculateMinMax, OnRender, OnChartViewportChanging, OnChartViewportChanged.

Let's see on example of development a logic of well-known strategy Moving Average (MA) crossover. Assume, we have 2 MA indicators: _simpleMovingAverageFast,  _simpleMovingAverageSlow and the strategy should work the following way: to go long when  _simpleMovingAverageFast crosses _simpleMovingAverageSlow from below and to go short when - from above;

The implementation of such a logic can be the following:

private MoneyManagementResult CalculateQuantityToEnterPosition(TradeAction tradeAction) => CalculateMoneyManagementForCurrentBar(tradeAction);

protected override void OnBarUpdate(IBarSeries barSeries)
{
      _simpleMovingAverageFast.AssertNoNull();
      _simpleMovingAverageSlow.AssertNoNull();

      base.OnBarUpdate(barSeries);

      var crossDirection = _simpleMovingAverageFast.Cross(_simpleMovingAverageSlow, Bars.Count - 1);
     if (crossDirection > 0 &&
                !TradeManager.IsAnyWorkingMarketOrders() && //If there are some still working market orders, we skip the signal
                TradeManager.GetNettingPositionQuantity().Sign <= 0)
      {
          TradeManager.Exit();

          var quantity = CalculateQuantityToEnterPosition(TradeAction.Buy);
          if (!quantity.IsSuccessful)
              Print($"Error: cannot calculate quantity for new Long position: {quantity.ErrorMessage}");
          if (quantity.Quantity.HasValue)
          {
              TradeManager.BuyByMarket(quantity.Quantity, "Cross upward");
          }
      }
      else if (crossDirection < 0 &&
                     !TradeManager.IsAnyWorkingMarketOrders() && //If there are some still working market orders, we skip the signal
                     TradeManager.GetNettingPositionQuantity().Sign >= 0)
      {
          TradeManager.Exit();

          var quantity = CalculateQuantityToEnterPosition(TradeAction.Sell);
          if (!quantity.IsSuccessful)
                    Print($"Error: cannot calculate quantity for new Short position: {quantity.ErrorMessage}");
          if (quantity.Quantity.HasValue)
          {
              TradeManager.SellByMarket(quantity.Quantity, "Cross downward");
          }
      }
}

Here we should pay attention to the following things:

1. All strategy logic should be implemented inside OnBarUpdate function.

2. When signal to open a position is given we should close existing position, calculate quantity with/without help of money management model and then place an order which will open a position.

3. All functions to open/close positions are contained in TradeManagerClass.

4. Orders in FinScript work asynchronously. It means, that any order a developer submits will not be filled at once at the same function. It will pass several updates before it. Any of these updates can be handled with help of OnOrderUpdate function.

Cookies Notice

We use cookies to improve your experience, personalize content, and analyze our traffic. By clicking "Accept All Cookies," you agree to the storing of cookies on your device. You can manage your cookie preferences at any time by visiting our Cookie Settings.