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 910111213141516171819202122
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{protectedoverridevoidOnStart(){// Trial setup checks (account type, symbol, expiry, etc.)// Restrictions logic for the trial// before or around your normal trading logic.}protectedoverridevoidOnBar(){// Restrictions logic for the trial// before or around your normal trading logic.}privatevoidRunStrategy(){// Normal trading logic goes here// e.g., signal detection, risk management, order placement.}}
1 2 3 4 5 6 7 8 910111213141516171819202122
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():defon_start(self):# Trial setup checks (account type, symbol, expiry, etc.)# Restrictions logic for the trial# before or around your normal trading logic.passdefon_bar(self):# Restrictions logic for the trial# before or around your normal trading logic.passdefrun_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 9101112131415161718192021
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{protectedoverridevoidOnStart(){if(Account.IsLive){Print("This trial version can be used only on demo accounts.");Stop();// Stops the cBot for real accountsreturn;}RunStrategy();// <<< Actual trading logic}privatevoidRunStrategy(){// Normal trading logic goes here// e.g., signal detection, risk management, order placement.}}
1 2 3 4 5 6 7 8 910111213141516171819
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():defon_start(self):ifapi.Account.IsLive:print("This trial version can be used only on demo accounts.")api.Stop()# Stops the cBot for real accountsreturnself.run_strategy()# <<< Actual trading logicdefrun_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 9101112131415
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{protectedoverridevoidOnStart(){if(RunningMode==RunningMode.RealTime){Print("This trial version can run only in backtesting mode.");Stop();// Stops cBotreturn;}}// Rest of your cBot logic}
1 2 3 4 5 6 7 8 91011121314
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():defon_start(self):ifapi.RunningMode==RunningMode.RealTime:print("This trial version can run only in backtesting mode.")api.Stop()# Stops cBotreturn# 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.
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{privateconstdoubleMaxDailyProfit=20.0;protectedoverridevoidOnStart(){StopIfMaxDailyProfitReached();Positions.Closed+=_=>StopIfMaxDailyProfitReached();}privatevoidStopIfMaxDailyProfitReached(){vartodayProfit=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 91011121314151617181920212223
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():MaxDailyProfit=20.0defon_start(self):self.stop_if_max_daily_profit_reached()api.Positions.Closed+=lambda_:self.stop_if_max_daily_profit_reached()defstop_if_max_daily_profit_reached(self):todayProfit=[t.NetProfitfortinHistoryift.ClosingTime.Date==api.Server.Time.Date]iftodayProfit<self.MaxDailyProfit:returnprint("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 910111213141516171819202122
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{privateconststringValidSymbolName="EURUSD";privateconststringValidTimeFrame="Hour";protectedoverridevoidOnStart(){StopIfSymbolOrTimeFrameIsInvalid();}privatevoidStopIfSymbolOrTimeFrameIsInvalid(){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 9101112131415161718192021
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():ValidSymbolName="EURUSD"ValidTimeFrame="Hour"defon_start(self):self.stop_if_symbol_or_time_frame_is_invalid()defstop_if_symbol_or_time_frame_is_invalid(self):ifapi.SymbolName==self.ValidSymbolNameandapi.TimeFrame.Name==self.ValidTimeFrame:returnprint(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 910111213
[Robot(AccessRights = AccessRights.None)]publicclassTest: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:privateconststringTrialVolumeInLots=0.01// Rest of your cBot logic}
1 2 3 4 5 6 7 8 9101112131415161718
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():# 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.
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{privateconstintMaxTradesPerDay=5;protectedoverridevoidOnBar(){if(IsMaxDailyTradeReached())return;// Skip trading logic if max trade number reached}privatevoidIsMaxDailyTradeReached(){vartodayTradesCount=History.Where(t=>t.ClosingTime.Date==Server.Time.Date).Count();if(todayTradesCount<MaxTradesPerDay)returnfalse;Print("Trial limit reached: maximum trades for today.");returntrue;}// Rest of your cBot logic}
1 2 3 4 5 6 7 8 910111213141516171819202122
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():MaxTradesPerDay=5defon_bar(self):ifself.is_max_daily_trade_reached():return# Skip trading logic if max trade number reacheddefis_max_daily_trade_reached(self):todayTradesCount=len([tfortinapi.Historyift.ClosingTime.Date==Server.Time.Date])iftodayTradesCount<self.MaxTradesPerDay:returnFalseprint(f"Trial limit reached: maximum trades for today.")returnTrue# 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 910111213141516171819202122232425
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{privateconstintTrialDays=5;protectedoverridevoidOnStart(){// Do the same check on other methods like OnTick, OnBar, etc...if(Server.Time>=GetTrialStartTime().AddDays(TrialDays))OnTrialExpired();}privateDateTimeGetTrialStartTime(){// Add here the logic for getting trial start time from your secure remote service}privatevoidOnTrialExpired(){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 91011121314151617181920212223
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():TrialDays=5defon_start(self):# Do the same check on other methods like on_tick, on_bar, etc...ifapi.Server.Time>=self.get_trial_start_time().AddDays(self.TrialDays):self.on_trial_expired()defget_trial_start_time(self):# Add here the logic for getting trial start time from your secure remote servicepassdefon_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 91011121314
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{protectedoverridevoidOnBar(){// Do the same check on other methods like OnTick, etc...if(!IsValidSession())return}privateboolIsValidSession()=>MarketSessions.HasFlag(MarketSession.London)// Rest of your cBot logic}
1 2 3 4 5 6 7 8 910111213141516
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():defon_bar(self):# Do the same check on other methods like on_tick, etc...ifself.is_valid_session()==False:returndefis_valid_session(self):returnapi.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 91011121314151617181920
[Robot(AccessRights = AccessRights.None)]publicclassTest:Robot{protectedoverridevoidOnBar(){// Example: simple signalvarbuySignal=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 actionPrint($"[TRIAL] Would open BUY position at {Symbol.Ask}");}}// Rest of your cBot logic}
1 2 3 4 5 6 7 8 910111213141516171819
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():defon_bar(self):# Example: simple signalbuySignal=api.Bars.ClosePrices.Last(1)>api.Bars.OpenPrices.Last(1)ifbuySignal:# 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 actionprint(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:
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():definitialize(self):# Trial setup checks go herepassdefcalculate(self,index):# Trial indicator logic goes herepass
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 9101112131415161718
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]publicclassTest:Indicator{[Output("Result", LineColor = "DodgerBlue")]publicIndicatorDataSeriesResult{get;set;}protectedoverridevoidInitialize(){}publicoverridevoidCalculate(intindex){// Trial build: simplified logic onlyResult[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 9101112131415
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():definitialize(self):passdefcalculate(self,index):# Trial build: simplified logic onlyapi.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.
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]publicclassTest:Indicator{[Output("Result", LineColor = "DodgerBlue")]publicIndicatorDataSeriesResult{get;set;}privateconststringValidSymbolName="EURUSD";privateconststringValidTimeFrame="Hour";protectedoverridevoidInitialize(){if(!IsSymbolAndTimeFrameValid()){Chart.DrawStaticText("symbolLimit","Trial version supports only EURUSD and GBPUSD.",VerticalAlignment.Center,HorizontalAlignment.Center,Color.Yellow);return;}}publicoverridevoidCalculate(intindex){if(!IsSymbolAndTimeFrameValid())return;// Rest of your indicator logic}privatevoidIsSymbolAndTimeFrameValid(){returnSymbolName==ValidSymbolName&&TimeFrame.Name==ValidTimeFrame;}}
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():ValidSymbolName="EURUSD"ValidTimeFrame="Hour"definitialize(self):ifself.is_symbol_and_time_frame_valid()==False:api.Chart.DrawStaticText("symbolLimit","Trial version supports only EURUSD and GBPUSD.",VerticalAlignment.Center,HorizontalAlignment.Center,Color.Yellow)returndefcalculate(self,index):ifself.is_symbol_and_time_frame_valid()==False:return# Rest of your indicator logicdefis_symbol_and_time_frame_valid(self):returnapi.SymbolName==self.ValidSymbolNameandapi.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.
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]publicclassTest:Indicator{[Output("Result", LineColor = "DodgerBlue")]publicIndicatorDataSeriesResult{get;set;}protectedoverridevoidInitialize(){}publicoverridevoidCalculate(intindex){if(IsTrialExpired()){Result[index]=double.NaN;return;}// Rest of your indicator logic}privateboolIsTrialExpired(){// 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// );}}
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*importmathclassTest():definitialize(self):passdefcalculate(self,index):ifself.is_trial_expired():api.Result[index]=math.nanreturn# Rest of your indicator logicdefis_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 9101112
[Plugin(AccessRights = AccessRights.None)]publicclassTest:Plugin{protectedoverridevoidOnStart(){// Trial setup or logic here}protectedoverridevoidOnStop(){}}
1 2 3 4 5 6 7 8 910111213
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():defon_start(self):# Trial setup or logic herepassdefon_stop(self,index):pass
Funcionalidade reduzida
A versão de teste inclui apenas funcionalidades básicas, enquanto a versão completa fornece tudo.
[Plugin(AccessRights = AccessRights.None)]publicclassTest:Plugin{protectedoverridevoidOnStart(){// Use same check inside your plugin other methodsif(IsTrialExpired())OnTrialExpired();// You can also run timer and keep checking trial expiryTimer.Start(100)}protectedoverridevoidOnTimer(){if(IsTrialExpired())OnTrialExpired();}privateDateTimeIsTrialExpired(){// 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}privatevoidOnTrialExpired(){// Do whatever you want when trial is expired hereMessageBox.Show("Trial expired");}}
importclrclr.AddReference("cAlgo.API")# Import cAlgo API typesfromcAlgo.APIimport*# Import trading wrapper functionsfromrobot_wrapperimport*classTest():defon_start(self):# Use same check inside your plugin other methodsifself.is_trial_expired():self.on_trial_expired()# You can also run timer and keep checking trial expiryTimer.Start(100)defon_timer(self,index):ifself.is_trial_expired():self.on_trial_expired()defis_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.Timedefon_trial_expired(self):# Do whatever you want when the trial has expired hereapi.MessageBox.Show("Trial expired")