Dkoderweb Repainting Issue Fix 戦略
この戦略は、シンプルなジグザグアプローチを使ってハーモニックパターンを検出し、価格がフィボナッチリトレースメントレベルに戻ったときにトレードを行います。強気パターンが形成され、価格がエントリー窓まで引き戻されると、戦略は事前に定義されたテイクプロフィットとストップロスのレベルでロングポジションを開きます。弱気パターンは逆方向に同じロジックを発動させます。
詳細
- エントリー条件:
- ロング: ABCDハーモニックパターンかつ終値がエントリーFibonacciレベル以下。
- ショート: ABCDハーモニックパターンかつ終値がエントリーFibonacciレベル以上。
- ロング/ショート: 両方。
- エグジット条件:
- 価格がテイクプロフィットまたはストップロスのFibonacciレベルに到達。
- ストップ: はい。
- デフォルト値:
TradeSize= 1EntryRate= 0.382TakeProfitRate= 0.618StopLossRate= -0.618
- フィルター:
- カテゴリ: パターン認識
- 方向: 両方
- インジケーター: ZigZag
- ストップ: はい
- 複雑さ: 中程度
- 時間軸: 短期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: はい
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Harmonic ABCD pattern strategy with Fibonacci entry, target and stop.
/// A zigzag point is set when the candle colour flips: the higher high of the two candles after an up-to-down flip,
/// the lower low after a down-to-up flip. The last four points A, B, C, D form an ABCD pattern when BC retraces 0.382-0.886 of AB
/// and CD extends 1.13-2.618 of BC; D below C is bullish and D above C bearish.
/// Fibonacci levels are measured from D back over the C-D range. A bullish pattern buys when the close is at or below the EntryRate level,
/// a bearish one sells when the close is at or above it. The TakeProfitRate and StopLossRate levels at entry close the position.
/// </summary>
public class DkoderwebRepaintingIssueFixStrategy : Strategy
{
private readonly StrategyParam<decimal> _tradeSize;
private readonly StrategyParam<decimal> _entryRate;
private readonly StrategyParam<decimal> _takeProfitRate;
private readonly StrategyParam<decimal> _stopLossRate;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _points = [];
private ICandleMessage _prevCandle;
private int _direction;
private decimal? _takePrice;
private decimal? _stopPrice;
/// <summary>
/// Order volume.
/// </summary>
public decimal TradeSize { get => _tradeSize.Value; set => _tradeSize.Value = value; }
/// <summary>
/// Fibonacci rate of the entry level.
/// </summary>
public decimal EntryRate { get => _entryRate.Value; set => _entryRate.Value = value; }
/// <summary>
/// Fibonacci rate of the take profit level.
/// </summary>
public decimal TakeProfitRate { get => _takeProfitRate.Value; set => _takeProfitRate.Value = value; }
/// <summary>
/// Fibonacci rate of the stop loss level.
/// </summary>
public decimal StopLossRate { get => _stopLossRate.Value; set => _stopLossRate.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public DkoderwebRepaintingIssueFixStrategy()
{
_tradeSize = Param(nameof(TradeSize), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade Size", "Order volume", "Trading");
_entryRate = Param(nameof(EntryRate), 0.382m)
.SetDisplay("Entry Rate", "Fibonacci rate of the entry level", "Fibonacci");
_takeProfitRate = Param(nameof(TakeProfitRate), 0.618m)
.SetDisplay("Take Profit Rate", "Fibonacci rate of the take profit level", "Fibonacci");
_stopLossRate = Param(nameof(StopLossRate), -0.618m)
.SetDisplay("Stop Loss Rate", "Fibonacci rate of the stop loss level", "Fibonacci");
_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()
{
_points.Clear();
_prevCandle = null;
_direction = 0;
_takePrice = null;
_stopPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
Volume = TradeSize;
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;
UpdateZigZag(candle);
_prevCandle = candle;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if ((_takePrice is decimal take && candle.HighPrice >= take) || (_stopPrice is decimal stop && candle.LowPrice <= stop))
{
SellMarket(Position);
_takePrice = null;
_stopPrice = null;
return;
}
}
else if (Position < 0)
{
if ((_takePrice is decimal take && candle.LowPrice <= take) || (_stopPrice is decimal stop && candle.HighPrice >= stop))
{
BuyMarket(-Position);
_takePrice = null;
_stopPrice = null;
return;
}
}
if (_points.Count < 4)
return;
var a = _points[^4];
var b = _points[^3];
var c = _points[^2];
var d = _points[^1];
var ab = Math.Abs(a - b);
var bc = Math.Abs(b - c);
var cd = Math.Abs(c - d);
if (ab == 0m || bc == 0m)
return;
var abc = bc / ab;
var bcd = cd / bc;
if (abc < 0.382m || abc > 0.886m || bcd < 1.13m || bcd > 2.618m)
return;
decimal Fib(decimal rate) => d > c ? d - cd * rate : d + cd * rate;
var close = candle.ClosePrice;
if (d < c && close <= Fib(EntryRate) && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_takePrice = Fib(TakeProfitRate);
_stopPrice = Fib(StopLossRate);
}
else if (d > c && close >= Fib(EntryRate) && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_takePrice = Fib(TakeProfitRate);
_stopPrice = Fib(StopLossRate);
}
}
private void UpdateZigZag(ICandleMessage candle)
{
if (_prevCandle is not { } prev)
return;
var isUp = candle.ClosePrice >= candle.OpenPrice;
var isDown = candle.ClosePrice <= candle.OpenPrice;
var prevIsUp = prev.ClosePrice >= prev.OpenPrice;
var prevIsDown = prev.ClosePrice <= prev.OpenPrice;
var prevDirection = _direction;
_direction = prevIsUp && isDown ? -1 : prevIsDown && isUp ? 1 : prevDirection;
decimal? point = null;
if (prevIsUp && isDown && prevDirection != -1)
point = Math.Max(candle.HighPrice, prev.HighPrice);
else if (prevIsDown && isUp && prevDirection != 1)
point = Math.Min(candle.LowPrice, prev.LowPrice);
if (point is decimal p)
{
_points.Add(p);
if (_points.Count > 5)
_points.RemoveAt(0);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class dkoderweb_repainting_issue_fix_strategy(Strategy):
"""
Harmonic ABCD pattern strategy with Fibonacci entry, target and stop.
A zigzag point is set when the candle colour flips: the higher high of the two candles after an up-to-down flip,
the lower low after a down-to-up flip. The last four points A, B, C, D form an ABCD pattern when BC retraces 0.382-0.886 of AB
and CD extends 1.13-2.618 of BC; D below C is bullish and D above C bearish.
Fibonacci levels are measured from D back over the C-D range. A bullish pattern buys when the close is at or below the EntryRate level,
a bearish one sells when the close is at or above it. The TakeProfitRate and StopLossRate levels at entry close the position.
"""
def __init__(self):
super(dkoderweb_repainting_issue_fix_strategy, self).__init__()
self._trade_size = self.Param("TradeSize", 1.0).SetGreaterThanZero().SetDisplay("Trade Size", "Order volume", "Trading")
self._entry_rate = self.Param("EntryRate", 0.382).SetDisplay("Entry Rate", "Fibonacci rate of the entry level", "Fibonacci")
self._take_profit_rate = self.Param("TakeProfitRate", 0.618).SetDisplay("Take Profit Rate", "Fibonacci rate of the take profit level", "Fibonacci")
self._stop_loss_rate = self.Param("StopLossRate", -0.618).SetDisplay("Stop Loss Rate", "Fibonacci rate of the stop loss level", "Fibonacci")
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 GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def _reset_state(self):
self._points = []
self._prev_candle = None
self._direction = 0
self._take_price = None
self._stop_price = None
def OnReseted(self):
super(dkoderweb_repainting_issue_fix_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(dkoderweb_repainting_issue_fix_strategy, self).OnStarted2(time)
self._reset_state()
self.Volume = Decimal(self._trade_size.Value)
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
current = (float(candle.OpenPrice), float(candle.HighPrice), float(candle.LowPrice), float(candle.ClosePrice))
self._update_zigzag(current)
self._prev_candle = current
if not self.IsFormedAndOnlineAndAllowTrading():
return
_, high, low, close = current
if self.Position > 0:
if (self._take_price is not None and high >= self._take_price) or (self._stop_price is not None and low <= self._stop_price):
self.SellMarket(self.Position)
self._take_price = None
self._stop_price = None
return
elif self.Position < 0:
if (self._take_price is not None and low <= self._take_price) or (self._stop_price is not None and high >= self._stop_price):
self.BuyMarket(-self.Position)
self._take_price = None
self._stop_price = None
return
if len(self._points) < 4:
return
a, b, c, d = self._points[-4:]
ab = abs(a - b)
bc = abs(b - c)
cd = abs(c - d)
if ab == 0 or bc == 0:
return
abc = bc / ab
bcd = cd / bc
if abc < 0.382 or abc > 0.886 or bcd < 1.13 or bcd > 2.618:
return
def fib(rate):
return d - cd * rate if d > c else d + cd * rate
entry_rate = float(self._entry_rate.Value)
tp_rate = float(self._take_profit_rate.Value)
sl_rate = float(self._stop_loss_rate.Value)
if d < c and close <= fib(entry_rate) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._take_price = fib(tp_rate)
self._stop_price = fib(sl_rate)
elif d > c and close >= fib(entry_rate) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._take_price = fib(tp_rate)
self._stop_price = fib(sl_rate)
def _update_zigzag(self, current):
prev = self._prev_candle
if prev is None:
return
open_price, high, low, close = current
prev_open, prev_high, prev_low, prev_close = prev
is_up = close >= open_price
is_down = close <= open_price
prev_is_up = prev_close >= prev_open
prev_is_down = prev_close <= prev_open
prev_direction = self._direction
if prev_is_up and is_down:
self._direction = -1
elif prev_is_down and is_up:
self._direction = 1
point = None
if prev_is_up and is_down and prev_direction != -1:
point = max(high, prev_high)
elif prev_is_down and is_up and prev_direction != 1:
point = min(low, prev_low)
if point is not None:
self._points.append(point)
if len(self._points) > 5:
self._points.pop(0)
def CreateClone(self):
return dkoderweb_repainting_issue_fix_strategy()