forked from nique_372/MQLArticles
870 lines
57 KiB
MQL5
870 lines
57 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| RMt_Functions.mqh |
| |
//| Niquel y Leo, Copyright 2025 |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Niquel y Leo, Copyright 2025"
| |
#property link "https://www.mql5.com"
| |
#property strict
| |
| |
#ifndef MQLARTICLES_RM_RFUNCTIONS_MQH
| |
#define MQLARTICLES_RM_RFUNCTIONS_MQH
| |
| |
#include "LoteSizeCalc.mqh"
| |
| |
//+-------------------------------------------------------------------------------------------------+
| |
//+----------------------------------- Functions -------------------------------------+
| |
//+-------------------------------------------------------------------------------------------------+
| |
//+------------------------------------------------------------------+
| |
double GetPositionCommission(ulong ticket)
| |
{
| |
// Verificar si la posición existe y obtener su tiempo de apertura
| |
if(!PositionSelectByTicket(ticket))
| |
{
| |
Print("Error: Posición con ticket ", ticket, " no encontrada");
| |
return 0.0;
| |
}
| |
datetime open_time = (datetime)PositionGetInteger(POSITION_TIME);
| |
| |
// Usar un rango de tiempo estrecho: desde el tiempo de apertura hasta 1 segundo después
| |
if(!HistorySelect(open_time, open_time + 1))
| |
{
| |
Print("Error: No se pudo cargar el historial de deals para el ticket ", ticket);
| |
return 0.0;
| |
}
| |
| |
// Obtener el primer deal del historial
| |
ulong deal_ticket = HistoryDealGetTicket(0);
| |
if(deal_ticket == 0)
| |
{
| |
Print("Error: No se encontró un deal para el ticket ", ticket);
| |
return 0.0;
| |
}
| |
| |
// Verificar si el deal corresponde a la posición y es de entrada
| |
if(HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID) == ticket)
| |
{
| |
ENUM_DEAL_TYPE deal_type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
| |
if(deal_type == DEAL_TYPE_BUY || deal_type == DEAL_TYPE_SELL)
| |
{
| |
return HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
| |
}
| |
}
| |
| |
Print("Error: No se encontró un deal de apertura válido para el ticket ", ticket);
| |
return 0.0;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
inline CGetLote* CreateLotePtr(string symbol)
| |
{
| |
CGetLote* l = new CGetLote(symbol);
| |
return l;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
double GetTotalPositionProfitNoCurrent(ulong position_ticket)
| |
{
| |
double total_profit = 0.0;
| |
| |
//---
| |
if(HistorySelectByPosition(position_ticket))
| |
{
| |
int deals_count = HistoryDealsTotal();
| |
| |
for(int i = 0; i < deals_count; i++)
| |
{
| |
ulong deal_ticket = HistoryDealGetTicket(i);
| |
if(deal_ticket <= 0)
| |
continue;
| |
| |
ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
| |
| |
if(entry == DEAL_ENTRY_OUT || entry == DEAL_ENTRY_IN)
| |
{
| |
total_profit += HistoryDealGetDouble(deal_ticket, DEAL_PROFIT) +
| |
HistoryDealGetDouble(deal_ticket, DEAL_SWAP) +
| |
HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
| |
}
| |
}
| |
}
| |
| |
return total_profit;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Retrieve the magic number associated with a ticket |
| |
//+------------------------------------------------------------------+
| |
inline ulong GetMagic(const ulong ticket)
| |
{
| |
HistoryOrderSelect(ticket);
| |
return HistoryOrderGetInteger(ticket, ORDER_MAGIC);
| |
}
| |
| |
| |
| |
//+----------------------------------------------------------------------------------------+
| |
//| Calculates the net profit since a given date for a specific magic number or all trades |
| |
//+----------------------------------------------------------------------------------------+
| |
const bool GetNetProfitOmitirDeal[18] =
| |
{
| |
false, //compra
| |
false, //venta
| |
true, //balance
| |
true, //credito
| |
true, //carga adicionales
| |
false, //correcion
| |
true, //bonos
| |
false, //comisiones adicional
| |
false, //comisions del dia
| |
false, //comision del mes
| |
false, //comosions agente dia
| |
false, //comosiion agenet mes,
| |
false, //interes
| |
false, //compra cancelada
| |
false, //ventan cancelada
| |
false, //dividendo
| |
false, //dividendo frankeado, con beneficions
| |
false //impuestos
| |
};
| |
| |
//---
| |
double GetNetProfitSince(bool include_all_magic, ulong specific_magic, datetime start_date)
| |
{
| |
double total_net_profit = 0.0; // Initialize the total net profit
| |
ResetLastError(); // Reset any previous errors
| |
| |
//---
| |
if(start_date > 0 && start_date != D'1971.01.01 00:00')
| |
{
| |
if(!HistorySelect(start_date, TimeCurrent()))
| |
{
| |
Print("Error when selecting orders: ", _LastError);
| |
return 0.00; // Exit if unable to select the history
| |
}
| |
| |
const int total_deals = HistoryDealsTotal(); // Count total deals in the history
| |
| |
for(int i = 0; i < total_deals; i++)
| |
{
| |
const ulong deal_ticket = HistoryDealGetTicket(i); // Get the deal ticket
| |
| |
if(GetNetProfitOmitirDeal[HistoryDealGetInteger(deal_ticket, DEAL_TYPE)])
| |
continue;
| |
| |
//---
| |
const ulong deal_magic = HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
| |
if(!include_all_magic && deal_magic != specific_magic)
| |
continue;
| |
| |
//---
| |
const double deal_profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
| |
const double deal_commission = HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
| |
const double deal_swap = HistoryDealGetDouble(deal_ticket, DEAL_SWAP);
| |
total_net_profit += (deal_profit + deal_commission + deal_swap);
| |
}
| |
}
| |
| |
//---
| |
return total_net_profit;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| Function to close orders |
| |
//+------------------------------------------------------------------+
| |
// Converts an order type to its corresponding flag
| |
#define OrderTypeToFlag(type) OrdensToFlagArray[type]
| |
| |
| |
const int ALL_FLAGS_STOPS = FLAG_ORDER_TYPE_BUY_STOP | FLAG_ORDER_TYPE_SELL_STOP;
| |
const int ALL_FLAGS_LIMITS = FLAG_ORDER_TYPE_BUY_LIMIT | FLAG_ORDER_TYPE_SELL_LIMIT;
| |
const int ALL_FLAGS_ORDERS = (FLAG_ORDER_TYPE_BUY | FLAG_ORDER_TYPE_SELL | FLAG_ORDER_TYPE_BUY_LIMIT |
| |
FLAG_ORDER_TYPE_SELL_LIMIT | FLAG_ORDER_TYPE_BUY_STOP | FLAG_ORDER_TYPE_SELL_STOP |
| |
FLAG_ORDER_TYPE_BUY_STOP_LIMIT | FLAG_ORDER_TYPE_SELL_STOP_LIMIT | FLAG_ORDER_TYPE_CLOSE_BY);
| |
| |
| |
// Close all orders that match the flags in `flags`
| |
void CloseAllOrders(int flags, CTrade & obj_trade, ulong magic_number_ = NOT_MAGIC_NUMBER)
| |
{
| |
ResetLastError();
| |
for(int i = OrdersTotal() - 1; i >= 0; i--)
| |
{
| |
ulong ticket = OrderGetTicket(i);
| |
| |
if(OrderSelect(ticket))
| |
{
| |
ENUM_ORDER_TYPE type_order = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
| |
ulong magic = OrderGetInteger(ORDER_MAGIC);
| |
int bandera = OrderTypeToFlag(type_order);
| |
if((bandera & flags) != 0 && (magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER))
| |
{
| |
if(type_order == ORDER_TYPE_BUY || type_order == ORDER_TYPE_SELL)
| |
obj_trade.PositionClose(ticket);
| |
else
| |
obj_trade.OrderDelete(ticket);
| |
}
| |
}
| |
else
| |
{
| |
PrintFormat("Error selecting order %d, last error %d", ticket, GetLastError());
| |
}
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Function to obtain the positions opened by the EA or user |
| |
//+------------------------------------------------------------------+
| |
int PositionTypeToFlag(ENUM_POSITION_TYPE type)
| |
{
| |
if(type == POSITION_TYPE_BUY)
| |
return FLAG_POSITION_TYPE_BUY;
| |
else
| |
if(type == POSITION_TYPE_SELL)
| |
return FLAG_POSITION_TYPE_SELL;
| |
| |
return FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL;
| |
}
| |
| |
//---
| |
int Get_Positions(int flags = FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL, ulong magic_number_ = NOT_MAGIC_NUMBER)
| |
{
| |
int counter = 0;
| |
| |
for(int i = PositionsTotal() - 1; i >= 0; i--)
| |
{
| |
ulong position_ticket = PositionGetTicket(i);
| |
if(!PositionSelectByTicket(position_ticket))
| |
continue; // Si la selección falla, pasa a la siguiente posición
| |
ulong position_magic = PositionGetInteger(POSITION_MAGIC);
| |
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
| |
// Check if the position type matches the flags
| |
if((flags & PositionTypeToFlag(type)) != 0 &&
| |
(position_magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER))
| |
{
| |
counter++;
| |
}
| |
}
| |
| |
return counter;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
template<typename T>
| |
bool RemoveIndexFromAnArrayOfPositions(T & array[], const ulong ticket, int reserve)
| |
{
| |
const int size = ArraySize(array);
| |
int index = -1;
| |
// Search index and move elements in a single loop
| |
for(int i = 0; i < size; i++)
| |
{
| |
if(array[i].ticket == ticket)
| |
{
| |
index = i;
| |
}
| |
if(index != -1 && i < size - 1)
| |
{
| |
array[i] = array[i + 1]; // Move the elements
| |
}
| |
}
| |
| |
if(index == -1)
| |
return false;
| |
| |
// Reducir el tamaño del array
| |
ArrayResize(array, size - 1, reserve);
| |
return true;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void PrintArrayAsTable(double & array[], string fila_descripcion, string columna_prefijo = "Valor")
| |
{
| |
string header = fila_descripcion;
| |
int len = StringLen(header);
| |
string values = StringRepeat(" ", len + 2);
| |
| |
int max_len = StringLen(header);
| |
for(int i = 0; i < ArraySize(array); i++)
| |
{
| |
string col_name = columna_prefijo + " " + IntegerToString(i + 1);
| |
max_len = MathMax(max_len, StringLen(col_name));
| |
}
| |
| |
header = StringFormat("%-" + (string)(max_len + 2) + "s", header);
| |
| |
for(int i = 0; i < ArraySize(array); i++)
| |
{
| |
string col_name = columna_prefijo + " " + IntegerToString(i + 1);
| |
header += StringFormat("| %-10s ", col_name);
| |
values += StringFormat("| %-10.2f ", array[i]);
| |
}
| |
| |
Print(header);
| |
Print(values);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void SetDynamicUsingFixedParameters(
| |
double _balance_percentage_to_activate_the_risk_1, double _balance_percentage_to_activate_the_risk_2, double _balance_percentage_to_activate_the_risk_3, double _balance_percentage_to_activate_the_risk_4,
| |
double _percentage_to_be_modified_1, double _percentage_to_be_modified_2, double _percentage_to_be_modified_3, double _percentage_to_be_modified_4,
| |
string & percentages_to_activate, string & risks_to_be_applied)
| |
{
| |
percentages_to_activate = DoubleToString(_balance_percentage_to_activate_the_risk_1) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_2) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_3)
| |
+ "," + DoubleToString(_balance_percentage_to_activate_the_risk_4);
| |
risks_to_be_applied = DoubleToString(_percentage_to_be_modified_1) + "," + DoubleToString(_percentage_to_be_modified_2) + "," + DoubleToString(_percentage_to_be_modified_3)
| |
+ "," + DoubleToString(_percentage_to_be_modified_4);
| |
}
| |
| |
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| Clase CTicketsInfo |
| |
//+------------------------------------------------------------------+
| |
Position EMPTY_POSITION;
| |
| |
//---
| |
class CTicketsInfo : public CLoggerBase
| |
{
| |
private:
| |
//--- Data
| |
Position m_positions[];
| |
| |
//--- Hash
| |
uint16_t m_hash_table[];
| |
bool m_hash_filled[];
| |
uint16_t m_hash_size;
| |
uint16_t m_hash_count;
| |
| |
//--- Static
| |
static const float LOAD_FACTOR_LIMIT;
| |
static const uint16_t INITIAL_HASH_SIZE;
| |
| |
//--- Internal functions
| |
int16_t FindHash(const ulong ticket) const;
| |
bool InsertHash(ulong ticket, uint16_t pos_idx);
| |
bool RemoveHash(ulong ticket);
| |
void UpdateHashAfterSwap(ulong ticket, uint16_t new_pos_idx);
| |
void ResizeHashTable();
| |
bool IsPrime(uint16_t n);
| |
| |
public:
| |
CTicketsInfo(void);
| |
~CTicketsInfo(void);
| |
| |
//--- Data
| |
// Get
| |
bool GetByTicket(const ulong ticket, Position& pos, int16_t& out_idx) const;
| |
bool GetByTicket(const ulong ticket, Position& pos) const;
| |
inline Position GetByTicket(const ulong ticket) const;
| |
inline Position GetByIndex(const int16_t index) const { return m_positions[index]; }
| |
| |
// Delete
| |
bool DeleteByIndex(const int16_t pos_idx);
| |
bool DeleteByTicket(const ulong ticket);
| |
bool DeleteByTicket(const ulong ticket, Position &position);
| |
| |
// Add
| |
bool Add(Position &p);
| |
| |
// Update
| |
inline void UpdatePositionPartial(double net, double close_volume, int16_t pos_idx);
| |
inline void UpdateTp(double new_tp, ulong ticket);
| |
inline void UpdateSl(double new_sl, ulong ticket);
| |
| |
| |
//--- General Getters
| |
// Index
| |
inline bool IsValidIdx(int16_t index) const; // Is Valid index
| |
__forceinline int GetIndex(const ulong ticket) const { return FindHash(ticket);}
| |
| |
// Total
| |
__forceinline int16_t Total() const { return (int16_t)ArraySize(m_positions); }
| |
__forceinline uint16_t TotalU() const { return (uint16_t)m_positions.Size(); }
| |
| |
//--- Exist
| |
inline bool Exists(const ulong ticket) const;
| |
| |
//--- Free
| |
void Free();
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
const float CTicketsInfo::LOAD_FACTOR_LIMIT = 0.7f;
| |
const uint16_t CTicketsInfo::INITIAL_HASH_SIZE = 9973;
| |
#define CTicketsInfo_HashTicket(ticket) uint16_t(ticket % m_hash_size)
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
CTicketsInfo::CTicketsInfo(void) : m_hash_size(INITIAL_HASH_SIZE), m_hash_count(0)
| |
{
| |
ArrayResize(m_hash_table, m_hash_size);
| |
ArrayResize(m_hash_filled, m_hash_size);
| |
ArrayInitialize(m_hash_table, 65535);
| |
ArrayInitialize(m_hash_filled, false);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
CTicketsInfo::~CTicketsInfo(void)
| |
{
| |
ArrayFree(m_positions);
| |
ArrayFree(m_hash_table);
| |
ArrayFree(m_hash_filled);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
inline bool CTicketsInfo::IsValidIdx(int16_t index) const
| |
{
| |
if(index >= 0 && index < Total())
| |
return true;
| |
LogError(StringFormat("El índice %d es inválido", index), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
int16_t CTicketsInfo::FindHash(const ulong ticket) const
| |
{
| |
//---
| |
const uint16_t h = (uint16_t)(ticket % m_hash_size);
| |
const uint16_t data_size = TotalU();
| |
| |
//---
| |
for(uint16_t i = 0; i < m_hash_size; i++)
| |
{
| |
//---
| |
const uint16_t idx = (h + i) % m_hash_size;
| |
| |
//---
| |
if(!m_hash_filled[idx])
| |
{
| |
LogCaution(StringFormat("El slot del ticket %I64u esta vacio", ticket), FUNCION_ACTUAL);
| |
return -1;
| |
}
| |
| |
//---
| |
const uint16_t pos_idx = m_hash_table[idx];
| |
if(pos_idx != 65535 && pos_idx < data_size && m_positions[pos_idx].ticket == ticket)
| |
{
| |
LogInfo(StringFormat("Ticket %I64u encontrado en idx=%u, pos_idx=%u", ticket, idx, pos_idx), FUNCION_ACTUAL);
| |
return (int16_t)pos_idx;
| |
}
| |
| |
//---
| |
if(pos_idx >= data_size && pos_idx != 65535)
| |
{
| |
LogError(StringFormat("Indice inválido pos_idx=%u para ticket %I64u en idx=%u", pos_idx, ticket, idx), FUNCION_ACTUAL);
| |
return -1;
| |
}
| |
}
| |
| |
//---
| |
LogWarning(StringFormat("El ticket %I64u no se ha encontrado tras recorrer toda la tabla", ticket), FUNCION_ACTUAL);
| |
return -1;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::InsertHash(ulong ticket, uint16_t pos_idx)
| |
{
| |
//---
| |
if((float)(m_hash_count + 1) / m_hash_size > LOAD_FACTOR_LIMIT)
| |
ResizeHashTable();
| |
| |
//---
| |
const uint16_t h = CTicketsInfo_HashTicket(ticket);
| |
for(uint16_t i = 0; i < m_hash_size; i++)
| |
{
| |
const uint16_t idx = (h + i) % m_hash_size;
| |
if(!m_hash_filled[idx])
| |
{
| |
m_hash_table[idx] = pos_idx;
| |
m_hash_filled[idx] = true;
| |
m_hash_count++;
| |
LogInfo(StringFormat("Ticket %I64u insertado en idx=%u, pos_idx=%u", ticket, idx, pos_idx), FUNCION_ACTUAL);
| |
return true;
| |
}
| |
}
| |
| |
//---
| |
LogError(StringFormat("Tabla hash llena para ticket %I64u", ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::RemoveHash(ulong ticket)
| |
{
| |
//---
| |
const uint16_t h = CTicketsInfo_HashTicket(ticket);
| |
const uint16_t data_size = TotalU();
| |
| |
//---
| |
for(uint16_t i = 0; i < m_hash_size; i++)
| |
{
| |
//---
| |
const uint16_t idx = (h + i) % m_hash_size;
| |
if(!m_hash_filled[idx])
| |
{
| |
LogCaution(StringFormat("El ticket %I64u no existe (casilla vacía en idx=%u)", ticket, idx), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
const uint16_t pos_idx = m_hash_table[idx];
| |
if(pos_idx != 65535 && pos_idx < data_size && m_positions[pos_idx].ticket == ticket)
| |
{
| |
LogInfo(StringFormat("Se eliminó exitosamente el ticket %I64u en idx=%u, pos_idx=%u", ticket, idx, pos_idx), FUNCION_ACTUAL);
| |
m_hash_filled[idx] = false;
| |
m_hash_table[idx] = 65535;
| |
m_hash_count--;
| |
return true;
| |
}
| |
| |
//---
| |
if(pos_idx >= data_size && pos_idx != 65535)
| |
{
| |
LogError(StringFormat("Índice inválido pos_idx=%u para ticket %I64u en idx=%u", pos_idx, ticket, idx), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
}
| |
| |
//---
| |
LogWarning(StringFormat("El ticket %I64u no se ha encontrado tras recorrer toda la tabla", ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
void CTicketsInfo::UpdateHashAfterSwap(ulong ticket, uint16_t new_pos_idx)
| |
{
| |
//---
| |
const uint16_t h = CTicketsInfo_HashTicket(ticket);
| |
const uint16_t data_size = TotalU();
| |
| |
//---
| |
for(uint16_t i = 0; i < m_hash_size; i++)
| |
{
| |
//---
| |
const uint16_t idx = (h + i) % m_hash_size;
| |
if(!m_hash_filled[idx])
| |
return;
| |
| |
//---
| |
const uint16_t pos_idx = m_hash_table[idx];
| |
if(pos_idx < data_size && m_positions[new_pos_idx].ticket == ticket)
| |
{
| |
m_hash_table[idx] = new_pos_idx;
| |
return;
| |
}
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
void CTicketsInfo::ResizeHashTable()
| |
{
| |
//---
| |
LogInfo("Se está aumentando el tamaño de la tabla hash al doble", FUNCION_ACTUAL);
| |
| |
//---
| |
uint16_t new_size = m_hash_size * 2 + 1;
| |
while(!IsPrime(new_size))
| |
new_size += 2;
| |
| |
//---
| |
uint16_t old_hash_table[];
| |
bool old_hash_filled[];
| |
ArrayCopy(old_hash_table, m_hash_table);
| |
ArrayCopy(old_hash_filled, m_hash_filled);
| |
| |
//---
| |
m_hash_size = new_size;
| |
m_hash_count = 0;
| |
ArrayResize(m_hash_table, m_hash_size);
| |
ArrayResize(m_hash_filled, m_hash_size);
| |
ArrayInitialize(m_hash_table, 65535);
| |
ArrayInitialize(m_hash_filled, false);
| |
| |
//---
| |
for(uint16_t i = 0; i < ArraySize(old_hash_table); i++)
| |
{
| |
if(old_hash_filled[i] && old_hash_table[i] != 65535)
| |
{
| |
uint16_t pos_idx = old_hash_table[i];
| |
if(pos_idx < ArraySize(m_positions))
| |
InsertHash(m_positions[pos_idx].ticket, pos_idx);
| |
}
| |
}
| |
//---
| |
ArrayFree(old_hash_table);
| |
ArrayFree(old_hash_filled);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::IsPrime(uint16_t n)
| |
{
| |
if(n < 2)
| |
return false;
| |
for(uint16_t i = 2; i * i <= n; i++)
| |
if(n % i == 0)
| |
return false;
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::GetByTicket(const ulong ticket, Position &pos, int16_t &out_idx) const
| |
{
| |
out_idx = FindHash(ticket);
| |
if(out_idx >= 0)
| |
{
| |
pos = m_positions[out_idx];
| |
return true;
| |
}
| |
else
| |
return false;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::GetByTicket(const ulong ticket, Position &pos) const
| |
{
| |
const int16_t pos_idx = FindHash(ticket);
| |
if(pos_idx >= 0)
| |
{
| |
pos = m_positions[pos_idx];
| |
return true;
| |
}
| |
else
| |
return false;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
inline Position CTicketsInfo::GetByTicket(const ulong ticket) const
| |
{
| |
const int16_t pos_idx = FindHash(ticket);
| |
return pos_idx >= 0 ? m_positions[pos_idx] : EMPTY_POSITION;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
inline void CTicketsInfo::UpdateSl(double new_sl, ulong ticket)
| |
{
| |
const int16_t pos_idx = FindHash(ticket);
| |
if(pos_idx >= 0)
| |
m_positions[pos_idx].sl = new_sl;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
inline void CTicketsInfo::UpdateTp(double new_tp, ulong ticket)
| |
{
| |
const int16_t pos_idx = FindHash(ticket);
| |
if(pos_idx >= 0)
| |
m_positions[pos_idx].tp = new_tp;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
inline void CTicketsInfo::UpdatePositionPartial(double net, double close_volume, int16_t pos_idx)
| |
{
| |
if(pos_idx >= 0)
| |
{
| |
m_positions[pos_idx].volume -= close_volume;
| |
m_positions[pos_idx].profit += net;
| |
LogInfo(StringFormat("Volumen cerrado = %.4f (new= %.4f) y profit obtenido = %.2f, al cierra parcial de la posicion = %I64u",
| |
close_volume,
| |
m_positions[pos_idx].volume,
| |
net,
| |
m_positions[pos_idx].ticket), FUNCION_ACTUAL);
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::Add(Position &p)
| |
{
| |
//---
| |
if(FindHash(p.ticket) >= 0)
| |
{
| |
LogCaution(StringFormat("La posición %I64u ya existe", p.ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
const uint16_t new_pos_idx = (uint16_t)ArraySize(m_positions);
| |
ArrayResize(m_positions, new_pos_idx + 1);
| |
m_positions[new_pos_idx] = p;
| |
| |
//---
| |
if(!InsertHash(p.ticket, new_pos_idx))
| |
{
| |
ArrayResize(m_positions, new_pos_idx);
| |
LogError(StringFormat("No se pudo insertar el ticket %I64u en la tabla hash", p.ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
// LogInfo(StringFormat("Añadiendo el ticket %I64u al array de posiciones >> tipo: %s >> profit: %+.2f", p.ticket, EnumToString(p.type), p.profit), FUNCION_ACTUAL);
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::DeleteByIndex(const int16_t pos_idx)
| |
{
| |
//---
| |
if(pos_idx < 0)
| |
{
| |
LogError(StringFormat("Indice invalido %d", pos_idx), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
const ulong ticket = m_positions[pos_idx].ticket;
| |
if(!RemoveHash(ticket))
| |
{
| |
LogError(StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
const int16_t last_idx = Total() - 1;
| |
if(pos_idx != last_idx)
| |
{
| |
m_positions[pos_idx] = m_positions[last_idx];
| |
UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx);
| |
}
| |
| |
//---
| |
ArrayResize(m_positions, last_idx);
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::DeleteByTicket(const ulong ticket)
| |
{
| |
//---
| |
const int16_t pos_idx = FindHash(ticket);
| |
if(pos_idx < 0)
| |
{
| |
LogError(StringFormat("No se pudo eliminar el ticket %I64u, ya que no existe", ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
if(!RemoveHash(ticket))
| |
{
| |
LogError(StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
const int16_t last_idx = Total() - 1;
| |
if(pos_idx != last_idx)
| |
{
| |
m_positions[pos_idx] = m_positions[last_idx];
| |
UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx);
| |
}
| |
| |
//---
| |
ArrayResize(m_positions, last_idx);
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsInfo::DeleteByTicket(const ulong ticket, Position &position)
| |
{
| |
//---
| |
const int16_t pos_idx = FindHash(ticket);
| |
if(pos_idx < 0)
| |
{
| |
LogError(StringFormat("No se pudo eliminar el ticket %I64u, ya que no existe", ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
position = m_positions[pos_idx];
| |
| |
//---
| |
if(!RemoveHash(ticket))
| |
{
| |
LogError(StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//---
| |
const int16_t last_idx = Total() - 1;
| |
if(pos_idx != last_idx)
| |
{
| |
m_positions[pos_idx] = m_positions[last_idx];
| |
UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx);
| |
}
| |
| |
//---
| |
ArrayResize(m_positions, last_idx);
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
void CTicketsInfo::Free(void)
| |
{
| |
ArrayFree(m_positions);
| |
m_hash_count = 0;
| |
ArrayInitialize(m_hash_table, 65535);
| |
ArrayInitialize(m_hash_filled, false);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
inline bool CTicketsInfo::Exists(const ulong ticket) const
| |
{
| |
return FindHash(ticket) >= 0;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class CAccountGestor : public CAllClassEventsBasic
| |
{
| |
public:
| |
CAccountGestor(const bool& is_g) : CAllClassEventsBasic(is_g) {}
| |
~CAccountGestor() {}
| |
| |
//--- Positions
| |
virtual void OnOpenClosePosition(const ROnOpenClosePosition &pos) {} //Function that will be executed each time a position is opened or closed
| |
virtual void OnPositionModify(const Position& pos, const uint8_t change_flags) { }
| |
| |
//--- Orders
| |
virtual void OnOrderAdd(const ROrder& order) { }
| |
virtual void OnOrderUpdate(const ROrder& order) { }
| |
virtual void OnOrderDelete(const ROrder& order) { }
| |
| |
//-- Function that is executed only once, where only the account profit fields are filled, such as account_gross_profit
| |
//daily, weekly, etc.
| |
virtual void OnNewProfit(const ROnOpenClosePosition &profit, const datetime& curr_time) { }
| |
| |
//--- Function that is executed each time TesterDeposit or TesterWithdrawal is called... or capital is added to the account
| |
virtual void OnWithdrawalDeposit(const double& value) { } //If the value is positive it means a deposit, otherwise a withdrawal
| |
| |
//--- Function that is executed only once, only if there are previously open trades, only the position structure
| |
virtual void OnInitNewPos(const ROnOpenClosePosition &position) { }
| |
| |
//---
| |
virtual void OnLossProfit(const double& p) {}
| |
};
| |
| |
| |
//+------------------------------------------------------------------+
| |
#endif // MQLARTICLES_RM_RFUNCTIONS_MQH
|