неплохая статья Анатолия Уткина,
перепост с
http://anatoly-utkin.livejournal.com/7040.html
О влиянии плечей на неопытных трейдеров23 февраля, 11:51
Для большинства не слишком искушенных трейдеров ценовые движения рынка представляют собой полностью случайное, броуновское движение. Почему это так, я попытался раскрыть здесь:
http://www.2stocks.ru/utkin/?p=319. Если бы не было комиссий, то динамика счета такого трейдера также была бы броуновской, и он жил бы долго. При наличии комиссий происходит плавное сползание счета, то есть время на обучение ограничено. Но есть еще одна, очень существенная опасность для счета “броуновского” трейдера–это взятие плечей, и в настоящей статье я бы хотел пояснить существо этой проблемы.
ээДля моделирования ситуации возьмем случай вкладывания постоянной доли капитала. Тогда капитал после очередной сделки, s_next, выражается через капитал до этой сделки, s как s_next=s*(1+f*x/100), где f–доля вложенных средств, x–результат сделки на один лот. В этой простой формуле уже содержится ответ. Во-первых, следующий капитал–это предыдущий УМНОЖИТЬ на что-то. Поэтому если мы умножимся на ноль, то дальше капитал всегда будет нулем. Таким образом, критическим является вопрос о том, на что умножается капитал, то есть о величине (1+f*x/100). Ясно, что x не может быть слишком большим, по крайней мере, оно ограничено размером -100% (для лонга, по крайней мере). Поэтому для любых долей f, меньших единицы, множитель (1+f*x/100) всегда больше нуля, то есть на ноль при отсутствии плеча мы не умножимся никогда. Напротив, при f больших единицы (взятии плеча) существуют такие x, при которых множитель равен нулю. К примеру, при f=5 достаточно x=-20, чтобы капитал занулился. В общем-то, это очевидная вещь, но очевидные вещи надо повторять.
Рассмотрим теперь проблему количественно. Смоделируем динамику капитала трейдера при помощи генератора случайных чисел. Для этого в формулу s_next=s*(1+f*x/100) будем подставлять в качестве x случайные числа. Здесь достаточно тонким является вопрос о выборе распределения для x. Очевидный, казалось бы, выбор распределения с нулевым средним (например, нормального) не будет правильным, поскольку при этом появится постоянное смещение счета вниз из-за геометрических эффектов. Не вдаваясь в детали, скажу, что правильным распределением будет, например, логнормальное. Запишу сразу конечную формулу для x, которая использовалась: x=exp(sigma*z)-1+M, где sigma–параметр, определяющий волатильность цены инструмента, z–случайная величина, распределенная нормально с нулевым средним и единичным СКО, M–параметр качества трейдера, для “броуновских” трейдеров он равен комиссии, для опытных он положителен.
Перейду к результатам моделирования. Прежде всего, рассмотрим случай отсутствия комиссий и плечей, то есть M=0, f=1. В этом случае счет показывает обычную броуновскую динамику:
Отсюда видно, что шансы продержаться неплохи, аж за 10000 сделок ничего особо не происходит (sigma равнялась 1%, что соответствует интрадэйным сделкам). Замечу, что по одной реализации выводы делать не стоит, поэтому в конце я привожу код VBA, по которому каждый может повторить это моделирование. Далее, введем комиссию в размере 0.03%. В общем-то, очевидно, что будет. Счет уменьшится на порядок за число сделок порядка 1/0.03%=около 3000 сделок:
Комиссии убивают счет и это путь неплечевых “броуновских” трейдеров. Вывод такой, что у “броуновского” неплечевого трейдера есть около 0.5/комиссия сделок, пока его счет уменьшится в два раза.
Теперь самое интересное–влияние плечей. Выберем в качестве f тройку–это соответствует вложению 300% капитала в каждую сделку. А комиссию занулим–без комиссий! Я привожу достаточно типичную картинку:
Это так похоже на работу многих трейдеров (смотри, например, на comon.ru), не правда ли? Рассмотрев множество таких картинок, можно прийти к выводу, что использование плечей СИСТЕМАТИЧЕСКИ убивает счет. То, что было нейтральным при отсутствии плеча (первый рисунок), становится убийственным при его присутствии. Качественные причины этого приведены во втором абзаце статьи. Ну, и наконец, динамика счет при наличии комиссии 0.03% и f=3:
Тут, как говорится, без комментариев. 500 сделок–и полсчета нет, 1000 сделок–нет счета полностью.
Выводы:
1) Не умеешь–не торгуй!
2) Если не знаешь, умеешь или нет, торгуй без плечей и пореже–будет больше времени научиться.
3) Единственный способ заработать–это сделать M положительным (то есть, как говорят, торговать с положительным матожиданием). Но даже при этом большое плечо все равно убьет счет–желающие могут убедиться, поэкспериментировав с кодом.
””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””
ээНиже прикладываю код VBA. Он выдает эквити в третий столбец того листа Excel, к которому он приложен. Последовательность действий:
1) Открываем новую книгу Excel (пусть она называется book1)
2) Жмем alt+F11–открываем Microsoft Visual Basic
3) Слева выбираем VBAProject (book1)–Microsoft Excel Objects–Sheet1. Жмем туда правой кнопкой мыши и выбираем View Code
4) В открывшуюся вкладку копируем код.
5) Жмем на сохранить
6) Возвращаемся в excel, жмем alt+F8 и выбираем макрос Sheet1.Leverage. Запускаем его–три первых столбца листа Sheet1 должны заполниться.
7) Строим диаграмму в виде графика по третьему столбцу.
Управление параметрами M (comiss в коде), share и sigma–через код VBA. Положительные M соответствуют положительной комиссии и приводят к убыванию счета.
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”
Option Explicit
Sub Leverage()
Dim i, imax As Integer
Dim s, comiss, sigma, share, x, y As Single
imax = 10000
For i = 2 To imax
x = Rnd()
y = Rnd()
Cells(i, 1) = (-2 * Log(x)) ^ (0.5) * Cos(2 * 3.1415926 * y)
Next i
sigma = 1
sigma = sigma / 100
comiss = 0.03
comiss = comiss / 100
share = 3
For i = 2 To imax
Cells(i, 2) = Exp(sigma * Cells(i, 1)) — 1 — comiss
Next i
s = 1
For i = 2 To imax
s = s * (1 + share * Cells(i, 2))
If s < 0 Then s = 0
Cells(i, 3) = s
Next i
End Sub
К Вам не относится))
Эта игра моделирует систему рынок-трейдер. Вы--очень хороший трейдер, я--рынок.
В этой игре вы, если будете ставить все, что у вас есть, неизбежно проиграете. Даже с гигантским перевесом в шансах 80/20. Тогда как если будете ставить 1/10 капитала--будете жить за мой счет долго и счастливо. Вот про это и статья.
Я не утверждал, что «код нерабочий».
Да, собственно, весь код
В этом беда — тупо копипастят не читая и даже не задумываясь — что, куда, зачем… Влазит в экран, не влазит. Лишь бы «плюсикофф, плюсикофф!»
Ну, самое первое — то, с чего начали — декларация переменных.
В VBA надо декларировать _каждую_ переменную. В твоей записи:
Dim i, imax As Integer
Dim s, comiss, sigma, share, x, y As Single
явно продекларированы только imax и y. Всем остальным по умолчанию присвоен тип variant.
Правильной запись будет такой:
Dim i As Integer, imax As Integer
Dim s As Single, comiss As Single, sigma As Single, share As Single, x As Single, y As Single
или, сократив:
Dim i%, imax%
Dim s!, comiss!, sigma!, share!, x!, y!
Чуть позже отпишусь по остальным скользким моментам.
По поводу декларирования. Попробовал--да, вы правы. Вот такой код:
Option Explicit
Sub a()
Dim x, y As Single
MsgBox (TypeName(x))
End Sub
выдает x как empty(то есть variant в итоге), а код:
Option Explicit
Sub a()
Dim x as Single, y As Single
MsgBox (TypeName(x))
End Sub
выдает x как Single. Спасибо!
В данном случае это, может, и не принципиально, но коллизий преобразования типов могут вылезти где и когда угодно. Поэтому — декларируем все явно. В этом контексте — респект за тсрользование Option Explicit. Грамотно и полезно.
Идем далее.
x = Rnd()
y = Rnd()
Мало того, что это делается в цикле (тут свои подводные камни), так еще и без использования Randomize.
У меня не было времени понять истинное математическое назначение этих рэндомов. Но в данном написании и x, и y будут генериться из ОДНОГО И ТОГО ЖЕ ряда. Я чуть позже вернусь, а ты пока загляни в хэлп VBA по этим функциям — там кратенько описаны нюансы каждой ф-ции.
If Randomize is not used, the Rnd function (with no arguments) uses the same number as a seed the first time it is called, and thereafter uses the last generated number as a seed value.
«В данном случае это, может, и не принципиально, но коллизий преобразования типов могут вылезти где и когда угодно.»
Это уж точно. Я к option explicit не так просто пришел :)
По Rnd()--а) согласен, и б) я в курсе--данные действительно из одного квазислучайного ряда. Для целей данной заметки сойдет и так--ибо:
а) требования к рандому невилики
б) заметка все-таки не рассчитана на высокий уровень владения математикой и программированием--так что код самый простейший.
Но если по уму, то мне вообще рандом экселевский не нравится. Более хороший ГСЧ--вихрь Мерсенна, есть и куча других. Обсуждалось у меня на блоге: www.2stocks.ru/utkin/?p=113 В комменты тоже загляните--до седьмого, дальше спам.
Про рэндомы — главное что ты в курсе и что это — сознательное допущение, а не неведение. Но! Я не случайно рекомендовал в хелп заглягуть.
The Rnd function returns a value less than 1 but greater than or _equal_to_zero_!!!
Как тебе? Log(0), а?
Идем далее. Не забываем, что я докапываюсь по канону :)
Все исходные — синглы, но расчеты ты ведешь, базируясь на значениях ранее расчитанных ячеек. А в них хранятся — даблы. Ты аостоянно то пишешь на лист, то читаешь с него. Медленная и некорректная операция с т.з. что задействован механизм Экселя — расчеты на листе, который может еще и измениться.
Вот. Загрубляется значение Cells(i, 2):
s = s * (1 + share * Cells(i, 2))
If s < 0 Then s = 0
Cells(i, 3) = s
Опять же — вероятность коллизий. Поэтому — делаем дополнительные переменные для каждого этапа вычисления и потом разом сбрасываем их на лист.
Я шары по очереди выкатываю, чтобы не в куче, а постепенно. Я потом выложу окончательный код, как я его вижу.
А генератор вот сюда переехал
www.ntrand.com/
Sub Leverage_21()
Const PIPI As Double = 3.14159265358979
Dim dS#, dComiss#, dSigma#, dShare#, _
dX#, dY#, dVal#(1 To 3) ' все в даблах
Dim i%, iMax%
Dim j As Byte
iMax = 10000
dSigma = 1: dSigma = dSigma / 100
dComiss = 0.03: dComiss = dComiss / 100
dShare = 3
dS = 1
Randomize ' инициализация Rnd
For i = 2 To iMax
dX = Rnd(): dY = Rnd()
dVal(1) = ((-2 * Log(dX)) ^ (0.5)) * Cos(2 * PIPI * dY)
dVal(2) = Exp(dSigma * dVal(1)) — 1 — dComiss
dS = -dS * (1 + dShare * dVal(2)) * CInt(dS > 0)
dVal(3) = dS ' dS оставлено для понимания
For j = 1 To 3
Cells(i, j).Value = dVal(j)
Next
If dS = 0 Then Exit For ' добавил от себя
Next
End Sub
Dim dXX#(), dYY#()
ReDim dXX(2 To iMax): ReDim dYY(2 To iMax)
Randomize ' инициализация Rnd
For i = 2 To iMax
dXX(i) = Rnd
Next
Randomize ' инициализация Rnd
For i = 2 To iMax
dYY(i) = Rnd
Next
а в основном цикле убрать генерирование на лету, и подсталять элементы массивов.
1) Спасибо большое! Честно говоря, просто приятно :)
2) Я, когда роботов пишу, то за каждое замечание типа тех, что вы делаете, плачу деньги. И немалые :) И культура кода нарабатывается путем денежных ударов по башке :)
3) По деталям:
а) Логарифма нуля бояться не надо. Вероятность того, что x будет нулем, равна нулю. Вообще, это стандартный способ получения нормального распределения из двух равномерных. Подробнее здесь: www.rsdn.ru/forum/alg/1513899.flat --второй коммент (ссылка не особо, просто первое, что попалось в гугле)
б) Постоянно пересчитывать из переменной в ячейку листа и обратно--это дурной стиль, согласен. Когда эта статья писалась, я на тиках еще не тестировал, сейчас бы мне такая мысль даже в голову бы не пришла.
в) За код спасибо--буду изучать новые приемы. Честно говоря, там есть некоторые вещи, про которые я просто не знал.
Чай, и мы в лесу не звери ?)
По логарифму… Я не математик, поэтому это утверждение могу только на веру принять. А с точки зрения канонов программирования — лучше предостеречься. х ведь у нас генерится через Rnd, а в мануале написано: Rnd возвращает «greater than or equal to zero» — надо выполнять, поскольку у нас Log(Rnd())
И, все-таки, я хотел бы услышать твое авторитетное мнение касательно правильности генерации x и y для обеих частей итогового генератора
((-2 * Log(dX)) ^ (0.5)) * Cos(2 * PIPI * dY)
Правильна ли моя посылка, что генераторы каждого должны быть полностью изолированы друг от друга — по последнему моему варианту? Я-то в этом слабо разбираюсь :(
обнfружил ошибку в своем коде!
Необходимо заменить
dS = -dS * (1 + dShare * dVal(2)) * CInt(dS > 0)на
dS = -dS * (1 + dShare * dVal(2))
dS = -dS * CInt(dS > 0) ' или If Not (dS > 0) Then dS = 0
«Правильна ли моя посылка, что генераторы каждого должны быть полностью изолированы друг от друга — по последнему моему варианту? Я-то в этом слабо разбираюсь :(»
Да, конечно. Строго говоря, равномерно распределенные на [0,1] случайные величины x и y должны быть независимы друг от друга. Вот ссылку нормальную нашел: ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%91%D0%BE%D0%BA%D1%81%D0%B0_%E2%80%94_%D0%9C%D1%8E%D0%BB%D0%BB%D0%B5%D1%80%D0%B0
Я в свое время когда этот метод генерации нормального распределения осваивал, пришел к выводу, что можно не морочиться особо для грубых целей--и писать как в моем коде. Если же по уму, то надо действовать как вы--изначально рожать два независимых случайных массива. Кроме того, надо отдельно прописать случай, когда x=0 (в этом случае ответ должен быть плюс или минус машинная бесконечность, либо менять x в этом случае на 1E-15).
Обрати внимание на жирное — как проводится проверка на ноль. Это тебе на будущее (безотносительно к твоему коду, просто — важный момент) — никогда не проверяй даблы и пр. с плав. точкой на равенство! Нулю, другому даблу и пр. Только на больше-меньше.
------------------------------------------------
Sub Leverage_22()
Const PIPI As Double = 3.14159265358979
Const MIND As Double = 4.94065645841247E-324
Dim dS#, dComiss#, dSigma#, dShare#, _
dXX#(), dYY#(), dVal#(1 To 3) ' все в даблах
Dim i%, iMax%
Dim j As Byte
dSigma = 1: dSigma = dSigma / 100
dComiss = 0.03: dComiss = dComiss / 100
dShare = 3
dS = 1
iMax = 10000
ReDim dXX(2 To iMax): ReDim dYY(2 To iMax)
Randomize ' инициализация Rnd
For i = 2 To iMax
dXX(i) = Rnd
' закомментировать ненужное
If Not (dXX(i) > 0) Then dXX(i) = Rnd ' сгенерить следующее
If Not (dXX(i) > 0) Then dXX(i) = MIND ' взять мин возм double
If Not (dXX(i) > 0) Then dXX(i) = CDbl(«4E-324») ' взять мин возм double
Next
Randomize ' инициализация Rnd
For i = 2 To iMax
dYY(i) = Rnd
Next
For i = 2 To iMax
dVal(1) = ((-2 * Log(dXX(i))) ^ (0.5)) * Cos(2 * PIPI * dYY(i))
dVal(2) = Exp(dSigma * dVal(1)) — 1 — dComiss
dS = dS * (1 + dShare * dVal(2))
If Not (dS > 0) Then dS = 0 ' dS = -dS * CInt(dS > 0)
dVal(3) = dS ' dS оставлено для понимания
For j = 1 To 3
Cells(i, j).Value = dVal(j)
Next
'If dS = 0 Then Exit For ' добавил от себя
Next
End Sub
Все изучил--код рабочий. Особое спасибо за вот это:
Dim dS#, dComiss#. Ибо переменные надо объявлять правильно--это очень важно.
Вопросы:
1) почему нельзя проверять на равенство floating point?
2) Что делает команда CInt(dS>0). CInt(dS)--преобразует dS в целую. А CInt(dS>0)?
1. Сравнивать, конечно, можно, но ясно отдавать себе отчет — что ты делаешь и зачем. Это особенности вычислений с плавающей точкой. Можно дисер написать :) Загугли фразу «сравнение double».
2. «dS > 0» дает boolean true или false, а «CInt(dS>0)» преобразует в, соответственно, "-1" или «0». Можно было написать короче, положившись на неявное преобразование типов «dS = -dS * (dS > 0)»
Имеем выигрыш и в записи и в скорости.
Тут надо иметь ввиду, что это справедливо лишь для VBA(VB). Если портировать на C++, к примеру, то там «false == 0» тоже, а вот «true == +1», а не "-1".
И, кстати, в самом Экселе, на листе, нотация именно как в C++ :) Чехарада, то есть…
Также — никогда не используй VBA-шную ф-цию округления Round() — это не школьное округление! Школьное, как раз, реализовано в ф-ции листа Эксель.
Debug.Print Round(4.49999999)
А можно прямо из окна дебаггера — Immediate Window — Ctrl+G:
? Round(4.49999999)
Это явно не документировано, но если до запятой четное — то округляет в меньшую, если нечетное — то в большую:
? Round(12.5)
12
? Round(13.5)
14
? 13.5 — 12.5
1
? Round(13.5) — Round(12.5)
2
Так что — совсем не фигня. В цепочках вычислений такую муть тебе наведет… На порядки улететь можно… Будешь считать пересеение мувингов :) А, тем более, ты ММ считаешь…
Но, дело хозяйское. Мое дело — предупредить.
Ладно, спасибо большое за проведенные уроки. Такие вещи способны сэкономить кучу времени. Если будут программистские вопросы--обращусь. Хотя обычно сам во всем разбираюсь--правда, времени уходит много :)
Я не считаю пересечения мувингов--такие трендовые системы уже плохи для меня. И тем более не считаю ММ--а что там считать то? Там вычисления на уровне два плюс два. К примеру, в обсуждаемой статье как ни верти--плечи и/или комиссы убьют счет--это очевидно вообще без расчетов (см. мой коммент к этой статье от 29.07 13:30)--что и написано в самом начале статьи. Вообще, мой подход к трейдингу изложен здесь: anatoly-utkin.livejournal.com/10280.html и здесь: anatoly-utkin.livejournal.com/14058.html
Общие слова. Любая корректно поставленная задача является грубой. То есть важна сама идея--а детали расчетов не принципиальны (до определенной степени, конечно--тут все расплывчато). В трейдинге (да и вообще в реальном мире) все задачи--грубые. Поэтому если торговая система работает только при периоде мувинга в 157 (это Резвяков небезызвестный рассказывал)--то это плохая система. Также и хороший алгоритм не должен быть особо чувствителен к мелким деталям. Но тут вопрос--что такое мелкие? :)
Но, я тебя просто так не отпущу :) Я твою задачу модернизирвал в несколько ином разрезе. Хотел, чтобы ты взглянул на корректность моих выкладок. Я имею ввиду не выводы по результатам, хоть они и очень интересны, а методику алгоритма на формальную правильность.
Вот, картинка для затравки:
Это 1000 раз прогнанный твой тест, только на реальном контракте CME ES, с реальными комиссами и реальной маржой от депо 50К. По оси Y — шаг, на котором депо становилось меньше залоговой маржи, т.е. невозможность открыть хотя бы одну позу по этому контракту.
Обрати внимание — достаточно много несливных прогонов — потолок 10000.
А по поводу картинки--очевидно, что за конечное время (10000 шагов) есть шанс, что счет не сольется. И вы приближенно, методом Монте-Карло, сосчитали вероятность этого--это число несливных прогонов, деленное на общее число прогонов. Можно строить зависимости этой вероятности от времени, комиссов, плечей, итд. Но все это--вещь в себе и не особо нужно (для трейдера, по крайней мере).
Я просто прогнал тест из 10000 циклов 1000 раз и на оси Y отложил цикл, на котором депо заканчивалось :)
А сделал это из того, что, резонно предположив, что я не броуновский трейдер и 50К за 3-4 тыс ходок явно не солью, меня сильно заинтересовал параметр М:
«M–параметр качества трейдера, для “броуновских” трейдеров он равен комиссии, для опытных он положителен.»
Твой вариант с равенством его комиссии изучен. Давай двинемся дальше — как его, М, можно модифицировать, чтобы приблизить к реалиям конкретного трейдера?
«Если бы Остап знал, что играет такие мудреные партии, то сильно удивился бы :)
Я просто прогнал тест из 10000 циклов 1000 раз и на оси Y отложил цикл, на котором депо заканчивалось :) „
Я понимаю :) Я просто переформулирую на более глубокий язык то, что вы сделали :)
В коде для изучения ситуации в зависимости от М надо регулировать comiss. Используемая формула: s = s * (1 + share * Cells(i, 2))--это пересчет капитала на каждом шаге (я кривой изначальный код использую--разберетесь), Cells(i,2)--это случайная величина, являющаяся суммой логнормального распределения (exp(sigma*N)-1, где N--нормально (0,1) распределенная величина) и постоянного смещения comiss (которое есть comiss=комиссия (% от суммы сделки) минус средняя сделка трейдера (%)). Соответственно, единственный параметр в коде, отвечающий за качество работы трейдера--это comiss. Отрицательные comiss соответствуют тому, что трейдер зарабатывает больше, чем тратит на комиссии.
Именно поэтому и спрашиваю. В рамках данной статьи и приведенного кода в предположении, что трейдер — броуновский, М принимается равным комиссии. Если комиссию примем за К, а трейдера за Т, то формула М будет следующей:
или М = К — Т (для броуна Т = 0, для сознательного Т > 0)
или М = К / Т (бля броуна Т = 1, для сознательного Т > 1)
Мне хочется понять — как перейти от броуновских Т к более-менее реальным?
1) Если комиссы фиксированные (в рублях), то их надо вычитать непосредственно из счета. Вот так: s=s(1+share*x)-comiss.
2) Если комиссы в процентах, то применима моя формула, где М=К-Т.
3) Еще раз, Т--это средняя сделка трейдера в %. Я ее не могу знать для вас. У меня для разных систем она варьируется от единиц до сотых долей процента.
4) Из другой оперы. Хочу, чтобы некий код (Account) исполнялся один раз в час в течение недели. Вот код:
Sub Start_System()
Dim T, Delta As Date
Dim i As Integer
T = Now
Delta = 0.04166
Application.OnTime TimeValue(T + 0.0007), «Account»
For i = 1 To 168
Application.OnTime TimeValue(T + i * Delta), «Account»
Next i
End Sub
Кривость задания дельты теперь осознаю. Код работает некорректно. Симптомы: первые сутки после запуска работает, а дальше--нет. С чем это может быть связано?
Application.OnTime мне не нравится. Какие-то траблы по ней были и оговорки. Пороюсь в архивах — я там плотно ее прорабатывал. Чуть позже отпишусь.
А навскидку — не связыывайся ты с явными даблами, не задавай их явно, константой, как ты присвоил дельту и пр. Вкури представление датыа в VBA — целая часть — число дней откуда-то там, дробная — часть суток. Отсюда и пляши.
0. T опять непродекларирована
1. Delta = 1 / 24
2. Ты задаешь сразу все события. Я бы не стал. Скорее всего там есть какие-то ограничения и интерференции. Шедулер-то не бесконечный. Назначай следующее задание из процедуры Account по коректному ее завершению. Другими словами — запусти Account один раз и она по завершении будет сама себя назначать.
Нашел я. Оказалось под руками :) И все вспомнил.
Я хотел через OnTime прикрутить к собственному календарю событий прикрутить оповещатель. Малой кровью типо обойтись. Хрен там. Плюнул. Код — сплошные костыли. Кривая эта OnTime, как турецкая сабля.
Ты подключи к проекту библиотеку Access. Там есть прекрасный асинхронный таймер формы. Я тебе класс готовый для работы с ним подкину. Робот у меня с ним работает неделями. Это стопудово проверено.
Про дату я в курсе. А 0.04166=1/24 :)
понятно-то — понятно, что 1/24, а вот запись некорректная.
? 1/24
4.16666666666667E-02
И еще вопросы:
— что в этой процедуре?
— отТкуда есть еще обращения к OnTime?
Респект. Всякие шедуллеры как раз надо не по нулям ставить, а со сдвигом — чтобы вероятность пересечения с другими была ниже.
Предлагаю эту тему закрыть, раз простых решений нам не известно. Я думал, что вы, такой крутой, все сразу скажете :) Поиграюсь с onTime, придумаю, как все наладить. На самом деле я обычно каждый день на рынок поглядываю, так что не в напряг запустить. Оно уж так работает кучу времени--годы :) Кроме того, можно и с сотового зарядить макрос--ибо все давно через виртуалку реализовано.
Я не про это :) Я спрашивал — что там такого в коде этой процедуры, что для нее требуется 168 + 1 автономных таймера?
Ты не ответил:
— откуда есть еще обращения к OnTime?
Почему не обойтись одним, перезаряжаемым таймером?
А крутой — так крутой не стал бы и разговаривать без всего кода и точных ответов на вопросы.
Вот тебе код. Вот такой таймер будет по науке, а не этот костыль OnTime. Создай новый модуль, скопируй все в него.
То, что дохтур прописал. Один раз зарядишь — будет пахать, пока не убьешь.
Option Explicit
Private Declare Function SetTimer Lib «user32» ( _
ByVal hwnd As Long, _
ByVal nIDEvent As Long, _
ByVal uElapse As Long, _
ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib «user32» ( _
ByVal hwnd As Long, _
ByVal nIDEvent As Long) As Long
Private lCnt&, lTmrID&
'
Private Sub sb_TmrEvent(ByVal HandleW&, ByVal msg&, ByVal idEvent&, ByVal TimeSys&)
' callback for WM_Timer event of SetTimer
Debug.Print HandleW
Debug.Print msg
Debug.Print idEvent
Debug.Print TimeSys
lCnt = lCnt + 1
Debug.Print «Timer_» & lTmrID, lCnt
Debug.Print vbLf
End Sub
Private Sub sb_TmrCrt() ' Create Timer
lCnt = 0
lTmrID = SetTimer(0&, 0&, 1000, AddressOf sb_TmrEvent)
Select Case lTmrID = 0
Case True: Debug.Print «Can't create timer!»
Case False: Debug.Print «Created Timer ID » & lTmrID
End Select
End Sub
Private Sub sb_TmrKil() ' Kill Timer
Select Case lTmrID = 0
Case True
'
Case False
Call KillTimer(0&, lTmrID)
Debug.Print «Killed Timer ID » & lTmrID
lTmrID = 0
End Select
End Sub
По таймеру. Главная беда этой OnTime — у нее нет обратной связи. Смотри — она и процедуру-то вызывает по имени — ее строковому определению. А SetTimer обращается к процедуре по адресу. Имо, она как раз на базе этого SetTimer и сделана, только максимально упрощенно.
Главной подводный камень — поставить проверку на соответствие lTmrID, если вдруг случайно их несколько запустишь.
И, второе, явно убить его, когда не будет нужен.
В sb_TmrEvent вся начинка — демонстрашка. Смело можно убить. Ну, может, счетчик срабатываний оставить. А уже оттуда — вызывай свою «Account».
Прежде всего хочу спросить именно по заголовку статьи — про плечо :)
В твоих расчетах плечо — константа. В реальности же, при прочих равных, плечо пропорционально уменьшается с ростом депо и наоборот.
«x–результат сделки на один лот.»
Вообще говоря, мной применено приближение малого лота. То есть размер лота много меньше размера капитала. В рамках такого приближения мы можем войти точно 23.45% капитала. Условно говоря, у нас 1 мио, а торгуем акцией стоимостью в 1 рубль. Или у нас 100 ярдов, а торгуем RI стоимостью в 100к. Конечно, это приближение, а реально надо учитывать конечную дискретизацию лота (особенно когда капитал уже мал и сравним с размером лота)--но это ничего принципиально не поменяет.
Да, если мы торгуем 1 контрактом, то мы задействуем 100% капитала.
Плечо = Цена_Контракта / Депо
Растет депо — уменьшается плечо и наоборот. Изменением Цена_Контракта можно пренебречь, в рамках описанной модели (sigma = 1%)
Плечо = Цена_Контракта / (Депо — Маржа)
Я примерно понимаю твою мысль, но лишь примерно. Прокомментируй, плз:
CME, ES (50$ x Indes S&P500). Торгуем одним контрактом.
Цена 85'000, депо 50'000, плечо 1.7
Если сделка идет в профит +10000 с S&P 1'700 на 1'900
Цена 95'000, депо 60'000, плечо 1.58 или
Цена 85'000, депо 60'000, плечо 1.42 без учета роста.
Если сделка идет в лосс -10000 с S&P 1'700 на 1'500
Цена 75'000, депо 40'000, плечо 1.88 или
Цена 85'000, депо 40'000, плечо 2.13 без учета падения.
Понятно, что внутри дня такого движняка не будет и официальный пересчет будет только на клиринге. Поэтому, при принятой sigma = 1% и установке интрадей можно пренебрегать изменением цены контракта.
Вот что у меня: s=s(1+share*x). x--это результат закрытой (купил-продал или вшортил-откупил) сделки. То есть внутрь сделки мы просто не лезем. То есть x--это результаты сделок и прямой связи с графиком инструмента они вообще не имеют.
Вообще-то, я просил тебя прокомментировать мои выкладки. Будь добр.
вот определение результата сделки: результат сделки=(цена выхода-цена входа)/цена входа для лонга и результат сделки=(цена входа-цена выхода)/цена входа для шорта.
Я не знаю, где ты берешь такие «определения» и «формулы». Искренне удивлен… :((
PL = (Price_In — Price_Out) * Deal_Side, где
Deal_Side = -1 для BUY, LONG и
Deal_Side = +1 для SEL, SHORT
Знаки — не принципиально, можно поменять, переставив местами слагаемые. Хотя именно такая нотация (покупка с минусом) мною вполне обоснована.
Если вкладывается 100% капитала, то плечо=1, если 200%--то плечо=2, если 50%--то плечо=0.5.
Я именно так и считаю: «Цена 85'000, депо 50'000, плечо 1.7»
85'000 / 50'000 = 1.7
Я не прав?
То, что считаю я--это на вашем языке надо на каждом клиринге приводить долю в соответствие с капиталом--рекапитализовываться. Это не торговля одним контрактом, ибо один контракт--это не постоянное плечо, а я хочу рассмотреть постоянное.
По поводу этого:
"
PL = (Price_In — Price_Out) * Deal_Side, где
Deal_Side = -1 для BUY, LONG и
Deal_Side = +1 для SEL, SHORT
"
Это результат сделки в абсолютных единицах. А мне нужен--результат в процентах. Я же это сто раз уже повторил. Хотите в таком виде--да пожалуйста, считайте так. Если сосчитаете правильно, выводы будут те же.
По вашим выкладкам. Я же сказал, что слова клиринг, динамика цены, итд, напрямую к теме не имеют отношения. В расчете есть только процентные результаты сделок--а уж откуда они берутся--это следующий вопрос.
Короче. Считайте финансовый результат--и все. Можно точно, можно приближенно. Описываемый эффект настолько груб, что от мелких деталей не зависит. Как именно считать--да не все ли равно. Можно вообще даже слово плечо не употреблять.
«Можно вообще даже слово плечо не употреблять.»
Ага. И выбросить его из заголовка обеих статей :)
Я чуть позже отвечу подробнее.