Game Theory 策略
Game Theory Trading Strategy 结合了群体行为分析、流动性陷阱侦测、机构资金流和纳什均衡区间,用于反向与动量交易。
策略监控 RSI 极值和成交量激增以识别群体买入或卖出。近期高低点附近的流动性陷阱、AD 指标和「聪明资金」偏向共同优化进场。基于均线和标准差的价格带定义纳什均衡用于回归交易。当价格接近均衡或出现机构成交量时,仓位大小会自动调整。
细节
- 数据: 价格与成交量 K 线。
- 入场条件: 反向、动量或纳什回归信号。
- 离场条件: 止损/止盈或相反信号。
- 止损: 可选的止损和止盈。
- 默认参数:
RsiLength= 14VolumeMaLength= 20HerdThreshold= 2.0LiquidityLookback= 50InstVolumeMultiplier= 2.5InstMaLength= 21NashPeriod= 100NashDeviation= 0.02UseStopLoss= TrueStopLossPercent= 2UseTakeProfit= TrueTakeProfitPercent= 5
- 过滤器:
- 类型: 反向与动量混合
- 方向: 多空皆可
- 指标: RSI, SMA, Accumulation/Distribution, StandardDeviation, Highest/Lowest
- 复杂度: 高级
- 风险等级: 中等
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>
/// Game theory trading strategy.
/// Herd behaviour is an RSI extreme (above 70 or below 30) on volume above HerdThreshold times its VolumeMaLength average. A liquidity
/// trap is a sweep beyond the LiquidityLookback high or low that closes back inside. Institutional flow is volume above
/// InstVolumeMultiplier times its average, and the smart money bias is the accumulation/distribution line against its InstMaLength
/// average. The Nash equilibrium is the NashPeriod SMA with bands one standard deviation away.
/// Longs come from herd selling into a bear trap or accumulation (contrarian), institutional buying with accumulation above the
/// InstMaLength SMA (momentum), or a close back above the lower Nash band (reversion); shorts mirror them. The size is increased by
/// half on institutional volume and halved when price is within NashDeviation of the equilibrium. Opposite signals reverse, and
/// optional percent stop loss and take profit close the position.
/// </summary>
public class GameTheoryTradingStrategy : Strategy
{
private const decimal _rsiOverbought = 70m;
private const decimal _rsiOversold = 30m;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _volumeMaLength;
private readonly StrategyParam<decimal> _herdThreshold;
private readonly StrategyParam<int> _liquidityLookback;
private readonly StrategyParam<decimal> _instVolumeMultiplier;
private readonly StrategyParam<int> _instMaLength;
private readonly StrategyParam<int> _nashPeriod;
private readonly StrategyParam<decimal> _nashDeviation;
private readonly StrategyParam<bool> _useStopLoss;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<bool> _useTakeProfit;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _volumeMa;
private SimpleMovingAverage _adMa;
private decimal? _prevHighest;
private decimal? _prevLowest;
private decimal? _prevClose;
private decimal? _prevUpperBand;
private decimal? _prevLowerBand;
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Length of the volume average.
/// </summary>
public int VolumeMaLength
{
get => _volumeMaLength.Value;
set => _volumeMaLength.Value = value;
}
/// <summary>
/// Volume to average ratio that marks herd behaviour.
/// </summary>
public decimal HerdThreshold
{
get => _herdThreshold.Value;
set => _herdThreshold.Value = value;
}
/// <summary>
/// Candles whose high and low define liquidity pools.
/// </summary>
public int LiquidityLookback
{
get => _liquidityLookback.Value;
set => _liquidityLookback.Value = value;
}
/// <summary>
/// Volume to average ratio that marks institutional flow.
/// </summary>
public decimal InstVolumeMultiplier
{
get => _instVolumeMultiplier.Value;
set => _instVolumeMultiplier.Value = value;
}
/// <summary>
/// Length of the institutional trend and A/D averages.
/// </summary>
public int InstMaLength
{
get => _instMaLength.Value;
set => _instMaLength.Value = value;
}
/// <summary>
/// Period of the Nash equilibrium average and deviation.
/// </summary>
public int NashPeriod
{
get => _nashPeriod.Value;
set => _nashPeriod.Value = value;
}
/// <summary>
/// Relative distance from the equilibrium regarded as near it.
/// </summary>
public decimal NashDeviation
{
get => _nashDeviation.Value;
set => _nashDeviation.Value = value;
}
/// <summary>
/// Enable the stop loss.
/// </summary>
public bool UseStopLoss
{
get => _useStopLoss.Value;
set => _useStopLoss.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Enable the take profit.
/// </summary>
public bool UseTakeProfit
{
get => _useTakeProfit.Value;
set => _useTakeProfit.Value = value;
}
/// <summary>
/// Take profit percentage.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GameTheoryTradingStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "Herd");
_volumeMaLength = Param(nameof(VolumeMaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Volume MA Length", "Length of the volume average", "Herd");
_herdThreshold = Param(nameof(HerdThreshold), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Herd Threshold", "Volume to average ratio that marks herd behaviour", "Herd");
_liquidityLookback = Param(nameof(LiquidityLookback), 50)
.SetGreaterThanZero()
.SetDisplay("Liquidity Lookback", "Candles whose high and low define liquidity pools", "Liquidity");
_instVolumeMultiplier = Param(nameof(InstVolumeMultiplier), 2.5m)
.SetGreaterThanZero()
.SetDisplay("Inst Volume Multiplier", "Volume to average ratio that marks institutional flow", "Institutional");
_instMaLength = Param(nameof(InstMaLength), 21)
.SetGreaterThanZero()
.SetDisplay("Inst MA Length", "Length of the institutional trend and A/D averages", "Institutional");
_nashPeriod = Param(nameof(NashPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Nash Period", "Period of the Nash equilibrium average and deviation", "Nash");
_nashDeviation = Param(nameof(NashDeviation), 0.02m)
.SetNotNegative()
.SetDisplay("Nash Deviation", "Relative distance from the equilibrium regarded as near it", "Nash");
_useStopLoss = Param(nameof(UseStopLoss), true)
.SetDisplay("Use Stop Loss", "Enable the stop loss", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_useTakeProfit = Param(nameof(UseTakeProfit), true)
.SetDisplay("Use Take Profit", "Enable the take profit", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 5m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_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()
{
_prevHighest = null;
_prevLowest = null;
_prevClose = null;
_prevUpperBand = null;
_prevLowerBand = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ad = new AccumulationDistributionLine();
var nashMa = new SimpleMovingAverage { Length = NashPeriod };
var nashStd = new StandardDeviation { Length = NashPeriod };
var instMa = new SimpleMovingAverage { Length = InstMaLength };
var highest = new Highest { Length = LiquidityLookback };
var lowest = new Lowest { Length = LiquidityLookback };
_volumeMa = new SimpleMovingAverage { Length = VolumeMaLength };
_adMa = new SimpleMovingAverage { Length = InstMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ad, nashMa, nashStd, instMa, highest, lowest, ProcessCandle)
.Start();
StartProtection(
UseTakeProfit && TakeProfitPercent > 0 ? new Unit(TakeProfitPercent, UnitTypes.Percent) : new Unit(),
UseStopLoss && StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : new Unit(),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, nashMa);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ad, decimal nashMa, decimal nashStd, decimal instMa, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
var volumeValue = _volumeMa.Process(new DecimalIndicatorValue(_volumeMa, candle.TotalVolume, candle.OpenTime) { IsFinal = true });
var adValue = _adMa.Process(new DecimalIndicatorValue(_adMa, ad, candle.OpenTime) { IsFinal = true });
var close = candle.ClosePrice;
var upperBand = nashMa + nashStd;
var lowerBand = nashMa - nashStd;
// Liquidity pools are the extremes of the candles before this one.
var poolHigh = _prevHighest;
var poolLow = _prevLowest;
var prevClose = _prevClose;
var prevUpper = _prevUpperBand;
var prevLower = _prevLowerBand;
_prevHighest = highest;
_prevLowest = lowest;
_prevClose = close;
_prevUpperBand = upperBand;
_prevLowerBand = lowerBand;
if (!_volumeMa.IsFormed || !_adMa.IsFormed || poolHigh is not decimal high || poolLow is not decimal low || prevClose is not decimal pc)
return;
if (prevUpper is not decimal pUpper || prevLower is not decimal pLower)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var volumeAvg = volumeValue.GetValue<decimal>();
var adAvg = adValue.GetValue<decimal>();
var volume = candle.TotalVolume;
var volumeSpike = volumeAvg > 0 && volume > volumeAvg * HerdThreshold;
var herdBuying = rsi > _rsiOverbought && volumeSpike;
var herdSelling = rsi < _rsiOversold && volumeSpike;
var bullTrap = candle.HighPrice > high && close < high;
var bearTrap = candle.LowPrice < low && close > low;
var institutional = volumeAvg > 0 && volume > volumeAvg * InstVolumeMultiplier;
var accumulation = ad > adAvg;
var distribution = ad < adAvg;
var contrarianLong = herdSelling && (bearTrap || accumulation);
var contrarianShort = herdBuying && (bullTrap || distribution);
var momentumLong = institutional && accumulation && close > instMa && close > candle.OpenPrice;
var momentumShort = institutional && distribution && close < instMa && close < candle.OpenPrice;
var nashLong = pc < pLower && close > lowerBand;
var nashShort = pc > pUpper && close < upperBand;
var size = Volume;
if (institutional)
size *= 1.5m;
if (nashMa > 0 && Math.Abs(close - nashMa) / nashMa <= NashDeviation)
size /= 2m;
if ((contrarianLong || momentumLong || nashLong) && Position <= 0)
BuyMarket(size + Math.Abs(Position));
else if ((contrarianShort || momentumShort || nashShort) && Position >= 0)
SellMarket(size + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex, AccumulationDistributionLine, SimpleMovingAverage, StandardDeviation, Highest, Lowest, DecimalIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class game_theory_trading_strategy(Strategy):
"""
Game theory trading strategy.
Herd behaviour is an RSI extreme (above 70 or below 30) on volume above HerdThreshold times its VolumeMaLength average. A liquidity
trap is a sweep beyond the LiquidityLookback high or low that closes back inside. Institutional flow is volume above
InstVolumeMultiplier times its average, and the smart money bias is the accumulation/distribution line against its InstMaLength
average. The Nash equilibrium is the NashPeriod SMA with bands one standard deviation away.
Longs come from herd selling into a bear trap or accumulation (contrarian), institutional buying with accumulation above the
InstMaLength SMA (momentum), or a close back above the lower Nash band (reversion); shorts mirror them. The size is increased by
half on institutional volume and halved when price is within NashDeviation of the equilibrium. Opposite signals reverse, and
optional percent stop loss and take profit close the position.
"""
RSI_OVERBOUGHT = 70
RSI_OVERSOLD = 30
def __init__(self):
super(game_theory_trading_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "Herd")
self._volume_ma_length = self.Param("VolumeMaLength", 20).SetGreaterThanZero().SetDisplay("Volume MA Length", "Length of the volume average", "Herd")
self._herd_threshold = self.Param("HerdThreshold", 2.0).SetGreaterThanZero().SetDisplay("Herd Threshold", "Volume to average ratio that marks herd behaviour", "Herd")
self._liquidity_lookback = self.Param("LiquidityLookback", 50).SetGreaterThanZero().SetDisplay("Liquidity Lookback", "Candles whose high and low define liquidity pools", "Liquidity")
self._inst_volume_multiplier = self.Param("InstVolumeMultiplier", 2.5).SetGreaterThanZero().SetDisplay("Inst Volume Multiplier", "Volume to average ratio that marks institutional flow", "Institutional")
self._inst_ma_length = self.Param("InstMaLength", 21).SetGreaterThanZero().SetDisplay("Inst MA Length", "Length of the institutional trend and A/D averages", "Institutional")
self._nash_period = self.Param("NashPeriod", 100).SetGreaterThanZero().SetDisplay("Nash Period", "Period of the Nash equilibrium average and deviation", "Nash")
self._nash_deviation = self.Param("NashDeviation", 0.02).SetNotNegative().SetDisplay("Nash Deviation", "Relative distance from the equilibrium regarded as near it", "Nash")
self._use_stop_loss = self.Param("UseStopLoss", True).SetDisplay("Use Stop Loss", "Enable the stop loss", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._use_take_profit = self.Param("UseTakeProfit", True).SetDisplay("Use Take Profit", "Enable the take profit", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 5.0).SetNotNegative().SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._volume_ma = None
self._ad_ma = None
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_highest = None
self._prev_lowest = None
self._prev_close = None
self._prev_upper_band = None
self._prev_lower_band = None
def OnReseted(self):
super(game_theory_trading_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(game_theory_trading_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
ad = AccumulationDistributionLine()
nash_ma = SimpleMovingAverage()
nash_ma.Length = self._nash_period.Value
nash_std = StandardDeviation()
nash_std.Length = self._nash_period.Value
inst_ma = SimpleMovingAverage()
inst_ma.Length = self._inst_ma_length.Value
highest = Highest()
highest.Length = self._liquidity_lookback.Value
lowest = Lowest()
lowest.Length = self._liquidity_lookback.Value
self._volume_ma = SimpleMovingAverage()
self._volume_ma.Length = self._volume_ma_length.Value
self._ad_ma = SimpleMovingAverage()
self._ad_ma.Length = self._inst_ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, ad, nash_ma, nash_std, inst_ma, highest, lowest, self._process_candle).Start()
take = Unit(Decimal(self._take_profit_percent.Value), UnitTypes.Percent) if self._use_take_profit.Value and self._take_profit_percent.Value > 0 else Unit()
stop = Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent) if self._use_stop_loss.Value and self._stop_loss_percent.Value > 0 else Unit()
self.StartProtection(take, stop, useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, nash_ma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, rsi, ad, nash_ma, nash_std, inst_ma, highest, lowest):
if candle.State != CandleStates.Finished:
return
volume_input = DecimalIndicatorValue(self._volume_ma, candle.TotalVolume, candle.OpenTime)
volume_input.IsFinal = True
volume_value = self._volume_ma.Process(volume_input)
ad_input = DecimalIndicatorValue(self._ad_ma, ad, candle.OpenTime)
ad_input.IsFinal = True
ad_value = self._ad_ma.Process(ad_input)
close = candle.ClosePrice
upper_band = nash_ma + nash_std
lower_band = nash_ma - nash_std
# Liquidity pools are the extremes of the candles before this one.
pool_high = self._prev_highest
pool_low = self._prev_lowest
prev_close = self._prev_close
prev_upper = self._prev_upper_band
prev_lower = self._prev_lower_band
self._prev_highest = highest
self._prev_lowest = lowest
self._prev_close = close
self._prev_upper_band = upper_band
self._prev_lower_band = lower_band
if not self._volume_ma.IsFormed or not self._ad_ma.IsFormed or pool_high is None or pool_low is None or prev_close is None:
return
if prev_upper is None or prev_lower is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
volume_avg = volume_value.GetValue[Decimal](None)
ad_avg = ad_value.GetValue[Decimal](None)
volume = candle.TotalVolume
volume_spike = volume_avg > 0 and volume > volume_avg * Decimal(self._herd_threshold.Value)
herd_buying = rsi > self.RSI_OVERBOUGHT and volume_spike
herd_selling = rsi < self.RSI_OVERSOLD and volume_spike
bull_trap = candle.HighPrice > pool_high and close < pool_high
bear_trap = candle.LowPrice < pool_low and close > pool_low
institutional = volume_avg > 0 and volume > volume_avg * Decimal(self._inst_volume_multiplier.Value)
accumulation = ad > ad_avg
distribution = ad < ad_avg
contrarian_long = herd_selling and (bear_trap or accumulation)
contrarian_short = herd_buying and (bull_trap or distribution)
momentum_long = institutional and accumulation and close > inst_ma and close > candle.OpenPrice
momentum_short = institutional and distribution and close < inst_ma and close < candle.OpenPrice
nash_long = prev_close < prev_lower and close > lower_band
nash_short = prev_close > prev_upper and close < upper_band
size = self.Volume
if institutional:
size = size * Decimal(1.5)
if nash_ma > 0 and abs(close - nash_ma) / nash_ma <= Decimal(self._nash_deviation.Value):
size = size / Decimal(2)
if (contrarian_long or momentum_long or nash_long) and self.Position <= 0:
self.BuyMarket(size + abs(self.Position))
elif (contrarian_short or momentum_short or nash_short) and self.Position >= 0:
self.SellMarket(size + abs(self.Position))
def CreateClone(self):
return game_theory_trading_strategy()