Автор |
Сообщение |
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
По моему нет. Только Sell. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
В общем, все это время пытался решить эту проблему. Так и не решил. Естественно, пришлось залезть в кастомный тестер.
Код: |
for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{
bo.RawTextOutput( "\t" + "Positionsize: " + ( sig.PosSize * -1 - 2000 ) );
if( sig.IsEntry() )
bo.EnterTrade( i, sig.Symbol, sig.IsLong(), sig.Price, sig.PosSize );
if( sig.IsExit() )
bo.ExitTrade( i, sig.Symbol, sig.Price );
if( sig.IsScale() AND sig.Type == 5 )
bo.ScaleTrade( i, sig.Symbol, True, sig.Price, sig.PosSize );
if( sig.IsScale() AND sig.Type == 6 )
{
trade = bo.FindOpenPos( sig.Symbol );
bo.RawTextOutput( "\t" + "trade.shares: " + trade.Shares );
bo.RawTextOutput( "\t" + "sig.PosSize: " + (sig.PosSize * -1 - 2000) );
if( trade AND (trade.Shares <= ( sig.PosSize * -1 - 2000 )) )
{
bo.RawTextOutput( "\t" + "This is exit in scaleout");
bo.ExitTrade( i, sig.Symbol, sig.Price );
}
else
{
bo.RawTextOutput( "\t" + "This is scaleout");
bo.ScaleTrade( i, sig.Symbol, False, sig.Price, sig.PosSize );
}
}
}
|
Когда sig.Type == 6, это означает ScaleOut. Дальше идет проверка: если количество акций в открытой позиции меньше или равно количеству акций по сигналу продажи, то осуществляется закрытие позиции. Иначе - просто уменьшение. Так вот, в детальном отчете видно, что ScaleOut в самом деле меняется на Exit, но, видимо, какой-то недокументированный флаг открытой позиции все равно отмечает позицию открытой.
Если я тестирую позиции только в лонг или только в шорт, все идет нормально. Но если, допустим, у меня открытвается позиция в лонг, потом пару закрывается (частично и полностью), а потом открывается позиция в шорт, начинается какой-то треш с появлением сигнала за закрытие лонга. (Это в случае, если в тестере стоит включенным флаг Reverse entry signal forces exit). Если же этого флага нет, то сигнал в шорт вообще игнорируется, как будто позиция в лонг все еще открыта.
Ну вот разве я многого хочу? Мне нужно, чтобы торговая система следила за размером открытой позиции. И как только все акции проданы, позиция должна быть закрыта. В Tradestation это в одну строчку делается. Здесь же мало того, что сама концепция управление позицией очень сложная, так еще и не работает... А на форуме Амиброкера нашел ответ Томаша на вопрос о Low level Custom Backtesting - вы, говорит, вообще это не трогайте. Не для вас оно сделано! Ну, офигеть теперь!. |
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
А если взять цикл как на первой странице обсуждения и добавить туда проверку на размер позиции == 0 и если равно 0, то закрывать? |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
В смысле, учитывать размер текущей позиции отдельными переменными, и вообще не лезть в кастомный бэктестер? Потому что вот это:
Код: |
if (trade.Shares <= ( sig.PosSize * -1 - 2000 )) |
будет работать только внутри него... Ну, наверное, это возможно. Просто, не хотелось бы. Потому что есть специальный инструментарий для таких вещей (но он почему-то не очень работает), и не хочется возвращаться к каким-то суррогатам.
Если интересно, вот, собственно, сам код:
Код: |
//Кастомный тестер
SetCustomBacktestProc( "" );
if( Status( "action" ) == actionPortfolio )
{
bo = GetBacktesterObject(); // Get backtester object
bo.PreProcess(); // Do pre-processing
doDetailedLog = GetOption( "PortfolioReportMode" ) == 1;
dt = DateTime();
for( i = 0; i < BarCount; i++ ) // Loop through all bars
{
//Следующий блок посвящен исключительно формированию детального лога и не влияет на результат
if( doDetailedLog )
{
bo.RawTextOutput( NumToStr( dt[i], formatDateTime ) );
cntEntrySig = bo.GetSignalQty( i, 1 );
cntExitSig = bo.GetSignalQty( i, 2 );
bo.RawTextOutput( "\t" + "# Entry Signals=" + cntEntrySig + ". # Exit Signals=" + cntExitSig + "." );
entrySigList = "\tEntry Signals(score): ";
exitSigList = "\tExit Signals: ";
for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{
if( sig.IsEntry() )
{
if( sig.IsLong )
dir = "Buy";
else
dir = "Short";
entrySigList += sig.Symbol + "=" + dir + "(" + sig.PosScore + "), ";
}
if( sig.IsExit() )
{
bo.RawTextOutput( "\t Exit Sig: " + sig.Symbol + " Price=" + sig.Price + " isExit=" + sig.isExit + " isLong=" + sig.IsLong + " reason=" + sig.Reason + " type=" + sig.type );
if( sig.IsLong )
dir = "Sell";
else
dir = "Cover";
exitSigList += sig.Symbol + "=" + dir + ", ";
}
}
bo.RawTextOutput( entrySigList );
bo.RawTextOutput( exitSigList );
}
//Здесь начинается кастомный бэктестинг
//На каждом баре проводится проверка наличия сигналов и их обработка
for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{
bo.RawTextOutput( "\t" + "Positionsize: " + ( sig.PosSize * -1 - 2000 ) ); // это просто комментарий, который отобразится в детальном логе
if( sig.IsEntry() )
bo.EnterTrade( i, sig.Symbol, sig.IsLong(), sig.Price, sig.PosSize );
if( sig.IsExit() )
bo.ExitTrade( i, sig.Symbol, sig.Price );
if( sig.IsScale() AND sig.Type == 5 )
bo.ScaleTrade( i, sig.Symbol, True, sig.Price, sig.PosSize );
//В следующем блоке содержится непонятная проблема
//Здесь обрабатываются сигналы на ScaleOut
if( sig.IsScale() AND sig.Type == 6 )
{
trade = bo.FindOpenPos( sig.Symbol );
bo.RawTextOutput( "\t" + "trade.shares: " + trade.Shares );
bo.RawTextOutput( "\t" + "sig.PosSize: " + (sig.PosSize * -1 - 2000) );
// Здесь я проверяю наличие открытой позиции, и сравниваю размер позиции с размером сигнала на Scaleout.
// Если все условия утвердительны, ScaleOut заменяется на действие Exit
if( trade AND (trade.Shares <= ( sig.PosSize * -1 - 2000 )) )
{
bo.RawTextOutput( "\t" + "This is exit in scaleout");
bo.ExitTrade( i, sig.Symbol, sig.Price );
}
else // иначе, оставляю ScaleOut
{
bo.RawTextOutput( "\t" + "This is scaleout");
bo.ScaleTrade( i, sig.Symbol, False, sig.Price, sig.PosSize );
}
}
}
// Стандартная обработка служебных вещей
bo.HandleStops( i ); // Handle programmed stops at this bar
bo.UpdateStats( i, 1 ); // Update MAE/MFE stats for bar
bo.UpdateStats( i, 2 ); // Update stats at bar's end
} // End of for loop over bars
bo.PostProcess(); // Do post-processing
}
//Код основной программы. Сперва выполняется он.
//Вход в лонг выполняется 7 октября, выход 28 октября и 7 ноября 2019
//Вход в шорт 20 февраля 2020, выход 10 марта 2020
//Входы и выходы - скейлами
Sell = 0;
Cover = 0;
Buy = IIf( DateNum() == 1191007, sigScaleIn, IIf( ( DateNum() == 1191028 OR DateNum() == 1191107 ), sigScaleOut, 0 ) );
Short = IIf( DateNum() == 1200220, sigScaleIn, IIf( DateNum() == 1200310, sigScaleOut, 0 ) );
//Изменяем размер выхода, в зависимости от даты
SetPositionSize( IIf( DateNum() == 1191028, 50, 100 ), spsShares );
|
|
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
Вообще любопытно. По идее bo.ExitTrade должен бы закрывать позицию полностью. Странно.
Гляну... |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
Спасибо.
Кстати, там при запуске этого кода в тестера выскакивает непонятная ошибка. Непонятно, о чем она говорит. Нажимаешь окей, и отчёт теста составляется. Но она возникает только если тест запускается сразу и по лонгам и по шортам. А также, должен быть включён флаг в тестере о закрытии текущей позиции при сигнале на открытие позиции в обратную сторону. В противном случае все отрабатывается без всплывающего предупреждения.
P.S. Еще один момент. В детальном логе в моменты ScaleOut и Exit отмечается, что присутствуют 2 сигнала (во время входа - один). Что это за второй сигнал, я так и не понял. Строчка #22 в коде
Код: |
cntExitSig = bo.GetSignalQty( i, 2 ); |
находит 2 сигнала на выход каждый раз, когда происходит уменьшение позиции. |
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
По моему забыл
Код: |
bo.ProcessTradeSignals( i ); |
Код: |
//Кастомный тестер
SetCustomBacktestProc( "" );
if( Status( "action" ) == actionPortfolio )
{
bo = GetBacktesterObject(); // Get backtester object
bo.PreProcess(); // Do pre-processing
for( i = 0; i < BarCount; i++ ) // Loop through all bars
{
//Здесь начинается кастомный бэктестинг
//На каждом баре проводится проверка наличия сигналов и их обработка
for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{
if( sig.IsEntry() )
bo.EnterTrade( i, sig.Symbol, sig.IsLong(), sig.Price, sig.PosSize );
if( sig.IsExit() )
bo.ExitTrade( i, sig.Symbol, sig.Price );
if( sig.IsScale() AND sig.Type == 5 )
bo.ScaleTrade( i, sig.Symbol, True, sig.Price, sig.PosSize );
//В следующем блоке содержится непонятная проблема
//Здесь обрабатываются сигналы на ScaleOut
if( sig.IsScale() AND sig.Type == 6 )
{
trade = bo.FindOpenPos( sig.Symbol );
// Здесь я проверяю наличие открытой позиции, и сравниваю размер позиции с размером сигнала на Scaleout.
// Если все условия утвердительны, ScaleOut заменяется на действие Exit
if( trade AND (trade.Shares <= ( sig.PosSize * -1 - 2000 )) )
{
bo.ExitTrade( i, sig.Symbol, sig.Price );
}
else // иначе, оставляю ScaleOut
{
bo.ScaleTrade( i, sig.Symbol, False, sig.Price, sig.PosSize );
}
bo.ProcessTradeSignals( i );
}
}
// Стандартная обработка служебных вещей
bo.HandleStops( i ); // Handle programmed stops at this bar
bo.UpdateStats( i, 1 ); // Update MAE/MFE stats for bar
bo.UpdateStats( i, 2 ); // Update stats at bar's end
} // End of for loop over bars
bo.PostProcess(); // Do post-processing
}
//Код основной программы. Сперва выполняется он.
//Вход в лонг выполняется 7 октября, выход 28 октября и 7 ноября 2019
//Вход в шорт 20 февраля 2020, выход 10 марта 2020
//Входы и выходы - скейлами
Sell = Cover = 0;
Buy = IIf( DateNum() == 1201201, 1, IIf( ( DateNum() == 1201203 OR DateNum() == 1201207 ), sigScaleOut, 0 ) );
Short = IIf( DateNum() == 1201209, 1, IIf( DateNum() == 1201211, sigScaleOut, 0 ) );
//Изменяем размер выхода, в зависимости от даты
SetPositionSize( IIf( DateNum() == 1201201 OR DateNum() == 1201209, 100, 50 ), spsShares );
|
|
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
Не. Нифига. Не оно.. Не работает. Сукаааа. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
В общем так.
Тестирование при помощи porfolio backtester interface проходит 2 фазы
1 фаза - обработка сигналов
2 фаза - симуляция торговли.
То, что мы описываем после SetOption("UseCustomBacktestProc", True ) это вторая фаза. Из второй фазы повлиять на первую невозможно.
Поэтому если после сигнала Buy никакие другие сигналы на открытие позиции просто не попадают во вторую фазу пока не будет сигнала Sell. Невозможно закрыть позицию используя sigScaleOut. Т.е. физически она станет равной 0 и даже будет считаться закрытой, но последующий сигналы тупо не будут приходить т.к. они фильтруются в первой фазе.
И если противоположный сигнал все таки открыть можно используя Reverse entry signal forces exit, то сигнал в ту же сторону вообще никак не откроешь.
А в остальном код, после моей поправки, нормально работает. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
Олег,
вот это:
Код: |
bo.ProcessTradeSignals( i ); |
вставлять никак нельзя. Кастомный бэктестер работает в трех режимах: hi-level, mid-level, low-level. Эта операция относится к среднему уровню. Мы же занимаемся ручной обработкой сигналов, поэтому выбираем low-level. Если вставить эту функцию, она будет конфликтовать с тем, что мы делали до этого (манипулировали сигналами).
В Амиброкере есть сниппеты кодов:
Mid-level:
Код: |
SetCustomBacktestProc("");
if (Status("action") == actionPortfolio)
{
bo = GetBacktesterObject(); // Get backtester object
bo.PreProcess(); // Do pre-processing (always required)
for (i = 0; i < BarCount; i++) // Loop through all bars
{
for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{
// do your custom signal processing
}
bo.ProcessTradeSignals( i ); // Process trades at bar (always required)
}
bo.PostProcess(); // Do post-processing (always required)
}
|
Low-level:
Код: |
SetCustomBacktestProc("");
if (Status("action") == actionPortfolio)
{
bo = GetBacktesterObject(); // Get backtester object
bo.PreProcess(); // Do pre-processing
for (i = 0; i < BarCount; i++) // Loop through all bars
{
for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{ // Loop through all signals at this bar
if( sig.IsEntry() )
bo.EnterTrade( i, sig.Symbol, sig.IsLong(), sig.Price, sig.PosSize );
if( sig.IsExit() )
bo.ExitTrade( i, sig.Symbol, sig.Price );
}
bo.HandleStops( i ); // Handle programmed stops at this bar
bo.UpdateStats( i, 1 ); // Update MAE/MFE stats for bar
bo.UpdateStats( i, 2 ); // Update stats at bar's end
} // End of for loop over bars
bo.PostProcess(); // Do post-processing
} |
В Low-level видно, что bo.ProcessTradeSignals( i ); отсутствует.
Все равно, спасибо. Буду копать дальше. |
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
Попробуй мой код и увидишь, что с он работает корректно и не открывает 2 позиции. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
В любом случае решить проблему с сигналами при помощи porfolio backtester невозможно. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
Astrobiolog
Зарегистрирован: 27.01.2013
Сообщения: 66
|
000 писал(а): |
В любом случае решить проблему с сигналами при помощи porfolio backtester невозможно. |
Боюсь, ты ошибаешься. Возможно. Я решил. Но это очень сложно и нетривиально. |
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
Делись. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
Astrobiolog
Зарегистрирован: 27.01.2013
Сообщения: 66
|
Я тоже несколько лет уже назад попытался пойти по пути sigScaleIn/sigScaleOut в low-level CBT.
Но та проблема, которая описана здесь, что код Томаша работает так - можно отлиться (sigScaleOut) в ноль - однако ты будешь все-равно в позиции (ноль, но в позиции - ты как-бы должен сам следить, когда отлился в ноль - и имплементировать НЕ sigScaleOut а Exit !!!) привела меня к тому, что я этим не пользуюсь (хотя и так возможно).
Лучше и проще присваивать идентификаторы вообще каждому входу "вручную", если можно так сказать, из первой фазы во вторую (например через переменную sig.Score, есть и еще варианты - внимательно RTFM) и отслеживать судьбу каждого входа отдельно в CBT low-level.
Надеюсь, понятно, что речь идет о множественных входах-выходах (доливках-отливках по одному и тому же тикеру) это вообще-то тождественно sigScaleIn/Out, но однако так, что все работает как песня.
Надеюсь, то, что вышенаписано понятно ? |
|
|
Посмотреть профиль Отправить личное сообщение |
|
|
|
Следующая тема
Предыдущая тема
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах Вы не можете вкладывать файлы Вы не можете скачивать файлы
|
|