在 GitHub 上查看
Up3x1 Krohabor Shift 策略
概览
up3x1 Krohabor D 策略移植自 MetaTrader 4 专家顾问 up3x1_Krohabor_D.mq4。策略核心思想是对三条前移的简单移动平均线 (SMA) 进行比较,以便在所选周期上识别趋势延续突破。本 C# 版本基于 StockSharp 的高层 API,通过 K 线订阅与指标绑定完成信号计算,同时针对 .NET 环境重新实现了风控与仓位管理。
交易逻辑
- 计算三条基于收盘价的简单移动平均线:
- 快速 SMA(默认 24 根 K 线)
- 中速 SMA(默认 60 根 K 线)
- 慢速 SMA(默认 120 根 K 线)
- 所有均线都会向前平移可配置的已完成 K 线数量(默认 6)。策略分别比较当前与上一根 K 线的移位值。
- 多头进场条件:
- 当前与上一根慢速 SMA 均位于当前与上一根快/中速 SMA 之下,显示出多头排列。
- 中速 SMA 相对快速 SMA 下行(上一根中速高于快速,当前中速低于快速)。
- 空头进场条件:与多头条件完全相反。
- 策略同一时间只允许一笔仓位;若当前没有持仓则等待新的入场信号,否则执行离场管理。
离场规则与保护
- 通过监控 K 线高低点来模拟保护性订单:
- 止损距离以最小价格步长(默认 110 点)表示,建仓后立即生效。
- 止盈同样使用价格步长表示(默认 5 点)。
- 当未实现盈亏超过阈值时(默认 10 点)启用移动止损,并始终沿盈利方向收紧止损价位。
- 若快速 SMA 再次穿越中速与慢速 SMA,则按照原始 EA 的逻辑立即平仓。
- 连续亏损后会自动减小下次下单手数,并受到最小手数限制,以模拟 MT4 版本的资金管理。
参数
| 名称 |
说明 |
FastPeriod |
快速 SMA 的周期。 |
MediumPeriod |
中速 SMA 的周期。 |
SlowPeriod |
慢速 SMA 的周期。 |
MaShift |
向前平移的已完成 K 线数量。 |
Volume |
新开仓的基础手数。 |
MinVolume |
连续亏损后允许的最小手数。 |
LossReductionFactor |
连续亏损时缩减手数所使用的除数。 |
StopLossPoints |
止损距离(价格步长)。 |
TakeProfitPoints |
止盈距离(价格步长)。 |
TrailingPoints |
移动止损距离与触发阈值(价格步长)。 |
CandleType |
用于分析的 K 线类型或周期。 |
备注
- 通过
SubscribeCandles 与 Bind 获取指标结果,无需手动读取指标值。
- 止损、止盈与移动止损均在策略内部模拟,可在实盘中替换为真实委托实现。
- 代码中的注释全部采用英文,以符合项目规范。
- 项目未附带自动化测试,请在 StockSharp 回测环境中验证不同参数组合。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Up3x1KrohaborShiftStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevMid; private bool _hasPrev;
private int _cooldown;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Up3x1KrohaborShiftStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 20).SetDisplay("Channel Period", "Channel lookback", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevMid = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevMid = mid;
return;
}
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevMid = mid;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class up3x1_krohabor_shift_strategy(Strategy):
def __init__(self):
super(up3x1_krohabor_shift_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 20).SetDisplay("Channel Period", "Channel lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown = 0
@property
def channel_period(self): return self._channel_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(up3x1_krohabor_shift_strategy, self).OnReseted()
self._prev_close = 0.0; self._prev_mid = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(up3x1_krohabor_shift_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
highest = Highest(); highest.Length = self.channel_period
lowest = Lowest(); lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): return
close = float(candle.ClosePrice)
mid = (float(highest) + float(lowest)) / 2.0
if not self._has_prev:
self._prev_close = close; self._prev_mid = mid; self._has_prev = True; return
if self._cooldown > 0:
self._cooldown -= 1; self._prev_close = close; self._prev_mid = mid; return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume); self._cooldown = 6
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume); self._cooldown = 6
self._prev_close = close; self._prev_mid = mid
def CreateClone(self): return up3x1_krohabor_shift_strategy()