[Quantproject-developers] QuantProject/b7_Scripts/ArbitrageTesting/PairTrading/SimplePairTrading Sim
Brought to you by:
glauco_1
|
From: Marco M. <mi...@us...> - 2006-05-14 15:25:34
|
Update of /cvsroot/quantproject/QuantProject/b7_Scripts/ArbitrageTesting/PairTrading/SimplePairTrading In directory sc8-pr-cvs7.sourceforge.net:/tmp/cvs-serv7517/PairTrading/SimplePairTrading Added Files: SimplePTGenomeManipulator.cs RunSimplePairTrading.cs GenomeMeaningSimplePT.cs GenomeManagerForSimplePT.cs EndOfDayTimerHandlerSimplePT.cs Log Message: Added SimplePairTrading strategy, based on arbitrage concepts --- NEW FILE: EndOfDayTimerHandlerSimplePT.cs --- /* QuantProject - Quantitative Finance Library EndOfDayTimerHandlerSimplePT.cs Copyright (C) 2003 Marco Milletti This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; using System.Data; using System.Collections; using QuantProject.ADT; using QuantProject.Business.Financial.Accounting; using QuantProject.Business.Financial.Instruments; using QuantProject.Business.Financial.Ordering; using QuantProject.Business.Timing; using QuantProject.Business.DataProviders; using QuantProject.Data.DataProviders; using QuantProject.Data.Selectors; using QuantProject.Data.DataTables; using QuantProject.ADT.Optimizing.Genetic; using QuantProject.Scripts.TickerSelectionTesting.EfficientPortfolios; using QuantProject.Scripts.ArbitrageTesting.PairTrading.SimplePairTrading.InSample; namespace QuantProject.Scripts.ArbitrageTesting.PairTrading.SimplePairTrading { /// <summary> /// Implements MarketOpenEventHandler, /// MarketCloseEventHandler and OneHourAfterMarketCloseEventHandler /// for the implementation of the simple pair trading strategy /// </summary> [Serializable] public class EndOfDayTimerHandlerSimplePT { private string tickerGroupID; protected string[] chosenTickers = new string[10]; //this array is designed for containing //five couples of eligible tickers for //the pair trading strategy protected double[] averageGapsOfChosenTickers = new double[10]; protected double[] stdDevGapsOfChosenTickers = new double[10]; protected string[] lastOrderedTickers = new string[2]; protected int addedTickersCounter = 0; protected float currentPair_firstTickerGain = 0.0F; protected float currentPair_secondTickerGain = 0.0F; private int numberOfEligibleTickers; private int numDaysForOptimizationPeriod; private int generationNumberForGeneticOptimizer; private int populationSizeForGeneticOptimizer; protected string benchmark; protected DateTime startDate; protected DateTime endDate; protected double maxNumOfStdDevForNormalGap; private double maxLevelForNormalGap; private int minNumOfDaysForGapComputation; private int maxNumOfDaysForGapComputation; private int numDaysBetweenEachOptimization; private int numDaysElapsedSinceLastOptimization; private double maxRunningHours; protected Account account; private int seedForRandomGenerator; protected double minimumGainForClosingPositions; protected double maximumToleratedLoss; protected double initialAccountValue = 0.0; protected int counterForDaysWithPositions = 0; protected int currentNumDaysForGapComputation; //it stores the value for the new account protected HistoricalAdjustedQuoteProvider historicalAdjustedQuoteProvider; private OptimizationOutput optimizationOutput; public OptimizationOutput OptimizationOutput { get{return this.optimizationOutput;} } public EndOfDayTimerHandlerSimplePT(string tickerGroupID, int numberOfEligibleTickers, int numDaysForOptimizationPeriod, int generationNumberForGeneticOptimizer, int populationSizeForGeneticOptimizer, string benchmark, DateTime startDate, DateTime endDate, double maxNumOfStdDevForNormalGap, int minNumOfDaysForGapComputation, int maxNumOfDaysForGapComputation, int numDaysBetweenEachOptimization, double maxRunningHours, Account account) { this.tickerGroupID = tickerGroupID; this.numberOfEligibleTickers = numberOfEligibleTickers; this.numDaysForOptimizationPeriod = numDaysForOptimizationPeriod; this.generationNumberForGeneticOptimizer = generationNumberForGeneticOptimizer; this.populationSizeForGeneticOptimizer = populationSizeForGeneticOptimizer; this.benchmark = benchmark; this.startDate = startDate; this.endDate = endDate; this.maxNumOfStdDevForNormalGap = maxNumOfStdDevForNormalGap; this.minNumOfDaysForGapComputation = minNumOfDaysForGapComputation; this.maxNumOfDaysForGapComputation = maxNumOfDaysForGapComputation; this.numDaysBetweenEachOptimization = numDaysBetweenEachOptimization; this.maxRunningHours = maxRunningHours; this.account = account; this.numDaysElapsedSinceLastOptimization = 0; this.seedForRandomGenerator = ConstantsProvider.SeedForRandomGenerator; this.historicalAdjustedQuoteProvider = new HistoricalAdjustedQuoteProvider(); this.minimumGainForClosingPositions = 0.002; this.maximumToleratedLoss = 0.02; this.optimizationOutput = new OptimizationOutput(); } public void MarketOpenEventHandler( Object sender , EndOfDayTimingEventArgs endOfDayTimingEventArgs ) { } #region MarketCloseEventHandler public void marketCloseEventHandler_closePositions() { if(this.account.Portfolio[ this.lastOrderedTickers[0] ] != null) this.account.ClosePosition( this.lastOrderedTickers[0] ); if(this.account.Portfolio[ this.lastOrderedTickers[1] ] != null) this.account.ClosePosition( this.lastOrderedTickers[1] ); } private bool marketCloseEventHandler_isCurrentGainGoodEnoughOrStopLossConditionReached(EndOfDayDateTime currentEndOfDayDateTime) { bool returnValue = false; double currentGain = (this.account.GetMarketValue() - this.initialAccountValue)/ this.initialAccountValue; if(currentGain >= this.minimumGainForClosingPositions || -currentGain >= this.maximumToleratedLoss) //gain is good enough or loss is too high ... returnValue = true; return returnValue; } private float marketCloseEventHandler_openPositions_getGap(int indexForPair, DateTime firstDate, DateTime lastDate) { Quotes firstTickerQuotes = new Quotes(this.chosenTickers[indexForPair], firstDate, lastDate); Quotes secondTickerQuotes = new Quotes(this.chosenTickers[indexForPair+1], firstDate, lastDate); this.currentPair_firstTickerGain = (float)firstTickerQuotes.Rows[1]["quClose"]/(float)firstTickerQuotes.Rows[0]["quClose"]; this.currentPair_secondTickerGain = (float)secondTickerQuotes.Rows[1]["quClose"]/(float)secondTickerQuotes.Rows[0]["quClose"]; return (this.currentPair_firstTickerGain - this.currentPair_secondTickerGain); } private void marketCloseEventHandler_openPositions_open(int indexForPair) { double cashForSinglePosition = this.account.CashAmount/2; long quantityForFirstTicker = Convert.ToInt64( Math.Floor( cashForSinglePosition / this.account.DataStreamer.GetCurrentBid( this.chosenTickers[indexForPair] ) ) ); long quantityForSecondTicker = Convert.ToInt64( Math.Floor( cashForSinglePosition / this.account.DataStreamer.GetCurrentBid( this.chosenTickers[indexForPair+1] ) ) ); OrderType orderTypeForFirstTicker = OrderType.MarketBuy; OrderType orderTypeForSecondTicker = OrderType.MarketSellShort; if(this.currentPair_firstTickerGain > this.currentPair_secondTickerGain) { orderTypeForFirstTicker = OrderType.MarketSellShort; orderTypeForSecondTicker = OrderType.MarketBuy; } this.account.AddOrder(new Order( orderTypeForFirstTicker, new Instrument( this.chosenTickers[indexForPair] ) , quantityForFirstTicker )); this.account.AddOrder(new Order( orderTypeForSecondTicker, new Instrument( this.chosenTickers[indexForPair+1] ) , quantityForSecondTicker )); this.lastOrderedTickers[0] = this.chosenTickers[indexForPair]; this.lastOrderedTickers[1] = this.chosenTickers[indexForPair+1]; } private void marketCloseEventHandler_openPositions_setMaxLevelForNormalGap(int indexForPair) { this.maxLevelForNormalGap = this.averageGapsOfChosenTickers[indexForPair] + this.maxNumOfStdDevForNormalGap * this.stdDevGapsOfChosenTickers[indexForPair]; } private void marketCloseEventHandler_openPositions(EndOfDayDateTime currentEndOfDayDateTime, IndexBasedEndOfDayTimer timer) { if(this.chosenTickers[0] != null) //potential tickers for pair trading have been set { for(int i = 0; i<this.chosenTickers.Length - 1 && this.account.Portfolio.Count == 0; i++) { this.marketCloseEventHandler_openPositions_setMaxLevelForNormalGap(i); if(Math.Abs(this.marketCloseEventHandler_openPositions_getGap(i, timer.GetPreviousDateTime(), timer.GetCurrentTime().DateTime)) >= (float)this.maxLevelForNormalGap) //currentGap is too high for being considered rational //so there is an arbitrage opportunity this.marketCloseEventHandler_openPositions_open(i); } this.initialAccountValue = this.account.GetMarketValue(); //this.account.Portfolio.GetMarketValue(currentEndOfDayDateTime,this.historicalAdjustedQuoteProvider); } } public virtual void MarketCloseEventHandler( Object sender , EndOfDayTimingEventArgs endOfDayTimingEventArgs ) { if(this.account.Portfolio.Count == 0) //portfolio is empty { if(this.account.Transactions.Count == 0) this.account.AddCash(30000); this.marketCloseEventHandler_openPositions(endOfDayTimingEventArgs.EndOfDayDateTime, (IndexBasedEndOfDayTimer)sender); } else // portfolio is not empty { this.counterForDaysWithPositions++; if(this.counterForDaysWithPositions == this.currentNumDaysForGapComputation || this.marketCloseEventHandler_isCurrentGainGoodEnoughOrStopLossConditionReached(endOfDayTimingEventArgs.EndOfDayDateTime)) //a number of days equal to the number of days //used for gap computation has elapsed or //the current gain is good enough or //loss is too high { this.marketCloseEventHandler_closePositions(); this.counterForDaysWithPositions = 0; } } } #endregion #region OneHourAfterMarketCloseEventHandler protected DataTable getSetOfTickersToBeOptimized(DateTime currentDate) { SelectorByGroup temporizedGroup = new SelectorByGroup(this.tickerGroupID, currentDate); SelectorByQuotationAtEachMarketDay quotedAtEachMarketDayFromEligible = new SelectorByQuotationAtEachMarketDay( temporizedGroup.GetTableOfSelectedTickers(), false, currentDate.AddDays(-this.numDaysForOptimizationPeriod), currentDate, this.numberOfEligibleTickers, this.benchmark); return quotedAtEachMarketDayFromEligible.GetTableOfSelectedTickers(); } private void setTickers_setChosenTickers_addGenomeForOptimizationOutput(Genome genome, DateTime currentDate) { this.optimizationOutput.Add(new GenomeRepresentation(this.maxNumOfStdDevForNormalGap, genome, currentDate.AddDays(-this.numDaysForOptimizationPeriod), currentDate)); } private void setTickers_setChosenTickers_addTickers(Genome genome) { this.chosenTickers[this.addedTickersCounter] = ((GenomeMeaningSimplePT)genome.Meaning).FirstTicker; this.chosenTickers[this.addedTickersCounter+1] = ((GenomeMeaningSimplePT)genome.Meaning).SecondTicker; this.averageGapsOfChosenTickers[this.addedTickersCounter] = ((GenomeMeaningSimplePT)genome.Meaning).AverageGap; this.stdDevGapsOfChosenTickers[this.addedTickersCounter] = ((GenomeMeaningSimplePT)genome.Meaning).StdDevGap; this.currentNumDaysForGapComputation = ((GenomeMeaningSimplePT)genome.Meaning).NumOfDaysForGap; this.addedTickersCounter += 2; } private bool setTickers_setChosenTickers_isAnyTickerOfGenomeInChosenTickers(Genome genome) { bool returnValue = false; foreach(string ticker in this.chosenTickers) { if(ticker == ((GenomeMeaningSimplePT)genome.Meaning).FirstTicker || ticker == ((GenomeMeaningSimplePT)genome.Meaning).SecondTicker) returnValue = true; } return returnValue; } private void setTickers_setChosenTickers(GeneticOptimizer GO, DateTime currentDate) { this.addedTickersCounter = 0; for(int i = 0; i<GO.CurrentGeneration.Count -1 && addedTickersCounter<this.chosenTickers.Length; i++) { Genome currentGenome = (Genome)GO.CurrentGeneration[GO.CurrentGeneration.Count-i-1]; if(!this.setTickers_setChosenTickers_isAnyTickerOfGenomeInChosenTickers(currentGenome)) { this.setTickers_setChosenTickers_addTickers(currentGenome); this.setTickers_setChosenTickers_addGenomeForOptimizationOutput(currentGenome, currentDate); } } } private void setTickers(DateTime currentDate) { DataTable setOfTickersToBeOptimized = this.getSetOfTickersToBeOptimized(currentDate); IGenomeManager genManForSimplePT = new GenomeManagerForSimplePT(setOfTickersToBeOptimized, currentDate.AddDays(-this.numDaysForOptimizationPeriod), currentDate, this.minNumOfDaysForGapComputation, this.maxNumOfDaysForGapComputation, this.maxNumOfStdDevForNormalGap); GeneticOptimizer GO = new GeneticOptimizer(genManForSimplePT, this.populationSizeForGeneticOptimizer, this.generationNumberForGeneticOptimizer, this.seedForRandomGenerator); GO.Run(false); this.setTickers_setChosenTickers(GO, currentDate); } /// <summary> /// Handles a "One hour after market close" event. /// </summary> /// <param name="sender"></param> /// <param name="eventArgs"></param> public virtual void OneHourAfterMarketCloseEventHandler( Object sender , EndOfDayTimingEventArgs endOfDayTimingEventArgs ) { this.seedForRandomGenerator++; if(this.numDaysElapsedSinceLastOptimization == this.numDaysBetweenEachOptimization - 1) { this.setTickers(endOfDayTimingEventArgs.EndOfDayDateTime.DateTime); //sets tickers to be chosen at next Market Open event this.numDaysElapsedSinceLastOptimization = 0; } else { this.numDaysElapsedSinceLastOptimization++; } } #endregion } } --- NEW FILE: SimplePTGenomeManipulator.cs --- /* QuantProject - Quantitative Finance Library SimplePTGenomeManipulator.cs Copyright (C) 2003 Marco Milletti This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; using System.Collections; using QuantProject.ADT; using QuantProject.ADT.Optimizing.Genetic; namespace QuantProject.Scripts.ArbitrageTesting.PairTrading.SimplePairTrading { /// <summary> /// Class providing static methods for manipulating Genomes /// (used by GenomeManagerForSimplePT) /// </summary> [Serializable] public sealed class SimplePTGenomeManipulator { public static Random RandomGenerator; private static int genomeSize; private static Genome[] childs; private static int[,] maskForChilds; static SimplePTGenomeManipulator() { RandomGenerator = new Random(ConstantsProvider.SeedForRandomGenerator); childs = new Genome[2]; } private static void initializeStaticMembers(Genome parent1, Genome parent2) { genomeSize = parent1.Size; childs[0]=parent1.Clone(); childs[1]=parent2.Clone(); maskForChilds = new int[childs.Length, genomeSize]; for(int i = 0; i<genomeSize; i++) { maskForChilds[0,i]=1; maskForChilds[1,i]=2; } //maskForChilds has been set in order to re-create //a copy of parents by using setChildsUsingMaskForChilds() } private static void setMaskForChildsForMixingWithoutDuplicates(Genome parent1, Genome parent2) { //exchange numOfDaysForGap maskForChilds[0, 0] = 2; maskForChilds[1, 0] = 1; //exchange tickers if possible if(!IsTickerContainedInGenome(parent1.GetGeneValue(1), parent2)) maskForChilds[1, 1] = 1; if(!IsTickerContainedInGenome(parent2.GetGeneValue(1), parent1)) maskForChilds[0, 1] = 2; } private static void setChildsUsingMaskForChilds(Genome parent1, Genome parent2) { for(int childIndex = 0; childIndex < 2; childIndex++) { for(int genePos = 0 ; genePos < SimplePTGenomeManipulator.genomeSize ; genePos++) { if(maskForChilds[childIndex,genePos]==1) childs[childIndex].SetGeneValue(parent1.GetGeneValue(genePos), genePos); else//maskForChilds[childIndex,genePos]==2 childs[childIndex].SetGeneValue(parent2.GetGeneValue(genePos), genePos); } } } /// <summary> /// This method returns an array of genomes based on /// a mix of the genes of parents, such that the 2 childs, /// if possible, are different from parents and, at /// the same time, childs' genes are not duplicated /// </summary> /// <param name="parent1">First genome parent from which genes are to be mixed in offspring</param> /// <param name="parents">Second genome parent from which genes are to be mixed in offspring</param> /// <param name="constToDiscoverGenesDuplicates">Gene y is a duplicate of gene x iff y = |x| - 1 /// or y = -|x| -1</param> public static Genome[] MixGenesWithoutDuplicates(Genome parent1, Genome parent2) { initializeStaticMembers(parent1, parent2); if(parent1.Size > (parent1.MaxValueForGenes - parent1.MinValueForGenes + 1)) //it is impossible not to duplicate genes if size is too // large for the range of variation of each gene throw new Exception("Impossible to avoid duplicates with the given size!"); if(parent1.Size != parent2.Size) throw new Exception("Genomes must have the same size!"); setMaskForChildsForMixingWithoutDuplicates(parent1, parent2); setChildsUsingMaskForChilds(parent1, parent2); //throwExcIfAChildHasDuplicateGenes(); //just for debugging purposes return childs; } /// <summary> /// Returns true if a given gene, when decoded by the /// GenomeManagerForSimplePT, refers to a /// ticker already contained in a given genome /// </summary> /// <param name="geneCorrespondingToATicker">Gene, corresponding to a certain ticker, that has to be checked</param> /// <param name="genome">Genome containing or not the ticker geneCorrespondingToATicker refers to</param> public static bool IsTickerContainedInGenome(int geneCorrespondingToATicker, Genome genome) { return geneCorrespondingToATicker == genome.GetGeneValue(1) || geneCorrespondingToATicker == genome.GetGeneValue(2); } } } --- NEW FILE: GenomeManagerForSimplePT.cs --- /* QuantProject - Quantitative Finance Library GenomeManagerForSimplePT.cs Copyright (C) 2003 Marco Milletti This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; using System.Data; using System.Collections; using QuantProject.ADT.Statistics; using QuantProject.ADT.Optimizing.Genetic; using QuantProject.Data; using QuantProject.Data.DataTables; using QuantProject.Scripts.TickerSelectionTesting.EfficientPortfolios; namespace QuantProject.Scripts.ArbitrageTesting.PairTrading.SimplePairTrading { /// <summary> /// This class implements IGenomeManager interface, in order to find /// tickers that best suite the pair trading strategy /// </summary> [Serializable] public class GenomeManagerForSimplePT : IGenomeManager { private DataTable setOfInitialTickers; private PairTradingCandidate[] candidates; private DateTime firstQuoteDate; private DateTime lastQuoteDate; private int minNumOfDaysForGapComputation; private int maxNumOfDaysForGapComputation; private double maxNumOfStdDevForNormalGap; private int genomeSize; private int minValueForGenes; private int maxValueForGenes; private GeneticOptimizer currentGeneticOptimizer; private float[] gaps; //it will contain absolute gaps of gain for tickers private double[] pairTradingPortfolioGains; //IGenomeManager implementation for properties public int GenomeSize { get{return this.genomeSize;} } public int MinValueForGenes { get{return this.minValueForGenes;} } public int MaxValueForGenes { get{return this.maxValueForGenes;} } public GeneticOptimizer CurrentGeneticOptimizer { get{return this.currentGeneticOptimizer;} set{this.currentGeneticOptimizer = value;} } //end of interface implementation for properties public GenomeManagerForSimplePT(DataTable setOfInitialTickers, DateTime firstQuoteDate, DateTime lastQuoteDate, int minNumOfDaysForGapComputation, int maxNumOfDaysForGapComputation, double maxNumOfStdDevForNormalGap) { this.genomeSize = 3;//1° pos for numIntervalDays; //2° and 3° positions for tickers this.setOfInitialTickers = setOfInitialTickers; this.setMinAndMaxValueForGenes(); this.firstQuoteDate = firstQuoteDate; this.lastQuoteDate = lastQuoteDate; this.minNumOfDaysForGapComputation = minNumOfDaysForGapComputation; this.maxNumOfDaysForGapComputation = maxNumOfDaysForGapComputation; this.maxNumOfStdDevForNormalGap = maxNumOfStdDevForNormalGap; this.candidates = new PairTradingCandidate[this.setOfInitialTickers.Rows.Count]; this.retrieveData(); this.pairTradingPortfolioGains = new double[this.candidates[0].ArrayOfAdjustedCloseQuotes.Length]; } private void setMinAndMaxValueForGenes() { this.minValueForGenes = 0; this.maxValueForGenes = this.setOfInitialTickers.Rows.Count - 1; } private float[] retrieveData_getArrayOfAdjustedCloseQuotes(string ticker) { float[] returnValue = null; Quotes tickerQuotes = new Quotes(ticker, this.firstQuoteDate, this.lastQuoteDate); returnValue = ExtendedDataTable.GetArrayOfFloatFromColumn(tickerQuotes,"quAdjustedClose"); return returnValue; } private void retrieveData() { for(int i = 0; i<this.setOfInitialTickers.Rows.Count; i++) { string ticker = (string)setOfInitialTickers.Rows[i][0]; this.candidates[i] = new PairTradingCandidate(ticker, this.retrieveData_getArrayOfAdjustedCloseQuotes(ticker)); } } private void getFitnessValue_setGaps(Genome genome) { int numOfDaysForGap = genome.GetGeneValue(0); int gapsLength = (this.candidates[0].ArrayOfAdjustedCloseQuotes.Length - 1)/numOfDaysForGap; this.gaps = new float[gapsLength]; int j = 0;//counter for gaps array for(int i = 0;i<gapsLength-numOfDaysForGap;i += numOfDaysForGap) { this.gaps[j] = this.candidates[genome.GetGeneValue(0)].ArrayOfAdjustedCloseQuotes[i+numOfDaysForGap]/ this.candidates[genome.GetGeneValue(0)].ArrayOfAdjustedCloseQuotes[i] - this.candidates[genome.GetGeneValue(1)].ArrayOfAdjustedCloseQuotes[i+numOfDaysForGap]/ this.candidates[genome.GetGeneValue(1)].ArrayOfAdjustedCloseQuotes[i] ; j++; } } public double GetFitnessValue(Genome genome) { double returnValue = 0.0; this.getFitnessValue_setGaps(genome); //float maxGap = this.maxNumOfStdDevForNormalGap * // foreach(float gap in this.gaps) //utilizzare l'equity line della strategia //individuare i giorni in cui si entra nel mercato //in base al segnale (gap > maxGap) ? ottimizzare il maxGap? //crea portafoglio di pair trading //calcola gain o stop loss ad ogni chiusura in base agli adjCloses //se gain o stopLoss non fanno uscire, //aggiungi ritorno al portafoglio di pair trading //se gain o stopLoss fanno uscire, azzera gain e stopLoss //ripeti ciclo esaminando primo gap utile //esauriti i gap, //calcolare sharpeRatio di equityLine returnValue = (1/Math.Abs(BasicFunctions.SimpleAverage(this.gaps)));// /BasicFunctions.StdDev(this.gaps); if(Double.IsNaN(returnValue) || Double.IsInfinity(returnValue)) returnValue = 0.0; return returnValue; } public Genome[] GetChilds(Genome parent1, Genome parent2) { return SimplePTGenomeManipulator.MixGenesWithoutDuplicates(parent1, parent2); } public int GetNewGeneValue(Genome genome, int genePosition) { int returnValue; if(genePosition == 0) //this positions needs the n° of days for the gap //between tickers'gains { returnValue = GenomeManagement.RandomGenerator.Next(this.minNumOfDaysForGapComputation, this.maxNumOfDaysForGapComputation + 1); } else //in 2° or 3° position there must be a ticker different from // the one contained in 3° or 2° position { returnValue = GenomeManagement.RandomGenerator.Next(genome.MinValueForGenes, genome.MaxValueForGenes + 1); while(genePosition>0 && SimplePTGenomeManipulator.IsTickerContainedInGenome(returnValue, genome)) //while in the given position has to be stored //a new gene pointing to a ticker and //the proposed returnValue points to a ticker //already stored in the given genome { // a new returnValue has to be generated returnValue = GenomeManagement.RandomGenerator.Next(genome.MinValueForGenes, genome.MaxValueForGenes + 1); } } return returnValue; } public void Mutate(Genome genome, double mutationRate) { int newValueForGene; int genePositionToBeMutated = GenomeManagement.RandomGenerator.Next(genome.Size); if(genePositionToBeMutated == 0) //this positions needs the n° of days for the gap //between tickers'gains { newValueForGene = GenomeManagement.RandomGenerator.Next(this.minNumOfDaysForGapComputation, this.maxNumOfDaysForGapComputation + 1); } else { newValueForGene = GenomeManagement.RandomGenerator.Next(genome.MinValueForGenes, genome.MaxValueForGenes + 1); while(genePositionToBeMutated>0 && SimplePTGenomeManipulator.IsTickerContainedInGenome(newValueForGene, genome)) //while in the proposed genePositionToBeMutated has to be stored //a new gene pointing to a ticker and //the proposed newValueForGene points to a ticker //already stored in the given genome { newValueForGene = GenomeManagement.RandomGenerator.Next(genome.MinValueForGenes, genome.MaxValueForGenes + 1); } } GenomeManagement.MutateOneGene(genome, mutationRate, genePositionToBeMutated, newValueForGene); } public object Decode(Genome genome) { int numOfDaysForGap = genome.GetGeneValue(0); string firstTicker = this.candidates[genome.GetGeneValue(1)].Ticker; string secondTicker = this.candidates[genome.GetGeneValue(2)].Ticker; double averageGap = BasicFunctions.SimpleAverage(this.gaps); double stdDevGap = BasicFunctions.StdDev(this.gaps); return new GenomeMeaningSimplePT(numOfDaysForGap, averageGap, stdDevGap, firstTicker, secondTicker); } } } --- NEW FILE: RunSimplePairTrading.cs --- /* QuantProject - Quantitative Finance Library RunSimplePairTrading.cs Copyright (C) 2003 Marco Milletti This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; using QuantProject.ADT.FileManaging; using QuantProject.Business.Financial.Accounting; using QuantProject.Business.Financial.Accounting.Reporting; using QuantProject.Business.Timing; using QuantProject.Business.Financial.Accounting.Commissions; using QuantProject.Data.DataProviders; using QuantProject.Scripts.TickerSelectionTesting.EfficientPortfolios; using QuantProject.Scripts.ArbitrageTesting.PairTrading.SimplePairTrading.InSample; namespace QuantProject.Scripts.ArbitrageTesting.PairTrading.SimplePairTrading { /// <summary> /// Script to test the pair trading strategy on two single tickers /// </summary> [Serializable] public class RunSimplePairTrading : RunPairTrading { private EndOfDayTimerHandlerSimplePT endOfDayTimerHandler; public RunSimplePairTrading(string tickerGroupID, int numberOfEligibleTickers, int numDaysForOptimizationPeriod, int generationNumberForGeneticOptimizer, int populationSizeForGeneticOptimizer, string benchmark, DateTime startDate, DateTime endDate, double maxNumOfStdDevForNormalGap, int minNumOfDaysForGapComputation, int maxNumOfDaysForGapComputation, int numDaysBetweenEachOptimization, double maxRunningHours): base(tickerGroupID, numberOfEligibleTickers, numDaysForOptimizationPeriod, generationNumberForGeneticOptimizer, populationSizeForGeneticOptimizer, benchmark, startDate, endDate, minNumOfDaysForGapComputation, maxNumOfDaysForGapComputation, maxNumOfStdDevForNormalGap, numDaysBetweenEachOptimization, maxRunningHours) { } #region auxiliary overriden methods for Run protected override void run_initializeEndOfDayTimerHandler() { this.endOfDayTimerHandler = new EndOfDayTimerHandlerSimplePT(this.tickerGroupID, this.numberOfEligibleTickers, this.numDaysForOptimizationPeriod, this.generationNumberForGeneticOptimizer, this.populationSizeForGeneticOptimizer, this.benchmark, this.startDateTime.DateTime, this.endDateTime.DateTime, this.maxNumOfStdDevForNormalGap, this.minNumOfDaysForGapComputation, this.maxNumOfDaysForGapComputation, this.numDaysBetweenEachOptimization, this.maxRunningHours, this.account); } protected override void run_addEventHandlers() { this.endOfDayTimer.MarketOpen += new MarketOpenEventHandler( this.endOfDayTimerHandler.MarketOpenEventHandler); this.endOfDayTimer.MarketClose += new MarketCloseEventHandler( this.endOfDayTimerHandler.MarketCloseEventHandler); this.endOfDayTimer.MarketClose += new MarketCloseEventHandler( this.checkDateForReport); this.endOfDayTimer.OneHourAfterMarketClose += new OneHourAfterMarketCloseEventHandler( this.endOfDayTimerHandler.OneHourAfterMarketCloseEventHandler ); } #endregion //necessary far calling RunPairTrading.Run() //in classes that inherit from this class public override void Run() { base.Run(); } public override void SaveScriptResults() { string fileName = "From"+this.numberOfEligibleTickers + "OptDays" + this.numDaysForOptimizationPeriod + "GenNum" + this.generationNumberForGeneticOptimizer + "PopSize" + this.populationSizeForGeneticOptimizer; string dirNameWhereToSaveAccounts = System.Configuration.ConfigurationSettings.AppSettings["AccountsArchive"] + "\\" + this.ScriptName + "\\"; string dirNameWhereToSaveTransactions = System.Configuration.ConfigurationSettings.AppSettings["TransactionsArchive"] + "\\" + this.ScriptName + "\\"; // string dirNameWhereToSaveBestGenomes = System.Configuration.ConfigurationSettings.AppSettings["GenomesArchive"] + // "\\" + this.ScriptName + "\\"; this.checkDateForReport_createDirIfNotPresent(dirNameWhereToSaveAccounts); this.checkDateForReport_createDirIfNotPresent(dirNameWhereToSaveTransactions); // this.checkDateForReport_createDirIfNotPresent(dirNameWhereToSaveBestGenomes); ObjectArchiver.Archive(this.account, dirNameWhereToSaveAccounts + fileName + ".qPa"); ObjectArchiver.Archive(this.account.Transactions, dirNameWhereToSaveTransactions + fileName + ".qPt"); // OptimizationOutput optimizationOutput = new OptimizationOutput(); // foreach(GenomeRepresentation genomeRepresentation in this.endOfDayTimerHandler.BestGenomes) // optimizationOutput.Add(genomeRepresentation); // ObjectArchiver.Archive(optimizationOutput, // dirNameWhereToSaveBestGenomes + // fileName + ".bgn"); this.endOfDayTimer.Stop(); new OutputDisplayer(this.startDateTime.DateTime, this.endDateTime.DateTime, this.endOfDayTimerHandler.OptimizationOutput).Show(); } } } --- NEW FILE: GenomeMeaningSimplePT.cs --- /* QuantProject - Quantitative Finance Library GenomeMeaningSimplePT.cs Copyright (C) 2003 Marco Milletti This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; namespace QuantProject.Scripts.ArbitrageTesting.PairTrading.SimplePairTrading { /// <summary> /// This is the class representing the meaning for genome /// found by a GeneticOptimizer initialized by GenomeManagerForSimplePT /// </summary> [Serializable] public class GenomeMeaningSimplePT { private string firstTicker; private string secondTicker; private int numOfDaysForGap; private double averageGap; private double stdDevGap; public string FirstTicker { get{return this.firstTicker;} } public string SecondTicker { get{return this.secondTicker;} } public int NumOfDaysForGap { get{return this.numOfDaysForGap;} } public double AverageGap { get{return this.averageGap;} } public double StdDevGap { get{return this.stdDevGap;} } public GenomeMeaningSimplePT(int numOfDaysForGap, double averageGap, double stdDevGap, string firstTicker, string secondTicker) { this.numOfDaysForGap = numOfDaysForGap; this.firstTicker = firstTicker; this.secondTicker = secondTicker; this.averageGap = averageGap; this.stdDevGap = stdDevGap; } } } |