Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 23): Yeni emir sistemi (VI)
Giriş
Bir önceki Sıfırdan bir ticaret Uzman Danışmanı Geliştirme (Bölüm 22): Yeni emir sistemi (V) başlıklı makalede, bekleyen emirleri ve pozisyon eşik seviyelerini taşımak için bir sistem geliştirdik. Bu yöntem daha güvenli olsa da (çünkü işlem sunucusunda bulunanları yansıtır), seviyeleri hızlı bir şekilde hareket ettirmenin en iyi yolu değildir.
Sorun şu ki, fareyi kullanarak bir şeyi her değiştirdiğimizde, bu olay sunucuya gönderilir ve ardından bir yanıt beklememiz gerekir. Bu, olayın her tikte gönderilmesiyle ilişkilidir, yani bir seferde bir seviyeyi birkaç tik hareket ettirmemiz gerekirse, tüm ara değerlerden geçmemiz gerekir ve bu da tüm süreci çok yavaşlatır. İşte bu makalede, sistemi daha akıcı hale getirmek ve böylece seviyeleri çok daha hızlı değiştirebilmek için kodda yapılması gereken değişiklikleri ele alacağız.
1.0. Planlama
Değişiklikleri uygulamak için çok basit bir şey yapmamız gerekiyor: sunucuyu tüm değişiklikler hakkında bilgilendirmemek, sadece gerekli değişiklik hakkında bilgilendirmek. Sadece bunu yapmak bile her şeyin yolunda gitmesini sağlayacaktır, ancak her şeyin tam olarak yaptığımız gibi olduğundan kesinlikle emin olamayacağız.
Şimdi kodu nerelerde değiştirmemiz gerektiğini görelim. Aşağıda gösterilen ayrı bir fonksiyon kullanıyoruz:
#define macroGetPrice(A) StringToDouble(ObjectGetString(Terminal.Get_ID(), MountName(ticket, A, EV_LINE), OBJPROP_TOOLTIP)) void MoveSelection(double price, uint keys) { static string memStr = NULL; static ulong ticket = 0; static eIndicatorTrade it; eEventType ev; double tp, sl, pr; bool isPending; string sz0 = m_TradeLine.GetObjectSelected(); if (sz0 != NULL) { if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev); isPending = OrderSelect(ticket); switch (it) { case IT_TAKE: if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP)); else ModifyPosition(ticket, price, macroGetPrice(IT_STOP)); break; case IT_STOP: if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price); else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price); break; case IT_PENDING: pr = macroGetPrice(IT_PENDING); tp = macroGetPrice(IT_TAKE); sl = macroGetPrice(IT_STOP); ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr)); break; } }; } #undef macroGetPrice
Ancak fonksiyon içerisindeki değişikliklerin yanı sıra, fare olaylarıyla ilgili bazı değişiklikleri de uygulamamız gerekecektir. İlk olarak buna odaklanacağız.
Vurgulanan kısımların, kullanılacak yeni pozisyonları temsil etmek için başka bir şeyle değiştirilmesi gerekir. Ancak tüm değişiklikler kullanıcı için anlaşılabilir olmalıdır.
Tüm kod yapısında büyük değişiklikler yapmadan her şeyi anlaşılması kolay ve aynı zamanda işlevsel hale getirmenin etkili bir yolunu buldum. Bu, ihtiyaç duyulana kadar görünmeyecek bir hayalet gösterge oluşturmaktır. Neyse ki, MetaTrader 5 bunu yapmak için çok basit bir yol sunmaktadır. Dolayısıyla, bu makaleyi anlamak, bu serinin önceki makalelerindeki materyallere zaten aşina olanlar için çok kolay olacaktır.
2.0. Uygulama
Hayalet göstergeyi basitçe gerçek göstergeyle birlikte oluşturacağız. Hayalet gösterge, bir önceki makalede gösterdiğimiz şekilde fiyatı değiştireceğimiz zamana kadar gerçek göstergenin tam gölgesi olacaktır. Gerçek gösterge hareket ettikçe hayalet gösterge grafikte görünecektir. Bu, neler olup bittiğini kolayca karşılaştırmanızı ve değişiklik yapıp yapmamanız gerektiğini anlamanızı sağlayacaktır.
2.0.1. Hayalet gösterge oluşturma
Tüm değişiklikler C_IndicatorTradeView sınıfı içerisinde uygulanır. Üç yeni yönerge tanımlayarak başlayalım:
#define def_IndicatorGhost "G" #define def_IndicatorReal "R" #define def_IndicatorGhostColor clrDimGray
Amacımız MetaTrader 5'in bizim için çalışmasını sağlamaktır. Kural şudur: önce bir hayalet gösterge oluştururuz, sonra da bir gerçek gösterge oluştururuz. Böylece MetaTrader 5, hayalet göstergenin gerçekten görmemiz gereken ana kadar görünmemesini sağlayacaktır. Bu MetaTrader 5'in kendisi tarafından yapıldığından, programlamada çok tasarruf edeceğiz.
Önemli bir ayrıntı: hayaletin rengini değiştirmek istiyorsanız, yukarıda vurgulanan parçada belirtilen rengi değiştirmeniz yeterlidir.
Bir sonraki adım benzersiz adların oluşturulmasını sağlayan fonksiyonu değiştirmektir.
inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev, bool isGhost = false) { return StringFormat("%s%c%c%c%llu%c%c%c%s", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)(isGhost ? ev + 32 : ev), def_SeparatorInfo, (isGhost ? def_IndicatorGhost : def_IndicatorReal)); }
Vurgulanan parçalar bir önceki versiyona göre eklenmiş veya değiştirilmiştir. MetaTrader 5'in benzersiz adlar oluşturmasını sağlayabiliriz. Yani, bu konuda endişelenmemize gerek yoktur. Olaylara değerler eklediğime dikkat edin. Bunu hayaletin olayları almasını ve kontrolü ele geçirmeye çalışmasını önlemek için yaptım.
Bir sonraki adım nettir- göstergenin kendisini oluşturmalıyız:
inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it) { color cor1, cor2, cor3; string sz0, sz1; switch (it) { case IT_TAKE : cor1 = clrForestGreen; cor2 = clrDarkGreen; cor3 = clrNONE; break; case IT_STOP : cor1 = clrFireBrick; cor2 = clrMaroon; cor3 = clrNONE; break; case IT_PENDING: cor1 = clrCornflowerBlue; cor2 = clrDarkGoldenrod; cor3 = def_ColorVolumeEdit; break; case IT_RESULT : default: cor1 = clrDarkBlue; cor2 = clrDarkBlue; cor3 = def_ColorVolumeResult; break; } m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE, true), def_IndicatorGhostColor); m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE), cor2); if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE)); if (it != IT_RESULT) m_BackGround.Create(ticket, sz0 = MountName(ticket, it, EV_GROUND, true), def_IndicatorGhostColor); m_BackGround.Create(ticket, sz1 = MountName(ticket, it, EV_GROUND), cor1); switch (it) { case IT_TAKE: case IT_STOP: case IT_PENDING: m_BackGround.Size(sz0, 92, 22); m_BackGround.Size(sz1, 92, 22); break; case IT_RESULT: m_BackGround.Size(sz1, 84, 34); break; } m_BtnClose.Create(ticket, MountName(ticket, it, EV_CLOSE), def_BtnClose); m_EditInfo1.Create(ticket, sz0 = MountName(ticket, it, EV_EDIT, true), def_IndicatorGhostColor, 0.0); m_EditInfo1.Create(ticket, sz1 = MountName(ticket, it, EV_EDIT), cor3, 0.0); m_EditInfo1.Size(sz0, 60, 14); m_EditInfo1.Size(sz1, 60, 14); if (it != IT_RESULT) { m_BtnMove.Create(ticket, sz0 = MountName(ticket, it, EV_MOVE, true), "Wingdings", "u", 17, def_IndicatorGhostColor); m_BtnMove.Create(ticket, sz1 = MountName(ticket, it, EV_MOVE), "Wingdings", "u", 17, cor2); m_BtnMove.Size(sz1, 21, 21); }else { m_EditInfo2.Create(ticket, sz1 = MountName(ticket, it, EV_PROFIT), clrNONE, 0.0); m_EditInfo2.Size(sz1, 60, 14); } }
Vurgulanan tüm satırlar hayaleti oluşturur. Bir şey garip gelebilir: neden tüm unsurları yeniden üretmiyoruz? Aslında hayalet, gerçek göstergenin tam bir kopyası değildir, sadece gölgesidir. Bu nedenle, tüm unsurları yeniden oluşturmamıza gerek yoktur. Gerçek gösterge, ne olması gerektiğini tanımlayacak olan göstergedir; hayalet ise yalnızca işlem sunucusunun ne göreceğine dair bir referans görevi görür.
Şimdi MetaTrader 5'in gerçekten çok çalışacağı, daha detaylı bir çalışma gerektiren kısım geliyor. Nesneleri doğru yerlere yerleştirmenin çok fazla çalışma gerektireceğini düşünebilirsiniz, ancak kaynak kodda gerçekte neyin değiştiğine bakalım.
#define macroSetAxleY(A, B) { \ m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND, B), y); \ m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE, B), y); \ m_BtnClose.PositionAxleY(MountName(ticket, A, EV_CLOSE, B), y); \ m_EditInfo1.PositionAxleY(MountName(ticket, A, EV_EDIT, B), y, (A == IT_RESULT ? -1 : 0)); \ m_BtnMove.PositionAxleY(MountName(ticket, A, EV_MOVE, B), (A == IT_RESULT ? 9999 : y)); \ m_EditInfo2.PositionAxleY(MountName(ticket, A, EV_PROFIT, B), (A == IT_RESULT ? y : 9999), 1); \ } #define macroSetAxleX(A, B, C) { \ m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND, C), B); \ m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE, C), B); \ m_BtnClose.PositionAxleX(MountName(ticket, A, EV_CLOSE, C), B + 3); \ m_EditInfo1.PositionAxleX(MountName(ticket, A, EV_EDIT, C), B + 21); \ m_BtnMove.PositionAxleX(MountName(ticket, A, EV_MOVE, C), B + 80); \ m_EditInfo2.PositionAxleX(MountName(ticket, A, EV_PROFIT, C), B + 21); \ } //+------------------------------------------------------------------+ inline void ReDrawAllsIndicator(void) { int max = ObjectsTotal(Terminal.Get_ID(), -1, -1); ulong ticket; double price; eIndicatorTrade it; eEventType ev; for (int c0 = 0; c0 <= max; c0++) if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, -1), ticket, price, it, ev)) PositionAxlePrice(ticket, it, price); } //+------------------------------------------------------------------+ inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price) { int x, y, desl; ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y); ObjectSetString(Terminal.Get_ID(), MountName(ticket, it, EV_LINE), OBJPROP_TOOLTIP, DoubleToString(price)); macroSetAxleY(it, true); macroSetAxleY(it, false); switch (it) { case IT_TAKE: desl = 110; break; case IT_STOP: desl = 220; break; default: desl = 0; } macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), true); macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), false); } #undef macroSetAxleX #undef macroSetAxleY
Hepsi bu kadar mı? Sadece makroları mı değiştireceğiz? Evet, tüm kodu yeniden oluşturmaya gerek yoktur, onu sadece düzenleriz. Gerçekten yapmamız gereken tek şey MetaTrader 5'e manipüle ettiğimiz nesnenin adını söylemektir, MetaTrader 5 gerisini bizim için halledecektir. Bunun için fonksiyonlar oluşturmamıza gerek yoktur. Vurgulanan parçaları eklememiz yeterlidir.
Burada değiştirilecek başka bir fonksiyon aşağıda gösterilmektedir:
void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0) { double finance; switch (it) { case IT_RESULT : PositionAxlePrice(ticket, it, priceOpen); PositionAxlePrice(ticket, IT_PENDING, 0); m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1); case IT_PENDING: value0 = value0 / Terminal.GetVolumeMinimal(); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor); break; case IT_TAKE : case IT_STOP : finance = (value1 / Terminal.GetAdjustToTrade()) * value0; m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor); break; } }
Burada vurgulanan kısımları ekledik. Böylece hayalet oluşturuldu. Şu anda, gerçek göstergede neler olduğunu doğru bir şekilde yansıtmaktadır. O kadar iyi yansıtmaktadır ki düzgün çalışması için birkaç şey daha uygulamamız gerekecektir. Kod yanlış değildir, ancak hayalet gerçek göstergeyle çok yakından ilişkilidir, ki aslında bundan kaçınmalıyız.
Burada değiştirilecek son fonksiyon aşağıda gösterilmektedir:
inline void RemoveIndicator(ulong ticket, eIndicatorTrade it = IT_NULL) { #define macroDestroy(A, B) { \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND, B)); \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE, B)); \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_CLOSE, B)); \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_EDIT, B)); \ if (A != IT_RESULT) ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_MOVE, B)); \ else ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_PROFIT, B)); \ } ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false); if ((it == IT_NULL) || (it == IT_PENDING) || (it == IT_RESULT)) { macroDestroy(IT_RESULT, true); macroDestroy(IT_RESULT, false); macroDestroy(IT_PENDING, true); macroDestroy(IT_PENDING, false); macroDestroy(IT_TAKE, true); macroDestroy(IT_TAKE, false); macroDestroy(IT_STOP, true); macroDestroy(IT_STOP, false); } else { macroDestroy(it, true); macroDestroy(it, false); } ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true); #undef macroDestroy }
Vurgulanan parçalar değişti, burada özel bir şey yoktur.
2.0.2. Hayaleti gerçekten ayırma
Önceki bölümde yapılan değişiklikler bir hayalet oluşturur. Ancak bir sorunumuz vardır - bu hayalet, gerçek nesneye çok yakındır. Bu, ilk bakışta uygulanması çok zor bir şey gibi görünebilir. Ancak koda baktığımızda, zaten bir çözümümüz olduğunu görüyoruz. Ancak bu çözüm yanlış yerde bulunmaktadır. Çözümün bulunduğu yeri değiştirmemiz ve bunu sınıf genelinde daha görünür hale getirmemiz gerekiyor.
Çözüm aşağıdaki kodda gösterilmektedir:
void DispatchMessage(int id, long lparam, double dparam, string sparam) { ulong ticket; double price, tp, sl; bool isBuy, bKeyBuy, bKeySell, bEClick; long info; datetime dt; uint mKeys; eIndicatorTrade it; eEventType ev; static bool bMounting = false, bIsDT = false, bIsMove = false; static double leverange = 0, valueTp = 0, valueSl = 0, memLocal = 0; switch (id) { case CHARTEVENT_MOUSE_MOVE: // ... The rest of the code...
Çözüm, vurgulanan kod parçasıdır. Ama bu nasıl mümkün olabilir? Bir bekleyen emir yerleştirirken, sistemin verileri oluşturmayı ve manipüle etmeyi yönettiğini unutmayın, böylece sonunda grafikte emrin nereye yerleştirileceğini ifade eden göstergelerin bir temsiline sahip oluruz. Bu, aşağıdaki kod kullanılarak yapılır.
case CHARTEVENT_MOUSE_MOVE: Mouse.GetPositionDP(dt, price); mKeys = Mouse.GetButtonStatus(); bEClick = (mKeys & 0x01) == 0x01; //left mouse click bKeyBuy = (mKeys & 0x04) == 0x04; //SHIFT pressed bKeySell = (mKeys & 0x08) == 0x08; //CTRL pressed if (bKeyBuy != bKeySell) { if (!bMounting) { Mouse.Hide(); bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl); valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange); valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange); m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE)); bMounting = true; } tp = price + (bKeyBuy ? valueTp : (-valueTp)); sl = price + (bKeyBuy ? (-valueSl) : valueSl); UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy); if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT); }else if (bMounting) { UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false); Mouse.Show(); memLocal = 0; bMounting = false; //... Rest of the code...
Alış için SHIFT tuşuna veya satış için CTRL tuşuna basmanıza bağlı olarak, sistem oluşturulmakta olan bekleyen emrin bir temsilini oluşturacaktır. Doğrudan grafik üzerinde görüntülenecektir. Kârı Al veya Zararı Durdur olarak kullanılacak değerler ticaret arayüzünde yakalanır, ardından temsili seviyeyi emri yerleştirmek istediğiniz seviyeye taşırsınız ve sonrasında farenin sol düğmesine tıklayarak sisteme oraya bir emir yerleştirilmesi gerektiğini söylersiniz. Fare tekrar hareket eder etmez, emri temsil etmek için kullanılan gösterge kaldırılır ve emir göstergesi bırakılır.
Şimdiye kadar özel bir şey yoktu. Ancak koda bakarsanız, aşağıdakileri göreceksiniz:
// ... CHARTEVENT_MOUSE_MOVE code.... }else if ((!bMounting) && (bKeyBuy == bKeySell)) { if (bEClick) { bIsMove = false; m_TradeLine.SpotLight(); } MoveSelection(price, mKeys); } break; // ... The rest of the code....
Böylece, bir bekleyen emir oluşturmadığımızda ve tuşlar serbest bırakıldığında, farenin fiyat konumunu seçilebilecek bir göstergeye göndeririz. Bu, vurgulanan satırda yapılır. Sol tıkladığımız anda, bu aktarımı sonlandırırız, göstergenin seçimi kaldırılır ve şu anda yerel olan değişkenin (bIsMove) durumu değiştirilir. Ancak bunu değiştireceğiz. Bu değişkenin durumu yalnızca DispatchMessage fonksiyonu içerisindeki başka bir olay tarafından değiştirilmektedir. Bu olay aşağıda görünmektedir:
// ... Code .... case EV_MOVE: if (bIsMove) { m_TradeLine.SpotLight(); bIsMove = false; }else { m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE)); bIsMove = true; } break;
Bu kod bIsMove değişkeninin durumunu değiştirecektir ve aynı zamanda da seçili olup olmadığını ifade etmek için göstergeyi değiştirecektir.
Dolayısıyla, bu değişkeni sınıf boyunca görünür hale getirirsek, hayaleti gerçek göstergeden ayırabiliriz ve bu sayede yalnızca gerçek ya da yalnızca hayaleti manipüle etmek mümkün olacaktır - bu seçim ilgilendiğiniz şeye bağlıdır. Biz burada gerçek göstergeyi değiştireceğiz, hayalet ise işlem sunucusunun ne gördüğünü gösterecektir.
Bu şekilde, kodla fazla uğraşmak zorunda kalmayız, sadece birkaç ayrıntıyı ayarlarız. Sol tıklama yapıldıktan ve nesne taşındıktan sonra, bekleyen emri veya eşik seviyesini değiştirmek için bir emir gönderilecektir.
Bunun pratikte nasıl yapıldığını görelim. İlk olarak, bir private değişken oluşturalım.
bool m_bIsMovingSelect;
Bu, yukarıda açıkladığım şeyi yansıtmaktadır. Ancak başlatılması gerekir.
C_IndicatorTradeView() : m_bIsMovingSelect(false) {}
Şimdi, DispatchMessage'a gidiyoruz ve bIsMove yerine onu kullanıyoruz.
void DispatchMessage(int id, long lparam, double dparam, string sparam) { ulong ticket; // ... Internal code... switch (id) { case CHARTEVENT_MOUSE_MOVE: // ... Internal code... }else if ((!bMounting) && (bKeyBuy == bKeySell)) { if (bEClick) { m_bIsMovingSelect = false; m_TradeLine.SpotLight(); } MoveSelection(price, mKeys); } break; // ... Internal code... case CHARTEVENT_OBJECT_CLICK: if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev) { // ... Internal code.... case EV_MOVE: if (m_bIsMovingSelect) { m_TradeLine.SpotLight(); m_bIsMovingSelect = false; }else { m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE)); m_bIsMovingSelect = true; } break; } break; } }
Değişiklikler vurgulanmıştır. Böylece artık tüm sınıf göstergeyle ne zaman çalışıp çalışmadığımızı bilecektir. Bu şekilde hayaleti gerçek göstergeden ayırabilir ve daha doğru bir sunum elde edebiliriz.
2.0.3. Sadece ilgili olanları taşıma
Önceki iki konuda, hayalet göstergeye sahip olmak için sistem oluşturduk ve bazı düzeltmeler yaptık. Şimdi bileşenleri taşımamız gerekiyor ve bunun için bazı çalışmalar yapmamız gerekecek. İlk adım, fonksiyonlar arasında aşırı çağrılardan kaçınmak adına ihtiyaç duyduğumuz çeşitli bilgileri depolamak için bir yapı oluşturmaktır. Yapı aşağıda gösterilmektedir:
struct st00 { eIndicatorTrade it; bool bIsMovingSelect, bIsBuy; ulong ticket; double vol, pr, tp, sl; }m_InfoSelection;
Vurgulanan kısım daha önce kodda mevcuttu, ancak şimdi yapının bir parçasıdır. Merak etmeyin. İlerledikçe, yapının neden bu unsurlara sahip olduğu daha açık hale gelecektir.
Burada, değiştirilen ve açıklama gerektiren fonksiyonları inceleyeceğiz. Bunlardan ilki SetTextValue'dur.
void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0) { double finance; switch (it) { case IT_RESULT : PositionAxlePrice(ticket, it, priceOpen); PositionAxlePrice(ticket, IT_PENDING, 0); m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1); case IT_PENDING: value0 = value0 / Terminal.GetVolumeMinimal(); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit); if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor); break; case IT_TAKE : case IT_STOP : finance = (value1 / Terminal.GetAdjustToTrade()) * value0; m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance); if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor); break; } }
Bir şeyi taşırken, hayaletin yeni veriyi takip etmesini istemeyiz, sabit kalmasını ve göstergenin öncesinde nerede olduğunu göstermesini isteriz. Bu, vurgulanan parçalar eklenerek kolayca yapılabilir. Böylece, gerçek gösterge serbestçe hareket ederken hayalet gösterge hareketsiz kalır. Hayalet göstergede hareketin kendisine değil, sunucuda bulunan değerlerin ifadesine sahip olacağız.
Sırada aşağıda gösterilen hareket kodu vardır:
void MoveSelection(double price) { double tp, sl; if (!m_InfoSelection.bIsMovingSelect) return; switch (m_InfoSelection.it) { case IT_TAKE: UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy); break; case IT_STOP: UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price, m_InfoSelection.vol, m_InfoSelection.bIsBuy); break; case IT_PENDING: tp = (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr); sl = (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr); UpdateInfosIndicators(0, m_InfoSelection.ticket, price, tp, sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy); break; } }
Lütfen vurgulanan kısma dikkat edin: sistemin verileri doğru bir şekilde göstermesi için hareketi ayarlar. Garip görünebilir, ancak UpdateINfosIndicators fonksiyonu başka bir düzeltmeye sahiptir. Eğer bunu şimdi yapmazsak, daha sonra sorun yaşayacağız. Diğer fonksiyonlar oldukça basittir ve açıklama gerektirmez.
void SetPriceSelection(double price) { bool isPending; if (!m_InfoSelection.bIsMovingSelect) return; isPending = OrderSelect(m_InfoSelection.ticket); m_InfoSelection.bIsMovingSelect = false; m_TradeLine.SpotLight(); switch (m_InfoSelection.it) { case IT_TAKE: if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl); else ModifyPosition(m_InfoSelection.ticket, price, m_InfoSelection.sl); break; case IT_STOP: if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price); else ModifyPosition(m_InfoSelection.ticket, m_InfoSelection.tp, price); break; case IT_PENDING: ModifyOrderPendent(m_InfoSelection.ticket, price, (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr), (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr)); break; } }
Yukarıdaki fonksiyon sunucuyu neler olduğu ve yeni verinin ne olduğu hakkında bilgilendirecektir. Lütfen vurgulanan satırlarda göstergenin seçilmesi gerektiğini unutmayın, aksi takdirde sunucuya istek yapılmayacaktır.
Göze çarpan son fonksiyon aşağıda gösterilmektedir:
void DispatchMessage(int id, long lparam, double dparam, string sparam) { ulong ticket; double price; bool bKeyBuy, bKeySell, bEClick; datetime dt; uint mKeys; char cRet; eIndicatorTrade it; eEventType ev; static bool bMounting = false, bIsDT = false; static double valueTp = 0, valueSl = 0, memLocal = 0; switch (id) { case CHARTEVENT_MOUSE_MOVE: Mouse.GetPositionDP(dt, price); mKeys = Mouse.GetButtonStatus(); bEClick = (mKeys & 0x01) == 0x01; //Left mouse click bKeyBuy = (mKeys & 0x04) == 0x04; //Pressed SHIFT bKeySell = (mKeys & 0x08) == 0x08; //Pressed CTRL if (bKeyBuy != bKeySell) { if (!bMounting) { Mouse.Hide(); bIsDT = Chart.GetBaseFinance(m_InfoSelection.vol, valueTp, valueSl); valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_InfoSelection.vol); valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_InfoSelection.vol); m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE)); m_InfoSelection.it = IT_PENDING; m_InfoSelection.ticket = def_IndicatorTicket0; m_InfoSelection.bIsMovingSelect = true; m_InfoSelection.pr = price; bMounting = true; } m_InfoSelection.tp = m_InfoSelection.pr + (bKeyBuy ? valueTp : (-valueTp)); m_InfoSelection.sl = m_InfoSelection.pr + (bKeyBuy ? (-valueSl) : valueSl); m_InfoSelection.bIsBuy = bKeyBuy; MoveSelection(price); if ((bEClick) && (memLocal == 0)) { MoveSelection(0); m_InfoSelection.bIsMovingSelect = false; CreateOrderPendent(m_InfoSelection.vol, bKeyBuy, memLocal = price, price + m_InfoSelection.tp - m_InfoSelection.pr, price + m_InfoSelection.sl - m_InfoSelection.pr, bIsDT); } }else if (bMounting) { MoveSelection(0); m_InfoSelection.bIsMovingSelect = false; Mouse.Show(); memLocal = 0; bMounting = false; }else if ((!bMounting) && (bKeyBuy == bKeySell)) { if (bEClick) SetPriceSelection(price); else MoveSelection(price); } break; case CHARTEVENT_OBJECT_DELETE: if (GetIndicatorInfos(sparam, ticket, price, it, ev)) { CreateIndicatorTrade(ticket, it); GetInfosTradeServer(ticket); m_InfoSelection.bIsMovingSelect = false; UpdateInfosIndicators(0, ticket, m_InfoSelection.pr, m_InfoSelection.tp, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy); } break; case CHARTEVENT_CHART_CHANGE: ChartSetInteger(ChartID(), CHART_SHOW_OBJECT_DESCR, false); ReDrawAllsIndicator(); break; case CHARTEVENT_OBJECT_CLICK: if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev) { case EV_CLOSE: if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it) { case IT_PENDING: case IT_RESULT: if (cRet < 0) RemoveOrderPendent(ticket); else ClosePosition(ticket); break; case IT_TAKE: case IT_STOP: m_InfoSelection.bIsMovingSelect = true; SetPriceSelection(0); break; } break; case EV_MOVE: if (m_InfoSelection.bIsMovingSelect) { m_TradeLine.SpotLight(); m_InfoSelection.bIsMovingSelect = false; }else { m_InfoSelection.ticket = ticket; m_InfoSelection.it = it; if (m_InfoSelection.bIsMovingSelect = (GetInfosTradeServer(ticket) != 0)) m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE)); } break; } break; } }
Bu fonksiyonla ilgili dikkat edilmesi gereken birkaç şey vardır, ancak önceki versiyondan ne kadar farklı hale geldiğine dikkat edin: burada çok daha fazla kodu yeniden kullanıyoruz. Böylece, başka bir yerde kodda bir hata varsa, bunu hızlı bir şekilde fark edebilir ve düzeltebiliriz. Öncesinde, bu fonksiyonun kodu bazı düzeltmeler ve diğer kısımlardaki kusurlarla birlikte biraz dengesizdi. Ana noktalardan biri, bir bekleyen emir yerleştirirken, hareket eden göstergeler için ayrı bir sistem olmasıydı, ancak şimdi tek bir sisteme sahibiz (grafikte emir yerleştirmek için kullanılanla aynı). Nesneleri taşımak için de kullanılır, yani artık aynı CHARTEVENT_MOUSE_MOVE olayı hem bekleyen emir yerleştirmek hem de nesneleri taşımak için kullanılır. Küçük bir detay gibi görünebilir, ancak koddaki herhangi bir değişikliği görünür hale getirir ve bir sorunumuz varsa, fare olayını kullandığımız sürece ortaya çıkacaktır.
Sonuç
Yapılan değişikliklerle ilgili daha net bir fikir edinmek için lütfen aşağıdaki videoyu izleyin. Uzman Danışmanı emir sistemi açısından tamamen eksiksiz hale getirmek için şimdi sadece birkaç ayrıntıya daha ihtiyacımız olduğunu göreceksiniz.
MetaQuotes Ltd tarafından Portekizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/pt/articles/10563
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz