예제 #1
0
파일: Main.cs 프로젝트: HaoranXue/LiquidETF
        public static void BackTest(string Date, string Weeks)
        {
            ///////////////////////////
            ///
            /// Setting backtest object
            ///
            //////////////////////////

            Backtest Mybacktest     = new Backtest();
            Backtest Mybacktest_adj = new Backtest();

            // Initial the two backtest

            Mybacktest.Init();
            Mybacktest_adj.Init();

            ///////////////////////////
            ///
            /// Setting data container to store the information
            /// outside the for loop
            ///
            ///////////////////////////

            // setting netvalue lists for both standard strategy and dynamic strategy

            List <double> Hisc_netValue = new List <double>();
            List <double> Adj_netValue  = new List <double>();

            // setting lists to store Fixed Income ETFs and Equity ETFs
            // we are holding during this trading week for the caculation of
            // Turnover Utility function

            List <string> ETFs_holding_FI  = new List <string>();
            List <string> ETFs_holding_Equ = new List <string>();

            // setting matrix to store the trading history during this backtest

            string[][] trading_history_ETF           = new string[Convert.ToInt64(Weeks)][];
            double[][] trading_history_allocation    = new double[Convert.ToInt64(Weeks)][];
            double[][] ADJtrading_history_allocation = new double[Convert.ToInt64(Weeks)][];

            // setting two variable drawdown and position ratio for the caculation
            // in Dynamic strategy, position ratio means the percentage of Fixed Income
            // ETFs we are currently holding

            double DrawDown       = 0;
            double Position_ratio = 0.2;

            int FI_holding_weeks  = 0;
            int Equ_holding_weeks = 0;

            double[] FI_holding_allocation  = new double[5];
            double[] Equ_holding_allocation = new double[5];

            //////////////////////////////////
            ///
            /// For loop backtest
            ///
            /////////////////////////////////


            for (int i = 0; i < Convert.ToInt64(Weeks); i++)
            {
                // seting Datapreprocessing class for both Fixed Income ETFs and Equity ETFs

                DataPreProcessing pro_FI  = new DataPreProcessing();
                DataPreProcessing pro_Equ = new DataPreProcessing();

                // caculate the date of today start from the 'Date'
                // which is given by backtest function

                var Today = DateTime.Parse(Date).AddDays(i * 7);

                // print the date we trained the model and trade

                Console.WriteLine("Date: {0}", Today.ToString());

                // cleaning data use data preprocessing class

                pro_FI.Run(Today.ToString(), 112, "Fixed Income");
                pro_Equ.Run(Today.ToString(), 112, "Equity");

                // Set prediction vector

                double[] predictions_FI  = new double[pro_FI.Trade_ETF.Count];
                double[] predictions_Equ = new double[pro_Equ.Trade_ETF.Count];

                // Set blend ETFs list to store the Top 10 etfs which is going to be
                // longed by the algorithm

                List <string> Blend_ETFs = new List <string>();

                /////////////////////////////
                ///                   ///////
                /// FI ETF prediction ///////
                ///                   ///////
                /////////////////////////////

                for (int j = 0; j < pro_FI.Trade_ETF.Count; j++)
                {
                    // Grab the data from the datapreprocessing object class

                    var y = pro_FI.Target_List[j];
                    var fy = new FrameBuilder.Columns <DateTime, string> {
                        { "Y", y }
                    }.Frame;
                    var data          = pro_FI.Feature_List[j].Join(fy);
                    var pred_Features = pro_FI.pred_Feature_List[j];

                    data.SaveCsv("dataset.csv");

                    // Training machine learning and predict

                    var prediction = Learning.FitGBT(pred_Features);

                    predictions_FI[j] = prediction;
                }

                // Get the minimum scores of top 5 ETF

                var hold_FI = PredRanking(predictions_FI, 5);

                // Get the namelist of top 5 ETF

                List <string> ETFs_FI = new List <string>();

                for (int m = 0; m < pro_FI.Trade_ETF.Count; m++)
                {
                    if (predictions_FI[m] >= hold_FI)
                    {
                        ETFs_FI.Add(pro_FI.Trade_ETF[m]);
                    }
                }


                // Caculate the bid-ask spread cost if trade all 5 ETFs given by algorithm

                double[] FixedIncomeSpread = new double[5];
                FixedIncomeSpread = GetBidAskSpread(ETFs_FI.ToArray());

                // Cacualte the Unitility and decide if we should trade this week

                if (i == 0)
                {
                    ETFs_holding_FI = ETFs_FI;
                }
                else
                {
                    // get all prediction results of both holding etf and etfs which we may be going to trade

                    double[] holding_pred = ETFname2Prediction(ETFs_holding_FI, predictions_FI, pro_FI);
                    double[] long_pred    = ETFname2Prediction(ETFs_FI, predictions_FI, pro_FI);

                    // caculate the trade diff which is the utility of trading this week

                    double trade_diff = long_pred.Sum() -
                                        holding_pred.Sum() -
                                        CaculateRebalanceCost(ETFs_FI,
                                                              ETFs_holding_FI,
                                                              FI_holding_allocation,
                                                              pro_FI);

                    // check if it is worth of trading

                    if (trade_diff < 0)
                    {
                        // It is not worth of trading and ETFs portfolio will be same

                        FI_holding_weeks += 1;
                        ETFs_FI           = ETFs_holding_FI;

                        // setting spread equals to 0 because we are not going to trade this week

                        FixedIncomeSpread = new double[] { 0, 0, 0, 0, 0 };
                    }
                    else
                    {
                        FI_holding_weeks = 0;

                        // It is worth of changing positions and trading this week !

                        // recaculate the spread costs because we may not going to trade all
                        // ETFs which we are holding right now.

                        for (int m = 0; m < 5; m++)
                        {
                            for (int n = 0; n < 5; n++)
                            {
                                if (ETFs_FI[m] == ETFs_holding_FI[n])
                                {
                                    FixedIncomeSpread[m] = 0;
                                }
                                else
                                {
                                    continue;
                                }
                            }
                        }

                        // resetting the fixed income ETFs  we are holding

                        ETFs_holding_FI = ETFs_FI;
                    }
                }

                Console.WriteLine("Long the following ETFs: ");

                // Store the Fixed Income ETFs namelist to blend ETFs list

                for (int n = 0; n < ETFs_FI.Count; n++)
                {
                    Console.WriteLine(ETFs_FI[n]);
                    Blend_ETFs.Add(ETFs_FI[n]);
                }

                ///////////////////////////////////
                ///                         ///////
                /// Equity ETF prediction   ///////
                ///                         ///////
                ///////////////////////////////////


                for (int j = 0; j < pro_Equ.Trade_ETF.Count; j++)
                {
                    // Run machine learning and predict next week for all ETFss

                    var y = pro_Equ.Target_List[j];

                    var fy = new FrameBuilder.Columns <DateTime, string> {
                        { "Y", y }
                    }.Frame;
                    var data          = pro_Equ.Feature_List[j].Join(fy);
                    var pred_Features = pro_Equ.pred_Feature_List[j];

                    data.SaveCsv("dataset.csv");

                    var prediction = Learning.FitGBT(pred_Features);

                    predictions_Equ[j] = prediction;
                }


                List <string> ETFs_Equ = new List <string>();

                // Find the min score of top 5 best ETFs

                var hold_Equ = PredRanking(predictions_Equ, 5);

                for (int m = 0; m < pro_Equ.Trade_ETF.Count; m++)
                {
                    if (predictions_Equ[m] >= hold_Equ)
                    {
                        ETFs_Equ.Add(pro_Equ.Trade_ETF[m]);
                    }
                }

                // caculate the bidAsk Spread

                double[] EquityBASpread = new double[5];
                EquityBASpread = GetBidAskSpread(ETFs_Equ.ToArray());

                if (i == 0)
                {
                    ETFs_holding_Equ = ETFs_Equ;
                }
                else
                {
                    double[] holding_pred = ETFname2Prediction(ETFs_holding_Equ, predictions_Equ, pro_Equ);
                    double[] long_pred    = ETFname2Prediction(ETFs_Equ, predictions_Equ, pro_Equ);

                    // Caculate the Utility

                    double trade_diff = long_pred.Sum() -
                                        holding_pred.Sum() -
                                        CaculateRebalanceCost(ETFs_Equ,
                                                              ETFs_holding_Equ,
                                                              Equ_holding_allocation,
                                                              pro_Equ);

                    // check if it is worth of trading this week

                    if (trade_diff < 0)
                    {
                        Equ_holding_weeks += 1;
                        ETFs_Equ           = ETFs_holding_Equ;
                        EquityBASpread     = new double[] { 0, 0, 0, 0, 0 };
                    }
                    else
                    {
                        // Recacluate the spread costs

                        Equ_holding_weeks = 0;

                        for (int m = 0; m < 5; m++)
                        {
                            for (int n = 0; n < 5; n++)
                            {
                                if (ETFs_Equ[m] == ETFs_holding_Equ[n])
                                {
                                    EquityBASpread[m] = 0;
                                }
                                else
                                {
                                    continue;
                                }
                            }
                        }

                        ETFs_holding_Equ = ETFs_Equ;
                    }
                }

                // Store the Equity ETFs we are going to long in Blend ETFs list

                for (int n = 0; n < ETFs_Equ.Count; n++)
                {
                    Console.WriteLine(ETFs_Equ[n]);
                    Blend_ETFs.Add(ETFs_Equ[n]);
                }

                //  Caculate optimized allocations for both Fixed income and Equity ETFs

                //////////////////////////////

                Console.WriteLine("Holding weeks for current Fixed Income ETFs is {0}", FI_holding_weeks);
                Console.WriteLine("Holding weeks for current Equity ETFs is {0}", Equ_holding_weeks);

                double[] AllocationFI = new double[5];

                if (FI_holding_weeks == 0)
                {
                    AllocationFI          = PO.ETF2AllocationOwnOptim(ETFs_FI, pro_FI);
                    FI_holding_allocation = AllocationFI;
                }
                else if (FI_holding_weeks < 15)
                {
                    AllocationFI = FI_holding_allocation;
                }
                else
                {
                    FI_holding_weeks      = 0;
                    AllocationFI          = PO.ETF2AllocationOwnOptim(ETFs_FI, pro_FI);
                    FI_holding_allocation = AllocationFI;
                }

                //////////////////////////////

                double[] AllocationEqu = new double[5];

                if (Equ_holding_weeks == 0)
                {
                    AllocationEqu          = PO.ETF2AllocationOwnOptim(ETFs_Equ, pro_Equ);
                    Equ_holding_allocation = AllocationEqu;
                }
                else if (Equ_holding_weeks < 15)
                {
                    AllocationEqu = Equ_holding_allocation;
                }
                else
                {
                    Equ_holding_weeks      = 0;
                    AllocationEqu          = PO.ETF2AllocationOwnOptim(ETFs_Equ, pro_Equ);
                    Equ_holding_allocation = AllocationEqu;
                }

                //////////////////////////////

                // Setting allocations which is an array to store allocations for strandard strategy

                double[] allocations = new double[10];

                for (int fi = 0; fi < 5; fi++)
                {
                    allocations[fi] = AllocationFI[fi] * 0.2;
                }

                for (int equ = 0; equ < 5; equ++)
                {
                    allocations[equ + 5] = AllocationEqu[equ] * 0.8;
                }


                // Setting ALLOCATION which is an array to store allocations for strandard strategy

                double[] ALLOCATION = new double[10];

                for (int fi = 0; fi < 5; fi++)
                {
                    ALLOCATION[fi] = AllocationFI[fi] * Position_ratio;
                }

                for (int equ = 0; equ < 5; equ++)
                {
                    ALLOCATION[equ + 5] = AllocationEqu[equ] * (1 - Position_ratio);
                }

                //  Transform ETFs list to an array

                string[] ETFs = new string[10];

                for (int etf = 0; etf < 10; etf++)
                {
                    ETFs[etf] = Blend_ETFs[etf];
                }

                // Storing the ETFs trading history and allocations

                trading_history_ETF[i] = new string[10];
                trading_history_ETF[i] = ETFs;

                trading_history_allocation[i] = new double[10];
                trading_history_allocation[i] = allocations;

                ADJtrading_history_allocation[i] = new double[10];
                ADJtrading_history_allocation[i] = ALLOCATION;

                // Get the spread array for all ETFs

                double[] spread = new double[10];

                Array.Copy(FixedIncomeSpread, spread, FixedIncomeSpread.Length);
                Array.Copy(EquityBASpread, 0, spread, FixedIncomeSpread.Length, EquityBASpread.Length);

                // Caculate the weighted spread for the adjustment in netvalue

                double weighted_spread = 0;

                for (int spreadItem = 0; spreadItem < 10; spreadItem++)
                {
                    weighted_spread += allocations[spreadItem] * spread[spreadItem];
                }

                // clearing the NetValue

                Hisc_netValue.Add(Mybacktest.Rebalance(Today, ETFs, allocations) * (1 - weighted_spread));
                Adj_netValue.Add(Mybacktest_adj.Rebalance(Today, ETFs, ALLOCATION) * (1 - weighted_spread));

                // Caculate the current drawdown and adjust the position ratio which is
                //  the percentage for the fixed income ETFs

                if (i == 0)
                {
                    DrawDown = 0;
                }
                else
                {
                    DrawDown = 1 - Hisc_netValue.Last() / Hisc_netValue.Max();
                }

                // Adjust the position ratio

                if (DrawDown > 0.1)
                {
                    Position_ratio = 0.8;
                }
                else if (DrawDown > 0.08)
                {
                    Position_ratio = 0.6;
                }
                else if (DrawDown > 0.05)
                {
                    Position_ratio = 0.4;
                }
                else
                {
                    Position_ratio = 0.2;
                }

                // Print out the current drawdown and position ratio.

                Console.WriteLine("Current drawdown is: {0}", DrawDown);
                Console.WriteLine("Fixed Income has been adjusted to: {0} %", Position_ratio * 100);
            }


            // Result analysis for NetValue

            /////////////////////
            ///
            /// Backtest Metrics of Strandard Strategy
            ///
            ////////////////////


            var StrategyNetValue = Hisc_netValue.ToArray();

            double MaxDD = 0;

            for (int i = 1; i < StrategyNetValue.Length; i++)
            {
                var    MaxNetValue = StrategyNetValue.Take(i).Max();
                double drawdown    = 1 - StrategyNetValue[i] / MaxNetValue;

                if (drawdown > MaxDD)
                {
                    MaxDD = drawdown;
                }
            }

            Console.WriteLine("Maximum drawdown of This Strategy is: {0}", MaxDD);

            var AnnualReturn = Math.Log(StrategyNetValue.Last()) /
                               (Convert.ToDouble(Weeks) / 50);

            Console.WriteLine("Annual Return of This Strategy is: {0}", AnnualReturn);

            var StrategyReturn = NetValue2Return(StrategyNetValue);

            Console.WriteLine("Standard Deviation of This Strategy is: {0}",
                              Statistics.StandardDeviation(StrategyReturn));

            double[] BTmetrics = new double[3];
            BTmetrics[0] = AnnualReturn;
            BTmetrics[1] = MaxDD;
            BTmetrics[2] = Statistics.StandardDeviation(StrategyReturn) * Math.Sqrt(50);

            // Result analysis for AdjNetValue

            /////////////////////
            ///
            /// Backtest Metrics of Dynamic Strategy
            ///
            /////////////////////

            var ADJStrategyNetValue = Adj_netValue.ToArray();

            double ADJMaxDD = 0;

            for (int i = 1; i < ADJStrategyNetValue.Length; i++)
            {
                var    MaxNetValue = ADJStrategyNetValue.Take(i).Max();
                double drawdown    = 1 - ADJStrategyNetValue[i] / MaxNetValue;

                if (drawdown > ADJMaxDD)
                {
                    ADJMaxDD = drawdown;
                }
            }


            Console.WriteLine("Maximum drawdown of ADJ Strategy is: {0}", ADJMaxDD);

            var ADJAnnualReturn = Math.Log(ADJStrategyNetValue.Last()) /
                                  (Convert.ToDouble(Weeks) / 50);

            Console.WriteLine("Annual Return of ADJ Strategy is: {0}", ADJAnnualReturn);

            var ADJStrategyReturn = NetValue2Return(ADJStrategyNetValue);

            Console.WriteLine("Standard Deviation of This Strategy is: {0}",
                              Statistics.StandardDeviation(ADJStrategyReturn));

            double[] ADJBTmetrics = new double[3];
            ADJBTmetrics[0] = ADJAnnualReturn;
            ADJBTmetrics[1] = ADJMaxDD;
            ADJBTmetrics[2] = Statistics.StandardDeviation(ADJStrategyReturn) * Math.Sqrt(50);

            // Output all results to CSV
            // Without position adjustment
            SaveArrayAsCSV_(trading_history_allocation, "StrandardTradingHistoryAllocation.csv");
            SaveArrayAsCSV(BTmetrics, "StandardBacktestMetrics.csv");
            SaveArrayAsCSV(StrategyNetValue, "StandardNet_value.csv");
            // With position adjustmnet
            SaveArrayAsCSV_(ADJtrading_history_allocation, "DynamicTradingHistoryAllocation.csv");
            SaveArrayAsCSV(ADJBTmetrics, "DynamicBacktestMetrics.csv");
            SaveArrayAsCSV(ADJStrategyNetValue, "DynamicNetValue.csv");

            SaveArrayAsCSV_ <string>(trading_history_ETF, "ETFTradingHistoryforALL.csv");

            Console.ReadKey();
        }