CopyTrader_Demo

Author: Rodrigo Pereira
Price Data Components
Series array that contains tick volumes of each bar
0 Views
0 Downloads
0 Favorites
CopyTrader_Demo
ÿþ//+------------------------------------------------------------------+

//| CopyTrader - Demo Account (Signal Sender)

//+------------------------------------------------------------------+

#property copyright "Rodrigo Pereira"

#property version   "1.00"



// Constantes

#define MAGIC_NUMBER 11

#define STATUS_PENDING "PENDING"

#define STATUS_EXECUTED "EXECUTED"

#define STATUS_CLOSED "CLOSED"



// Constantes para o banco de dados

#define DB_NAME "CopyTrader.sqlite"

#define TABLE_SIGNALS "signals"



// Input parameters

input long COPY_MAGIC_NUMBER = 11; // Magic Number do robô a ser copiado



// Estrutura para armazenar estado das ordens

struct OrderState {

   ulong ticket;

   string symbol;

   ENUM_POSITION_TYPE type;

   double volume;

   double price;

   double sl;

   double tp;

};



// Variáveis globais

OrderState previous_orders[];

ulong previous_positions[];



// Includes necessários

#include <Files\File.mqh>

#include <Trade\Trade.mqh>

#include <Trade\DealInfo.mqh>



// Variável global

CTrade trade;



class CSignalDB {

private:

   int m_db_handle;

   

public:

   CSignalDB() {

      m_db_handle = INVALID_HANDLE;

   }

   

   ~CSignalDB() {

      if(m_db_handle != INVALID_HANDLE) {

         DatabaseClose(m_db_handle);

      }

   }

   

   bool Init() {

      Print("=== INICIANDO BANCO DE DADOS ===");

      

      string common_path = TerminalInfoString(TERMINAL_COMMONDATA_PATH);

      string db_path = common_path + "\\Files\\" + DB_NAME;

      Print("Tentando criar/abrir banco em: ", db_path);

      

      string files_path = common_path + "\\Files";

      if(!FolderCreate(files_path)) {

         Print("Aviso: Pasta já existe ou erro ao criar: ", GetLastError());

      }

      

      ResetLastError();

      m_db_handle = DatabaseOpen(DB_NAME, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);

      

      if(m_db_handle == INVALID_HANDLE) {

         Print("L' Erro ao abrir banco de dados: ", GetLastError());

         Print("Caminho tentado: ", db_path);

         return false;

      }

      

      Print("' Banco aberto com sucesso! Handle: ", m_db_handle);

      

      string create_signals = StringFormat(

         "CREATE TABLE IF NOT EXISTS %s ("

         "id INTEGER PRIMARY KEY AUTOINCREMENT,"

         "ticket INTEGER NOT NULL,"

         "signal_type TEXT NOT NULL,"

         "symbol TEXT NOT NULL,"

         "order_type INTEGER NOT NULL,"

         "volume REAL NOT NULL,"

         "price REAL NOT NULL,"

         "sl REAL,"

         "tp REAL,"

         "status TEXT NOT NULL DEFAULT 'PENDING',"

         "created_at DATETIME DEFAULT CURRENT_TIMESTAMP"

         ")", TABLE_SIGNALS);

         

      Print("Executando query de criação da tabela signals...");

      if(!DatabaseExecute(m_db_handle, create_signals)) {

         Print("L' Erro ao criar tabela signals: ", GetLastError());

         return false;

      }

      Print("' Tabela signals criada/verificada com sucesso!");

      

      string create_history = 

         "CREATE TABLE IF NOT EXISTS signals_history ("

         "id INTEGER PRIMARY KEY AUTOINCREMENT,"

         "ticket INTEGER NOT NULL,"

         "signal_type TEXT NOT NULL,"

         "symbol TEXT NOT NULL,"

         "order_type INTEGER NOT NULL,"

         "volume REAL NOT NULL,"

         "price REAL NOT NULL,"

         "sl REAL,"

         "tp REAL,"

         "status TEXT NOT NULL,"

         "created_at DATETIME,"

         "closed_at DATETIME DEFAULT CURRENT_TIMESTAMP"

         ")";

         

      Print("Executando query de criação da tabela history...");

      if(!DatabaseExecute(m_db_handle, create_history)) {

         Print("L' Erro ao criar tabela history: ", GetLastError());

         return false;

      }

      Print("' Tabela history criada/verificada com sucesso!");

            

      return true;

   }

   

   

   

   bool MoveToHistory(ulong ticket) {

      string check_sql = StringFormat(

         "SELECT COUNT(*) FROM %s WHERE ticket=%d AND status != '%s'",

         TABLE_SIGNALS, ticket, STATUS_EXECUTED

      );

      

      int request = DatabasePrepare(m_db_handle, check_sql);

      if(request == INVALID_HANDLE) {

         Print("L' Erro ao preparar query de verificação");

         return false;

      }

      

      long pending_count = 0;

      if(DatabaseRead(request)) {

         DatabaseColumnLong(request, 0, pending_count);

      }

      

      DatabaseFinalize(request);

      

      if(pending_count > 0) {

         Print("Ainda existem sinais pendentes para o ticket ", ticket);

         return false;

      }

      

      string move_sql = StringFormat(

         "INSERT INTO signals_history SELECT * FROM %s WHERE ticket=%d",

         TABLE_SIGNALS, ticket

      );

      

      if(!DatabaseExecute(m_db_handle, move_sql)) {

         Print("L' Erro ao mover registros para histórico");

         return false;

      }

      

      string delete_sql = StringFormat(

         "DELETE FROM %s WHERE ticket=%d",

         TABLE_SIGNALS, ticket

      );

      

      return DatabaseExecute(m_db_handle, delete_sql);

   }

   

   bool GetPendingSignals(ulong &tickets[]) {

      string sql = StringFormat(

         "SELECT DISTINCT ticket FROM %s WHERE status='%s'",

         TABLE_SIGNALS, STATUS_PENDING

      );

      

      int request = DatabasePrepare(m_db_handle, sql);

      if(request == INVALID_HANDLE) {

         Print("L' Erro ao preparar query de sinais pendentes");

         return false;

      }

      

      ArrayResize(tickets, 0);

      

      while(DatabaseRead(request)) {

         long ticket_value;

         if(DatabaseColumnLong(request, 0, ticket_value)) {

            int size = ArraySize(tickets);

            ArrayResize(tickets, size + 1);

            tickets[size] = (ulong)ticket_value;

         }

      }

      

      DatabaseFinalize(request);

      return true;

   }

   

   bool WriteSignal(string signal_type, ulong ticket, string symbol, 

                   ENUM_POSITION_TYPE type, double volume, 

                   double price, double sl, double tp, 

                   string status = STATUS_PENDING) {

                   

      if(signal_type == "NEW") {

         if(TicketExists(ticket, "NEW")) {

            Print(" &þ Ordem já existe no banco - Ticket: ", ticket);

            return false;

         }

      }

      

      string query = StringFormat(

         "INSERT INTO %s "

         "(ticket, signal_type, symbol, order_type, volume, price, sl, tp, status) "

         "VALUES (%d,'%s','%s',%d,%.2f,%.5f,%.5f,%.5f,'%s')",

         TABLE_SIGNALS, ticket, signal_type, symbol, type, volume, price, sl, tp, status

      );

      

      Print("Executando query: ", query);

      

      bool result = DatabaseExecute(m_db_handle, query);

      if(!result) {

         Print("L' Erro ao executar query: ", GetLastError());

      } else {

         Print("' Query executada com sucesso!");

      }

      

      return result;

   }

   

   bool UpdateSignalStatus(ulong ticket, string new_status) {

      string query = StringFormat(

         "UPDATE %s SET status='%s' WHERE ticket=%d AND status='%s'",

         TABLE_SIGNALS, new_status, ticket, STATUS_PENDING

      );

      

      return DatabaseExecute(m_db_handle, query);

   }

   

   bool TicketExists(ulong ticket, string signal_type) {

      string query = StringFormat(

         "SELECT COUNT(*) FROM %s WHERE ticket = %d AND signal_type = '%s'",

         TABLE_SIGNALS, ticket, signal_type);

         

      int request = DatabasePrepare(m_db_handle, query);

      

      if(request != INVALID_HANDLE) {

         if(DatabaseRead(request)) {

            int count = 0;

            if(DatabaseColumnInteger(request, 0, count)) {

               return (count > 0);

            }

         }

         DatabaseFinalize(request);

      }

      

      return false;

   }



   bool CheckDatabase(string table_name) {

      string sql = StringFormat("SELECT COUNT(*) FROM %s", table_name);

      

      int request = DatabasePrepare(m_db_handle, sql);

      if(request == INVALID_HANDLE) {

         Print("L' Erro ao preparar query: ", GetLastError());

         return false;

      }

      

      long count = 0;

      if(DatabaseRead(request)) {

         DatabaseColumnLong(request, 0, count);

      }

      

      DatabaseFinalize(request);

      return (count > 0);

   }

};



// Variável global

CSignalDB signalDB;



//+------------------------------------------------------------------+

//| Expert initialization function                                     |

//+------------------------------------------------------------------+

int OnInit() {

   Print("=== INICIANDO COPYTRADER DEMO ===");

   

   // Inicializa banco de dados

   if(!signalDB.Init()) {

      Print("Falha ao inicializar banco de dados!");

      return INIT_FAILED;

   }

   

   CheckInitialOrders();

   

   // Configura Magic Number

   trade.SetExpertMagicNumber(COPY_MAGIC_NUMBER);

   

   Print("CopyTrader Demo iniciado com sucesso!");

   return INIT_SUCCEEDED;

}



//+------------------------------------------------------------------+

//| Verifica ordens abertas no início                                 |

//+------------------------------------------------------------------+

void CheckInitialOrders() {

   Print("Verificando ordens abertas no início...");

   

   for(int i = PositionsTotal() - 1; i >= 0; i--) {

      ulong ticket = PositionGetTicket(i);

      if(ticket <= 0) continue;

      

      if(PositionSelectByTicket(ticket)) {

         long position_magic = PositionGetInteger(POSITION_MAGIC);

         if(position_magic != COPY_MAGIC_NUMBER) continue;

         

         ulong order_id = PositionGetInteger(POSITION_IDENTIFIER);

         

         Print("Posição encontrada - Ticket:", ticket, " Order ID:", order_id);

         

         if(order_id == 0) {

            Print("Erro: Não foi possível obter o order_id para a posição ", ticket);

            continue;

         }

         

         OrderState order;

         order.ticket = ticket;

         order.symbol = PositionGetString(POSITION_SYMBOL);

         order.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

         order.volume = PositionGetDouble(POSITION_VOLUME);

         order.price = PositionGetDouble(POSITION_PRICE_OPEN);

         order.sl = PositionGetDouble(POSITION_SL);

         order.tp = PositionGetDouble(POSITION_TP);

         

         signalDB.WriteSignal("NEW", order.ticket, order.symbol, order.type,

                           order.volume, order.price, order.sl, order.tp);

         

         int size = ArraySize(previous_orders);

         ArrayResize(previous_orders, size + 1);

         previous_orders[size] = order;

         

         size = ArraySize(previous_positions);

         ArrayResize(previous_positions, size + 1);

         previous_positions[size] = ticket;

         

         Print("Ordem existente processada - Ticket:", ticket, 

               " Symbol:", order.symbol,

               " Type:", EnumToString(order.type),

               " Volume:", order.volume,

               " Order ID:", order_id);

      }

   }

   

   Print("Verificação inicial concluída. Ordens encontradas: ", ArraySize(previous_orders));

}



//+------------------------------------------------------------------+

//| Expert tick function                                              |

//+------------------------------------------------------------------+

void OnTick() {

   // Verifica mudanças nas ordens

}



//+------------------------------------------------------------------+

//| Escaneia ordens atuais                                           |

//+------------------------------------------------------------------+

void ScanCurrentOrders(OrderState &orders[]) {

   Print("=== Escaneando ordens atuais ===");

   

   for(int i = PositionsTotal() - 1; i >= 0; i--) {

      ulong ticket = PositionGetTicket(i);

      if(ticket <= 0) continue;

      

      if(PositionSelectByTicket(ticket)) {

         long position_magic = PositionGetInteger(POSITION_MAGIC);

         if(position_magic != COPY_MAGIC_NUMBER) continue;

         

         bool exists = false;

         for(int j = 0; j < ArraySize(orders); j++) {

            if(orders[j].ticket == ticket) {

               exists = true;

               break;

            }

         }

         

         if(!exists && PositionSelectByTicket(ticket)) {

            int size = ArraySize(orders);

            ArrayResize(orders, size + 1);

            

            orders[size].ticket = ticket;

            orders[size].symbol = PositionGetString(POSITION_SYMBOL);

            orders[size].type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

            orders[size].volume = PositionGetDouble(POSITION_VOLUME);

            orders[size].price = PositionGetDouble(POSITION_PRICE_OPEN);

            orders[size].sl = PositionGetDouble(POSITION_SL);

            orders[size].tp = PositionGetDouble(POSITION_TP);

            

            Print("Ordem escaneada - Ticket:", ticket,

                  " SL:", orders[size].sl,

                  " TP:", orders[size].tp,

                  " Volume:", orders[size].volume);

         }

      }

   }

   

   Print("Total de ordens escaneadas: ", ArraySize(orders));

}



//+------------------------------------------------------------------+

//| Funções de envio de sinais                                        |

//+------------------------------------------------------------------+

void SendNewOrderSignal(OrderState &orderState) {

   Print("\n=== Enviando Sinal de Nova Ordem ===");

   Print("Ticket: ", orderState.ticket);

   

   if(!signalDB.WriteSignal("NEW", orderState.ticket, orderState.symbol, orderState.type,

                           orderState.volume, orderState.price, orderState.sl, orderState.tp)) {

      Print("L' Erro ao escrever sinal de nova ordem no banco (possível duplicata)");

      return;

   }

   

   Print("' Sinal de nova ordem registrado com sucesso!");

}



void SendModifySignal(OrderState &order) {

   signalDB.WriteSignal("MODIFY", order.ticket, order.symbol, order.type,

                     order.volume, order.price, order.sl, order.tp);

}



void SendCloseSignal(OrderState &order) {

   Print("\n=== Enviando Sinal de Fechamento ===");

   Print("Ticket da posição: ", order.ticket);

   

   if(!signalDB.WriteSignal("CLOSE", order.ticket, order.symbol, order.type,

                           order.volume, order.price, order.sl, order.tp)) {

      Print("L' Erro ao escrever sinal de fechamento no banco!");

      return;

   }

   

   Print("' Sinal de fechamento registrado com sucesso!");

}



void OnTradeTransaction(const MqlTradeTransaction& trans,

                       const MqlTradeRequest& request,

                       const MqlTradeResult& result) {

   

   Print("\n=== Nova Transação Detectada ===");

   Print("Tipo da transação: ", EnumToString(trans.type));

   Print("Position ticket: ", trans.position);

   Print("Deal ticket: ", trans.deal);

   Print("Order ticket: ", trans.order);

   Print("Order type: ", EnumToString(trans.order_type));

   Print("Deal type: ", EnumToString(trans.deal_type));

   

   if(trans.type == TRADE_TRANSACTION_REQUEST) {

      if(request.action == TRADE_ACTION_SLTP) {

         Print("Detectada modificação de SL/TP");

         Print("Position ticket do request: ", request.position);

         

         if(!PositionSelectByTicket(request.position)) {

            Print("L' Posição não encontrada: ", request.position);

            return;

         }

         

         if(PositionGetInteger(POSITION_MAGIC) != COPY_MAGIC_NUMBER) {

            Print("L' Magic number diferente: ", PositionGetInteger(POSITION_MAGIC));

            return;

         }

         

         OrderState orderState;

         orderState.ticket = request.position;

         orderState.symbol = PositionGetString(POSITION_SYMBOL);

         orderState.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

         orderState.volume = PositionGetDouble(POSITION_VOLUME);

         orderState.price = PositionGetDouble(POSITION_PRICE_OPEN);

         orderState.sl = request.sl;

         orderState.tp = request.tp;

         

         SendModifySignal(orderState);

         return;

      }

   }

   

   else if(trans.type == TRADE_TRANSACTION_DEAL_ADD) {

      if(trans.deal_type == DEAL_TYPE_BUY || trans.deal_type == DEAL_TYPE_SELL) {

         if(!PositionSelectByTicket(trans.position)) {

            Print("L' Posição não encontrada: ", trans.position);

            return;

         }

         

         if(PositionGetInteger(POSITION_MAGIC) != COPY_MAGIC_NUMBER) {

            Print("L' Magic number diferente: ", PositionGetInteger(POSITION_MAGIC));

            return;

         }

         

         if(HistoryDealSelect(trans.deal)) {

            long deal_entry = HistoryDealGetInteger(trans.deal, DEAL_ENTRY);

            if(deal_entry == DEAL_ENTRY_IN) {

               OrderState orderState;

               orderState.ticket = trans.position;

               orderState.symbol = PositionGetString(POSITION_SYMBOL);

               orderState.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

               orderState.volume = PositionGetDouble(POSITION_VOLUME);

               orderState.price = PositionGetDouble(POSITION_PRICE_OPEN);

               orderState.sl = PositionGetDouble(POSITION_SL);

               orderState.tp = PositionGetDouble(POSITION_TP);

               

               Print("' Nova posição detectada - Ticket: ", orderState.ticket);

               SendNewOrderSignal(orderState);

            }

         }

      }

   }

   

   else if(trans.type == TRADE_TRANSACTION_HISTORY_ADD) {

      Print("Detectado possível fechamento de posição");

      

      if(!PositionSelectByTicket(trans.position)) {

         if(HistorySelectByPosition(trans.position)) {

            CDealInfo deal;

            for(int i = HistoryDealsTotal() - 1; i >= 0; i--) {

               if(deal.SelectByIndex(i)) {

                  if(deal.Entry() == DEAL_ENTRY_OUT && deal.Magic() == COPY_MAGIC_NUMBER) {

                     Print("' Confirmado fechamento da posição: ", trans.position);

                     

                     OrderState orderState;

                     orderState.ticket = trans.position;

                     orderState.symbol = deal.Symbol();

                     orderState.type = (ENUM_POSITION_TYPE)deal.DealType();

                     orderState.volume = deal.Volume();

                     orderState.price = deal.Price();

                     orderState.sl = 0;

                     orderState.tp = 0;

                     

                     SendCloseSignal(orderState);

                     break;

                  }

               }

            }

         }

      }

   }

}

Comments