Загрузка вчерашних данных для торговли на Квике. Код старый - не факт, что работает с новым API.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Candles.Compression;
using StockSharp.Algo.History;
using StockSharp.Algo.History.Finam;
using StockSharp.Algo.History.Rts;
using StockSharp.Algo.Storages;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Logging;
namespace VgnRobot.RobotEngine
{
/// <summary>
/// Базовый класс для свечных стратегий
/// </summary>
public abstract class CandleStrategyBase : Strategy
{
public ITimeCheckProvider TimeCheckProvider { get; set; }
public IStopProvider StopProvider { get; set; }
//protected StopOrdersChecker stopOrdersChecker;
public readonly SyncObject Locker = new SyncObject();
protected CandleStrategyBase()
{
// добавление отдельного логирования стратегии
LogManager = new LogManager();
ExchangeBoard.Forts.ApplyHolidays2013();
//ExchangeBoard.Forts.ApplyHolidays2012();
}
private void InitSettings()
{
decimal minStepPrice;
MinStepPriceSetting = Settings.TryGetProperty("MinStepPrice", out minStepPrice, this)
? minStepPrice
: (decimal?)null;
}
private decimal? MinStepPriceSetting { get; set; }
/// <summary>
/// Настройки стратегии
/// (для доступа к общим настройкам из родителького класса)
/// </summary>
public System.Configuration.SettingsBase Settings { get; set; }
public CandleSeries Series { get; private set; }
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; }
/// <summary>
/// Движок создания заявок
/// </summary>
public IOrderHelper OrderHelper { get; set; }
public Unit PriceDelta { get; set; }
public Type CandleFrameType { get; set; }
/// <summary>
/// Текстовая обёртка CandleFrameType. Работает в обе стороны.
/// </summary>
public string CandleFrame
{
get
{
return CandleFrameType.Name;
}
set
{
CandleFrameType = Type.GetType(string.Format("StockSharp.Algo.Candles.{0}, StockSharp.Algo", value), true);
}
}
public object FrameSize { get; set; }
DateTime _strategyStartTime;
//public IList<Candle> GetHistoryTrades(DateTime beginDate, DateTime endDate)
//{
// string uri = GetInstrumentUrl(beginDate, endDate);
// WebRequest request = WebRequest.Create(uri);
// using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream()))
// {
// IList<Candle> result = new CandleReader().ReadCandles(sr);
// return result;
// }
//}
#region AddHistoryCandles Загрузка исторических свечек за прошлый день
/// <summary>
/// Класс-затычка для хранилища истории
/// </summary>
private class DumbSecurityStorage : ISecurityStorage
{
private Security _security;
public Security LoadBy(string fieldName, object fieldValue)
{
return _security;
}
public void Save(Security security)
{
_security = security;
}
}
Trade ParseTrade(string finamString)
{
var culture = new CultureInfo(System.Threading.Thread.CurrentThread.CurrentCulture.Name) { NumberFormat = { NumberDecimalSeparator = "." } };
var parts = finamString.Split(',');
var trade = new Trade
{
Time = DateTime.ParseExact(parts[0] + parts[1], "yyyyMMddHHmmss", culture),
Price = decimal.Parse(parts[2], culture),
Volume = decimal.Parse(parts[3], culture),
Id = long.Parse(parts[4]),
Security = Security
};
return trade;
//{
// //<DATE>,<TIME>,<LAST>,<VOL>,<ID>
// Time = DateTime.ParseExact(parts[0] + parts[1], "yyyyMMddHHmmss", CultureInfo.InvariantCulture),
// Price = decimal.Parse(parts[2], CultureInfo.CurrentCulture),
// Volume = decimal.Parse(parts[3], CultureInfo.CurrentCulture),
// Id = long.Parse(parts[4]),
// Security = Security,
//};
}
/// <summary>
/// Получение сделок из файла, загруженного вручную с Финама
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
IEnumerable<Trade> GetTradesFromTxt(string filePath)
{
var tradeHistory = new List<Trade>();
if (!File.Exists(filePath))
{
return tradeHistory;
}
var history = File.ReadAllLines(filePath).Select(ParseTrade);
return history;
}
/// <summary>
/// Получение сделок с сайта Финама
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
IEnumerable<Trade> GetTradesFromFinam(DateTime date)
{
var storage = new DumbSecurityStorage();
storage.Save(Security);
var source = new FinamHistorySource(storage) { DumpFolder = GetTempPath() };
long finamMarketId;
Settings.TryGetProperty("HistoryFinamMarketId", out finamMarketId); //14
long finamSecurityId;
Settings.TryGetProperty("HistoryFinamSecurityId", out finamSecurityId); //82813;
Security.ExtensionInfo["FinamMarketId"] = finamMarketId;
Security.ExtensionInfo["FinamSecurityId"] = finamSecurityId;
return source.GetTrades(Security, date);
}
/// <summary>
/// Получение сделок с FTP RTS
/// </summary>
/// <param name="date"></param>
/// <param name="loadEvening"></param>
/// <returns></returns>
IEnumerable<Trade> GetTradesFromRts(DateTime date, bool loadEvening = false)
{
var storage = new DumbSecurityStorage();
storage.Save(Security);
var source = new RtsHistorySource(storage) { DumpFolder = GetTempPath(), LoadEveningSession = loadEvening};
var trades = new Dictionary<Security, IList<Trade>>();
source.LoadTrades(date, trades);
if (!trades.ContainsKey(Security))
throw new ApplicationException("Не получены данные за прошлый день");
return trades[Security];
}
/// <summary>
/// Папка временных файлов для всех способов загрузки
/// </summary>
/// <returns></returns>
private string GetTempPath()
{
var tempPath = Path.Combine(@"E:\TradeStorage\", "TemporaryFiles");
if (!Directory.Exists(tempPath))
Directory.CreateDirectory(tempPath);
return tempPath;
}
/// <summary>
/// Получение даты прошлого дня с учётов выходных и праздников
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
DateTime GetLastDateBefore(DateTime date)
{
do
{
date = date.AddDays(-1);
if (ExchangeBoard.Forts.WorkingTime.IsTradeDate(date, true))
return date;
} while (true);
}
protected TradeStorageCandleBuilderSource HistorySource;
/// <summary>
/// Добавление в список свечей данных из хранилища
/// </summary>
private void AddHistoryCandles()
{
try
{
this.AddDebugLog("Загрузка сделок за прошлый день");
var storageRegistry = new StorageRegistry();
string tradeStoragePath;
Settings.TryGetProperty("HistoryTradeStoragePath", out tradeStoragePath);
((LocalMarketDataDrive) storageRegistry.DefaultDrive).Path = tradeStoragePath;
var storage = storageRegistry.GetTradeStorage(Security);
var thisDate = LoggingHelper.Now.Date;
var lastDate = GetLastDateBefore(thisDate);
string historySource;
Settings.TryGetProperty("HistorySource", out historySource);
switch (historySource)
{
case "RTS":
if (!storage.Dates.Contains(thisDate))
{
this.AddDebugLog("Загрузка сделок с RTS");
var trades = GetTradesFromRts(lastDate, true);
//
storage.Save(trades.Where(t => t.Time > lastDate && t.Price > 0));
//пропуск закачки более старых (нужна закачка целыми днями) и игнорирование ошибки данных
// на ftp ртс данные выложены по сессиям с 19.00 до 19.00
//trades = GetTradesFromRts(thisDate, true);
//storage.Save(trades.Where(t => t.Time < thisDate && t.Price > 0));
//пропуск закачки сегодняшних и игнорирование ошибки данных
//todo: обработка ошибок закачки (стратегия останавливается)
}
break;
case "Finam":
if (!storage.Dates.Contains(lastDate))
{
this.AddDebugLog("Загрузка сделок с Финама");
var trades = GetTradesFromFinam(lastDate);
storage.Save(trades);
}
break;
default:
if (!storage.Dates.Contains(lastDate))
{
this.AddDebugLog("Загрузка сделок из файла");
//var trades = GetTradesFromTxt(@"E:\Downloads\RIH3_130312_130312.txt");
var trades = GetTradesFromTxt(string.Format(
"{0}{1}_{2}_{2}.txt",
tradeStoragePath, Security.Code, lastDate));
storage.Save(trades);
}
break;
}
// берём только сделки раньше тех, что есть в таблице
var lastTimeSource = Trader.Trades.Select(t => t.Time).ToArray();
var lastTime = lastTimeSource.Any() ? lastTimeSource.Min() : thisDate;
HistorySource = new TradeStorageCandleBuilderSource
{
StorageRegistry = storageRegistry,
Filter = trade => trade.Time > lastDate && trade.Time < lastTime,
};
TraderBuilder.CandleManager.Sources.OfType<TickCandleBuilder>().Single().Sources.Add(HistorySource);
}
catch (Exception ex)
{
this.AddErrorLog(ex);
}
}
/// <summary>
/// Очистка памяти от истории за прошлый день.
/// </summary>
protected void DeleteHistory()
{
if (HistorySource == null)
return;
var source = HistorySource;
HistorySource = null;
TraderBuilder.CandleManager.Sources.OfType<TickCandleBuilder>().Single().Sources.Remove(source);
source.Dispose();
GC.Collect(2, GCCollectionMode.Forced);
}
#endregion
//readonly object _candleProcessLocker = new object();
protected override void OnStarted()
{
try
{
InitSettings();
Trader = TraderBuilder.BuildTrader();
OrderHelper = TraderBuilder.OrderHelper;
if (Trader == null)
throw new ApplicationException(string.Format("Отсутствует трейдер {0}.", TraderBuilder.Title));
_strategyStartTime = Trader.GetMarketTime(ExchangeBoard.Forts.Exchange);
Portfolio = PortfolioSelector.GetPortfolio(Trader);
if (Portfolio == null)
throw new ApplicationException(string.Format("Отсутствует портфель {0}.", PortfolioSelector.Title));
Security = SecuritySelector.GetSecurity(Trader);
if (Security == null)
throw new ApplicationException(string.Format("Отсутствует инструмент {0}.", SecuritySelector.Title));
Trader.RegisterSecurity(Security);
Trader.RegisterMarketDepth(Security);
if (VolumeSizer != null)
{
Volume = VolumeSizer.GetVolume(Portfolio, Security);
this.AddInfoLog("Объем: {0}", Volume);
}
AddHistoryCandles();
Series = new CandleSeries(
CandleFrameType, Security, FrameSize) { WorkingTime = ExchangeBoard.Forts.WorkingTime };
// Если Security - это WeightedIndexSecurity, то _series не заполняется
Series
.WhenCandlesFinished()
.Do(CandleFinishedCall)
.Sync(Locker)
.Apply(this);
TraderBuilder.CandleManager.Start(Series);
base.OnStarted();
}
catch (Exception ex)
{
this.AddErrorLog(ex);
Stop();
}
}
public event Action<Candle> CandleFinished;
/// <summary>
/// Свеча сформирована
/// </summary>
/// <param name="candle"></param>
private void CandleFinishedCall(Candle candle)
{
try
{
//todo: померять время
ProcessCandle(candle);
if (CandleFinished != null)
CandleFinished(candle); // стратегия уже должна обработать все индикаторы
}
catch (Exception ex)
{
this.AddErrorLog(ex);
}
}
protected override void OnStopping()
{
//todo: закрытие позиций
if (Security != null)
Trader.UnRegisterMarketDepth(Security);
}
protected override void OnStopped()
{
base.OnStopped();
}
protected virtual void ProcessCandle(Candle candle)
{}
private decimal _minStepPrice;
/// <summary>
/// Получение минимального лота из настроек, потому что соответствующее свойство Security иногда глючит
/// </summary>
public decimal MinStepPrice
{
get
{
if (_minStepPrice == 0)
{
_minStepPrice = MinStepPriceSetting != null
? MinStepPriceSetting.Value
: Security.MinStepPrice;
}
return _minStepPrice;
}
}
/// <summary>
/// Получение лучшей цены из стакана (цены изнутри спреда).
/// </summary>
/// <param name="direction"></param>
/// <returns></returns>
public decimal GetBestInnerPrice(OrderDirections direction)
{
var price = direction == OrderDirections.Buy
? (Security.BestBid != null ? Security.BestBid.Price + MinStepPrice : 0)
: (Security.BestBid != null ? Security.BestAsk.Price - MinStepPrice : 0);
return price;
}
/// <summary>
/// Создание и регистрация лимитных заявок в рамках стратегии
/// </summary>
/// <param name="direction"></param>
/// <param name="volume"></param>
protected void SendStrategyOrders(OrderDirections direction, decimal volume)
{
try
{
CancelActiveOrders(o => o.Type == OrderTypes.Limit);
//while (Position != Portfolio.GetPosition() || Orders.Any(o => o.State == OrderStates.Active))
//{
// this.AddWarningLog("* Разбег позиций перед сделкой: PositionManager {0} Trader {1}",
// Position, Portfolio.GetPosition());
// Thread.Sleep(100);
//}
var price = GetBestInnerPrice(direction);
var order = OrderHelper.CreateLimitOrder(
Security,
direction,
volume,
price,
null);
var newTradesRule = order.WhenNewTrades();
newTradesRule.Do(OnStrategyTrades).Sync(Locker).Apply();
TimeCheckProvider.CurrentWaiting = 0;
this.AddInfoLog(
"* Новая заявка: Объём {0} Цена {1} Направление {2}",
volume, price, direction);
RegisterOrder(order);
}
catch (Exception ex)
{
this.AddErrorLog("* Ошибка регистрации заявки: {0}", ex);
}
}
/// <summary>
/// Обработка получения новых сделок
/// </summary>
/// <param name="trades"></param>
private void OnStrategyTrades(IEnumerable<MyTrade> trades)
{
//todo: логирование новых сделок здесь?
// возможно стоит создать некий буфер, чтобы логировать и здесь и по событию получения сделок (здесь, наверное, быстрее)
StopProvider.AddStops(trades);
}
public LogManager LogManager { get; set; }
public IChartPresenter ChartPresenter { get; set; }
}
}