Very Blonde System Strategie
Gitterbasierte Gegentrend-Strategie, inspiriert vom originalen "Very Blonde System" für MetaTrader. Die Strategie sucht nach einem großen Abstand zwischen dem aktuellen Preis und jüngsten Extremwerten und handelt in die entgegengesetzte Richtung.
Strategielogik
- Den höchsten Hochpunkt und niedrigsten Tiefpunkt über die letzten Count Bars Kerzen berechnen.
- Wenn keine offenen Positionen vorhanden sind:
- Wenn der Abstand vom jüngsten Hoch zum aktuellen Preis Limit Ticks übersteigt, zum Marktpreis kaufen.
- Wenn der Abstand vom aktuellen Preis zum jüngsten Tief Limit Ticks übersteigt, zum Marktpreis verkaufen.
- Nach dem Einstieg vier zusätzliche Limitorders alle Grid Ticks platzieren, dabei das Volumen auf jeder Stufe verdoppeln.
- Wenn eine Position vorhanden ist:
- Wenn der Gesamtgewinn Amount Währungseinheiten übersteigt, die Position schließen und alle ausstehenden Orders stornieren.
- Wenn Lock Down größer als null ist, aktiviert die Strategie nach einer Preisbewegung um diese Anzahl Ticks in günstiger Richtung einen Gewinnsicherungsschutz. Kehrt der Preis zum Einstandsniveau zurück, werden alle Positionen geschlossen.
Parameter
| Name | Beschreibung |
|---|---|
CountBars |
Anzahl der Kerzen für die Suche nach Hochs und Tiefs. |
Limit |
Mindestabstand vom Extremwert in Ticks zum Öffnen eines Trades. |
Grid |
Abstand in Ticks zwischen zusätzlichen Gitterorders. |
Amount |
Gewinnziel in Währung zum Schließen aller Positionen. |
LockDown |
Abstand in Ticks zum Aktivieren des Gewinnsicherungsschutzes. |
CandleType |
Kerzentyp für Berechnungen. |
Die Strategie verwendet Marktorders für initiale Einstiege und Limitorders für Gitterstufen. Alle Kommentare im Code sind auf Englisch verfasst.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Grid strategy based on distance from recent extremes.
/// Buys when price drops far below recent high and sells when price rises far above recent low.
/// Places additional limit orders forming a martingale grid and exits on total profit.
/// </summary>
public class VeryBlondeSystemStrategy : Strategy
{
private readonly StrategyParam<int> _countBars;
private readonly StrategyParam<decimal> _limit;
private readonly StrategyParam<decimal> _grid;
private readonly StrategyParam<decimal> _amount;
private readonly StrategyParam<decimal> _lockDown;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private bool _isLong;
private bool _lockActivated;
private decimal _lockPrice;
/// <summary>
/// Number of candles to search extremes.
/// </summary>
public int CountBars
{
get => _countBars.Value;
set => _countBars.Value = value;
}
/// <summary>
/// Minimum distance from recent extreme in ticks to trigger entry.
/// </summary>
public decimal Limit
{
get => _limit.Value;
set => _limit.Value = value;
}
/// <summary>
/// Grid distance in ticks between additional orders.
/// </summary>
public decimal Grid
{
get => _grid.Value;
set => _grid.Value = value;
}
/// <summary>
/// Target profit to close all positions.
/// </summary>
public decimal Amount
{
get => _amount.Value;
set => _amount.Value = value;
}
/// <summary>
/// Breakeven activation distance in ticks.
/// </summary>
public decimal LockDown
{
get => _lockDown.Value;
set => _lockDown.Value = value;
}
/// <summary>
/// Type of candles for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public VeryBlondeSystemStrategy()
{
_countBars = Param(nameof(CountBars), 10)
.SetDisplay("Count Bars", "Number of candles to search extremes", "General")
.SetGreaterThanZero()
.SetOptimize(5, 30, 5);
_limit = Param(nameof(Limit), 500m)
.SetDisplay("Limit", "Minimum distance from extreme in ticks", "Trading")
.SetGreaterThanZero();
_grid = Param(nameof(Grid), 35m)
.SetDisplay("Grid", "Grid distance in ticks", "Trading")
.SetGreaterThanZero();
_amount = Param(nameof(Amount), 40m)
.SetDisplay("Amount", "Target profit to close all positions", "Risk")
.SetGreaterThanZero();
_lockDown = Param(nameof(LockDown), 0m)
.SetDisplay("Lock Down", "Breakeven activation distance in ticks", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_isLong = false;
_lockActivated = false;
_lockPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highest = new Highest { Length = CountBars };
var lowest = new Lowest { Length = CountBars };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var step = Security.PriceStep ?? 1m;
if (Position == 0)
{
CheckForOpen(candle, high, low, step);
}
else
{
CheckForClose(candle, step);
}
}
private void CheckForOpen(ICandleMessage candle, decimal high, decimal low, decimal step)
{
var close = candle.ClosePrice;
if (high - close > Limit * step)
{
OpenPosition(true, close, step);
}
else if (close - low > Limit * step)
{
OpenPosition(false, close, step);
}
}
private void OpenPosition(bool isBuy, decimal price, decimal step)
{
var volume = Volume;
if (isBuy)
{
BuyMarket(volume);
}
else
{
SellMarket(volume);
}
_entryPrice = price;
_isLong = isBuy;
_lockActivated = false;
_lockPrice = 0m;
}
private void CheckForClose(ICandleMessage candle, decimal step)
{
var currentProfit = Position * (candle.ClosePrice - _entryPrice);
if (currentProfit >= Amount)
{
CloseAll();
return;
}
if (LockDown <= 0m)
return;
if (_isLong)
{
if (!_lockActivated && candle.ClosePrice - _entryPrice > LockDown * step)
{
_lockActivated = true;
_lockPrice = _entryPrice;
}
else if (_lockActivated && candle.ClosePrice <= _lockPrice)
{
CloseAll();
}
}
else
{
if (!_lockActivated && _entryPrice - candle.ClosePrice > LockDown * step)
{
_lockActivated = true;
_lockPrice = _entryPrice;
}
else if (_lockActivated && candle.ClosePrice >= _lockPrice)
{
CloseAll();
}
}
}
private void CloseAll()
{
if (Position > 0)
SellMarket(Math.Abs(Position));
else if (Position < 0)
BuyMarket(Math.Abs(Position));
_entryPrice = 0m;
_lockActivated = false;
_lockPrice = 0m;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class very_blonde_system_strategy(Strategy):
def __init__(self):
super(very_blonde_system_strategy, self).__init__()
self._count_bars = self.Param("CountBars", 10)
self._limit = self.Param("Limit", 500.0)
self._grid = self.Param("Grid", 35.0)
self._amount = self.Param("Amount", 40.0)
self._lock_down = self.Param("LockDown", 0.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._entry_price = 0.0
self._is_long = False
self._lock_activated = False
self._lock_price = 0.0
@property
def CountBars(self):
return self._count_bars.Value
@CountBars.setter
def CountBars(self, value):
self._count_bars.Value = value
@property
def Limit(self):
return self._limit.Value
@Limit.setter
def Limit(self, value):
self._limit.Value = value
@property
def Grid(self):
return self._grid.Value
@Grid.setter
def Grid(self, value):
self._grid.Value = value
@property
def Amount(self):
return self._amount.Value
@Amount.setter
def Amount(self, value):
self._amount.Value = value
@property
def LockDown(self):
return self._lock_down.Value
@LockDown.setter
def LockDown(self, value):
self._lock_down.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(very_blonde_system_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._is_long = False
self._lock_activated = False
self._lock_price = 0.0
highest = Highest()
highest.Length = self.CountBars
lowest = Lowest()
lowest.Length = self.CountBars
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self.ProcessCandle).Start()
def ProcessCandle(self, candle, high_val, low_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
high = float(high_val)
low = float(low_val)
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position == 0:
self._check_open(close, high, low, step)
else:
self._check_close(candle, step)
def _check_open(self, close, high, low, step):
limit_dist = float(self.Limit) * step
if high - close > limit_dist:
self.BuyMarket()
self._entry_price = close
self._is_long = True
self._lock_activated = False
self._lock_price = 0.0
elif close - low > limit_dist:
self.SellMarket()
self._entry_price = close
self._is_long = False
self._lock_activated = False
self._lock_price = 0.0
def _check_close(self, candle, step):
close = float(candle.ClosePrice)
current_profit = self.Position * (close - self._entry_price)
if current_profit >= float(self.Amount):
self._close_all()
return
lock_down = float(self.LockDown)
if lock_down <= 0.0:
return
if self._is_long:
if not self._lock_activated and close - self._entry_price > lock_down * step:
self._lock_activated = True
self._lock_price = self._entry_price
elif self._lock_activated and close <= self._lock_price:
self._close_all()
else:
if not self._lock_activated and self._entry_price - close > lock_down * step:
self._lock_activated = True
self._lock_price = self._entry_price
elif self._lock_activated and close >= self._lock_price:
self._close_all()
def _close_all(self):
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._entry_price = 0.0
self._lock_activated = False
self._lock_price = 0.0
def OnReseted(self):
super(very_blonde_system_strategy, self).OnReseted()
self._entry_price = 0.0
self._is_long = False
self._lock_activated = False
self._lock_price = 0.0
def CreateClone(self):
return very_blonde_system_strategy()