Создание роботов с помощью S#. Часть 2. Базовый класс для всех стратегий
Все наши стратегии будем наследовать от класса StandardStrategy. Зачем он нужен? Дело в том, что все стратегии часто должны уметь делать одни и теже вещи: брать откуда-то торговую систему, портфель, инструмент, исторические свечки, хранить состояние, устанавливать размер позиции. Чтобы не решать каждый раз эти проблемы, можно сделать так:
Code
public abstract class StandardStrategy : Strategy
{
public virtual string Description { get { return Name; } }
public ITraderBuilder TraderBuilder { get; set; }
public IPortfolioSelector PortfolioSelector { get; set; }
public ISecuritySelector SecuritySelector { get; set; }
public IVolumeSizer VolumeSizer { get; set; }
public IHistoryCandleProvider HistoryCandleProvider { get; set; }
public ISettingsProvider SettingsProvider { get; set; }
public Unit PriceDelta { get; set; }
protected StandardStrategy()
{
PriceDelta = 0.1.Percents();
}
}
ITraderBuilder будет уметь волшебным образом создавать торговую систему:
Code
public interface ITraderBuilder : IDisposable
{
string Title { get; }
ITrader Trader { get; }
ICandleManager CandleManager { get; }
void RunTerminal();
ITrader BuildTrader();
event Action IsConnectedChanged;
}
IPortfolioSelector будет находить портфель:
Code
public interface IPortfolioSelector
{
string Title { get; }
Portfolio GetPortfolio(ITrader trader);
}
ISecuritySelector находить инструмент:
Code
public interface ISecuritySelector
{
string Title { get; }
Security GetSecurity(ITrader trader);
}
IVolumeSizer расчитывать размер позиции:
Code
public interface IVolumeSizer
{
int GetVolume(Portfolio portfolio, Security security);
}
IHistoryCandleProvider получать исторические свечки:
Code
public interface IHistoryCandleProvider
{
List<TimeFrameCandle> GetHistoryCandles(DateTime beginDate, DateTime endDate);
}
ISettingsProvider хранить состояние стратегии:
Code
public interface ISettingsProvider
{
string ReadSetting(string name);
void WriteSetting(string name, string value);
}
Представим, что у нас уже есть реалзации всех этих интерфесов (этим мы займемся позже). Как будет выглядить конкретная стратегия?
Code
public class StrikeStrategy : StandardStrategy
{
public override string Description
{
get { return "Стратегия для ловли ударного дня"; }
}
CandleToken candleToken;
DateTime strategyStartTime;
public TimeSpan TimeFrame { get; set; }
public StrikeStrategy()
{
TimeFrame = TimeSpan.FromMinutes(5);
}
protected override void OnStarting()
{
try
{
Trader = TraderBuilder.BuildTrader();
if (Trader == null)
throw new ApplicationException(string.Format("Отсутствует трейдер {0}.", TraderBuilder.Title));
strategyStartTime = Trader.MarketTime;
Portfolio = PortfolioSelector.GetPortfolio(Trader);
if (Portfolio == null)
throw new ApplicationException(string.Format("Отсутствует портфель {0}.", PortfolioSelector.Title));
Security = SecuritySelector.GetSecurity(Trader);
if (this.Security == null)
throw new ApplicationException(string.Format("Отсутствует инструмент {0}.", SecuritySelector.Title));
Trader.RegisterQuotes(Security);
if (VolumeSizer != null)
{
Volume = VolumeSizer.GetVolume(Portfolio, Security);
this.AddInfoLog("Объем: {0}", Volume);
}
if (TraderBuilder.CandleManager.IsTimeFrameCandlesRegistered(Security, TimeFrame))
{
candleToken = TraderBuilder.CandleManager.GetToken(typeof(TimeFrameCandle), Security, TimeFrame);
}
else
{
candleToken = TraderBuilder.CandleManager.RegisterTimeFrameCandles(Security, TimeFrame);
}
this.When(candleToken.CandlesFinished())
.Do<IEnumerable<Candle>>(OnCandlesFinished);
}
catch (ApplicationException ex)
{
this.AddErrorLog(ex.Message);
}
base.OnStarting();
}
void OnCandlesFinished(IEnumerable<Candle> newCandles)
{
foreach (Candle candle in newCandles)
{
OnCandleFinished(candle);
}
}
void OnCandleFinished(Candle candle)
{
DateTime currentDate = candle.Time;
if (candle.Time < strategyStartTime)
return;
...
}
}
Автор статьи — Вадим Чижов