440 lines
16 KiB
MQL5
440 lines
16 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| sonic |
|
|
//| Copyright 2021, NhatNamBe |
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#include "init.mqh"
|
|
#include "pnlInfo.mqh"
|
|
|
|
input int MagicNumber=1234567; // Expert Advisor ID
|
|
input bool DescriptionModeFull=true; // Detailed output mode
|
|
input double inputProfit=0.00005; // Profit
|
|
input double step=0.00005; // Step
|
|
input double amount=0.01; // Amount
|
|
input double reachPNL=0.001; // Reach PNL
|
|
input int maxOrder=5; // Max Order
|
|
input string testSide="Buy"; // Side for backtest
|
|
datetime history_start;
|
|
ulong currentPendingBuyTicket=0;
|
|
ulong currentPendingSellTicket=0;
|
|
bool tradingLong=false;
|
|
bool tradingShort=false;
|
|
|
|
CpnlInfo ExtScript;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//|Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
//--- check if autotrading is allowed
|
|
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
|
|
{
|
|
Alert("Autotrading in the terminal is disabled, Expert Advisor will be removed.");
|
|
ExpertRemove();
|
|
return(-1);
|
|
}
|
|
int ret=CheckAccount();
|
|
if(ret)
|
|
{
|
|
ExpertRemove();
|
|
return(ret);
|
|
}
|
|
GetSymbolInfo();
|
|
//--- save the time of launching the Expert Advisor for receiving trading history
|
|
history_start=TimeCurrent();
|
|
//---
|
|
if(MQLInfoInteger(MQL_TESTER))
|
|
{
|
|
Print("Backtest");
|
|
if(testSide=="Buy")
|
|
{
|
|
tradingLong=true;
|
|
CheckPositionAndPlaceNewTrade(POSITION_TYPE_BUY);
|
|
}
|
|
else
|
|
if(testSide=="Sell")
|
|
{
|
|
tradingShort=true;
|
|
CheckPositionAndPlaceNewTrade(POSITION_TYPE_SELL);
|
|
}
|
|
}
|
|
else
|
|
CreateBuySellButtons();
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
//--- delete all graphical objects
|
|
ObjectDelete(0,"Buy");
|
|
ObjectDelete(0,"Sell");
|
|
EventKillTimer();
|
|
//---
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimer()
|
|
{
|
|
PNLStart();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void PNLStart()
|
|
{
|
|
double pPnl = ExtScript.Processing(0.001);
|
|
if(pPnl)
|
|
{
|
|
// TODO: Do something here
|
|
closePosition();
|
|
ExtScript.SetLastPnl(pPnl);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void closePosition()
|
|
{
|
|
// TODO:
|
|
// 1. cancel all open-order
|
|
// 2. closed trade
|
|
ENUM_POSITION_TYPE targetType = POSITION_TYPE_BUY;
|
|
CPositionInfo position;
|
|
double nearestTakeProfit=0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket>0)
|
|
{
|
|
position.SelectByTicket(ticket);
|
|
ENUM_POSITION_TYPE posType = position.PositionType();
|
|
double tpPrice = position.TakeProfit();
|
|
Print(EnumToString(posType) + " : " + (string)ticket + " price: " + tpPrice);
|
|
CTrade trade;
|
|
//trade.PositionClose(_Symbol);
|
|
if(!trade.PositionClose(ticket))
|
|
{
|
|
Print("Loi roi....");
|
|
}
|
|
}
|
|
}
|
|
while(true)
|
|
{
|
|
int i = PositionsTotal() - 1;
|
|
if(i <= 0)
|
|
{
|
|
break;
|
|
}
|
|
Sleep(1000);
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| TradeTransaction function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTradeTransaction(const MqlTradeTransaction &trans,
|
|
const MqlTradeRequest &request,
|
|
const MqlTradeResult &result)
|
|
{
|
|
ulong lastOrderID =trans.order;
|
|
ENUM_ORDER_TYPE lastOrderType =trans.order_type;
|
|
ENUM_ORDER_STATE lastOrderState=trans.order_state;
|
|
//--- the name of the symbol, for which a transaction was performed
|
|
string trans_symbol=trans.symbol;
|
|
//--- type of transaction
|
|
ENUM_TRADE_TRANSACTION_TYPE trans_type=trans.type;
|
|
switch(trans.type)
|
|
{
|
|
case TRADE_TRANSACTION_HISTORY_ADD: // adding an order to the history
|
|
{
|
|
//--- order ID in an external system - a ticket assigned by an Exchange
|
|
string Exchange_ticket="";
|
|
if(lastOrderState==ORDER_STATE_FILLED)
|
|
{
|
|
if(HistoryOrderSelect(lastOrderID))
|
|
Exchange_ticket=HistoryOrderGetString(lastOrderID,ORDER_EXTERNAL_ID);
|
|
if(Exchange_ticket!="")
|
|
Exchange_ticket=StringFormat("(Exchange ticket=%s)",Exchange_ticket);
|
|
}
|
|
|
|
PrintFormat("MqlTradeTransaction: %s order #%d %s %s %s %s",EnumToString(trans_type),
|
|
lastOrderID,EnumToString(lastOrderType),trans_symbol,EnumToString(lastOrderState),Exchange_ticket);
|
|
if(lastOrderState==ORDER_STATE_FILLED)
|
|
{
|
|
ENUM_POSITION_TYPE targetType;
|
|
if(lastOrderID==currentPendingBuyTicket)
|
|
currentPendingBuyTicket=0;
|
|
else
|
|
if(lastOrderID==currentPendingSellTicket)
|
|
currentPendingSellTicket=0;
|
|
}
|
|
}
|
|
break;
|
|
default: // other transactions
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void OnTrade(void)
|
|
{
|
|
static int positions=0; // number of open positions
|
|
int curr_positions=PositionsTotal();
|
|
if(curr_positions!=positions)
|
|
{
|
|
positions=curr_positions;
|
|
if(tradingLong)
|
|
CheckPositionAndPlaceNewTrade(POSITION_TYPE_BUY);
|
|
if(tradingShort)
|
|
CheckPositionAndPlaceNewTrade(POSITION_TYPE_SELL);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CheckPositionAndPlaceNewTrade(ENUM_POSITION_TYPE targetType)
|
|
{
|
|
if((targetType==POSITION_TYPE_BUY && !tradingLong) || (targetType==POSITION_TYPE_SELL && !tradingShort))
|
|
{
|
|
Print("### " + EnumToString(targetType) + " is disable");
|
|
return;
|
|
}
|
|
CPositionInfo position;
|
|
CTrade trade;
|
|
COrderInfo orderInfo;
|
|
double nearestPos=0;
|
|
double farthestPos=0;
|
|
ulong farthestTicket=0;
|
|
ulong currentPendingTicket;
|
|
int openPosNum=0;
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket>0)
|
|
{
|
|
position.SelectByTicket(ticket);
|
|
ENUM_POSITION_TYPE posType = position.PositionType();
|
|
double posPrice = position.PriceOpen();
|
|
//Print(EnumToString(posType) + " : " + (string)ticket + " price: " + tpPrice);
|
|
if(targetType==posType)
|
|
{
|
|
if(!nearestPos || nearestPos*GetStepWise(targetType) > posPrice*GetStepWise(targetType))
|
|
nearestPos=posPrice;
|
|
if(!farthestTicket || farthestPos*GetStepWise(targetType) < posPrice*GetStepWise(targetType))
|
|
{
|
|
farthestPos=posPrice;
|
|
farthestTicket=ticket;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
openPosNum+=1;
|
|
if(openPosNum > maxOrder)
|
|
{
|
|
//--- Cut farthest one
|
|
Print("### Reach max order, cut farthest Pos " + farthestTicket + " at " + farthestPos);
|
|
trade.PositionClose(farthestTicket);
|
|
}
|
|
|
|
if(targetType==POSITION_TYPE_BUY)
|
|
currentPendingTicket=currentPendingBuyTicket;
|
|
else
|
|
currentPendingTicket=currentPendingSellTicket;
|
|
|
|
if(!nearestPos)
|
|
{
|
|
Print("#### " + EnumToString(targetType) + " there is no pos yet");
|
|
if(currentPendingTicket)
|
|
{
|
|
//--- delete existing pending order
|
|
trade.OrderDelete(currentPendingTicket);
|
|
}
|
|
//--- receiving a buy price
|
|
double price=(targetType==POSITION_TYPE_BUY)?SymbolInfoDouble(_Symbol,SYMBOL_ASK):SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
//--- filling comments
|
|
ENUM_ORDER_TYPE order_type=(targetType==POSITION_TYPE_BUY)?ORDER_TYPE_BUY:ORDER_TYPE_SELL;
|
|
string comment=EnumToString(order_type) + " market "+_Symbol+" "+amount+" at "+ price;
|
|
Print(comment);
|
|
//--- everything is ready, trying to open a buy position
|
|
currentPendingTicket=PlaceOrder(_Symbol, targetType, order_type, amount, price, 0, inputProfit, comment);
|
|
}
|
|
else
|
|
{
|
|
Print("#### " + EnumToString(targetType) + " nearestPos: " + nearestPos);
|
|
//--- receiving a buy price
|
|
double price=nearestPos - GetStepWise(targetType)*step;
|
|
//--- filling comments
|
|
ENUM_ORDER_TYPE order_type=(targetType==POSITION_TYPE_BUY)?ORDER_TYPE_BUY_LIMIT:ORDER_TYPE_SELL_LIMIT;
|
|
string comment=EnumToString(order_type) + " " +_Symbol+" "+amount+" at "+ price;
|
|
Print(comment);
|
|
//--- everything is ready, trying to open a buy position
|
|
if(currentPendingTicket)
|
|
{
|
|
//--- check if new pending order is same to existing one
|
|
orderInfo.Select(currentPendingTicket);
|
|
double existPrice=orderInfo.PriceOpen();
|
|
if(existPrice==price)
|
|
{
|
|
Print("existing price ", existPrice);
|
|
return;
|
|
}
|
|
//--- delete existing pending order
|
|
trade.OrderDelete(currentPendingTicket);
|
|
}
|
|
currentPendingTicket=PlaceOrder(_Symbol, targetType, order_type, amount, price, 0, inputProfit, comment);
|
|
|
|
}
|
|
if(targetType==POSITION_TYPE_BUY)
|
|
currentPendingBuyTicket=currentPendingTicket;
|
|
else
|
|
currentPendingSellTicket=currentPendingTicket;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| ChartEvent function |
|
|
//+------------------------------------------------------------------+
|
|
void OnChartEvent(const int id,
|
|
const long &lparam,
|
|
const double &dparam,
|
|
const string &sparam)
|
|
{
|
|
CTrade trade;
|
|
//--- handling CHARTEVENT_CLICK event ("Clicking the chart")
|
|
if(id==CHARTEVENT_OBJECT_CLICK)
|
|
{
|
|
Print("=> ",__FUNCTION__,": sparam = ",sparam);
|
|
//--- minimum volume for a deal
|
|
// double volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
|
|
//--- if "Buy" button is pressed, then buy
|
|
if(sparam=="Buy")
|
|
{
|
|
tradingLong=true;
|
|
CheckPositionAndPlaceNewTrade(POSITION_TYPE_BUY);
|
|
//--- unpress the button
|
|
ObjectSetInteger(0,"Buy",OBJPROP_STATE,false);
|
|
}
|
|
//--- if "Sell" button is pressed, then sell
|
|
if(sparam=="Sell")
|
|
{
|
|
tradingShort=true;
|
|
CheckPositionAndPlaceNewTrade(POSITION_TYPE_SELL);
|
|
//--- unpress the button
|
|
ObjectSetInteger(0,"Sell",OBJPROP_STATE,false);
|
|
}
|
|
if(sparam=="BuyStop")
|
|
{
|
|
//--- unpress the button
|
|
ObjectSetInteger(0,"BuyStop",OBJPROP_STATE,false);
|
|
if(currentPendingBuyTicket)
|
|
trade.OrderDelete(currentPendingBuyTicket);
|
|
tradingLong=false;
|
|
}
|
|
//--- if "Sell" button is pressed, then sell
|
|
if(sparam=="SellStop")
|
|
{
|
|
//--- unpress the button
|
|
ObjectSetInteger(0,"SellStop",OBJPROP_STATE,false);
|
|
if(currentPendingSellTicket)
|
|
trade.OrderDelete(currentPendingSellTicket);
|
|
tradingShort=false;
|
|
}
|
|
ChartRedraw();
|
|
}
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Create two buttons for buying and selling |
|
|
//+------------------------------------------------------------------+
|
|
void CreateBuySellButtons()
|
|
{
|
|
//--- check the object named "Buy"
|
|
if(ObjectFind(0,"Buy")>=0)
|
|
{
|
|
//--- if the found object is not a button, delete it
|
|
if(ObjectGetInteger(0,"Buy",OBJPROP_TYPE)!=OBJ_BUTTON)
|
|
ObjectDelete(0,"Buy");
|
|
}
|
|
else
|
|
ObjectCreate(0,"Buy",OBJ_BUTTON,0,0,0); // create "Buy" button
|
|
//--- configure "Buy" button
|
|
ObjectSetInteger(0,"Buy",OBJPROP_CORNER,CORNER_RIGHT_UPPER);
|
|
ObjectSetInteger(0,"Buy",OBJPROP_XDISTANCE,100);
|
|
ObjectSetInteger(0,"Buy",OBJPROP_YDISTANCE,50);
|
|
ObjectSetInteger(0,"Buy",OBJPROP_XSIZE,70);
|
|
ObjectSetInteger(0,"Buy",OBJPROP_YSIZE,30);
|
|
ObjectSetString(0,"Buy",OBJPROP_TEXT,"Buy");
|
|
ObjectSetInteger(0,"Buy",OBJPROP_COLOR,clrGreen);
|
|
//--- check presence of the object named "Sell"
|
|
if(ObjectFind(0,"Sell")>=0)
|
|
{
|
|
//--- if the found object is not a button, delete it
|
|
if(ObjectGetInteger(0,"Sell",OBJPROP_TYPE)!=OBJ_BUTTON)
|
|
ObjectDelete(0,"Sell");
|
|
}
|
|
else
|
|
ObjectCreate(0,"Sell",OBJ_BUTTON,0,0,0); // create "Sell" button
|
|
//--- configure "Sell" button
|
|
ObjectSetInteger(0,"Sell",OBJPROP_CORNER,CORNER_RIGHT_UPPER);
|
|
ObjectSetInteger(0,"Sell",OBJPROP_XDISTANCE,100);
|
|
ObjectSetInteger(0,"Sell",OBJPROP_YDISTANCE,100);
|
|
ObjectSetInteger(0,"Sell",OBJPROP_XSIZE,70);
|
|
ObjectSetInteger(0,"Sell",OBJPROP_YSIZE,30);
|
|
ObjectSetString(0,"Sell",OBJPROP_TEXT,"Sell");
|
|
ObjectSetInteger(0,"Sell",OBJPROP_COLOR,clrRed);
|
|
|
|
//--- check the object named "BuyStop"
|
|
if(ObjectFind(0,"BuyStop")>=0)
|
|
{
|
|
//--- if the found object is not a button, delete it
|
|
if(ObjectGetInteger(0,"BuyStop",OBJPROP_TYPE)!=OBJ_BUTTON)
|
|
ObjectDelete(0,"BuyStop");
|
|
}
|
|
else
|
|
ObjectCreate(0,"BuyStop",OBJ_BUTTON,0,0,0); // create "Buy" button
|
|
//--- configure "Buy" button
|
|
ObjectSetInteger(0,"BuyStop",OBJPROP_CORNER,CORNER_RIGHT_UPPER);
|
|
ObjectSetInteger(0,"BuyStop",OBJPROP_XDISTANCE,250);
|
|
ObjectSetInteger(0,"BuyStop",OBJPROP_YDISTANCE,50);
|
|
ObjectSetInteger(0,"BuyStop",OBJPROP_XSIZE,150);
|
|
ObjectSetInteger(0,"BuyStop",OBJPROP_YSIZE,30);
|
|
ObjectSetString(0,"BuyStop",OBJPROP_TEXT,"Pause Buy");
|
|
ObjectSetInteger(0,"BuyStop",OBJPROP_COLOR,clrGreen);
|
|
//--- check presence of the object named "SellStop"
|
|
if(ObjectFind(0,"SellStop")>=0)
|
|
{
|
|
//--- if the found object is not a button, delete it
|
|
if(ObjectGetInteger(0,"SellStop",OBJPROP_TYPE)!=OBJ_BUTTON)
|
|
ObjectDelete(0,"SellStop");
|
|
}
|
|
else
|
|
ObjectCreate(0,"SellStop",OBJ_BUTTON,0,0,0); // create "SellStop" button
|
|
//--- configure "SellStop" button
|
|
ObjectSetInteger(0,"SellStop",OBJPROP_CORNER,CORNER_RIGHT_UPPER);
|
|
ObjectSetInteger(0,"SellStop",OBJPROP_XDISTANCE,250);
|
|
ObjectSetInteger(0,"SellStop",OBJPROP_YDISTANCE,100);
|
|
ObjectSetInteger(0,"SellStop",OBJPROP_XSIZE,150);
|
|
ObjectSetInteger(0,"SellStop",OBJPROP_YSIZE,30);
|
|
ObjectSetString(0,"SellStop",OBJPROP_TEXT,"Pause Sell");
|
|
ObjectSetInteger(0,"SellStop",OBJPROP_COLOR,clrRed);
|
|
//--- perform forced update of the chart to see the buttons immediately
|
|
ChartRedraw();
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|