We wcześniejszych dwóch wpisach
dotyczących automatów (automaty cz.1 i automaty cz.2) opisywałem pierwsze moje
kroki w tej dziedzinie. Testując kolejne wersje i dziesiątki ustawień
parametrów, czułem się trochę jak włamywacz szukający właściwej kombinacji cyfr
otwierającej skarbiec. Jak pisałem wcześniej główną bolączką był brak
wystarczającej historii danych dla różnych instrumentów. Osiągałem rewelacyjne
wyniki, ale w krótkich okresach czasowych. Szybko się zorientowałem, że
dopasowuję automat do kawałka wykresu – w największym uproszczeniu: jeżeli
stworzę automat, który będzie tylko dokonywał transakcji zakupu (bez znaczenia
kryterium otwarcia pozycji), to przy dynamicznym rynku wzrostowym będzie zawsze
zarabiał. Analogicznie przy tendencji spadkowej tracił, a nie o to chodziło.
Kolejnym weryfikującym ten problem działaniem było testowanie obiecujących robotów na
platformie MT4 u alternatywnego brokera (dla danych historycznych). I tutaj
niemiła niespodzianka. Dla tego samego robota i tego samego okresu czasowego
testy dawały mocno odmienne wyniki! Okazał się, że wykresy tych samych instrumentów
są różnie "rysowane" przez brokerów MM. Ogólnie dane są te same, ale
ponieważ pobierane od innych dostawców to jednak się różnią. Dodatkowo brokerzy stosują inne "spread'y", oraz czasami rozpoczynają i kończą notowania o rożnych porach. Tak więc dla
automatu działającego np. na interwale 15-sto minutowym wykres, a za nim wyniki są inne. No ale jak Osiołek wyruszył, to wpół drogi nie zawróci (pomimo, że zna
już tę pierwszą połówkę). Cel nadal pozostawał ten sam – Perpetuum Constitues
czyli robot wiecznie zarabiający:)
W dalszych pracach skupiłem się na parze EURUSD, gdyż
jest to instrument uniwersalny, notowany 24 godziny na dobę, a
wykresy są najbardziej zbliżone u różnych brokerów (choć nie identyczne).
Dodatkowo udało mi się zdobyć najdłuższą, prawie trzy letnią historię notowań
nadająca się do testów dla dwóch niezależnych brokerów. Parę ładnych nocek
zarwałem śledząc działanie robota na danych historycznych. Ustawiałem prędkość
testów tak, abym mógł "na żywo" obserwować otwieranie i zamykanie
pozycji na wykresie. Wyłapywałem błędy i ulepszałem automat. I tak narodził się
robot: OMA SL 3Phases Extended
W testach historycznych krzywa
kapitału pięła się raz wolniej raz szybciej do góry, ale bez żadnych większych
tąpnięć. Robot był tak zaprogramowany, żeby wyłapywać dłuższe zmiany trendu –
łowić grube ryby, a w trendzie bocznym nie tracić za wiele. Testy za trzy lata
było zadowalające i to u różnych brokerów:
Nie zostało już nic innego jak
ożywić naszego przyszłego super robota!
Na początku uruchomiłem go na
domowym PC, ale szybko napotkałem trudności nie do przejścia. "Albo
przestanie mi tu szumieć całą noc ten komputer, albo wylecisz z nim na
wycieraczkę" – ultimatum żony brzmiało groźnie:) Uruchomienie go w firmie
też nie było dobrym wyjściem. Najpierw wyłączyła mi go tradycyjnie sprzątaczka,
po za tym potrafił się zresetować od czasu do czasu (jak to PC). I tu
uświadomiła mi się kolejna słaba strona tej zabawy. Żeby robot działał zgodnie
z założeniem, to musi być cały czas uruchomiona platforma MT4 oraz zapewniony
dostęp do sieci Internet. Każda przerwa w działaniu, to zgubienie synchronizacji
robota. Potem zajmowało mu kilka dni powrót na właściwe tory. Tak więc trzeba było
podejść do tematu profesjonalnie. Złożyłem porządny serwer z macierzą dyskową; software oparłem o Windows Server 2003 i taki zestaw w sierpniu powędrował do firmowej
serwerowni.
Ustawiłem
wielkość transakcji na 0,2 lota, a wartość portfela wyniosła 5 tys. złotych. Robot na "raz" otwierał tylko jedna pozycję. Już za
trzecim razem wykonał transakcję, którą utrzymywał przez przeszło trzy tygodnie i
złowił swoją pierwszą tłustą rybkę: 3.237 zł – zgodnie z ustawieniami max. 450% wartości
depozytu. Niestety do końca roku nie powtórzył już tego
sukcesu i częściej tracił niż zarabiał. Na początku roku zwiększyłem wolumen do
0,3 lota i w lutym udał się kolejny połów, choć trochę mniejszy: 2.955 zł. To
zachęciło mnie do kolejnego zwiększania wolumenu aż do 1 lota. W sumie przez
prawie 8 miesięcy ciągłej pracy robot wygenerował 4.070 zł zysku. Szczegółowe
zestawienie transakcji poniżej.
I końcowe podsumowanie
Sukces czy porażka?
Patrząc pod kontem
osiągnięcia dodatniego salda przez dłuższy okres – na pewno sukces. Z kolei
patrząc pod kontem wstępnych wyników opartych na danych historycznych – na
pewno poniżej poziomu oczekiwań. Dodatkowo biorąc pod uwagę nakład czasu pracy,
zakup i obsługę serwera, zużyty prąd itd. to niestety szału nie było. Tak więc
w marcu robot został odłączony od "Matrixa":) Nie miałem już więcej
czasu na dalszą zabawę w zestawieniu z zaniedbanymi projektami, które generowały
znacznie poważniejsze stopy zwrotu. Mimo wszystko sądzę, że jeszcze kiedyś
wrócę do tego tematu, bogatszy o dotychczasowe doświadczenia i z nowymi
pomysłami.
Na końcu wpisu kod źródłowy
mojego "Championa"
PS1: Jak ktoś na
bazie moich doświadczeń i kodów stanie się milionerem to prośba o tantiemy za prawa
autorskie:)
PS2: Zająłem krótką
pozycję na PKN Orlen (51 zł). Z jednej strony kurs rósł mocno do góry, ze względu na
rosnące marże - spadek cen ropy szybszy niż spadek cen na stacjach. Ale z drugiej
strony mocno rośnie USDPLN, co za chwilę powinno się przełożyć na spadek marży i
w konsekwencji na obniżkę notowań. Dodatkowo założony blisko stoploss na
poziomie 53 zł.
PS3. Z kolei
osłabienie złotego powinno poprawiać wyniki eksporterów w tym po części JSW. Ale
to w dłuższym terminie. Na razie rządzą emocje i możemy oglądać rollercoaster na
wykresie tego waloru. Dlatego też zająłem pozycję na tradycyjnym rynku, a nie na leawrowanym,
co by tylko upadłość spółki zakończyła tę inwestycję.
Opis i Kod robota:
//+------------------------------------------------------------------+
//| OMA SL3PhasesExtended.mq4 |
//| SR&WP|
//| |
//+------------------------------------------------------------------+
#property copyright "www.donkey.org.pl"
#property link ""
// Otwieranie Moving Average
// Przesuwanie StopLoss trójfazowe
// Dodatkowo definiowane łowienie "chudszych" ryb w 2. fazie
#define MAGICOMASL3PE 20120823
// parametr wielkości
extern double Lots = 0.01;
// parametry moving average
extern int MovingPeriod = 12;
extern int MovingShift = 6;
// 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 int Phase2IndependentMovingMode = 2; // 0, 1 lub 2
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()==MAGICOMASL3PE)
{
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;
ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//---- sell conditions
if(Open[1]>ma && Close[1]<ma)
{
r = Ask/AccountLeverage();
res=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,Ask+(r*MaximumLoss*0.01),0,"",MAGICOMASL3PE,0,Red);
return;
}
//---- buy conditions
if(Open[1]<ma && Close[1]>ma)
{
r = Bid/AccountLeverage();
res=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,Bid-(r*MaximumLoss*0.01),0,"",MAGICOMASL3PE,0,Blue);
return;
}
//----
}
void CheckForModify() {
bool modify;
double StopLoss;
double granicznyStopLoss1;
double granicznyStopLoss2;
int phase;
double minChange = NormalizeDouble(MathPow(0.1, Digits), Digits);
for(int i=0;i<OrdersTotal();i++) {
modify = false;
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICOMASL3PE && 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)));
if (Phase2IndependentMovingMode == 1) {
if (Volume[0]==1) StopLoss = MathMin(StopLoss, OrderStopLoss()-minChange);
}
else if (Phase2IndependentMovingMode == 2) {
if (Volume[0]==1 && Open[1]>Close[1]) StopLoss = MathMin(StopLoss, OrderStopLoss()-minChange);
}
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)));
if (Phase2IndependentMovingMode == 1) {
if (Volume[0]==1) StopLoss = MathMax(StopLoss, OrderStopLoss()+minChange);
}
else if (Phase2IndependentMovingMode == 2) {
if (Volume[0]==1 && Close[1]>Open[1]) StopLoss = MathMax(StopLoss, OrderStopLoss()+minChange);
}
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 < MovingPeriod+MovingShift+5) return(0);
if (IsTradeAllowed() == false) return(0);
if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();
else CheckForModify();
//----
return(0);
}
//+------------------------------------------------------------------+
Gratulację za wytrwanie w tworzeniu robota.
OdpowiedzUsuńRozumiem że krótką sprzedaż otwarłeś na zasadzie kontraktu CFD.
Można wiedzieć u jakiego brokera ? Bo zazwyczaj mają spore prowizje za otwarcie :(
Co do brokerów to na nich oceny przyjdzie jeszcze czas. Generalnie każdy poważny broker ma już teraz instrumenty CFD na Polskę i świat. Ze względu na mocno spekulacyjny charakter tej zagrywki pozycja jest nieduża 450 lotów; depozyt ok. 3.500 tys.; spread (czyli prowizja) zmienny, zależny od momentu zawierania transakcji. W moim przypadku wyniósł ok. 20 zł.
OdpowiedzUsuń