跳转至

为 cTrader 商店编写试用算法

算法创作者通常需要一个算法的试用版,以便在 cTrader 商店 中实施有效的分发策略。 本文解释了如何在代码级别为 cBot、指标和插件的试用版引入限制。

提示

复制您的主算法以创建试用版,按照本指南中的说明添加限制,然后在 cTrader 商店中 发布试用版

试用限制

试用限制确保潜在买家可以测试算法的整体行为、逻辑和质量,但无法完全复制付费版本的功能。

试用算法代码中的常见限制包括:

  • 仅限模拟账户操作
  • 仅限回测操作
  • 每日利润上限
  • 硬编码的品种和周期
  • 硬编码的参数
  • 以及更多

cBot

试用 cBot 的代码结构可能如下:

 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  

仅限模拟账户

允许试用版仅在模拟账户上运行。 如果用户将其附加到实盘账户,cBot 将停止执行。

 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  

仅限回测

允许 cBot 仅在回测期间运行。 如果用户尝试在真实条件下使用该算法进行交易,则不会发生任何操作。

 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

每日利润上限

编程使 cBot 在每日净利润达到设定值后停止运行。

 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

硬编码的品种和周期

如果用户尝试在其他品种或周期上运行 cBot,则不会发生任何操作。

 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

硬编码的参数

编程使 cBot 始终使用不利的内部常量,例如较小的交易量或手数。

 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

每日交易次数限制

配置 cBot 在给定数量的交易后停止当天的交易。

 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

时间限制操作

编程使 cBot 在启动时发送与用户 cTID 关联的起始日期,并配置其在固定时间段后停止运行。 为防止用户绕过试用起始配置,请考虑使用远程 API 服务或类似的安全解决方案。

 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

基于会话的限制

允许 cBot 仅在一个交易时段内进行交易。

 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

仅限模拟交易

编程使 cBot 记录其将在何处进行交易,但不执行交易。

 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

指标

试用指标的代码结构可能如下:

 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

功能缩减

试用版指标执行基本计算,而完整版本包含更高级的逻辑。

 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.

硬编码的品种和时间框架

编程使指标仅在特定品种和时间框架下显示输出。

 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

时间限制操作

编程使指标在过期后停止显示输出,仅显示一条消息。

 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
        # )

插件

试用插件的代码结构可能如下:

 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

功能缩减

试用版仅包含基本功能,而完整版本提供所有功能。

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

时间限制操作

编程使插件在过期后停止运行,仅显示一条消息。

 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")