在 GitHub 上查看
随机方向策略
概述
该策略是 MetaTrader 5 专家顾问“At random”(MQL ID 39835)的 StockSharp 移植版本。原始机器人展示了在必须持续持仓的情况下,完全随机的决策流程会如何表现。每根完成的 K 线都会触发一次“抛硬币”,决定下一步是买入还是卖出。StockSharp 版本保留了这一思想,并使用高级 API 原语(SubscribeCandles、BuyMarket、SellMarket)实现,从而可以方便地在 Designer 或 Runner 中运行。
移植实现刻意不包含止盈、止损或移动止损,与参考的 MQL 脚本保持一致。因此它更适合作为测试用的工具或教学示例,而不是盈利策略。
交易逻辑
- 订阅配置好的蜡烛序列(
CandleType)。默认周期为 15 分钟,用以模拟 MetaTrader 的“当前时间框架”。
- 当一根蜡烛完成时,检查是否需要关闭旧持仓。启用
CloseBeforeReversal 时,策略会先平仓并等待持仓归零后才会发出下一笔订单。
- 使用伪随机数生成器产生一个交易方向。可选参数
RandomSeed 允许通过固定种子生成可复现的序列,便于回测对比。
- 按固定的
TradeVolume 下达市价单。多空对称,且没有任何保护性订单。可以通过 LogSignals 开启日志,以便追踪每次随机决策。
由于每根蜡烛只会触发一次随机决策,策略在任何时刻要么空仓,要么只持有一笔仓位。仓位只会在下一根 K 线出现时被平仓或反向。
订单管理与风险
- 所有开仓和平仓都通过
BuyMarket / SellMarket 完成,没有限价或止损订单。
- 如果关闭
CloseBeforeReversal,策略可能会连续持仓:新的随机信号可以直接开出相反方向,而无需显式平掉上一笔交易。
- 策略没有资金管理或账户保护逻辑。移植的目的在于还原原始专家顾问的行为,便于教学和基础设施测试。
参数
| 参数 |
说明 |
TradeVolume |
每次随机下单使用的基础数量,必须为正数。 |
CloseBeforeReversal |
是否在执行下一笔随机交易前强制平掉当前持仓。 |
LogSignals |
每次生成随机方向时写入 AddInfoLog 日志。 |
CandleType |
决定随机抛硬币触发频率的蜡烛时间框架。 |
RandomSeed |
伪随机数生成器的种子,设置为 0 表示使用系统时间。 |
使用提示
- 与原始 MQL 程序一样,移植版本没有止盈止损。如需实盘实验,必须自行添加风险控制。
- 固定随机种子可以生成可复现的数据集,便于优化或对比不同的随机序列。
- 建议在测试阶段启用日志,因为纯随机策略在图表上的反馈非常有限。
using System;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that randomly opens long or short positions based on a random threshold.
/// Mirrors the "At random" MetaTrader expert.
/// </summary>
public class AtRandomStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _randomSeed;
private Random _random;
private int _barCount;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RandomSeed
{
get => _randomSeed.Value;
set => _randomSeed.Value = value;
}
public AtRandomStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe that triggers random decisions", "Data");
_randomSeed = Param(nameof(RandomSeed), 42)
.SetDisplay("Random Seed", "Seed for the pseudo random generator (0 = system clock)", "Diagnostics");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_random = RandomSeed == 0 ? new Random() : new Random(RandomSeed);
_barCount = 0;
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;
_barCount++;
// Only trade occasionally to keep turnover within runner limits.
if (_random.Next(0, 6) != 0)
return;
var volume = Volume;
if (volume <= 0)
volume = 1;
// Random direction
var goLong = _random.Next(0, 2) == 0;
if (goLong)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
_random = null;
_barCount = 0;
base.OnReseted();
}
}
import clr
import random
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.Strategies import Strategy
class at_random_strategy(Strategy):
def __init__(self):
super(at_random_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._random_seed = self.Param("RandomSeed", 42)
self._rng = None
self._bar_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RandomSeed(self):
return self._random_seed.Value
@RandomSeed.setter
def RandomSeed(self, value):
self._random_seed.Value = value
def OnReseted(self):
super(at_random_strategy, self).OnReseted()
self._rng = None
self._bar_count = 0
def OnStarted2(self, time):
super(at_random_strategy, self).OnStarted2(time)
seed = self.RandomSeed
self._rng = random.Random(seed if seed != 0 else None)
self._bar_count = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._bar_count += 1
# Only trade occasionally
if self._rng.randint(0, 5) != 0:
return
go_long = self._rng.randint(0, 1) == 0
if go_long:
if self.Position <= 0:
self.BuyMarket()
else:
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return at_random_strategy()