// More information about this indicator can be found at: // http://fxcodebase.com/code/viewtopic.php?f=38&t=67333 //+------------------------------------------------------------------+ //| Copyright © 2019, Gehtsoft USA LLC | //| http://fxcodebase.com | //+------------------------------------------------------------------+ //| Developed by : Mario Jemic | //| mario.jemic@gmail.com | //+------------------------------------------------------------------+ //| Support our efforts by donating | //| Paypal : https://goo.gl/9Rj74e | //+------------------------------------------------------------------+ //| Patreon : https://goo.gl/GdXWeN | //| BitCoin : 15VCJTLaz12Amr7adHSBtL9v8XomURo9RF | //| BitCoin Cash : 1BEtS465S3Su438Kc58h2sqvVvHK9Mijtg | //| Ethereum : 0x8C110cD61538fb6d7A2B47858F0c0AaBd663068D | //| LiteCoin : LLU8PSY2vsq7B9kRELLZQcKf5nJQrdeqwD | //+------------------------------------------------------------------+ #property copyright "Copyright © 2019, Gehtsoft USA LLC" #property link "http://fxcodebase.com" #property version "1.0" #property strict extern double HDist = 20; // Hedging distance extern int MaxEntries = 3; // Max entries extern double Amount1 = 1; // Trade Amount 1 in Lots extern double Amount2 = 3; // Trade Amount 2 in Lots extern double Amount3 = 6; // Trade Amount 3 in Lots extern double Amount4 = 12; // Trade Amount 4 in Lots extern double Amount5 = 24; // Trade Amount 5 in Lots extern double Amount6 = 48; // Trade Amount 6 in Lots // Trade controller v.2.11 #define LIVE_TRADING #define REVERSABLE_LOGIC_FEATURE #define STOP_LOSS_FEATURE extern #define TAKE_PROFIT_FEATURE extern extern string GeneralSection = ""; // == General == extern int slippage_points = 3; // Slippage, points extern int magic_number = 42; // Magic number STOP_LOSS_FEATURE string StopLossSection = ""; // == Stop loss == enum StopLimitType { StopLimitDoNotUse, // Do not use StopLimitPercent, // Set in % StopLimitPips, // Set in Pips StopLimitDollar, // Set in $, StopLimitRiskReward // Set in % of stop loss }; enum PositionSizeType { PositionSizeAmount, // $ PositionSizeContract, // In contracts PositionSizeEquity, // % of equity PositionSizeRisk // Risk in % of equity }; STOP_LOSS_FEATURE StopLimitType stop_loss_type = StopLimitDoNotUse; // Stop loss type STOP_LOSS_FEATURE double stop_loss_value = 10; // Stop loss value TAKE_PROFIT_FEATURE string TakeProfitSection = ""; // == Take Profit == TAKE_PROFIT_FEATURE StopLimitType take_profit_type = StopLimitDoNotUse; // Take profit type TAKE_PROFIT_FEATURE double take_profit_value = 10; // Take profit value // Instrument info v.1.1 class InstrumentInfo { string _symbol; double _mult; double _point; double _pipSize; int _digits; double _tickSize; public: InstrumentInfo(const string symbol) { _symbol = symbol; _point = MarketInfo(symbol, MODE_POINT); _digits = (int)MarketInfo(symbol, MODE_DIGITS); _mult = _digits == 3 || _digits == 5 ? 10 : 1; _pipSize = _point * _mult; _tickSize = MarketInfo(_symbol, MODE_TICKSIZE); } static double GetBid(const string symbol) { return MarketInfo(symbol, MODE_BID); } double GetBid() { return GetBid(_symbol); } static double GetAsk(const string symbol) { return MarketInfo(symbol, MODE_ASK); } double GetAsk() { return GetAsk(_symbol); } double GetPipSize() { return _pipSize; } string GetSymbol() { return _symbol; } double GetSpread() { return (GetAsk() - GetBid()) / GetPipSize(); } int GetDigits() { return _digits; } double GetTickSize() { return _tickSize; } double RoundRate(const double rate) { return NormalizeDouble(MathCeil(rate / _tickSize + 0.5) * _tickSize, _digits); } }; interface ICondition { public: virtual bool IsPass(const int period) = 0; }; interface IConditionFactory { public: virtual ICondition *CreateCondition(const int order) = 0; }; class ABaseCondition : public ICondition { protected: ENUM_TIMEFRAMES _timeframe; InstrumentInfo *_instrument; public: ABaseCondition(const string symbol, ENUM_TIMEFRAMES timeframe) { _instrument = new InstrumentInfo(symbol); _timeframe = timeframe; } ~ABaseCondition() { delete _instrument; } }; class LongCondition : public ABaseCondition { public: LongCondition(const string symbol, ENUM_TIMEFRAMES timeframe) :ABaseCondition(symbol, timeframe) { } bool IsPass(const int period) { //TODO: implement return false; } }; class ExitLongCondition : public ABaseCondition { public: ExitLongCondition(const string symbol, ENUM_TIMEFRAMES timeframe) :ABaseCondition(symbol, timeframe) { } bool IsPass(const int period) { //TODO: implement return false; } }; class ShortCondition : public ABaseCondition { public: ShortCondition(const string symbol, ENUM_TIMEFRAMES timeframe) :ABaseCondition(symbol, timeframe) { } bool IsPass(const int period) { //TODO: implement return false; } }; class ExitShortCondition : public ABaseCondition { public: ExitShortCondition(const string symbol, ENUM_TIMEFRAMES timeframe) :ABaseCondition(symbol, timeframe) { } bool IsPass(const int period) { //TODO: implement return false; } }; class DisabledCondition : public ICondition { public: bool IsPass(const int period) { return false; } }; class NoCondition : public ICondition { public: bool IsPass(const int period) { return true; } }; enum OrderSide { BuySide, SellSide }; // Orders iterator v 1.7 enum CompareType { CompareLessThan }; class OrdersIterator { bool _useMagicNumber; int _magicNumber; bool _useOrderType; int _orderType; bool _trades; bool _useSide; bool _isBuySide; int _lastIndex; bool _useSymbol; string _symbol; bool _useProfit; double _profit; CompareType _profitCompare; bool _orders; public: OrdersIterator() { _useOrderType = false; _useMagicNumber = false; _useSide = false; _lastIndex = INT_MIN; _trades = false; _useSymbol = false; _useProfit = false; _orders = false; } OrdersIterator *WhenSymbol(const string symbol) { _useSymbol = true; _symbol = symbol; return &this; } OrdersIterator *WhenProfit(const double profit, const CompareType compare) { _useProfit = true; _profit = profit; _profitCompare = compare; return &this; } OrdersIterator *WhenTrade() { _trades = true; return &this; } OrdersIterator *WhenOrder() { _orders = true; return &this; } OrdersIterator *WhenSide(const OrderSide side) { _useSide = true; _isBuySide = side == BuySide; return &this; } OrdersIterator *WhenOrderType(const int orderType) { _useOrderType = true; _orderType = orderType; return &this; } OrdersIterator *WhenMagicNumber(const int magicNumber) { _useMagicNumber = true; _magicNumber = magicNumber; return &this; } int GetOrderType() { return OrderType(); } double GetProfit() { return OrderProfit(); } double IsBuy() { return OrderType() == OP_BUY; } double IsSell() { return OrderType() == OP_SELL; } int Count() { int count = 0; for (int i = OrdersTotal() - 1; i >= 0; i--) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && PassFilter()) { count++; } } return count; } bool Next() { if (_lastIndex == INT_MIN) { _lastIndex = OrdersTotal() - 1; } else _lastIndex = _lastIndex - 1; while (_lastIndex >= 0) { if (OrderSelect(_lastIndex, SELECT_BY_POS, MODE_TRADES) && PassFilter()) return true; _lastIndex = _lastIndex - 1; } return false; } bool Any() { for (int i = OrdersTotal() - 1; i >= 0; i--) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && PassFilter()) { return true; } } return false; } private: bool PassFilter() { if (_useMagicNumber && OrderMagicNumber() != _magicNumber) return false; if (_useOrderType && OrderType() != _orderType) return false; if (_trades && !IsTrade()) return false; if (_orders && IsTrade()) return false; if (_useSymbol && OrderSymbol() != _symbol) return false; if (_useProfit) { switch (_profitCompare) { case CompareLessThan: if (OrderProfit() >= _profit) return false; break; } } if (_useSide) { if (_trades) { if (_isBuySide && !IsBuy()) return false; if (!_isBuySide && !IsSell()) return false; } else { //TODO: IMPLEMENT!!!! } } return true; } bool IsTrade() { return (OrderType() == OP_BUY || OrderType() == OP_SELL) && OrderCloseTime() == 0.0; } }; // Trade calculator v.1.12 class TradeCalculator { InstrumentInfo *_symbol; TradeCalculator(const string symbol) { _symbol = new InstrumentInfo(symbol); } public: static TradeCalculator *Create(const string symbol) { ResetLastError(); double temp = MarketInfo(symbol, MODE_POINT); if (GetLastError() != 0) return NULL; return new TradeCalculator(symbol); } ~TradeCalculator() { delete _symbol; } double GetPipSize() { return _symbol.GetPipSize(); } string GetSymbol() { return _symbol.GetSymbol(); } double GetBid() { return _symbol.GetBid(); } double GetAsk() { return _symbol.GetAsk(); } int GetDigits() { return _symbol.GetDigits(); } double GetSpread() { return _symbol.GetSpread(); } static bool IsBuyOrder() { switch (OrderType()) { case OP_BUY: case OP_BUYLIMIT: case OP_BUYSTOP: return true; } return false; } double GetBreakevenPrice(const int side, const int magicNumber, double &totalAmount) { totalAmount = 0.0; double lotStep = SymbolInfoDouble(_symbol.GetSymbol(), SYMBOL_VOLUME_STEP); double price = side == OP_BUY ? GetBid() : GetAsk(); double totalPL = 0; OrdersIterator it1(); it1.WhenMagicNumber(magicNumber); it1.WhenSymbol(_symbol.GetSymbol()); it1.WhenOrderType(side); while (it1.Next()) { double orderLots = OrderLots(); totalAmount += orderLots / lotStep; if (side == OP_BUY) totalPL += (price - OrderOpenPrice()) * (OrderLots() / lotStep); else totalPL += (OrderOpenPrice() - price) * (OrderLots() / lotStep); } if (totalAmount == 0.0) return 0.0; double shift = -(totalPL / totalAmount); return side == OP_BUY ? price + shift : price - shift; } double CalculateTakeProfit(const bool isBuy, const double takeProfit, const StopLimitType takeProfitType, const double amount, double basePrice) { int direction = isBuy ? 1 : -1; switch (takeProfitType) { case StopLimitPercent: return RoundRate(basePrice + basePrice * takeProfit / 100.0 * direction); case StopLimitPips: return RoundRate(basePrice + takeProfit * _symbol.GetPipSize() * direction); case StopLimitDollar: return RoundRate(basePrice + CalculateSLShift(amount, takeProfit) * direction); } return 0.0; } double CalculateStopLoss(const bool isBuy, const double stopLoss, const StopLimitType stopLossType, const double amount, double basePrice) { int direction = isBuy ? 1 : -1; switch (stopLossType) { case StopLimitPercent: return RoundRate(basePrice - basePrice * stopLoss / 100.0 * direction); case StopLimitPips: return RoundRate(basePrice - stopLoss * _symbol.GetPipSize() * direction); case StopLimitDollar: return RoundRate(basePrice - CalculateSLShift(amount, stopLoss) * direction); } return 0.0; } double GetLots(PositionSizeType lotsType, double lotsValue, double stopDistance) { switch (lotsType) { case PositionSizeAmount: return GetLotsForMoney(lotsValue); case PositionSizeContract: return LimitLots(RoundLots(lotsValue)); case PositionSizeEquity: return GetLotsForMoney(AccountEquity() * lotsValue / 100.0); case PositionSizeRisk: { double affordableLoss = AccountEquity() * lotsValue / 100.0; double unitCost = MarketInfo(_symbol.GetSymbol(), MODE_TICKVALUE); double tickSize = _symbol.GetTickSize(); double possibleLoss = unitCost * stopDistance / tickSize; if (possibleLoss <= 0.01) return 0; return LimitLots(RoundLots(affordableLoss / possibleLoss)); } } return lotsValue; } bool IsLotsValid(const double lots, PositionSizeType lotsType, string &error) { switch (lotsType) { case PositionSizeContract: return IsContractLotsValid(lots, error); } return true; } double NormalizeLots(const double lots) { return LimitLots(RoundLots(lots)); } double RoundRate(const double rate) { return _symbol.RoundRate(rate); } private: bool IsContractLotsValid(const double lots, string &error) { double minVolume = SymbolInfoDouble(_symbol.GetSymbol(), SYMBOL_VOLUME_MIN); if (minVolume > lots) { error = "Min. allowed lot size is " + DoubleToString(minVolume); return false; } double maxVolume = SymbolInfoDouble(_symbol.GetSymbol(), SYMBOL_VOLUME_MAX); if (maxVolume < lots) { error = "Max. allowed lot size is " + DoubleToString(maxVolume); return false; } return true; } double GetLotsForMoney(const double money) { double marginRequired = MarketInfo(_symbol.GetSymbol(), MODE_MARGINREQUIRED); if (marginRequired <= 0.0) { Print("Margin is 0. Server misconfiguration?"); return 0.0; } double lots = RoundLots(money / marginRequired); return LimitLots(lots); } double RoundLots(const double lots) { double lotStep = SymbolInfoDouble(_symbol.GetSymbol(), SYMBOL_VOLUME_STEP); if (lotStep == 0) return 0.0; return floor(lots / lotStep) * lotStep; } double LimitLots(const double lots) { double minVolume = SymbolInfoDouble(_symbol.GetSymbol(), SYMBOL_VOLUME_MIN); if (minVolume > lots) return 0.0; double maxVolume = SymbolInfoDouble(_symbol.GetSymbol(), SYMBOL_VOLUME_MAX); if (maxVolume < lots) return maxVolume; return lots; } double CalculateSLShift(const double amount, const double money) { double unitCost = MarketInfo(_symbol.GetSymbol(), MODE_TICKVALUE); double tickSize = _symbol.GetTickSize(); return (money / (unitCost / tickSize)) / amount; } }; // Trading commands v.2.3 class TradingCommands { public: static void DeleteOrders(const int magicNumber) { OrdersIterator it1(); it1.WhenMagicNumber(magicNumber); it1.WhenOrder(); while (it1.Next()) { int ticket = OrderTicket(); if (!OrderDelete(ticket)) { Print("Failed to delete the order " + IntegerToString(ticket)); } } } static bool DeleteCurrentOrder(string &error) { int ticket = OrderTicket(); if (!OrderDelete(ticket)) { error = "Failed to delete the order " + IntegerToString(ticket); return false; } return true; } static bool CloseCurrentOrder(const int slippage, string &error) { int orderType = OrderType(); if (orderType == OP_BUY) return CloseCurrentOrder(InstrumentInfo::GetBid(OrderSymbol()), slippage, error); if (orderType == OP_SELL) return CloseCurrentOrder(InstrumentInfo::GetAsk(OrderSymbol()), slippage, error); return false; } static bool CloseCurrentOrder(const double price, const int slippage, string &error) { bool closed = OrderClose(OrderTicket(), OrderLots(), price, slippage); if (closed) return true; int lastError = GetLastError(); switch (lastError) { case ERR_TRADE_NOT_ALLOWED: error = "Trading is not allowed"; break; default: error = "Last error: " + IntegerToString(lastError); break; } return false; } static int CloseTrades(OrdersIterator &it, const int slippage) { int closedPositions = 0; while (it.Next()) { int orderType = it.GetOrderType(); string error; if (!CloseCurrentOrder(slippage, error)) Print("Failed to close positoin. ", error); else ++closedPositions; } return closedPositions; } }; // Order builder v.1.1 class OrderBuilder { OrderSide _orderSide; string _instrument; double _amount; double _rate; int _slippage; double _stop; double _limit; int _magicNumber; string _comment; public: OrderBuilder *SetSide(const OrderSide orderSide) { _orderSide = orderSide; return &this; } OrderBuilder *SetInstrument(const string instrument) { _instrument = instrument; return &this; } OrderBuilder *SetAmount(const double amount) { _amount = amount; return &this; } OrderBuilder *SetRate(const double rate) { _rate = NormalizeDouble(rate, Digits); return &this; } OrderBuilder *SetSlippage(const int slippage) { _slippage = slippage; return &this; } OrderBuilder *SetStop(const double stop) { _stop = NormalizeDouble(stop, Digits); return &this; } OrderBuilder *SetLimit(const double limit) { _limit = NormalizeDouble(limit, Digits); return &this; } OrderBuilder *SetMagicNumber(const int magicNumber) { _magicNumber = magicNumber; return &this; } OrderBuilder *SetComment(const string comment) { _comment = comment; return &this; } int Execute(string &errorMessage) { int orderType; if (_orderSide == BuySide) { orderType = _rate > Ask ? OP_BUYSTOP : OP_BUYLIMIT; } else { orderType = _rate < Bid ? OP_SELLSTOP : OP_SELLLIMIT; } double minstoplevel = MarketInfo(_instrument,MODE_STOPLEVEL); int order = OrderSend(_instrument, orderType, _amount, _rate, _slippage, _stop, _limit, _comment, _magicNumber); if (order == -1) { int error = GetLastError(); switch (error) { case ERR_TRADE_NOT_ALLOWED: errorMessage = "Trading is not allowed"; break; case 130: errorMessage = "Failed to create order: stoploss/takeprofit is too close"; break; default: errorMessage = "Failed to create order: " + IntegerToString(error); break; } } return order; } }; int entriesCount = 0; int amountIndex = 0; double GetAmount() { amountIndex++; switch (amountIndex) { case 1: return Amount1; case 2: return Amount2; case 3: return Amount3; case 4: return Amount4; case 5: return Amount5; case 6: return Amount6; } return 0; } TradeCalculator *calc; int OnInit() { calc = TradeCalculator::Create(_Symbol); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { delete calc; } void OnTick() { if (entriesCount >= MaxEntries) return; OrdersIterator it(); bool ordersExists = it.WhenMagicNumber(magic_number) .WhenOrder() .WhenSymbol(_Symbol) .Any(); if (ordersExists) return; OrdersIterator trades(); trades.WhenTrade() .WhenSymbol(_Symbol); InstrumentInfo instr(_Symbol); while (trades.Next()) { OrderBuilder *orderBuilder = new OrderBuilder(); double amount = GetAmount(); if (trades.IsBuy()) { orderBuilder.SetRate(OrderOpenPrice() - HDist * instr.GetPipSize()) .SetSide(SellSide) .SetStop(calc.CalculateStopLoss(false, stop_loss_value, stop_loss_type, amount, instr.GetBid())) .SetLimit(calc.CalculateTakeProfit(false, take_profit_value, take_profit_type, amount, instr.GetBid())); } else { orderBuilder.SetRate(OrderOpenPrice() + HDist * instr.GetPipSize()) .SetSide(BuySide) .SetStop(calc.CalculateStopLoss(true, stop_loss_value, stop_loss_type, amount, instr.GetAsk())) .SetLimit(calc.CalculateTakeProfit(true, take_profit_value, take_profit_type, amount, instr.GetAsk())); } string error; int order = orderBuilder .SetInstrument(_Symbol) .SetAmount(amount) .SetSlippage(slippage_points) .SetMagicNumber(magic_number) .Execute(error); delete orderBuilder; if (order == -1) { Print("Failed to open position: " + error); } else { entriesCount++; } } }