inwCoin 马丁格尔策略
该策略在比特币上实现简单的马丁格尔加仓方案。 可选三种入场信号:MACD 柱状图上穿零轴、Stochastic RSI 的 %D 线上穿 20、 或者价格突破基于 ATR 的通道。 每次买入后,当价格回撤达到设定百分比时仓位可按倍数增加。 当价格高于平均持仓价达到设定盈利百分比时全部平仓。
细节
- 入场信号
- MACD Line > 0:柱状图上穿零轴。
- STO RSI cross up:%D 线在超卖区上穿 20。
- ATR Channel:收盘价突破 EMA 加 ATR 乘数。
- 止盈:价格超出平均价达到设定百分比时平仓。
- 马丁格尔:价格下跌达到设定百分比时加仓。
- 方向:仅做多。
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()