Codifique un algoritmo de prueba para cTrader Store
Una versión de prueba de un algoritmo suele ser necesaria para que los creadores de algoritmos puedan seguir una estrategia de distribución eficaz en cTrader Store. Este artículo explica cómo introducir limitaciones en las versiones de prueba de cBots, indicadores y plugins a nivel de código.
Consejo
Duplique su algoritmo principal para crear una versión de prueba, añádale limitaciones como se explica en esta guía y luego publique una prueba en cTrader Store.
Limitaciones de la prueba
Las limitaciones de la prueba garantizan que los posibles compradores puedan probar el comportamiento general, la lógica y la calidad de su algoritmo, pero no puedan replicar completamente la funcionalidad de la versión de pago.
Las limitaciones comunes en el código de los algoritmos de prueba incluyen:
Operaciones solo en demo
Operaciones solo en backtesting
Límite de beneficios diarios
Símbolo y período codificados
Parámetros codificados
Y más
cBots
La estructura de código de un cBot de prueba puede parecerse a esto:
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
Solo demo
Permita que el bot de prueba se ejecute solo en cuentas demo. Si un usuario lo adjunta a una cuenta real, el cBot deja de ejecutarse.
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
Solo backtesting
Permita que el cBot funcione solo durante los backtests. Si un usuario intenta utilizar el algoritmo para operar en condiciones reales, no ocurre nada.
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
Límite de beneficios diarios
Programe el cBot para que deje de operar una vez que el beneficio neto diario alcance un valor establecido.
[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 y período codificados
Si un usuario intenta ejecutar el cBot en otro símbolo o período, no ocurre nada.
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
Codifique el cBot para que siempre utilice constantes internas desfavorables, como un volumen o tamaño de lote pequeño.
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
Operaciones diarias limitadas
Configure el cBot para que deje de operar durante el resto del día después de un número determinado de operaciones.
[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
Operaciones limitadas en el tiempo
Codifique el cBot para que envíe una fecha de inicio vinculada al cTID del usuario cuando se inicie, y configúrelo para que deje de funcionar después de un período fijo. Para evitar que los usuarios eviten la configuración de inicio de la prueba, considere utilizar un servicio de API remoto o una solución segura similar.
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
Limitación basada en sesiones
Permita que el cBot opere solo en una sesión.
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
Solo operaciones simuladas
Codifique el cBot para que registre dónde operaría y no ejecute operaciones.
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
La estructura de código de un indicador de prueba puede parecerse a esto:
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
Funcionalidad reducida
El indicador de prueba ejecuta un cálculo básico, mientras que la versión completa contiene una lógica más avanzada.
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 y marco temporal codificados
Codifique el indicador para que muestre resultados solo cuando se utilice un determinado símbolo y marco temporal.
[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
Operaciones limitadas en el tiempo
Codifique el indicador para que deje de mostrar resultados una vez que caduque y muestre solo un mensaje.
[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
La estructura de código de un plugin de prueba puede parecerse a esto:
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
Funcionalidad reducida
La versión de prueba incluye solo funciones básicas, mientras que la versión completa proporciona todo.
[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")