ビッグランナー戦略
ビッグランナー戦略は、終値と高速SMAが両方とも低速SMAの方向にクロスしたとき、すなわち強いモメンタムを示したときに取引します。ポジションサイズはポートフォリオ価値のパーセンテージにレバレッジを掛けた値から算出されます。オプションのストップロスとテイクプロフィットのレベルでリスクを管理します。
詳細
- エントリー条件:
- 終値が高速SMAを上抜け、かつ高速SMAが低速SMAを上抜けたときに買い。
- 終値が高速SMAを下抜け、かつ高速SMAが低速SMAを下抜けたときに売り。
- ロング/ショート: ロングとショート。
- エグジット条件:
- エントリー価格に基づくオプションのストップロスとテイクプロフィット。
- 逆シグナルで既存ポジションをクローズ。
- ストップ: ストップロスとテイクプロフィットのパーセンテージは設定可能。
- デフォルト値:
FastLength= 5SlowLength= 20TakeProfitLongPercent= 4TakeProfitShortPercent= 7StopLossLongPercent= 2StopLossShortPercent= 2PercentOfPortfolio= 10Leverage= 1
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: ロング & ショート
- インジケーター: SMA
- ストップ: はい
- 複雑さ: 低
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Big Runner strategy.
/// Buys when on the same candle the close crosses above the fast SMA and the fast SMA crosses above the slow SMA, and sells on the
/// mirrored crosses; an opposite signal reverses the position. The order size is PercentOfPortfolio of the portfolio value times
/// Leverage divided by the price. Separate long and short percent stop losses and take profits are measured from the entry close
/// (0 disables a level).
/// </summary>
public class BigRunnerStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _takeProfitLongPercent;
private readonly StrategyParam<decimal> _takeProfitShortPercent;
private readonly StrategyParam<decimal> _stopLossLongPercent;
private readonly StrategyParam<decimal> _stopLossShortPercent;
private readonly StrategyParam<decimal> _percentOfPortfolio;
private readonly StrategyParam<decimal> _leverage;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevClose;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal _entryPrice;
/// <summary>
/// Fast SMA period.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// Long take profit in percent.
/// </summary>
public decimal TakeProfitLongPercent
{
get => _takeProfitLongPercent.Value;
set => _takeProfitLongPercent.Value = value;
}
/// <summary>
/// Short take profit in percent.
/// </summary>
public decimal TakeProfitShortPercent
{
get => _takeProfitShortPercent.Value;
set => _takeProfitShortPercent.Value = value;
}
/// <summary>
/// Long stop loss in percent.
/// </summary>
public decimal StopLossLongPercent
{
get => _stopLossLongPercent.Value;
set => _stopLossLongPercent.Value = value;
}
/// <summary>
/// Short stop loss in percent.
/// </summary>
public decimal StopLossShortPercent
{
get => _stopLossShortPercent.Value;
set => _stopLossShortPercent.Value = value;
}
/// <summary>
/// Share of the portfolio value used per trade, in percent.
/// </summary>
public decimal PercentOfPortfolio
{
get => _percentOfPortfolio.Value;
set => _percentOfPortfolio.Value = value;
}
/// <summary>
/// Leverage applied to the position value.
/// </summary>
public decimal Leverage
{
get => _leverage.Value;
set => _leverage.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BigRunnerStrategy()
{
_fastLength = Param(nameof(FastLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast SMA period", "Indicators");
_slowLength = Param(nameof(SlowLength), 20)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow SMA period", "Indicators");
_takeProfitLongPercent = Param(nameof(TakeProfitLongPercent), 4m)
.SetNotNegative()
.SetDisplay("Take Profit Long %", "Long take profit in percent", "Risk");
_takeProfitShortPercent = Param(nameof(TakeProfitShortPercent), 7m)
.SetNotNegative()
.SetDisplay("Take Profit Short %", "Short take profit in percent", "Risk");
_stopLossLongPercent = Param(nameof(StopLossLongPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss Long %", "Long stop loss in percent", "Risk");
_stopLossShortPercent = Param(nameof(StopLossShortPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss Short %", "Short stop loss in percent", "Risk");
_percentOfPortfolio = Param(nameof(PercentOfPortfolio), 10m)
.SetGreaterThanZero()
.SetDisplay("Percent Of Portfolio", "Share of the portfolio value used per trade", "Money Management");
_leverage = Param(nameof(Leverage), 1m)
.SetGreaterThanZero()
.SetDisplay("Leverage", "Leverage applied to the position value", "Money Management");
_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();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var fast = new SimpleMovingAverage { Length = FastLength };
var slow = new SimpleMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ResetState()
{
_prevClose = null;
_prevFast = null;
_prevSlow = null;
_entryPrice = 0;
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var prevClose = _prevClose;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevClose = close;
_prevFast = fast;
_prevSlow = slow;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (CheckStops(candle))
return;
if (prevClose is not decimal pc || prevFast is not decimal pf || prevSlow is not decimal ps)
return;
var longSignal = pc <= pf && close > fast && pf <= ps && fast > slow;
var shortSignal = pc >= pf && close < fast && pf >= ps && fast < slow;
if (longSignal && Position <= 0)
{
BuyMarket(GetOrderVolume(close) + Math.Abs(Position));
_entryPrice = close;
}
else if (shortSignal && Position >= 0)
{
SellMarket(GetOrderVolume(close) + Math.Abs(Position));
_entryPrice = close;
}
}
private bool CheckStops(ICandleMessage candle)
{
if (_entryPrice <= 0)
return false;
if (Position > 0)
{
var stopHit = StopLossLongPercent > 0 && candle.LowPrice <= _entryPrice * (1m - StopLossLongPercent / 100m);
var takeHit = TakeProfitLongPercent > 0 && candle.HighPrice >= _entryPrice * (1m + TakeProfitLongPercent / 100m);
if (stopHit || takeHit)
{
SellMarket(Position);
_entryPrice = 0;
return true;
}
}
else if (Position < 0)
{
var stopHit = StopLossShortPercent > 0 && candle.HighPrice >= _entryPrice * (1m + StopLossShortPercent / 100m);
var takeHit = TakeProfitShortPercent > 0 && candle.LowPrice <= _entryPrice * (1m - TakeProfitShortPercent / 100m);
if (stopHit || takeHit)
{
BuyMarket(-Position);
_entryPrice = 0;
return true;
}
}
return false;
}
private decimal GetOrderVolume(decimal price)
{
var equity = Portfolio?.CurrentValue ?? Portfolio?.BeginValue ?? 0m;
if (equity <= 0 || price <= 0)
return Volume;
var volume = equity * PercentOfPortfolio / 100m * Leverage / price;
if (Security?.VolumeStep is decimal step && step > 0)
volume = Math.Floor(volume / step) * step;
if (Security?.MaxVolume is decimal maxVolume && maxVolume > 0 && volume > maxVolume)
volume = maxVolume;
return volume > 0 ? volume : Volume;
}
}
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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class big_runner_strategy(Strategy):
"""
Big Runner strategy.
Buys when on the same candle the close crosses above the fast SMA and the fast SMA crosses above the slow SMA, and sells on the
mirrored crosses; an opposite signal reverses the position. The order size is PercentOfPortfolio of the portfolio value times
Leverage divided by the price. Separate long and short percent stop losses and take profits are measured from the entry close
(0 disables a level).
"""
def __init__(self):
super(big_runner_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 5).SetGreaterThanZero().SetDisplay("Fast Length", "Fast SMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 20).SetGreaterThanZero().SetDisplay("Slow Length", "Slow SMA period", "Indicators")
self._take_profit_long_percent = self.Param("TakeProfitLongPercent", 4.0).SetNotNegative().SetDisplay("Take Profit Long %", "Long take profit in percent", "Risk")
self._take_profit_short_percent = self.Param("TakeProfitShortPercent", 7.0).SetNotNegative().SetDisplay("Take Profit Short %", "Short take profit in percent", "Risk")
self._stop_loss_long_percent = self.Param("StopLossLongPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss Long %", "Long stop loss in percent", "Risk")
self._stop_loss_short_percent = self.Param("StopLossShortPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss Short %", "Short stop loss in percent", "Risk")
self._percent_of_portfolio = self.Param("PercentOfPortfolio", 10.0).SetGreaterThanZero().SetDisplay("Percent Of Portfolio", "Share of the portfolio value used per trade", "Money Management")
self._leverage = self.Param("Leverage", 1.0).SetGreaterThanZero().SetDisplay("Leverage", "Leverage applied to the position value", "Money Management")
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_close = None
self._prev_fast = None
self._prev_slow = None
self._entry_price = Decimal(0)
def OnReseted(self):
super(big_runner_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(big_runner_strategy, self).OnStarted2(time)
self._reset_state()
fast = SimpleMovingAverage()
fast.Length = self._fast_length.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
prev_close = self._prev_close
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_close = close
self._prev_fast = fast
self._prev_slow = slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._check_stops(candle):
return
if prev_close is None or prev_fast is None or prev_slow is None:
return
long_signal = prev_close <= prev_fast and close > fast and prev_fast <= prev_slow and fast > slow
short_signal = prev_close >= prev_fast and close < fast and prev_fast >= prev_slow and fast < slow
if long_signal and self.Position <= 0:
self.BuyMarket(self._get_order_volume(close) + abs(self.Position))
self._entry_price = close
elif short_signal and self.Position >= 0:
self.SellMarket(self._get_order_volume(close) + abs(self.Position))
self._entry_price = close
def _check_stops(self, candle):
if self._entry_price <= 0:
return False
hundred = Decimal(100)
one = Decimal(1)
if self.Position > 0:
sl = Decimal(self._stop_loss_long_percent.Value)
tp = Decimal(self._take_profit_long_percent.Value)
stop_hit = sl > 0 and candle.LowPrice <= self._entry_price * (one - sl / hundred)
take_hit = tp > 0 and candle.HighPrice >= self._entry_price * (one + tp / hundred)
if stop_hit or take_hit:
self.SellMarket(self.Position)
self._entry_price = Decimal(0)
return True
elif self.Position < 0:
sl = Decimal(self._stop_loss_short_percent.Value)
tp = Decimal(self._take_profit_short_percent.Value)
stop_hit = sl > 0 and candle.HighPrice >= self._entry_price * (one + sl / hundred)
take_hit = tp > 0 and candle.LowPrice <= self._entry_price * (one - tp / hundred)
if stop_hit or take_hit:
self.BuyMarket(-self.Position)
self._entry_price = Decimal(0)
return True
return False
def _get_order_volume(self, price):
portfolio = self.Portfolio
equity = None
if portfolio is not None:
equity = portfolio.CurrentValue if portfolio.CurrentValue is not None else portfolio.BeginValue
if equity is None or equity <= 0 or price <= 0:
return self.Volume
volume = equity * Decimal(self._percent_of_portfolio.Value) / Decimal(100) * Decimal(self._leverage.Value) / price
security = self.Security
if security is not None:
step = security.VolumeStep
if step is not None and step > 0:
volume = Math.Floor(volume / step) * step
max_volume = security.MaxVolume
if max_volume is not None and max_volume > 0 and volume > max_volume:
volume = max_volume
return volume if volume > 0 else self.Volume
def CreateClone(self):
return big_runner_strategy()