| //+------------------------------------------------------------------+
|
| //| Main.mqh |
|
| //| Copyright 2025, Niquel Mendoza. |
|
| //| https://www.mql5.com/es/users/nique_372/news |
|
| //+------------------------------------------------------------------+
|
| #property copyright "Copyright 2025, Niquel Mendoza."
|
| #property link "https://www.mql5.com/es/users/nique_372/news"
|
| #property strict
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| #include "Confirmacion.mqh"
|
|
|
| // Source: https://www.youtube.com/watch?v=1gDEl5FxsGA
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| class CEstrategia : public TSN::CStrategyBase
|
| {
|
| private:
|
| //--- General
|
| datetime m_next_max_espera;
|
| ENUM_ESTRATEGIA_SENSEI_STATE m_state;
|
| bool m_operate;
|
|
|
|
|
| //--- Htf
|
| ENUM_TIMEFRAMES m_timeframe_htf;
|
| int8_t m_index_contoler_htf;
|
| IConfirmacion* m_confirmacion_htf;
|
|
|
| //--- MidTimeframe
|
| ENUM_TIMEFRAMES m_timeframe_mid_tf;
|
|
|
| // Fibbo
|
| TSN::CFibbo m_fibbo;
|
| double m_price_fibbo;
|
| int m_level_fibbo;
|
|
|
| // Espera
|
| int m_max_espera_to_descuento_in_bars; // Velas en timeframe curr
|
|
|
| //--- Timefgrame de entrada
|
| IConfirmacion* m_confirmation_entrada;
|
|
|
|
|
| //---
|
| void Clean();
|
| void OpenOrder(ENUM_POSITION_TYPE type);
|
|
|
| //---
|
| void ChangeTimeframeConcepts(ENUM_TIMEFRAMES tf);
|
|
|
|
|
|
|
| public:
|
| CEstrategia(ulong magic_number_, string symbol_, bool draw_fibbo, ENUM_TIMEFRAMES timeframe_ = PERIOD_CURRENT, long chart_id_ = 0,
|
| int subwindow_ = 0, ulong max_deviation_ = NO_MAX_DEVIATION_DEFINED);
|
| ~CEstrategia(void);
|
|
|
| //---
|
| bool SetFibbo(color clr_fibbo, string fibbo_levels, string fibbo_colors, double fibbo_level_principal = 0.50);
|
| void SetConfirmations(IConfirmacion* confirmation_htf, IConfirmacion* confirmaction_entrada);
|
| void Init(ENUM_TIMEFRAMES tf_mif, ENUM_TIMEFRAMES tf_high, int max_epsera_para_descuento, ENUM_ESTRATEGIA_SENSEI_DIRECCION_IND tpye_dir);
|
|
|
| //---
|
| void OnNewBar(const datetime curr_time) override final;
|
| void OnNewWeek(const datetime curr_time) override final;
|
| void OnTick(const datetime curr_time) override final { }
|
|
|
| //--- Error
|
| static void FormatTrace(const TSN::TSNErrorFrame &trace[], int &trace_idx, string &out);
|
| };
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| CEstrategia::CEstrategia(ulong magic_number_, string symbol_, bool draw_fibbo, ENUM_TIMEFRAMES timeframe_ = PERIOD_CURRENT, long chart_id_ = 0,
|
| int subwindow_ = 0, ulong max_deviation_ = NO_MAX_DEVIATION_DEFINED)
|
| : CStrategyBase(magic_number_, symbol_, timeframe_, chart_id_, subwindow_, max_deviation_, "Estrategia sensei"), m_fibbo(draw_fibbo),
|
| m_confirmacion_htf(NULL), m_confirmation_entrada(NULL), m_index_contoler_htf(NULL), m_operate(true),
|
| m_state(ESTRATEGIA_SENSEI_STATE_BUSCAR_DIR), m_price_fibbo(0.00), m_level_fibbo(-1), m_next_max_espera(0) // no se llama Clean() aqui: los conceptos globales aun no estan creados
|
| {
|
| CBasicEvents::RegisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_WEEK);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| CEstrategia::~CEstrategia(void)
|
| {
|
| if(CBasicEvents::IsActive())
|
| CBasicEvents::UnregisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_WEEK);
|
|
|
| if(::CheckPointer(m_confirmation_entrada) == POINTER_DYNAMIC)
|
| delete m_confirmation_entrada;
|
| if(::CheckPointer(m_confirmacion_htf) == POINTER_DYNAMIC)
|
| delete m_confirmacion_htf;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| bool CEstrategia::SetFibbo(color clr_fibbo, string fibbo_levels, string fibbo_colors, double fibbo_level_principal = 0.500000)
|
| {
|
| // estos ya ponen su error en el trace
|
| if(!m_fibbo.Create(m_chart_id, "FibboSensei", m_subwindow, clr_fibbo, STYLE_SOLID, 1, "Fibbo", true))
|
| return false;
|
| if(!m_fibbo.InitFibbo(fibbo_levels, fibbo_colors, STYLE_SOLID, 1, ','))
|
| return false;
|
|
|
| // este no pone error (usa un hashmap de bajo nivel), hace falta que lo hagamos nosotros
|
| if(!m_fibbo.GetLevelIndex(fibbo_level_principal, m_level_fibbo))
|
| return TSN::CErrorTrace::FailInit<CEstrategia>(ESTRATEGIA_ERR_FAILED_CALC_LEVEL_IDX, TSN::CBitsReinterprete::ToBasic<ulong, double>(fibbo_level_principal));
|
| return true;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| static void CEstrategia::FormatTrace(const TSN::TSNErrorFrame &trace[], int &trace_idx, string &out)
|
| {
|
| TSNSTD_ERRMAN_PUSHINI(out, trace_idx)
|
| switch(trace[trace_idx].code)
|
| {
|
| case ESTRATEGIA_ERR_NOT:
|
| TSNSTD_ERRMAN_PUSHOUT(out, "Not error", trace_idx)
|
| break;
|
| case ESTRATEGIA_ERR_FAILED_CALC_LEVEL_IDX:
|
| TSNSTD_ERRMAN_PUSHOUT(out, StringFormat("The chosen fibbo level [%.4f] is not one of the fibbo levels (InpStrategyFibboLevels)",
|
| TSN::CBitsReinterprete::ToBasic<double, ulong>(trace[trace_idx].info)), trace_idx)
|
| break;
|
| default:
|
| TSNSTD_ERRMAN_PUSHOUT(out, "Unknow error", trace_idx)
|
| break;
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CEstrategia::OnNewWeek(const datetime curr_time)
|
| {
|
| // Limpieza semanal
|
| g_fvg.DeleteAll();
|
| g_ob.DeleteAll();
|
| g_zz.DeleteAll();
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CEstrategia::SetConfirmations(IConfirmacion *confirmation_htf, IConfirmacion *confirmaction_entrada)
|
| {
|
| m_confirmacion_htf = confirmation_htf;
|
| m_confirmation_entrada = confirmaction_entrada;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CEstrategia::Init(ENUM_TIMEFRAMES tf_mif, ENUM_TIMEFRAMES tf_high, int max_epsera_para_descuento, ENUM_ESTRATEGIA_SENSEI_DIRECCION_IND tpye_dir)
|
| {
|
| //---
|
| m_timeframe_htf = tf_high;
|
| m_timeframe_mid_tf = tf_mif;
|
| m_max_espera_to_descuento_in_bars = max_epsera_para_descuento;
|
|
|
| //---
|
| // Aqui algo.. el HTF siempre debera de ser mayor al timeframe de la estrateiga el current
|
| if(tpye_dir == SENSEI_DIR_MANIPULACION_SESIONES)
|
| g_new_bar_manager.GetPosExecute(m_timeframe, m_index_contoler_htf); // Usamos directament ela funcion para registar el TF
|
| else
|
| g_new_bar_manager.GetPosExecute(tf_high, m_index_contoler_htf);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CEstrategia::Clean(void)
|
| {
|
| m_state = ESTRATEGIA_SENSEI_STATE_BUSCAR_DIR;
|
| m_price_fibbo = 0.00;
|
| m_next_max_espera = 0;
|
| g_ob.SetParo();
|
| g_fvg.SetParo();
|
| // g_zz.SetParo(); // zz se ejecuta en todo momento
|
| }
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CEstrategia::ChangeTimeframeConcepts(ENUM_TIMEFRAMES tf)
|
| {
|
| g_fvg.ChangeTimeframe(tf);
|
| g_ob.ChangeTimeframe(tf);
|
| g_fvg.SetCalculatePrevInNextBar();
|
| g_ob.SetCalculatePrevInNextBar();
|
| }
|
|
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CEstrategia::OnNewBar(const datetime curr_time)
|
| {
|
| if(risk.GetPositionsTotal() > 0)
|
| return;
|
|
|
| //---
|
| if(g_session_operativa.EnSesion())
|
| {
|
| //---
|
| if(g_session_operativa.IsInitSession())
|
| {
|
| LogInfo("Inicio de sesion", FUNCION_ACTUAL);
|
| ChangeTimeframeConcepts(m_timeframe_htf);
|
| m_operate = true;
|
| }
|
|
|
| //---
|
| if(!m_operate)
|
| return; // No ejeuctamos nada
|
|
|
| //---
|
| g_fvg.OnNewBar();
|
| g_ob.OnNewBar();
|
|
|
| //---
|
| do
|
| {
|
| switch(m_state)
|
| {
|
| case ESTRATEGIA_SENSEI_STATE_BUSCAR_DIR:
|
| {
|
| // Importnate descomentamos debido a que;
|
| // Dado queestamos segurmos que no habra "colgamiento" para empeozae
|
| // OnNewBar se jecuta cada M3 current mismo perido que m_confirmation_entrada asi que por mas que este listo para entener
|
| // Esperara una vela para limpiar data previa por lo que no quedara "data colgada" ademas otra razon es que solo llaamsmo a una vez
|
| // SizeAlcista y SizeBajistas y par ala siutiente llamada si o si es un diferte "timestap" no el mismo..
|
| // Ahora cuando SI LLAMAR este caos: digmaos que OnNEWbAR DE AQUI SE ejeucta m1 entonces luego de esto busco doble mtiigaicon de fvg o cosas que esten
|
| // En la misma insurccion o conseuticaaos en este caso si el timeframel dle ob e smaytor entonce quedaria "colgado" pro lqo ei la siugietne codnicon
|
| // Se elualria como TRUE cuando no lo es ahi si vale la pena controlar la arptuira de las velas
|
| // if(true /*CNewBarManager_IsNewBar(g_new_bar_manager, m_index_contoler_htf)*/) // OB-LOWHIGH-FVG (NO SABEMOS NO ESTAMOS SEGUROs)
|
| // {
|
| // }
|
|
|
| //---
|
| if(m_confirmacion_htf.ConfirmacionCompra())
|
| {
|
| LogInfo("Direccion detectada: compra", FUNCION_ACTUAL);
|
| m_next_max_espera = curr_time + (m_period_in_seconds * m_max_espera_to_descuento_in_bars);
|
|
|
| //---
|
| // Fibbo de compra
|
| ::SymbolInfoTick(m_symbol, m_tick);
|
| const Swings swing = g_zz.GetSwingHighMasCeracanoPorPrecioYTime(m_tick.bid, SWING_MAYOR_A_TARGET_PRICE);
|
| const double minimo = ::iLow(m_symbol, m_timeframe_mid_tf, ::iLowest(m_symbol, m_timeframe_mid_tf, MODE_LOW, ::iBarShift(m_symbol, m_timeframe_mid_tf, swing.time), 0));
|
| //Print(minimo);
|
| m_fibbo.Move(swing.time, m_tick.time, swing.price, minimo);
|
| m_price_fibbo = m_fibbo.GetLevelPrice(m_level_fibbo);
|
|
|
| //---
|
| ChangeTimeframeConcepts(m_timeframe); // Timefrmae de entrada
|
| m_state = ESTRATEGIA_SENSEI_STATE_BUSCAR_DESCUENTO_COMPRA;
|
| continue; // "al toque" sale a laa siuginte (Esto para uqe sea "fiel" a como estaba antes)
|
| }
|
| else
|
| if(m_confirmacion_htf.ConfirmacionVenta())
|
| {
|
| m_next_max_espera = curr_time + (m_period_in_seconds * m_max_espera_to_descuento_in_bars);
|
|
|
| //---
|
| // Fibob de venta
|
| ::SymbolInfoTick(m_symbol, m_tick);
|
| const Swings swing = g_zz.GetSwingLowMasCeracanoPorPrecioYTime(m_tick.bid, SWING_MENOR_A_TARGET_PRICE);
|
| const double maximo = ::iHigh(m_symbol, m_timeframe_mid_tf, ::iHighest(m_symbol, m_timeframe_mid_tf, MODE_HIGH, ::iBarShift(m_symbol, m_timeframe_mid_tf, swing.time), 0));
|
| m_fibbo.Move(swing.time, m_tick.time, maximo, swing.price);
|
| m_price_fibbo = m_fibbo.GetLevelPrice(m_level_fibbo);
|
|
|
| //---
|
| ChangeTimeframeConcepts(m_timeframe); // Timefrmae de entrada
|
| LogInfo("Direccion detectada: venta", FUNCION_ACTUAL);
|
| m_state = ESTRATEGIA_SENSEI_STATE_BUSCAR_DESCUENTO_VENTA;
|
| continue; // "al toque" sale a laa siuginte (Esto para uqe sea "fiel" a como estaba antes)
|
| }
|
| break;
|
| }
|
|
|
| //--- Ya hay direccion de compra: esperamos que el precio este en descuento (concepto MID-TF)
|
| case ESTRATEGIA_SENSEI_STATE_BUSCAR_DESCUENTO_COMPRA:
|
| {
|
| if(g_zz.IsNewBarConcept()) // El desucuento siempre sera el concepto MID-TF
|
| {
|
| // Actulizacion del fibbo y vemos si estamos en descuento
|
| m_fibbo.UpdateTime2(curr_time);
|
| ::SymbolInfoTick(m_symbol, m_tick);
|
|
|
| if(m_price_fibbo > m_tick.bid) // Bid en desucneto
|
| {
|
| m_state = ESTRATEGIA_SENSEI_STATE_BUSCAR_ENTRADA_COMPRA; // la entrada se busca en la siguiente llamada
|
| LogInfo("Precio en descuento", FUNCION_ACTUAL);
|
| }
|
| }
|
|
|
| // Maxima espera (se evalua aunque en esta misma llamada ya se haya llegado al descuento, igual que antes)
|
| if(curr_time > m_next_max_espera)
|
| {
|
| m_state = ESTRATEGIA_SENSEI_STATE_BUSCAR_DIR; // Reinicio
|
| LogInfo("Max espera para fibbo superada", FUNCION_ACTUAL);
|
| // Aqui podemos hacer dos cosas o cambiar de timeframe
|
| m_operate = false;
|
| // O calendar el trade m_operate = false
|
| }
|
| break;
|
| }
|
|
|
| //--- Ya hay direccion de venta: esperamos que el precio este en premium
|
| case ESTRATEGIA_SENSEI_STATE_BUSCAR_DESCUENTO_VENTA:
|
| {
|
| if(g_zz.IsNewBarConcept())
|
| {
|
| m_fibbo.UpdateTime2(curr_time);
|
| ::SymbolInfoTick(m_symbol, m_tick);
|
|
|
| if(m_tick.bid > m_price_fibbo) // Bid en premiun
|
| {
|
| m_state = ESTRATEGIA_SENSEI_STATE_BUSCAR_ENTRADA_VENTA;
|
| LogInfo("Precio en premiun", FUNCION_ACTUAL);
|
| }
|
| }
|
|
|
| if(curr_time > m_next_max_espera)
|
| {
|
| m_state = ESTRATEGIA_SENSEI_STATE_BUSCAR_DIR; // Reinicio
|
| LogInfo("Max espera para fibbo superada", FUNCION_ACTUAL);
|
| m_operate = false;
|
| }
|
| break;
|
| }
|
|
|
| //--- Entrada
|
| case ESTRATEGIA_SENSEI_STATE_BUSCAR_ENTRADA_COMPRA:
|
| {
|
| if(m_confirmation_entrada.ConfirmacionCompra())
|
| OpenOrder(POSITION_TYPE_BUY);
|
| break;
|
| }
|
| case ESTRATEGIA_SENSEI_STATE_BUSCAR_ENTRADA_VENTA:
|
| {
|
| if(m_confirmation_entrada.ConfirmacionVenta())
|
| OpenOrder(POSITION_TYPE_SELL);
|
| break;
|
| }
|
| default:
|
| break;
|
| }
|
|
|
| //--- sale siempre
|
| break;
|
| }
|
| while(true);
|
| }
|
| else
|
| if(g_session_operativa.IsEndSession()) // Session finalizada
|
| {
|
| Clean();
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CEstrategia::OpenOrder(ENUM_POSITION_TYPE type)
|
| {
|
| ::SymbolInfoTick(m_symbol, m_tick);
|
|
|
| if(type == POSITION_TYPE_BUY)
|
| {
|
| double entry = m_tick.ask;
|
| double sl = GetSL(entry, type, m_tick.time);
|
| double tp = GetTP(entry, type, m_tick.time);
|
|
|
| risk.SetStopLoss(m_symbol, entry - sl);
|
| double l = m_LOT_SIZE > 0.00 ? m_LOT_SIZE : risk.GetLote(ORDER_TYPE_BUY, entry, m_max_deviation, 0);
|
| m_trade.Buy(l, m_symbol, entry, sl, tp, "Ea buy");
|
| }
|
| else
|
| if(type == POSITION_TYPE_SELL)
|
| {
|
| double entry = m_tick.bid;
|
| double sl = GetSL(entry, type, m_tick.time);
|
| double tp = GetTP(entry, type, m_tick.time);
|
|
|
| risk.SetStopLoss(m_symbol, sl - entry);
|
| double l = m_LOT_SIZE > 0.00 ? m_LOT_SIZE : risk.GetLote(ORDER_TYPE_SELL, entry, m_max_deviation, 0);
|
| m_trade.Sell(l, m_symbol, entry, sl, tp, "Ea sell");
|
| }
|
|
|
| m_operate = false;
|
| Clean();
|
| }
|
| //+------------------------------------------------------------------+
|
|
|
| //+------------------------------------------------------------------+
|