動的契約サイズによる月次購入戦略
口座資産の固定パーセンテージを使用して、毎月選択した日に動的な数の契約を購入します。ドローダウンは参考情報として記録されます。
詳細
- エントリー条件: 時刻 >= StartDate かつ 月の日付 = BuyDay
- ロング/ショート: ロングのみ
- エグジット条件: なし
- ストップ: なし
- デフォルト値:
CandleType= 1日StartDate= 2010-01-01PercentOfEquity= 0.03BuyDay= 1
- フィルター:
- カテゴリ: ドルコスト平均法
- 方向: ロング
- インジケーター: いいえ
- ストップ: いいえ
- 複雑さ: 初心者
- 時間軸: 長期
- 季節性: 月次
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 低
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Monthly purchase strategy with dynamic contract size.
/// From StartDate on, buys on the candle whose day of month equals BuyDay. The size is PercentOfEquity of the
/// current portfolio value divided by the close price. Positions are never closed; the equity drawdown is only tracked.
/// </summary>
public class MonthlyPurchaseWithDynamicContractSizeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DateTimeOffset> _startDate;
private readonly StrategyParam<decimal> _percentOfEquity;
private readonly StrategyParam<int> _buyDay;
private DateTime? _lastBuyDate;
private decimal _peakEquity;
private decimal _maxDrawdown;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Purchases are allowed from this date on.
/// </summary>
public DateTimeOffset StartDate
{
get => _startDate.Value;
set => _startDate.Value = value;
}
/// <summary>
/// Fraction of equity spent on each purchase.
/// </summary>
public decimal PercentOfEquity
{
get => _percentOfEquity.Value;
set => _percentOfEquity.Value = value;
}
/// <summary>
/// Day of the month to buy.
/// </summary>
public int BuyDay
{
get => _buyDay.Value;
set => _buyDay.Value = value;
}
/// <summary>
/// Largest equity drawdown seen so far, for information only.
/// </summary>
public decimal MaxDrawdown => _maxDrawdown;
/// <summary>
/// Constructor.
/// </summary>
public MonthlyPurchaseWithDynamicContractSizeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_startDate = Param(nameof(StartDate), new DateTimeOffset(2010, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("Start Date", "Purchases are allowed from this date on", "General");
_percentOfEquity = Param(nameof(PercentOfEquity), 0.03m)
.SetGreaterThanZero()
.SetDisplay("Percent of Equity", "Fraction of equity spent on each purchase", "Trading");
_buyDay = Param(nameof(BuyDay), 1)
.SetRange(1, 31)
.SetDisplay("Buy Day", "Day of the month to buy", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastBuyDate = null;
_peakEquity = 0m;
_maxDrawdown = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_lastBuyDate = null;
_peakEquity = 0m;
_maxDrawdown = 0m;
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;
var equity = Portfolio?.CurrentValue ?? 0m;
if (equity > _peakEquity)
_peakEquity = equity;
if (_peakEquity > 0m)
_maxDrawdown = Math.Max(_maxDrawdown, (_peakEquity - equity) / _peakEquity);
if (!IsFormedAndOnlineAndAllowTrading())
return;
var openTime = candle.OpenTime;
if (openTime < StartDate.UtcDateTime || openTime.Day != BuyDay || _lastBuyDate == openTime.Date)
return;
var close = candle.ClosePrice;
if (close <= 0m || equity <= 0m)
return;
var step = Security?.VolumeStep ?? 1m;
if (step <= 0m)
step = 1m;
var volume = Math.Floor(equity * PercentOfEquity / close / step) * step;
if (volume <= 0m)
return;
BuyMarket(volume);
_lastBuyDate = openTime.Date;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, DateTimeOffset, Decimal, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class monthly_purchase_with_dynamic_contract_size_strategy(Strategy):
"""
Monthly purchase strategy with dynamic contract size.
From StartDate on, buys on the candle whose day of month equals BuyDay. The size is PercentOfEquity of the
current portfolio value divided by the close price. Positions are never closed; the equity drawdown is only tracked.
"""
def __init__(self):
super(monthly_purchase_with_dynamic_contract_size_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._start_date = self.Param("StartDate", DateTimeOffset(2010, 1, 1, 0, 0, 0, TimeSpan.Zero)).SetDisplay("Start Date", "Purchases are allowed from this date on", "General")
self._percent_of_equity = self.Param("PercentOfEquity", 0.03).SetGreaterThanZero().SetDisplay("Percent of Equity", "Fraction of equity spent on each purchase", "Trading")
self._buy_day = self.Param("BuyDay", 1).SetRange(1, 31).SetDisplay("Buy Day", "Day of the month to buy", "Trading")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
@property
def max_drawdown(self):
return self._max_drawdown
def _reset_state(self):
self._last_buy_date = None
self._peak_equity = Decimal(0)
self._max_drawdown = Decimal(0)
def OnReseted(self):
super(monthly_purchase_with_dynamic_contract_size_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(monthly_purchase_with_dynamic_contract_size_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
equity = Decimal(0)
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
equity = self.Portfolio.CurrentValue
if equity > self._peak_equity:
self._peak_equity = equity
if self._peak_equity > 0:
self._max_drawdown = max(self._max_drawdown, (self._peak_equity - equity) / self._peak_equity)
if not self.IsFormedAndOnlineAndAllowTrading():
return
open_time = candle.OpenTime
if open_time < self._start_date.Value.UtcDateTime or open_time.Day != self._buy_day.Value or self._last_buy_date == open_time.Date:
return
close = candle.ClosePrice
if close <= 0 or equity <= 0:
return
step = Decimal(1)
if self.Security is not None and self.Security.VolumeStep is not None and self.Security.VolumeStep > 0:
step = self.Security.VolumeStep
volume = Math.Floor(equity * Decimal(self._percent_of_equity.Value) / close / step) * step
if volume <= 0:
return
self.BuyMarket(volume)
self._last_buy_date = open_time.Date
def CreateClone(self):
return monthly_purchase_with_dynamic_contract_size_strategy()