Mikhail Sukhov
|
Дата: 21.03.2011
Иванов Андрей Не хотите разбираться с представлением чисел на компьютере, просто попробуйте. У вас число в примере четыре знака, представляя его в виде double или decimal, результат не изменится. Такие числа можно хоть в float хранить, с семью знаками, ничего не изменится. Просто смысла нет -- рантайм может его запросто хранить в 64 битах, хоть он и 32 бита, и процессор его наверняка будет считать в 64 битах.
Дело то не в максимальной размерности и точности после запятой. А в точности результирующего значения. Ему верить нельзя. Иванов Андрей Если я неправ, покажите код или псевдокод, который приводит к 100.99999 в double и не приводит к тому же в decimal.
Да легко: Кодvar priceF = 109.1;
for (var i = 0; i < 9; i++) { priceF += 0.1 * i; }
priceF /= 0.1;
if (priceF != 1127) throw new Exception();
Код var priceD = 109.1m;
for (var i = 0; i < 9; i++) { priceD += 0.1m * i; }
priceD /= 0.1m;
if (priceD != 1127) throw new Exception();
|
|
Спасибо:
|
|
|
|
|
Иванов Андрей
|
Дата: 22.03.2011
Поставьте не 0.1, а 0.01, посмотрите, что получится. Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double. Это особенности представления вещественных чисел на компьютере, плата за скорость.
Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.
|
|
Спасибо:
|
|
|
|
|
anothar
|
Дата: 22.03.2011
|
|
|
|
Иванов Андрей Поставьте не 0.1, а 0.01, посмотрите, что получится. Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double. Это особенности представления вещественных чисел на компьютере, плата за скорость.
Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки. Вот неплохой пост: http://stackoverflow.com/questions/803225/when-should-i-use-double-instead-of-decimal. В целом он повторяет Ваши мысли и мысли Михаила. Смысл примерно такой что для финансовых вычислений скорее всего больше подходит именно decimal(кроме тех случаев когда у Вас очень большой расчет) по той причине, которую указал Михаил, а для всех остальных double(или float)-но это как я понял.
|
|
Спасибо:
|
|
|
|
|
Mikhail Sukhov
|
Дата: 22.03.2011
|
|
|
|
Иванов Андрей Поставьте не 0.1, а 0.01, посмотрите, что получится. Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double. Это особенности представления вещественных чисел на компьютере, плата за скорость.
При торговле в реальном времени что double что decimal не несет никакой разницы в производительности. Но мне приходится каждый раз делать округление, так как я не могу гарантировать в коде, что double будет сформирован правильно. То, что при 0.01 мой пример работает, а при 0.1 не работает - это еще хуже, чем если бы не работало всегда. Я не могу однозначность сказать - все будет работать. Поэтому лишняя перестраховка в виде выравнивания цены. Иванов Андрей Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки.
Если я вычисляю сред арифметическое, то я точно знаю, что нужно округлять. И я точно знаю, что код, подобный моему примеру, не требует округления. Главное для меня, точно знать как будет работать. Я уже довольно много натыкался на грабли, когда код не работал банально из-за неточности double. Часами выискивал проблему, которая может и вообще сегодня не проявиться. А такая бага самая худшая, когда то работает, то нет (ваш пример с ИИС). Иванов Андрей И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.
Ее тяжесть начала проявляться на бэк тестинге. Если в реальном времени больше времени занимает получение данных, то в виртуальном времени - торговая часть.
|
|
Спасибо:
|
|
|
|
|
Иванов Андрей
|
Дата: 22.03.2011
|
|
|
|
Mikhail Sukhov Иванов Андрей Поставьте не 0.1, а 0.01, посмотрите, что получится. Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double. Это особенности представления вещественных чисел на компьютере, плата за скорость.
При торговле в реальном времени что double что decimal не несет никакой разницы в производительности. Но мне приходится каждый раз делать округление, так как я не могу гарантировать в коде, что double будет сформирован правильно. То, что при 0.01 мой пример работает, а при 0.1 не работает - это еще хуже, чем если бы не работало всегда. Я не могу однозначность сказать - все будет работать. Поэтому лишняя перестраховка в виде выравнивания цены. В интернетах есть мнение, что стандарт IEEE 754 убьёт мир =) http://www.yur.ru/science/computer/IEEE754.htm
Лишняя перестраховка в виде выравнивания цены не хуже лишней перестраховки в виде decimal =) Цитата:Иванов Андрей Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки.
Если я вычисляю сред арифметическое, то я точно знаю, что нужно округлять. И я точно знаю, что код, подобный моему примеру, не требует округления. Главное для меня, точно знать как будет работать. Я уже довольно много натыкался на грабли, когда код не работал банально из-за неточности double. Часами выискивал проблему, которая может и вообще сегодня не проявиться. А такая бага самая худшая, когда то работает, то нет (ваш пример с ИИС). В вашем примере надо тоже делать округление, потому что делите. Я думаю, что на всякий случай надо делать округление перед каждой заявкой, чтобы всё продолжало работать после внесения изменений в середину кода. Чтобы получалось слабое связывание. Цитата:Иванов Андрей И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.
Ее тяжесть начала проявляться на бэк тестинге. Если в реальном времени больше времени занимает получение данных, то в виртуальном времени - торговая часть. У меня половину времени занимает чтение лога сделок из файла. На истории [20 декабря, сейчас] по GMKN всё примерно 5.5 секунд, из них секунды 3 это чтение истории из файлов, мегабайтов 90 где-то. Для одного дня программа дольше запускается, чем работает. Вполне возможно, что я использую какие-то очень простые инструменты. На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд. Уверен, что decimal не принесёт денег, зато принесёт хлопоты.
|
|
Спасибо:
|
|
|
|
|
Mikhail Sukhov
|
Дата: 22.03.2011
Иванов Андрей На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд.
Уверен, что decimal не принесёт денег, зато принесёт хлопоты. Я уже переделал до 3.0 еще. Но это не выносил в публичную ветку. Получилось быстрее, потому что из своей стратегии я повыкидывал Shrink.
|
|
Спасибо:
|
|
|
|
|
Иванов Андрей
|
Дата: 26.03.2011
|
|
|
|
Андрей Ефимов Иванов Андрей Поставьте не 0.1, а 0.01, посмотрите, что получится. Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double. Это особенности представления вещественных чисел на компьютере, плата за скорость.
Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки. Вот неплохой пост: http://stackoverflow.com/questions/803225/when-should-i-use-double-instead-of-decimal. В целом он повторяет Ваши мысли и мысли Михаила. Смысл примерно такой что для финансовых вычислений скорее всего больше подходит именно decimal(кроме тех случаев когда у Вас очень большой расчет) по той причине, которую указал Михаил, а для всех остальных double(или float)-но это как я понял. Не совсем так. "Финансы" это слишком обще. Когда вам надо суммировать операции по счетам клиентов, то выбора нет, только decimal. Потому что представить в double 0.1 (10 копеек), например, невозможно. Когда у вас есть арифметика (считаете проценты, например), то double обычно точнее и всегда значительно быстрее. В случае с роботами суммирование операций это вспомогательные вычисления, основные как раз разнообразная арифметика. Мне так кажется =) По сути, это вопрос религии или требования к производительности -- сложив три раза 1/3 в decimal получишь 0.9999999999, а в double 1.0, но сложив 10 раз 0.1 для decimal будет 1.0 и 0.999999999 для double. То есть, округлять надо независимо от выбора представления и результат будет приемлемый в любом случае. А если бездумно делить, повторюсь в который раз, decimal не поможет. Mikhail Sukhov Иванов Андрей На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд.
Уверен, что decimal не принесёт денег, зато принесёт хлопоты. Я уже переделал до 3.0 еще. Но это не выносил в публичную ветку. Получилось быстрее, потому что из своей стратегии я повыкидывал Shrink. Хорошо. "Посмотрим, что скажет стая" =) Интересно будет послушать про результаты -- стало быстрее и/или больше денег. Может быть выложить параллельно версию с decimal?
|
|
Спасибо:
|
|
|
|
|
Иванов Андрей
|
Дата: 26.03.2011
|
|
|
|
Исходя из описания и именования, я бы ShrinkPrice сделал вот таким. Проверить мне его не на чем, но не вижу причин, почему бы ему не работать в несколько десятков раз быстрее. Было Код public static double ShrinkPrice(this Security security, double price, ShrinkRules rule = ShrinkRules.Auto) { if (security == null) throw new ArgumentNullException("security"); decimal num = (decimal) price; decimal minStepSize = (decimal) security.MinStepSize; if (minStepSize == 0M) throw new ArgumentException("trololo"); return (double) num.Round(minStepSize, security.Decimals, ((rule == ShrinkRules.Auto) ? null : new MidpointRounding?((rule == ShrinkRules.Less) ? MidpointRounding.AwayFromZero : MidpointRounding.ToEven))); }
Стало Код public static double ShrinkPrice(this Security security, double price, ShrinkRules rule = ShrinkRules.Auto) { if (security == null) throw new ArgumentNullException("security"); if (price <= 0.0) throw new ArgumentException("Получена цена меньше либо равная нулю.", "price"); if (security.MinStepSize <= 0.0) throw new InvalidOperationException("Невозможно выровнять цену для инструмента без установленного минимального шага цены."); // security.MinStepSize не параметр метода price /= security.MinStepSize; if (rule == ShrinkRules.Auto) price = Math.Round(price, MidpointRounding.AwayFromZero); else if (rule == ShrinkRules.More) price = Math.Ceiling(price); else if (rule == ShrinkRules.Less) price = Math.Floor(price); else throw new ArgumentOutOfRangeException("rule", rule, "Получено неизвестное правило округления."); price *= security.MinStepSize; return price; }
|
|
Спасибо:
|
|
|
|
|
anothar
|
Дата: 26.03.2011
Вот комментарий по производительности: Decimal perfomanceЕсли ему верить, то у decimal производительность не ахти. А что если внутри представлять все ввиде целого Int64? Ну а наружу-без разницы. ТО есть по сути будет такая структура: фиксированное число знаков после точки, которое зависит только от инструмента и значение ввиде Int64(например 0.0012-это четыре знака после точки и значение 12). Если Вы преимущественно складываете, делите и т.д в рамках одного инструмента то все ок, но насколько это извращенно? По сути это и есть идея того поста.
|
|
Спасибо:
|
|
|
|
|
Иванов Андрей
|
Дата: 27.03.2011
Это не будет быстрее double =) Зато будет больше места для ошибок. И голову надо под такое перестраивать, как под ассемблер.
Если вам не нужна точность за вашими знаками (среднее арифметическое между 0.0012 и 0.0015 при использовании long что даст?), double будет работать не хуже. Чтобы накопить в double ошибку в четвёртом знаке, надо несколько миллионов или десятков миллионов операций. Ошибка быстро накапливается, если используются значения из разных диапазонов -- цена какого-нибудь ИнтерРАО с лотом 100 тысяч и объём торгов за час. То есть, отличаться они должны на несколько порядков. Чем больше порядков различие, тем быстрее накапливается ошибка.
|
|
Спасибо:
|
|
|
|