InwCoin Martingale-Strategie
Diese Strategie implementiert einen einfachen Martingale-Ansatz für Long-Positionen auf Bitcoin. Sie unterstützt drei optionale Einstiegssignale: MACD-Histogramm kreuzt über null, Stochastic RSI %D kreuzt über das Niveau 20, oder der Kurs bricht einen ATR-basierten Kanal. Nach jedem Kauf kann die Positionsgröße verdoppelt werden, wenn der Kurs um einen konfigurierten Prozentsatz fällt. Die gesamte Position wird geschlossen, wenn der Gewinn einen festgelegten Prozentsatz über dem durchschnittlichen Einstiegspreis erreicht.
Details
- Einstiegssignale
- MACD Line > 0: Histogramm kreuzt über null.
- STO RSI cross up: %D-Linie kreuzt über 20, während %K in der überverkauften Zone liegt.
- ATR Channel: Schlusskurs kreuzt über EMA plus ATR-Multiplikator.
- Take profit: Position wird geschlossen, wenn der Kurs den Durchschnittspreis um den konfigurierten Prozentsatz übersteigt.
- Martingale: Zusätzliche Käufe erfolgen, wenn der Kurs um den konfigurierten Prozentsatz vom Durchschnittspreis fällt.
- Richtung: Nur Long.
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>
/// Martingale strategy for Bitcoin with MACD entry signal.
/// Enters long on MACD histogram crossover, averages down on price drops,
/// and exits on take profit.
/// </summary>
public class InwCoinMartingaleStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _martingalePercent;
private readonly StrategyParam<decimal> _martingaleMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal _avgPrice;
private int _martingaleCount;
private decimal _prevHistogram;
private bool _isFirst = true;
/// <summary>
/// Take profit percent.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Percent drop to trigger martingale.
/// </summary>
public decimal MartingalePercent
{
get => _martingalePercent.Value;
set => _martingalePercent.Value = value;
}
/// <summary>
/// Multiplier for each martingale step.
/// </summary>
public decimal MartingaleMultiplier
{
get => _martingaleMultiplier.Value;
set => _martingaleMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="InwCoinMartingaleStrategy"/>.
/// </summary>
public InwCoinMartingaleStrategy()
{
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
.SetDisplay("Take Profit %", "Profit percent to exit.", "Parameters");
_martingalePercent = Param(nameof(MartingalePercent), 5m)
.SetDisplay("Martingale %", "Price drop percent for averaging.", "Parameters");
_martingaleMultiplier = Param(nameof(MartingaleMultiplier), 2m)
.SetDisplay("Multiplier", "Volume multiplier for martingale.", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles.", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_avgPrice = 0m;
_martingaleCount = 0;
_prevHistogram = 0m;
_isFirst = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_avgPrice = 0m;
_martingaleCount = 0;
_prevHistogram = 0m;
_isFirst = true;
var emaShort = new ExponentialMovingAverage { Length = 12 };
var emaLong = new ExponentialMovingAverage { Length = 26 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(emaShort, emaLong, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaShort);
DrawIndicator(area, emaLong);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaShortVal, decimal emaLongVal)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
var histogram = emaShortVal - emaLongVal;
if (_isFirst)
{
_prevHistogram = histogram;
_isFirst = false;
return;
}
// MACD histogram crossover as entry signal
var buySignal = _prevHistogram <= 0 && histogram > 0;
if (buySignal && Position <= 0 && _martingaleCount == 0)
{
BuyMarket();
_avgPrice = price;
_martingaleCount = 1;
}
else if (Position > 0 && _avgPrice > 0)
{
// Check for martingale averaging down
var drop = (price - _avgPrice) / _avgPrice * 100m;
if (drop <= -MartingalePercent && _martingaleCount < 5)
{
BuyMarket();
_avgPrice = ((_avgPrice * (Position)) + price) / (Position + 1);
_martingaleCount++;
}
// Check take profit
var profit = (price - _avgPrice) / _avgPrice * 100m;
if (profit >= TakeProfitPercent)
{
SellMarket();
_martingaleCount = 0;
_avgPrice = 0m;
}
}
_prevHistogram = histogram;
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class inw_coin_martingale_strategy(Strategy):
def __init__(self):
super(inw_coin_martingale_strategy, self).__init__()
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0) \
.SetDisplay("Take Profit %", "Profit percent to exit", "Parameters")
self._martingale_percent = self.Param("MartingalePercent", 5.0) \
.SetDisplay("Martingale %", "Price drop percent for averaging", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._avg_price = 0.0
self._martingale_count = 0
self._prev_histogram = 0.0
self._is_first = True
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(inw_coin_martingale_strategy, self).OnReseted()
self._avg_price = 0.0
self._martingale_count = 0
self._prev_histogram = 0.0
self._is_first = True
def OnStarted2(self, time):
super(inw_coin_martingale_strategy, self).OnStarted2(time)
ema_short = ExponentialMovingAverage()
ema_short.Length = 12
ema_long = ExponentialMovingAverage()
ema_long.Length = 26
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema_short, ema_long, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_short)
self.DrawIndicator(area, ema_long)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_short_val, ema_long_val):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
histogram = float(ema_short_val) - float(ema_long_val)
if self._is_first:
self._prev_histogram = histogram
self._is_first = False
return
buy_signal = self._prev_histogram <= 0 and histogram > 0
if buy_signal and self.Position <= 0 and self._martingale_count == 0:
self.BuyMarket()
self._avg_price = price
self._martingale_count = 1
elif self.Position > 0 and self._avg_price > 0:
drop = (price - self._avg_price) / self._avg_price * 100.0
mart_pct = float(self._martingale_percent.Value)
if drop <= -mart_pct and self._martingale_count < 5:
self.BuyMarket()
self._avg_price = (self._avg_price * self.Position + price) / (self.Position + 1)
self._martingale_count += 1
profit = (price - self._avg_price) / self._avg_price * 100.0
tp = float(self._take_profit_percent.Value)
if profit >= tp:
self.SellMarket()
self._martingale_count = 0
self._avg_price = 0.0
self._prev_histogram = histogram
def CreateClone(self):
return inw_coin_martingale_strategy()