Alexander:
А ведь ни цены, ни объём не могут быть отрицательными.
Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?
А зачем ulong?! =) Я думаю, что даже у американцев не бывает таких сделок, чтобы они в int не поместились =) Аккумуляторы вы можете использовать такие, какие посчитаете нужным.
Андрей Ефимов:
Заинтересовал вопрос, почему параметры в 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 знаков).
Иванов Андрей:
Зачем?! =) О какой точности идёт речь?! Точность 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, потому что первый не нужно округлять.
И да, при современных-то компьютерам. У меня весь рынок можно уместить виртуально на персональный ПС. А вот заявки все равно скользят.
Alexander:
А ведь ни цены, ни объём не могут быть отрицательными.
Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?
А зачем ulong?! =) Я думаю, что даже у американцев не бывает таких сделок, чтобы они в int не поместились =) Аккумуляторы вы можете использовать такие, какие посчитаете нужным.
Андрей Ефимов:
Заинтересовал вопрос, почему параметры в 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 вам в этом случае не поможет.
Иванов Андрей:
Зачем?! =) О какой точности идёт речь?! Точность 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, потому что первый не нужно округлять.
И да, при современных-то компьютерам. У меня весь рынок можно уместить виртуально на персональный ПС. А вот заявки все равно скользят.
Файлы тоже медленно работают, по сравнению с быстрым процессором =)
Я не люблю рассуждения про современные компьютеры. Это странная причина для того, чтобы делать плохо, когда можно сделать хорошо и без напряжения.
Иванов Андрей:
Не хотите разбираться с представлением чисел на компьютере, просто попробуйте. У вас число в примере четыре знака, представляя его в виде 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();
Alexander Mukhanchikov:
А ведь ни цены, ни объём не могут быть отрицательными.
Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?
Зато их разница - может
Текущая чистая позиция - разница объёмов, может быть отрицательная
"% изменения от закрытия"
Вход. чист. поз.
Разница цены последней к оценке предыдущей сессиии
Разница цены последней к предыдущей сессиии
и т.д.
Половина парметров, которыми я пользуюсь из КВИК, могут быть отрицательными
Alexander Mukhanchikov:
А ведь ни цены, ни объём не могут быть отрицательными.
Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?
Зато их разница - может
Текущая чистая позиция - разница объёмов, может быть отрицательная
"% изменения от закрытия"
Вход. чист. поз.
Разница цены последней к оценке предыдущей сессиии
Разница цены последней к предыдущей сессиии
и т.д.
Половина парметров, которыми я пользуюсь из КВИК, могут быть отрицательными
Поставьте не 0.1, а 0.01, посмотрите, что получится.
Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double.
Это особенности представления вещественных чисел на компьютере, плата за скорость.
Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.
Иванов Андрей:
Поставьте не 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)-но это как я понял.
Иванов Андрей:
Поставьте не 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, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.
Ее тяжесть начала проявляться на бэк тестинге. Если в реальном времени больше времени занимает получение данных, то в виртуальном времени - торговая часть.
Иванов Андрей:
Поставьте не 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 не принесёт денег, зато принесёт хлопоты.
Иванов Андрей:
На 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 не поможет.
Иванов Андрей:
На 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;
}
Вот комментарий по производительности:Decimal perfomance
Если ему верить, то у decimal производительность не ахти. А что если внутри представлять все ввиде целого Int64? Ну а наружу-без разницы. ТО есть по сути будет такая структура: фиксированное число знаков после точки, которое зависит только от инструмента и значение ввиде Int64(например 0.0012-это четыре знака после точки и значение 12). Если Вы преимущественно складываете, делите и т.д в рамках одного инструмента то все ок, но насколько это извращенно? По сути это и есть идея того поста.
Это не будет быстрее double =) Зато будет больше места для ошибок. И голову надо под такое перестраивать, как под ассемблер.
Если вам не нужна точность за вашими знаками (среднее арифметическое между 0.0012 и 0.0015 при использовании long что даст?), double будет работать не хуже. Чтобы накопить в double ошибку в четвёртом знаке, надо несколько миллионов или десятков миллионов операций. Ошибка быстро накапливается, если используются значения из разных диапазонов -- цена какого-нибудь ИнтерРАО с лотом 100 тысяч и объём торгов за час. То есть, отличаться они должны на несколько порядков. Чем больше порядков различие, тем быстрее накапливается ошибка.
Поздно конечно пить боржомии...
Напишу основной минус, который вижу для себя.
Размер необходимой памяти для дабл в два раз больше.
И для хранения в SQL и для ОЗУ.
Если с SQL не так все критично, можно докупить диски,
то с памятью конечно больше неприятностей.
На текущий момент бэктестинг еле влезает в 8Гб ОЗУ.
Больше ОЗУ добавить низя. Нужен новый сервер.
Правда там же крутится SQL.
Крайне неохото разбивать процесс на этапы, что бы
оставаться на том же железе. Пока еще не перевел
все на decimal. Очень надеюсь, что все влезет.
Вначале пути тоже выбирал double или decimal.
Выбрал double исключительно из-за ОЗУ.
Это все так... Лирика.
Больше некому поплакаться, не поймут. 🙄
Maxim:
Поздно конечно пить боржомии...
Напишу основной минус, который вижу для себя.
Размер необходимой памяти для дабл в два раз больше.
И для хранения в SQL и для ОЗУ.
При тестировании на истории Sql не используется. Насчет занимаемой памяти я так и не понял, возросло с переходом на decimal или не возросло.
Данные для тестирования беру из SQL.
Результаты тестирования сохраняю в SQL.
SQL кушает порядка гигабайта.
Насчет того, насколько увеличилось потребление памяти
при переходе на decimal сейчас сказать не могу. Все еще
в процессе переделывания на новый лад. Как сделаю, напишу.
Перенос всей программы на decimal то еще увлекательное мероприятие :)
Но чисто теоретически, double занимает 8байт, decimal 16 байт.
71млн значений цены для сбера занимали ~500Мб, теперь должны занимать ~1000Мб.
Производных данных от цены меньше, но надо будет посмотреть сколько это все вместе
будет теперь занимать.
Наверно, для понимания моей ситуации стоит отметить, перед первым
запуском одной стратегии мне необходимо обсчитать все вспомогательные данные
за всю историю. При следующих запусках этого не нужно делать.
Пока все это писал, понял, что Вы скорей всего не поймете
моей ситуации, так как для этого надо более детально описывать
структуру моего торгового робота.
Проехали :)
Основная мысль, что переход на decimal для меня тот еще геморой.
А с другой стороны все устраивало на double.
Maxim:
Данные для тестирования беру из SQL.
Наверно, для понимания моей ситуации стоит отметить, перед первым
запуском одной стратегии мне необходимо обсчитать все вспомогательные данные
за всю историю. При следующих запусках этого не нужно делать.
B для этого Вам нужно забивать 7 гигов памяти? А не проще ли сохранять значения в sql или подгружать данные не все разом а постепенно пакетно? А вообще мне очень интересно как Вы умудряетесь забить 5-7 гигов памяти, ведь наверняка активно пользуете намного меньше.
Вот кстати сегодня тоже обнаружил что мой робот кушает 470мб оперативки.
Это работа с 4мя квиками (где берутся ленты и стаканы из каждого квика), и по 3 стратегии на каждом квике.
Не знаю много это или нет, т.к. не помню сколько было.
Maxim:
Перевел на decimal.
Потребление ОЗУ увеличилось с 5.6Гб до 6.6Гб.
Пока что влажу в лимит.
Все правильно, размер Trade увеличился на 1.18. Меня пугает размер 5.6. Это получается что-то порядка 136 282 616 тиков. Уверены, что такой сайз нужен в памяти? Больше выделение объемов памяти плохо сказывается на производительности GC. Плюс не совсем понятно, зачем такой объем держать. Чтобы оптимизировать стратегию необходимо небольшой участок данных. Далее, найденные значения применяются ко всему периоду, который подгружается частями и выгружается. Держать больший период в памяти имеет смысл, если на нем идет частая проверка данных. Если это как раз оптимизация - то это уже плохо с точки зрения поиска на истории, так как идет подгонка параметров.
Алгоритм приблизительно такой:
пробегаем по всем историческим данным и находим несколько производных значений от цены и объема, например, для каждой секунды.
Конечно, можно сделать разбиение на части и уложится даже в 10Мб.
Но зачем усложнять жизнь, если все можно одним куском в память запихнуть?
В случаи не бектестинга, а робота, это надо сделать только один раз.
Просчитать все данные. После этого прога работает в 32 битном режиме и 7 гиов
ей не надо.
Maxim:
Алгоритм приблизительно такой:
пробегаем по всем историческим данным и находим несколько производных значений от цены и объема, например, для каждой секунды.
Конечно, можно сделать разбиение на части и уложится даже в 10Мб.
Но зачем усложнять жизнь, если все можно одним куском в память запихнуть?
В случаи не бектестинга, а робота, это надо сделать только один раз.
Просчитать все данные. После этого прога работает в 32 битном режиме и 7 гиов
ей не надо.
Если у Вас несколько таких роботов будет, то сколько памяти потребуется? И нет тут никакого особого усложнения жизни - сделаете еще одного робота, и придется все равно ее себе "усложнять". И дело не в double-decimal, а в проектировании программы: если так делать, то Вам вскорости никакой памяти не хватит.
Все верно пишите.
Только как раз не хотелось заниматься разбитием процесса на части (загрузки и выгрузки частями).
Если касательно торгового робота (где я использую S#), то 5.6 гигов необходимо один раз, при первом старте стратегии, что бы просчитать все данные которые, были с начала истории по текущий момент.
Если касательно бэктестинга, то там я S# не использую. Но там много памяти надо постоянно :)
Считаю, что если алгоритм с небольшим количеством параметров (у меня ~7) дает хорошие результаты на 10 годах истории, то с большой вероятностью он будет хорошим и в ближайшие пол года. То есть подгонка в некоторых случаях имеет право на жизнь.
Вы не согласны?
Maxim:
Все верно пишите.
Только как раз не хотелось заниматься разбитием процесса на части (загрузки и выгрузки частями).
Так сделано сейчас в HistoryEmulationTrader. Вы еще что-то на части разбиваете?
Maxim:
Если касательно торгового робота (где я использую S#), то 5.6 гигов необходимо один раз, при первом старте стратегии, что бы просчитать все данные которые, были с начала истории по текущий момент.
А вы пробовали в конце сессии писать результирующие данные, чтобы на следующий день не делать пересчет для 6 гигов тиков?
Maxim:
Если касательно бэктестинга, то там я S# не использую. Но там много памяти надо постоянно :)
Давайте разбираться с конкретикой. На какой версии пробовали, сколько занимало, какой период выбирали, сколько тестировалось стратегий и инструментов.
Maxim:
Считаю, что если алгоритм с небольшим количеством параметров (у меня ~7) дает хорошие результаты на 10 годах истории, то с большой вероятностью он будет хорошим и в ближайшие пол года. То есть подгонка в некоторых случаях имеет право на жизнь.
Вы не согласны?
Если говорить о тех анализе, я его вообще считаю профанацией.😂 А если ближе к деталям, то у нас рынок меняется каждые 2 года и кризисы каждые 5 лет🙂 . С этими факторами российского стиля жизни сложно найти инструменту, который бы был стабилет на стратегии в течении 10 лет. Так что да, я не верю, что алго даст профит хотя бы на ближайшие пол года. Я думаю это плохое тестирование, которые в реальности не показало бы на прошедших 10 годах тот показатель, что был получен на истории.
Mikhail Sukhov:
Так сделано сейчас в HistoryEmulationTrader. Вы еще что-то на части разбиваете?
Для бектестинга не использую S#. Все свое :)
Mikhail Sukhov:
А вы пробовали в конце сессии писать результирующие данные, чтобы на следующий день не делать пересчет для 6 гигов тиков?
Это надо сделать самый первый раз при запуске одной стратегии.
Потом эти данные считаются и записываются онлайн. И стока памяти не надо.
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; d1, Decimal&amp; d2)
at System.Decimal.op_Division(Decimal d1, Decimal d2)
at Goricap.Mts.BuySell.&lt;initProfitTimer&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; d1, Decimal&amp; d2)
at System.Decimal.op_Division(Decimal d1, Decimal d2)
at Goricap.Mts.BuySell.&lt;initProfitTimer&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.
Буду благодарен помощи.
Гугление как то не дает результатов.
Comments (40)
Login or Create account, Log in or register to leave a comment
Избежать лишней конвертации при интеграции с другими системами.
В итоге, после 3-ем месяцев осмысления, я понял - переводу на decimal быть. Сейчас это сделано в отдельной ветке S#, и появиться скорее всего, в 3.1.
🙂 Замечательно-все будет очень точно)))
А ведь ни цены, ни объём не могут быть отрицательными. Почему бы не использовать для цен тот же самый Decimal, а для объёмов ulong?
Потому что не CLS compliant.
Точность у decimal выше, а ёмкость целой части такая же =) Сказки про использование decimal как решение под точные расчёты для детского сада. Потому что если бездумно делить, ошибка будет огромная и с decimal. Использовать decimal обычно неразумно -- он медленнее, больше ест памяти и не даёт ничего, кроме дополнительного порядка или двух ёмкости. Дополнительная ёмкость имеет смысл для агрегирования, ну так и используйте decimal в агрегировании за безумные интервалы. Хотя я бы в этой ситуации всё равно использовал double под промежуточные расчёты с агрегированием в ulong (20 знаков).
Мне нравиться вот этот пост http://www.rsdn.ru/forum/cpp/2640596.1.aspx Скажем, если бы я писал графическое приложение, где бы вычислял координаты - да тут точность не так важна. В случае с торговым приложение - очень важно чтобы цена были точной. Как минимум, чтобы не получить ошибку при регистрации заявок. Поэтому все всегда приходится вызывать ShrinkPrice чтобы гарантировать правильность подачи заявок.
С чем я сам столкнулся. 1 - когда я восстанавливаю стаканы из файла. Там я как раз умножаю на минимальный шаг + сдвиг от предыдущей котировкки. В итоге цена котировки 100.99999 вместо 101. 2 - когда я группирую стакан по ценовым уровня. Так же приходится округлять каждую строчку.
Если цена вычисляется через математически формулы, то ей верить без округления нельзя. В итоге ShrinkPrice кушает как раз тот прирост, что дает double перед decimal. И еще чуть больше. Получается обратная ситуация - decimal быстрее перед double, потому что первый не нужно округлять.
И да, при современных-то компьютерам. У меня весь рынок можно уместить виртуально на персональный ПС. А вот заявки все равно скользят.
decimal нужен там, где у вас большая целая часть и большая дробная. Попробуйте привести пример, где оно надо. Уверен, что decimal вам в этом случае не поможет.
Про пост на RSDN. Человек по вашей ссылке возмущается тому же, чему возмущаюсь я. Там кто-то написал, что double сравнивать нельзя, вы пишете, что decimal вам даст большую точность.
Дело то не в максимальной размерности и точности после запятой. А в точности результирующего значения. Ему верить нельзя.
Да легко:
Зато их разница - может Текущая чистая позиция - разница объёмов, может быть отрицательная "% изменения от закрытия" Вход. чист. поз.
Разница цены последней к оценке предыдущей сессиии Разница цены последней к предыдущей сессиии и т.д.
Половина парметров, которыми я пользуюсь из КВИК, могут быть отрицательными
И? Мы и используем decimal поэтому.
Поставьте не 0.1, а 0.01, посмотрите, что получится. Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double. Это особенности представления вещественных чисел на компьютере, плата за скорость.
Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.
При торговле в реальном времени что double что decimal не несет никакой разницы в производительности. Но мне приходится каждый раз делать округление, так как я не могу гарантировать в коде, что double будет сформирован правильно. То, что при 0.01 мой пример работает, а при 0.1 не работает - это еще хуже, чем если бы не работало всегда. Я не могу однозначность сказать - все будет работать. Поэтому лишняя перестраховка в виде выравнивания цены.
Если я вычисляю сред арифметическое, то я точно знаю, что нужно округлять. И я точно знаю, что код, подобный моему примеру, не требует округления. Главное для меня, точно знать как будет работать. Я уже довольно много натыкался на грабли, когда код не работал банально из-за неточности double. Часами выискивал проблему, которая может и вообще сегодня не проявиться. А такая бага самая худшая, когда то работает, то нет (ваш пример с ИИС).
Ее тяжесть начала проявляться на бэк тестинге. Если в реальном времени больше времени занимает получение данных, то в виртуальном времени - торговая часть.
Лишняя перестраховка в виде выравнивания цены не хуже лишней перестраховки в виде decimal =)
На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд.
Уверен, что decimal не принесёт денег, зато принесёт хлопоты.
Я уже переделал до 3.0 еще. Но это не выносил в публичную ветку. Получилось быстрее, потому что из своей стратегии я повыкидывал Shrink.
Когда вам надо суммировать операции по счетам клиентов, то выбора нет, только decimal. Потому что представить в double 0.1 (10 копеек), например, невозможно. Когда у вас есть арифметика (считаете проценты, например), то double обычно точнее и всегда значительно быстрее. В случае с роботами суммирование операций это вспомогательные вычисления, основные как раз разнообразная арифметика. Мне так кажется =)
По сути, это вопрос религии или требования к производительности -- сложив три раза 1/3 в decimal получишь 0.9999999999, а в double 1.0, но сложив 10 раз 0.1 для decimal будет 1.0 и 0.999999999 для double. То есть, округлять надо независимо от выбора представления и результат будет приемлемый в любом случае. А если бездумно делить, повторюсь в который раз, decimal не поможет.
Исходя из описания и именования, я бы ShrinkPrice сделал вот таким. Проверить мне его не на чем, но не вижу причин, почему бы ему не работать в несколько десятков раз быстрее.
Было
Стало
Вот комментарий по производительности:Decimal perfomance Если ему верить, то у decimal производительность не ахти. А что если внутри представлять все ввиде целого Int64? Ну а наружу-без разницы. ТО есть по сути будет такая структура: фиксированное число знаков после точки, которое зависит только от инструмента и значение ввиде Int64(например 0.0012-это четыре знака после точки и значение 12). Если Вы преимущественно складываете, делите и т.д в рамках одного инструмента то все ок, но насколько это извращенно? По сути это и есть идея того поста.
Это не будет быстрее double =) Зато будет больше места для ошибок. И голову надо под такое перестраивать, как под ассемблер.
Если вам не нужна точность за вашими знаками (среднее арифметическое между 0.0012 и 0.0015 при использовании long что даст?), double будет работать не хуже. Чтобы накопить в double ошибку в четвёртом знаке, надо несколько миллионов или десятков миллионов операций. Ошибка быстро накапливается, если используются значения из разных диапазонов -- цена какого-нибудь ИнтерРАО с лотом 100 тысяч и объём торгов за час. То есть, отличаться они должны на несколько порядков. Чем больше порядков различие, тем быстрее накапливается ошибка.
Поздно конечно пить боржомии... Напишу основной минус, который вижу для себя. Размер необходимой памяти для дабл в два раз больше. И для хранения в SQL и для ОЗУ.
Если с SQL не так все критично, можно докупить диски, то с памятью конечно больше неприятностей.
На текущий момент бэктестинг еле влезает в 8Гб ОЗУ. Больше ОЗУ добавить низя. Нужен новый сервер. Правда там же крутится SQL.
Крайне неохото разбивать процесс на этапы, что бы оставаться на том же железе. Пока еще не перевел все на decimal. Очень надеюсь, что все влезет.
Вначале пути тоже выбирал double или decimal. Выбрал double исключительно из-за ОЗУ.
Это все так... Лирика. Больше некому поплакаться, не поймут. 🙄
При тестировании на истории Sql не используется. Насчет занимаемой памяти я так и не понял, возросло с переходом на decimal или не возросло.
Данные для тестирования беру из SQL. Результаты тестирования сохраняю в SQL. SQL кушает порядка гигабайта.
Насчет того, насколько увеличилось потребление памяти при переходе на decimal сейчас сказать не могу. Все еще в процессе переделывания на новый лад. Как сделаю, напишу. Перенос всей программы на decimal то еще увлекательное мероприятие :)
Но чисто теоретически, double занимает 8байт, decimal 16 байт. 71млн значений цены для сбера занимали ~500Мб, теперь должны занимать ~1000Мб. Производных данных от цены меньше, но надо будет посмотреть сколько это все вместе будет теперь занимать.
Наверно, для понимания моей ситуации стоит отметить, перед первым запуском одной стратегии мне необходимо обсчитать все вспомогательные данные за всю историю. При следующих запусках этого не нужно делать.
Пока все это писал, понял, что Вы скорей всего не поймете моей ситуации, так как для этого надо более детально описывать структуру моего торгового робота.
Проехали :)
Основная мысль, что переход на decimal для меня тот еще геморой. А с другой стороны все устраивало на double.
Вот кстати сегодня тоже обнаружил что мой робот кушает 470мб оперативки. Это работа с 4мя квиками (где берутся ленты и стаканы из каждого квика), и по 3 стратегии на каждом квике.
Не знаю много это или нет, т.к. не помню сколько было.
Перевел на decimal. Потребление ОЗУ увеличилось с 5.6Гб до 6.6Гб. Пока что влажу в лимит.
Эх, все же придется задуматься об оптимизации алгоритмов.
Также замечу, что некоторые методы Math и DateTime не принимают Decimal. Лишнее преобразование в Double :)
Все правильно, размер Trade увеличился на 1.18. Меня пугает размер 5.6. Это получается что-то порядка 136 282 616 тиков. Уверены, что такой сайз нужен в памяти? Больше выделение объемов памяти плохо сказывается на производительности GC. Плюс не совсем понятно, зачем такой объем держать. Чтобы оптимизировать стратегию необходимо небольшой участок данных. Далее, найденные значения применяются ко всему периоду, который подгружается частями и выгружается. Держать больший период в памяти имеет смысл, если на нем идет частая проверка данных. Если это как раз оптимизация - то это уже плохо с точки зрения поиска на истории, так как идет подгонка параметров.
Алгоритм приблизительно такой: пробегаем по всем историческим данным и находим несколько производных значений от цены и объема, например, для каждой секунды. Конечно, можно сделать разбиение на части и уложится даже в 10Мб.
Но зачем усложнять жизнь, если все можно одним куском в память запихнуть?
В случаи не бектестинга, а робота, это надо сделать только один раз. Просчитать все данные. После этого прога работает в 32 битном режиме и 7 гиов ей не надо.
Все верно пишите. Только как раз не хотелось заниматься разбитием процесса на части (загрузки и выгрузки частями).
Если касательно торгового робота (где я использую S#), то 5.6 гигов необходимо один раз, при первом старте стратегии, что бы просчитать все данные которые, были с начала истории по текущий момент.
Если касательно бэктестинга, то там я S# не использую. Но там много памяти надо постоянно :)
Считаю, что если алгоритм с небольшим количеством параметров (у меня ~7) дает хорошие результаты на 10 годах истории, то с большой вероятностью он будет хорошим и в ближайшие пол года. То есть подгонка в некоторых случаях имеет право на жизнь. Вы не согласны?
Так сделано сейчас в HistoryEmulationTrader. Вы еще что-то на части разбиваете?
А вы пробовали в конце сессии писать результирующие данные, чтобы на следующий день не делать пересчет для 6 гигов тиков?
Давайте разбираться с конкретикой. На какой версии пробовали, сколько занимало, какой период выбирали, сколько тестировалось стратегий и инструментов.
Если говорить о тех анализе, я его вообще считаю профанацией.😂 А если ближе к деталям, то у нас рынок меняется каждые 2 года и кризисы каждые 5 лет🙂 . С этими факторами российского стиля жизни сложно найти инструменту, который бы был стабилет на стратегии в течении 10 лет. Так что да, я не верю, что алго даст профит хотя бы на ближайшие пол года. Я думаю это плохое тестирование, которые в реальности не показало бы на прошедших 10 годах тот показатель, что был получен на истории.
Вам то удалось самому найти такой алго?
Это надо сделать самый первый раз при запуске одной стратегии. Потом эти данные считаются и записываются онлайн. И стока памяти не надо.
Я не использую S# для бектестинга )
Торговый робот работает с нового года. До этого времени шел поиск золотого грааля :) Но постоянно возникают косяки, которые мешают оценить результативность алго. (Стоит уточнить, что в среднем одна-две сделки в неделю) Так что через месяц другой может смогу дать Вам ответ.
А это, мне кажется, надо сделать слоганом S# :)
Значит нужно сделать переход на S# и решить проблему с потреблением памяти.
Это мое личное мнение. А так, на S# чего только не делалось (не мной). В том числе и тех анализ.
Михаил, может сталкивались со следующим?
При выполнении участка кода:
Получается исключение:
Исключения появляется при исполнении using (SqlDataReader _dataReader = _sqlCommand.ExecuteReader())
Раньше было Double, все работало. Ошибка появилась после перехода на Decimal. Буду благодарен помощи. Гугление как то не дает результатов.
И при чем тут деление? Не понимаю :)
Подозреваю, что кусов кода и текст ошибки не связаны между собой.
Я тоже это подозреваю. Потому что программа просто заканчивается. Никаких привычных исключений в дебагере не появляется.
Исключение, которое я написал, появляется в Output окне.