ТС «Игра теней». Завязка.
Описание системы здесь
smart-lab.ru/mobile/topic/872068/
Простыми словами:
При движении цены в одном направлении на пол-процента вероятность движения еще на одну десятую составляет 80%
При этом вероятность возврата цены к открытию составляет 20%
В публикации при расчете доходности системы была допущена ошибка, да простят меня Гаусс, Лейбниц и Стендаль, зато похвалит Росстат.
На скажу что она случайна, однако хорошо, что никто не заметил, застыдили б...
Сегодня я расскажу как увеличить доходность системы простой математикой.
Напоминаю расчет складывается из вероятности движения цены в одном направлении 80% и дополнительном движении цены еще на 0,1% также 80%.
В формуле складывается вероятность обоих событий, однако включать в формулу вероятность свершившегося события, мягко говоря не логично.
Открытие позиции происходит только при движении цены на 0,5%. Таким образом с вероятностью 80% движение цены принесет нам одну десятую процента, а не 64 как было указано.
Годовая доходность системы при данных показателях составляет 120% (200 дней 0,8% плюс, 50% дней 0,8% минус при использовании плеча срочного рынка, симметричный тейк/стоп).
На этом хорошие новости закончились.
При использовании симметричного тейк/стопа добавляются равновероятные события.
Так может сработать тейк, стоп или одно из трех.
Таким образом из 80% вероятности достижения тейка остается всего 27%.
В случае установки стопа по цене открытия дня ситуация не лучше, из 10 дней — 2 по 0,5 литра убытка против 8 дней по 100 грамм прибыли.
Что ниже точки равновесия — система генерирует убытки.
Тут и сказочке конец, кто не слушал молодец.
Простыми словами: использование стопов не уменьшает ваши убытки — напротив увеличивает их.
В принципе это все, что нужно знать о стопах, алготрейдинге и вообще о бирже.
Что же делать? Спросите вы меня… Выход есть — изменить параметры системы.
Вернемся к графикам
Увеличение потенциального дохода до 0,2 процентов при снижении вероятности его достижения до 70%
дает нам 50% годовых, теже 2 дня по 0,5 против 7 дней по 0.2, ежедневная прибыль 4 сотых процента движения базового инструмента.
Система протестирована и неплохо работает на высоколиквидных маловолатильных инструментах. Как по настоящему прикрыть ваши задницы, в смысле депозиты расскажу в следующий раз, а пока держите скрипт расчета цен открытия позиции.
скопируйте его в файл, смените расширение на html, откройте в любом браузере.
Кнопка Laod data прочитает котировки с mdf, из-за ограничений безопасности javascript скачанный файл надо открыть, для этого вторая кнопка.
Читаются котировки инструментов входящих в индекс ММВБ на дату написания скрипта, где то 2 года назад, хотите что то добавить — добавьте коды инструментов в строку запроса.
Если получите ошибку 500 прочитайте данные еще раз, это внутренняя ошибка сервера.
На экране появится табличка в светофорном стиле, зеленый цвет — актив лучше рынка, даже если цена снижалась, красный — хуже, даже если цена росла.
Можете сменить период свечи на недельный — поставьте период = 8, заодно проверите систему AlexChi лучшие бумаги недели, или часовой период = 6,
не забудьте поменять период запроса данных — расчетная переменная StartDate
Сортировка таблицы по доходности предпоследней свечи — хотите по последней найдите в тексте sort, поменяйте индекс массива на 0.
last — последняя цена, Buy — открытие + 0,5%, Sell — открытие — 0,5 %
Дальше интересней — не переключайтесь....
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<body>
<a href="SomeLink" id="ButtonLoadData" download><input type='button' class='button-css' value='Load data...' /></a>
<input type="file" onChange="readFiles(this);" multiple />
<br>
<br>
<table id="table" border="1"></table>
<script> // загрузка файла
var DimTikers = [];
var DimColumns = [];
var iMoex;
/*
var Cost = {
var Period;
var Open;
var Close;
var Min;
var Max;
var Percent;
};
var DimTiker={
var Tiker;
var Dividends[];
var Cost = [];
};
*/
function readFiles(e)
{
const files = e.files;
readOneFile(0,files);
function readOneFile(indexFiles,files){
if (indexFiles>=files.length) {
return;
}
const file = files[indexFiles];
const reader = new FileReader();
reader.addEventListener('loadend', event => {
let content = event.target.result;
let rows = content.split(/\r\n|\r|\n/g);
for (j=0; j<rows.length; ++j) {
let row = rows[j].split(";");
if (row[0]!="<TICKER>" && rows[j] ) {
PosTiker=DimTikers.findIndex(item => item.Tiker == row[0]);
if (PosTiker==-1) {PosTiker=DimTikers.length; DimTikers[PosTiker]={};DimTikers[PosTiker].Cost=[];}
DimTikers[PosTiker].Tiker=row[0];
if (DimColumns.findIndex(item => item==row[2])==-1) DimColumns[DimColumns.length]=row[2];
DimTikers[PosTiker].Cost[row[2]]={Period:row[2], Open:row[4], Close:row[7],
Low:row[6], High:row[5],
Percent:( row[4]==0 ? 0 : Math.trunc(10000* (row[7]-row[4])/row[4] )/100) };
}
}
if (indexFiles+1==files.length){
DimColumns.sort(function(a,b){
//console.log("-"+a+" "+b);
if (a>b) return -1; else if (a<b) return 1; else return 0;
}
);
for (let i = 0; i < DimTikers.length; i++) {
for (let j=0; j<DimColumns.length; j++){
try {
DimTikers[i].Cost[DimColumns[j]].Percent = (DimTikers[i].Cost[DimColumns[j+1]].Close==0) ? 0 :
Math.trunc(10000*(DimTikers[i].Cost[DimColumns[j]].Close-DimTikers[i].Cost[DimColumns[j+1]].Close)
/DimTikers[i].Cost[DimColumns[j+1]].Close)/100;
}
catch{
try {
DimTikers[i].Cost[DimColumns[j]].Percent = (DimTikers[i].Cost[DimColumns[j]].Open==0) ? 0 :
Math.trunc(10000*(DimTikers[i].Cost[DimColumns[j]].Close-DimTikers[i].Cost[DimColumns[j]].Open)
/DimTikers[i].Cost[DimColumns[j]].Open)/100;
}
catch{
}
}
}
}
DimTikers.sort(function(a,b){ if (a.Cost[DimColumns[1]].Percent<b.Cost[DimColumns[1]].Percent) return 1; else if (a.Cost[DimColumns[1]].Percent>b.Cost[DimColumns[1]].Percent) return -1; else return 0;});
//console.log(DimTikers);
let table = document.querySelector('#table');
let tr = document.createElement('tr');
let td = document.createElement('td');
tr.appendChild(td);
let td3 = document.createElement('td'); //Last
td3.innerHTML='Last';
tr.appendChild(td3);
let td1 = document.createElement('td'); //Buy
td1.innerHTML='Buy';
tr.appendChild(td1);
let td2 = document.createElement('td'); //Sell
td2.innerHTML='Sell';
tr.appendChild(td2);
for (let i=0; i<DimColumns.length; i++){
let td = document.createElement('td');
td.innerHTML=DimColumns[i].substring(6,8)+"."+DimColumns[i].substring(4,6);
tr.appendChild(td);
}
table.appendChild(tr);
iMoex=DimTikers.find(item => item.Tiker == "Индекс МосБиржи");
for (let i = 0; i < DimTikers.length; i++) {
let tr = document.createElement('tr'); //align="left"
let td = document.createElement('td'); td.style.align="right";
td.innerHTML=DimTikers[i].Tiker;
tr.appendChild(td);
let td3 = document.createElement('td'); td3.style.align="left"; //last
td3.innerHTML=(DimTikers[i].Cost[DimColumns[0]].Close).toString().replace(".",",");
tr.appendChild(td3);
let td1 = document.createElement('td'); td1.style.align="left"; //buy
//td1.innerHTML=(Math.trunc(100*(DimTikers[i].Cost[DimColumns[1]].High-(DimTikers[i].Cost[DimColumns[1]].High-DimTikers[i].Cost[DimColumns[2]].Low)/3))/100).toString().replace(".",",");
td1.innerHTML=(Math.trunc(100*(DimTikers[i].Cost[DimColumns[1]].Open*1.005))/100).toString().replace(".",",");
console.log(""+DimTikers[i].Tiker+" "+DimTikers[i].Cost[DimColumns[1]].High+" "+DimTikers[i].Cost[DimColumns[2]].Low+" "+(DimTikers[i].Cost[DimColumns[1]].High-DimTikers[i].Cost[DimColumns[2]].Low)/3);
tr.appendChild(td1);
let td2 = document.createElement('td'); td2.style.align="left"; //Sell
//td2.innerHTML=(Math.trunc(100*(DimTikers[i].Cost[DimColumns[1]].High-(DimTikers[i].Cost[DimColumns[1]].High-DimTikers[i].Cost[DimColumns[2]].Low)/1.5))/100).toString().replace(".",",");
td2.innerHTML=(Math.trunc(100*(DimTikers[i].Cost[DimColumns[1]].Open*0.995))/100).toString().replace(".",",");
tr.appendChild(td2);
let summa=0;
for (let j=0; j<DimColumns.length; j++){
let td = document.createElement('td');
let color="black";
try{
if (iMoex.Tiker==DimTikers[i].Tiker)
color="black";
else
color = (iMoex.Cost[DimColumns[j]].Percent < DimTikers[i].Cost[DimColumns[j]].Percent) ? "green":"red";
}
catch {
color="yellow";
}
try {
td.innerHTML = '<span style="color: '+color+';">'+DimTikers[i].Cost[DimColumns[j]].Percent.toString().replace(".",",")+'</span>';
}
catch {
}
tr.appendChild(td);
}
table.appendChild(tr);
}
}
});
reader.readAsText(file);
readOneFile(indexFiles+1,files);
}
}
</script>
<script>
var myUrl0='https://mfd.ru/export/handler.ashx/IMOEX_1week.txt?TickerGroup=14&Tickers=140335&Alias=false&Alias149952=IMOEX&Period=7&timeframeValue=1&timeframeDatePart=day&';
var myUrl='https://mfd.ru/export/handler.ashx/mfdexport.txt?TickerGroup=16&Tickers=145325%2C248695%2C246238%2C59186%2C237123%2C251365%2C231825%2C56252%2C202443%2C66525%2C66538%2C41369%2C183%2C258%2C330%2C336%2C121191%2C140335%2C489%2C629%2C632%2C832%2C82984%2C716%2C51353%2C826%2C913%2C948%2C330%2C336%2C121191%2C140335%2C489%2C629%2C632%2C832%2C82984%2C716%2C51353%2C826%2C913%2C948%2C1019%2C1279%2C103315%2C1373%2C54102%2C1383%2C246271%2C1389%2C1463%2C1464%2C913%2C948%2C1019%2C1279%2C103315%2C1373%2C54102%2C1383%2C246271%2C1389%2C1463%2C1476%2C1503%2C1542%2C1543%2C1613%2C1614%2C1658%2C37859%2C1712%2C107301&Alias=false&Period=7&timeframeValue=1&timeframeDatePart=day&';
var myUrl1='&SaveFormat=0&SaveMode=0&FileName=mfdexport.txt&FieldSeparator=%253b&DecimalSeparator=.&DateFormat=yyyyMMdd&TimeFormat=HHmmss&DateFormatCustom=&TimeFormatCustom=&AddHeader=true&RecordFormat=0&Fill=false';
var now = new Date();
var enddata = strRight(2,"0"+now.getDate().toString())+"."+strRight(2,"0"+(now.getMonth()+1).toString())+"."+now.getFullYear().toString();
var Month=now.getMonth()-1;var year=now.getFullYear();
if (Month<0) {Month=12+Month+1; year--;};
var startdata = strRight(2,"0"+now.getDate().toString())+"."+strRight(2,"0"+Month.toString())+"."+(year).toString();
myUrl2=myUrl+"StartDate="+startdata+"&EndDate="+enddata+myUrl1;
document.getElementById('ButtonLoadData').href = myUrl2;
function strRight(n,str){
return str.substring(str.length-n,str.length);
// параметр e - объект файла из элемента выбора
/**
*
* @param data данные из файла CSV
* @param delimiter разделитель, используемый в файле
* @param firstRow пропускать или оставлять первую строку - заголовок
*/
function convertCSV2Array(data, delimiter = ',', firstRow = false)
{
return data
.slice(firstRow ? data.indexOf('\n') + 1 : 0)
.split('\n')
.map(row => row.split(delimiter));
}
}
</script>
</body>
</html>
Итого получаем:
плюсовое направление: 80% вероятности движения 0.1%.
минусовое направление: 20% вероятности движения 0.5%
мат. ожидание:
плюсовое: 0.1 * 0.8 = 0.08%
минусовое: 0.5 * 0.2 = 0.1%
Где я ошибся?
Все верно, отрицательное.
Изменив параметры получим положительное 7 дней по 0.2 плюс, 2 дня по 0.5 минус
И как вот из этого: можно сделать вывод: Надо бы определить сначала и в цифрах изобразить, что значит направленное. Тогда и проверить что-то возможно станет.
У каждой свечи есть тень до открытия, у 80 процентов свечей эта тень меньше полпроцента движения цены, таким образом мы может предположить, что и последующих свечей тень не превысит полпроцента. В таком случае если цена прошла этот порог, то врятли развернется, поскольку первое предположение будет не верно
Простите, если опять не понятно, как смог… Мой талант преподавателя иссяк много лет назад. 🙂
1) Если минимум текущей свечи отклонился от её открытия более чем на 0,5%, то ничто не мешает в этой же свече переписать максимум так же более чем на 0,5% от открытия. Как-то это в статистике учитывается?
2) Не ясно в какой момент входить и в какую сторону. Преодоление минимального отклонения в 0,5% никак статистически не влияет на последующее поведение цены в оставшееся время свечи. Оно по-прежнему равновероятно в обе стороны. Легко проверяется тестами.
1.Все верно, ничего не мешает, но случится это только для 20 свечей из ста (согласно статистике)
2.Опять все верно, но стоит учесть, что движение цены, в этом направлении, 70 свечей из 100 достигнет 0.7%. Не ясно только когда, и какие движение будут этому предшествовать.
Какие выводы по представленному подходу?
Вы должны выбрать период свечей, в котором статистика наиболее проявлена в нужном ключе. Для одного инструмента он более-менее стабилен.
Это всё слегка взлетит только для некоторых инструментов.
В общем случае устойчивого заработка нет. Мелкие выигрыши по 0,1-0,2% съест комиссия более чем наполовину. А добьют накопленный небольшой плюс те 20% случаев больших сливов, когда будут случаться походы к противоположным полюсам свечи.
Выводы интересны тем, что при уменьшении вероятности получения прибыли она растет.
Наилучшие показатели были 0.54% вход, 5% тейк.
Феномен не изучен, поэтому удивляет. 🙂
Следующим шагом надо увидеть, что есть инструменты и ТФ, при которых входить надо вообще в другую сторону.
А общее замечание — в вашем подходе нужно использовать логарифмы цен. Сейчас у Вас % вверх ничем не ограничен, а вниз только 100%. Статистика перекошена.
Использование сложных математических расчетов никак не влияет на движение цены. Движения случайны по определению, но есть какая то вероятность, что цена придет в нужную мне точку, весь расчет только на это.
Вероятности, на которые Вы уповаете, надо уметь считать. Или получить иным достоверным способом.
Сгенерируйте СБ. Это тоже нетривиальная задача — сгенерировать свечи, полученные по СБ. Постройте ваши линии на них. Характеристика линии входа при СБ — нулевое матожидание результата для каждой точки входа при нулевой комиссии.
Затем откладываете двойную комиссию от неё вверх и вниз для данной цены. Получите коридор, в котором выходы будут убыточными из-за комиссии.
После чего можете строить линию выходов реального инструмента. Для входа в продолжение она должна быть выше коридора входов, для входа на возврат — ниже коридора. Расстояния от выходов до коридора и есть средний доход на одну сделку (вх+вых).