跳转至

Grid cBot

策略概述

Grid cBot 实施了一种网格交易策略,其中以固定的价格间隔或“步长”为交易品种下达多个买入或卖出订单,形成“网格”仓位。 随着交易品种价格的变化,不断开立新仓位以利用波动。

Grid cBot 使用简单的算术和逻辑检查的组合来开仓、计算利润并管理其交易操作。 它在区间震荡或横盘市场中最为有效。 它在低波动性条件下以及已知阻力位和支撑位的市场中表现良好。

cBot 创建

了解如何通过我们的分步指南使用 C# 或 Python 创建 cBot

Grid cBot 的代码可在我们的公共 C#Python 代码库中找到。 相同的代码在 cTrader Windows 或 Mac 的算法创建向导中作为模板提供,或者您可以简单地复制并使用以下代码片段:

 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class Grid : Robot
    {
        [Parameter("Volume (lots)", DefaultValue = 0.01, MinValue = 0.01, Step = 0.01)]
        public double VolumeInLots { get; set; }

        [Parameter("Trade Side")]
        public TradeType TradeType { get; set; }

        [Parameter("Step (pips)", DefaultValue = 5, MinValue = 0.1, Step = 0.1)]
        public double StepPips { get; set; }

        [Parameter("Target Profit", DefaultValue = 20)]
        public double TargetProfit { get; set; }

        private bool enoughMoney = true;

        protected override void OnStart()
        {
            if (GridPositions.Length == 0)
                OpenPosition();
        }

        protected override void OnTick()
        {
            if (GridPositions.Sum(p => p.NetProfit) >= TargetProfit)
            {
                Print("Target profit is reached. Closing all grid positions");
                CloseGridPositions();
                Print("All grid positions are closed. Stopping cBot");
                Stop();
            }
            if (GridPositions.Length > 0 && enoughMoney)
            {
                var lastGridPosition = GridPositions.OrderBy(p => p.Pips).Last();
                var distance = CalculateDistanceInPips(lastGridPosition);

                if (distance >= StepPips)
                    OpenPosition();
            }
        }

        private Position[] GridPositions
        {
            get
            {
                return Positions
                    .Where(p => p.SymbolName == SymbolName && p.TradeType == TradeType)
                    .ToArray();
            }
        }

        private double CalculateDistanceInPips(Position position)
        {
            if (position.TradeType == TradeType.Buy)
                return (position.EntryPrice - Symbol.Ask) / Symbol.PipSize;
            else
                return (Symbol.Bid - position.EntryPrice) / Symbol.PipSize;
        }

        private void OpenPosition()
        {
            var result = ExecuteMarketOrder(TradeType, SymbolName, Symbol.QuantityToVolumeInUnits(VolumeInLots), "Grid");
            if (result.Error == ErrorCode.NoMoney)
            {
                enoughMoney = false;
                Print("Not enough money to open additional positions");
            }
        }

        private void CloseGridPositions()
        {
            while (GridPositions.Length > 0)
            {
                foreach (var position in GridPositions)
                {
                    ClosePosition(position);
                }
            }
        }
    }
}
 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
39
40
41
42
43
44
45
46
47
48
49
50
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *

# Import trading wrapper functions
from robot_wrapper import *

class GridSample():
    def on_start(self):
        self.volumeInUnits = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.enoughMoney = True
        if len(self.get_grid_positions()) == 0:
            self.open_position()

    def on_tick(self):
        grid_positions = self.get_grid_positions()
        net_profit_sum = sum([p.NetProfit for p in grid_positions])
        if net_profit_sum >= api.TargetProfit:
            api.Print("Target profit is reached. Closing all grid positions")
            self.close_grid_positions()
            api.Print("All grid positions are closed. Stopping cBot")
            api.Stop()

        if len(grid_positions) > 0 and self.enoughMoney == True:
            position_with_highest_pips = sorted(grid_positions, key=lambda pos: pos.Pips, reverse=True)[0]
            distance = self.get_distance_in_pips(position_with_highest_pips)          
            if distance >= api.StepPips:
                self.open_position()

    def get_grid_positions(self):
        return [pos for pos in api.Positions if pos.SymbolName == api.SymbolName and pos.TradeType == api.TradeType]

    def open_position(self):
        result = api.ExecuteMarketOrder(api.TradeType, api.SymbolName, self.volumeInUnits, "Grid")
        if result.Error == ErrorCode.NoMoney:
            self.enoughMoney = False
            api.Print("Not enough money to open additional positions")

    def close_grid_positions(self):
        for position in self.get_grid_positions():
            position.Close()

        if len(self.get_grid_positions()) > 0:
            self.close_grid_positions()

    def get_distance_in_pips(self, position):
        return (position.EntryPrice - api.Symbol.Ask if position.TradeType == TradeType.Buy else api.Symbol.Bid - position.EntryPrice) / api.Symbol.PipSize

指标集成

Grid cBot 不依赖任何指标来定义其网格策略;它不会尝试预测交易品种价格是上涨还是下跌。 相反,它从任何方向的价格波动中寻找盈利交易。

cBot 根据以下简单规则执行交易:如果交易品种价格移动超过指定的点数(由 StepPips 参数定义),cBot 将开立一个新头寸。

计算和逻辑

初始交易执行

当 cBot 启动时,它会检查网格中是否有任何未平仓头寸。 如果没有未平仓头寸,cBot 会立即通过 OpenPosition() 方法开立第一个头寸。 这第一步确保网格以基于指定交易方向 (TradeType) 的初始交易开始。

盈利监控

在每个 tick(例如,每次价格更新)时,cBot 会检查所有未平仓网格头寸的累计净盈利是否达到或超过指定的目标盈利 (TargetProfit)。 如果达到目标盈利,所有未平仓头寸将被平仓,cBot 停止运行。

目标盈利是网格中每个头寸的净盈利之和。

\[ \text{总净利润} = \sum_{i=1}^{n} \text{净利润}(p_i) \]

\(n\) – 网格中未平仓头寸的数量

\(ext{NetProfit}(p_i)\) – 头寸 \(i\) 的净盈利

开仓

如果未达到目标盈利且存在未平仓头寸,cBot 会计算最后一个开立的网格头寸与当前市场价格之间的距离(以点为单位)。

对于买入头寸:

\[ D_b = \frac{P_e - P_a}{S} \]

对于卖出头寸:

\[ D_s = \frac{P_b - P_e}{S} \]

\(D_b\) – 建仓价与当前卖出报价之间的距离(以点为单位)。

\(D_s\) – 当前买入报价与建仓价之间的距离(以点为单位)。

\(P_e\) – 交易品种最后一个买入头寸的开仓价格。

\(P_a\) – 交易品种的当前卖出报价。

\(P_b\) – 交易品种的当前买入报价。

\(S\) – 交易品种的每点价值。

如果计算的距离大于或等于定义的步长 (StepPips),则使用 OpenPosition() 方法开立一个新头寸。

每次开立新头寸之前,cBot 都会检查并确认有足够的资金进行操作。 如果资金不足,cBot 将停止尝试开立新头寸,并记录“没有足够的资金开立额外头寸”的消息。

平仓

一旦达到 TargetProfit,cBot 会记录“已达到目标盈利。 正在平掉所有网格头寸”的消息。

然后,CloseGridPositions() 方法会遍历网格中的所有未平仓头寸,将其平仓,然后 cBot 会记录“所有网格头寸已平仓。 停止 cBot”的消息。 随后,cBot 停止运行。

参数

参数 单位 定义 提示
数量 每笔交易的交易量。 保守型交易者可能会指定较小的手数,以减少敞口并谨慎管理风险,尤其是在波动性较大的市场中。 这样,如果市场走势与网格相反,任何潜在的损失都会被最小化。

激进型交易者可能会指定较大的手数,以在市场信心充足时最大化利润。 虽然这种方法风险较高,但当网格有效捕捉到大幅价格波动时,它会带来更大的回报。
交易方 交易方向(买入或卖出)。 在上涨市场中,交易者可能会选择买入。 当交易品种暂时回调下跌时,cBot 会在较低水平买入。 随着市场回升,每个买入头寸都会变得有利可图。

在下跌市场中,交易者可能会选择卖出。 当交易品种价格暂时回调上涨时,cBot 会在较高水平卖出。 随着市场回落,每个卖出头寸都会变得有利可图。
步骤 两个连续网格位置之间的距离。 步骤越小,新交易的开仓频率越高,而较大的步骤会导致交易次数减少。 在横盘或低波动性市场中,交易者可能会设置较小的值。 此设置确保 cBot 更频繁地开仓并捕捉小幅价格波动,尤其是在价格在窄幅区间内波动时。

在趋势性或波动性较大的市场中,交易者可能会设置较大的值。 此设置减少了开仓次数,使 cBot 能够捕捉更大的波动,并有助于在强劲趋势期间避免过度交易。
目标利润 账户货币中的累计利润,必须达到此值后 cBot 才会关闭所有未平仓头寸并停止交易。 寻求快速小额利润的交易者可能会设置较低的值。 此设置确保网格在获得小额利润后立即关闭,最大限度地降低在波动市场中持有头寸过久带来的风险。

偏好等待大额利润的交易者可以设置较高的值。 此设置允许网格保持更长时间的开放,使 cBot 能够在一系列较大的市场波动中累积利润,然后再关闭所有头寸。

应用

区间震荡或横盘市场

Grid cBot 在区间震荡市场中最为有效,价格在支撑位和阻力位之间振荡。 在此类市场中,由于价格经常在定义的区间内上下波动,cBot 会定期开仓并利用波动获利。

用例

考虑一种情景,EURUSD 保持在 1.1000 和 1.1100 之间的区间内。 当价格下跌接近 1.1000 时,cBot 会开立买入头寸,并在接近 1.1100 时开立卖出头寸。 随着价格在这些水平之间振荡,cBot 从每次波动中捕捉利润。

最佳实践

  • 设置较小的步骤大小以捕捉频繁的价格波动。
  • 设置适中的目标利润,使网格频繁关闭,从而确保获得小而一致的利润。
  • 监控市场新闻或即将发生的事件,这些事件可能导致区间突破并造成大幅回撤。

低波动市场

在低波动市场中,价格走势往往较为缓慢且不明显。 随着价格逐渐移动,Grid cBot 有充足的时间开设多个头寸,而不会面临市场大幅快速波动的风险。

用例

考虑一个场景,其中 USDJPY 表现出低波动性,这在亚洲交易时段经常发生。 Grid cBot 可以以较小的步长开设头寸,并捕捉在这些平静时段发生的小幅渐进价格波动。

最佳实践

  • 使用紧密的步长以利用低波动市场中典型的小幅价格波动。
  • 设置较低的交易量,以在波动性意外增加时降低风险。
  • 密切关注市场状况、意外新闻或事件,并准备快速停止 cBot。

已知水平的市场

Grid cBot 在具有强劲且明确的支撑和阻力水平的市场中表现良好。 这些水平充当价格障碍,导致市场频繁反弹,从而形成一个可供 cBot 利用的范围。

用例

考虑一个场景,其中黄金价格(XAUUSD)在 USD 2,000 的强劲支撑水平和 USD 2,050 的阻力水平之间波动。 Grid cBot 在 USD 2,000 附近开设买单,在 USD 2,050 附近开设卖单,从价格在这些水平之间的反弹中获利。

最佳实践

  • 根据市场波动性设置步长。 高波动市场可能需要较大的步长,而低波动市场可以使用较小的步长。
  • 在具有明确支撑和阻力水平的市场周围使用 Grid cBot,以避免被突破行情困住。
  • 注意可能导致突破的事件或新闻,以避免大额亏损。 cBot 无法处理市场突破支撑或阻力水平的情况。

非定向市场

某些货币对表现出非定向运动,价格频繁波动,但其价格并未显示出强劲的长期趋势。 Grid cBot 在此类市场中表现良好,因为它可以捕捉频繁波动,而不会被长期趋势困住。

用例

考虑一个场景,其中 EURCHF 以非定向方式移动,鉴于欧元区和瑞士经济的稳定性,这种情况很常见。 Grid cBot 可以以较小的步长开设头寸,从频繁波动中获利,而无需担心大幅定向趋势。

最佳实践

  • 使用较小的步长以捕捉频繁的价格波动。
  • 考虑设置较低的交易量,以在市场突破形成趋势时管理风险。

摘要

Grid cBot 的运作假设是,在牛市和熊市中,都会出现回调或回撤。 它在牛市中低买高卖,在熊市中高卖低买;当市场在回调后恢复主要趋势时,cBot 会捕捉利润。

由于 cBot 不使用指标,它无法评估市场是处于趋势还是盘整状态。 它仅根据价格走势开仓,忽略市场情绪或趋势强度。

没有指标使得交易过程完全机械化;cBot 仅依赖预定义的规则在固定间隔开仓。 虽然这种方法在盘整市场中可能有利可图,但在趋势明显、高度波动或快速变化的市场中往往表现不佳。

除了配置 cBot 以应用其个人策略外,交易者还可以考虑引入指标来根据市场条件过滤交易:

  • Moving Averages (MA) 可以帮助识别市场趋势,使 cBot 仅在趋势方向上进行交易。
  • Relative Strength Index (RSI) 可以发出超买或超卖信号,帮助 cBot 在市场单边拉伸时避免开设新头寸。
  • Bollinger Bands 可以提供价格波动性的洞察,使 cBot 能够调整网格间距或在波动性高时暂停交易。