教程 · 单套解锁 教程 · EA量化教学

EA04 cAlgo/cTrader cBot 开发

使用手册 + 操作教程 共 20 章全部免费开放,一步一步把软件用起来;核心知识点 7 章,¥69 一次解锁讲透原理。

作者 枢汇安全课堂 更新 2026-10-13 阅读 88 收录 EA量化教学 · 27 章
本课程 · 单套解锁
¥69¥104
单品永久解锁 · 会员可阅 · 一次购买终身使用
✓使用手册 + 操作教程 共 20 章全文免费 ✓核心知识点 7 章,¥69 一次解锁 ✓支持电脑 / 手机 · 永久免费更新
注册后解锁核心知识点
免费学习区 使用手册 + 操作教程 共 20 章全部免费开放 · 核心知识点 7 章为付费内容 ¥69 解锁核心知识点
PART ONE

使用手册

功能在哪 · 是什么 · 怎么调出来

一、cAlgo 是什么

cAlgo 是 cTrader 平台的算法交易开发框架,现在官方叫 cTrader Automate。用 真正的 C# 写自动交易程序(cBot)和自定义指标。

和 MQL4/MQL5 的区别:

  • 真 C#,不是方言,.NET 生态随便用;
  • 官方支持 NuGet 包(cTrader.Automate),可以用 Visual Studio / VS Code 开发;
  • 回测基于真实 Tick 数据,支持点差与滑点模拟;
  • 严格面向对象:你的策略就是一个继承 Robot 的类。
白话注解:把 cBot 想成一个"住在 cTrader 里的机器人"。你用 C# 告诉它什么时候买、什么时候卖,它替你盯盘执行。

二、Account 账户信息

Account(类型 IAccount)常用属性(官方包核验):

属性含义
Account.Balance余额(已实现盈亏)
Account.Equity净值(余额 + 浮动盈亏)
Account.Margin已用保证金
Account.FreeMargin可用保证金
Account.MarginLevel保证金比例(%),低于强平线会被强平
Account.StopOutLevel强平线
Account.Asset账户结算货币
Account.IsLivetrue=实盘,false=模拟
Account.BrokerName / Account.Number券商名 / 账号
Account.UnrealizedGrossProfit / UnrealizedNetProfit浮动盈亏(毛/净)
// 账户级风控示例:保证金比例低于 300% 就不再开新仓
bool canOpen = Account.FreeMargin > 0 && Account.MarginLevel > 300;
白话注解:Balance 是"已落袋的钱",Equity 是"现在的真实身家"。风控盯 Equity,别盯 Balance——浮亏没平仓时 Balance 纹丝不动,Equity 已经在流血了。

三、与 MQL4 / MQL5 对比

对比项cAlgo (cTrader)MQL4 (MT4)MQL5 (MT5)
语言真 C#类 Pascal 风格类 C# 方言
订单模型净持仓(Positions)+ 订单(Orders)ticket 流水净持仓/对冲可选
回测精度Tick 级,可模拟点差滑点OHLC 近似为主Tick 级
优化遗传算法 + 自定义 GetFitness遗传算法遗传算法
包管理NuGet 官方支持无无
持仓标识Label + PositionIdMagicNumberMagicNumber

1、MQL4 迁移对照表

MQL4 习惯cAlgo 对应
OrderSend()ExecuteMarketOrder(...) / PlaceLimitOrder(...)
OrderClose()ClosePosition(pos)
OrderModify()ModifyPosition / pos.ModifyStopLossPrice
OrdersTotal() + 循环Positions.FindAll(label, symbol)
MagicNumberLabel(字符串标签)
iMA(NULL,0,period,0,MODE_EMA,CLOSE,shift)Indicators.ExponentialMovingAverage(Bars.ClosePrices, period).Result.Last(1)
AccountBalance() / AccountEquity()Account.Balance / Account.Equity
MarketInfo(Symbol, MODE_POINT)Symbol.PipSize / Symbol.TickSize
OnInit / OnDeinitOnStart / OnStop
白话注解:迁移最大的思维转变是"标签代替魔法数字"。MQL4 里散落各处的 ticket 管理逻辑,在 cAlgo 里收敛成 Label + FindAll 两个词。
PART TWO

操作教程

一步一步 · 跟着做完一件事

四、开发环境与 Automate 面板

1、打开方式

  1. 登录 cTrader 桌面版或网页版;
  2. 左侧菜单点 Automate(自动化);
  3. 点 New cBot → 进入内置代码编辑器。

Automate 编辑器布局:

  • 左侧:cBot / Indicator 文件树;
  • 中间:代码区(带智能提示);
  • 右侧:参数面板(对应 [Parameter] 声明);
  • 底部:编译输出与日志。

2、两种开发路线

路线适合说明
内置 Automate 编辑器新手、中小策略零配置,改完点 Build 就能跑
Visual Studio + cTrader.Automate NuGet 包复杂项目、要版本管理dotnet add package cTrader.Automate,官方 SDK 出包格式 ALGO

3、文件结构

cAlgo/
├── Sources/cBots/ ← 你的 cBot 源码项目
├── Sources/Indicators/ ← 自定义指标
└── References/ ← 引用的第三方 DLL
白话注解:不想折腾就选内置编辑器。想做大的、要 Git 管代码,再上 Visual Studio。

五、第一个 cBot:双均线完整代码

这是能直接编译运行的最小完整策略:

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class DualMaBot : Robot
    {
        [Parameter("快线周期", DefaultValue = 10, Group = "均线")]
        public int FastPeriod { get; set; }

        [Parameter("慢线周期", DefaultValue = 30, Group = "均线")]
        public int SlowPeriod { get; set; }

        [Parameter("下单量(单位)", DefaultValue = 10000, MinValue = 1000, Step = 1000, Group = "资金")]
        public double TradeVolume { get; set; }

        private ExponentialMovingAverage _fastMa;
        private ExponentialMovingAverage _slowMa;

        protected override void OnStart()
        {
            _fastMa = Indicators.ExponentialMovingAverage(Bars.ClosePrices, FastPeriod);
            _slowMa = Indicators.ExponentialMovingAverage(Bars.ClosePrices, SlowPeriod);
        }

        protected override void OnBarClosed()
        {
var fastNow = _fastMa.Result.Last(1);
            var fastPrev = _fastMa.Result.Last(2);
var slowNow = _slowMa.Result.Last(1);
            var slowPrev = _slowMa.Result.Last(2);

            // 已有多仓就不重复开
            if (Positions.Find("DualMA", SymbolName, TradeType.Buy) != null) return;

            // 金叉
            if (fastNow > slowNow && fastPrev <= slowPrev)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, TradeVolume, "DualMA");
            }
            // 死叉
            else if (fastNow < slowNow && fastPrev >= slowPrev)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, TradeVolume, "DualMA");
            }
        }

        protected override void OnStop()
        {
            Print("cBot 停止");
        }
    }
}

逐块拆解:

  1. [Robot(...)] 特性:声明这是个 cBot,时区用 UTC,不申请额外权限;
  2. [Parameter(...)]:参数会出现在右侧面板,回测优化时自动遍历;
  3. OnStart:加载时跑一次,创建两条 EMA 指标对象;
  4. OnBarClosed:每根 K 线收盘跑一次,判断金叉死叉;
  5. Positions.Find(标签, 品种, 方向):查重,防止同一根 K 线反复开仓;
  6. ExecuteMarketOrder:市价下单,打上 "DualMA" 标签方便管理。
白话注解:标签(Label)就是你给每笔单子贴的便利贴。之后查仓、平仓、统计历史,都靠这个标签过滤,只动"自己开的单"。

六、生命周期:八个事件函数

Robot 基类(继承自 cAlgo.API.Internals.Algo)提供以下可重写的虚方法,全部经过官方包核验:

方法触发时机
OnStart()cBot 加载到图表时,跑一次
OnTick()每个报价变动
OnBar()新 K 线出现时
OnBarClosed()K 线收盘时(比 OnBar 更适合"收线确认"策略)
OnTimer()配合 Timer.Start(...) 定时触发
OnError(Error error)交易请求失败时回调
OnException()未捕获异常导致实例出错时
OnStop()cBot 卸载/停止时,跑一次

另有 GetFitness(GetFitnessArgs args),用于自定义优化适应度,见第十章。

1、OnTimer 用法

protected override void OnStart()
{
    // 每 60 秒触发一次 OnTimer
    Timer.Start(TimeSpan.FromSeconds(60));
}

protected override void OnTimer()
{
    Print("定时任务:", Server.Time);
    // 不需要时可以 Timer.Stop();
}

2、Stop():程序自己停下

if (Account.Equity <= Account.Balance * 0.8) // 净值回撤20%
{
    Print("触发熔断,cBot 自行停止");
    Stop();
}
白话注解:Stop() 是"自杀按钮"。净值回撤到阈值主动停机,是给策略上的最后一道保险。

七、参数系统 [Parameter]

参数写在属性上,cTrader 自动生成参数面板。ParameterAttribute 支持的全部命名参数(官方包核验):

参数作用
Name / 构造函数第一参面板显示名
DefaultValue默认值
MinValue / MaxValue取值范围(优化时也是边界)
Step步长(优化遍历的间隔)
Group分组名,面板里折叠显示
Description描述文字
[Parameter("止损(点)", DefaultValue = 50, MinValue = 10, MaxValue = 200, Step = 10, Group = "风控")]
public double StopLossPips { get; set; }

[Parameter("交易方向", DefaultValue = TradeType.Buy)]
public TradeType Direction { get; set; }

支持的参数类型:int / double / bool / string / enum / TimeFrame / DataSeries(价格源选择)等。字符串和枚举参数在面板上会显示为下拉框或输入框。

八、下单:ExecuteMarketOrder 与 TradeResult

1、市价单重载族

Robot.ExecuteMarketOrder 的重载按参数从少到多(官方包核验的最常用几个):

// 1) 最简:方向 + 品种 + 交易量(units)
ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000);

// 2) 带标签
ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000, "DualMA");

// 3) 带标签 + 止损/止盈(单位是"点/pips",可空)
ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000, "DualMA", 50, 100);

// 4) 再加注释 comment
ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000, "DualMA", 50, 100, "突破买入");
注意:第 3 种重载里的止损止盈是相对点数(pips),cTrader 会自动换算成价格挂上。别和"绝对价格"搞混。

2、返回值 TradeResult:必须检查

所有下单函数都返回 TradeResult,属性(官方包核验):

属性含义
IsSuccessful是否成功
Position成功时的持仓对象
PendingOrder挂单成功时的挂单对象
Error失败时的错误对象(Error.Code 是 ErrorCode)
var result = ExecuteMarketOrder(TradeType.Buy, SymbolName, TradeVolume, "DualMA", 50, 100);
if (!result.IsSuccessful)
{
    Print("下单失败:", result.Error.Code);
}

3、异步版本

每个交易函数都有 ...Async 版本(如 ExecuteMarketOrderAsync),不阻塞 OnTick,适合需要并发处理的场景。回调通过 Action<TradeResult> 传入。

ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, TradeVolume, "DualMA",
    r => Print("异步下单完成:", r.IsSuccessful));

九、挂单:Limit / Stop / StopLimit

1、三类挂单函数

// 限价单:低于现价买 / 高于现价卖
PlaceLimitOrder(TradeType.Buy, SymbolName, 10000, Symbol.Bid - 100 * Symbol.PipSize, "挂单");

// 止损单:突破价位才入场(追涨杀跌型)
PlaceStopOrder(TradeType.Buy, SymbolName, 10000, Symbol.Ask + 50 * Symbol.PipSize, "突破单");

// 止损限价单:先触发、再按限定区间成交
PlaceStopLimitOrder(TradeType.Buy, SymbolName, 10000,
    Symbol.Ask + 50 * Symbol.PipSize, 10, "止损限价单");

2、撤单与改单

// 撤单:遍历 PendingOrders 集合
foreach (var order in PendingOrders)
{
    if (order.Label == "DualMA" && order.SymbolName == SymbolName)
    {
        CancelPendingOrder(order);
    }
}

// 改单:ModifyPendingOrder(按订单类型选择对应重载)
// 挂单对象 PendingOrder 上可读:TargetPrice / StopLossPips / TakeProfitPips /
// ExpirationTime / OrderType / VolumeInUnits / DistancePips / StopLimitRangePips 等
白话注解:PendingOrders 是当前账户所有挂单的集合,遍历时一定先过滤 Label,不然会把手动挂的单也撤了。

十、持仓管理:查、平、改、反手

1、查仓:Find / FindAll / FindById

Positions 集合的三个查找方法(官方包核验签名):

Positions.Find(string label);
Positions.Find(string label, string symbolName);
Positions.Find(string label, string symbolName, TradeType tradeType); // 找不到返回 null

Positions.FindAll(string label); // 返回 Position[] 数组
Positions.FindAll(string label, string symbolName);
Positions.FindAll(string label, string symbolName, TradeType tradeType);

Positions.FindById(int positionId);

2、平仓:ClosePosition

// 平掉指定持仓
foreach (var pos in Positions.FindAll("DualMA", SymbolName, TradeType.Buy))
{
    ClosePosition(pos);
}

// 部分平仓:第二参传交易量
ClosePosition(pos, 5000);
警告:cAlgo 里没有 CloseAll() 这个一键平仓函数。本教程核查 cTrader.Automate 1.0.21 官方包,Robot 类和 Positions 接口上都不存在名为 CloseAll 的方法。想"平掉本策略全部仓位",标准写法是 FindAll 过滤后循环 ClosePosition:
// 正确的"全部平仓"写法
foreach (var pos in Positions.FindAll("DualMA", SymbolName))
{
    ClosePosition(pos);
}

3、改仓:ModifyPosition 与 Position 实例方法

Robot.ModifyPosition(position, stopLoss, takeProfit) 中的止损止盈是绝对价格(可空)。Position 对象上还有更细粒度的实例方法:

// 方式一:Robot.ModifyPosition(绝对价格)
ModifyPosition(pos, Symbol.Bid - 50 * Symbol.PipSize, pos.TakeProfit);

// 方式二:Position 实例方法(官方包核验,二选一用)
pos.ModifyStopLossPips(50); // 按点数改止损
pos.ModifyStopLossPrice(1.0850); // 按价格改止损
pos.ModifyTakeProfitPips(100);
pos.ModifyVolume(20000); // 改仓位交易量

4、反手:ReversePosition

平掉旧仓并立刻反向开同等仓位的单子,一个函数搞定:

var pos = Positions.Find("DualMA", SymbolName, TradeType.Buy);
if (pos != null && 死叉条件)
{
ReversePosition(pos); // 多翻空
}
白话注解:均线策略最常见的用法就是反手——金叉持多、死叉翻空。ReversePosition 比"先平后开"少一次请求,速度更快、空窗更短。

十一、OnBar / OnTick / OnBarClosed 怎么选

事件触发点适合坑
OnTick每个报价tick 级逻辑:追踪止损、风控盯净值一根K线触发几十上百次,信号逻辑放这里会重复开单
OnBar新K线诞生开盘才决策的策略新K线刚开,指标只能拿到"上一根"的完整值
OnBarClosedK线收盘收线确认型策略(最常见)信号比 OnBar 晚"一根K线诞生瞬间"一点点

推荐组合拳:

protected override void OnBarClosed()
{
    // 信号判断:只在这里做,用 Last(1) 已收盘数据
}

protected override void OnTick()
{
    // 仓位管理:追踪止损、移动止盈,逐 tick 跟
    foreach (var pos in Positions.FindAll("DualMA", SymbolName))
    {
        if (pos.TradeType == TradeType.Buy && pos.Pips > 30)
        {
            pos.ModifyStopLossPrice(Symbol.Bid - 30 * Symbol.PipSize);
        }
    }
}
白话注解:一句话记住——信号放收盘,风控放 tick。混在一起写,不是重复开单就是止损跟不上。

十二、手数与 VolumeInUnits 单位换算

cAlgo 交易函数的交易量单位是 VolumeInUnits(基础货币单位数),不是"手"。EURUSD 一标准手 = 100,000 units,所以 10000 = 0.1 手。

1、Symbol 上的手数工具(官方包核验)

成员含义
Symbol.VolumeInUnitsMin最小交易量
Symbol.VolumeInUnitsMax最大交易量
Symbol.VolumeInUnitsStep步长(不按步长下单会被拒)
Symbol.LotSize一标准手的 units 数
Symbol.QuantityToVolumeInUnits(double lots)手数 → units
Symbol.VolumeInUnitsToQuantity(double units)units → 手数
Symbol.NormalizeVolumeInUnits(double units, RoundingMode)把任意数量规整到合法值

2、标准防拒单写法

double volume = Symbol.QuantityToVolumeInUnits(0.5); // 0.5手 → units
volume = Symbol.NormalizeVolumeInUnits(volume, RoundingMode.Down); // 规整到合法步长
volume = Math.Max(Symbol.VolumeInUnitsMin,
Math.Min(Symbol.VolumeInUnitsMax, volume)); // 夹在最小最大之间

var result = ExecuteMarketOrder(TradeType.Buy, SymbolName, volume, "DualMA");
错误码 BadVolume 的头号来源:交易量不在 VolumeInUnitsMin/Max 区间内、或不符 VolumeInUnitsStep 步长。下单前用 12.2 的三步处理,一劳永逸。

十三、Position 对象全解

1、属性(官方包核验)

属性含义
Id / Label / Comment仓位号 / 标签 / 注释
SymbolName / Symbol品种名 / 品种对象
TradeType方向(Buy/Sell)
VolumeInUnits / Quantity交易量(units) / 手数
EntryPrice / EntryTime开仓价 / 开仓时间
CurrentPrice当前价
StopLoss / TakeProfit止损价 / 止盈价(可空)
GrossProfit / NetProfit浮动盈亏(毛/净,含点差佣金)
Pips浮动点数(正=赚)
Commissions / Swap佣金 / 隔夜利息
Margin该仓位占用保证金
HasTrailingStop是否启用了平台端追踪止损

2、实例方法(官方包核验)

方法作用
pos.Close()平掉该仓位
pos.ModifyStopLossPips(pips)按点数改止损
pos.ModifyStopLossPrice(price)按价格改止损
pos.ModifyTakeProfitPips(pips) / ModifyTakeProfitPrice(price)改止盈
pos.ModifyVolume(units)改仓位大小
pos.Reverse()反手
pos.ModifyTrailingStop(bool)开关追踪止损
pos.ModifyStopLossBreakEven(...)保本止损设置
// 经典保本逻辑:浮盈超过30点,止损挪到开仓价
foreach (var pos in Positions.FindAll("DualMA", SymbolName))
{
    if (pos.Pips > 30 && pos.StopLoss < pos.EntryPrice && pos.TradeType == TradeType.Buy)
    {
        pos.ModifyStopLossPrice(pos.EntryPrice);
    }
}
白话注解:能盈利的策略和能亏穿的策略,差距往往就在"浮盈后会不会把止损挪到成本线"。保本逻辑是性价比最高的风控,没有之一。

十四、交易历史 History

History 集合存已平仓的交易记录,元素类型 HistoricalTrade。

1、查询方法(官方包核验)

History.FindAll(string label);
History.FindAll(string label, string symbolName);
History.FindAll(string label, string symbolName, TradeType tradeType);

History.FindLast(string label); // 最近一笔
History.FindByPositionId(int positionId); // 按仓位号

2、HistoricalTrade 常用属性

属性含义
NetProfit / GrossProfit净盈亏 / 毛盈亏
Pips盈利点数
EntryPrice / ClosingPrice开仓价 / 平仓价
EntryTime / ClosingTime开仓时间 / 平仓时间
VolumeInUnits / Quantity交易量
Commissions / Swap / Balance佣金 / 隔夜利息 / 平仓后余额
PositionId / ClosingDealId关联仓位号 / 平仓成交号

3、实用例:连亏计数器

private int _consecutiveLosses;

protected override void OnBarClosed()
{
    var last = History.FindLast("DualMA");
    if (last != null && last.ClosingTime > Server.Time.AddDays(-1))
    {
        _consecutiveLosses = last.NetProfit < 0 ? _consecutiveLosses + 1 : 0;
    }

    // 连亏3笔停一天
    if (_consecutiveLosses >= 3)
    {
        Print("连亏3笔,暂停交易");
        Stop();
    }
}
白话注解:实盘和回测最大的差别是"状态"。连亏保护、日内次数上限这类规则,都得靠 History 回看历史才能实现。

十五、多品种与多周期

1、多周期:MarketData.GetBars

// 核验签名:GetBars(TimeFrame) 与 GetBars(TimeFrame, string symbolName)
private Bars _h4Bars;
private SimpleMovingAverage _h4Ma;

protected override void OnStart()
{
    _h4Bars = MarketData.GetBars(TimeFrame.Hour4);
_h4Ma = Indicators.SimpleMovingAverage(_h4Bars.ClosePrices, 50);
}

大周期定方向、小周期找入场,是双周期策略的标准结构:

protected override void OnBarClosed()
{
bool h4Bullish = _h4Bars.ClosePrices.Last(1) > _h4Ma.Result.Last(1); // 大周期多头

    if (h4Bullish && 金叉条件)
    {
        ExecuteMarketOrder(TradeType.Buy, SymbolName, TradeVolume, "DualMA");
    }
}

2、多品种:Symbols 与 GetBars 组合

// Symbols 方法核验:Exists / GetSymbol / GetSymbols
foreach (string name in new[] { "EURUSD", "GBPUSD", "USDJPY" })
{
    if (!Symbols.Exists(name)) continue;
var sym = Symbols.GetSymbol(name);
var bars = MarketData.GetBars(TimeFrame.Hour, name); // 跨品种取K线
    Print(name, " 最新收盘:", bars.ClosePrices.Last(1));
}

3、Tick 数据

var ticks = MarketData.GetTicks(SymbolName); // 核验签名:GetTicks(string symbolName)
var lastTick = ticks.LastTick;
注意:跨品种、跨周期数据是惰性加载的,第一次访问时才从服务器拉取,OnStart 里就取好存成员变量,别放到 OnTick 里反复请求。

十六、滑点控制:ExecuteMarketRangeOrder

普通市价单 ExecuteMarketOrder 不限制成交价,剧烈行情可能滑很远。ExecuteMarketRangeOrder(市价区间单)限定"最差可接受价":

// 核验最小签名:(TradeType, symbolName, volumeInUnits, marketRangePips, basePrice)
var result = ExecuteMarketRangeOrder(
    TradeType.Buy,
    SymbolName,
    TradeVolume,
20, // marketRangePips:最多容忍滑20点
Symbol.Ask, // basePrice:基准价(通常传当前 Ask/Bid)
    "DualMA");

if (!result.IsSuccessful)
{
    // 区间内没成交会被拒绝,ErrorCode 通常是 Timeout
    Print("区间单被拒:", result.Error.Code);
}
下单方式成交保证价格保证
ExecuteMarketOrder保证成交不保证价格
ExecuteMarketRangeOrder不保证成交保证不差于基准价+区间
白话注解:这就是"限价市价单"。数据公布瞬间点差乱飞,用区间单宁可不成交也不吃大滑点;平稳行情用普通市价单保证进得去。

十七、事件驱动进阶:Positions 事件与 Timer

1、Positions 三个事件

Positions 集合带 Opened / Closed / Modified 三个事件(官方包核验),不用在 OnTick 里轮询:

protected override void OnStart()
{
Positions.Opened += OnPositionOpened;
Positions.Closed += OnPositionClosed;
}

private void OnPositionOpened(PositionOpenedEventArgs args)
{
    Print("新仓开出:", args.Position.Label, " 方向:", args.Position.TradeType);
}

private void OnPositionClosed(PositionClosedEventArgs args)
{
    Print("仓位已平:", args.Position.Label, " 净盈亏:", args.Position.NetProfit);
}
注意:事件是全账户触发的——手动开的单、别的 cBot 开的单都会进来。回调里务必用 args.Position.Label 过滤,只处理自己的单。

2、定时任务:Timer + OnTimer

protected override void OnStart()
{
Timer.Start(TimeSpan.FromMinutes(15)); // 每15分钟
}

protected override void OnTimer()
{
    // 例:定期检查净值曲线,画日志
    Print("净值检查:", Account.Equity);
}

十八、图表交互与画图

cBot 运行所在的图表可以直接画东西,画布对象从 Chart 拿(ChartArea 提供 DrawText / DrawTrendLine 等方法,官方包核验):

protected override void OnBarClosed()
{
    // 收盘价在图表上标个记号
    Chart.DrawText("mark" + Bars.Count,
        Bars.ClosePrices.Last(1).ToString("F5"),
        Bars.OpenTimes.Last(1), Bars.ClosePrices.Last(1),
        Color.Orange);
}

回调里画信号箭头、画趋势线标记支撑位,回测复盘时图表就是天然的"策略日记"。

1、常用图表属性

Chart.SymbolName // 当前图表品种
Chart.TimeFrame // 当前图表周期
Chart.TopY / BottomY // 可视区域上下边界
Chart.IndexOfFirstVisibleBar
白话注解:把每次开平仓的原因画到图上。回测一眼扫过去就知道策略"死在哪",比翻日志快十倍。

十九、防重复下单与工程化

1、四种防重复手段(按强度递进)

// ① 查仓判重:最基础
if (Positions.Find("DualMA", SymbolName, TradeType.Buy) == null)
{
    ExecuteMarketOrder(TradeType.Buy, SymbolName, TradeVolume, "DualMA");
}

// ② 挂单也查:避免信号重复时挂单堆积
foreach (var o in PendingOrders)
    if (o.Label == "DualMA" && o.SymbolName == SymbolName) return;

// ③ 状态标记:同一根K线只处理一次
private DateTime _lastSignalBar;
if (_lastSignalBar == Bars.OpenTimes.Last(0)) return;
_lastSignalBar = Bars.OpenTimes.Last(0);

// ④ 冷却期:平仓后N分钟内不开新仓
private DateTime _lastCloseTime;
if ((Server.Time - _lastCloseTime).TotalMinutes < 30) return;

2、回测与实盘分流

// IsBacktesting:官方文档明确——回测模式返回 true
if (IsBacktesting)
{
    Print("回测中:跳过实盘通知等副作用");
}
else
{
    // 实盘才做的事:发通知、写外部日志等
}

3、时间与时区

Server.Time // 按 [Robot] 特性里 TimeZone 换算后的"服务器时间"
Server.TimeInUtc // UTC 时间(官方包核验)
Bars.OpenTimes // K线开盘时间
注意:做"时段过滤"(比如只做伦敦盘)时,先确认 [Robot(TimeZone = ...)] 设的是哪个时区。Server.Time 的含义取决于这个设置,搞错了时段过滤全偏。

4、日志与调试

Print("参数:", FastPeriod, " / ", SlowPeriod); // 输出到 Log 标签
// Sleep(int ms):暂停(回测里可用,实盘慎用,会阻塞事件)
// RefreshData():强制刷新行情数据

二十、本课小结

  • cAlgo = 真 C# + Robot 基类,生命周期 OnStart → OnTick/OnBar/OnBarClosed → OnStop;
  • 信号放 OnBarClosed 用 Last(1),风控放 OnTick 逐 tick 跟;
  • 开仓 ExecuteMarketOrder,务必检查 TradeResult.IsSuccessful;
  • 查仓 FindAll(label, symbol),平仓循环 ClosePosition——没有 CloseAll();
  • 交易量单位是 units,先 NormalizeVolumeInUnits 再下单,防 BadVolume;
  • 错误码分"可重试"与"不可重试",重试前先分类;
  • 优化时重写 GetFitness,把回撤罚进去,拒绝过拟合参数;
  • 实盘 24 小时运行上 Windows VPS。
本站固定免责声明:本教程仅讲解软件编程与操作方法,不构成投资建议或策略推荐。回测业绩不代表未来收益,EA自动交易风险大,请谨慎决策。
付费专享 · 核心知识点 7 章

继续阅读:核心知识点,搞懂原理

EA04 cAlgo/cTrader cBot 开发 — 共 27 章,已免费阅读 20 章,剩余 7 章核心知识点讲透「为什么这样、点了会怎样」。

二十一、内置指标:Indicators 工厂
二十二、回测与优化
二十三、Symbol 对象速查
二十四、错误处理:TradeResult / OnError / ErrorCode
二十五、FAQ 与 API 速查表
二十六、学习路线
…等共 7 章核心内容
✍️ 注册后扫码购买 🔐 已有账号,去登录 单套 ¥69 永久 · 会员可阅
SERVICE & PRICING
服务收费说明 · 明码标价,无隐藏收费
全部课程统一 ¥39-69,买一套永久使用。以下为购买与使用的完整流程。
01先免费试读

每套教程的「使用手册」部分均可免费阅读,先确认内容是否适合自己再决定,不必盲买。

02扫码付款

选定课程后点「立即解锁」,页面展示收款二维码与客服微信,扫码转账即可,无需注册海外支付。

03获取卡密

付款完成后客服第一时间发送卡密号与卡密密码,站内消息与微信同步通知。

04兑换解锁

在用户中心「兑换卡密」输入卡密号与密码,系统即时解锁全部章节,无需二次付款。

一次付费,终身可用 · 解锁后课程永久留在账号里,不限次数、不限时间阅读 持续免费更新 · 平台改版、规则变化时课程同步修订
RECOMMEND
相关教程推荐 · 同栏目更多上手机会

💬 用户评价 (0)

🔒 登录并成为会员后可发表评价
还没有评价 · 成为第一个评价的人吧 🎯