使用手册
功能在哪 · 是什么 · 怎么调出来一、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.IsLive | true=实盘,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 + PositionId | MagicNumber | MagicNumber |
1、MQL4 迁移对照表
| MQL4 习惯 | cAlgo 对应 |
|---|---|
OrderSend() | ExecuteMarketOrder(...) / PlaceLimitOrder(...) |
OrderClose() | ClosePosition(pos) |
OrderModify() | ModifyPosition / pos.ModifyStopLossPrice |
OrdersTotal() + 循环 | Positions.FindAll(label, symbol) |
MagicNumber | Label(字符串标签) |
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 / OnDeinit | OnStart / OnStop |
白话注解:迁移最大的思维转变是"标签代替魔法数字"。MQL4 里散落各处的 ticket 管理逻辑,在 cAlgo 里收敛成Label+FindAll两个词。
操作教程
一步一步 · 跟着做完一件事四、开发环境与 Automate 面板
1、打开方式
- 登录 cTrader 桌面版或网页版;
- 左侧菜单点 Automate(自动化);
- 点 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 停止");
}
}
}
逐块拆解:
[Robot(...)]特性:声明这是个 cBot,时区用 UTC,不申请额外权限;[Parameter(...)]:参数会出现在右侧面板,回测优化时自动遍历;OnStart:加载时跑一次,创建两条 EMA 指标对象;OnBarClosed:每根 K 线收盘跑一次,判断金叉死叉;Positions.Find(标签, 品种, 方向):查重,防止同一根 K 线反复开仓;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线刚开,指标只能拿到"上一根"的完整值 |
OnBarClosed | K线收盘 | 收线确认型策略(最常见) | 信号比 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自动交易风险大,请谨慎决策。
💬 用户评价 (0)
立即登录