ハンマーと流れ星戦略
この戦略はHammerとShooting Starのローソク足パターンを取引します。 前のローソク足がHammerの場合にロングポジションを建て、 Shooting Starの場合にショートポジションに入ります。 エグジットはシグナルローソク足の高値と安値をテイクプロフィットとストップロスに使用します。
詳細
- エントリー条件:
- ロング: 前のローソク足がHammer
- ショート: 前のローソク足がShooting Star
- ロング/ショート: 両方
- エグジット条件: 前のローソク足の安値/高値でストップロスとテイクプロフィット
- ストップ: あり、シグナルローソク足の極値で固定
- デフォルト値:
WickFactor= 0.9MaxOppositeWickFactor= 0.45MinBodyRangePct= 0.2CandleType= 1分
- フィルター:
- カテゴリ: パターン
- 方向: 両方
- インジケーター: ローソク足
- ストップ: はい
- 複雑さ: 基本
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Hammer and Shooting Star strategy.
/// A candle whose body is at least MinBodyRangePct of its range is a hammer when its lower wick is at least WickFactor times the
/// body and its upper wick at most MaxOppositeWickFactor times the body; a shooting star mirrors this. After a hammer closes the
/// strategy buys with the stop at the hammer's low and the target at its high; after a shooting star it sells with the stop at
/// its high and the target at its low. An opposite pattern reverses the position.
/// </summary>
public class HammerShootingStarStrategy : Strategy
{
private readonly StrategyParam<decimal> _wickFactor;
private readonly StrategyParam<decimal> _maxOppositeWickFactor;
private readonly StrategyParam<decimal> _minBodyRangePct;
private readonly StrategyParam<DataType> _candleType;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Minimum main wick length as a multiple of the body.
/// </summary>
public decimal WickFactor
{
get => _wickFactor.Value;
set => _wickFactor.Value = value;
}
/// <summary>
/// Maximum opposite wick length as a multiple of the body.
/// </summary>
public decimal MaxOppositeWickFactor
{
get => _maxOppositeWickFactor.Value;
set => _maxOppositeWickFactor.Value = value;
}
/// <summary>
/// Minimum body size as a fraction of the candle range.
/// </summary>
public decimal MinBodyRangePct
{
get => _minBodyRangePct.Value;
set => _minBodyRangePct.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HammerShootingStarStrategy()
{
_wickFactor = Param(nameof(WickFactor), 0.9m)
.SetGreaterThanZero()
.SetDisplay("Wick Factor", "Minimum main wick length as a multiple of the body", "Pattern");
_maxOppositeWickFactor = Param(nameof(MaxOppositeWickFactor), 0.45m)
.SetNotNegative()
.SetDisplay("Max Opposite Wick", "Maximum opposite wick length as a multiple of the body", "Pattern");
_minBodyRangePct = Param(nameof(MinBodyRangePct), 0.2m)
.SetNotNegative()
.SetDisplay("Min Body/Range", "Minimum body size as a fraction of the candle range", "Pattern");
_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();
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_stopPrice = 0m;
_takePrice = 0m;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Exits of the open position against the signal candle levels; the stop is checked first.
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
{
SellMarket(Position);
return;
}
}
else if (Position < 0)
{
if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice)
{
BuyMarket(-Position);
return;
}
}
var range = candle.HighPrice - candle.LowPrice;
if (range <= 0)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
if (body <= 0 || body < MinBodyRangePct * range)
return;
var upperWick = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);
var lowerWick = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
var isHammer = lowerWick >= WickFactor * body && upperWick <= MaxOppositeWickFactor * body;
var isShootingStar = upperWick >= WickFactor * body && lowerWick <= MaxOppositeWickFactor * body;
if (isHammer && !isShootingStar && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = candle.LowPrice;
_takePrice = candle.HighPrice;
}
else if (isShootingStar && !isHammer && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = candle.HighPrice;
_takePrice = candle.LowPrice;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class hammer_shooting_star_strategy(Strategy):
"""
Hammer and Shooting Star strategy.
A candle whose body is at least MinBodyRangePct of its range is a hammer when its lower wick is at least WickFactor times the
body and its upper wick at most MaxOppositeWickFactor times the body; a shooting star mirrors this. After a hammer closes the
strategy buys with the stop at the hammer's low and the target at its high; after a shooting star it sells with the stop at
its high and the target at its low. An opposite pattern reverses the position.
"""
def __init__(self):
super(hammer_shooting_star_strategy, self).__init__()
self._wick_factor = self.Param("WickFactor", 0.9).SetGreaterThanZero().SetDisplay("Wick Factor", "Minimum main wick length as a multiple of the body", "Pattern")
self._max_opposite_wick_factor = self.Param("MaxOppositeWickFactor", 0.45).SetNotNegative().SetDisplay("Max Opposite Wick", "Maximum opposite wick length as a multiple of the body", "Pattern")
self._min_body_range_pct = self.Param("MinBodyRangePct", 0.2).SetNotNegative().SetDisplay("Min Body/Range", "Minimum body size as a fraction of the candle range", "Pattern")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._stop_price = 0.0
self._take_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hammer_shooting_star_strategy, self).OnReseted()
self._stop_price = 0.0
self._take_price = 0.0
def OnStarted2(self, time):
super(hammer_shooting_star_strategy, self).OnStarted2(time)
self._stop_price = 0.0
self._take_price = 0.0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
open_price = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
# Exits of the open position against the signal candle levels; the stop is checked first.
if self.Position > 0:
if low <= self._stop_price or high >= self._take_price:
self.SellMarket(self.Position)
return
elif self.Position < 0:
if high >= self._stop_price or low <= self._take_price:
self.BuyMarket(-self.Position)
return
rng = high - low
if rng <= 0:
return
body = abs(close - open_price)
if body <= 0 or body < float(self._min_body_range_pct.Value) * rng:
return
upper_wick = high - max(open_price, close)
lower_wick = min(open_price, close) - low
wick_factor = float(self._wick_factor.Value)
opposite_factor = float(self._max_opposite_wick_factor.Value)
is_hammer = lower_wick >= wick_factor * body and upper_wick <= opposite_factor * body
is_shooting_star = upper_wick >= wick_factor * body and lower_wick <= opposite_factor * body
if is_hammer and not is_shooting_star and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = low
self._take_price = high
elif is_shooting_star and not is_hammer and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = high
self._take_price = low
def CreateClone(self):
return hammer_shooting_star_strategy()