finsight / QUIKSharp

QUIK# (QUIK Sharp) is the QUIK Lua interface ported to .NET.
Other
232 stars 135 forks source link

stop limit and take profit + лимитные ордера и выход цены за диапазон. #209

Open iflash5 opened 5 years ago

iflash5 commented 5 years ago

Доброго времени суток.

  1. Просмотрел оба примера и исходник до дыр возможно что-то не вижу в упор: не нашел как поставить одной операцией stop limit and take profit.

на луа можно так

    -- Заполняет структуру для отправки транзакции на Стоп-лосс и Тейк-профит
    local Transaction = {

**["PRICE"]               = price, -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0)
        ["STOPPRICE"]           = stopprice, -- Цена Тейк-Профита
        ["STOP_ORDER_KIND"]     = "TAKE_PROFIT_AND_STOP_LIMIT_ORDER", -- Тип стоп-заявки**

                ["ACTION"]              = "NEW_STOP_ORDER", -- Тип заявки
        ["TRANS_ID"]            = tostring(trans_id),
        ["CLASSCODE"]           = CLASS_CODE,
        ["SECCODE"]             = SEC_CODE,
        ["ACCOUNT"]             = ACCOUNT,
        ["OPERATION"]           = operation, -- Операция ("B" - покупка(BUY), "S" - продажа(SELL))
        ["QUANTITY"]            = "1", -- Количество в лотах

        ["EXPIRY_DATE"]         = "TODAY", -- Срок действия стоп-заявки ("GTC" – до отмены,"TODAY" - до окончания текущей торговой сессии, Дата в формате "ГГММДД")

Подскажите как это сделать с использованием данной библиотеки?

  1. Не нашел понятного примера выставления стоп loss и take profit отдельно. возможно нужно выставить 2 лимитированные заявки вида order = await _quik.Orders.SendLimitOrder(tool.ClassCode, tool.SecurityCode, tool.AccountID, Operation.Buy, priceInOrder, 1).ConfigureAwait(false); с разными ценами, но не понятно почему одна из них не сработает тут же и не понятно почему не заполняется conditionprice..

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

  1. ну и постоянно получаю ошибки вида "QuikSharp.TransactionException: ' Неправильно указана стоп-цена: "24498,000000"' at QuikSharp.MessageConverter.Create(Type objectType, JObject jObject) in Q:\Work\Trading\Development\QUIKSharp-master\src\QuikSharp\Serialization.cs:line 265 at QuikSharp.JsonCreationConverter`1.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) in Q:\Work\Trading\Development\QUIKSharp-master\src\QuikSharp\Serialization.cs:line 405 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)

при этом как бы не выставлял цены ниже, выше....

var minPrice = Math.Floor(decimal.Parse(_connection.Quik.Trading
                    .GetParamEx(ClassCode, SecurityCode, ParamNames.PRICEMIN).Result.ParamValue.Replace('.', separator)));

                stopprice = orderPrice + TAKE_PROFIT * priceStep; //--Уровень цены, когда активируется Тейк - профит
                stopprice2 = orderPrice - STOP_LOSS * priceStep; //--Уровень цены, когда активируется Стоп - лосс

                slOrder.ConditionPrice = stopprice2;
                slOrder.Price = minPrice;
                slOrder.StopOrderType = StopOrderType.StopLimit;
                slOrder.Operation = Operation.Sell;

                tpOrder.ConditionPrice = stopprice;
                tpOrder.Price = minPrice;
                tpOrder.StopOrderType = StopOrderType.TakeProfit;
                tpOrder.Operation = Operation.Sell;

var sl = _connection.Quik.StopOrders.CreateStopOrder(slOrder).Result;
var tp = _connection.Quik.StopOrders.CreateStopOrder(tpOrder).Result;

единственное пока решение которое вижу  делать через 
Quik.Trading.SendTransaction но не уверен что это правильное решение.
iflash5 commented 5 years ago

я понимаю что все очень заняты и стараюсь вообще вопросов не задавать. , но через тест на луа я выяснил что ошибка получается из-за присутствия дробной части, если ее убрать то ошибка уходит. Вопрос как сделать это с помощью данной библиотеки?

п.с. прочитал все закрытые задачи.

пробовал расширить не сильно помогло.

 public async Task<long> CreateStopOrder(StopOrder stopOrder)
        {
            Transaction newStopOrderTransaction = new Transaction
            {
........
                STOPPRICE2 = stopOrder.ConditionPrice2,
            };

   private StopOrderKind ConvertStopOrderType(StopOrderType stopOrderType)
        {
            switch (stopOrderType)
            {
                case StopOrderType.StopLimit:
                    return StopOrderKind.SIMPLE_STOP_ORDER;

                case StopOrderType.TakeProfit:
                    return StopOrderKind.TAKE_PROFIT_STOP_ORDER;

                case StopOrderType.StopLimitAndTakeProfit:
                    return StopOrderKind.TAKE_PROFIT_AND_STOP_LIMIT_ORDER;

                default:
                    throw new Exception("Not implemented stop order type: " + stopOrderType);
            }
        }

вы только скажите что этого нет или это баг и я попробую сделать коммит....

В крайнем случае ПОЖАЛУЙСТА напишите, что просто игнорите чтобы понимать что вас не ждать.

Pr0phet1c commented 5 years ago

Начнем с того, что сюда вообще редко кто заглядывает, если не возникает проблем. Я хоть и подписан на оповещения, но тоже не всегда могу оперативно отреагировать.

Базовой функцией, для отправки заявок, в QLua является SendTransaction. В библиотеке также. От этого и отталкивайтесь. Все дополнительные функции типа "CreateStopOrder" или "CreateOrder" - это все надстройки над базовой функцией, созданные для упрощения процесса в СТАНДАРТНЫХ ситуациях. Когда появляются специфические запросы, то решать их нужно через базовый функционал самостоятельно.

Теперь по порядку Ваших вопросов:

  1. Никогда не пытался создавать такую заявку программным способом, ни на Lua ни на QUIK#. Опыта нет, подсказывать нечего.
  2. Когда-то давно я начинал использовать StopLoss и TakeProfit заявки. Были написаны отдельные функции для выставления этих заявок, и они работали. Функции были основаны на SendTransaction. Позже я отказался от использования таких заявок в пользу обычных лимитированных. Думал найду эти функции, но оказалось, что я их полностью вырезал из кода, и выглядит это теперь так:

    public async Task SetNewStopOrder()
    {
        await Task.Delay(1);
    }

    :) так что поделиться не получится. Единственное, что могу посоветовать, это внимательно изучить код функций в свободно распространяемой библиотеке QL.lua. В свое время я оттуда много полезного почерпнул.

  3. Рискну предположить (хотя могу и ошибаться), что ошибка появляется из-за региональных настроек на Вашем компе. Чаще всего это связано с используемым в системе символом разделения дробной части ("." или ","). Заметьте, что Вы единственный, у кого возникает данная проблема. Это должно было натолкнуть Вас на соответствующие мысли. Обратите внимание на строчку:

    readonly Char separator = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];

В демо-приложении, а также где и как используется эта переменная. Возможно, это натолкнет Вас на какие-то мысли.

З.Ы.: Т.к. разработчики квика весьма странные товарищи в некоторых вопросах, то при изучении функционала какой-либо функции, не забывайте обращать внимание на то в каком формате происходит обмен данными. Например, нередко, там, где мы передаем число, разработчики квика организовали этот процесс через передачу данных в текстовом формате, ну и т.п.

iflash5 commented 5 years ago

@Pr0phet1c

огромнейшее спасибо что ответили!

Насчет моей ошибки: Дело оказалось не системных настройках. есть графики у которых нет дробной части и шаг целый в этом проблема для библиотеки. решил, но в библиотеке она осталась. (может кину коммит чуть позже. не уверен что примут т.к. по сути это обработка ошибок qLua и данных с quik) decimal "123.000" или "123,000" вызовет ошибку, нужно кидать "123"

"QLua ,SendTransaction, QL.lua" спасибо за детальный ответ и направления! думаю мне это очень поможет.

Pr0phet1c commented 5 years ago

То, что Вы называете проблемой библиотеки, на самом деле таковой не является. Еще раз повторюсь: Изначальная задача библиотеки - повторить функционал QLua. Я почти уверен, что если вы попытаетесь подсунуть дробную часть цены, там где ее не должно быть, при формировании заявки непосредственно в QLua, то столкнетесь с той же проблемой. Но Вы же не станете писать разработчикам Квика о том, что у них ошибка...

Я призываю Вас внимательно изучить код демонстрационных приложений. Думаете следующее сочетание строчек:

                    decimal priceInOrder = Math.Round(tool.LastPrice + tool.Step * 5, tool.PriceAccuracy);
                    long transactionID = (await _quik.Orders.SendLimitOrder(tool.ClassCode, tool.SecurityCode, tool.AccountID, Operation.Buy, priceInOrder, 1).ConfigureAwait(false)).TransID;

просто так написано? Обнаруживая, что-то, что Вам кажется проблемой или ошибкой, в первую очередь задайтесь вопросом: Если это повсеместно используемая функция, то почему уже достаточно длительное время, никто не шлет коммиты на эту тему? Может быть это и не проблема вовсе, а давно решенная кем-то задачка?

Я специально написал демонстрационные приложения, в том числе рабочего робота, чтобы новые пользователи библиотеки в первую очередь осознали, что полноценный робот на этой библиотеке УЖЕ построен и полностью работоспособен. И если у Вас не получается сделать своего робота, то скорее всего Вы сами делаете что-то не так.

iflash5 commented 5 years ago

@Pr0phet1c

То, что Вы называете проблемой библиотеки, на самом деле таковой не является. Еще раз повторюсь: Изначальная задача библиотеки - повторить функционал QLua.

да я все больше это осознаю в т.ч. благодаря вам! за это спасибо.

Я почти уверен, что если вы попытаетесь подсунуть дробную часть цены, там где ее не должно быть, при формировании заявки непосредственно в QLua, то столкнетесь с той же проблемой. Но Вы же не станете писать разработчикам Квика о том, что у них ошибка...

Попробуйте посмотреть на ситуацию с точки зрения человека который первый раз видит библиотеку и хочет ее использовать. впервые сталкиваюсь с ситуацией когда decimal 123.000 != decimal 123 что делать если не могу найти ответ на свой вопрос? может faq поможет? там нет такой проблемы может были такие проблемы у кого-то и посмотреть в список закрытых багов - там тоже не нашел. (кстати только там приходит осознание что основная функция sendTransaction, а не набор вспомогательных) отладка - да, но упираемся в exception со стороны qlua => проблема там и копаем в qlua изучить демки(классно что они есть кстати!) - тоже хорошая идея и я это сделал тремя проходами. но во-первых найти 1 строчку в 1000 строк кода не всегда очевидно особенно когда не понимаешь что искать и вместо стопов и профитов используются обычные заявки (как я это увидел + еще куча вопросов вида как решается проскальзование и т.д.), а во-вторых робот бывает просто вылетает т.к. конфликтуют потоки да конечно же первая мысль попробовать поправить хоть как-то и пишется

void AddLog(TextBox _tb, string _text)
 {
           try
            {
                _tb.Text = _text + _tb.Text;
                //_tb.SelectionStart = _tb.TextLength;
                //_tb.SelectionLength = 0;
                //_tb.Select(_tb.Text.Length-3,2);
            }
            catch (Exception ex)
            {
                var t = ex;
            }

            return;
            if (_tb.InvokeRequired)
            {
                var d = new TextBoxTextDelegate(AddLog);
                _tb.Invoke(d, _tb, _text);
                //_tb.BeginInvoke(new Action(() => { _tb.Text = _text; }));
            }
            else _tb.Text = _text;
        }
}

но и тогда не все так гладко ему не хватает данных и мы видим (картинку)

изображение

для любых других библиотек можно загуглить, а тут как?

и по-моему задавать вопросы это норм.. было бы здорово если был может какой-то форум..


Я призываю Вас внимательно изучить код демонстрационных приложений. Думаете следующее сочетание строчек:

                decimal priceInOrder = Math.Round(tool.LastPrice + tool.Step * 5, tool.PriceAccuracy);
                long transactionID = (await _quik.Orders.SendLimitOrder(tool.ClassCode, tool.SecurityCode, tool.AccountID, Operation.Buy, priceInOrder, 1).ConfigureAwait(false)).TransID;

просто так написано?

это замечательное решение в пределах обозначенной задачи и спасиб за него! найти его не так очевидно, я видел эти строки, но они воспринимаются скорее как вы используете их для каких-то своих целей (возможно так требует алгоритм) делаете округление... без пояснения не совсем очевидно что они решают проблему.

И если у Вас не получается сделать своего робота, то скорее всего Вы сами делаете что-то не так.

моя задача была тривиальнее: помимо осуществления сделки по рыночной заявке выставить стоп и тейк настоящие со всеми параметрами. Может быть я не увидел где-то в комментариях или в коде или в faq

  1. выполнение рыночной сделки ( это да повсеместно)
  2. выставление стоп
  3. выставление тейк (описание параметров и несколько примеров как использовать с описанием подводных камней) значит не досмотрел...

если это место есть и вы укажите на него был бы благодарен.

 void ClosePosition(decimal price)
        {
            if (settings.RobotMode == "Боевой")
            {
                if (position.toolQty > 0) position.closingOrderID = NewOrder(_quik, tool, Operation.Sell, price, Math.Abs(position.toolQty / tool.Lot));
                else if (position.toolQty < 0) position.closingOrderID = NewOrder(_quik, tool, Operation.Buy, price, Math.Abs(position.toolQty / tool.Lot));
long NewOrder(Quik _quik, Tool _tool, Operation operation, decimal price, int qty)
        {
            long res = 0;
            if (settings.RobotMode == "Боевой")
            {
                Order order_new     = new Order();
                order_new.ClassCode = _tool.ClassCode;
                order_new.SecCode   = _tool.SecurityCode;
                order_new.Operation = operation;
                order_new.Price     = price;
                order_new.Quantity  = qty;
                order_new.Account   = _tool.AccountID;
                try
                {
                    res = _quik.Orders.CreateOrder(order_new).Result;
                }
                catch
                {
                    Console.WriteLine("Неудачная попытка отправки заявки");
                }
            }
            else

с одной стороны new Order который из Order.cs с другой стороны Operation которая из StopOrder что очень сильно смущает, а если поискать по слову Profit то вообще ничего нет с этой операцией. Хотя скорее всего profit получается здесь обычной операцией

 void ClosePosition(decimal price)
        {
            if (settings.RobotMode == "Боевой")
            {
                if (position.toolQty > 0) position.closingOrderID = NewOrder(_quik, tool, Operation.Sell, price, Math.Abs(position.toolQty / tool.Lot));
                else if (position.toolQty < 0) position.closingOrderID = NewOrder(_quik, tool, Operation.Buy, price, Math.Abs(position.toolQty / tool.Lot));
                AddLog(textBoxLogs, "ID заявки - " + position.closingOrderID + Environment.NewLine);
            }
            else
            {
                decimal profit;
                if (position.toolQty > 0) // для логовых позиций
                {
                    profit = Math.Round((price * Math.Abs(position.toolQty / tool.Lot) * tool.Lot - position.priceEntrance * Math.Abs(position.toolQty / tool.Lot) * tool.Lot) / tool.Step * tool.PriceStep, tool.PriceAccuracy);
                }
                else // для шортовых позиций
                {
                    profit = Math.Round((position.priceEntrance * Math.Abs(position.toolQty / tool.Lot) * tool.Lot - price * Math.Abs(position.toolQty / tool.Lot) * tool.Lot) / tool.Step * tool.PriceStep, tool.PriceAccuracy);
                }
                summProfit = summProfit + profit;
                position.toolQty = 0;
                position.priceEntrance = 0;
                position.stopLoss = 0;
                directionPosition = "-";
                textBoxPositionDirection.Text = "-";
                textBoxPositionPE.Text = "";
                textBoxPositionQty.Text = "";
                ClosePos2Table(DateTime.Now, price, profit, summProfit);
                closePause = false;
            }
        }

вполне возможно я как-то не так воспринимаю сами сделки. для меня это операция с параметрами. И в зависимости от параметров имеется разное поведение, например:

Как итог хотелось бы по возможности большей документированности в любом виде чтобы всем было проще использовать.

Меня радует что вы не послали меня в код - "сам дурак, копай там все есть"

спасибо вам за пояснения и пример кода. Однозначно буду использовать.

Pr0phet1c commented 5 years ago

Не ждите бОльшей документированности. Это не платный проект, где Вы за свои деньги получаете техническую поддержку. Еще раз: Задача библиотеки - повторить функционал QLua. Эта задача выполнена (где-то на 99% наверное). На этом все. Хотите получить понимание - напишите полноценного робота на QLua. После того как справитесь с этой задачей, к тому как написать робота на C#, с использованием библиотеки, вопросов скорее всего уже не останется.

З.Ы.: Некоторые из функций и классов, которые Вы можете наблюдать в демо-приложениях, я выдирал из своих личных библиотек и роботов (в ряде случаев, упрощая их). Моя задача была продемонстрировать наглядное доказательство работоспособности библиотеки, и на мой взгляд, с этой задачей я справился. Объяснять и/или доказывать почему и для чего я выполнил ту или иную реализацию отдельных частей кода я не планировал и не планирую.

З.З.Ы: Ну и для справки - я вообще ни разу не программист. Однако, получилось и собственноручно роботов написать и даже помочь в развитии данной библиотеки. Было бы желание...

Prival1 commented 4 years ago

Может это поможет. На срочном рынке, есть только лимитные ордера. Других алгоритм сведения сделок на бирже не понимает. Все SL и ТП реализованы на базе этого ордера программным обеспечением. Ударить по рынку, это тоже лимитный ордер с ценой исполнения на планке дня. Надо как то реализовать функцию OCO (один ордер отменяет другой для ТП и CЛ). Биржа за нас это делать не будет, нужно самим это программировать. Надеюсь помог

OldSchoolMan commented 4 years ago

@iflash5 не уверен, что актуально: стоп-заявки с StopOrderType.TakeProfit сейчас в библиотеке не реализовано, т.к. нет соответствующих полей в StopOrder и использования в CreateStopOrder параметров OFFSET, OFFSET_UNITS, SPREAD, SPREAD_UNITS, MARKET_STOP_LIMIT, MARKET_TAKE_PROFIT и т.д. в Transaction. Сейчас пытаюсь это все реализовать на локальной копии.

OldSchoolMan commented 4 years ago

@iflash5 добавлена поддержка стоп-заявок с типом "Тейк-профит стоп-лимит".