Część pierwszą dotyczącą automatów
poświęciłem opisem rozpoczęcia prac związanych z Expert Advisor. Etap
ten zakończyłem napisaniem pierwszego robota tego typu, instalacją go na platformie
MT4, oraz krótkimi testami na danych historycznych. Od strony technicznej
wszystko zaczęło działać – czas na poważniejsze prace – czyli poszukiwanie
"Świętego Grala".
Pierwszym pomysłem przedstawionym
wcześniej był robot SL Pending. Jego zadaniem było automatyczne otwieranie
pozycji z wykorzystaniem średnich ruchomych i prowadzenie jej z procentowym
zdefiniowanym poziomem stop loss. Kiedy kurs zmieniał się korzystnie poziom ten
podążał w stałej "odległości" za "ceną "danego waloru, a pozostawał
nieruchomy w przeciwnym razie. Pozycja była zamykana wtedy, kiedy niekorzystna
zmiana kursu przecięła poziom stop loss.
Ewolucją tego pomysłu był robot SL
Pending Hyperbolic. Modyfikacja polegała na zmianie sposobu prowadzenia poziomu
stop loss. Nie podążał on już liniowo za kursem, tylko hiperbolicznie. To
znaczy czym bardziej kurs "przesuwał się" w pożądanym kierunku tym
szybciej linia obronna go goniła. Miało to na celu zmniejszenie ilości
stratnych transakcji – pozytywna zmiana kierunku szybciutko przesuwała SL do
punktu "break even". Dodatkowo, żeby w przypadku dalszej korzystnej
zmiany kursu nie stracić części zysku, wprowadziłem nowy parametr
"Noise" – czyli szum. Zadaniem tego parametru było zmienić sposób
prowadzenia linii stop loss z wykładniczej znowu na liniową, tak żeby można
było maksymalizować zysk. Przykładowo, ustawiony
parametr Noise na 20%, powoduje, że zbliżająca się szybko linia SL do
aktualnego poziomu kursu danego waloru zatrzyma się w odległości 20% wielkości
depozytu i dalej już będzie podążać liniowo (analogicznie do trailing stop),
oczywiście tylko w przypadku korzystnej zmiany ceny instrumentu bazowego.
Dane o testach historycznych
poniżej, a kod programu dostępny jest na końcu wpisu.
Zanim przejdę do kolejnych
automatów, parę słów o ich testowaniu. Pierwszym i jak się okaże później poważnym
problemem była dostępność danych historycznych. Standardowo wraz z platformą
broker dostarczał informację o wykresach tylko za parę miesięcy wstecz. To
stanowczo za mało, żeby móc wyciągać jakiekolwiek wnioski. Mogę dać przykłady
automatów, które zarabiały kilkaset procent w krótkim okresie czasu, żeby
równie szybko stracić drugie tyle w kolejnych miesiącach. Na zadane pytanie do
brokera MM co do dostępności danych historycznych otrzymałem następującą
odpowiedź: „Nie udostępniamy takich danych”, Można z tego wysnuć następujące
wnioski:
- być może ich po prostu nie
przechowują,
- być może testy automatów za
okres kilkuletni pokazują ich nieskuteczność, co może w ogóle zniechęcać
uczestników do gry na forex,
- a może bronią się w ten sposób
przed napisaniem robota zarabiającego pieniądze w dłuższych okresach czasowych:)
Generalnie jest to słaby punkt
całej zabawy. Dzięki różnym zabiegom technicznym udało mi się pozyskać dane dla
niektórych walorów za okres do jednego roku, a na najpopularniejszej parze EURUSD
nawet za okres trzech lat. Dodatkowo przy testach historycznych pojawiał się
parametr jakości modelowania, który wynosił w zależności od testu od 30 do 95%, co też nie było zbyt budujące.
Pomimo powyższych niedogodności
prace trwały dalej. Jak już niektórzy pewnie zauważyli, duży nacisk kładłem na
sposób prowadzenia pozycji. Osobiście z mojej praktyki uważam, że jest to jeden
z najważniejszych czynników decydujący o naszej skuteczności i umiejętności
zarabiania na rynku lewarowanym.
Kolejnymi wersjami robotów były OMA
SL3Phaze i OMACD SL3Phaze.
Tutaj mamy już dość istotne zmiany
w porównaniu do poprzednich rozwiązań. Po pierwsze mamy nowy inny sposób
otwierania pozycji. W pierwszym przypadku wykorzystujemy przecinanie się
średnich kroczących. W drugim rozwiązaniu do otwierania pozycji wykorzystujemy
wskaźnik MACD. Został też dodany zaawansowany sposób prowadzenia pozycji.
Oprócz parametrów SL i Nosie istnieje możliwość definiowania różnych prędkości
przesuwania poziomu SL, w zależności od zmiany ceny naszego waloru. Przykładowo,
kiedy w pierwszej fazie kurs zaczyna się korzystnie zmieniać automat szybko
przesuwa SL do poziomu break even lub trochę powyżej. Dalej możemy ustawić np.
prędkość liniową, żeby spokojnie holować naszą pozycję przy korzystnej zmianie
kursu, tak aby drobne korekty nie wyrzuciły nas z rynku. Po osiągnięciu
zakładanego zysku np. 3R (3x depozyt) możemy znowu zmienić prędkość
podążania SL na wykładniczą tak aby nie stracić tego co wirtualnie zarobiliśmy
(realny zysk będzie dopiero po zamknięciu pozycji), a jednocześnie dać szanse
na powiększenie zysku przy gwałtownych korzystnych dla nas ruchach cen.
Dane dla wybranych walorów oraz
parametrów, które osiągały dodatnie wyniki poniżej w tabeli. Kod całego robota OMACD
SL3Phaze na końcu wpisu.
Wyniki były coraz bardziej
obiecujące, a automaty coraz bardziej zaawansowane. Ale to wszystko tylko na
danych historycznych. Jeżeli chciałem się posuwać dalej, to zbliżał się czas
„wypuszczenia robotów w świat”, czyli uruchomienia ich na rzeczywistym rachunku
z prawdziwą gotówką. O kolejnych robotach pracujących już w „realu” zapraszam w
kolejnym wpisie dotyczącym tego tematu.
KOD SL Pending:
//+------------------------------------------------------------------+
//| OMA SLHyperbolicWithMinimalDistance.mq4 |
//| SR & WP |
//| |
//+------------------------------------------------------------------+
#property copyright "www.donkey.org.pl"
#property link ""
/* Otwieranie wg Moving Average */
/* Zamykanie wg hiperbolicznego StopLoss z minimaln╣ odleg│oťci╣ */
#define MAGICOMASLH 20100719
extern int MovingPeriod = 12;
extern int MovingShift = 6;
extern double StopLossPercentage = 90.0; // jako procent wartoťci depozytu
extern double NoiseTolerancePercentage = 30.0; // jako procent wartoťci depozytu
double SLDist, NTDist;
//+------------------------------------------------------------------+
//| Calculate open positions |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
{
int buys=0,sells=0;
//----
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICOMASLH)
{
if(OrderType()==OP_BUY) buys++;
if(OrderType()==OP_SELL) sells++;
}
}
//---- return orders volume
if(buys>0) return(buys);
else return(-sells);
}
void CheckForOpen()
{
double ma;
int res;
//---- go trading only for first tiks of new bar
if(Volume[0]>1) return;
//---- get Moving Average
ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//---- sell conditions
if(Open[1]>ma && Close[1]<ma)
{
SLDist = Ask*StopLossPercentage*0.01/AccountLeverage();
NTDist = Ask*NoiseTolerancePercentage*0.01/AccountLeverage();
res=OrderSend(Symbol(),OP_SELL,0.01,Bid,3,Ask+SLDist,0,"",MAGICOMASLH,0,Red);
return;
}
//---- buy conditions
if(Open[1]<ma && Close[1]>ma)
{
SLDist = Bid*StopLossPercentage*0.01/AccountLeverage();
NTDist = Bid*NoiseTolerancePercentage*0.01/AccountLeverage();
res=OrderSend(Symbol(),OP_BUY,0.01,Ask,3,Bid-SLDist,0,"",MAGICOMASLH,0,Blue);
return;
}
//----
}
void CheckForModify() {
bool modify = false;
double StopLoss;
for(int i=0;i<OrdersTotal();i++)
{
modify = false;
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICOMASLH && OrderType() <= 1)
{
switch (OrderType()) {
case OP_SELL:
if (Ask < OrderOpenPrice()) {
StopLoss = Ask+(SLDist*SLDist/(OrderOpenPrice()-Ask+SLDist));
if (StopLoss < Ask+NTDist) {
StopLoss = Ask+NTDist;
}
if (StopLoss < OrderStopLoss()) modify = true;
}
break;
case OP_BUY:
if (Bid > OrderOpenPrice()) {
StopLoss = Bid-(SLDist*SLDist/(Bid-OrderOpenPrice()+SLDist));
if (StopLoss > Bid-NTDist) {
StopLoss = Bid-NTDist;
}
if (StopLoss > OrderStopLoss()) modify = true;
}
break;
}
if (modify) {
OrderModify(OrderTicket(),OrderOpenPrice(),StopLoss,0,0);
}
}
}
}
//+------------------------------------------------------------------+
//| script program start function |
//+------------------------------------------------------------------+
int start()
{
//----
if(Bars<MovingPeriod+MovingShift+5 || IsTradeAllowed()==false) return;
if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();
else CheckForModify();
//----
return(0);
}
//+------------------------------------------------------------------+
KOD OMACD SL3Phaze:
//+------------------------------------------------------------------+
//| OMACD SL3Phases.mq4 |
//| SR&WP|
//| |
//+------------------------------------------------------------------+
#property copyright "www.donkey.org.pl"
#property link ""
// Otwieranie MACD
// Przesuwanie StopLoss trójfazowe
#define MAGICOMACDSL3P 20120725
// parametr wielkości
extern double Lots = 0.01;
// parametry MACD
extern double MACDOpenLevel=3;
//extern double MACDCloseLevel=2;
extern int MATrendPeriod=26;
extern int FastEMA=12;
extern int SlowEMA=26;
extern int MACDSMA=9;
// parametry przesuwania StopLoss
extern double Noise = 30.0; // szum w procentach postawionej stawki - SL nigdy nie przesunie się bliżej aktualnego kursu
extern double MaximumLoss = 95.0; // maksymalna możliwa strata w procentach postawionej stawki
extern double Phase1Speed = 3.0; // prędkość zmiany StopLoss w fazie pierwszej;
//ile wynosi ta wartość, tylukrotnie zmniejszy się dystans między StopLoss a aktualnym kursem, jeśli aktualny kurs zmieni się o R na korzyść
extern double Phase1End = 55.0; // tyle procent ZYSKU oznacza moment końca pierwszej fazy
extern double Phase2Speed = 0.1; // o tyle jednostek przesunie się w drugiej fazie StopLoss, gdy aktualny kurs przesunie się o jedną jednostkę na korzyść
extern double Phase2End = 500.0; // tyle procent ZYSKU oznacza moment końca drugiej fazy
extern double Phase3Speed = 4.0; // jak Phase1Speed
double r;
//+------------------------------------------------------------------+
//| Calculate open positions |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
{
int buys=0,sells=0;
//----
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICOMACDSL3P)
{
if(OrderType()==OP_BUY) buys++;
if(OrderType()==OP_SELL) sells++;
}
}
//---- return orders volume
if(buys>0) return(buys);
else return(-sells);
}
void CheckForOpen() {
double MacdCurrent, MacdPrevious, SignalCurrent;
double SignalPrevious, MaCurrent, MaPrevious;
int res;
//---- go trading only for first tiks of new bar
//if(Volume[0]>1) return;
MacdCurrent=iMACD(NULL,0,FastEMA,SlowEMA,MACDSMA,PRICE_CLOSE,MODE_MAIN,0);
MacdPrevious=iMACD(NULL,0,FastEMA,SlowEMA,MACDSMA,PRICE_CLOSE,MODE_MAIN,1);
SignalCurrent=iMACD(NULL,0,FastEMA,SlowEMA,MACDSMA,PRICE_CLOSE,MODE_SIGNAL,0);
SignalPrevious=iMACD(NULL,0,FastEMA,SlowEMA,MACDSMA,PRICE_CLOSE,MODE_SIGNAL,1);
MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
//---- sell conditions
if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
MacdCurrent>(MACDOpenLevel*Point) && MaCurrent<MaPrevious)
{
r = Ask/AccountLeverage();
res=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,Ask+(r*MaximumLoss*0.01),0,"",MAGICOMACDSL3P,0,Red);
return;
}
//---- buy conditions
if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious)
{
r = Bid/AccountLeverage();
res=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,Bid-(r*MaximumLoss*0.01),0,"",MAGICOMACDSL3P,0,Blue);
return;
}
//----
}
void CheckForModify() {
bool modify;
double StopLoss;
double granicznyStopLoss1;
double granicznyStopLoss2;
int phase;
for(int i=0;i<OrdersTotal();i++) {
modify = false;
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICOMACDSL3P && OrderType() <= 1) {
switch(OrderType()) {
case OP_SELL:
if (Bid >= OrderOpenPrice()) break;
if (Bid <= OrderOpenPrice()-(r*Phase2End*0.01)) { phase=3; }
else {
if (Bid <= OrderOpenPrice()-(r*Phase1End*0.01)) { phase=2; }
else { phase=1; }
}
switch (phase) {
case 1:
StopLoss = Bid+((r*MaximumLoss*0.01)/(MathPow(Phase1Speed,(OrderOpenPrice()-Bid)/r)));
break;
case 2:
granicznyStopLoss1 = (OrderOpenPrice()-(r*Phase1End*0.01))+((r*MaximumLoss*0.01)/(MathPow(Phase1Speed,Phase1End*0.01)));
StopLoss = granicznyStopLoss1-(Phase2Speed*(Bid-OrderOpenPrice()+(r*Phase1End*0.01)));
break;
case 3:
granicznyStopLoss1 = (OrderOpenPrice()-(r*Phase1End*0.01))+((r*MaximumLoss*0.01)/(MathPow(Phase1Speed,Phase1End*0.01)));
granicznyStopLoss2 = granicznyStopLoss1-(Phase2Speed*r*0.01*(Phase1End-Phase2End));
StopLoss = Bid+(granicznyStopLoss2/(MathPow(Phase3Speed,(OrderOpenPrice()-Bid)/r)));
break;
}
if (StopLoss < Ask+(r*Noise*0.01)) { StopLoss = Ask+(r*Noise*0.01); }
if (StopLoss < OrderStopLoss()) { modify = true; }
break;
case OP_BUY:
if (Ask <= OrderOpenPrice()) break;
if (Ask >= OrderOpenPrice()+(r*Phase2End*0.01)) { phase=3; }
else {
if (Ask >= OrderOpenPrice()+(r*Phase1End*0.01)) { phase=2; }
else { phase=1; }
}
switch (phase) {
case 1:
StopLoss = Ask-((r*MaximumLoss*0.01)/(MathPow(Phase1Speed,(Ask-OrderOpenPrice())/r)));
break;
case 2:
granicznyStopLoss1 = (OrderOpenPrice()+(r*Phase1End*0.01))-((r*MaximumLoss*0.01)/(MathPow(Phase1Speed,Phase1End*0.01)));
StopLoss = granicznyStopLoss1+(Phase2Speed*(OrderOpenPrice()-Ask+(r*Phase1End*0.01)));
break;
case 3:
granicznyStopLoss1 = (OrderOpenPrice()+(r*Phase1End*0.01))-((r*MaximumLoss*0.01)/(MathPow(Phase1Speed,Phase1End*0.01)));
granicznyStopLoss2 = granicznyStopLoss1+(Phase2Speed*r*0.01*(Phase1End-Phase2End));
StopLoss = Ask-(granicznyStopLoss2/(MathPow(Phase3Speed,(Ask-OrderOpenPrice())/r)));
break;
}
if (StopLoss > Bid-(r*Noise*0.01)) { StopLoss = Bid-(r*Noise*0.01); }
if (StopLoss > OrderStopLoss()) { modify = true; }
break;
}
if (modify) {
OrderModify(OrderTicket(),OrderOpenPrice(),StopLoss,0,0);
}
}
}
}
//+------------------------------------------------------------------+
//| script program start function |
//+------------------------------------------------------------------+
int start()
{
//----
if (Bars < 100) return(0);
if (IsTradeAllowed() == false) return(0);
if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();
else CheckForModify();
//----
return(0);
}
//+------------------------------------------------------------------+
witam,
OdpowiedzUsuńpo roku od swoich pierwszych doświadczeń z FX wracam do banalnie prostych strategii z SMA i EMA. Dodałem do tego przesunięcie SMA o 1 do 5 okresów i muszę stwierdzić, że otrzymuję ciekawe wyniki. Reszta to tylko umiejętne zarządzanie ryzykiem oraz odpowiednie wyjście z pozycji.
Pozdrawiam.
Antoni
Antoni możesz podać do siebie namiary chciałbym porozmawiać.
OdpowiedzUsuń