Событие MarketQuotingStrategy.NewMyTrades и QuikTrader
Atom Ответить
06.01.2012


ak

Фотография
Здравствуйте, коллеги.

Столкнулся с проблемой: в боевом режиме, т.е. при использовании объекта QuikTrader, не вызывается событие NewMyTrades у MarketQuotingStrategy, однако в демо режиме, т.е. при использовании new RealTimeEmulationTrader<QuikTrader>, событие вызывается как положено. В обоих случаях событие ITrader.NewMyTrades вызывается нормально.

Важный момент, проблема наблюдается в последних версиях, проверял в 13417, 12460. Однако все работает в версии 12366. К сожалению, точнее определить версию в которой начала наблюдаться ошибка не могу. Из-за неудобного способа получения новых версий (codeplex download archive) обновляюсь не часто. Пользуясь моментом, хочу попросить доступ на чтение к репозиторию (мой id на codeplex: akramarev).

Спасибо за помощь.

--

Значимые на мой взгляд детали.

Инициализация объекта типа ITrader:

Код
if (rbFightMode.IsChecked.Value)
{
    _trader = new QuikTrader(this.Path.Text);
}
else
{
    _trader = new RealTimeEmulationTrader<QuikTrader>(new QuikTrader(this.Path.Text));
}


Выставление order'а внутри стратегии:

Код
if (UseQuoting)
{
    MarketQuotingStrategy marketQuotingStrategy = new MarketQuotingStrategy(order, new Unit(), new Unit());
    marketQuotingStrategy.NewMyTrades += ProtectMyNewTrades;
    base.ChildStrategies.Add(marketQuotingStrategy);
}
else
{
    base.RegisterOrder(order);
}


Обработчик события marketQuotingStrategy.NewMyTrades (именно этот обработчик не вызвается в боевом режиме):

Код
private void ProtectMyNewTrades(IEnumerable<MyTrade> trades)
{
    var basket = new BasketStrategy(BasketStrategyFinishModes.All);

    foreach (MyTrade trade in trades)
    {
        var s = new BasketStrategy(BasketStrategyFinishModes.First) { Name = "ProtectStrategy" };

        var takeProfit = new TakeProfitStrategy(trade, this.TakeProfitUnit)
        {
            Name = "TakeProfitStrategy",
            BestPriceOffset = 15,
            PriceOffset = 3,
            UseQuoting = this.UseQuoting
        };

        var stopLoss = new StopLossStrategy(trade, this.StopLossUnit)
        {
            Name = "StopLossStrategy",
            PriceOffset = 3
        };

        s.ChildStrategies.Add(takeProfit);
        s.ChildStrategies.Add(stopLoss);

        basket.ChildStrategies.Add(s);
    }

    base.ChildStrategies.Add(basket);
}


Лог работы при использовании RealTimeEmulationTrader<QuikTrader>:
Цитата:
MQS | 05.01.2012 12:30:05.081 | | Заканчиваем котирование.
MQS | 05.01.2012 12:30:05.097 | | Стратегия остановлена.
MQS | 05.01.2012 12:30:05.081 | | Стратегия останавливается.
MQS | 05.01.2012 12:30:05.081 | | Позиция изменилась на -2. Оставшийся объем 0.
StopLossStrategy | 05.01.2012 12:30:05.081 | | Котирование на Buy объема 2.
StopLossStrategy | 05.01.2012 12:30:05.081 | | Защита сделки 1 заявки 43787560.
StopLossStrategy | 05.01.2012 12:30:05.081 | | Стратегия запущена.
TakeProfitStrategy | 05.01.2012 12:30:05.081 | | Котирование на Buy объема 2.
TakeProfitStrategy | 05.01.2012 12:30:05.081 | | Защита сделки 1 заявки 43787560.
TakeProfitStrategy | 05.01.2012 12:30:05.081 | | Стратегия запущена.
ProtectStrategy | 05.01.2012 12:30:05.081 | | Стратегия запущена.
BS | 05.01.2012 12:30:05.081 | | Стратегия запущена.
MQS | 05.01.2012 12:30:05.065 | | Новая Sell сделка 1 по цене 8449 на 2 заявки 43787560.
EmaEventModelStrategy | 05.01.2012 12:30:05.065 | | Новая Sell сделка 1 по цене 8449 на 2 заявки 43787560.
MQS | 05.01.2012 12:30:05.065 | | Перекотирование зарегистрировано для заявки 43787561 на Sell с ценой 8450 объемом 2.
MQS | 05.01.2012 12:30:05.065 | | Котирование заявки 43787560 на Sell с ценой 8449 объемом 2.
MQS | 05.01.2012 12:30:05.065 | | Лучший бид 8449 и лучший аск 8450.
MQS | 05.01.2012 12:30:05.065 | | Цена текущей 8449 и лучшей 8450.
MQS | 05.01.2012 12:30:04.063 | | Заявка 43787560 принята биржей.
MQS | 05.01.2012 12:30:04.063 | | Перекотирование зарегистрировано для заявки 43787560 на Sell с ценой 8449 объемом 2.
MQS | 05.01.2012 12:30:04.063 | | Котирование заявки 43787559 на Sell с ценой 8450 объемом 2.
MQS | 05.01.2012 12:30:04.063 | | Лучший бид 8448 и лучший аск 8449.
MQS | 05.01.2012 12:30:04.063 | | Цена текущей 8450 и лучшей 8449.
MQS | 05.01.2012 12:30:03.061 | | Заявка 43787559 принята биржей.
MQS | 05.01.2012 12:30:03.061 | | Перекотирование зарегистрировано для заявки 43787559 на Sell с ценой 8450 объемом 2.
MQS | 05.01.2012 12:30:03.061 | | Котирование заявки 43787558 на Sell с ценой 8454 объемом 2.
MQS | 05.01.2012 12:30:03.061 | | Лучший бид 8449 и лучший аск 8450.
MQS | 05.01.2012 12:30:03.061 | | Цена текущей 8454 и лучшей 8450.
MQS | 05.01.2012 12:30:02.059 | | Заявка 43787558 принята биржей.
MQS | 05.01.2012 12:30:02.059 | | Перекотирование зарегистрировано для заявки 43787558 на Sell с ценой 8454 объемом 2.
MQS | 05.01.2012 12:30:02.059 | | Котирование заявки 43787557 на Sell с ценой 8455 объемом 2.
MQS | 05.01.2012 12:30:02.059 | | Лучший бид 8451 и лучший аск 8454.
MQS | 05.01.2012 12:30:02.059 | | Цена текущей 8455 и лучшей 8454.
MQS | 05.01.2012 12:30:01.057 | | Заявка 43787557 принята биржей.
MQS | 05.01.2012 12:30:01.057 | | Перекотирование зарегистрировано для заявки 43787557 на Sell с ценой 8455 объемом 2.
MQS | 05.01.2012 12:30:01.057 | | Котирование заявки 43787556 на Sell с ценой 8456 объемом 2.
MQS | 05.01.2012 12:30:01.057 | | Лучший бид 8454 и лучший аск 8455.
MQS | 05.01.2012 12:30:01.057 | | Цена текущей 8456 и лучшей 8455.
MQS | 05.01.2012 12:30:00.383 | | Заявка 43787556 принята биржей.
MQS | 05.01.2012 12:30:00.368 | | Заявка 43787556 на Sell отправлена с ценой 8456 объемом 2.
MQS | 05.01.2012 12:30:00.352 | | Регистрация новой заявки на Sell с ценой 8456 и объемом 2.
MQS | 05.01.2012 12:30:00.352 | | Лучший бид 8455 и лучший аск 8456.
MQS | 05.01.2012 12:30:00.352 | | Цена текущей NULL и лучшей 8456.
MQS | 05.01.2012 12:30:00.321 | | Котирование на Sell объема 2.
MQS | 05.01.2012 12:30:00.321 | | Стратегия запущена.
EmaEventModelStrategy | 05.01.2012 12:30:00.305 | | Xing Down appeared (CandleTime: 05.01.2012 12:25:00), and filter allowed the deal.
EmaEventModelStrategy | 05.01.2012 12:09:50.704 | | Стратегия запущена.



Лог работы при использовании QuikTrader:
Цитата:
MQS | 06.01.2012 15:10:47.107 | | Позиция изменилась на -2. Оставшийся объем 0.
EmaEventModelStrategy | 06.01.2012 10:35:57.031 | | Стратегия запущена.
EmaEventModelStrategy | 06.01.2012 12:15:01.821 | | Xing Up appeared (CandleTime: 06.01.2012 12:10:00), but filter blocked the deal.
EmaEventModelStrategy | 06.01.2012 15:10:18.733 | | Xing Down appeared (CandleTime: 06.01.2012 15:05:00), and filter allowed the deal.
MQS | 06.01.2012 15:10:18.765 | | Стратегия запущена.
MQS | 06.01.2012 15:10:18.765 | | Котирование на Sell объема 2.
MQS | 06.01.2012 15:10:18.796 | | Цена текущей NULL и лучшей 8332.
MQS | 06.01.2012 15:10:18.796 | | Лучший бид 8330 и лучший аск 8332.
MQS | 06.01.2012 15:10:18.796 | | Регистрация новой заявки на Sell с ценой 8332 и объемом 2.
MQS | 06.01.2012 15:10:18.812 | | Заявка 38153494 на Sell отправлена с ценой 8332 объемом 2.
MQS | 06.01.2012 15:10:18.984 | | Заявка 38153494 принята биржей.
MQS | 06.01.2012 15:10:26.788 | | Цена текущей 8332 и лучшей 8331.
MQS | 06.01.2012 15:10:26.788 | | Лучший бид 8330 и лучший аск 8331.
MQS | 06.01.2012 15:10:26.788 | | Котирование заявки 38153494 на Sell с ценой 8332 объемом 2.
MQS | 06.01.2012 15:10:26.788 | | Перекотирование зарегистрировано для заявки 38153495 на Sell с ценой 8331 объемом 2.
MQS | 06.01.2012 15:10:26.977 | | Заявка 38153495 принята биржей.
MQS | 06.01.2012 15:10:42.867 | | Цена текущей 8331 и лучшей 8330.
MQS | 06.01.2012 15:10:42.867 | | Лучший бид 8329 и лучший аск 8330.
MQS | 06.01.2012 15:10:42.867 | | Котирование заявки 38153495 на Sell с ценой 8331 объемом 2.
MQS | 06.01.2012 15:10:42.867 | | Перекотирование зарегистрировано для заявки 38153496 на Sell с ценой 8330 объемом 2.
MQS | 06.01.2012 15:10:43.134 | | Заявка 38153496 принята биржей.
EmaEventModelStrategy | 06.01.2012 15:10:47.107 | | Новая позиция -2.
MQS | 06.01.2012 15:10:47.107 | | Новая позиция -2.
MQS | 06.01.2012 15:10:47.107 | | Заканчиваем котирование.
MQS | 06.01.2012 15:10:47.107 | | Стратегия останавливается.
MQS | 06.01.2012 15:10:47.122 | | Стратегия остановлена.
EmaEventModelStrategy | 06.01.2012 15:10:47.154 | | Новая Sell сделка 485257991 по цене 8330 на 2 заявки 38153496.


Теги:


Спасибо:




18 Ответов
Alexander

Фотография
Дата: 06.01.2012
Ответить


как понимаете что не вызывается обработчик? а сделки-то у MQS проходят?
Спасибо:

ak

Фотография
Дата: 06.01.2012
Ответить


Alexander Mukhanchikov Перейти
как понимаете что не вызывается обработчик?


В хронологическом порядке как обнаруживал проблему:
  1. Не создаются защитные стратегии для совершенных сделок
  2. Breakpoint на первой строчке внутри цикла foreach (MyTrade trade in trades) не срабатывает при отладке
  3. Тестовое сообщение в лог, вставленное первой строчкой в метод ProtectMyNewTrades, не появляется в логе


Alexander Mukhanchikov Перейти
а сделки-то у MQS проходят?

Да. Более того, событие ITrader.NewMyTrades вызывается нормально, подписавшись на него, я, например, нормально заполняю TradeGrid новыми сделками.
Автор топика
Спасибо:

Alexander

Фотография
Дата: 08.01.2012
Ответить


Дело в том, что порой стратегия MQS может завершится раньше чем придёт событие о новой сделки (NewMyTrades)
Что у вас и происходит.

Есть 2 выхода:
1) Поставить флаг RemoveChildStrategies у дочерней стратегии в false - тогда стратегия MQS после завершения не будет удаляться и события должны придти;
2) Реагировать не на событие новых сделок, а на событие изменения отправленного ордера.

P.S. Доступ на codeplex даём тем, кто готов вносить изменения в исходный код, выложенный там.
Спасибо:

ak

Фотография
Дата: 09.01.2012
Ответить


Большое спасибо за ответ, Александр.

Сразу возникает вопрос, не видите ли вы способа внутри MQS синхронизировать генерацию события NewMyTrade и смены ProcessState стратегии (и генерации ProcessStateChanged соответственно), чтобы событие нового трейда гарантированно файрилось до того, как стратегия отрапортует о своем завершении? Сложно что-то предполагать не видя реализации, но судя по всему метод генерирующий NewMyTrade (что-нибудь вроде OnNewMyTrade) слишком долго ждет какого-то ресурса (шлюза ITrader, например, пока другие события успешно генерируются), может его можно пересмотреть? Или, например, заставить Strategy.DisposeManaged() (если именно этот метод вызывается для уничтожения родительской стратегии - дочерней) как-то подождать завершения генерации всех событий.

И еще небольшой вопрос, я не совсем понял второе решение:
Цитата:
Реагировать не на событие новых сделок, а на событие изменения отправленного ордера.

Вы имеете ввиду order переданный в конструктор MQS? Так ведь он не имеет ничего общего с ордером, который будет на самом деле создан и исполнен в ходе котирования.

Например этот анонимный метод никогда не будет вызван:

Код
this.
When(order.Changed())
.Do(() => {
    int i = 1;
});


Или я неправильно вас понял?


Автор топика
Спасибо:

Alexander

Фотография
Дата: 09.01.2012
Ответить


Нет, внутрях MQS не будет ничего меняться.
События из QuikTrader, да и из любого трейдера не синхронны - нам событие о новой сделке может придти до \ после события о новом ордере.

У MQS есть событие NewOrders
для этих новых ордеров можно создавать любые правила которые угодны


И да, вариант с RemoveChildStrategies намного проще.
Спасибо:

ak

Фотография
Дата: 09.01.2012
Ответить


Цитата:
И да, вариант с RemoveChildStrategies намного проще.

Безусловно, но подобные сообщения в логе напрягают:
Цитата:
SLS | 05.01.2012 20:40:30.000 | Внимание | Котирование в состоянии Stopped.


Все же не очень понятно.
Цитата:
События из QuikTrader, да и из любого трейдера не синхронны - нам событие о новой сделке может придти до \ после события о новом ордере.

Какое условие уничтожения дочерней стратегии/изменения MQS.ProcessState на Stopped? Приход в MQS стратегию события от ITrader о новой сделке (в самом простом случае), так? Так почему же MQS.NewMyTrade не появляется мгновенно после этого и только потом в том же потоке не происходит изменение ProcessState на Stopped и генерация ProcessStateChanged? Как тут вообще может возникнуть состояние гонок?

Автор топика
Спасибо:

ak

Фотография
Дата: 09.01.2012
Ответить


Alexander Mukhanchikov Перейти
У MQS есть событие NewOrders
для этих новых ордеров можно создавать любые правила которые угодны


Спасибо, понял. Но если событие MQS.NewMyTrades может не успеть выполниться до уничтожения объекта стратегии, где гарантия, что события на порожденные ею orders успеют выполниться? Или эти orders никак не затрагиваются при dispose стратегии?
Автор топика
Спасибо:

Alexander

Фотография
Дата: 09.01.2012
Ответить


Цитата:
Какое условие уничтожения дочерней стратегии/изменения MQS.ProcessState на Stopped? Приход в MQS стратегию события от ITrader о новой сделке (в самом простом случае), так? Так почему же MQS.NewMyTrade не появляется мгновенно после этого и только потом в том же потоке не происходит изменение ProcessState на Stopped и генерация ProcessStateChanged? Как тут вообще может возникнуть состояние гонок?


Нет, не так.
MQS останавливается следующим образом:
- у стратегии меняется позиция (по заявкам(!), не по сделкам). По умолчанию мониторим изменение позиции стратегии именно по ордерам, а не сделкам.
- проверяется условие можем ли окончить
- если да - заканчиваем


либо - по TimeOut.


Т.к. не так - остальное рассуждение не требует ответа.


Цитата:
Спасибо, понял. Но если событие MQS.NewMyTrades может не успеть выполниться до уничтожения объекта стратегии, где гарантия, что события на порожденные ею orders успеют выполниться? Или эти orders никак не затрагиваются при dispose стратегии?

Стратегия останавливается либо по таймауту, либо по позиции. Позиция меняется по ордерам.
Следовательно ордеры придут.
Спасибо:

ak

Фотография
Дата: 09.01.2012
Ответить


Спасибо, теперь стало все гораздо яснее. Первичность ордеров перед сделками в принятии решения на остановку было ключевым моментом.
Автор топика
Спасибо:

risty

Фотография
Программист
Дата: 11.01.2012
Ответить


Думаю мой вопрос примерно в эту тему.
Заранее прошу сильно не ругаться т.к. только начинаю осваивать C# и S# (4.0.14.0)

Есть некая событийная стратегия FirstStrategy. По замыслу она должна торговать не более чем одним контрактом(RIH2)
Код

private int CurrentPosition;

public FirstStrategy() 
        {
            this.RemoveChildStrategies = false;
            this.CurrentPosition = 0;
        }

protected override void OnStarting()
        {
            
            this
                .When(base.Security.SecurityNewTrades())
                .Do(base.PositionManager.Init)
                .Once();
         

            this
                 .When(base.Security.SecurityNewTrades())
                 .Do(UpOrDown);

            this
                .When(this.Stopping())
                .ClosePosition();

            base.OnStarting();
        }

private void UpOrDown()
        {
            // если наша стратегия в процессе остановки
            if (ProcessState == ProcessStates.Stopping)
            {
                // отменяем активные заявки
                CancelActiveOrders();
                return;
            }

            //свежие сделки ?
            if ((base.Security.LastTrade != null) && ((base.Trader.MarketTime - base.Security.LastTrade.Time).Duration() > new TimeSpan(0, 0, 5))) return;
            

            if ((base.Security.LastTrade.Price != 0) && (this.LevelUP != null) && (this.LevelDown != null))
            {

                //UpMoving
                if ((new Unit(base.Security.LastTrade.Price)) > this.LevelUP)
                {
                    if ((this.CurrentPosition == 0) | (this.CurrentPosition == -1))
                    {
                        _orderDirection = OrderDirections.Buy;
                        var order = this.CreateOrder(_orderDirection, Security.GetMarketPrice(_orderDirection), Volume);
                        CurrentPosition++;
                        var strategyUp = new MarketQuotingStrategy(order, new Unit(0), new Unit(0));
                        ChildStrategies.Add(strategyUp);
                        return;
                    }
                }

                //DownMoving
                if ((new Unit(base.Security.LastTrade.Price)) < this.LevelDown)
                {
                    if ((this.CurrentPosition == 0) | (this.CurrentPosition == 1))
                    {
                        _orderDirection = OrderDirections.Sell;
                        var order = this.CreateOrder(_orderDirection, Security.GetMarketPrice(_orderDirection), Volume);
                        CurrentPosition--;
                        var strategyDown = new MarketQuotingStrategy(order, new Unit(0), new Unit(0));
                        ChildStrategies.Add(strategyDown);
                        return;
                    }
                }
                        
            }
        }

В связи с тем, что this.PositionManager.Position основной стратегии запаздывает относительно Котировния, для учета позиции использую this.CurrentPosition.
При тестировании SampleHistoryTesting поля CurrentPosition и this.PositionManager.Position ведут себя изменяются в диапазоне -1,0,1, что и требуется.
А при тестировании SampleRealTimeTesting this.PositionManager.Position вылетает далеко за рамки одного контракта.
В связи с этим вопрос - как правильно учитывать позицию основной стратегии в тестах и в бою?
Спасибо:

ak

Фотография
Дата: 11.01.2012
Ответить


Гораздо, но не полностью, к сожалению.

Александр, подскажите пожалуйста, почему подобная конструкция также не работает с QuikTrader'ом:


Код
this
    .When(marketQuotingStrategy.StrategyNewOrder())
    .Do((qOrder) =>
    {
        this
            .When(qOrder.NewTrades())
            .Do(ProtectMyNewTrades)
            .Periodical(() => qOrder.IsMatched());
    });


Симптомы те же: ProtectMyNewTrades вызывается в RealTimeEmulationTrader и не вызывается в QuikTrader. Как же правильно защитить сделки после котирования?
Автор топика
Спасибо:

Alexander

Фотография
Дата: 12.01.2012
Ответить


Ни разу не вызывается?

Подпишитесь на Trader.NewMyTrades и посмотрите приходят ли там сделки, равные qOrder.
Спасибо:

ak

Фотография
Дата: 12.01.2012
Ответить


Alexander Mukhanchikov Перейти
Ни разу не вызывается?

Подпишитесь на Trader.NewMyTrades и посмотрите приходят ли там сделки, равные qOrder.


Прошу игнорировать последний мой вопрос, приведенная выше конструкция работает стабильно (насколько мне удалось потестировать) с QuikTrader'ом (при RemoveChildStrategies == true). Проблема, мешавшая вызываться обработчику, заключалась в том, что поток прерывался Exception'ом в другом обработчике (в логи, к сожалению, ничего не попадало):

Код

...
_strategy.NewMyTrades += OnNewTrades;
...

private void OnNewTrades(IEnumerable<MyTrade> trades)
{
    TradesGrid.Trades.AddRange(trades);

    var newTradeLogMessage = "I've {0} {1} future contracts at {2}";
    trades.ForEach(t => this._log.AddLog(
        new ExtendedLogMessage(this._log, DateTime.Now, ErrorTypes.Warning, ExtendedLogMessage.ImportanceLevel.High,
            newTradeLogMessage,
            (t.Trade.OrderDirection.Value == OrderDirections.Buy) ? "bought" : "sold",
            t.Trade.Volume,
            t.Trade.Price)));
}


t.Trade.OrderDirection есть Nullable enum и в нем регулярно был null вместо значения, чего я не ожидал (хотя должен был). Александр, подскажите, почему в сформированном trade OrderDirection может быть null (все остальные поля объекта были заполнены верными значениями)? И самое интересное TradeGrid подобные трейды отображал нормально, с верным направлением - что необходимо вызвать дополнительно чтобы OrderDirection заполнился верным значением?
Автор топика
Спасибо:

Alexander

Фотография
Дата: 12.01.2012
Ответить


Поставьте бряку в начало функции, посмотрите - есть ли там OrderDirection у сделок в пришедшем массиве
Спасибо:

ak

Фотография
Дата: 12.01.2012
Ответить


Alexander Mukhanchikov Перейти
Поставьте бряку в начало функции, посмотрите - есть ли там OrderDirection у сделок в пришедшем массиве


Вот как интересно:

Код
((StockSharp.BusinessEntities.MyTrade[])(trades))[0].Trade.OrderDirection = null
((StockSharp.BusinessEntities.MyTrade[])(trades))[0].Order.Direction = Buy
Автор топика
Спасибо:

Alexander

Фотография
Дата: 12.01.2012
Ответить


ak Перейти
Alexander Mukhanchikov Перейти
Поставьте бряку в начало функции, посмотрите - есть ли там OrderDirection у сделок в пришедшем массиве


Вот как интересно:

Код
((StockSharp.BusinessEntities.MyTrade[])(trades))[0].Trade.OrderDirection = null
((StockSharp.BusinessEntities.MyTrade[])(trades))[0].Order.Direction = Buy


Ничего интересного вроде нет

у order направление есть всегда
у сделки оно может отсутствовать если шлюз эту информацию не транслирует. смартком - не транслирует. (плаза - тоже не транслирует. квик - проставляет)
Спасибо:

ak

Фотография
Дата: 12.01.2012
Ответить


OK, спасибо!

Это очевидно, когда знаешь и видишь как заполняется объект, тут же, когда не видишь исходного кода, - просто ожидаешь наиболее очевидного варианта. Здесь я думал, что значение, в случае не передачи шлюзом, просто копируется из породившего сделку order'а.

Цитата:

https://stocksharp.ru/do...Trade_OrderDirection.htm
Направление заявки (покупка или продажа), которая привела к сделке.


Спасибо за ваше время, ответы и ваш труд над S#.
Автор топика
Спасибо:

Alexander

Фотография
Дата: 12.01.2012
Ответить


Нет, документация как раз тоже не говорит о том что что-то куда-то копируется.
У нас для ордера на Sell легко могут проходить как Buy, так и Sell сделки.

Хотя, наверное, можно было понять двояко.
Спасибо:


Добавить файлы через драг-н-дроп, , или вставить из буфера обмена.

loading
clippy