クロスシンプル戦略
Kloss Simple Strategy は、MetaTrader 4 エキスパート アドバイザー Kloss_.mq4 を直接変換したものです。 StockSharp の高レベルの API を使用して元の取引アイデアを再構築し、加重終値に基づいて計算された指数移動平均 (EMA)、商品チャネル指数 (CCI)、および Stochastic オシレーターというインジケーター セットを同一に保ちます。シグナルは、MQL バージョンの 1 バー シフト ロジックを反映して、以前に完了したローソク足から生成されます。ポジションサイジングは、元のロット計算ルールと同様に、固定注文量またはポートフォリオ値のリスクパーセンテージのいずれかに依存できます。
コアアイデア
- 中立レベル付近の CCI および Stochastic のしきい値を使用して、運動量のコンテキストを監視します。
- 加重終値の短期的な EMA で勢いシグナルを確認します。
- 前のローソク足がすべてのシグナル条件を満たしている場合にのみポジションを入力し、不完全な市場データでの早まった取引を防ぎます。
- MT4 スクリプトの「MaxOrders」パラメータをエミュレートして、構成可能な制限まで同じ方向の複数のエントリを許可します。
インジケーターの構成
- EMA (MaPeriod): 加重終値
(Close * 2 + High + Low) / 4を使用して、MetaTrader からのPRICE_WEIGHTEDと一致します。短期トレンドフィルターとして機能します。 - CCI (CciPeriod): 平均価格からの勢いの偏差を評価します。しきい値
±CciLevelは、積極的なエントリと保守的なエントリを定義します。 - Stochastic (StochasticKPeriod / DPeriod / Smooth): メインの %K ラインを使用して、ニュートラルな 50 レベルを基準とした買われすぎまたは売られすぎの状態を検出します。 50 からの偏差は
StochasticLevelによって制御されます。
すべてのインジケーターは、CandleType で定義された主要なローソク足シリーズで動作します。この戦略は、完成したローソク足のインジケーター値のみを更新し、安定したバックテストと実際の動作を保証します。
取引ロジック
長いセットアップ
- 前回のローソク足の終値は、前の EMA の値を上回っています。
- 以前の CCI の値は
-CciLevelを下回っており、売られすぎの勢いを示しています。 - 以前の Stochastic %K 値は
50 - StochasticLevelを下回っており、売られ過ぎのオシレーションを確認しています。 - 条件が満たされると、既存のロング注文の数が
MaxOrdersを下回っている限り、ショート エクスポージャーはクローズされ、新しいロング ポジションがオープンされます。
簡単なセットアップ
- 前回のローソク足の終値は、前の EMA の値を下回っています。
- 以前の CCI の値は
+CciLevelを上回っており、買われすぎの勢いを示しています。 - 以前の Stochastic %K 値は
50 + StochasticLevelを上回っており、買われ過ぎのオシレーションを確認しています。 - 条件が満たされると、長時間露光はクローズされ、
MaxOrdersの制限に従って新しいショート ポジションがオープンされます。
出口管理
- ストップロス/テイクプロフィット: オプションの商品ポイントの絶対距離。いずれかの値がゼロより大きい場合、StockSharp の組み込み位置保護が有効になります。
- 反対シグナル: 反対方向にオープンする前に、元のエキスパートアドバイザーを模倣するために現在のポジションがクローズされます。
ポジションサイズ
- OrderVolume: MT4 から
Lotsパラメータを複製するデフォルトの固定サイズ。 - RiskPercentage: ゼロより大きい場合、ストラテジーは取引サイズをポートフォリオ値のパーセンテージとして計算します。利用可能な場合は商品証拠金要件を使用し、利用できない場合は価格ベースのサイジングに戻り、MQL コードの
Lots == 0の動作を再現します。 - MaxOrders: 最大
MaxOrders * OrderVolume件の露出を許可することで、方向ごとの累積数量を制限します。
パラメーター
| パラメータ | 説明 |
|---|---|
OrderVolume |
RiskPercentage がゼロの場合に使用される基本注文サイズ。 |
MaPeriod |
加重終値に基づいて構築された EMA の長さ。 |
CciPeriod |
CCI の計算に使用されるバーの数。 |
CciLevel |
信号生成の絶対 CCI しきい値。 |
StochasticKPeriod |
Stochastic %K 行をルックバックします。 |
StochasticDPeriod |
%D ラインの移動平均期間。 |
StochasticSmooth |
追加の平滑化が %K に適用されました。 |
StochasticLevel |
買われすぎ/売られすぎの検出に使用される 50 からの偏差。 |
MaxOrders |
方向ごとに許可されるエントリの最大数。 |
StopLossPoints |
価格ポイントでのオプションのストップロス距離。 |
TakeProfitPoints |
価格ポイントでのオプションのテイクプロフィットディスタンス。 |
RiskPercentage |
動的なポジションサイジングのためのポートフォリオの割合。 |
CandleType |
すべての計算に使用されるローソク足シリーズ。 |
実用上の注意
- 短期オシレーターが価格変動に素早く反応する日中データに最適です。
- 加重終値により、ローソク足の高値/安値範囲を組み込みながら、EMA の応答性が維持されます。
- すべての決定は前のローソク足に依存するため、この戦略はバー内の再描画を回避し、ヒストリカル テストにおいて決定性を維持します。
OrderVolumeとMaxOrdersが実行可能な取引サイズに対応するように、リスク管理をブローカーの契約仕様に合わせる必要があります。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on EMA, CCI, and Stochastic oscillator signals.
/// Converts the original Kloss expert advisor from MetaTrader 4 to StockSharp.
/// </summary>
public class KlossSimpleStrategy : Strategy
{
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLevel;
private readonly StrategyParam<int> _stochasticKPeriod;
private readonly StrategyParam<int> _stochasticDPeriod;
private readonly StrategyParam<int> _stochasticSmooth;
private readonly StrategyParam<decimal> _stochasticLevel;
private readonly StrategyParam<int> _maxOrders;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _riskPercentage;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema;
private CommodityChannelIndex _cci;
private StochasticOscillator _stochastic;
private decimal? _previousCci;
/// <summary>
/// Initializes a new instance of the <see cref="KlossSimpleStrategy"/> class.
/// </summary>
public KlossSimpleStrategy()
{
_orderVolume = Param(nameof(OrderVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Volume", "Base order volume", "Trading");
_maPeriod = Param(nameof(MaPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Length of the exponential moving average", "Indicators")
.SetOptimize(3, 20, 1);
_cciPeriod = Param(nameof(CciPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Length of the commodity channel index", "Indicators")
.SetOptimize(5, 30, 5);
_cciLevel = Param(nameof(CciLevel), 200m)
.SetGreaterThanZero()
.SetDisplay("CCI Level", "Distance from zero to trigger signals", "Indicators")
.SetOptimize(50m, 200m, 10m);
_stochasticKPeriod = Param(nameof(StochasticKPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
.SetOptimize(3, 20, 1);
_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Period of the %D line", "Indicators")
.SetOptimize(1, 10, 1);
_stochasticSmooth = Param(nameof(StochasticSmooth), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic Smooth", "Smoothing factor for %K", "Indicators")
.SetOptimize(1, 10, 1);
_stochasticLevel = Param(nameof(StochasticLevel), 30m)
.SetGreaterThanZero()
.SetDisplay("Stochastic Level", "Distance from 50 to trigger signals", "Indicators")
.SetOptimize(10m, 40m, 5m);
_maxOrders = Param(nameof(MaxOrders), 1)
.SetNotNegative()
.SetDisplay("Max Orders", "Maximum number of positions per direction", "Trading");
_stopLossPoints = Param(nameof(StopLossPoints), 0m)
.SetNotNegative()
.SetDisplay("Stop Loss (pts)", "Stop loss distance in points", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 0m)
.SetNotNegative()
.SetDisplay("Take Profit (pts)", "Take profit distance in points", "Risk");
_riskPercentage = Param(nameof(RiskPercentage), 10m)
.SetNotNegative()
.SetDisplay("Risk %", "Portfolio percentage for dynamic position sizing", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary candle series for calculations", "General");
}
/// <summary>Base order volume for new entries.</summary>
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
/// <summary>EMA length used to filter price action.</summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>CCI period for momentum detection.</summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>Absolute level that CCI must exceed to signal an entry.</summary>
public decimal CciLevel
{
get => _cciLevel.Value;
set => _cciLevel.Value = value;
}
/// <summary>Stochastic %K period.</summary>
public int StochasticKPeriod
{
get => _stochasticKPeriod.Value;
set => _stochasticKPeriod.Value = value;
}
/// <summary>Stochastic %D period.</summary>
public int StochasticDPeriod
{
get => _stochasticDPeriod.Value;
set => _stochasticDPeriod.Value = value;
}
/// <summary>Smoothing applied to the %K line.</summary>
public int StochasticSmooth
{
get => _stochasticSmooth.Value;
set => _stochasticSmooth.Value = value;
}
/// <summary>Offset around 50 used for stochastic thresholds.</summary>
public decimal StochasticLevel
{
get => _stochasticLevel.Value;
set => _stochasticLevel.Value = value;
}
/// <summary>Maximum number of simultaneous entries per direction.</summary>
public int MaxOrders
{
get => _maxOrders.Value;
set => _maxOrders.Value = value;
}
/// <summary>Stop loss distance expressed in price points.</summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>Take profit distance expressed in price points.</summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>Portfolio percentage used to size positions dynamically.</summary>
public decimal RiskPercentage
{
get => _riskPercentage.Value;
set => _riskPercentage.Value = value;
}
/// <summary>Candle type used for indicator calculations.</summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousCci = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new EMA { Length = MaPeriod };
_cci = new CommodityChannelIndex { Length = CciPeriod };
_stochastic = new StochasticOscillator();
_stochastic.K.Length = StochasticKPeriod;
_stochastic.D.Length = StochasticDPeriod;
Volume = OrderVolume;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
Unit stopLossUnit = null;
Unit takeProfitUnit = null;
var priceStep = Security?.PriceStep ?? 0m;
if (StopLossPoints > 0m && priceStep > 0m)
{
stopLossUnit = new Unit(StopLossPoints * priceStep, UnitTypes.Absolute);
}
if (TakeProfitPoints > 0m && priceStep > 0m)
{
takeProfitUnit = new Unit(TakeProfitPoints * priceStep, UnitTypes.Absolute);
}
if (stopLossUnit != null || takeProfitUnit != null)
{
StartProtection(stopLoss: stopLossUnit, takeProfit: takeProfitUnit);
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawIndicator(area, _cci);
DrawIndicator(area, _stochastic);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var maResult = _ema.Process(candle).ToNullableDecimal();
var cciResult = _cci.Process(candle).ToNullableDecimal();
var stochasticValue = (StochasticOscillatorValue)_stochastic.Process(candle);
if (maResult == null || cciResult == null)
return;
if (stochasticValue.K is not decimal stochasticK)
return;
if (!_ema.IsFormed || !_cci.IsFormed || !_stochastic.IsFormed)
return;
var maValue = maResult.Value;
var cciValue = cciResult.Value;
var lowerStochastic = 50m - StochasticLevel;
var upperStochastic = 50m + StochasticLevel;
// Buy signal: CCI crosses up through -level from oversold territory
var cciBuyXover = _previousCci != null && _previousCci.Value < -CciLevel && cciValue >= -CciLevel;
// Sell signal: CCI crosses down through +level from overbought territory
var cciSellXover = _previousCci != null && _previousCci.Value > CciLevel && cciValue <= CciLevel;
if (cciBuyXover && stochasticK < lowerStochastic)
{
CloseShortPositions();
TryEnterLong(candle);
}
else if (cciSellXover && stochasticK > upperStochastic)
{
CloseLongPositions();
TryEnterShort(candle);
}
_previousCci = cciValue;
}
private void CloseLongPositions()
{
var longVolume = Position > 0m ? Position : 0m;
if (longVolume <= 0m)
return;
// Close existing long volume before reversing into short trades.
SellMarket(longVolume);
}
private void CloseShortPositions()
{
var shortVolume = Position < 0m ? Position.Abs() : 0m;
if (shortVolume <= 0m)
return;
// Close existing short volume before opening new long trades.
BuyMarket(shortVolume);
}
private void TryEnterLong(ICandleMessage candle)
{
var volume = CalculateOrderVolume(candle.ClosePrice);
if (volume <= 0m)
return;
var currentLongVolume = Position > 0m ? Position : 0m;
if (MaxOrders > 0)
{
var maxVolume = volume * MaxOrders;
if (currentLongVolume >= maxVolume)
return;
var additionalVolume = volume.Min(maxVolume - currentLongVolume);
if (additionalVolume <= 0m)
return;
// Add new long exposure without exceeding MaxOrders limit.
BuyMarket(additionalVolume);
}
else
{
BuyMarket(volume);
}
}
private void TryEnterShort(ICandleMessage candle)
{
var volume = CalculateOrderVolume(candle.ClosePrice);
if (volume <= 0m)
return;
var currentShortVolume = Position < 0m ? Position.Abs() : 0m;
if (MaxOrders > 0)
{
var maxVolume = volume * MaxOrders;
if (currentShortVolume >= maxVolume)
return;
var additionalVolume = volume.Min(maxVolume - currentShortVolume);
if (additionalVolume <= 0m)
return;
// Add new short exposure without exceeding MaxOrders limit.
SellMarket(additionalVolume);
}
else
{
SellMarket(volume);
}
}
private decimal CalculateOrderVolume(decimal referencePrice)
{
var volume = OrderVolume;
if (RiskPercentage > 0m)
{
var portfolioValue = Portfolio?.CurrentValue ?? Portfolio?.BeginValue ?? 0m;
var riskCapital = portfolioValue * RiskPercentage / 100m;
if (riskCapital > 0m)
{
var margin = GetSecurityValue<decimal?>(Level1Fields.MarginBuy) ?? GetSecurityValue<decimal?>(Level1Fields.MarginSell) ?? 0m;
if (margin > 0m)
{
volume = riskCapital / margin;
}
else if (referencePrice > 0m)
{
volume = riskCapital / referencePrice;
}
}
}
volume = RoundVolume(volume);
var minVolume = Security?.MinVolume;
if (minVolume != null && minVolume.Value > 0m && volume < minVolume.Value)
{
volume = minVolume.Value;
}
var maxVolume = Security?.MaxVolume;
if (maxVolume != null && maxVolume.Value > 0m && volume > maxVolume.Value)
{
volume = maxVolume.Value;
}
return volume;
}
private decimal RoundVolume(decimal volume)
{
if (volume <= 0m)
return 0m;
var step = Security?.VolumeStep ?? 0m;
if (step <= 0m)
return volume;
var steps = Math.Floor(volume / step);
var rounded = steps * step;
if (rounded <= 0m)
rounded = step;
return rounded;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, UnitTypes, Unit, Level1Fields
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, CommodityChannelIndex, StochasticOscillator
class kloss_simple_strategy(Strategy):
def __init__(self):
super(kloss_simple_strategy, self).__init__()
self._order_volume = self.Param("OrderVolume", 0.1).SetDisplay("Volume", "Base order volume", "Trading")
self._ma_period = self.Param("MaPeriod", 5).SetDisplay("EMA Period", "Length of the exponential moving average", "Indicators")
self._cci_period = self.Param("CciPeriod", 10).SetDisplay("CCI Period", "Length of the commodity channel index", "Indicators")
self._cci_level = self.Param("CciLevel", 200.0).SetDisplay("CCI Level", "Distance from zero to trigger signals", "Indicators")
self._stochastic_k_period = self.Param("StochasticKPeriod", 5).SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
self._stochastic_d_period = self.Param("StochasticDPeriod", 3).SetDisplay("Stochastic %D", "Period of the %D line", "Indicators")
self._stochastic_smooth = self.Param("StochasticSmooth", 3).SetDisplay("Stochastic Smooth", "Smoothing factor for %K", "Indicators")
self._stochastic_level = self.Param("StochasticLevel", 30.0).SetDisplay("Stochastic Level", "Distance from 50 to trigger signals", "Indicators")
self._max_orders = self.Param("MaxOrders", 1).SetDisplay("Max Orders", "Maximum number of positions per direction", "Trading")
self._stop_loss_points = self.Param("StopLossPoints", 0.0).SetDisplay("Stop Loss (pts)", "Stop loss distance in points", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 0.0).SetDisplay("Take Profit (pts)", "Take profit distance in points", "Risk")
self._risk_percentage = self.Param("RiskPercentage", 10.0).SetDisplay("Risk %", "Portfolio percentage for dynamic position sizing", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Primary candle series for calculations", "General")
self._previous_cci = None
@property
def OrderVolume(self): return self._order_volume.Value
@property
def MaPeriod(self): return self._ma_period.Value
@property
def CciPeriod(self): return self._cci_period.Value
@property
def CciLevel(self): return self._cci_level.Value
@property
def StochasticKPeriod(self): return self._stochastic_k_period.Value
@property
def StochasticDPeriod(self): return self._stochastic_d_period.Value
@property
def StochasticSmooth(self): return self._stochastic_smooth.Value
@property
def StochasticLevel(self): return self._stochastic_level.Value
@property
def MaxOrders(self): return self._max_orders.Value
@property
def StopLossPoints(self): return self._stop_loss_points.Value
@property
def TakeProfitPoints(self): return self._take_profit_points.Value
@property
def RiskPercentage(self): return self._risk_percentage.Value
@property
def CandleType(self): return self._candle_type.Value
def OnStarted2(self, time):
super(kloss_simple_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.MaPeriod
self._cci = CommodityChannelIndex()
self._cci.Length = self.CciPeriod
self._stochastic = StochasticOscillator()
self._stochastic.K.Length = self.StochasticKPeriod
self._stochastic.D.Length = self.StochasticDPeriod
self.Volume = self.OrderVolume
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._ema, self._cci, self._stochastic, self.ProcessIndicators).Start()
price_step = 0.0
if self.Security is not None and self.Security.PriceStep is not None:
price_step = float(self.Security.PriceStep)
sl_unit = None
tp_unit = None
sl_pts = float(self.StopLossPoints)
tp_pts = float(self.TakeProfitPoints)
if sl_pts > 0 and price_step > 0:
sl_unit = Unit(sl_pts * price_step, UnitTypes.Absolute)
if tp_pts > 0 and price_step > 0:
tp_unit = Unit(tp_pts * price_step, UnitTypes.Absolute)
if sl_unit is not None or tp_unit is not None:
self.StartProtection(stopLoss=sl_unit, takeProfit=tp_unit)
def ProcessIndicators(self, candle, ema_value, cci_value, stoch_value):
if candle.State != CandleStates.Finished:
return
if ema_value.IsEmpty or cci_value.IsEmpty or stoch_value.IsEmpty:
return
stoch_k = stoch_value.K if hasattr(stoch_value, 'K') else None
if stoch_k is None:
return
cci_val = float(cci_value)
stoch_k_val = float(stoch_k)
lower_stoch = 50.0 - float(self.StochasticLevel)
upper_stoch = 50.0 + float(self.StochasticLevel)
cci_lev = float(self.CciLevel)
# Buy signal: CCI crosses up through -level from oversold territory
cci_buy_xover = self._previous_cci is not None and self._previous_cci < -cci_lev and cci_val >= -cci_lev
# Sell signal: CCI crosses down through +level from overbought territory
cci_sell_xover = self._previous_cci is not None and self._previous_cci > cci_lev and cci_val <= cci_lev
if cci_buy_xover and stoch_k_val < lower_stoch:
self._close_short_positions()
self._try_enter_long(candle)
elif cci_sell_xover and stoch_k_val > upper_stoch:
self._close_long_positions()
self._try_enter_short(candle)
self._previous_cci = cci_val
def _close_long_positions(self):
long_volume = float(self.Position) if self.Position > 0 else 0.0
if long_volume <= 0:
return
self.SellMarket(long_volume)
def _close_short_positions(self):
short_volume = abs(float(self.Position)) if self.Position < 0 else 0.0
if short_volume <= 0:
return
self.BuyMarket(short_volume)
def _try_enter_long(self, candle):
volume = self._calculate_order_volume(float(candle.ClosePrice))
if volume <= 0:
return
current_long = float(self.Position) if self.Position > 0 else 0.0
max_orders = int(self.MaxOrders)
if max_orders > 0:
max_vol = volume * max_orders
if current_long >= max_vol:
return
additional = min(volume, max_vol - current_long)
if additional <= 0:
return
self.BuyMarket(additional)
else:
self.BuyMarket(volume)
def _try_enter_short(self, candle):
volume = self._calculate_order_volume(float(candle.ClosePrice))
if volume <= 0:
return
current_short = abs(float(self.Position)) if self.Position < 0 else 0.0
max_orders = int(self.MaxOrders)
if max_orders > 0:
max_vol = volume * max_orders
if current_short >= max_vol:
return
additional = min(volume, max_vol - current_short)
if additional <= 0:
return
self.SellMarket(additional)
else:
self.SellMarket(volume)
def _calculate_order_volume(self, reference_price):
volume = float(self.OrderVolume)
risk_pct = float(self.RiskPercentage)
if risk_pct > 0:
portfolio_value = 0.0
if self.Portfolio is not None:
if self.Portfolio.CurrentValue is not None and float(self.Portfolio.CurrentValue) != 0:
portfolio_value = float(self.Portfolio.CurrentValue)
elif self.Portfolio.BeginValue is not None:
portfolio_value = float(self.Portfolio.BeginValue)
risk_capital = portfolio_value * risk_pct / 100.0
if risk_capital > 0:
margin = 0.0
if reference_price > 0:
volume = risk_capital / reference_price
volume = self._round_volume(volume)
if self.Security is not None:
min_vol = self.Security.MinVolume
if min_vol is not None and float(min_vol) > 0 and volume < float(min_vol):
volume = float(min_vol)
max_vol = self.Security.MaxVolume
if max_vol is not None and float(max_vol) > 0 and volume > float(max_vol):
volume = float(max_vol)
return volume
def _round_volume(self, volume):
if volume <= 0:
return 0.0
step = 0.0
if self.Security is not None and self.Security.VolumeStep is not None:
step = float(self.Security.VolumeStep)
if step <= 0:
return volume
steps = Math.Floor(volume / step)
rounded = steps * step
if rounded <= 0:
rounded = step
return rounded
def OnReseted(self):
super(kloss_simple_strategy, self).OnReseted()
self._previous_cci = None
def CreateClone(self):
return kloss_simple_strategy()