Moadip
|
Date: 1/15/2013
Если хотите получить ответ на вопрос, пишите максимум информации. Показывайте код, где и как вы делает сохранение, чтобы не приходилось догадываться. Телепаты в отпуске.[smile]
UPD: Создавайте темы в соответствующих разделах. К разделу Обучение программированию роботов ваш вопрос отношения не имеет.
|
|
|
|
|
developer_29
|
Date: 1/15/2013
|
|
|
|
Moadip Показывайте код, где и как вы делает сохранение, чтобы не приходилось догадываться. Телепаты в отпуске.[smile]
Code
namespace SampleSMA
{
using System;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Indicators.Trend;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
class SmaStrategy : TimeFrameStrategy
{
private readonly CandleManager _candleManager;
private bool _isShortLessThenLong;
private DateTime _nextTime;
public SmaStrategy(CandleManager candleManager, SimpleMovingAverage longSma, SimpleMovingAverage shortSma, TimeSpan timeFrame)
: base(timeFrame)
{
_candleManager = candleManager;
LongSma = longSma;
ShortSma = shortSma;
}
public SimpleMovingAverage LongSma { get; private set; }
public SimpleMovingAverage ShortSma { get; private set; }
protected override void OnStarting()
{
// запоминаем текущее положение относительно друг друга
_isShortLessThenLong = ShortSma.LastValue < LongSma.LastValue;
// вычисляем время окончания текущей пятиминутки
_nextTime = TimeFrame.GetCandleBounds(Trader).Max;
base.OnStarting();
}
protected override ProcessResults OnProcess()
{
// если наша стратегия в процессе остановки
if (ProcessState == ProcessStates.Stopping)
{
// отменяем активные заявки
CancelActiveOrders();
// так как все активные заявки гарантированно были отменены, то возвращаем ProcessResults.Stop
return ProcessResults.Stop;
}
// событие обработки торговой стратегии вызвалось впервый раз, что раньше, чем окончания текущей 5-минутки.
if (Trader.MarketTime < _nextTime)
{
// возвращаем ProcessResults.Continue, так как наш алгоритм еще не закончил свою работу, а просто ожидает следующего вызова.
return ProcessResults.Continue;
}
// получаем сформированную свечку
var candle = _candleManager.GetTimeFrameCandle(Security, TimeFrame, _nextTime - TimeFrame);
// если свечки не существует (не было ни одной сделке в тайм-фрейме), то ждем окончания следующей свечки.
if (candle == null)
{
// если прошло больше 10 секунд с момента окончания свечки, а она так и не появилась,
// значит сделок в прошедшей 5-минутке не было, и переходим на следующую свечку
if ((Trader.MarketTime - _nextTime) > TimeSpan.FromSeconds(10))
_nextTime = TimeFrame.GetCandleBounds(Trader.MarketTime).Max;
return ProcessResults.Continue;
}
_nextTime += TimeFrame;
// вычисляем новое положение относительно друг друга
var isShortLessThenLong = ShortSma.LastValue < LongSma.LastValue;
// Вот здесь и происходить сохранение
System.IO.File.AppendAllText(@"D:\temp.txt", Security.BestAsk + "\t\t" + Security.BestBid + "\r\n");
return ProcessResults.Continue;
}
}
}
|
|
Thanks:
|
|
|
|
|
Alexander
|
Date: 1/15/2013
C чего взяли что задержка? Как часто вызывается OnProcess?
|
|
|
|
|
developer_29
|
Date: 1/15/2013
|
|
|
|
Quote:C чего взяли что задержка? Как часто вызывается OnProcess? Я и пытаюсь выяснить, в связи с чем она, записи же происходят раз в пять минут, как и положено стртегии на 5-минутных свечах, только вот происходят они с запаздыванием в полторы минуты почему-то. А управление в свою очередь производится из этого кода, я оттуда удалил ни на что не влияющую прорисовку свечей. Код весь взят из SampleSMA, который находится у Вас на сайте stocksharp.com Code
namespace SampleSMA
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using MessageBox = System.Windows.MessageBox;
using AmCharts.Windows.Stock;
using Ecng.Collections;
using Ecng.Common;
using Ecng.Xaml;
using Ecng.ComponentModel;
using StockSharp.Algo.Logging;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Reporting;
using StockSharp.Algo.Strategies;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Indicators.Trend;
using StockSharp.BusinessEntities;
using StockSharp.Quik;
using StockSharp.Xaml;
public partial class MainWindow
{
private readonly TimeSpan _timeFrame = TimeSpan.FromMinutes(5);
private QuikTrader _trader;
private SmaStrategy _strategy;
private bool _isDdeStarted;
private DateTime _lastCandleTime;
private bool _isTodaySmaDrawn;
private CandleManager _candleManager;
private readonly ICollection<CustomChartIndicator> _longSmaGraph;
private readonly ICollection<CustomChartIndicator> _shortSmaGraph;
private Security _lkoh;
public MainWindow()
{
InitializeComponent();
// изменяет текущий формат, чтобы нецелое числа интерпритировалось как разделенное точкой.
var cci = new CultureInfo(Thread.CurrentThread.CurrentCulture.Name) { NumberFormat = { NumberDecimalSeparator = "." } };
Thread.CurrentThread.CurrentCulture = cci;
_longSmaGraph = _chart.CreateTrend("Длинная", GraphType.Line);
_shortSmaGraph = _chart.CreateTrend("Короткая", GraphType.Line);
}
private void _orders_OrderSelected(object sender, EventArgs e)
{
CancelOrders.IsEnabled = !_orders.SelectedOrders.IsEmpty();
}
protected override void OnClosing(CancelEventArgs e)
{
if (_trader != null)
{
if (_isDdeStarted)
StopDde();
_trader.Dispose();
}
base.OnClosing(e);
}
private void FindPath_Click(object sender, RoutedEventArgs e)
{
var dlg = new FolderBrowserDialog();
if (!Path.Text.IsEmpty())
dlg.SelectedPath = Path.Text;
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Path.Text = dlg.SelectedPath;
}
}
private void Connect_Click(object sender, RoutedEventArgs e)
{
if (_trader == null || !_trader.IsConnected)
{
if (_trader == null)
{
if (Path.Text.IsEmpty())
{
//MessageBox.Show(this, "Путь к Quik не выбран.");
//return;
Path.Text = @"c:\BCS_Work\QUIK";
}
// создаем шлюз
_trader = new QuikTrader(Path.Text);
Portfolios.Trader = _trader;
_trader.Connected += () =>
{
_candleManager = new CandleManager(_trader);
foreach (var builder in _candleManager.Sources.OfType<CandleBuilder>())
{
builder.IsSyncRegister = true;
}
_trader.NewSecurities += securities => this.GuiAsync(() =>
{
// находим нужную бумагу
var lkoh = securities.FirstOrDefault(s => s.Code == "RIH3");
if (lkoh != null)
{
_lkoh = lkoh;
this.GuiAsync(() =>
{
Start.IsEnabled = true;
});
}
});
_trader.NewMyTrades += trades => this.GuiAsync(() =>
{
if (_strategy != null)
{
// найти те сделки, которые совершила стратегия скользящей средней
trades = trades.Where(t => _strategy.Orders.Any(o => o == t.Order));
_trades.Trades.AddRange(trades);
}
});
_candleManager.CandlesStarted += (token, candles) =>
{
};
//_trader.ProcessDataError += ex => this.Sync(() => MessageBox.Show(this, ex.ToString()));
_trader.ConnectionError += ex =>
{
if (ex != null)
this.GuiAsync(() => MessageBox.Show(this, ex.ToString()));
};
this.GuiAsync(() =>
{
ConnectBtn.IsEnabled = false;
ExportDde.IsEnabled = true;
Report.IsEnabled = true;
});
};
}
_trader.Connect();
}
else
_trader.Disconnect();
}
private void OnNewOrder(Order order)
{
_orders.Orders.Add(order);
this.GuiAsync(() => _chart.Orders.Add(order));
}
private void OnLog(LogMessage message)
{
// если стратегия вывела не просто сообщение, то вывести на экран.
if (message.Type != ErrorTypes.None)
this.GuiAsync(() => MessageBox.Show(this, message.Message));
}
private void DrawSma1()
{
// нас не интересует текущая свечка, так как она еще не сформировалась
// и из нее нельзя брать цену закрытия
// вычисляем временные отрезки текущей свечки
var bounds = _timeFrame.GetCandleBounds(_trader);
// если появились новые полностью сформированные свечки
if ((_lastCandleTime + _timeFrame) < bounds.Min)
{
// отступ с конца интервала, чтобы не захватить текущую свечку.
var endOffset = TimeSpan.FromSeconds(1);
bounds = new Range<DateTime>(_lastCandleTime + _timeFrame, bounds.Min - endOffset);
// получаем эти свечки
var candles = _candleManager.GetTimeFrameCandles(_strategy.Security, _timeFrame, bounds);
if (!candles.IsEmpty())
{
foreach (var candle in candles)
{
// добавляем новую свечку
_strategy.LongSma.Process((DecimalIndicatorValue)candle.ClosePrice);
_strategy.ShortSma.Process((DecimalIndicatorValue)candle.ClosePrice);
}
// получаем время самой последней свечки и запоминаем его как новое начало
_lastCandleTime = candles.Max(c => c.Time);
}
}
}
private void OnStrategyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.GuiAsync(() =>
{
Status.Content = _strategy.ProcessState;
PnL.Content = _strategy.PnLManager.PnL;
Slippage.Content = _strategy.SlippageManager.Slippage;
Position.Content = _strategy.PositionManager.Position;
Latency.Content = _strategy.LatencyManager.Latency;
});
}
private void StartDde()
{
_trader.StartExport();
_isDdeStarted = true;
}
private void StopDde()
{
_trader.StopExport();
_isDdeStarted = false;
}
private void ExportDde_Click(object sender, RoutedEventArgs e)
{
if (_isDdeStarted)
StopDde();
else
StartDde();
}
private void CancelOrders_Click(object sender, RoutedEventArgs e)
{
_orders.SelectedOrders.ForEach(_trader.CancelOrder);
}
private void Start_Click(object sender, RoutedEventArgs e)
{
if (_strategy == null)
{
if (Portfolios.SelectedPortfolio == null)
{
MessageBox.Show(this, "Портфель не выбран.");
return;
}
var candles = File.ReadAllLines("LKOH_history.txt").Select(line =>
{
var parts = line.Split(',');
var time = DateTime.ParseExact(parts[0] + parts[1], "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
return new TimeFrameCandle
{
OpenPrice = parts[2].To<decimal>(),
HighPrice = parts[3].To<decimal>(),
LowPrice = parts[4].To<decimal>(),
ClosePrice = parts[5].To<decimal>(),
TimeFrame = _timeFrame,
Time = time,
TotalVolume = parts[6].To<decimal>(),
Security = _lkoh,
};
});
// создаем торговую стратегию, скользящие средние на 80 5-минуток и 10 5-минуток
_strategy = new SmaStrategy(_candleManager, new SimpleMovingAverage { Length = 80 }, new SimpleMovingAverage { Length = 10 }, _timeFrame)
{
Volume = 1,
Security = _lkoh,
Portfolio = Portfolios.SelectedPortfolio,
Trader = _trader,
};
_strategy.Log += OnLog;
_strategy.NewOrder += OnNewOrder;
_strategy.PropertyChanged += OnStrategyPropertyChanged;
var index = 0;
// начинаем вычислять скользящие средние
foreach (var candle in candles)
{
_strategy.LongSma.Process((DecimalIndicatorValue)candle.ClosePrice);
_strategy.ShortSma.Process((DecimalIndicatorValue)candle.ClosePrice);
index++;
_lastCandleTime = candle.Time;
}
// регистрируем наш тайм-фрейм
_candleManager.RegisterTimeFrameCandles(_lkoh, _timeFrame);
// вычисляем временные отрезки текущей свечки
var bounds = _timeFrame.GetCandleBounds(_trader);
candles = _candleManager.GetTimeFrameCandles(_strategy.Security, _timeFrame, new Range<DateTime>(_lastCandleTime + _timeFrame, bounds.Min));
foreach (var candle in candles)
{
_strategy.LongSma.Process((DecimalIndicatorValue)candle.ClosePrice);
_strategy.ShortSma.Process((DecimalIndicatorValue)candle.ClosePrice);
_lastCandleTime = candle.Time;
}
_isTodaySmaDrawn = true;
Report.IsEnabled = true;
}
if (_strategy.ProcessState == ProcessStates.Stopped)
{
// запускаем процесс получения стакана, необходимый для работы алгоритма котирования
_trader.RegisterQuotes(_strategy.Security);
_strategy.Start();
Start.Content = "Стоп";
}
else
{
_trader.UnRegisterQuotes(_strategy.Security);
_strategy.Stop();
Start.Content = "Старт";
}
}
private void Report_Click(object sender, RoutedEventArgs e)
{
// сгерерировать отчет по прошедшему тестированию
new ExcelStrategyReport(_strategy, "sma.xls").Generate();
// открыть отчет
Process.Start("sma.xls");
}
}
}
|
|
Thanks:
|
|
|
|
|
Alexander
|
Date: 1/15/2013
developer_29 Quote:C чего взяли что задержка? Как часто вызывается OnProcess? Я и пытаюсь выяснить, в связи с чем она, записи же происходят раз в пять минут, как и положено стртегии на 5-минутных свечах, только вот происходят они с запаздыванием в полторы минуты почему-то. А управление в свою очередь производится из этого кода, я оттуда удалил ни на что не влияющую прорисовку свечей. Код весь взят из SampleSMA, который находится у Вас на сайте stocksharp.com Что именно происходит с запаздыванием в 1.5 минуты? У вас код Code
System.IO.File.AppendAllText(@"D:\temp.txt", Security.BestAsk + "\t\t" + Security.BestBid + "\r\n");
будет выполняться не чаще чем каждые 5 минут - когда вызывается OnProcess. Может выполняться и реже - когда сработал один из return до этого года. Т.е. он может выполняться и раз в 15 минут, и раз в 5 минут - всё зависит от условий до вышего вывода. Что вы ожидаете от этого кода?
|
|
|
|
|
developer_29
|
Date: 1/15/2013
Alexander Mukhanchikov У вас код Code
System.IO.File.AppendAllText(@"D:\temp.txt", Security.BestAsk + "\t\t" + Security.BestBid + "\r\n");
будет выполняться не чаще чем каждые 5 минут - когда вызывается OnProcess. Может выполняться и реже - когда сработал один из return до этого года. Т.е. он может выполняться и раз в 15 минут, и раз в 5 минут - всё зависит от условий до вышего вывода. Что вы ожидаете от этого кода? Код выполняется не в 11-00, 11-05, 11-10 и т.д., а в 11-01, 11-06, 11-11 соответственно. Т.е., срабатывает примерно на минуты полторы позже, чем положено, причём -- каждый раз на полторы минуты позже, чем положено. Время на моём компьютере с точностью до нескольких секунд совпадает со временем Quik. Я ожидаю от кода, что запись будет происходить в 11-00, 11-05, 11-10 и т.д, а не в 11-01, 11-06, 11-11, как у меня сейчас. Так Вы тот самый основатель, если верить нику! Слушал Ваше интервью на youtube ещё до подробного знакомства с S#.
|
|
Thanks:
|
|
|
|
|
Alexander
|
Date: 1/15/2013
Вы используете таймфрейм стратегию, вызов OnProcess просто срабатывает раз в 5 минут. Никто не обещает что он срабатывает именно в 11-00, 11-05 и т.д. Он берёт время когда вы создаёте и запускаете стратегию и дальше делает +5 минут. Хотите чтоб чаще срабатывал OnProcess - задайте Interval.
P.S. Я не основатель :)
|
|
|
|
|
developer_29
|
Date: 1/15/2013
Alexander Mukhanchikov Он берёт время когда вы создаёте и запускаете стратегию и дальше делает +5 минут. Хотите чтоб чаще срабатывал OnProcess - задайте Interval.
Спасибо, с данным вопросом разобрался, буду разбираться с остальными. И ещё: торги начинаются в 10-00, а если я запущу вышенаписанный код в 9-00 (и пойду по делам), то он просто будет ждать начала торгов, а затем нормально отработает весь день (если ничего не портить и не ломать)? Иными словами, есть ли необходимость самостоятельно дописывать код, чтобы он "включался самостоятельно" к началу торгов или это уже учтено?
|
|
Thanks:
|
|
|
|
|
Alexander
|
Date: 1/15/2013
S# просто запускает OnProcess раз в timeframe. Всё что выполняется далее зависит от того что написано в OnProcess. Если у вас там написано слать заявки на каждый вызов - он будет слать заявки и в клиринг, и ночью.
|
|
|
|
|
developer_29
|
Date: 1/20/2013
|
|
|
|
Спасибо, понял. Только вот по ходу написания робота возникло несколько вопросов, напишу здесь, чтобы не плодить заголовков: 1 - при подключении к Quik сразу же возникают 2 портфеля, которые я не создавал. Каким из них пользоваться и если они служат для разных целей, то для каких именно и почему у них такие странные имена? 2 - Как создать заявку, которая просто "сметает" те предолжения, которые сейчас есть, невзирая на их цену (конечно же, они должны начинаться от лучшего к худшему)? Например, когда пишем Code
var order = this.CreateOrder(direction, Security.GetMarketPrice(direction), Volume);
// регистрируем заявку (обычным способом - лимитированной заявкой)
RegisterOrder(order);
Затем цена рынка поменялась в ту или иную сторону, меня она в любом случае устраивает, но заявка не выполняется, т.к. цена изменилась, и предложений по цене Security.GetMarketPrice(direction) сейчас нет. Что делать, чтобы в любом случае выполнилась заявка (съела часть стакана, даже если что-то выполнилось по худшей цене)? 3 - Правильно ли я понял, что для закрытия позиции надо совершить противоположную сделку с тем же инструментом (если купил, то потом продать такой же объём)? 4 - Как выставить stop-loss или зафиксировать прибыль: так Code
// registeredOrder - это ранее зарегистрированная заявка.
trader.CancelOrder(registeredOrder);
или созданием противоположной заявки, как только цена достигнет того уровня, на который мы ориентируемся? Под снятием заявки у Вас подразумеваются уже исполненные заявки или те, которые ещё не успели исполниться, но которые по какой-то причине надо снять? Можно ли заранее прописать stop-loss, чтобы сделка автоматически закрывалась, как только цена достигнет определённного уровня?
|
|
Thanks:
|
|
|
|