← 返回

Почему double, а не decimal?

Заинтересовал вопрос, почему параметры в double а не decimal, у которого фиксированная точка?

本主题对您有帮助吗?

分享主题

评论 (40)

登录创建账户, 登录或注册以发表评论

Mikhail Sukhov
Mikhail Sukhov

Андрей Ефимов: Заинтересовал вопрос, почему параметры в double а не decimal, у которого фиксированная точка?

Избежать лишней конвертации при интеграции с другими системами.

Mikhail Sukhov
Mikhail Sukhov

Андрей Ефимов: Заинтересовал вопрос, почему параметры в double а не decimal, у которого фиксированная точка?

В итоге, после 3-ем месяцев осмысления, я понял - переводу на decimal быть. Сейчас это сделано в отдельной ветке S#, и появиться скорее всего, в 3.1.

anothar
anothar 作者

🙂 Замечательно-все будет очень точно)))

Alexander
Alexander

А ведь ни цены, ни объём не могут быть отрицательными. Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?

Mikhail Sukhov
Mikhail Sukhov

Alexander: А ведь ни цены, ни объём не могут быть отрицательными. Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?

Потому что не CLS compliant.

Иванов Андрей
Иванов Андрей

Alexander: А ведь ни цены, ни объём не могут быть отрицательными. Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong? А зачем ulong?! =) Я думаю, что даже у американцев не бывает таких сделок, чтобы они в int не поместились =) Аккумуляторы вы можете использовать такие, какие посчитаете нужным.

Mikhail Sukhov:

Андрей Ефимов: Заинтересовал вопрос, почему параметры в double а не decimal, у которого фиксированная точка?

В итоге, после 3-ем месяцев осмысления, я понял - переводу на decimal быть. Сейчас это сделано в отдельной ветке S#, и появиться скорее всего, в 3.1. Зачем?! =) О какой точности идёт речь?! Точность double 15 знаков, где можно использовать бОльшую точность? Пока вы складываете и умножаете, разницы между double и decimal не будет. Сбер в пятницу на ММВБ наторговался на 24 876 860 765, это 11 знаков. То есть, чтобы добраться до ошибки, вам надо будет суммировать данные за 2000 дней. Если вы при этом перейдёте на decimal, вы получите ещё один порядок. С точки зрения обывателя это много, относительно исследуемых цифр мало -- когда у вас уже использовано 14 порядков, лишний порядок ничего не даст.

Точность у decimal выше, а ёмкость целой части такая же =) Сказки про использование decimal как решение под точные расчёты для детского сада. Потому что если бездумно делить, ошибка будет огромная и с decimal. Использовать decimal обычно неразумно -- он медленнее, больше ест памяти и не даёт ничего, кроме дополнительного порядка или двух ёмкости. Дополнительная ёмкость имеет смысл для агрегирования, ну так и используйте decimal в агрегировании за безумные интервалы. Хотя я бы в этой ситуации всё равно использовал double под промежуточные расчёты с агрегированием в ulong (20 знаков).

Mikhail Sukhov
Mikhail Sukhov

Иванов Андрей: Зачем?! =) О какой точности идёт речь?! Точность double 15 знаков, где можно использовать бОльшую точность? Пока вы складываете и умножаете, разницы между double и decimal не будет. Сбер в пятницу на ММВБ наторговался на 24 876 860 765, это 11 знаков. То есть, чтобы добраться до ошибки, вам надо будет суммировать данные за 2000 дней. Если вы при этом перейдёте на decimal, вы получите ещё один порядок. С точки зрения обывателя это много, относительно исследуемых цифр мало -- когда у вас уже использовано 14 порядков, лишний порядок ничего не даст.

Мне нравиться вот этот пост http://www.rsdn.ru/forum/cpp/2640596.1.aspx Скажем, если бы я писал графическое приложение, где бы вычислял координаты - да тут точность не так важна. В случае с торговым приложение - очень важно чтобы цена были точной. Как минимум, чтобы не получить ошибку при регистрации заявок. Поэтому все всегда приходится вызывать ShrinkPrice чтобы гарантировать правильность подачи заявок.

С чем я сам столкнулся. 1 - когда я восстанавливаю стаканы из файла. Там я как раз умножаю на минимальный шаг + сдвиг от предыдущей котировкки. В итоге цена котировки 100.99999 вместо 101. 2 - когда я группирую стакан по ценовым уровня. Так же приходится округлять каждую строчку.

Иванов Андрей: Точность у decimal выше, а ёмкость целой части такая же =) Сказки про использование decimal как решение под точные расчёты для детского сада. Потому что если бездумно делить, ошибка будет огромная и с decimal. Использовать decimal обычно неразумно -- он медленнее, больше ест памяти и не даёт ничего, кроме дополнительного порядка или двух ёмкости.

Если цена вычисляется через математически формулы, то ей верить без округления нельзя. В итоге ShrinkPrice кушает как раз тот прирост, что дает double перед decimal. И еще чуть больше. Получается обратная ситуация - decimal быстрее перед double, потому что первый не нужно округлять.

И да, при современных-то компьютерам. У меня весь рынок можно уместить виртуально на персональный ПС. А вот заявки все равно скользят.

anothar
anothar 作者

Иванов Андрей:

Alexander: А ведь ни цены, ни объём не могут быть отрицательными. Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong? А зачем ulong?! =) Я думаю, что даже у американцев не бывает таких сделок, чтобы они в int не поместились =) Аккумуляторы вы можете использовать такие, какие посчитаете нужным.

Mikhail Sukhov:

Андрей Ефимов: Заинтересовал вопрос, почему параметры в double а не decimal, у которого фиксированная точка?

В итоге, после 3-ем месяцев осмысления, я понял - переводу на decimal быть. Сейчас это сделано в отдельной ветке S#, и появиться скорее всего, в 3.1. Зачем?! =) О какой точности идёт речь?! Точность double 15 знаков, где можно использовать бОльшую точность? Пока вы складываете и умножаете, разницы между double и decimal не будет. Сбер в пятницу на ММВБ наторговался на 24 876 860 765, это 11 знаков. То есть, чтобы добраться до ошибки, вам надо будет суммировать данные за 2000 дней. Если вы при этом перейдёте на decimal, вы получите ещё один порядок. С точки зрения обывателя это много, относительно исследуемых цифр мало -- когда у вас уже использовано 14 порядков, лишний порядок ничего не даст.

Точность у decimal выше, а ёмкость целой части такая же =) Сказки про использование decimal как решение под точные расчёты для детского сада. Потому что если бездумно делить, ошибка будет огромная и с decimal. Использовать decimal обычно неразумно -- он медленнее, больше ест памяти и не даёт ничего, кроме дополнительного порядка или двух ёмкости. Дополнительная ёмкость имеет смысл для агрегирования, ну так и используйте decimal в агрегировании за безумные интервалы. Хотя я бы в этой ситуации всё равно использовал double под промежуточные расчёты с агрегированием в ulong (20 знаков). Если Вы не знали, то decimal создавался именно для финансовой математики. Он не нужен скажем для расчетов в играх (по хорошему там и float хватит) и дело тут в том, что у decimal и double разное представление. У decimal фиксированная целая и дробная часть. А вот с double проблема в том что фактически оно мало какие целые числа может представить( грубо говоря будет не целое число, а 0.999999) со всеми отсюда вытекающими последствиями.

Иванов Андрей
Иванов Андрей

Андрей Ефимов: Если Вы не знали, то decimal создавался именно для финансовой математики. Он не нужен скажем для расчетов в играх (по хорошему там и float хватит) и дело тут в том, что у decimal и double разное представление. У decimal фиксированная целая и дробная часть. А вот с double проблема в том что фактически оно мало какие целые числа может представить( грубо говоря будет не целое число, а 0.999999) со всеми отсюда вытекающими последствиями. Если вы не знаете, как работает двоичное представление десятичных чисел на компьютере, советую поинтересоваться =) Пока точности double хватает, разницы с decimal нет. По ссылке Мхаила рассматриваются числа 1е-20 (разницы с 1е20 нет, к слову) и эти числа в decimal тоже не поместятся. Где на бирже такие числа? ;) Точность максимум 4-5 знаков у акций Эффективных Менеджеров ВТБ, а с учётом размера лота эти 4-5 знаков превращаются в обычные 1-2 знака.

decimal нужен там, где у вас большая целая часть и большая дробная. Попробуйте привести пример, где оно надо. Уверен, что decimal вам в этом случае не поможет.

Иванов Андрей
Иванов Андрей

Mikhail Sukhov:

Иванов Андрей: Зачем?! =) О какой точности идёт речь?! Точность double 15 знаков, где можно использовать бОльшую точность? Пока вы складываете и умножаете, разницы между double и decimal не будет. Сбер в пятницу на ММВБ наторговался на 24 876 860 765, это 11 знаков. То есть, чтобы добраться до ошибки, вам надо будет суммировать данные за 2000 дней. Если вы при этом перейдёте на decimal, вы получите ещё один порядок. С точки зрения обывателя это много, относительно исследуемых цифр мало -- когда у вас уже использовано 14 порядков, лишний порядок ничего не даст.

Мне нравиться вот этот пост http://www.rsdn.ru/forum/cpp/2640596.1.aspx Скажем, если бы я писал графическое приложение, где бы вычислял координаты - да тут точность не так важна. В случае с торговым приложение - очень важно чтобы цена были точной. Как минимум, чтобы не получить ошибку при регистрации заявок. Поэтому все всегда приходится вызывать ShrinkPrice чтобы гарантировать правильность подачи заявок. А что, с decimal что-то меняется? ;) Два лишних знака на 15 знаках не дают ничего =) Про графическое приложение пример мимо цели ;) Я хоть и не писал графических приложений, но поверхностно в курсе про математику, там используемую. В MS Paint, без сомнений, хватит и short, а вот когда вам надо хранить освещённость точки от нуля до 50 знаков, decimal не поможет =)

Про пост на RSDN. Человек по вашей ссылке возмущается тому же, чему возмущаюсь я. Там кто-то написал, что double сравнивать нельзя, вы пишете, что decimal вам даст большую точность.

С чем я сам столкнулся. 1 - когда я восстанавливаю стаканы из файла. Там я как раз умножаю на минимальный шаг + сдвиг от предыдущей котировкки. В итоге цена котировки 100.99999 вместо 101. 2 - когда я группирую стакан по ценовым уровня. Так же приходится округлять каждую строчку. Это в отладчике так показывается? ;) Попробуйте вывести на консоль то, что в отладчике показывается в виде 100.99999. Не хотите разбираться с представлением чисел на компьютере, просто попробуйте. У вас число в примере четыре знака, представляя его в виде double или decimal, результат не изменится. Такие числа можно хоть в float хранить, с семью знаками, ничего не изменится. Просто смысла нет -- рантайм может его запросто хранить в 64 битах, хоть он и 32 бита, и процессор его наверняка будет считать в 64 битах. Если я неправ, покажите код или псевдокод, который приводит к 100.99999 в double и не приводит к тому же в decimal.

Иванов Андрей: Точность у decimal выше, а ёмкость целой части такая же =) Сказки про использование decimal как решение под точные расчёты для детского сада. Потому что если бездумно делить, ошибка будет огромная и с decimal. Использовать decimal обычно неразумно -- он медленнее, больше ест памяти и не даёт ничего, кроме дополнительного порядка или двух ёмкости.

Если цена вычисляется через математически формулы, то ей верить без округления нельзя. Не поверите -- с decimal то же самое будет =) Его точность нужна там, где большая целая часть и большая дробная. То есть, число представляется в виде 25 знаков суммарно, например. 14 на целую, 11 на дробную. Да, такое число в виде double не представить, но где у вас такие числа? Если заниматься бездумной арифметикой, decimal не спасёт.

В итоге ShrinkPrice кушает как раз тот прирост, что дает double перед decimal. И еще чуть больше. Получается обратная ситуация - decimal быстрее перед double, потому что первый не нужно округлять.

И да, при современных-то компьютерам. У меня весь рынок можно уместить виртуально на персональный ПС. А вот заявки все равно скользят. Файлы тоже медленно работают, по сравнению с быстрым процессором =) Я не люблю рассуждения про современные компьютеры. Это странная причина для того, чтобы делать плохо, когда можно сделать хорошо и без напряжения.

Mikhail Sukhov
Mikhail Sukhov

Иванов Андрей: Не хотите разбираться с представлением чисел на компьютере, просто попробуйте. У вас число в примере четыре знака, представляя его в виде 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();
Геннадий Ванин (Gennady Vanin)

Alexander Mukhanchikov: А ведь ни цены, ни объём не могут быть отрицательными. Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?

Зато их разница - может Текущая чистая позиция - разница объёмов, может быть отрицательная "% изменения от закрытия" Вход. чист. поз.

Разница цены последней к оценке предыдущей сессиии Разница цены последней к предыдущей сессиии и т.д.

Половина парметров, которыми я пользуюсь из КВИК, могут быть отрицательными

Alexander
Alexander

Геннадий Ванин (Gennady Vanin):

Alexander Mukhanchikov: А ведь ни цены, ни объём не могут быть отрицательными. Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?

Зато их разница - может Текущая чистая позиция - разница объёмов, может быть отрицательная "% изменения от закрытия" Вход. чист. поз.

Разница цены последней к оценке предыдущей сессиии Разница цены последней к предыдущей сессиии и т.д.

Половина парметров, которыми я пользуюсь из КВИК, могут быть отрицательными

И? Мы и используем decimal поэтому.

Иванов Андрей
Иванов Андрей

Поставьте не 0.1, а 0.01, посмотрите, что получится. Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double. Это особенности представления вещественных чисел на компьютере, плата за скорость.

Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.

anothar
anothar 作者

Иванов Андрей: Поставьте не 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
Mikhail Sukhov

Иванов Андрей: Поставьте не 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, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.

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

Иванов Андрей
Иванов Андрей

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
Mikhail Sukhov

Иванов Андрей: На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд.

Уверен, что decimal не принесёт денег, зато принесёт хлопоты.

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

Иванов Андрей
Иванов Андрей

Андрей Ефимов:

Иванов Андрей: Поставьте не 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?

Иванов Андрей
Иванов Андрей

Исходя из описания и именования, я бы 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
anothar 作者

Вот комментарий по производительности:Decimal perfomance Если ему верить, то у decimal производительность не ахти. А что если внутри представлять все ввиде целого Int64? Ну а наружу-без разницы. ТО есть по сути будет такая структура: фиксированное число знаков после точки, которое зависит только от инструмента и значение ввиде Int64(например 0.0012-это четыре знака после точки и значение 12). Если Вы преимущественно складываете, делите и т.д в рамках одного инструмента то все ок, но насколько это извращенно? По сути это и есть идея того поста.

Иванов Андрей
Иванов Андрей

Это не будет быстрее double =) Зато будет больше места для ошибок. И голову надо под такое перестраивать, как под ассемблер.

Если вам не нужна точность за вашими знаками (среднее арифметическое между 0.0012 и 0.0015 при использовании long что даст?), double будет работать не хуже. Чтобы накопить в double ошибку в четвёртом знаке, надо несколько миллионов или десятков миллионов операций. Ошибка быстро накапливается, если используются значения из разных диапазонов -- цена какого-нибудь ИнтерРАО с лотом 100 тысяч и объём торгов за час. То есть, отличаться они должны на несколько порядков. Чем больше порядков различие, тем быстрее накапливается ошибка.

Maxim
Maxim

Поздно конечно пить боржомии... Напишу основной минус, который вижу для себя. Размер необходимой памяти для дабл в два раз больше. И для хранения в SQL и для ОЗУ.

Если с SQL не так все критично, можно докупить диски, то с памятью конечно больше неприятностей.

На текущий момент бэктестинг еле влезает в 8Гб ОЗУ. Больше ОЗУ добавить низя. Нужен новый сервер. Правда там же крутится SQL.

Крайне неохото разбивать процесс на этапы, что бы оставаться на том же железе. Пока еще не перевел все на decimal. Очень надеюсь, что все влезет.

Вначале пути тоже выбирал double или decimal. Выбрал double исключительно из-за ОЗУ.

Это все так... Лирика. Больше некому поплакаться, не поймут. 🙄

Mikhail Sukhov
Mikhail Sukhov

Maxim: Поздно конечно пить боржомии... Напишу основной минус, который вижу для себя. Размер необходимой памяти для дабл в два раз больше. И для хранения в SQL и для ОЗУ.

При тестировании на истории Sql не используется. Насчет занимаемой памяти я так и не понял, возросло с переходом на decimal или не возросло.

Maxim
Maxim

Данные для тестирования беру из SQL. Результаты тестирования сохраняю в SQL. SQL кушает порядка гигабайта.

Насчет того, насколько увеличилось потребление памяти при переходе на decimal сейчас сказать не могу. Все еще в процессе переделывания на новый лад. Как сделаю, напишу. Перенос всей программы на decimal то еще увлекательное мероприятие :)

Но чисто теоретически, double занимает 8байт, decimal 16 байт. 71млн значений цены для сбера занимали ~500Мб, теперь должны занимать ~1000Мб. Производных данных от цены меньше, но надо будет посмотреть сколько это все вместе будет теперь занимать.

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

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

Проехали :)

Основная мысль, что переход на decimal для меня тот еще геморой. А с другой стороны все устраивало на double.

anothar
anothar 作者

Maxim: Данные для тестирования беру из SQL. Наверно, для понимания моей ситуации стоит отметить, перед первым запуском одной стратегии мне необходимо обсчитать все вспомогательные данные за всю историю. При следующих запусках этого не нужно делать. B для этого Вам нужно забивать 7 гигов памяти? А не проще ли сохранять значения в sql или подгружать данные не все разом а постепенно пакетно? А вообще мне очень интересно как Вы умудряетесь забить 5-7 гигов памяти, ведь наверняка активно пользуете намного меньше.

Alexander
Alexander

Вот кстати сегодня тоже обнаружил что мой робот кушает 470мб оперативки. Это работа с 4мя квиками (где берутся ленты и стаканы из каждого квика), и по 3 стратегии на каждом квике.

Не знаю много это или нет, т.к. не помню сколько было.

MaximMM
Maxim
Maxim

Перевел на decimal. Потребление ОЗУ увеличилось с 5.6Гб до 6.6Гб. Пока что влажу в лимит.

Эх, все же придется задуматься об оптимизации алгоритмов.

Также замечу, что некоторые методы Math и DateTime не принимают Decimal. Лишнее преобразование в Double :)

Mikhail Sukhov
Mikhail Sukhov

Maxim: Перевел на decimal. Потребление ОЗУ увеличилось с 5.6Гб до 6.6Гб. Пока что влажу в лимит.

Все правильно, размер Trade увеличился на 1.18. Меня пугает размер 5.6. Это получается что-то порядка 136 282 616 тиков. Уверены, что такой сайз нужен в памяти? Больше выделение объемов памяти плохо сказывается на производительности GC. Плюс не совсем понятно, зачем такой объем держать. Чтобы оптимизировать стратегию необходимо небольшой участок данных. Далее, найденные значения применяются ко всему периоду, который подгружается частями и выгружается. Держать больший период в памяти имеет смысл, если на нем идет частая проверка данных. Если это как раз оптимизация - то это уже плохо с точки зрения поиска на истории, так как идет подгонка параметров.

Maxim
Maxim

Алгоритм приблизительно такой: пробегаем по всем историческим данным и находим несколько производных значений от цены и объема, например, для каждой секунды. Конечно, можно сделать разбиение на части и уложится даже в 10Мб.

Но зачем усложнять жизнь, если все можно одним куском в память запихнуть?

В случаи не бектестинга, а робота, это надо сделать только один раз. Просчитать все данные. После этого прога работает в 32 битном режиме и 7 гиов ей не надо.

anothar
anothar 作者

Maxim: Алгоритм приблизительно такой: пробегаем по всем историческим данным и находим несколько производных значений от цены и объема, например, для каждой секунды. Конечно, можно сделать разбиение на части и уложится даже в 10Мб.

Но зачем усложнять жизнь, если все можно одним куском в память запихнуть?

В случаи не бектестинга, а робота, это надо сделать только один раз. Просчитать все данные. После этого прога работает в 32 битном режиме и 7 гиов ей не надо. Если у Вас несколько таких роботов будет, то сколько памяти потребуется? И нет тут никакого особого усложнения жизни - сделаете еще одного робота, и придется все равно ее себе "усложнять". И дело не в double-decimal, а в проектировании программы: если так делать, то Вам вскорости никакой памяти не хватит.

Maxim
Maxim

Все верно пишите. Только как раз не хотелось заниматься разбитием процесса на части (загрузки и выгрузки частями).

Если касательно торгового робота (где я использую S#), то 5.6 гигов необходимо один раз, при первом старте стратегии, что бы просчитать все данные которые, были с начала истории по текущий момент.

Если касательно бэктестинга, то там я S# не использую. Но там много памяти надо постоянно :)

Считаю, что если алгоритм с небольшим количеством параметров (у меня ~7) дает хорошие результаты на 10 годах истории, то с большой вероятностью он будет хорошим и в ближайшие пол года. То есть подгонка в некоторых случаях имеет право на жизнь. Вы не согласны?

Mikhail Sukhov
Mikhail Sukhov

Maxim: Все верно пишите. Только как раз не хотелось заниматься разбитием процесса на части (загрузки и выгрузки частями).

Так сделано сейчас в HistoryEmulationTrader. Вы еще что-то на части разбиваете?

Maxim: Если касательно торгового робота (где я использую S#), то 5.6 гигов необходимо один раз, при первом старте стратегии, что бы просчитать все данные которые, были с начала истории по текущий момент.

А вы пробовали в конце сессии писать результирующие данные, чтобы на следующий день не делать пересчет для 6 гигов тиков?

Maxim: Если касательно бэктестинга, то там я S# не использую. Но там много памяти надо постоянно :)

Давайте разбираться с конкретикой. На какой версии пробовали, сколько занимало, какой период выбирали, сколько тестировалось стратегий и инструментов.

Maxim: Считаю, что если алгоритм с небольшим количеством параметров (у меня ~7) дает хорошие результаты на 10 годах истории, то с большой вероятностью он будет хорошим и в ближайшие пол года. То есть подгонка в некоторых случаях имеет право на жизнь. Вы не согласны?

Если говорить о тех анализе, я его вообще считаю профанацией.😂 А если ближе к деталям, то у нас рынок меняется каждые 2 года и кризисы каждые 5 лет🙂 . С этими факторами российского стиля жизни сложно найти инструменту, который бы был стабилет на стратегии в течении 10 лет. Так что да, я не верю, что алго даст профит хотя бы на ближайшие пол года. Я думаю это плохое тестирование, которые в реальности не показало бы на прошедших 10 годах тот показатель, что был получен на истории.

Вам то удалось самому найти такой алго?

Maxim
Maxim

Mikhail Sukhov: Так сделано сейчас в HistoryEmulationTrader. Вы еще что-то на части разбиваете? Для бектестинга не использую S#. Все свое :)

Mikhail Sukhov: А вы пробовали в конце сессии писать результирующие данные, чтобы на следующий день не делать пересчет для 6 гигов тиков?

Это надо сделать самый первый раз при запуске одной стратегии. Потом эти данные считаются и записываются онлайн. И стока памяти не надо.

Maxim: Давайте разбираться с конкретикой. На какой версии пробовали, сколько занимало, какой период выбирали, сколько тестировалось стратегий и инструментов.

Я не использую S# для бектестинга )

Maxim: Вам то удалось самому найти такой алго?

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

Maxim
Maxim

Mikhail Sukhov: Если говорить о тех анализе, я его вообще считаю профанацией.😂

А это, мне кажется, надо сделать слоганом S# :)

Mikhail Sukhov
Mikhail Sukhov

Maxim:

Mikhail Sukhov: Так сделано сейчас в HistoryEmulationTrader. Вы еще что-то на части разбиваете? Для бектестинга не использую S#. Все свое :)

Значит нужно сделать переход на S# и решить проблему с потреблением памяти.

Mikhail Sukhov
Mikhail Sukhov

Maxim:

Mikhail Sukhov: Если говорить о тех анализе, я его вообще считаю профанацией.😂

А это, мне кажется, надо сделать слоганом S# :)

Это мое личное мнение. А так, на S# чего только не делалось (не мной). В том числе и тех анализ.

Maxim
Maxim

Михаил, может сталкивались со следующим?

При выполнении участка кода:


ring _query = "SELECT HistoryId, Money, Paper FROM {0} WHERE Date = (SELECT MAX(DATE) FROM {0} )";
_query = String.Format(_query, sqlTableName);
 SqlCommand _sqlCommand = new SqlCommand(_query, _sqlConn);

using (SqlDataReader _dataReader = _sqlCommand.ExecuteReader())                    
	while (_dataReader.Read())
	{
		HistoryId = _dataReader.GetInt64(0);
		Money = _dataReader.GetDecimal(1);
		Paper = _dataReader.GetInt32(2);
	}

Получается исключение:


A first chance exception of type 'System.DivideByZeroException' occurred in mscorlib.dll
System.Transactions Critical: 0 : <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>http://msdn.microsoft.com/TraceCodes/System/ActivityTracing/2004/07/Reliability/Exception/Unhandled</TraceIdentifier><Description>Unhandled exception</Description><AppDomain>Mts.vshost.exe</AppDomain><Exception><ExceptionType>System.DivideByZeroException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>Attempted to divide by zero.</Message><StackTrace>   at System.Decimal.FCallDivide(Decimal&amp;amp; d1, Decimal&amp;amp; d2)
   at System.Decimal.op_Division(Decimal d1, Decimal d2)
   at Goricap.Mts.BuySell.&amp;lt;initProfitTimer&amp;gt;b__c(Object data) in G:\C\Development\MTS_Decimal\Programm\BuySellClass.cs:line 333
   at System.Threading._TimerCallback.TimerCallback_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading._TimerCallback.PerformTimerCallback(Object state)</StackTrace><ExceptionString>System.DivideByZeroException: Attempted to divide by zero.
   at System.Decimal.FCallDivide(Decimal&amp;amp; d1, Decimal&amp;amp; d2)
   at System.Decimal.op_Division(Decimal d1, Decimal d2)
   at Goricap.Mts.BuySell.&amp;lt;initProfitTimer&amp;gt;b__c(Object data) in G:\C\Development\MTS_Decimal\Programm\BuySellClass.cs:line 333
   at System.Threading._TimerCallback.TimerCallback_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading._TimerCallback.PerformTimerCallback(Object state)</ExceptionString></Exception></TraceRecord>

Исключения появляется при исполнении using (SqlDataReader _dataReader = _sqlCommand.ExecuteReader())

Раньше было Double, все работало. Ошибка появилась после перехода на Decimal. Буду благодарен помощи. Гугление как то не дает результатов.

И при чем тут деление? Не понимаю :)

Mikhail Sukhov
Mikhail Sukhov

Maxim: И при чем тут деление? Не понимаю :)

Подозреваю, что кусов кода и текст ошибки не связаны между собой.

Maxim
Maxim

Mikhail Sukhov:

Maxim: И при чем тут деление? Не понимаю :)

Подозреваю, что кусов кода и текст ошибки не связаны между собой.

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

Исключение, которое я написал, появляется в Output окне.