Color Trend CF 策略
该策略是 MQL 专家 Exp_ColorTrend_CF 的移植版本。策略使用两条指数移动平均线来识别趋势变化。快速 EMA 对价格变化反应灵敏,慢速 EMA 用于过滤趋势。当快速 EMA 上穿慢速 EMA 时开多仓;当快速 EMA 下穿慢速 EMA 时开空仓。
参数
Period– 快速 EMA 的基础周期,慢速 EMA 使用其两倍。StopLoss– 以价格单位表示的止损距离。TakeProfit– 以价格单位表示的止盈距离。AllowBuyOpen– 允许开多仓。AllowSellOpen– 允许开空仓。AllowBuyClose– 允许在卖出信号时平多仓。AllowSellClose– 允许在买入信号时平空仓。CandleType– 指标计算使用的时间框架。
交易逻辑
- 订阅所选时间框架的K线。
- 计算快慢 EMA。
- 当快速 EMA 上穿慢速 EMA 时:
- 如允许则平空仓。
- 如允许则开多仓。
- 当快速 EMA 下穿慢速 EMA 时:
- 如允许则平多仓。
- 如允许则开空仓。
- 对已有仓位应用止损和止盈。
该实现使用 StockSharp 的高级 API 和指标绑定功能。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on crossing of two exponential moving averages.
/// Shorter EMA represents rising pressure while longer EMA acts as trend
/// filter.
/// </summary>
public class ColorTrendCfStrategy : Strategy {
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<bool> _allowBuyOpen;
private readonly StrategyParam<bool> _allowSellOpen;
private readonly StrategyParam<bool> _allowBuyClose;
private readonly StrategyParam<bool> _allowSellClose;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private bool _isLong;
/// <summary>
/// Base period for the fast EMA. Slow EMA uses double value.
/// </summary>
public int Period {
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Stop loss distance in price units.
/// </summary>
public decimal StopLoss {
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit distance in price units.
/// </summary>
public decimal TakeProfit {
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Whether long entries are allowed.
/// </summary>
public bool AllowBuyOpen {
get => _allowBuyOpen.Value;
set => _allowBuyOpen.Value = value;
}
/// <summary>
/// Whether short entries are allowed.
/// </summary>
public bool AllowSellOpen {
get => _allowSellOpen.Value;
set => _allowSellOpen.Value = value;
}
/// <summary>
/// Whether closing long positions on sell signals is allowed.
/// </summary>
public bool AllowBuyClose {
get => _allowBuyClose.Value;
set => _allowBuyClose.Value = value;
}
/// <summary>
/// Whether closing short positions on buy signals is allowed.
/// </summary>
public bool AllowSellClose {
get => _allowSellClose.Value;
set => _allowSellClose.Value = value;
}
/// <summary>
/// Candle type used for indicator calculation.
/// </summary>
public DataType CandleType {
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="ColorTrendCfStrategy"/>.
/// </summary>
public ColorTrendCfStrategy() {
_period = Param(nameof(Period), 30)
.SetGreaterThanZero()
.SetDisplay("CF Period", "Base period for fast EMA",
"Indicator")
.SetOptimize(10, 60, 10);
_stopLoss =
Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
.SetOptimize(100m, 2000m, 100m);
_takeProfit =
Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
.SetOptimize(100m, 4000m, 100m);
_allowBuyOpen = Param(nameof(AllowBuyOpen), true)
.SetDisplay("Allow Buy", "Permission to open long",
"Permissions");
_allowSellOpen =
Param(nameof(AllowSellOpen), true)
.SetDisplay("Allow Sell", "Permission to open short",
"Permissions");
_allowBuyClose =
Param(nameof(AllowBuyClose), true)
.SetDisplay("Close Long", "Allow closing long positions",
"Permissions");
_allowSellClose =
Param(nameof(AllowSellClose), true)
.SetDisplay("Close Short", "Allow closing short positions",
"Permissions");
_candleType =
Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator",
"General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)>
GetWorkingSecurities() {
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted() {
base.OnReseted();
_entryPrice = 0m;
_isLong = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time) {
base.OnStarted2(time);
var fastEma = new EMA { Length = Period };
var slowEma = new EMA { Length = Period * 2 };
var subscription = SubscribeCandles(CandleType);
var prevFast = 0m;
var prevSlow = 0m;
var initialized = false;
subscription
.Bind(fastEma, slowEma,
(candle, fast, slow) => {
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!initialized) {
prevFast = fast;
prevSlow = slow;
initialized = true;
return;
}
var crossUp = prevFast <= prevSlow && fast > slow;
var crossDown = prevFast >= prevSlow && fast < slow;
prevFast = fast;
prevSlow = slow;
if (crossUp) {
if (AllowSellClose && Position < 0)
BuyMarket(Math.Abs(Position));
if (AllowBuyOpen && Position <= 0) {
_entryPrice = candle.ClosePrice;
_isLong = true;
BuyMarket(Volume + Math.Abs(Position));
}
} else if (crossDown) {
if (AllowBuyClose && Position > 0)
SellMarket(Position);
if (AllowSellOpen && Position >= 0) {
_entryPrice = candle.ClosePrice;
_isLong = false;
SellMarket(Volume + Math.Abs(Position));
}
}
if (_entryPrice != 0m) {
if (_isLong && Position > 0) {
var stop = _entryPrice - StopLoss;
var take = _entryPrice + TakeProfit;
if (candle.LowPrice <= stop ||
candle.HighPrice >= take)
SellMarket(Position);
} else if (!_isLong && Position < 0) {
var stop = _entryPrice + StopLoss;
var take = _entryPrice - TakeProfit;
if (candle.HighPrice >= stop ||
candle.LowPrice <= take)
BuyMarket(Math.Abs(Position));
}
}
})
.Start();
StartProtection(null, null);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class color_trend_cf_strategy(Strategy):
def __init__(self):
super(color_trend_cf_strategy, self).__init__()
self._period = self.Param("Period", 30) \
.SetDisplay("CF Period", "Base period for fast EMA", "Indicator")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
self._allow_buy_open = self.Param("AllowBuyOpen", True) \
.SetDisplay("Allow Buy", "Permission to open long", "Permissions")
self._allow_sell_open = self.Param("AllowSellOpen", True) \
.SetDisplay("Allow Sell", "Permission to open short", "Permissions")
self._allow_buy_close = self.Param("AllowBuyClose", True) \
.SetDisplay("Close Long", "Allow closing long positions", "Permissions")
self._allow_sell_close = self.Param("AllowSellClose", True) \
.SetDisplay("Close Short", "Allow closing short positions", "Permissions")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for indicator", "General")
self._entry_price = 0.0
self._is_long = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._initialized = False
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def AllowBuyOpen(self):
return self._allow_buy_open.Value
@AllowBuyOpen.setter
def AllowBuyOpen(self, value):
self._allow_buy_open.Value = value
@property
def AllowSellOpen(self):
return self._allow_sell_open.Value
@AllowSellOpen.setter
def AllowSellOpen(self, value):
self._allow_sell_open.Value = value
@property
def AllowBuyClose(self):
return self._allow_buy_close.Value
@AllowBuyClose.setter
def AllowBuyClose(self, value):
self._allow_buy_close.Value = value
@property
def AllowSellClose(self):
return self._allow_sell_close.Value
@AllowSellClose.setter
def AllowSellClose(self, value):
self._allow_sell_close.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(color_trend_cf_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.Period
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.Period * 2
self.SubscribeCandles(self.CandleType) \
.Bind(fast_ema, slow_ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
if not self._initialized:
self._prev_fast = fast
self._prev_slow = slow
self._initialized = True
return
cross_up = self._prev_fast <= self._prev_slow and fast > slow
cross_down = self._prev_fast >= self._prev_slow and fast < slow
self._prev_fast = fast
self._prev_slow = slow
if cross_up:
if self.AllowSellClose and self.Position < 0:
self.BuyMarket(abs(self.Position))
if self.AllowBuyOpen and self.Position <= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long = True
self.BuyMarket()
elif cross_down:
if self.AllowBuyClose and self.Position > 0:
self.SellMarket(self.Position)
if self.AllowSellOpen and self.Position >= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long = False
self.SellMarket()
if self._entry_price != 0.0:
if self._is_long and self.Position > 0:
stop = self._entry_price - float(self.StopLoss)
take = self._entry_price + float(self.TakeProfit)
if float(candle.LowPrice) <= stop or float(candle.HighPrice) >= take:
self.SellMarket(self.Position)
elif not self._is_long and self.Position < 0:
stop = self._entry_price + float(self.StopLoss)
take = self._entry_price - float(self.TakeProfit)
if float(candle.HighPrice) >= stop or float(candle.LowPrice) <= take:
self.BuyMarket(abs(self.Position))
def OnReseted(self):
super(color_trend_cf_strategy, self).OnReseted()
self._entry_price = 0.0
self._is_long = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._initialized = False
def CreateClone(self):
return color_trend_cf_strategy()