Martin 1 策略
将 MetaTrader 5 的 "Martin 1" 智能交易程序移植到 StockSharp 高级策略 API。算法始终保持持仓,通过对冲式马丁格尔步骤在回撤中对冲,同时在顺势行情中做金字塔加仓。
交易逻辑
- 初始持仓:当账户为空仓时,无论时间过滤是否启用,都会立即按照
StartDirection指定的方向开仓。基础下单量来源于InitialVolume,并向下取整到交易所允许的VolumeStep。 - 时间窗口过滤:启用
UseTradingHours后,仅在StartHour到EndHour(含两端)的交易所时间内允许做加仓或对冲操作。 - 盈利加仓:每根已完成的 K 线都会评估现有持仓。如果多头持仓的浮动盈利超过设定的止盈距离且仍为正值,则再开一笔同样手数的多单;空头持仓采用对称逻辑。成交价视为当前 K 线的收盘价。
- 对冲马丁格尔:当起始方向为多头且浮亏超过
(StopLossPips × 点值 × (当前加倍次数 + 1))时,会按LotMultiplier放大当前手数(向下取整到步长)并开出新的空头作为对冲,同时递增马丁格尔计数。若起始方向为空头,则执行镜像逻辑。达到MaxMultiplications次后不再继续加倍对冲。 - 全局盈利目标:计算所有剩余持仓的未实现盈亏,使用
PriceStep和StepPrice将价差转换为货币。若总额超过MinProfit,则全部平仓并重置马丁格尔状态。
风险与资金管理
- 点值基于品种的最小报价步长计算;对于三位或五位报价,会额外乘以 10,以模拟原始 EA 的点值调整。
- 委托数量总是向下取整到最近的
VolumeStep,若结果低于步长则放弃下单。 - 只要仓位被完全平掉(无论是自然平仓还是达成目标盈利),马丁格尔计数与当前手数都会被重置。
- 浮动盈利估算不考虑佣金与隔夜利息,与原程序一样仅依赖未实现 PnL。
参数
| 名称 | 说明 | 默认值 |
|---|---|---|
CandleType |
用于驱动策略的 K 线类型。 | 1 分钟 |
UseTradingHours |
是否启用交易时间过滤。 | true |
StartHour |
允许执行加仓或对冲的起始小时。 | 2 |
EndHour |
停止执行加仓或对冲的截止小时。 | 21 |
LotMultiplier |
每次触发对冲前对手数的放大倍数。 | 1.6 |
MaxMultiplications |
最多允许的马丁格尔加倍次数。 | 5 |
StartDirection |
空仓后首次建仓的方向。 | Buy |
MinProfit |
触发一键平仓的浮动盈利(货币单位)。 | 1.5 |
InitialVolume |
初次建仓和重置时使用的基础手数。 | 0.1 |
StopLossPips |
触发下一次对冲的点数距离。 | 40 |
TakeProfitPips |
触发顺势加仓的点数距离。 | 100 |
实现说明
ProcessCandle通过高阶的SubscribeCandles().Bind(...)管线,只处理已完成的 K 线,符合平台指南。- 策略内部维护两个先进先出列表来跟踪多、空持仓,从而在净持仓账户上模拟 MetaTrader 的对冲行为。
- 盈亏换算优先使用
Security.PriceStep与Security.StepPrice;若无可用数据,则回退为价差乘以成交量。 - 策略持续运行;若关闭时间过滤或放宽交易时段,其行为将与原始全天候运行的 EA 相同。
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>
/// Hedging martingale strategy converted from the MetaTrader script "Martin 1".
/// Adds pyramid entries in profit and opens opposite hedges with increased volume after drawdowns.
/// </summary>
public class Martin1Strategy : Strategy
{
private sealed class PositionRecord
{
public decimal Volume { get; set; }
public decimal EntryPrice { get; set; }
}
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _useTradingHours;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _endHour;
private readonly StrategyParam<decimal> _lotMultiplier;
private readonly StrategyParam<int> _maxMultiplications;
private readonly StrategyParam<Sides> _startDirection;
private readonly StrategyParam<decimal> _minProfit;
private readonly StrategyParam<decimal> _initialVolume;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly List<PositionRecord> _longPositions = new();
private readonly List<PositionRecord> _shortPositions = new();
private decimal _currentVolume;
private int _multiplicationCount;
/// <summary>
/// Candle type for driving the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Enable the trading hour filter.
/// </summary>
public bool UseTradingHours
{
get => _useTradingHours.Value;
set => _useTradingHours.Value = value;
}
/// <summary>
/// Inclusive hour (exchange time) when the trading window opens.
/// </summary>
public int StartHour
{
get => _startHour.Value;
set => _startHour.Value = value;
}
/// <summary>
/// Inclusive hour (exchange time) when the trading window closes.
/// </summary>
public int EndHour
{
get => _endHour.Value;
set => _endHour.Value = value;
}
/// <summary>
/// Multiplier applied to the current volume after each hedging step.
/// </summary>
public decimal LotMultiplier
{
get => _lotMultiplier.Value;
set => _lotMultiplier.Value = value;
}
/// <summary>
/// Maximum number of hedging multiplications that can be triggered.
/// </summary>
public int MaxMultiplications
{
get => _maxMultiplications.Value;
set => _maxMultiplications.Value = value;
}
/// <summary>
/// Direction of the very first position that is opened when flat.
/// </summary>
public Sides StartDirection
{
get => _startDirection.Value;
set => _startDirection.Value = value;
}
/// <summary>
/// Minimum floating profit that triggers closing all positions.
/// </summary>
public decimal MinProfit
{
get => _minProfit.Value;
set => _minProfit.Value = value;
}
/// <summary>
/// Base order volume used for the initial trade.
/// </summary>
public decimal InitialVolume
{
get => _initialVolume.Value;
set => _initialVolume.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance expressed in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="Martin1Strategy"/> class.
/// </summary>
public Martin1Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used to evaluate conditions", "General");
_useTradingHours = Param(nameof(UseTradingHours), false)
.SetDisplay("Use Trading Hours", "Restrict entries to a time window", "General");
_startHour = Param(nameof(StartHour), 2)
.SetRange(0, 23)
.SetDisplay("Start Hour", "Hour to start monitoring for new trades", "General");
_endHour = Param(nameof(EndHour), 21)
.SetRange(0, 23)
.SetDisplay("End Hour", "Hour to stop opening hedges/pyramids", "General");
_lotMultiplier = Param(nameof(LotMultiplier), 1.6m)
.SetGreaterThanZero()
.SetDisplay("Lot Multiplier", "Factor applied to volume after a loss", "Money Management")
.SetOptimize(1.1m, 3m, 0.1m);
_maxMultiplications = Param(nameof(MaxMultiplications), 5)
.SetGreaterThanZero()
.SetDisplay("Max Multiplications", "Maximum hedging steps", "Money Management")
.SetOptimize(1, 10, 1);
_startDirection = Param(nameof(StartDirection), Sides.Buy)
.SetDisplay("Start Direction", "Side of the initial order", "Trading");
_minProfit = Param(nameof(MinProfit), 1.5m)
.SetDisplay("Min Profit", "Floating profit target to flatten", "Risk")
.SetOptimize(0.5m, 10m, 0.5m);
_initialVolume = Param(nameof(InitialVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Initial Volume", "Baseline order size", "Money Management");
_stopLossPips = Param(nameof(StopLossPips), 400)
.SetGreaterThanZero()
.SetDisplay("Stop Loss (pips)", "Distance before hedging the opposite side", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 1000)
.SetGreaterThanZero()
.SetDisplay("Take Profit (pips)", "Distance to pyramid in the same direction", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_longPositions.Clear();
_shortPositions.Clear();
_multiplicationCount = 0;
_currentVolume = AdjustVolume(InitialVolume);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (UseTradingHours && StartHour >= EndHour)
throw new InvalidOperationException("Start hour must be less than end hour when the filter is enabled.");
_longPositions.Clear();
_shortPositions.Clear();
_multiplicationCount = 0;
_currentVolume = AdjustVolume(InitialVolume);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// No indicators to check.
var closePrice = candle.ClosePrice;
var totalProfit = CalculateOpenProfit(closePrice);
var withinHours = !UseTradingHours || IsWithinTradingHours(candle.CloseTime);
if (withinHours)
{
if (_longPositions.Count > 0)
EvaluateLongPositions(closePrice);
if (_shortPositions.Count > 0)
EvaluateShortPositions(closePrice);
}
if (_longPositions.Count == 0 && _shortPositions.Count == 0)
{
ResetMartingale();
OpenInitialPosition(closePrice);
return;
}
if ((_longPositions.Count > 0 || _shortPositions.Count > 0) && totalProfit > MinProfit)
{
CloseAllPositions(closePrice);
ResetMartingale();
}
}
private void EvaluateLongPositions(decimal closePrice)
{
var takeProfitDistance = GetTakeProfitDistance();
var stopLossDistance = GetStopLossDistance();
var snapshot = _longPositions.ToArray();
foreach (var position in snapshot)
{
var priceGain = closePrice - position.EntryPrice;
var profit = ConvertPriceToMoney(priceGain, position.Volume);
if (profit > 0m && priceGain > takeProfitDistance)
ExecuteOrder(Sides.Buy, _currentVolume, closePrice);
if (StartDirection == Sides.Buy && stopLossDistance > 0m)
{
var lossDistance = position.EntryPrice - closePrice;
if (lossDistance > stopLossDistance * (_multiplicationCount + 1) &&
_multiplicationCount + 1 <= MaxMultiplications)
{
var newVolume = AdjustVolume(_currentVolume * LotMultiplier);
if (newVolume > 0m)
{
_multiplicationCount++;
_currentVolume = newVolume;
ExecuteOrder(Sides.Sell, newVolume, closePrice);
}
}
}
}
}
private void EvaluateShortPositions(decimal closePrice)
{
var takeProfitDistance = GetTakeProfitDistance();
var stopLossDistance = GetStopLossDistance();
var snapshot = _shortPositions.ToArray();
foreach (var position in snapshot)
{
var priceGain = position.EntryPrice - closePrice;
var profit = ConvertPriceToMoney(priceGain, position.Volume);
if (profit > 0m && priceGain > takeProfitDistance)
ExecuteOrder(Sides.Sell, _currentVolume, closePrice);
if (StartDirection == Sides.Sell && stopLossDistance > 0m)
{
var lossDistance = closePrice - position.EntryPrice;
if (lossDistance > stopLossDistance * (_multiplicationCount + 1) &&
_multiplicationCount + 1 <= MaxMultiplications)
{
var newVolume = AdjustVolume(_currentVolume * LotMultiplier);
if (newVolume > 0m)
{
_multiplicationCount++;
_currentVolume = newVolume;
ExecuteOrder(Sides.Buy, newVolume, closePrice);
}
}
}
}
}
private void OpenInitialPosition(decimal price)
{
var volume = _currentVolume;
if (volume <= 0m)
return;
var side = StartDirection == Sides.Sell ? Sides.Sell : Sides.Buy;
ExecuteOrder(side, volume, price);
}
private void CloseAllPositions(decimal price)
{
var longVolume = GetTotalVolume(_longPositions);
if (longVolume > 0m)
{
ExecuteOrder(Sides.Sell, longVolume, price);
}
var shortVolume = GetTotalVolume(_shortPositions);
if (shortVolume > 0m)
{
ExecuteOrder(Sides.Buy, shortVolume, price);
}
}
private void ExecuteOrder(Sides side, decimal volume, decimal price)
{
if (volume <= 0m)
return;
Volume = volume;
if (side == Sides.Buy)
BuyMarket();
else
SellMarket();
UpdatePositions(side, volume, price);
}
private void UpdatePositions(Sides side, decimal volume, decimal price)
{
if (volume <= 0m)
return;
if (side == Sides.Buy)
{
var remaining = volume;
var index = 0;
while (remaining > 0m && index < _shortPositions.Count)
{
var position = _shortPositions[index];
var qty = Math.Min(position.Volume, remaining);
position.Volume -= qty;
remaining -= qty;
if (position.Volume <= 0m)
{
_shortPositions.RemoveAt(index);
continue;
}
index++;
}
if (remaining > 0m)
{
_longPositions.Add(new PositionRecord
{
Volume = remaining,
EntryPrice = price
});
}
}
else
{
var remaining = volume;
var index = 0;
while (remaining > 0m && index < _longPositions.Count)
{
var position = _longPositions[index];
var qty = Math.Min(position.Volume, remaining);
position.Volume -= qty;
remaining -= qty;
if (position.Volume <= 0m)
{
_longPositions.RemoveAt(index);
continue;
}
index++;
}
if (remaining > 0m)
{
_shortPositions.Add(new PositionRecord
{
Volume = remaining,
EntryPrice = price
});
}
}
}
private decimal CalculateOpenProfit(decimal currentPrice)
{
var profit = 0m;
foreach (var position in _longPositions)
{
var diff = currentPrice - position.EntryPrice;
profit += ConvertPriceToMoney(diff, position.Volume);
}
foreach (var position in _shortPositions)
{
var diff = position.EntryPrice - currentPrice;
profit += ConvertPriceToMoney(diff, position.Volume);
}
return profit;
}
private decimal ConvertPriceToMoney(decimal priceDifference, decimal volume)
{
var priceStep = Security?.PriceStep ?? 0m;
var stepPrice = priceStep;
if (priceStep <= 0m || stepPrice <= 0m)
return priceDifference * volume;
var steps = priceDifference / priceStep;
return steps * stepPrice * volume;
}
private decimal GetStopLossDistance()
{
var pip = GetPipSize();
return StopLossPips * pip;
}
private decimal GetTakeProfitDistance()
{
var pip = GetPipSize();
return TakeProfitPips * pip;
}
private decimal GetPipSize()
{
var priceStep = Security?.PriceStep ?? 0m;
if (priceStep <= 0m)
return 1m;
var step = priceStep;
var digits = 0;
while (step < 1m && digits < 10)
{
step *= 10m;
digits++;
}
if (digits == 3 || digits == 5)
return priceStep * 10m;
return priceStep;
}
private decimal AdjustVolume(decimal volume)
{
if (volume <= 0m)
return 0m;
var step = Security?.VolumeStep ?? 0m;
if (step > 0m)
{
volume = Math.Floor(volume / step) * step;
if (volume < step)
return 0m;
}
return volume;
}
private static decimal GetTotalVolume(List<PositionRecord> positions)
{
var total = 0m;
foreach (var position in positions)
total += position.Volume;
return total;
}
private bool IsWithinTradingHours(DateTime time)
{
var hour = time.Hour;
return hour >= StartHour && hour <= EndHour;
}
private void ResetMartingale()
{
_multiplicationCount = 0;
_currentVolume = AdjustVolume(InitialVolume);
}
}
import clr
import math
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 martin1_strategy(Strategy):
"""Martin 1: hedging martingale with pyramid entries on profit and opposite hedges on drawdown."""
def __init__(self):
super(martin1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used to evaluate conditions", "General")
self._use_trading_hours = self.Param("UseTradingHours", False) \
.SetDisplay("Use Trading Hours", "Restrict entries to a time window", "General")
self._start_hour = self.Param("StartHour", 2) \
.SetDisplay("Start Hour", "Hour to start monitoring for new trades", "General")
self._end_hour = self.Param("EndHour", 21) \
.SetDisplay("End Hour", "Hour to stop opening hedges/pyramids", "General")
self._lot_multiplier = self.Param("LotMultiplier", 1.6) \
.SetGreaterThanZero() \
.SetDisplay("Lot Multiplier", "Factor applied to volume after a loss", "Money Management")
self._max_multiplications = self.Param("MaxMultiplications", 5) \
.SetGreaterThanZero() \
.SetDisplay("Max Multiplications", "Maximum hedging steps", "Money Management")
# 0=Buy, 1=Sell
self._start_direction = self.Param("StartDirection", 0) \
.SetDisplay("Start Direction", "0=Buy, 1=Sell", "Trading")
self._min_profit = self.Param("MinProfit", 1.5) \
.SetDisplay("Min Profit", "Floating profit target to flatten", "Risk")
self._initial_volume = self.Param("InitialVolume", 0.1) \
.SetGreaterThanZero() \
.SetDisplay("Initial Volume", "Baseline order size", "Money Management")
self._stop_loss_pips = self.Param("StopLossPips", 400) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss (pips)", "Distance before hedging the opposite side", "Risk")
self._take_profit_pips = self.Param("TakeProfitPips", 1000) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit (pips)", "Distance to pyramid in same direction", "Risk")
self._long_positions = []
self._short_positions = []
self._current_volume = 0.0
self._multiplication_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def UseTradingHours(self):
return self._use_trading_hours.Value
@property
def StartHour(self):
return int(self._start_hour.Value)
@property
def EndHour(self):
return int(self._end_hour.Value)
@property
def LotMultiplier(self):
return float(self._lot_multiplier.Value)
@property
def MaxMultiplications(self):
return int(self._max_multiplications.Value)
@property
def StartDirection(self):
return int(self._start_direction.Value)
@property
def MinProfit(self):
return float(self._min_profit.Value)
@property
def InitialVolume(self):
return float(self._initial_volume.Value)
@property
def StopLossPips(self):
return int(self._stop_loss_pips.Value)
@property
def TakeProfitPips(self):
return int(self._take_profit_pips.Value)
def _get_pip_size(self):
sec = self.Security
if sec is None or sec.PriceStep is None:
return 1.0
step = float(sec.PriceStep)
if step <= 0:
return 1.0
s = step
digits = 0
while s < 1.0 and digits < 10:
s *= 10
digits += 1
return step * 10.0 if (digits == 3 or digits == 5) else step
def _adjust_volume(self, volume):
if volume <= 0:
return 0.0
sec = self.Security
if sec is not None and sec.VolumeStep is not None:
step = float(sec.VolumeStep)
if step > 0:
volume = math.floor(volume / step) * step
if volume < step:
return 0.0
return volume
def OnStarted2(self, time):
super(martin1_strategy, self).OnStarted2(time)
self._long_positions = []
self._short_positions = []
self._multiplication_count = 0
self._current_volume = self._adjust_volume(self.InitialVolume)
self._pip_size = self._get_pip_size()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close_price = float(candle.ClosePrice)
total_profit = self._calc_open_profit(close_price)
within_hours = not self.UseTradingHours or self._is_within_hours(candle.CloseTime)
if within_hours:
if len(self._long_positions) > 0:
self._eval_longs(close_price)
if len(self._short_positions) > 0:
self._eval_shorts(close_price)
if len(self._long_positions) == 0 and len(self._short_positions) == 0:
self._reset_martingale()
self._open_initial(close_price)
return
if (len(self._long_positions) > 0 or len(self._short_positions) > 0) and total_profit > self.MinProfit:
self._close_all(close_price)
self._reset_martingale()
def _eval_longs(self, close_price):
tp_dist = self.TakeProfitPips * self._pip_size
sl_dist = self.StopLossPips * self._pip_size
for pos in list(self._long_positions):
price_gain = close_price - pos["entry"]
profit = self._price_to_money(price_gain, pos["volume"])
if profit > 0 and price_gain > tp_dist:
self._execute_order("buy", self._current_volume, close_price)
if self.StartDirection == 0 and sl_dist > 0:
loss_dist = pos["entry"] - close_price
if (loss_dist > sl_dist * (self._multiplication_count + 1)
and self._multiplication_count + 1 <= self.MaxMultiplications):
new_vol = self._adjust_volume(self._current_volume * self.LotMultiplier)
if new_vol > 0:
self._multiplication_count += 1
self._current_volume = new_vol
self._execute_order("sell", new_vol, close_price)
def _eval_shorts(self, close_price):
tp_dist = self.TakeProfitPips * self._pip_size
sl_dist = self.StopLossPips * self._pip_size
for pos in list(self._short_positions):
price_gain = pos["entry"] - close_price
profit = self._price_to_money(price_gain, pos["volume"])
if profit > 0 and price_gain > tp_dist:
self._execute_order("sell", self._current_volume, close_price)
if self.StartDirection == 1 and sl_dist > 0:
loss_dist = close_price - pos["entry"]
if (loss_dist > sl_dist * (self._multiplication_count + 1)
and self._multiplication_count + 1 <= self.MaxMultiplications):
new_vol = self._adjust_volume(self._current_volume * self.LotMultiplier)
if new_vol > 0:
self._multiplication_count += 1
self._current_volume = new_vol
self._execute_order("buy", new_vol, close_price)
def _open_initial(self, price):
vol = self._current_volume
if vol <= 0:
return
side = "sell" if self.StartDirection == 1 else "buy"
self._execute_order(side, vol, price)
def _close_all(self, price):
long_vol = sum(p["volume"] for p in self._long_positions)
if long_vol > 0:
self._execute_order("sell", long_vol, price)
short_vol = sum(p["volume"] for p in self._short_positions)
if short_vol > 0:
self._execute_order("buy", short_vol, price)
def _execute_order(self, side, volume, price):
if volume <= 0:
return
if side == "buy":
self.BuyMarket()
else:
self.SellMarket()
self._update_positions(side, volume, price)
def _update_positions(self, side, volume, price):
if volume <= 0:
return
if side == "buy":
remaining = volume
while remaining > 0 and len(self._short_positions) > 0:
pos = self._short_positions[0]
qty = min(pos["volume"], remaining)
pos["volume"] -= qty
remaining -= qty
if pos["volume"] <= 0:
self._short_positions.pop(0)
if remaining > 0:
self._long_positions.append({"volume": remaining, "entry": price})
else:
remaining = volume
while remaining > 0 and len(self._long_positions) > 0:
pos = self._long_positions[0]
qty = min(pos["volume"], remaining)
pos["volume"] -= qty
remaining -= qty
if pos["volume"] <= 0:
self._long_positions.pop(0)
if remaining > 0:
self._short_positions.append({"volume": remaining, "entry": price})
def _calc_open_profit(self, current_price):
profit = 0.0
for pos in self._long_positions:
diff = current_price - pos["entry"]
profit += self._price_to_money(diff, pos["volume"])
for pos in self._short_positions:
diff = pos["entry"] - current_price
profit += self._price_to_money(diff, pos["volume"])
return profit
def _price_to_money(self, price_diff, volume):
sec = self.Security
if sec is not None and sec.PriceStep is not None:
step = float(sec.PriceStep)
if step > 0:
steps = price_diff / step
return steps * step * volume
return price_diff * volume
def _is_within_hours(self, time):
hour = time.Hour
return hour >= self.StartHour and hour <= self.EndHour
def _reset_martingale(self):
self._multiplication_count = 0
self._current_volume = self._adjust_volume(self.InitialVolume)
def OnReseted(self):
super(martin1_strategy, self).OnReseted()
self._long_positions = []
self._short_positions = []
self._current_volume = 0.0
self._multiplication_count = 0
def CreateClone(self):
return martin1_strategy()