Proyecto del asesor - página 4

 
Vitaly Muzichenko:

Yo también, pero hace tiempo que llegué a la conclusión de que el código debe ser compacto en lugares donde nunca se mira, nunca se corrige y nunca se corregirá.

La dispersión del código de usuario por todos los inlays es un dolor de cabeza adicional, cuando necesitas arrastrar y soltar un archivo en otro terminal, o compartirlo, tendrás que arrastrar y soltar varios archivos. Por supuesto, puedes transferir los includniks a todos los terminales, pero si cambias o añades algo en un terminal, entonces todos ellos deben ser reemplazados por uno nuevo.

Los Asesores Expertos y los indicadores son tan pequeños que no tiene sentido alejarlos del cuerpo del programa. Para ser más correctos, no son pequeños, son de un solo archivo, no es como un sitio con 10 000 páginas donde no se puede prescindir de la clase y los inludes. Además, ahora hay estructuras, y son suficientes para escribir un código compacto y 100% viable.

Allá vamos.... ¿Conoces los enlaces simbólicos a las carpetas?http://skesov.ru/sozdanie-simvolnoy-ssyilki-dlya-papki/

Tengo todas mis bibliotecas en una carpeta, y en un montón de terminales, hay docenas de ellas, en las carpetas mql*\includes hay enlaces simbólicos a esta carpeta real. No hay que arrastrar nada a ninguna parte.

Además, uso activamente el almacenamiento, si guardo allí todo lo importante, puedo descargarlo en otro terminal en 5 segundos. Pero para las librerías son más convenientes los enlaces simbólicos, siempre la sincronización completa.

Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
  • 2013.07.24
  • skesov.ru
Доброго времени суток! Сегодня рассмотрим интересную тему под названием «Символьные ссылки». Вариантов использования данного инструмента не так уж много. К примеру, если вы используете часть оперативной памяти как RAM-диск, можно перенести какую-либо игру или её часть (скажем папки с графикой) и создать символьную ссылку. Это значительно...
 
Alexey Navoykov:
Recomiendo utilizar enlaces simbólicos o enlaces de unión para la carpeta MQL. Todos los terminales buscarán en la misma carpeta.

¿Y compartirlo con alguien más?

 
Vitaly Muzichenko:

¿Compartir con alguien?

Bueno, tienes que decidir qué es más importante para ti, el control o la conducción). ¿La facilidad de compartir con alguien es más importante para ti que la comodidad de la codificación? Si, por ejemplo, encuentras un error en alguna función que es utilizada por muchos Asesores Expertos, entonces tendrás que entrar en el código de cada uno de ellos y reescribir esta función - ¿no te molesta como programador?

 
Gracias a todos por discutir mi pregunta.
Decidí mirar hacia OOP en mqlx para empezar, mantener sus funciones (repetibles) en un archivo(s) separado(s). Y no tengas pereza de comentar.

¡Y otro + para el enlace simbólico en Windows! Lo he usado en linux pero lo he olvidado en Windows. Tendré que probarlo...
 
Alexey Navoykov:

Tienes que decidir qué es más importante para ti, el control o la conducción). ¿La sencillez del proceso de compartir con alguien es más importante para ti que la comodidad de la codificación? Si, por ejemplo, encuentras un error en una función, que es utilizada por muchos Asesores Expertos, entonces tendrás que entrar en el código de cada uno de ellos y reescribir esta función - ¿no te molesta como programador?

Sólo he tenido un caso así, hace un mes, pero he tenido que añadir la comprobación de la apertura del mercado allí, están todas las comprobaciones menos ésta, y ha salido por primera vez desde que lo uso.

Si hay que añadir algo, lo añado al programa actual, y después utilizo el archivo como plantilla para el siguiente programa. El resultado es que en pocos años la plantilla lo tiene todo, bueno, o casi todo, para que un bot de cualquier complejidad pueda ser escrito en media hora.

Todo el código ejecutable cabe en una pantalla, aunque el archivo contiene algo más de 4.000 líneas, pero yo miro ahí muy pocas veces, aunque sólo sea lo que necesito añadir. De las funciones en los bucles se negó, utiliza sólo dos, uno en el abierto recoge la información, el segundo en la historia, y todo esto en la estructura en la parte inferior del código. Todo es muy sencillo y cercano. El código principal está comentado. El proyecto se extiende con mucha facilidad y rapidez, sin ninguna pérdida.

 
Alexey Volchanskiy:

Se ve bien, ¿podemos ver también TRACE_*** y ASSERT?

Bueno... Para el autor de una clase magistral sobre seducción de mujeres, al que envidio con una envidia negra, de nada.

La versión de depuración se activa automáticamente en mi código, si se define la correspondiente macro del sistema. Sin embargo, si no está habilitado, también puedes habilitar las aserciones y el rastreo por definiciones:

#define _FORCETRACE 1
#define _FORCEASSERT 1

En este caso -independientemente de la configuración del sistema- se generan trazas de depuración y aserciones de depuración.

Tengo una directiva para conectar estas macros:

#include <MyLib\DebugOrRelease\DebugSupport.mqh>

Esta directiva conecta todos los archivos y definiciones necesarios. Los tengo todos en una carpeta separada DebugOrRelease. Adjuntándolos aquí. (El código fue escrito hace mucho tiempo, en su mayoría "apresuradamente", por lo que no es tan bonito como las interfaces y la clase de historia). Las afirmaciones y trazas en sí mismas para la versión de depuración están en los archivos AssertD y TraceD, las funciones reales son PerformAssert() y PerformTrace().

Además estos archivos y macros utilizan el archivo de registro global (si la salida al archivo de registro está configurada), ya lo he publicado una vez, pero, una vez más. El archivo de registro está en mi carpeta "Common".

Archivos adjuntos:
 
Andrey Kisselyov:

Buen trabajo, me gusta, pero no me gusta la POO e intento prescindir de ella. No me gustan los procesadores con división de flujos (por ejemplo, 4 núcleos y 8 hilos). Hay que tener claro que la división y cualquier virtualización es una pérdida de rendimiento y de tiempo de máquina para su implementación, ya sea la división de flujos en el kernel o la virtualización de funciones en el código.

La brevedad es la hermana del talento, creo que suena mejor.

Hace tiempo que aprendí que el mantenimiento y la reutilización del código son mucho más importantes que la reducción del rendimiento.

OOP - me ayuda mucho cuando vuelvo al código después de algún tiempo para modificarlo. Por no hablar de la reutilización.

Pero, estoy de acuerdo, no es ni mucho menos necesario utilizar OOP.

Digamos que tengo la clase CDataProvider:pulic CDataProviderI - proveedor de datos, que proporciona a los expertos con series de tiempo, indicadores, datos de la terminal y el medio ambiente. Un Asesor Experto puede contener muchos TPs - cada uno de ellos recibiría punteros a series de tiempo e indicadores del proveedor de datos (cada TP no necesita crear series de tiempo - el proveedor de datos proporcionaría punteros a las series de tiempo necesarias si ya existen, y sólo crearía series de tiempo, que aún no han sido creadas).

Cuando necesite obtener un indicador del proveedor de datos, rellene la estructura de descripción del indicador y, a continuación, solicite un indicador al proveedor que apunte a esta estructura.

En consecuencia, cada indicador dentro del proveedor de datos debe ser capaz de identificar su estructura (el proveedor de datos "conoce" sólo la clase base abstracta de la estructura) y de acuerdo con ella crear el objeto indicador listo, que será creado por el proveedor de datos.

Pero no es razonable hacer todo esto sólo con el propósito de comprobar una nueva idea de un indicador. Como resultado, para estos nuevos indicadores todo es "casero", sin ninguna OOP. Sin embargo, si veo que un indicador es útil para los Asesores Expertos, está escrito "correctamente" - con soporte OOP completo y creación dentro de un proveedor de datos.

P.D.

Por cierto, en el caso de los indicadores y el proveedor de datos vemos la ventaja de la virtualización de la herencia. Tenemos la interfaz básica de los parámetros del indicador CIndicatorParametersI, el sucesor de esta interfaz son los parámetros reales del indicador necesario. Al solicitar el indicador, declaramos estos parámetros y pasamos al proveedor de datos un puntero a la interfaz abstracta. Así, el propio proveedor de datos ni siquiera sabe qué indicador se solicita: se define en una función, en la que se crea el indicador según el nuevo tipo. Y sólo este indicador creado sabe qué parámetros se pasaron, recupera los parámetros requeridos del objeto pasado.

El truco es que casi en todas partes dentro del proveedor de datos hay una clase básica simple de parámetros (o indicadores) - sólo las funciones más simples y comunes de las interfaces básicas están disponibles para el proveedor de datos. Esto simplifica la modificación del código (cuando es necesario), y no crea la tentación de "manipular" el código de los indicadores del proveedor de datos. Si se quiere cambiar un indicador, se hace sólo dentro del indicador, el proveedor de datos es sólo un almacén de indicadores, lo máximo que puede hacer es crear un nuevo indicador.

 
George Merts:

Por cierto, me pone muy nervioso cuando el anidamiento es de más de dos niveles. Intento no escribirlo nunca así, repartiendo el código en funciones.

E incluso cuando hay dos niveles de anidación - siempre escribo un comentario después de cada paréntesis de cierre, que bloque cierra (por ejemplo, la cabecera del bucle duplicado).

En cuanto al estilo, aquí está mi código para seleccionaruna posición de la historia para MT5 (por magik especificado, símbolo, con rango de fechas especificado):

La propia clase historia es descendiente de la interfaz abstracta CTradeHistoryI:

Seleccionando el historial requerido - puede recalcular sus componentes (posiciones para MT5 u órdenes para MT4), y obtener una interfaz para cualquier componente como una interfaz abstracta:

Para MT4 existen las correspondientes clases de historial que también heredan de esas interfaces - de esta manera se proporciona la naturaleza multiplataforma al mismo tiempo - un EA no necesita averiguar dónde funciona, todo el trabajo con el historial se realiza a través de interfaces abstractas.


No es una gran crítica:

class CTradePosComponentI: public CMyObject
{
...
}

¿Por qué reinventar la rueda en forma de CMyObject, cuando existe un CObject estándar que todo el mundo entiende?

class class CTradeHistoryI: public CMyObject
{
// Расширенный интерфейс
   virtual void Sort(ESortTPCMode stmMode = STM_BY_OPEN_TIME_A) = 0;
}

La funcionalidad de CObject y CArrayObj está claramente copiada aquí. ¿Por qué? La clasificación rápida está integrada en los contenedores de datos estándar. Deberías usarlos.

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
}

Si la clase tiene una interfaz - es mejor ocultar su constructor en la sección protegida. Entonces su objeto no puede ser creado directamente.

¿Definir un constructor vacío? No lo sé. Yo no lo haría. Será mejor que no menciones el destructor si no lo necesitas.

for(iI=0;iI<iHistoryDealsTotal; ++iI)
...

Incremento no estándar iI, iteración no estándar ++iI, iHistoryDealsTotal - definidos en algún lugar por ahí, muy antes del bucle. Más sencillo:

for(int i = 0; i < HistoryDealsTotal();i++)

Es tan rápido como la versión anterior, pero mucho más evidente.

virtual bool               IsTPCInUnloss() const { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };

Los propios programadores parecen estar en contra de esos textos, pero ellos mismos los escriben en alguna parte. Nadie quiere investigar esas tonterías. Qué les impidió escribirlo así:

virtual bool IsTPCInUnloss() const
{
   if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE)
      return(false);
   if(GetTPCType() == POSITION_TYPE_BUY)
   { 
      if(GetTPCStopLoss() >= GetTPCOpenPrice())
         return(true);
   } 
   else
   {
     if(GetTPCStopLoss() <= GetTPCOpenPrice())
        return(true);
   }; 
   return (false);
};

Y ';' al final de las llaves - eso es obsoleto, no deberías hacerlo ahora.

Un método Select gigante consiste en un bucle for gigante:

for(iI=0;iI<iHistoryDealsTotal; ++iI)
      {
      ulCurTicket = HistoryDealGetTicket(iI);
      
      if(ulCurTicket == 0)
         return(WRONG_VALUE);
      
      // Получим направление сделки   
      if(HistoryDealGetInteger(ulCurTicket,DEAL_ENTRY,lCurEntry)!=true)
         {
         TRACE_INTEGER("Не удалось получить направление сделки ! Тикет: ",ulCurTicket);
         continue;
         };
      
      // Проверим направление сделки
      if(lCurEntry != DEAL_ENTRY_OUT)
         continue;
      
      // Получим магик сделки
      if(HistoryDealGetInteger(ulCurTicket,DEAL_MAGIC,lCurMagic)!=true)
         {
         TRACE_INTEGER("Не удалось получить магик сделки ! Тикет: ",ulCurTicket);
         continue;
         };
         
      // Проверим магик
      if(ulMagic != NULL && lCurMagic != ulMagic)
         {
         //TRACE_INTEGER("Сделка не подходит ! Имеет неверный магик ! Magic сделки: ",lCurMagic);
         //TRACE_INTEGER("Требуемый Magic : ",ulMagic);
         continue;
         };
      ...
}

Obviamente, todas las comprobaciones de la conformidad de la operación con el Asesor Experto actual deberían implementarse en un método separado, por ejemplo así:

for(iI=0;iI<iHistoryDealsTotal; ++iI)
{
   if(!CheckDeal(iI))
      continue;
   ...
}

Y en general, select debería dividirse en 3-4 métodos más para que quede claro lo que ocurre en él.

George Merts
El Asesor Experto en sí consta de cinco líneas. En este archivo se declara el objeto de la fábrica de piezas del EA y se incluyen las inclusiones.

La fábrica es un patrón muy controvertido. Es bueno usarlo, pero no recomiendo hacerlo todo a través de una fábrica.

George Merts
E incluso cuando hay dos niveles de anidación, es obligatorio escribir comentarios después de cada paréntesis de cierre, que bloque entierra (digamos, la cabecera del bucle duplicado).

Por eso lo escribes entre paréntesis en la fea forma de MQL. Si lo has escrito así:

if(OrderSelect())
{
   ...
}

Siempre verás qué corchete cierra cada bloque de código.

Habría una docena de otras advertencias en su código. Por supuesto, el código no es perfecto, pero se nota el gusto del autor por la belleza :))

 
Vasiliy Sokolov:

Una pequeña crítica:

О. Ese es el tipo de discusión que me encanta. Así que.

¿Por qué reinventar la rueda en forma de CMyObject, cuando existe un CObject estándar que todo el mundo entiende?

La funcionalidad de CObject y CArrayObj es obviamente copiada aquí. ¿Por qué? La clasificación rápida está integrada en los contenedores de datos estándar. Utilízalos.

CMyObject es un heredero del CObject estándar, todas las listas y arrays en mi código son descendientes de CArray (y otros arrays de la biblioteca estándar). Casi nunca utilizo las matrices estándar de array[].

Y, por supuesto, ordenar y trabajar con listas utiliza la funcionalidad básica de CObject.

Y la diferencia entre ellos es la siguiente: Un CObject estándar es "un objeto de lista o matriz ordenada". Un CMyObject es un CObject que tiene un tipo determinado, y contiene algún valor dado cuando fue creado. Necesitaba este objeto debido a la reducción generalizada de los objetos a la clase abstracta básica - para entender por puntero a qué objeto apunta "realmente". El tipo CMyObject se establece mediante esa misma función SetMyObjectType (). Esta función debe ser llamada necesariamente en los constructores de cualquier derivado de CMyObject, para asignar un identificador a la clase a la que pertenece el objeto.

También tiene SetUDCreationValue() - que establece un valor definido por el usuario cuando se crea. Rara vez se utiliza. Es necesario para distinguir diferentes objetos de la misma clase.

Si la clase tiene una interfaz, su constructor debe estar oculto en la sección protegida. Entonces su objeto no puede ser creado directamente.

Constructor protegido Sí, probablemente sea razonable para las interfaces, no sabía que fuera posible.

¿Definir un constructor vacío? No lo sé. Yo no lo haría. Es mejor no mencionar el destructor si no lo necesitas.

Es una "herencia maldita del pasado". Hace tiempo escribimos un proyecto bastante grande y allí si no definíamos un destructor vacío, tardaba bastante tiempo en eliminar los objetos por alguna razón. Así que, desde entonces, lo hago sobre la marcha. En general, el destructor también debe ser virtual.

Incremento no estándar iI, iteración no estándar ++iI, iHistoryDealsTotal - definidos en algún lugar por ahí, muy antes del bucle. Es mejor mantenerlo más simple:

No estoy de acuerdo. El incremento es perfectamente normal, - i, sólo la notación estandarizada - primero con una letra minúscula su tipo es entero, y luego con una letra mayúscula su nombre es I.

Parece que estás en contra de esas hojas, pero lo escribes en alguna parte. Nadie quiere analizar esas tonterías. ¿Qué le impidió escribirlo así?

En este caso tuve que elegir entre la "visibilidad" de la clase y la belleza de la función. He elegido la "visibilidad". La belleza sufrió.

El método Select gigante consiste en un bucle for gigante:

Obviamente, todas las comprobaciones de la conformidad de la operación con el Asesor Experto actual deberían estar en un método separado, por ejemplo, así:

Y en general, select necesita ser dividido en 3-4 métodos más para que quede claro lo que sucede en él.

Estoy de acuerdo. Aquí, en principio, este mismo ciclo ha "crecido", al principio no era tan grande.

Aunque no siempre es conveniente implementar comprobaciones menores en las funciones privadas, ya que estas comprobaciones no siempre son rastreables en el código.

La fábrica es un patrón muy controvertido. El uso está bien, pero no recomiendo hacer todo a través de una fábrica.

Ahora no recuerdo, había varias variantes de la construcción del Asesor Experto. Me detuve en la "fábrica de piezas de Expert Advisor". En principio, ya no se trata de un modelo clásico puro de "fábrica". En un principio, pretendía ser un patrón "clásico", pero ahora es más bien un "constructor-concentrador" de piezas de Asesor Experto. Y también es responsable de la retirada de esas piezas, lo que no es propio de una fábrica. Pero el nombre permaneció.

Por eso lo escribes entre paréntesis en la fea forma de MQL. Si lo has escrito así:

Siempre se vería qué paréntesis cierra cada bloque de código.

¿Por qué "la manera fea"?

La cabecera del bucle, luego el corchete de apertura con sangría, después todo el bloque con la misma sangría y, por último, el corchete de cierre, también con sangría.

¿Qué crees que es mejor?

 
Gregory Kovalenko:

Necesito obtener beneficios en 2 órdenes abiertas. La última orden abierta, la llamoOrderProfit2, y la siguiente orden -OrderProfit1.

Primero se abre el primer orden y luego el segundo, por lo que el primer orden del bucle se llama 2)

¿Dónde está el error?

Sólo estás revisando los pedidos. En ninguna parte se comprueba cuál es el primero y cuál el segundo.

Es necesario introducir un cheque en la hora de apertura. Así podrá distinguir entre la orden que se abrió antes y la que se abrió después. O tal vez puedan abrirse al mismo tiempo.

Razón de la queja: