maroudas

О влиянии плечей на неопытных трейдеров

неплохая статья Анатолия Уткина,
перепост с 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. В этом случае счет показывает обычную броуновскую динамику:
401_1.jpg
Отсюда видно, что шансы продержаться неплохи, аж за 10000 сделок ничего особо не происходит (sigma равнялась 1%, что соответствует интрадэйным сделкам). Замечу, что по одной реализации выводы делать не стоит, поэтому в конце я привожу код VBA, по которому каждый может повторить это моделирование. Далее, введем комиссию в размере 0.03%. В общем-то, очевидно, что будет. Счет уменьшится на порядок за число сделок порядка 1/0.03%=около 3000 сделок:
401_2.jpg
Комиссии убивают счет и это путь неплечевых “броуновских” трейдеров. Вывод такой, что у “броуновского” неплечевого трейдера есть около 0.5/комиссия сделок, пока его счет уменьшится в два раза.
Теперь самое интересное–влияние плечей. Выберем в качестве f тройку–это соответствует вложению 300% капитала в каждую сделку. А комиссию занулим–без комиссий! Я привожу достаточно типичную картинку:
401_3.jpg
Это так похоже на работу многих трейдеров (смотри, например, на comon.ru), не правда ли? Рассмотрев множество таких картинок, можно прийти к выводу, что использование плечей СИСТЕМАТИЧЕСКИ убивает счет. То, что было нейтральным при отсутствии плеча (первый рисунок), становится убийственным при его присутствии. Качественные причины этого приведены во втором абзаце статьи. Ну, и наконец, динамика счет при наличии комиссии 0.03% и f=3:
401_4.jpg
Тут, как говорится, без комментариев. 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) Строим диаграмму в виде графика по третьему столбцу.
8) Управление параметрами 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
★12
81 комментарий
Плечо — это обычный мультипликатор. Никак на торговлю оно не влияет, выводы высосаны из пальца. Плечо высвобождает лишние финансы из-под залога, что хорошо. Поэтому, больше плечи — лучше.
avatar
Дейтрейдер, как мне кажется, ключевое слово в названии статьи — «для неопытных».
К Вам не относится))
avatar
Дейтрейдер, Не высосаны. Поиграем в игру. У вас--1000 рублей, у меня--триллион рублей. Вы выбираете некоторую ставку, в случае вашего выигрыша я выплачиваю вам ее, в случае вашего проигрыша-вы выплачиваете мне ставку. Игра такова, что вы выигрываете в среднем в 80% случаев--то есть в четыре раза чаще чем я. Вы можете регулировать ставку.

Эта игра моделирует систему рынок-трейдер. Вы--очень хороший трейдер, я--рынок.

В этой игре вы, если будете ставить все, что у вас есть, неизбежно проиграете. Даже с гигантским перевесом в шансах 80/20. Тогда как если будете ставить 1/10 капитала--будете жить за мой счет долго и счастливо. Вот про это и статья.
avatar
огромный плюс! спасибо большое!
avatar
URKA, Пожалуйста :)
avatar
anatolyutkin, ваше творение?
avatar
автору: лучше сохранять картинки на смартлабе, а не просто ссылки на них. когда -нибудь на 2stocks будет переезд на другой адрес и этих картинок мы здесь уже не увидим
avatar
Евгений Александрович-1, Да, мое. Про картинки--согласен. Но работать лень :)
avatar
VBA не Си. Переменные так не декларируются — цедьной строкой. Каждую надо индивидуально.
avatar
ES1667, Приведенный код--рабочий. Проверьте.
avatar
anatolyutkin, зачем мне проверять? Мне глаз хватает :)
Я не утверждал, что «код нерабочий».
Да, собственно, весь код :down:

В этом беда — тупо копипастят не читая и даже не задумываясь — что, куда, зачем… Влазит в экран, не влазит. Лишь бы «плюсикофф, плюсикофф!»
avatar
anatolyutkin, я не обратил внимание, что ты и есть оригинальный автор материала. Если нужны подробные замечания по коду — велкам. Потрачу малость времени :)
avatar
ES1667, Я не программист--так что любые замечания с удовольствием принимаются.
avatar
anatolyutkin, так, если не программист, надо ли оно?

Ну, самое первое — то, с чего начали — декларация переменных.

В 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!

Чуть позже отпишусь по остальным скользким моментам.
avatar
ES1667, Мне--надо. Я трейдер :)

По поводу декларирования. Попробовал--да, вы правы. Вот такой код:
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. Спасибо!
avatar
anatolyutkin, ОК.

В данном случае это, может, и не принципиально, но коллизий преобразования типов могут вылезти где и когда угодно. Поэтому — декларируем все явно. В этом контексте — респект за тсрользование 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.
avatar
ES1667,
«В данном случае это, может, и не принципиально, но коллизий преобразования типов могут вылезти где и когда угодно.»
Это уж точно. Я к option explicit не так просто пришел :)

По Rnd()--а) согласен, и б) я в курсе--данные действительно из одного квазислучайного ряда. Для целей данной заметки сойдет и так--ибо:
а) требования к рандому невилики
б) заметка все-таки не рассчитана на высокий уровень владения математикой и программированием--так что код самый простейший.
Но если по уму, то мне вообще рандом экселевский не нравится. Более хороший ГСЧ--вихрь Мерсенна, есть и куча других. Обсуждалось у меня на блоге: www.2stocks.ru/utkin/?p=113 В комменты тоже загляните--до седьмого, дальше спам.
avatar
anatolyutkin, взгляну обязательно.

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

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

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

Я шары по очереди выкатываю, чтобы не в куче, а постепенно. Я потом выложу окончательный код, как я его вижу.
avatar
anatolyutkin, за ссылку снкс. Посмотрел.
А генератор вот сюда переехал
www.ntrand.com/
avatar
anatolyutkin, вот уточненный варинат:

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
avatar
ES1667, по уму, так надо вынести генератор в отдельные циклы перед основным:
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

а в основном цикле убрать генерирование на лету, и подсталять элементы массивов.
avatar
ES1667, в таком варианте dXX(i) и dYY(i)будут из независимо сгенеренных рядов, а не последовательно выбранные из одного ряда.
avatar
ES1667,
1) Спасибо большое! Честно говоря, просто приятно :)
2) Я, когда роботов пишу, то за каждое замечание типа тех, что вы делаете, плачу деньги. И немалые :) И культура кода нарабатывается путем денежных ударов по башке :)
3) По деталям:
а) Логарифма нуля бояться не надо. Вероятность того, что x будет нулем, равна нулю. Вообще, это стандартный способ получения нормального распределения из двух равномерных. Подробнее здесь: www.rsdn.ru/forum/alg/1513899.flat --второй коммент (ссылка не особо, просто первое, что попалось в гугле)
б) Постоянно пересчитывать из переменной в ячейку листа и обратно--это дурной стиль, согласен. Когда эта статья писалась, я на тиках еще не тестировал, сейчас бы мне такая мысль даже в голову бы не пришла.
в) За код спасибо--буду изучать новые приемы. Честно говоря, там есть некоторые вещи, про которые я просто не знал.
avatar
anatolyutkin, рад, что оказался полезен.
Чай, и мы в лесу не звери ?)

По логарифму… Я не математик, поэтому это утверждение могу только на веру принять. А с точки зрения канонов программирования — лучше предостеречься. х ведь у нас генерится через Rnd, а в мануале написано: Rnd возвращает «greater than or equal to zero» — надо выполнять, поскольку у нас Log(Rnd())

И, все-таки, я хотел бы услышать твое авторитетное мнение касательно правильности генерации x и y для обеих частей итогового генератора
((-2 * Log(dX)) ^ (0.5)) * Cos(2 * PIPI * dY)

Правильна ли моя посылка, что генераторы каждого должны быть полностью изолированы друг от друга — по последнему моему варианту? Я-то в этом слабо разбираюсь :(
avatar
anatolyutkin,
обн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
avatar
ES1667,
«Правильна ли моя посылка, что генераторы каждого должны быть полностью изолированы друг от друга — по последнему моему варианту? Я-то в этом слабо разбираюсь :(»

Да, конечно. Строго говоря, равномерно распределенные на [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).
avatar
anatolyutkin, ага. Понял. Вот тогда окончательный вариант с учетом поправок. Надеюсь нет ни ошибок, ни опечаток. Проверь сам обязательно:)

Обрати внимание на жирное — как проводится проверка на ноль. Это тебе на будущее (безотносительно к твоему коду, просто — важный момент) — никогда не проверяй даблы и пр. с плав. точкой на равенство! Нулю, другому даблу и пр. Только на больше-меньше.

------------------------------------------------
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
avatar
ES1667,
Все изучил--код рабочий. Особое спасибо за вот это:
Dim dS#, dComiss#. Ибо переменные надо объявлять правильно--это очень важно.

Вопросы:
1) почему нельзя проверять на равенство floating point?
2) Что делает команда CInt(dS>0). CInt(dS)--преобразует dS в целую. А CInt(dS>0)?
avatar
anatolyutkin,

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++ :) Чехарада, то есть…
avatar
ES1667, сравнение на больше-меньше позволяет обойти большинство подводных камней.

Также — никогда не используй VBA-шную ф-цию округления Round() — это не школьное округление! Школьное, как раз, реализовано в ф-ции листа Эксель.
avatar
ES1667, Что не так с Round()? Пишу MsgBox (Round(4.49999999))--выдает 4, пишу MsgBox (Round(4.511111))--выдает 5. Вроде, ровно все? Единственное, для ровно 4.5 Round выдает 4, в отличие от школьного алго, где будет 5. Но это фигня--одна точка ничего не значит.
avatar
anatolyutkin, ты не заморачивайся с этими MsgBox. Люди вот так делают, если из кода:
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

Так что — совсем не фигня. В цепочках вычислений такую муть тебе наведет… На порядки улететь можно… Будешь считать пересеение мувингов :) А, тем более, ты ММ считаешь…

Но, дело хозяйское. Мое дело — предупредить.
avatar
ES1667, Нда, такое округление--это весело! Остается лишь вопрос--а откуда про это узнать можно :)

Ладно, спасибо большое за проведенные уроки. Такие вещи способны сэкономить кучу времени. Если будут программистские вопросы--обращусь. Хотя обычно сам во всем разбираюсь--правда, времени уходит много :)

Я не считаю пересечения мувингов--такие трендовые системы уже плохи для меня. И тем более не считаю ММ--а что там считать то? Там вычисления на уровне два плюс два. К примеру, в обсуждаемой статье как ни верти--плечи и/или комиссы убьют счет--это очевидно вообще без расчетов (см. мой коммент к этой статье от 29.07 13:30)--что и написано в самом начале статьи. Вообще, мой подход к трейдингу изложен здесь: anatoly-utkin.livejournal.com/10280.html и здесь: anatoly-utkin.livejournal.com/14058.html

Общие слова. Любая корректно поставленная задача является грубой. То есть важна сама идея--а детали расчетов не принципиальны (до определенной степени, конечно--тут все расплывчато). В трейдинге (да и вообще в реальном мире) все задачи--грубые. Поэтому если торговая система работает только при периоде мувинга в 157 (это Резвяков небезызвестный рассказывал)--то это плохая система. Также и хороший алгоритм не должен быть особо чувствителен к мелким деталям. Но тут вопрос--что такое мелкие? :)
avatar
anatolyutkin, откуда узнать? Ну, из таких вот диалогов, как наш, например :) Из опыта… Инфы полно — но она вся разбросана.

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

Вот, картинка для затравки:



Это 1000 раз прогнанный твой тест, только на реальном контракте CME ES, с реальными комиссами и реальной маржой от депо 50К. По оси Y — шаг, на котором депо становилось меньше залоговой маржи, т.е. невозможность открыть хотя бы одну позу по этому контракту.

Обрати внимание — достаточно много несливных прогонов — потолок 10000.
avatar
ES1667, по нему бы колокол построить… чтобы распределение посмотреть…
avatar
ES1667, а округление это ВБА-шное — совершенно правильное, даже название свое имеет. Только нам не походит :)
avatar
ES1667, Да не нужно это все :) Основная модель рынка, отвечающая на все подобные вопросы--это СБ. И ответ для распределений, вероятностей, итд на реальных ликвидных рынках получится такой, какой должен быть для СБ. Имхо, конечно, но это неплохое такое имхо :)

А по поводу картинки--очевидно, что за конечное время (10000 шагов) есть шанс, что счет не сольется. И вы приближенно, методом Монте-Карло, сосчитали вероятность этого--это число несливных прогонов, деленное на общее число прогонов. Можно строить зависимости этой вероятности от времени, комиссов, плечей, итд. Но все это--вещь в себе и не особо нужно (для трейдера, по крайней мере).
avatar
anatolyutkin, что здесь значит СБ?
avatar
anatolyutkin, «вы приближенно, методом Монте-Карло, сосчитали вероятность этого--это число несливных прогонов, деленное на общее число прогонов» — Если бы Остап знал, что играет такие мудреные партии, то сильно удивился бы :)

Я просто прогнал тест из 10000 циклов 1000 раз и на оси Y отложил цикл, на котором депо заканчивалось :)

А сделал это из того, что, резонно предположив, что я не броуновский трейдер и 50К за 3-4 тыс ходок явно не солью, меня сильно заинтересовал параметр М:
«M–параметр качества трейдера, для “броуновских” трейдеров он равен комиссии, для опытных он положителен.»

Твой вариант с равенством его комиссии изучен. Давай двинемся дальше — как его, М, можно модифицировать, чтобы приблизить к реалиям конкретного трейдера?
avatar
ES1667, СБ--случайное блуждание. Каждый следующий шаг не зависит от предыдущего. Я же ссылок кучу дал :)

«Если бы Остап знал, что играет такие мудреные партии, то сильно удивился бы :)

Я просто прогнал тест из 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 соответствуют тому, что трейдер зарабатывает больше, чем тратит на комиссии.
avatar
anatolyutkin, формулу расчета я вкурил прежде, чем высказать свой вердикт о коде :)

Именно поэтому и спрашиваю. В рамках данной статьи и приведенного кода в предположении, что трейдер — броуновский, М принимается равным комиссии. Если комиссию примем за К, а трейдера за Т, то формула М будет следующей:
или М = К — Т (для броуна Т = 0, для сознательного Т > 0)
или М = К / Т (бля броуна Т = 1, для сознательного Т > 1)

Мне хочется понять — как перейти от броуновских Т к более-менее реальным?
avatar
ES1667, мне нужна реальная, а не абстрактная модель. Комиссы у меня фиксированные, не в процентах, и четко известные. Остается неизвестная Т. Ведь говоря о Т, как о конкретной величине, соответсвтующей броуновскому скиллу трейдера, ту, предполагаю, имел ввиду некоторые ее значения, соответствующие более высокому скиллу?
avatar
ES1667,
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

Кривость задания дельты теперь осознаю. Код работает некорректно. Симптомы: первые сутки после запуска работает, а дальше--нет. С чем это может быть связано?
avatar
anatolyutkin, по п.4

Application.OnTime мне не нравится. Какие-то траблы по ней были и оговорки. Пороюсь в архивах — я там плотно ее прорабатывал. Чуть позже отпишусь.

А навскидку — не связыывайся ты с явными даблами, не задавай их явно, константой, как ты присвоил дельту и пр. Вкури представление датыа в VBA — целая часть — число дней откуда-то там, дробная — часть суток. Отсюда и пляши.

0. T опять непродекларирована
1. Delta = 1 / 24
2. Ты задаешь сразу все события. Я бы не стал. Скорее всего там есть какие-то ограничения и интерференции. Шедулер-то не бесконечный. Назначай следующее задание из процедуры Account по коректному ее завершению. Другими словами — запусти Account один раз и она по завершении будет сама себя назначать.
avatar
anatolyutkin, по п.4

Нашел я. Оказалось под руками :) И все вспомнил.
Я хотел через OnTime прикрутить к собственному календарю событий прикрутить оповещатель. Малой кровью типо обойтись. Хрен там. Плюнул. Код — сплошные костыли. Кривая эта OnTime, как турецкая сабля.

Ты подключи к проекту библиотеку Access. Там есть прекрасный асинхронный таймер формы. Я тебе класс готовый для работы с ним подкину. Робот у меня с ним работает неделями. Это стопудово проверено.
avatar
ES1667, Аксессную dll подключить недолго (правда, найти бы ее еще--так вот сразу в tools--references ее нет). А вот дальше что делать? :)

Про дату я в курсе. А 0.04166=1/24 :)
avatar
anatolyutkin,
понятно-то — понятно, что 1/24, а вот запись некорректная.
? 1/24
4.16666666666667E-02

avatar
anatolyutkin, подожди пока, не ломай мозг с Аксом — там все не так просто :(
avatar
anatolyutkin, с Аксом не связывайся. Давай попробуем с этим OnTime разобраться. Ты так и не ответил — зачем тебе первый таймер через 60.48 сек и 168 щтук через час каждый, все завязанные на одну процедуру?

И еще вопросы:
— что в этой процедуре?
— отТкуда есть еще обращения к OnTime?
avatar
ES1667, Это бухучет сделок. Мне неохота нажимать на кнопки для торговли даже один раз в день--хочу раз в неделю.
avatar
anatolyutkin, 60.48 секунд--это случайность. Люблю повеселиться :)
avatar
anatolyutkin, «60.48 секунд--это случайность. Люблю повеселиться :)»
Респект. Всякие шедуллеры как раз надо не по нулям ставить, а со сдвигом — чтобы вероятность пересечения с другими была ниже.
avatar
ES1667, Да, причина веселья именно такая.

Предлагаю эту тему закрыть, раз простых решений нам не известно. Я думал, что вы, такой крутой, все сразу скажете :) Поиграюсь с onTime, придумаю, как все наладить. На самом деле я обычно каждый день на рынок поглядываю, так что не в напряг запустить. Оно уж так работает кучу времени--годы :) Кроме того, можно и с сотового зарядить макрос--ибо все давно через виртуалку реализовано.
avatar
anatolyutkin, «Это бухучет сделок»
Я не про это :) Я спрашивал — что там такого в коде этой процедуры, что для нее требуется 168 + 1 автономных таймера?

Ты не ответил:
— откуда есть еще обращения к OnTime?

Почему не обойтись одним, перезаряжаемым таймером?
avatar
Ого! В перепост попал. Всем ответил выше.
avatar
anatolyutkin, у меня вопросы еще отсались по твоим выкладкам. Впрочем, если ты задний ход включаешь, нет проблем… Сам разберусь.

А крутой — так крутой не стал бы и разговаривать без всего кода и точных ответов на вопросы.

Вот тебе код. Вот такой таймер будет по науке, а не этот костыль 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
avatar
ES1667, Не не, задний ход только по моему вопросу по onTime. За код спасибо--буду изучать. А по моей статье--велкам любые вопросы. Я родом из науки--а там принято за базар отвечать :) Меня вообще первый раз в трейдерской тусовке так спрашивают--необычные ощущения :)
avatar
anatolyutkin, ОК. Я пока только готовлю вопросы. Чтобы глупость не спросить ненароком :)

По таймеру. Главная беда этой OnTime — у нее нет обратной связи. Смотри — она и процедуру-то вызывает по имени — ее строковому определению. А SetTimer обращается к процедуре по адресу. Имо, она как раз на базе этого SetTimer и сделана, только максимально упрощенно.

Главной подводный камень — поставить проверку на соответствие lTmrID, если вдруг случайно их несколько запустишь.

И, второе, явно убить его, когда не будет нужен.

В sb_TmrEvent вся начинка — демонстрашка. Смело можно убить. Ну, может, счетчик срабатываний оставить. А уже оттуда — вызывай свою «Account».
avatar
ES1667, в том виде, как есть — запусти sb_TmrCrt() и смотри в окно дебаггера.
avatar
anatolyutkin, в контексте «Я пока только готовлю вопросы»
Прежде всего хочу спросить именно по заголовку статьи — про плечо :)

В твоих расчетах плечо — константа. В реальности же, при прочих равных, плечо пропорционально уменьшается с ростом депо и наоборот.
avatar
ES1667, Почему? Формула: s=s(1+share*deal_result) описывает случай постоянной доли. То есть в каждую сделку вкладываем share*100 процентов капитала. И от капитала доля и плечо не зависят.
avatar
anatolyutkin, контракт-то остается один. Или я что-то не так понял?

«x–результат сделки на один лот.»
avatar
ES1667, deal_result--это % результат на вложенное в сделку.

Вообще говоря, мной применено приближение малого лота. То есть размер лота много меньше размера капитала. В рамках такого приближения мы можем войти точно 23.45% капитала. Условно говоря, у нас 1 мио, а торгуем акцией стоимостью в 1 рубль. Или у нас 100 ярдов, а торгуем RI стоимостью в 100к. Конечно, это приближение, а реально надо учитывать конечную дискретизацию лота (особенно когда капитал уже мал и сравним с размером лота)--но это ничего принципиально не поменяет.
avatar
anatolyutkin, А в реальности я, например, торгую именно постоянную долю. Конечно, я не имею возможности менять лот, скажем, на RI или TRNFP при небольших изменениях капитала--у меня нет 100 ярдов :) Но это все мелкие брызги.
avatar
anatolyutkin, anatolyutkin, дискретность — это другая тема. На данном этапе можно пренебречь.

Да, если мы торгуем 1 контрактом, то мы задействуем 100% капитала.

Плечо = Цена_Контракта / Депо

Растет депо — уменьшается плечо и наоборот. Изменением Цена_Контракта можно пренебречь, в рамках описанной модели (sigma = 1%)
avatar
ES1667, ну, может быть

Плечо = Цена_Контракта / (Депо — Маржа)
avatar
ES1667, Плечо=вкладываемая доля капитала/1. Если вкладывается 100% капитала, то плечо=1, если 200%--то плечо=2, если 50%--то плечо=0.5. При этом вкладываемая доля капитала=цена одной акции(контракта)*количество акций(контрактов)/депо. К примеру, у меня 65к рублей. Я купил 1 лот RI. Полная цена 1 лота RI равна 100к (условно). Тогда плечо=(100к*1 лот)/65к=100/65=1.5. Для двух лотов плечо равно 2*100/65=3. А от ГО плечо вообще не зависит.
avatar
anatolyutkin, ОК. Давай к ГО вернемся позже. Пока не будем учитывать его.

Я примерно понимаю твою мысль, но лишь примерно. Прокомментируй, плз:

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% и установке интрадей можно пренебрегать изменением цены контракта.
avatar
ES1667, Это не моя мысль, это определение плеча :)

Вот что у меня: s=s(1+share*x). x--это результат закрытой (купил-продал или вшортил-откупил) сделки. То есть внутрь сделки мы просто не лезем. То есть x--это результаты сделок и прямой связи с графиком инструмента они вообще не имеют.
avatar
anatolyutkin, Пример по ES. Купили по 40000, продали по 50000. Результат этой сделки +25% на вложенное. Капитал до сделки был 100к, купили на 200к. Тогда плечо=2, капитал после сделки равен 100*(1+2*0.25)=150к. Вот и все дела.
avatar
anatolyutkin, Еще одно. Может, термин «результат сделки на вложенное»--не особо удачный. Вот определение результата сделки: результат сделки=(цена выхода-цена входа)/цена входа для лонга и результат сделки=(цена входа-цена выхода)/цена входа для шорта.
avatar
anatolyutkin, актив и ПЛ изменяются неодинаково. Стоимость пункта фиксирована, а не изменяется вместе с ценой актива. Ты это игнорируешь, вот у тебя и получается «просто».

Вообще-то, я просил тебя прокомментировать мои выкладки. Будь добр.
avatar
anatolyutkin,
вот определение результата сделки: результат сделки=(цена выхода-цена входа)/цена входа для лонга и результат сделки=(цена входа-цена выхода)/цена входа для шорта.
Я не знаю, где ты берешь такие «определения» и «формулы». Искренне удивлен… :((

PL = (Price_In — Price_Out) * Deal_Side, где
Deal_Side = -1 для BUY, LONG и
Deal_Side = +1 для SEL, SHORT

Знаки — не принципиально, можно поменять, переставив местами слагаемые. Хотя именно такая нотация (покупка с минусом) мною вполне обоснована.
avatar
anatolyutkin, Это не моя мысль, это определение плеча :)
Если вкладывается 100% капитала, то плечо=1, если 200%--то плечо=2, если 50%--то плечо=0.5.

Я именно так и считаю: «Цена 85'000, депо 50'000, плечо 1.7»
85'000 / 50'000 = 1.7

Я не прав?
avatar
ES1667, По моему, это все яйца выеденного не стоит. Считать можно как угодно--главное для основного вывода (чрезмерно большое плечо убивает счет)в каком-то виде учитывать рекапитализацию. Хотите переменную стоимость шага цены--да ради бога, учитывайте.

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

По поводу этого:
"
PL = (Price_In — Price_Out) * Deal_Side, где
Deal_Side = -1 для BUY, LONG и
Deal_Side = +1 для SEL, SHORT
"
Это результат сделки в абсолютных единицах. А мне нужен--результат в процентах. Я же это сто раз уже повторил. Хотите в таком виде--да пожалуйста, считайте так. Если сосчитаете правильно, выводы будут те же.

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

Короче. Считайте финансовый результат--и все. Можно точно, можно приближенно. Описываемый эффект настолько груб, что от мелких деталей не зависит. Как именно считать--да не все ли равно. Можно вообще даже слово плечо не употреблять.
avatar
anatolyutkin,
«Можно вообще даже слово плечо не употреблять.»

Ага. И выбросить его из заголовка обеих статей :)
Я чуть позже отвечу подробнее.
avatar

теги блога Alex Maroudas

....все тэги



UPDONW
Новый дизайн