Martin 1 Strategy
Conversion of the MetaTrader 5 expert advisor "Martin 1" into the StockSharp high level strategy API. The algorithm continuously maintains exposure and uses hedging-style martingale steps to recover drawdowns while pyramiding into profitable trends.
Trading Logic
- Initial exposure – when the strategy is flat it immediately opens a position in the direction defined by
StartDirection, regardless of the time filter. The base order size is taken fromInitialVolumeafter rounding to the instrument volume step. - Time window filter – when
UseTradingHoursis enabled, only scaling actions (pyramiding or hedging) are allowed betweenStartHourandEndHourinclusive, using the exchange time contained in candle timestamps. - Pyramiding winners – every open position is evaluated on each finished candle. If the floating profit of a long position exceeds the take-profit distance and remains positive, an additional long order with the current volume is sent. Short positions behave symmetrically. The new order price is assumed to be the close of the current candle.
- Hedging martingale – when the start direction is long and a long position loses more than
(StopLossPips × pip size × (multiplication index + 1)), the strategy opens an opposite short order. Before placing the hedge the volume is multiplied byLotMultiplier, rounded to the allowed step, and the multiplication counter is increased. The same logic is applied in reverse for short start direction. Hedging stops onceMaxMultiplicationssteps have been reached. - Global profit target – the unrealized profit across all remaining positions (converted to money using
PriceStep/StepPrice) is summed. If it exceedsMinProfit, every open position is closed by issuing a market order in the opposite direction, and the martingale state is reset.
Risk and Money Management
- The pip size is computed from the security price step. Three- and five-digit quotes multiply the step by ten to emulate the original MetaTrader pip adjustment.
- Volumes are rounded down to the nearest
VolumeStep. If the rounded value falls below the step, the order is skipped. - The martingale counter and current volume are reset whenever the book becomes flat, either naturally or after hitting the global profit target.
- Profit estimation ignores commissions and swaps, mirroring the behaviour of the original script which relied purely on floating PnL.
Parameters
| Name | Description | Default |
|---|---|---|
CandleType |
Candle type that drives all calculations. | 1 minute timeframe |
UseTradingHours |
Enables or disables the time window filter. | true |
StartHour |
Inclusive hour when the time filter allows new scaling actions. | 2 |
EndHour |
Inclusive hour when scaling actions stop. | 21 |
LotMultiplier |
Factor applied to the current volume before opening a hedge. | 1.6 |
MaxMultiplications |
Maximum number of hedging steps that may be triggered. | 5 |
StartDirection |
Direction of the very first order after the strategy becomes flat. | Buy |
MinProfit |
Floating profit (in money) that forces all positions to close. | 1.5 |
InitialVolume |
Base volume for the very first order and reset state. | 0.1 |
StopLossPips |
Pip distance that triggers the next martingale hedge. | 40 |
TakeProfitPips |
Pip distance that triggers a pyramiding entry. | 100 |
Implementation Notes
ProcessCandleuses the high-level candle subscription pipeline (SubscribeCandles().Bind(...)) and operates strictly on finished candles, complying with the platform guidelines.- Hedged exposure is tracked internally with two FIFO lists so that the strategy can emulate MetaTrader hedging behaviour even on netting accounts.
- Profit conversion relies on
Security.PriceStepandSecurity.StepPrice. When those values are unavailable the difference in price is multiplied directly by the traded volume as a fallback. - The strategy keeps trading continuously; disabling the time filter or setting wide hours will make the algorithm behave like the original always-on expert advisor.
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()