Автор |
Сообщение |
ustas235
Зарегистрирован: 13.03.2016
Сообщения: 5
|
Добрый день.
Пытаюсь написать стратегию, где будет происходить открытие нескольких сделок в одну сторону, с последовательным их закрытием.
Как я понял АМИ по умолчанию может открывать только одну сделку в одну сторону. Поэтому прошу помощи по двум возможным вариантам:
1. Доливка по существующие позиции. Можно пример кода и необходимые настройки в тестере, если таковые нужны? Просто фраза: "Для этого есть SigScaleIn/SigScaleOut", мне мало о чем говорит. Куда их нужно "прикрутить", чтобы ими пользоваться?
2. Можно разрешить Ами иметь одновременно несколько открытых позиций по одной бумаге. Я добавил строку в коде:
Код: |
SetBacktestMode( backtestRegularRawMulti ); |
и попробовал создать элементарный пример:
Код: |
Short[5]=1; Short[30]=1; Short[50]=1;Cover[80]=1; |
Данные сделки на графике видны, но при тестированиии открывается только одна сделка, и еще одна левая непонятно откуда взявшаяся:
https://gyazo.com/8ac84210a913027d4c90fee29841953f |
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
В Ами есть 3 способа открыть несколько сделок по одной бумаге.
1. Использовать SetBacktestMode( backtestRegularRawMulti );
Ты там сделал почти все правильно, но у тебя все деньги ушли в первую сделку. Следует ограничить сайз открываемой позиции, тогда откроются все сделки как и планировальось. Добавь
Код: |
SetPositionSize(1, 4); |
Тут следует иметь ввиду, что закрыть можно только все позиции сразу.
2. Использовать SigScaleIn/SigScaleOut. В хелпере нормальный пример. И даже не один.
Example 1: dollar-cost averaging (each month you buy stocks for fixed dollar amount)
Код: |
FixedDollarAmount = 500;
MonthBegin = Month() != Ref( Month(), -1 );
FirstPurchase = Cum( MonthBegin ) == 1;
Buy = IIf( FirstPurchase, 1, // True (or 1) represents regular buy signal
IIf( MonthBegin, sigScaleIn, // each month increase position
0 ) ); // otherwise no signal
Sell = 0; // we do not sell
PositionSize = FixedDollarAmount; |
Кроме того на форуме 1000000 раз это обсуждали. Воспользуйся поиском по форуму. Если что не поймешь, то тогда задавай конкретные вопросы ибо еще раз описывать как это работает и как использовать чето ломает.
Следует иметь ввиду, что в отчете тестера доливки отливки не отображаются. Отображается только начальное открытие позиции и ее закрытие.
3. Можно создать в базе клоны инструментов и разные сделки проводить на "параллельных" бумагах. Это тут тоже обсуждалось. Можно найти. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
У меня вопрос в тему. В моей системе предусмотрены два отдельных условия для входа (на разных ценовых уровнях), и вот на одном баре сработали оба эти условия. Мне нужно, чтобы на этом баре произошли два входа на обозначенных уровнях. Возможно ли это сделать?
(По форуму посмотрел, ничего подходящего не нашел).
В интернете попался комментарий Томаша на сходную ситуацию - тот предлагает использовать кастомный бэктестинг. Но, может есть способ по-проще? |
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
Сделай клон бумаги и торгуй не один, а два символа. Тогда легко можно на одном баре иметь 2 входа по разным ценам. |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
000 писал(а): |
Сделай клон бумаги и торгуй не один, а два символа. Тогда легко можно на одном баре иметь 2 входа по разным ценам. |
Ну, не знаю. Как вариант, конечно, можно использовать. Но, по-моему, это не выход. Что если мне нужно использовать не два, а десять разных входов? Или если одни входы имеют логическую зависимость с другими? Похоже, все-таки придется углубляться в кастомный бэктестинг... |
|
|
Посмотреть профиль Отправить личное сообщение |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
Решение найдено! (Ну, почти. Почему почти - скажу ниже).
Итак, условия:
Вычисляются уровни для входа в длинную позицию выше рынка:
Код: |
Range = High - Low;
ThePivot = ( Low + High + Close ) / 3;
Resistance1 = ( ThePivot * 2 ) - Low;
Resistance2 = ThePivot + Range;
Resistance3 = Resistance1 + Range; |
Определяется дополнительный фильтр для входа:
Код: |
//Determining market's character
BullMarket = MA( Close, 4 ) > MA( Close, 9 ) and MA( Close, 9 ) > MA( Close, 18 ); |
Условие для первого входа:
Код: |
BullR2 = Cross( High, Ref( Resistance2, -1 ) ); |
Выход по трейлинг стопу:
Код: |
TrailLongStop = Ref( LLV( Low, TrailBar ), -1 ); |
То есть при пробитии уровня Resistance2 мы входим в длинную позицию одним лотом. Закрываем позицию трейлинг стопом.
Остальные условия я решил прописать внутри кода кастомного тестинга. А именно:
Дополнительный вход при пересечении уровня Resistance3 одним лотом, даже если предыдущий вход произошел на том же баре (как правило, это и происходит).
Код: |
if( trade.GetPrice( i, "H" ) >= enter3[i] AND trade.GetPrice( i - 1, "H" ) < enter3[i] AND trade.Shares < trade.RoundLotSize * 2 )
{
bo.RawTextOutput( DateOfBar[i] );
scaleSize = trade.RoundLotSize * trade.GetPrice( i, "C" );
bo.ScaleTrade( i, trade.Symbol, True, enter3[i], scaleSize );
} |
Прошу обратить внимание на переменную enter3. Это уровень второго входа. Но так как тестер оперирует портфелем символов, ему нельзя просто передать значение уровня вне контекста конкретного символа. Поэтому был создан отдельный временный символ с требуемым уровнем:
Цитата: |
AddToComposite( Ref( Resistance3, -1 ), "~Resistance3_" + Name(), "C", atcFlagDefaults | atcFlagEnableInBacktest );
enter3 = Foreign( "~Resistance3_" + trade.Symbol, "C" ); |
Дополнительное условие выхода: первое прибыльное открытие:
Код: |
if( trade.GetPercentProfit() > 0 )
{
bo.RawTextOutput( DateOfBar[i] );
bo.ExitTrade( i, trade.Handle, trade.GetPrice( i, "O" ) );
} |
Весь код целиком:
Код: |
SetOption( "MaxOpenPositions", 1 );
SetPositionSize( RoundLotSize, spsShares );
SetBacktestMode( backtestRegularRaw );
SetOption( "AllowSameBarExit", False );
SetOption( "HoldMinBars", 1 );
TrailBar = 4;
//Calculating pivots, R levels
Range = High - Low;
ThePivot = ( Low + High + Close ) / 3;
Resistance1 = ( ThePivot * 2 ) - Low;
Resistance2 = ThePivot + Range;
Resistance3 = Resistance1 + Range;
//Determining market's character
BullMarket = MA( Close, 4 ) > MA( Close, 9 ) and MA( Close, 9 ) > MA( Close, 18 );
//Long enter condition
BullR2 = Cross( High, Ref( Resistance2, -1 ) );
TrailLongStop = Ref( LLV( Low, TrailBar ), -1 );
Buy = BullR2 AND BullMarket;
BuyPrice = Max( Ref( Resistance2, -1 ), Open );
Sell = Cross( TrailLongStop, Low ) ;
SellPrice = Min( TrailLongStop, Open );
AddToComposite( Ref( Resistance3, -1 ), "~Resistance3_" + Name(), "C", atcFlagDefaults | atcFlagEnableInBacktest );
DateOfBar = DateNum();
SetCustomBacktestProc( "" );
if( Status( "action" ) == actionPortfolio )
{
bo = GetBacktesterObject(); // Get backtester object
bo.PreProcess(); // Do pre-processing
for( i = 1; i < BarCount; i++ ) // Loop through all bars
{
bo.UpdateStats( i, 0 ); // Update MAE/MFE stats for bar
for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{
// Loop through all signals at this bar
bo.RawTextOutput( DateOfBar[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 );
}
bo.HandleStops( i ); // Handle programmed stops at this bar
for( trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() )
{
enter3 = Foreign( "~Resistance3_" + trade.Symbol, "C" );
if( trade.GetPrice( i, "H" ) >= enter3[i] AND trade.GetPrice( i - 1, "H" ) < enter3[i] AND trade.Shares < trade.RoundLotSize * 2 ) // AND trade.BarsInTrade <= 1 )
{
bo.RawTextOutput( DateOfBar[i] );
scaleSize = trade.RoundLotSize * trade.GetPrice( i, "C" );
bo.ScaleTrade( i, trade.Symbol, True, enter3[i], scaleSize );
}
if( trade.GetPercentProfit() > 0 )
{
bo.RawTextOutput( DateOfBar[i] );
bo.ExitTrade( i, trade.Handle, trade.GetPrice( i, "O" ) );
}
}
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
}
Plot( C, "C", IIf( BullMarket, colorPaleGreen, colorGrey40 ), styleBar, Null, Null, 0, 0, -10 );
Plot( BullR2 AND bullmarket, "BullR2", colorRed, styleHistogram | styleOwnScale );
Plot( Ref( Resistance2, -1 ), "R2", colorGreen );
Plot( Ref( Resistance3, -1 ), "R3", colorBlue );
Plot( TrailLongStop, "TrailLongStop", colorGrey40, styleDashed ); |
|
|
|
Посмотреть профиль Отправить личное сообщение |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
Теперь немного картинок. График с отмеченными уровнями входов по двум уровням на одном баре и выходе на открытии следующего. А также фрагмент detail log, с записями этих входов и выхода. |
|
|
Посмотреть профиль Отправить личное сообщение |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
Некоторые нюансы.
1. Шаблоном low level Амиброкера это не предусмотрено, но если не разместить команду
Код: |
bo.UpdateStats( i, 0 ); |
в начале цикла, то тестирование проходит с ошибками.
2. Detail Log при кастомном бэктестинге имеет вид неприглядный. Поэтому пришлось использовать команду
Код: |
bo.RawTextOutput( DateOfBar[i] ); |
на каждом обработанном сигнале, для отображения дат этих событий.
Теперь, почему найденное решение меня не вполне удовлетворило. Дело в том, что целью было написание входа, который на Easylanguage выглядел так:
Цитата: |
buy next bar at Resistance2 stop;
buy next bar at Resistance3 stop; |
То есть, открывались две независимые сделки на разных уровнях. В то время, как у меня просто менялся размер позиции, если акция пересекала дополнительный уровень. EL код подразумевал независимое закрытие обеих позиций, а также открытие двух позиций по первому условию (если они происходили на разных барах). У меня же доливка возможна только по второму условию... Понятно, что на AFL можно прописать условия, идентичные условиям, написанным выше на EL, но это уже будет другой код. В любом случае, программа-минимум выполнена: способ отработать два входа на одном баре одного символа найден. |
|
|
Посмотреть профиль Отправить личное сообщение |
|
000
Site Admin
Зарегистрирован: 10.12.2007
Сообщения: 9106
|
Вау. Браво! |
_________________ ceterum censeo carthaginem esse delendam
Удачи. Олег. |
|
Посмотреть профиль Отправить личное сообщение Посетить сайт автора |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
000 писал(а): |
Вау. Браво! |
Спасибо! |
|
|
Посмотреть профиль Отправить личное сообщение |
|
AlexLan73
Зарегистрирован: 25.06.2012
Сообщения: 87
|
Спасибо, Вам за проделанную работу. Круто!!!! |
|
|
Посмотреть профиль Отправить личное сообщение |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
AlexLan73 писал(а): |
Спасибо, Вам за проделанную работу. Круто!!!! |
Да не за что! На самом деле, мне самому было очень интересно разбираться с этой проблемой. Но, вообще, конечно, тут осталось еще много неясностей - копать и копать |
|
|
Посмотреть профиль Отправить личное сообщение |
|
trashfx
Зарегистрирован: 27.06.2015
Сообщения: 90
|
rupiter писал(а): |
Дополнительное условие выхода: первое прибыльное открытие:
Код: |
if( trade.GetPercentProfit() > 0 )
{
bo.RawTextOutput( DateOfBar[i] );
bo.ExitTrade( i, trade.Handle, trade.GetPrice( i, "O" ) );
} |
Весь код целиком:
Код: |
SetOption( "MaxOpenPositions", 1 );
SetPositionSize( RoundLotSize, spsShares );
SetBacktestMode( backtestRegularRaw );
SetOption( "AllowSameBarExit", False );
SetOption( "HoldMinBars", 1 );
TrailBar = 4;
//Calculating pivots, R levels
Range = High - Low;
ThePivot = ( Low + High + Close ) / 3;
Resistance1 = ( ThePivot * 2 ) - Low;
Resistance2 = ThePivot + Range;
Resistance3 = Resistance1 + Range;
//Determining market's character
BullMarket = MA( Close, 4 ) > MA( Close, 9 ) and MA( Close, 9 ) > MA( Close, 18 );
//Long enter condition
BullR2 = Cross( High, Ref( Resistance2, -1 ) );
TrailLongStop = Ref( LLV( Low, TrailBar ), -1 );
Buy = BullR2 AND BullMarket;
BuyPrice = Max( Ref( Resistance2, -1 ), Open );
Sell = Cross( TrailLongStop, Low ) ;
SellPrice = Min( TrailLongStop, Open );
AddToComposite( Ref( Resistance3, -1 ), "~Resistance3_" + Name(), "C", atcFlagDefaults | atcFlagEnableInBacktest );
DateOfBar = DateNum();
SetCustomBacktestProc( "" );
if( Status( "action" ) == actionPortfolio )
{
bo = GetBacktesterObject(); // Get backtester object
bo.PreProcess(); // Do pre-processing
for( i = 1; i < BarCount; i++ ) // Loop through all bars
{
bo.UpdateStats( i, 0 ); // Update MAE/MFE stats for bar
for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
{
// Loop through all signals at this bar
bo.RawTextOutput( DateOfBar[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 );
}
bo.HandleStops( i ); // Handle programmed stops at this bar
for( trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() )
{
enter3 = Foreign( "~Resistance3_" + trade.Symbol, "C" );
if( trade.GetPrice( i, "H" ) >= enter3[i] AND trade.GetPrice( i - 1, "H" ) < enter3[i] AND trade.Shares < trade.RoundLotSize * 2 ) // AND trade.BarsInTrade <= 1 )
{
bo.RawTextOutput( DateOfBar[i] );
scaleSize = trade.RoundLotSize * trade.GetPrice( i, "C" );
bo.ScaleTrade( i, trade.Symbol, True, enter3[i], scaleSize );
}
if( trade.GetPercentProfit() > 0 )
{
bo.RawTextOutput( DateOfBar[i] );
bo.ExitTrade( i, trade.Handle, trade.GetPrice( i, "O" ) );
}
}
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
}
Plot( C, "C", IIf( BullMarket, colorPaleGreen, colorGrey40 ), styleBar, Null, Null, 0, 0, -10 );
Plot( BullR2 AND bullmarket, "BullR2", colorRed, styleHistogram | styleOwnScale );
Plot( Ref( Resistance2, -1 ), "R2", colorGreen );
Plot( Ref( Resistance3, -1 ), "R3", colorBlue );
Plot( TrailLongStop, "TrailLongStop", colorGrey40, styleDashed ); |
|
Do not use Addtocomposite and Foreign but Static variables in order to pass from phase 1 to phase 2 because it is faster.
So ...
Код: |
StaticVarSet( "~Resistance3_" + Name(), Ref( Resistance3, -1 ) );
SetCustomBacktestProc( "" );
if( Status( "action" ) == actionPortfolio )
{
.....
enter3 = StaticVarGet( "~Resistance3_" + trade.Symbol );
.....
} |
|
|
|
Посмотреть профиль Отправить личное сообщение |
|
rupiter
Зарегистрирован: 16.09.2013
Сообщения: 75
|
trashfx писал(а): |
Do not use Addtocomposite and Foreign but Static variables in order to pass from phase 1 to phase 2 because it is faster.
So ...
Код: |
StaticVarSet( "~Resistance3_" + Name(), Ref( Resistance3, -1 ) );
SetCustomBacktestProc( "" );
if( Status( "action" ) == actionPortfolio )
{
.....
enter3 = StaticVarGet( "~Resistance3_" + trade.Symbol );
.....
} |
|
Спасибо, учту. |
|
|
Посмотреть профиль Отправить личное сообщение |
|
|
|
Следующая тема
Предыдущая тема
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах Вы не можете вкладывать файлы Вы не можете скачивать файлы
|
|