Ir para o conteúdo

Programar um algoritmo de teste para a cTrader Store

Uma versão de teste de um algoritmo é frequentemente necessária para os criadores de algoritmos seguirem uma estratégia de distribuição eficaz na cTrader Store. Este artigo explica como introduzir limitações às versões de teste de cBots, indicadores e plugins ao nível do código.

Dica

Duplique o seu algoritmo principal para criar uma versão de teste, adicione limitações conforme explicado neste guia e, em seguida, publique uma versão de teste na cTrader Store.

Limitações da versão de teste

As limitações da versão de teste garantem que os potenciais compradores podem testar o comportamento geral, a lógica e a qualidade do seu algoritmo, mas não podem replicar totalmente a funcionalidade da versão paga.

As limitações comuns no código dos algoritmos de teste incluem:

  • Operações apenas em demo
  • Operações apenas em testes de verificação
  • Limite diário de lucro
  • Símbolo e período codificados
  • Parâmetros codificados
  • E muito mais

cBots

A estrutura do código de um cBot de teste pode assemelhar-se a isto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    protected override void OnStart()
    {
        // Trial setup checks (account type, symbol, expiry, etc.)
        // Restrictions logic for the trial
        // before or around your normal trading logic.
    }

    protected override void OnBar()
    {
        // Restrictions logic for the trial
        // before or around your normal trading logic.
    }

    private void RunStrategy()
    {
        // Normal trading logic goes here
        // e.g., signal detection, risk management, order placement.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_start(self):
        # Trial setup checks (account type, symbol, expiry, etc.)
        # Restrictions logic for the trial
        # before or around your normal trading logic.
        pass

    def on_bar(self):
        # Restrictions logic for the trial
        # before or around your normal trading logic.
        pass

    def run_strategy(self):
        # Normal trading logic goes here
        # e.g., signal detection, risk management, order placement.
        pass  

Apenas demo

Permitir que o bot de teste seja executado apenas em contas demo. Se um utilizador o anexar a uma conta real, o cBot para de executar.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    protected override void OnStart()
    {
        if (Account.IsLive)
        {
            Print("This trial version can be used only on demo accounts.");
            Stop(); // Stops the cBot for real accounts
            return;
        }

        RunStrategy(); // <<< Actual trading logic
    }

    private void RunStrategy()
    {
        // Normal trading logic goes here
        // e.g., signal detection, risk management, order placement.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_start(self):
        if api.Account.IsLive:
            print("This trial version can be used only on demo accounts.")
            api.Stop() # Stops the cBot for real accounts
            return

        self.run_strategy() # <<< Actual trading logic

    def run_strategy(self):
        # Normal trading logic goes here
        # e.g., signal detection, risk management, order placement.
        pass  

Apenas testes de verificação

Permitir que o cBot funcione apenas durante os testes de verificação. Se um utilizador tentar usar o algoritmo para negociar em condições reais, nada acontece.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    protected override void OnStart()
    {
        if (RunningMode == RunningMode.RealTime)
        {
            Print("This trial version can run only in backtesting mode.");
            Stop(); // Stops cBot
            return;
        }
    }

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_start(self):
        if api.RunningMode == RunningMode.RealTime:
            print("This trial version can run only in backtesting mode.")
            api.Stop() # Stops cBot
            return

    # Rest of your cBot logic

Limite diário de lucro

Programar o cBot para parar de operar assim que o lucro líquido diário atingir um valor definido.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    private const double MaxDailyProfit = 20.0;

    protected override void OnStart()
    {
        StopIfMaxDailyProfitReached();

        Positions.Closed += _ => StopIfMaxDailyProfitReached();
    }

    private void StopIfMaxDailyProfitReached()
    {
        var todayProfit = History
            .Where(t => t.ClosingTime.Date == Server.Time.Date)
            .Sum(t => t.NetProfit);

        if (todayProfit < MaxDailyProfit)
            return;

        Print("Trial limit reached: daily profit cap.");
        Stop(); // Stops cBot
    }

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    MaxDailyProfit = 20.0

    def on_start(self):
        self.stop_if_max_daily_profit_reached()
        api.Positions.Closed += lambda _: self.stop_if_max_daily_profit_reached()

    def stop_if_max_daily_profit_reached(self):
        todayProfit = [t.NetProfit for t in History if t.ClosingTime.Date == api.Server.Time.Date]

        if todayProfit < self.MaxDailyProfit:
            return

        print("Trial limit reached: daily profit cap.")
        api.Stop(); # Stops cBot

    # Rest of your cBot logic

Símbolo e período codificados

Se um utilizador tentar executar o cBot noutro símbolo ou período, nada acontece.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    private const string ValidSymbolName = "EURUSD";
    private const string ValidTimeFrame = "Hour";

    protected override void OnStart()
    {
        StopIfSymbolOrTimeFrameIsInvalid();
    }

    private void StopIfSymbolOrTimeFrameIsInvalid()
    {
        if (SymbolName == ValidSymbolName && TimeFrame.Name == ValidTimeFrame)
            return;

        Print($"This trial version works only on {ValidSymbolName} {ValidTimeFrame}.");
        Stop(); // Stops cBot
    }

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    ValidSymbolName = "EURUSD"
    ValidTimeFrame = "Hour"

    def on_start(self):
        self.stop_if_symbol_or_time_frame_is_invalid()

    def stop_if_symbol_or_time_frame_is_invalid(self):
        if api.SymbolName == self.ValidSymbolName and api.TimeFrame.Name == self.ValidTimeFrame:
            return

        print(f"This trial version works only on {ValidSymbolName} {ValidTimeFrame}.")
        api.Stop(); # Stops cBot

    # Rest of your cBot logic

Parâmetros codificados

Programar o cBot para usar sempre constantes internas desfavoráveis, como um volume ou tamanho de lote pequeno.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    // In full version, you might have:
    // [Parameter("Volume (lots)", DefaultValue = 0.1)]
    // public double VolumeInLots { get; set; }

    // In trial version, you can omit parameters entirely
    // and always use a fixed volume amount when executing trades:
    private const string TrialVolumeInLots = 0.01

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    # In full version, you might have:
    # [Parameter("Volume (lots)", DefaultValue = 0.1)]
    # public double VolumeInLots { get; set; }
    # In Python algos you have to define parameters
    # in C# file of your algo

    # In trial version, you can omit parameters entirely
    # and always use a fixed volume amount when executing trades:
    TrialVolumeInLots = 0.01

    # Rest of your cBot logic

Negociações diárias limitadas

Configurar o cBot para parar de negociar durante o resto do dia após um determinado número de negociações.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    private const int MaxTradesPerDay = 5;

    protected override void OnBar()
    {
        if (IsMaxDailyTradeReached())
            return; // Skip trading logic if max trade number reached
    }

    private void IsMaxDailyTradeReached()
    {
        var todayTradesCount = History
            .Where(t => t.ClosingTime.Date == Server.Time.Date)
            .Count();

        if (todayTradesCount < MaxTradesPerDay)
            return false;

        Print("Trial limit reached: maximum trades for today.");
        return true;
    }

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    MaxTradesPerDay = 5

    def on_bar(self):
        if self.is_max_daily_trade_reached():
            return # Skip trading logic if max trade number reached

    def is_max_daily_trade_reached(self):
        todayTradesCount = len([t for t in api.History if t.ClosingTime.Date == Server.Time.Date])
        if todayTradesCount < self.MaxTradesPerDay:
            return False

        print(f"Trial limit reached: maximum trades for today.")
        return True

    # Rest of your cBot logic

Operações limitadas no tempo

Programar o cBot para enviar uma data de início associada ao cTID do utilizador quando é iniciado e configurá-lo para parar de funcionar após um período fixo. Para evitar que os utilizadores contornem a configuração de início da versão de teste, considere usar um serviço de API remoto ou uma solução segura semelhante.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    private const int TrialDays = 5;

    protected override void OnStart()
    {
        // Do the same check on other methods like OnTick, OnBar, etc...
        if (Server.Time >= GetTrialStartTime().AddDays(TrialDays))
            OnTrialExpired();
    }

    private DateTime GetTrialStartTime()
    {
        // Add here the logic for getting trial start time from your secure remote service
    }

    private void OnTrialExpired()
    {
        Print("Your trial period has expired. Please purchase the full version.");
        Stop();
    }

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    TrialDays = 5

    def on_start(self):
        # Do the same check on other methods like on_tick, on_bar, etc...
        if api.Server.Time >= self.get_trial_start_time().AddDays(self.TrialDays):
            self.on_trial_expired()

    def get_trial_start_time(self):
        # Add here the logic for getting trial start time from your secure remote service
        pass

    def on_trial_expired(self):
        print("Your trial period has expired. Please purchase the full version.")
        api.Stop()

    # Rest of your cBot logic

Limitação baseada na sessão

Permitir que o cBot negocie apenas numa sessão.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    protected override void OnBar()
    {
        // Do the same check on other methods like OnTick, etc...
        if (!IsValidSession())
            return
    }

    private bool IsValidSession() => MarketSessions.HasFlag(MarketSession.London)

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_bar(self):
        # Do the same check on other methods like on_tick, etc...
        if self.is_valid_session() == False:
            return

    def is_valid_session(self):
        return api.MarketSessions.HasFlag(MarketSession.London)

    # Rest of your cBot logic

Apenas negociações simuladas

Programar o cBot para registar onde negociaria e não executar negociações.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[Robot(AccessRights = AccessRights.None)]
public class Test : Robot
{
    protected override void OnBar()
    {
        // Example: simple signal
        var buySignal = Bars.ClosePrices.Last(1) > Bars.OpenPrices.Last(1);

        if (buySignal)
        {
            // In full version, this is where you would execute an order:
            // ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000);

            // In the trial: only log the virtual action
            Print($"[TRIAL] Would open BUY position at {Symbol.Ask}");
        }
    }

    // Rest of your cBot logic
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_bar(self):
        # Example: simple signal
        buySignal = api.Bars.ClosePrices.Last(1) > api.Bars.OpenPrices.Last(1)

        if buySignal:
            # In full version, this is where you would execute an order:
            # api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 10000);

            # In the trial: only log the virtual action
            print(f"[TRIAL] Would open BUY position at {api.Symbol.Ask}")

    # Rest of your cBot logic

Indicadores

A estrutura do código de um indicador de teste pode assemelhar-se a isto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Test : Indicator
{
    [Output("Result", LineColor = "DodgerBlue")]
    public IndicatorDataSeries Result { get; set; }

    protected override void Initialize()
    {
        // Trial setup checks go here
    }

    public override void Calculate(int index)
    {
        // Trial indicator logic goes here
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def initialize(self):
        # Trial setup checks go here
        pass

    def calculate(self, index):
        # Trial indicator logic goes here
        pass

Funcionalidade reduzida

O indicador de teste executa um cálculo básico, enquanto a versão completa contém uma lógica mais avançada.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Test : Indicator
{
    [Output("Result", LineColor = "DodgerBlue")]
    public IndicatorDataSeries Result { get; set; }

    protected override void Initialize()
    {
    }

    public override void Calculate(int index)
    {
        // Trial build: simplified logic only
        Result[index] = (Bars.HighPrices[index] + Bars.LowPrices[index]) / 2;

        // Full version might add more lines, buffers, filters, etc.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def initialize(self):
        pass

    def calculate(self, index):
        # Trial build: simplified logic only
        api.Result[index] = (api.Bars.HighPrices[index] + api.Bars.LowPrices[index]) / 2

        # Full version might add more lines, buffers, filters, etc.

Símbolo e período codificados

Programar o indicador para mostrar resultados apenas quando um determinado símbolo e período forem utilizados.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Test : Indicator
{
    [Output("Result", LineColor = "DodgerBlue")]
    public IndicatorDataSeries Result { get; set; }

    private const string ValidSymbolName = "EURUSD";
    private const string ValidTimeFrame = "Hour";

    protected override void Initialize()
    {
        if (!IsSymbolAndTimeFrameValid())
        {
            Chart.DrawStaticText("symbolLimit",
                "Trial version supports only EURUSD and GBPUSD.",
                VerticalAlignment.Center,
                HorizontalAlignment.Center,
                Color.Yellow);

            return;
        }
    }

    public override void Calculate(int index)
    {
        if (!IsSymbolAndTimeFrameValid())
            return;

        // Rest of your indicator logic
    }

    private void IsSymbolAndTimeFrameValid()
    {
        return SymbolName == ValidSymbolName && TimeFrame.Name == ValidTimeFrame;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    ValidSymbolName = "EURUSD"
    ValidTimeFrame = "Hour"

    def initialize(self):
        if self.is_symbol_and_time_frame_valid() == False:
            api.Chart.DrawStaticText("symbolLimit",
                "Trial version supports only EURUSD and GBPUSD.",
                VerticalAlignment.Center,
                HorizontalAlignment.Center,
                Color.Yellow)
            return

    def calculate(self, index):
        if self.is_symbol_and_time_frame_valid() == False:
            return

        # Rest of your indicator logic

    def is_symbol_and_time_frame_valid(self):
        return api.SymbolName == self.ValidSymbolName and api.TimeFrame.Name == self.ValidTimeFrame

Operações limitadas no tempo

Programar o indicador para parar de exibir resultados assim que expirar e mostrar apenas uma mensagem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Test : Indicator
{
    [Output("Result", LineColor = "DodgerBlue")]
    public IndicatorDataSeries Result { get; set; }

    protected override void Initialize()
    {
    }

    public override void Calculate(int index)
    {
        if (IsTrialExpired())
        {
            Result[index] = double.NaN;
            return;
        }

        // Rest of your indicator logic
    }

    private bool IsTrialExpired()
    {
        // Here you can check if trial is expired or not
        // You have to store trial start time somewhere secure outside
        // user reach and comapre it with Server.Time

        // You can also let user know trial is expired by showing
        // a text message on chart, ex:
        // Chart.DrawStaticText(
        // "trial_expired",
        // "Trial expired. Please purchase the full version.",
        // VerticalAlignment.Center,
        // HorizontalAlignment.Center,
        // Color.Red
        // );
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
import math
class Test():
    def initialize(self):
        pass

    def calculate(self, index):
        if self.is_trial_expired():
            api.Result[index] = math.nan
            return

        # Rest of your indicator logic

    def is_trial_expired(self):
        # Here you can check if trial is expired or not
        # You have to store trial start time somewhere secure outside
        # user reach and comapre it with api.Server.Time

        # You can also let user know trial is expired by showing
        # a text message on chart, ex:
        # api.Chart.DrawStaticText(
        # "trial_expired",
        # "Trial expired. Please purchase the full version.",
        # VerticalAlignment.Center,
        # HorizontalAlignment.Center,
        # Color.Red
        # )

Plugins

A estrutura do código de um plugin de teste pode assemelhar-se a isto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Plugin(AccessRights = AccessRights.None)]
public class Test : Plugin
{
    protected override void OnStart()
    {
        // Trial setup or logic here
    }

    protected override void OnStop()
    {
    }
}      
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_start(self):
        # Trial setup or logic here
        pass

    def on_stop(self, index):
        pass

Funcionalidade reduzida

A versão de teste inclui apenas funcionalidades básicas, enquanto a versão completa fornece tudo.

1
2
3
4
5
6
7
8
9
[Plugin(AccessRights = AccessRights.None)]
public class Test : Plugin
{
    protected override void OnStart()
    {
        // Basic features
    }

}      
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_start(self):
        # Basic features
        pass

Operações limitadas no tempo

Programar o plugin para parar de funcionar assim que expirar e mostrar apenas uma mensagem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[Plugin(AccessRights = AccessRights.None)]
public class Test : Plugin
{
    protected override void OnStart()
    {
        // Use same check inside your plugin other methods
        if (IsTrialExpired())
            OnTrialExpired();

        // You can also run timer and keep checking trial expiry
        Timer.Start(100)
    }

    protected override void OnTimer()
    {
        if (IsTrialExpired())
            OnTrialExpired();
    }

    private DateTime IsTrialExpired()
    {
        // Here you have to check if user trial period is expired
        // or not, for that you have to store the trial start time
        // somewhere secure outside user access and compare it with
        // Server.Time
    }

    private void OnTrialExpired()
    {
        // Do whatever you want when trial is expired here
        MessageBox.Show("Trial expired");
    }
}      
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import clr
clr.AddReference("cAlgo.API")
# Import cAlgo API types
from cAlgo.API import *
# Import trading wrapper functions
from robot_wrapper import *
class Test():
    def on_start(self):
        # Use same check inside your plugin other methods
        if self.is_trial_expired():
            self.on_trial_expired()

        # You can also run timer and keep checking trial expiry
        Timer.Start(100)

    def on_timer(self, index):
        if self.is_trial_expired():
            self.on_trial_expired()

    def is_trial_expired(self):
        # Here you have to check if user trial period is expired
        # or not, for that you have to store the trial start time
        # somewhere secure outside user access and compare it with
        # Server.Time

    def on_trial_expired(self):
        # Do whatever you want when the trial has expired here
        api.MessageBox.Show("Trial expired")