Evaluación de estrategias en series sintéticas (2ª Parte)
 
 
  • Usted está aquí:
  • Home
  • Optimización
  • Evaluación de estrategias en series sintéticas (2ª Parte)

Evaluación de estrategias en series sintéticas (2ª Parte)

 
AndyG - 5 Oct 2019
0 comentarios
 

Continuamos con la segunda parte de este artículo exponiendo la implementación el la plataforma NinjaTrader de nuestro método para la creación dinámica de series sintéticas y la evaluación automatizada de estrategias en dichas series.



4.- Punto de partida: Series calculadas en Excel barra a barra

 

Inicialmente, se parte de los cálculos barra a barra que alteran la serie original en sus modalidades con y sin anclaje:



Los cálculos han de hacerse barra a barra en base a tres números aleatorios que regulan las alteraciones del máximo, mínimo y cierre, así como el promedio de volatilidad de barras anteriores.

Con estos cálculos bien definidos y vista su viabilidad grosso-modo en el Excel, eso nos da la estructura general del algoritmo barra a barra, y ya nos podemos lanzar a implementarlo en la plataforma concreta.

 

5.- Implementación en una plataforma para backtesting

 

A la hora de implementar este proceso en una plataforma de trading, lo que se persigue es poder hacer backtesting de estrategias sobre series OHLC alteradas, es decir, llevar a cabo un proceso similar al de optimización (en el que se realizan un número de iteraciones de backtesting en un período de tiempo dado), pero en lugar de alterar los parámetros de la estrategia, alteraremos las series OHLC sobre las que se ejecutan, analizando a posteriori los resultados del backtesting a nivel de trade y de resultados globales.

 

Así, conceptualmente, la implementación debe cumplir estas características:


  • Antes de comenzar cada iteración, se debe generar una serie OHLC modificada (con o sin anclaje) para el período de tiempo dado.
  • Esa serie debe sustituir a la original en dos vertientes: en el cálculo de indicadores y setups de la estrategia que barra a barra deben procesarse correctamente, y en la apertura y cierre de la posición para que ésta se produzca  según el precio de la serie alterada.
  • Las series alteradas deben poder representarse gráficamente en la plataforma para evaluar si se están alterando correctamente y las aperturas y cierres con coherentes.
  • Los resultados de todas las iteraciones deben poder exportarse tanto a nivel de trade como a nivel de resultados globales, para su análisis. Resultaría especialmente interesante exportación en Excel con soporte gráfico y lo más procesada posible.


Se ha elegido implementarlo en NinjaTrader 7 por compatibilidad con el código de estrategias existentes, familiaridad con el código fuente y el desarrollo de módulos y su depuración, y la integración con Excel. El desarrollo en NinjaTrader versión 8 sería muy parecido y según se estudió durante el proceso, no aporta ventajas al respecto.


6.- Implementación en plataforma NinjaTrader7: Generar serie OHLC alterada.

 

Inicialmente se da por bueno el optimizador general que lleva NinjaTrader y bastaría simplemente con añadir un parámetro “iteracion” a la estrategia que no se utiliza en ella, sino que vaya toma valores desde 1 a N siendo N el número de iteraciones que se persiguen.

El primer paso lógico es crear un mecanismo que genere la serie alterada barra a barra y que en cada iteración la sustituya por la original. En cada barra de la ejecución de una estrategia en NinjaTrader, cuyo código se ubica en el procedimiento OnBarUpdate, existen cuatro DataSeries que nos permiten acceder a las series OHLC, llamados Open, High, Low y Close.

Por ejemplo, supongamos un ejemplo de estrategia extremadamente simple que abre posición si el cierre de la barra es mayor que la EMA de 100 períodos y la cierra si cae por debajo:


protected override void OnBarUpdate()

{

if (Close[0] > EMA(Close, 100) && Position.MarketPosition != MarketPosition.Long)

   EnterLong(1,"");

 

if (Close[0] < EMA(Close, 100) && Position.MarketPosition == MarketPosition.Long)

   ExitLong("","");

}

 

Así pues, la situación ideal sería no cambiar absolutamente nada del código de la estrategia y justo antes del comienzo de cada iteración generar los 4 DataSeries alterados que serían accesibles bajo la misma denominación. Lamentablemente, y tras consultar al servicio técnico de NinjaTrader, esto no es posible dado que la generación de estas series de datos forma parte del núcleo de código fuente y no se pueden alterar los DataSeries Open, High, Low y Close en ninguna de sus versiones, siendo siempre de sólo lectura.

Para resolver este problema, la opción más evidente es crear un indicador (llamado por ejemplo OHLCDespl) que barra a barra calcule las series alteradas de cada iteración y las contenga, siendo referenciado en el código de la estrategia. En realidad, en todo caso había que hacer un indicador para la representación gráfica de las series alteradas, pero tendrá la responsabilidad adicional de calcular esas series barra a barra.

La principal desventaja de este enfoque es clara: la estrategia deberá modificarse para incluir referencias a este indicador en su código, sustituyendo a las referencias a los DataSeries Open, High, Low y Close, quedando así nuestro código de ejemplo:


protected override void OnBarUpdate()

{

if (  OHLCDespl().Close[0] > EMA(OHLCDespl().Close, 100) &&

       Position.MarketPosition != MarketPosition.Long )

   EnterLong(1,"");

 

if ( OHLCDespl().Close[0] < EMA(OHLCDespl().Close, 100) &&

      Position.MarketPosition == MarketPosition.Long)

   ExitLong("","");

}

 

En la práctica el indicador OHLCDespl() tendrá parámetros como el número de barras para el promedio de volatilidad o el número de días para reiniciar el anclaje (en series con anclaje) y algún otro para agilizar el proceso de cálculo.

El cálculo del indicador es relativamente sencillo. Se utilizarán en su interior tantos DataSeries como columnas haya en el Excel de definición de los cálculos. Ello facilita enormemente su desarrollo y depuración en caso de problemas. Por ejemplo para calcular los ticks al alza, a la baja y entre cierres, se definen tres DataSeries UpTicks, DownTicks y CloseTicks que los van calculando en base a la serie original y que sirven como base para cálculos posteriores:



UpTicks[0]=(High[0]-Open[0])/Instrument.MasterInstrument.TickSize;

DownTicks[0]=(Open[0]-Low[0])/Instrument.MasterInstrument.TickSize;

CloseTicks[0]= Math.Abs(Close[0]-Close[1])/Instrument.MasterInstrument.TickSize;

 


El punto crítico del cálculo de las series alteradas es cuando se definen los valores de los DataSeries HighA, LowA y CloseA que determinan unos ticks arriba o abajo al azar en base a otros valores calculados de volatilidad. En definitiva es el único sitio del cálculo donde se deben usar valores aleatorios, por lo que es crítico para la variabilidad de los resultados. El método más razonable que se ha encontrado para que esa aletoriedad fuera representativa ha sido usar como semilla un hashing del Guid de .NET:


var rand = new Random(Guid.NewGuid().GetHashCode());

var ticksize= Instrument.MasterInstrument.TickSize;

HighA[0]= rand.Next(-1* (int) UpVolat[0], (int) UpVolat[0])* tickSize;

LowA[0]= rand.Next(-1* (int) DownVolat[0], (int) DownVolat[0])*tickSize;

CloseA[0]= rand.Next(-1* (int) CloseVol[0], (int) CloseTicks[0])*tickSize;

 


Así la serie alterada oscilará aleatoriamente entre los valores de volatilidad calculados de una forma bastante razonable.

Otro aspecto fundamental del indicador es que los valores de serie alterada se representasen de una forma visual para su revisión. 




En la representación gráfica del indicador se superpone una barra roja o verde con transparencia mostrando la alteración en la misma, así como la línea de mínimos y máximos con un poco más de grosor.

Para conseguir esta representación gráfica hay que definir unos elementos Brush (con transparencia) y Pen en el procedimiento OnStartUp y luego utilizarlos sobreescribiendo el procedimiento Plot que se encarga de dibujar (código simplificado valX es la serie alterada):



protected override void OnStartUp()

{

            if (ChartControl == null || Bars == null)

                return;

            brocharoja = new SolidBrush(Color.FromArgb(50,Color.DarkRed));

            brochaverde  = new SolidBrush(Color.FromArgb(50,Color.DarkGreen));

            lapiz = new Pen(Color.Black, 2);

             //linea high low si se quiere con guiones

//lapiz.DashStyle=DashStyle.Dash;

}

 

public override void Plot(Graphics graphics, Rectangle bounds, double min, double max)

{

int ancho = (int) (Math.Max(3, 1 + 2 * ((int)Bars.BarsData.ChartStyle.BarWidth - 1) + 2)*1.5);

for (int i = FirstBarIndexPainted; i <= LastBarIndexPainted; i++)

            {

                if (i - Displacement < 0 || i - Displacement >= BarsArray[0].Count ||  

                     (!ChartControl.ShowBarsRequired && i - Displacement < BarsRequired))

                    continue;

                int x = ChartControl.GetXByBarIdx(BarsArray[0], i);

                int y1 = ChartControl.GetYByValue(this, valO);

                int y2 = ChartControl.GetYByValue(this, valH);

                int y3 = ChartControl.GetYByValue(this, valL);

                int y4 = ChartControl.GetYByValue(this, valC);

                                                              

                //desplazamiento lateral lineas high low. con 1 pixel se solapa un poco

                int despl_x_hl=1;

                                                              

                //linea high

                graphics.DrawLine(lapiz, x+despl_x_hl, Math.Min(y1,y4), x+despl_x_hl, y2);

                //linea low

                graphics.DrawLine(lapiz, x+despl_x_hl, Math.Max(y1,y4), x+despl_x_hl, y3);

                                                              

             

if (y4 == y1)

                    graphics.DrawLine(lapiz, x - ancho / 2, y1, x + ancho / 2, y1);

                else

                {  //barra en verde o rojo

                    if (y4 > y1)

                        graphics.FillRectangle(brocharoja, x - ancho / 2, y1, ancho, y4 - y1);

                    else

                        graphics.FillRectangle(brochaverde, x - ancho / 2, y4, ancho, y1 - y4);

                }

             } //for

   }



7.- Implementación en plataforma NinjaTrader 7: Apertura/cierre de posiciones en la serie desplazada.

 

En este punto ya tenemos un indicador que calcula las series alteradas y las representa gráficamente y al que podemos referenciar desde las estrategias en lugar de las series originales. El siguiente problema a resolver es que no se puede sobreescribir o alterar los métodos que abren posiciones tipo EnterLong o EnterShort  para que esas posiciones se materialicen fuera de la serie original, con lo que aunque se calculen bien los setups y se usen las series alteradas, las posiciones sólo se podrán materializar si estamos dentro del los precios de la serie original.

 

La solución pasa por modificar un aspecto que sí permite NinjaTrader y que es el algoritmo de Fill, regulador del precio al que se abrirán las órdenes en sus distintas modalidades: a mercado, limitadas, de stop…

 

NinjaTrader proporciona dos tipos de Fill: Default o Liberal, cuyo código está disponible. Centrándonos por simplicidad en órdenes a mercado, el código para ambas es tan sencillo como que se abre posición en el precio de la apertura siguiente barra de la serie original:


                               public override void Fill(Order order)

                               {

                                               if (order.OrderType == OrderType.Market)

                                               {

                                                                                              FillPrice=NextOpen;

                                               }

                                               …. // resto de tipos de orden

                               }

 


Como se puede ver, lo único que hay que hacer es implementar un tipo Fill personalizado en el que el precio de apertura sea no el NextOpen de la serie original sino el NextOpen de la serie alterada.  El problema es que la serie alterada reside únicamente en el indicador OHLCDespl que hemos creado, que se instancia y referencia dentro del código de la estrategia, por lo que esos valores alterados son inaccesibles desde el código de un tipo Fill personalizado.

 

La solución consiste en definir una clase estática en C# que contenga código personalizado, el cual nos hará de intermediario entre el indicador, la estrategia y cualquier otro elemento que nos interese (en este caso el tipo de Fill personalizado). 






Estas clases son muy útiles cuando hay mucho código que conviene recaiga fuera del indicador o estrategia. Aunque en este caso es para poca cosa, luego iremos metiendo en ella más código auxiliar para controlar iteraciones o la exportación a Excel. Llamaremos a la clase estática OHLCDesplAux, metemos el código en un simple fichero OHLCDesplAux.cs en la carpeta de código y de momento será tan simple como una variable para el precio con su getter y setter:

 


namespace NinjaTrader.Indicator

{

                public static class OHLCDesplAux

                {                             

                               private static double oHLCDesplNextOpen;                       

                              

                               public static double OHLCDesplNextOpen {

                                               get{ return oHLCDesplNextOpen; }

                                               set{

                                                               if(value != oHLCDesplNextOpen){

                                                                              oHLCDesplNextOpen = value;                                                  

                                                               }

                                               }

                               }                             

}

 

Ahora que ya tenemos un intermediario visible desde cualquier estrategia o indicador en ejecución y desde el tipo de Fill, sólo queda que el indicador escriba el valor:

 

                …

double SiguienteOpen=0;

                switch(tipoSerie){

                               case 0: //con anclaje

                                               SiguienteOpen=CloseCAaux[0];

                                               break;

                                                                             

                               case 1: //sin anclaje                                                                     

                                               SiguienteOpen=CloseSAaux[0];

                                               break;

                                                                             

                }

                                                                                             

                //Valor del siguiente Open para el algoritmo de Fill

                OHLCDesplAux.OHLCDesplNextOpen=SiguienteOpen;               

                …

 

y que el tipo de Fill personalizado lo consuma:



 

                [Gui.Design.DisplayName("OHLCDespl")]

                public class OHLCDesplFillType : FillType

                {

                               private const double epsilon      = 0.00000001;

                               public override void Fill(Order order)

                               {

                                               if (order.OrderType == OrderType.Market)

                                               {

                                                               FillPrice=OHLCDesplAux.OHLCDesplNextOpen;

//FillPrice=NextOpen;

                                               }

                               ….

 

Una vez puestas las referencias, NinjaTrader compilará el código de la clase personalizada como un fichero más. Así, ya tenemos un tipo de Fill personalizado, accesible desde la GUI, que permitirá que las posiciones se materialicen en los precios de la serie alterada:






8.- Implementación en plataforma NinjaTrader 7: Control de iteraciones, concurrencia, graficación y aleatoriedad


Una vez implementado todo y funcionando adecuadamente, nos encontramos con un nuevo contratiempo. En NinjaTrader, cuando se le da a examinar los resultados de las iteraciones y su graficación se queda un tiempo procesando información antes de mostrarla:





Al no estar completamente documentados todos los comportamientos de la plataforma, podría pensarse que ese lapso de tiempo es debido a que está recopilando y representando la información contenida en memoria acerca del proceso de optimización, pero qué va: en realidad en el preciso momento en el que se selecciona una iteración en el listado de resultados de arriba, NinjaTrader inicia una instancia de la estrategia con los parámetros dados de la iteración y tras procesarla, grafica y muestra los resultados. Este comportamiento subóptimo (la información está en memoria y podría acceder a ella sin pegas) se sigue reproduciendo en NinjaTrader 8.

 

Esta clara limitación de NinjaTrader plantea un problema: los números aleatorios generados en cada barra de cada serie de cada iteración se vuelven a refrescar (por otros diferentes) cada vez que se selecciona una estrategia, por lo que será imposible ver la graficación y resultados correspondientes a una iteración dada, que se irá ejecutando sobre series alteradas diferentes. Esto es un grave problema que echa por tierra la posibilidad de examinar y graficar una iteración concreta, siempre se alteraría por otra nueva.

 

La solución a este problema pasa de nuevo por la clase estática en C#. En ella se definará una matriz de números aleatorios, tres por cada barra de cada iteración, de manera que siempre que se vuelva a una iteración dada, ésta siempre se ejecutará usando los mismos números aleatorios (y por lo tanto la misma serie alterada) y sus resultados y graficación serán los mismos.

 

 

private static List> aleatorios1 = new List>();          

private static List> aleatorios2 = new List>();          

private static List> aleatorios3 = new List>();          

private static int iteracion;

private static int barras;

                                                              

public static int Barras {

                get{ return barras; }

                set{barras=value;}

}

                              

public static int Iteracion {

                get{ return iteracion; }

                set{

if(iteracion!=value)

                                               llenar_aleatorios();                                                      

                                               iteracion=value;

                }

}

                              

public static int aleatorio_entero(int min, int max, int barra, int iteracion, int indice){

double rnd=0;

                switch(indice){

                               case 1: rnd=aleatorios1[iteracion-1][barra];    break;

                               case 2: rnd=aleatorios2[iteracion-1][barra];    break;

                               case 3: rnd=aleatorios3[iteracion-1][barra];    break;                                   

                }

    return (int) (rnd*(max-min)+min);

}

                                                              

public static void llenar_aleatorios(){

                var rand = new Random(Guid.NewGuid().GetHashCode());

                                                              

                List iteracionactual1= new List();

                List iteracionactual2= new List();

                List iteracionactual3= new List();

 

                for(int i=0;i

                               iteracionactual1.Add(rand.NextDouble());

                               iteracionactual2.Add(rand.NextDouble());

                               iteracionactual3.Add(rand.NextDouble());                                                       

                }                             

                aleatorios1.Add(iteracionactual1);        

                aleatorios2.Add(iteracionactual2);

                aleatorios3.Add(iteracionactual3);                                                                      

}

 


Como se puede ver en el código se definen tres matrices aleatorios1, aleatorios 2 y aleatorios3 que contienen los números necesarios para cada barra de cada iteración. Se llenan cuando se cambia de número de iteración (especificado por el setter de la variable) y para obtenerlos se usa la función aleatorio_entero cuya invocación desde el indicador sería ahora así:



 

var tickSize= Instrument.MasterInstrument.TickSize;

HighA[0]= OHLCDesplAux.aleatorio_entero(-1*UpVolat[0], UpVolat[0],CurrentBar,iter,2)

                   *tickSize;

LowA[0]=OHLCDesplAux.aleatorio_entero(1*DownVolat[0],DownVolat[0],CurrentBar,iter,2)

                  *tickSize;

CloseA[0]=OHLCDesplAux.aleatorio_entero(-1*CloseVol[0],CloseTicks[0],CurrentBar,iter,2)

                   *tickSize;

 


De esta manera cada iteración tendrá una serie alterada reproducible y podrá analizarse y graficarse correctamente en la pantalla de resultados.

 

Abundando en la pantalla de resultados, también se observó que las estadísticas que allí aparecían no tenían ni pies ni cabeza respecto a la ejecución de cada iteración, sus trades y graficación (en las capturas ya está corregido pero en su momento salían valores imposibles). Tras mucha investigación, se llegó a la conclusión de que había un problema de concurrencia, generado por la ejecución multihilo de NinjaTrader que aunque acelera el proceso, necesita de una sincronización fuerte entre procesos y control de la concurrencia, que al parecer no se implementa adecuadamente si hay dependencias entre ejecuciones.

 

Por ese motivo y para controlar la ejecución de cada iteración, llenado y limpieza de aleatorios y para exportación de resultados que se verá más adelante, se consideró necesario implementar una variante del optimizador de NinjaTrader, partiendo del estándar. Es un código sencillo que permite mantener el control y anular los problemas de concurrencia (sólo se exponen algunas funciones relevantes):




[Gui.Design.DisplayName("OHLCDespl")]

public class OHLCDesplOptimizationMethod : OptimizationMethod

{

                …

                public override void Initialize()

                {

                               base.Initialize();

                               OHLCDesplAux.limpiar_aleatorios();

                }

 

                public override bool MultiThreadSupport

                {

                               get { return false; }

                }

 

                public override void Optimize()

                {

                               //limpiamos los numeros aleatorios de otras optimizaciones                                                                  

                               Iterate(0);

                                              

                               WaitForIterationsCompleted(true);

                                OHLCDesplAux.iniciar_libro(4);

                               OHLCDesplAux.exportar_trades();                        

                }

                …

}

 


En el código del optimizador personalizado se puede ver cómo se anula el procesamiento multihilo, se limpian los aleatorios de otras ejecuciones y, aunque se verá más tarde, se espera a que todas las ejecuciones hayan terminado para exportar los resultados a Excel. Este optimizador personalizado deberá elegirse en la GUI de NinjaTrader igual que se hacía con el tipo de Fill:






Con todo ello tendremos un sólido y consistente control sobre el proceso global que garantiza que se podrán analizar y representar correctamente las iteraciones. Lamentablemente se pierder rendimiento al anular el procesamiento multihilo, pero es ineludible dado que no tenemos más control sobre el proceso seguido en NinjaTrader.

 

9.- Implementación en plataforma NinjaTrader 7: exportación de resultados y análisis en Excel


La pantalla de resultados ofrecida por NinjaTrader es bastante limitada y sobre todo efímera, es accesible sólo hasta que se cierra la ventana. La opción habitual para su exportación sería navegar por las iteraciones una a una, en su pestaña de “Trades” y exportarlas individualmente a un libro de excel. Está claro que ese proceso debe mejorarse y automatizarse para que poder hacer un análisis viable.

 

La primera aproximación que se nos puede ocurrir sería exportar los trades de cada iteración a un fichero de texto csv, por ejemplo sobreescribiendo el método OnTermination de la estrategia (o desde el optimizador personalizado si se dispone de él) expuesto de forma muy simplificada:

 


protected override void OnTermination()

{

                StreamWriter sw;

                String id=iteracion.ToString();

String path = @"c:windows emp"+id+".csv";

                if (!File.Exists(path)) 

                               sw = File.CreateText(path);

                                              

                foreach (Trade t in Performance.AllTrades)

                                {                             

                               String salida=id;                                                                                                                           

                               //datos trade

                               salida=salida+separador+t.TradeNumber.ToString();

salida=salida+((t.Entry.MarketPosition==MarketPosition.Long)?separador+

"Long":separador+"Short");

                               salida=salida+t.Entry.Time.ToString(separador+"dd/MM/yyyy;HH:mm:ss");

                               salida=salida+t.Exit.Time.ToString(separador+"dd/MM/yyyy;HH:mm:ss");                                     

                               salida=salida+separador+t.Exit.Price.ToString();

                               salida=salida+separador+Math.Round(t.ProfitCurrency).ToString();

                               sw.WriteLine(salida);

                                }                                                            

                sw.Close();                                                       

}

 

Este sencillo código en cualquier estrategia en NinjaTrader (que disponga de un parámetro “iteracion” que distinga cada ejecución en el optimizador) permitirá, de forma general, exportar la información de los trades de la misma con detalle de entrada, salida, precios y profit and loss.

 

No obstante, para hacer un buen análisis de los resultados sobre series alteradas, se requeriría posteriormente exportar toda esta información a otro libro excel, calcular la series de equity y graficarlas y otros estudios estadísticos. Este trabajo adicional puede ser muy tedioso y comprometer la viabilidad de llevar a cabo estos estudios.

 

Por ese motivo, aprovechando que NinjaTrader trabaja en C#, se ha optado por usar las capacidades de este lenguaje para la automatización en Excel, lo que se denomina Microsoft Office Interop Excel Automation.

 

Básicamente requiere que el equipo donde se ejecute NinjaTrader tenga también el Excel instalado, copiar al directorio bin y referenciar desde las opciones de References la DLL de Microsoft Office Interop:



(A esta pantalla se accede desde el editor de código de NinjaTrader, botón derecho y References)



y nombrarlas desde el código como la directiva using:


using System.Runtime.InteropServices;

using Excel = Microsoft.Office.Interop.Excel;

 

 

Aprovechando que tenemos una clase estática y tal y como se ha visto en el código del optimizador personalizado en el apartado anterior, todo el código de exportación se ha incluido allí por limpieza. A continuación se muestra un código genérico que permite crear un libro de excel con un número de hojas dado:

 

public static void iniciar_libro(int hojas){                                           

try

{

excelApp= (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");

}

                catch

                {

                excelApp = new Microsoft.Office.Interop.Excel.Application();

                }

                                              

excelWorkBook=excelApp.Workbooks.Add(Type.Missing);

 

int i;

int cuenta=excelWorkBook.Worksheets.Count;

Excel.Worksheet xlWorkSheet;

if(cuenta<=hojas)

for(i=0;i

xlWorkSheet=(Excel.Worksheet) excelWorkBook.Worksheets.Add((Excel.Worksheet)excelWorkBook.Worksheets.get_Item(1),System.Reflection.Missing.Value,hojas-cuenta,Excel.XlSheetType.xlWorksheet);

else                                                      

                for(i=0;i

                               xlWorkSheet = (Excel.Worksheet)excelWorkBook.Worksheets.get_Item(1);

                               xlWorkSheet.Delete();                                 

                }                                            

 

 

/*

En este punto se introduciría todo el código de Excel para llenar las hojas de valores, fórmulas, gráficos, etc, etc, articulado en funciones

*/

                                                              

String path = @"c:windows empsalida.xlsx";

if (File.Exists(path))

File.Delete(path);           

                                              

excelWorkBook.SaveAs(path,Excel.XlFileFormat.xlOpenXMLWorkbook,Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlExclusive, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

 

excelApp.Visible = true;                                              

excelApp.Quit();

 

Marshal.ReleaseComObject(excelWorkBook);

Marshal.ReleaseComObject(excelApp);                                                                            

}             

 


Como se puede ver, lo que hace la función es invocar a la aplicación de Excel y, si todo ha ido bien, añadir un libro. Cuando Excel inicia un nuevo libro suele tener 3 hojas, pero no es un parámetro fijo, por lo que el código cuenta las hojas que hay y añade o elimina hasta obtener las deseadas.

 

En la parte intermedia, antes de guardar el libro, deberemos implementar las distintas funciones que llenen de valores, fórmulas y gráficos las hojas del libro. También se podría hacer a posteriori reabriendo el libro existente. En todo caso finalmente se guarda y se sale del Excel.

 

El resultado final es un libro con todos los resultados exportados y su análisis exportados en diferentes pestañas:



 


(Continuará...)

Por: José Luis Gil y Andrés A. García

TradingSys.org, 2019


 

Añadir comentario

 
Modificado por AndyG - 19 Dic 2020
 
 

Secciones

 
 

Entradas recientes

 
 

Enlaces