Omega Galsky 策略
基于 EMA 交叉并带有保本移动止损的策略。
详情
- 入场条件:EMA8 上穿/下穿 EMA21 并结合 EMA89 的价格过滤。
- 多空:双向。
- 出场条件:止损、止盈或反向信号。
- 止损:是。
- 默认参数:
Ema8Period= 8Ema21Period= 21Ema89Period= 89FixedRiskReward= 1.0mSlPercentage= 0.001mTpPercentage= 0.0025mCandleType= TimeSpan.FromMinutes(1)
- 过滤器:
- 分类:趋势
- 方向:双向
- 指标:EMA
- 止损:是
- 复杂度:基础
- 时间框架:日内 (1m)
- 季节性:否
- 神经网络:否
- 背离:否
- 风险等级:中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Omega Galsky strategy.
/// A long opens when the fast EMA crosses above the middle EMA while the close is above the slow EMA, a short on the opposite cross
/// with the close below the slow EMA; an opposite signal reverses the position. The stop and the target are fractions of the entry
/// price (SlPercentage and TpPercentage). Once price moves FixedRiskReward times the initial risk in favour, the stop moves to break-even.
/// </summary>
public class OmegaGalskyStrategy : Strategy
{
private readonly StrategyParam<int> _ema8Period;
private readonly StrategyParam<int> _ema21Period;
private readonly StrategyParam<int> _ema89Period;
private readonly StrategyParam<decimal> _fixedRiskReward;
private readonly StrategyParam<decimal> _slPercentage;
private readonly StrategyParam<decimal> _tpPercentage;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevMiddle;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
private bool _breakEvenDone;
/// <summary>
/// Fast EMA period.
/// </summary>
public int Ema8Period
{
get => _ema8Period.Value;
set => _ema8Period.Value = value;
}
/// <summary>
/// Middle EMA period.
/// </summary>
public int Ema21Period
{
get => _ema21Period.Value;
set => _ema21Period.Value = value;
}
/// <summary>
/// Slow EMA period used as the price confirmation.
/// </summary>
public int Ema89Period
{
get => _ema89Period.Value;
set => _ema89Period.Value = value;
}
/// <summary>
/// Profit in multiples of the initial risk that moves the stop to break-even.
/// </summary>
public decimal FixedRiskReward
{
get => _fixedRiskReward.Value;
set => _fixedRiskReward.Value = value;
}
/// <summary>
/// Stop loss as a fraction of the entry price.
/// </summary>
public decimal SlPercentage
{
get => _slPercentage.Value;
set => _slPercentage.Value = value;
}
/// <summary>
/// Take profit as a fraction of the entry price.
/// </summary>
public decimal TpPercentage
{
get => _tpPercentage.Value;
set => _tpPercentage.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public OmegaGalskyStrategy()
{
_ema8Period = Param(nameof(Ema8Period), 8)
.SetGreaterThanZero()
.SetDisplay("EMA 8 Period", "Fast EMA period", "Indicators");
_ema21Period = Param(nameof(Ema21Period), 21)
.SetGreaterThanZero()
.SetDisplay("EMA 21 Period", "Middle EMA period", "Indicators");
_ema89Period = Param(nameof(Ema89Period), 89)
.SetGreaterThanZero()
.SetDisplay("EMA 89 Period", "Slow EMA period used as price confirmation", "Indicators");
_fixedRiskReward = Param(nameof(FixedRiskReward), 1.0m)
.SetNotNegative()
.SetDisplay("Break-Even R", "Profit in multiples of risk that moves the stop to break-even", "Risk");
_slPercentage = Param(nameof(SlPercentage), 0.001m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss as a fraction of entry price", "Risk");
_tpPercentage = Param(nameof(TpPercentage), 0.0025m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit as a fraction of entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevFast = null;
_prevMiddle = null;
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
_breakEvenDone = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ema8 = new ExponentialMovingAverage { Length = Ema8Period };
var ema21 = new ExponentialMovingAverage { Length = Ema21Period };
var ema89 = new ExponentialMovingAverage { Length = Ema89Period };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema8, ema21, ema89, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema8);
DrawIndicator(area, ema21);
DrawIndicator(area, ema89);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal middle, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
var prevFast = _prevFast;
var prevMiddle = _prevMiddle;
_prevFast = fast;
_prevMiddle = middle;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (ManagePosition(candle))
return;
if (prevFast is not decimal pf || prevMiddle is not decimal pm)
return;
var close = candle.ClosePrice;
if (pf <= pm && fast > middle && close > slow && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
SetLevels(close, true);
}
else if (pf >= pm && fast < middle && close < slow && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
SetLevels(close, false);
}
}
private void SetLevels(decimal entry, bool isLong)
{
_entryPrice = entry;
_breakEvenDone = false;
var stopDistance = entry * SlPercentage;
var takeDistance = entry * TpPercentage;
_stopPrice = SlPercentage > 0 ? (isLong ? entry - stopDistance : entry + stopDistance) : 0m;
_takePrice = TpPercentage > 0 ? (isLong ? entry + takeDistance : entry - takeDistance) : 0m;
}
// Returns true when the position was closed on this candle.
private bool ManagePosition(ICandleMessage candle)
{
var risk = _entryPrice * SlPercentage;
if (Position > 0)
{
if (_stopPrice > 0 && candle.LowPrice <= _stopPrice)
{
SellMarket(Position);
return true;
}
if (_takePrice > 0 && candle.HighPrice >= _takePrice)
{
SellMarket(Position);
return true;
}
if (!_breakEvenDone && risk > 0 && candle.HighPrice - _entryPrice >= risk * FixedRiskReward)
{
_stopPrice = _entryPrice;
_breakEvenDone = true;
}
}
else if (Position < 0)
{
if (_stopPrice > 0 && candle.HighPrice >= _stopPrice)
{
BuyMarket(-Position);
return true;
}
if (_takePrice > 0 && candle.LowPrice <= _takePrice)
{
BuyMarket(-Position);
return true;
}
if (!_breakEvenDone && risk > 0 && _entryPrice - candle.LowPrice >= risk * FixedRiskReward)
{
_stopPrice = _entryPrice;
_breakEvenDone = true;
}
}
return false;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class omega_galsky_strategy(Strategy):
"""
Omega Galsky strategy.
A long opens when the fast EMA crosses above the middle EMA while the close is above the slow EMA, a short on the opposite cross
with the close below the slow EMA; an opposite signal reverses the position. The stop and the target are fractions of the entry
price (SlPercentage and TpPercentage). Once price moves FixedRiskReward times the initial risk in favour, the stop moves to break-even.
"""
def __init__(self):
super(omega_galsky_strategy, self).__init__()
self._ema8_period = self.Param("Ema8Period", 8).SetGreaterThanZero().SetDisplay("EMA 8 Period", "Fast EMA period", "Indicators")
self._ema21_period = self.Param("Ema21Period", 21).SetGreaterThanZero().SetDisplay("EMA 21 Period", "Middle EMA period", "Indicators")
self._ema89_period = self.Param("Ema89Period", 89).SetGreaterThanZero().SetDisplay("EMA 89 Period", "Slow EMA period used as price confirmation", "Indicators")
self._fixed_risk_reward = self.Param("FixedRiskReward", 1.0).SetNotNegative().SetDisplay("Break-Even R", "Profit in multiples of risk that moves the stop to break-even", "Risk")
self._sl_percentage = self.Param("SlPercentage", 0.001).SetNotNegative().SetDisplay("Stop Loss", "Stop loss as a fraction of entry price", "Risk")
self._tp_percentage = self.Param("TpPercentage", 0.0025).SetNotNegative().SetDisplay("Take Profit", "Take profit as a fraction of entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_fast = None
self._prev_middle = None
self._entry_price = Decimal(0)
self._stop_price = Decimal(0)
self._take_price = Decimal(0)
self._break_even_done = False
def OnReseted(self):
super(omega_galsky_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(omega_galsky_strategy, self).OnStarted2(time)
self._reset_state()
ema8 = ExponentialMovingAverage()
ema8.Length = self._ema8_period.Value
ema21 = ExponentialMovingAverage()
ema21.Length = self._ema21_period.Value
ema89 = ExponentialMovingAverage()
ema89.Length = self._ema89_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema8, ema21, ema89, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema8)
self.DrawIndicator(area, ema21)
self.DrawIndicator(area, ema89)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast, middle, slow):
if candle.State != CandleStates.Finished:
return
prev_fast = self._prev_fast
prev_middle = self._prev_middle
self._prev_fast = fast
self._prev_middle = middle
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._manage_position(candle):
return
if prev_fast is None or prev_middle is None:
return
close = candle.ClosePrice
if prev_fast <= prev_middle and fast > middle and close > slow and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._set_levels(close, True)
elif prev_fast >= prev_middle and fast < middle and close < slow and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._set_levels(close, False)
def _set_levels(self, entry, is_long):
self._entry_price = entry
self._break_even_done = False
sl = Decimal(self._sl_percentage.Value)
tp = Decimal(self._tp_percentage.Value)
stop_distance = entry * sl
take_distance = entry * tp
if sl > 0:
self._stop_price = entry - stop_distance if is_long else entry + stop_distance
else:
self._stop_price = Decimal(0)
if tp > 0:
self._take_price = entry + take_distance if is_long else entry - take_distance
else:
self._take_price = Decimal(0)
# Returns True when the position was closed on this candle.
def _manage_position(self, candle):
risk = self._entry_price * Decimal(self._sl_percentage.Value)
trigger = risk * Decimal(self._fixed_risk_reward.Value)
if self.Position > 0:
if self._stop_price > 0 and candle.LowPrice <= self._stop_price:
self.SellMarket(self.Position)
return True
if self._take_price > 0 and candle.HighPrice >= self._take_price:
self.SellMarket(self.Position)
return True
if not self._break_even_done and risk > 0 and candle.HighPrice - self._entry_price >= trigger:
self._stop_price = self._entry_price
self._break_even_done = True
elif self.Position < 0:
if self._stop_price > 0 and candle.HighPrice >= self._stop_price:
self.BuyMarket(-self.Position)
return True
if self._take_price > 0 and candle.LowPrice <= self._take_price:
self.BuyMarket(-self.Position)
return True
if not self._break_even_done and risk > 0 and self._entry_price - candle.LowPrice >= trigger:
self._stop_price = self._entry_price
self._break_even_done = True
return False
def CreateClone(self):
return omega_galsky_strategy()