【C#】29. VIX指数的实现(使用上证50ETF为原始数据)

34 篇文章 1 订阅
32 篇文章 0 订阅

这是到现在为止,第一个用C#完整做出来的金融应用,虽然还有许多需要完善的地方,但是框架已经搭建好了。

</pre><pre name="code" class="csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;

namespace VIX
{
    public enum OptionType
    { 
        Call, Put
    }

    static class Read
    {
        //从CSV读取所有数据到DataTable,认为CSV文件中第一行为标题
        public static DataTable CSV(string path, List<Type> typeList, bool hasHead = true)
        {
            string line = "";
            DataTable dt = new DataTable();
            FileStream fs = new FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            StreamReader sr = new StreamReader(fs, System.Text.Encoding.Default);
            //写入列名称
            if (hasHead)
            {
                line = sr.ReadLine();
                string[] columnNames = line.Split(',');
                for (int i = 0; i < columnNames.Length; i++)
                {
                    dt.Columns.Add(columnNames[i],typeList[i]); //添加列明以及类型
                }
            }
            
            //开始读取数据
            line = sr.ReadLine();
            while (line != null)
            {
                dt.Rows.Add(line.Split(','));   //将内容分割成数组,并且写入行中
                line = sr.ReadLine();
            }
            sr.Close();
            fs.Close();
            return dt;
        }
    }

    static class TypeList
    {
        //格式化Info Table
        public static List<Type> GetInfoTypeList()
        {
            return new List<Type>(new Type[] { typeof(int), typeof(string), typeof(double), typeof(string), typeof(int) });
        }
        //格式化Trading Data的typeList
        public static List<Type> GetTrdTypeList(int numOptions)
        {
            List<Type> trdTypeList = new List<Type>(100);
            for (int i = 0; i < 2 + 2 * numOptions; i++)
            {
                if (i == 0)
                    trdTypeList.Add(typeof(DateTime));  //第一列为时间
                else
                    trdTypeList.Add(typeof(Double));    //第二列是ETF;后面的列都是Bid和Ask
            }
            return trdTypeList;
        }
    }

    static class Filter
    {
        //根据给定的 near / next (int) 类型,将 InfoDT 中的对应的符合条件的行筛选出来,组合成数据表(结果按照行权价格排列)
        static public DataTable NearOrNextInfoDT(DataTable infoDT, int intNearNext)
        {
            return infoDT.AsEnumerable().Select(dr => dr).Where(dr => dr.Field<int>("行权日") == intNearNext).OrderBy(dr => dr.Field<double>("行权价")).CopyToDataTable();
        }

        //给定 Forward Price,找出以它为标准的 out-of-money call 或者 put codes -> Strike
        //Key = code, Value = double [2] { strike, midquote}
        static public void GetDictCodesStrikeMidquote(ref Dictionary<string, double[]> ResultDict, 
            DataTable NearOrNextInfoTB, DataRow TrdDR,double K0, OptionType OpType)
        {
            #region 筛选出执行价格高于或者低于K0的call或者put
            if (OpType==OptionType.Call)
            {
                for (int i = 0; i < ResultDict.Count; i++)
                {
                    if (ResultDict.ElementAt(i).Value[0] <= K0)
                    {
                        ResultDict.Remove(ResultDict.ElementAt(i).Key);
                        i--;
                        ResultDict.OrderByDescending(kvp => kvp.Value[0]);  //按照行权价格降序排序
                    }
                }
                //dictCodeStrike = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field<double>("行权价") > K0 && dr.Field<string>("期权类型") == "认购")
                //    .OrderByDescending(dr => dr.Field<double>("行权价")).Select(dr => dr)
                //    .ToDictionary(dr => dr.Field<string>("期权代码"), dr =>new double[] {dr.Field<double>("行权价"),0});   // Key, Strike, MidQuote = 0
            }
            else if (OpType == OptionType.Put)
            {
                for (int i = 0; i < ResultDict.Count; i++)
                {
                    if (ResultDict.ElementAt(i).Value[0] >= K0)
                    {
                        ResultDict.Remove(ResultDict.ElementAt(i).Key);
                        i--;
                        ResultDict.OrderBy(kvp => kvp.Value[0]);    //按照行权价格升序排列
                    }
                }
                //dictCodeStrike = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field<double>("行权价") < K0 && dr.Field<string>("期权类型") == "认沽")
                //    .OrderBy(dr => dr.Field<double>("行权价")).Select(dr => dr)
                //    .ToDictionary(dr => dr.Field<string>("期权代码"), dr => new double[] { dr.Field<double>("行权价"), 0 });
            }
            #endregion

            Dictionary<string, double[]> tempDict = new Dictionary<string, double[]>();
            bool isLastBidZero = false; //上一个Bid是不是0?
            for (int i = ResultDict.Count - 1; i >= 0; i--)
            {
                if ((double)TrdDR[ResultDict.ElementAt(i).Key + "买一价"] == 0 && isLastBidZero == true)
                {
                    //相邻连续的两个put的bid都是0
                    break;
                    
                }
                else if ((double)TrdDR[ResultDict.ElementAt(i).Key + "买一价"] == 0 && isLastBidZero == false)
                {
                    //相邻第一次出现 0
                    isLastBidZero = true;
                    continue;
                }
                else
                {
                    //符合条件的加入到tempDict
                    tempDict.Add(ResultDict.ElementAt(i).Key,ResultDict.ElementAt(i).Value);
                    isLastBidZero = false;
                }   
            }
            ResultDict = tempDict;
        }
        
        //根据Forward Price返回K0
        static public double GetK0(ref DataTable InfoTB, double F)
        {
            return InfoTB.AsEnumerable().Select(dr => dr.Field<double>("行权价")).Where(K => K < F).Max();
        }

        //返回最终需要处理的数据
        static public Dictionary<string, double[]> GetFinalData(DataTable NearOrNextInfoTB, double K0, DataRow TrdDataRow,
            Dictionary<string, double[]> Call_DictCodeStrikeMidquote, Dictionary<string, double[]> Put_DictCodeStrikeMidquote)
        {
            //Add option with strike = K0
            string[] CallandPutAtK0 = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field<double>("行权价") == K0).Select(dr => dr.Field<string>("期权代码")).ToArray();
            double callMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[0]);   //trdDT.Rows[trdRow]
            double putMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[1]);
            double OpAtK0 = 0.5 * (callMid + putMid);

            //Combine Two Dict!!!
            Dictionary<string, double[]> dict = new Dictionary<string, double[]>();
            for (int i = 0; i < Call_DictCodeStrikeMidquote.Count; i++)
            {
                dict.Add(Call_DictCodeStrikeMidquote.ElementAt(i).Key, Call_DictCodeStrikeMidquote.ElementAt(i).Value);
            }
            dict.Add("OPATM", new double[] { K0, OpAtK0 });
            for (int i = 0; i < Put_DictCodeStrikeMidquote.Count; i++)
            {
                dict.Add(Put_DictCodeStrikeMidquote.ElementAt(i).Key, Put_DictCodeStrikeMidquote.ElementAt(i).Value);
            }
            return dict;
        }
    }

    static class Calculate
    {
        //Get the mid quote for the option wanted in Trade Table row option
        static public double MidQuote(DataRow TrdDataRow, string OptionCode)
        {
            return 0.5 * ((double)TrdDataRow[OptionCode + "卖一价"] + (double)TrdDataRow[OptionCode + "买一价"]);
        }

        //Calculate T1 or T2
        static public double T(DateTime CurrentDate, int intNearNext, double MINSINYEAR)
        {
            DateTime date = DateTime.ParseExact(intNearNext.ToString() + @" 15:00:00", "yyyyMMdd HH:mm:ss", null);
            TimeSpan ts = date - CurrentDate;
            return ts.TotalMinutes / MINSINYEAR;
        }

        //Get temp K, which is used to calculate F, via finding out the smallest difference between call and put
        static public void TempStrike(ref double tempK, ref double minDiff, double callMidQuote, double putMidQuote, int InfoRowNO, DataTable InfoTB)
        {
            double tempDiff = callMidQuote - putMidQuote;
            double absDiff = Math.Abs(tempDiff);

            //Find the lowest difference of | Call - Put |
            if (InfoRowNO == 0)
            {
                minDiff = tempDiff;
                tempK = (double)InfoTB.Rows[InfoRowNO]["行权价"];
            }
            else
            {
                if (Math.Abs(minDiff) > absDiff)
                {
                    minDiff = tempDiff;
                    tempK = (double)InfoTB.Rows[InfoRowNO]["行权价"];
                }
            }
        }

        //Return Sigma^2
        static public double SigmaSquare(Dictionary<string,double[]> DictCodeStrikeMidquote,double T,double R,double F,double K0)
        {
            double contributionSum = 0;
            int numPairs = DictCodeStrikeMidquote.Count;
            double deltaK;

            //将Dict按照执行价格排序
            KeyValuePair<string,double[]>[] dictArr = DictCodeStrikeMidquote.OrderBy(kvp => kvp.Value[0]).ToArray();

            if (numPairs <=1)
            {//如果数量少于或者等于一个,那么就直接返回0(可以作为异常)
                return 0;
            }
            else
            {
                for (int i = 0; i < numPairs; i++)
                {
                    //位于顶端和尾端的情况
                    if (i == 0)
                        deltaK = dictArr.ElementAt(i + 1).Value[0] - dictArr.ElementAt(i).Value[0];
                    else if (i == numPairs - 1)
                        deltaK = dictArr.ElementAt(i).Value[0] - dictArr.ElementAt(i - 1).Value[0];
                    else //中间的情况
                        deltaK = 0.5 * (dictArr.ElementAt(i + 1).Value[0] - dictArr.ElementAt(i - 1).Value[0]);
                    //累加各个期权的contribution
                    contributionSum += Contribution(deltaK, dictArr.ElementAt(i).Value[0], R, T, dictArr.ElementAt(i).Value[1]);
                }
                return 2 / T * contributionSum - 1 / T * Math.Pow(F / K0 - 1,2) ;
            }
        }

        //Return contribution of each option in the target range 
        static public double Contribution(double DeltaK, double K, double R, double T, double MidQuote)
        {
            return DeltaK / (K * K) * Math.Exp(R * T) * MidQuote;
        }

        Return the KeyValuePair List ( key is STRIKE, value is MID QUOTE ) for the calculation of sigma square
        //static public List<KeyValuePair<double, double>> DictListOFStrikeAndQuote(double K0, DataTable NearOrNextInfoTB, DataRow TrdDataRow,
        //    Dictionary<double, double> dictPutStrikesAndMidQuote, Dictionary<double, double> dictCallStikesAndMidQuote)
        //{
        //    //Select the strike price immediate below F1 for near
        //    Dictionary<string, double> nearFiltedPuts = Filter.GetDictCodesStrike(NearOrNextInfoTB, TrdDataRow, K0, OptionType.Put); //trdDT.Rows[trdRow]
        //    Dictionary<string, double> nearFiltedCalls = Filter.GetDictCodesStrike(NearOrNextInfoTB, TrdDataRow, K0, OptionType.Call);
        //    //Add option with strike = K0
        //    string[] CallandPutAtK0 = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field<double>("行权价") == K0).Select(dr => dr.Field<string>("期权代码")).ToArray();
        //    double callMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[0]); //((double)trdDT.Rows[trdRow][CallandPutAtK0[0] + "卖一价"] + (double)trdDT.Rows[trdRow][CallandPutAtK0[0] + "买一价"]) / 2;
        //    double putMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[1]); //((double)trdDT.Rows[trdRow][CallandPutAtK0[1] + "卖一价"] + (double)trdDT.Rows[trdRow][CallandPutAtK0[1] + "买一价"]) / 2;
        //    double OpAtK0 = 0.5 * (callMid + putMid);

        //    var q1 = from r1 in dictPutStrikesAndMidQuote
        //             join r2 in nearFiltedPuts
        //             on r1.Key equals r2.Value
        //             select r1;
        //    var q2 = from r1 in dictCallStikesAndMidQuote
        //             join r2 in nearFiltedCalls
        //             on r1.Key equals r2.Value
        //             select r1;
        //    List<KeyValuePair<double, double>> dict = q1.ToList();
        //    dict.Add(new KeyValuePair<double, double>(K0, OpAtK0));
        //    dict.AddRange(q2.ToList());
        //    dict.OrderBy(kvp => kvp.Key);
        //    return dict;
        //}



    }

    static class Loop
    {
        //the loop in near or next info table
        //使用了Out关键字来输出Call和Put的由 Code, Strike, MidQuote 组成的字典
        public static void inNearOrNextTB(DataTable NearOrNextInfoTB, DataRow TrdDataRow,out double Ktemp, out double minDiff,
            out Dictionary<string, double[]> Call_DictCodeStrikeMidquote, out Dictionary<string, double[]> Put_DictCodeStrikeMidquote)
        {
            //out 必须要初始化
            Call_DictCodeStrikeMidquote = new Dictionary<string, double[]>();
            Put_DictCodeStrikeMidquote = new Dictionary<string, double[]>();
            Ktemp = 0;
            minDiff = 0;

            for (int nearInfoRow = 0; nearInfoRow < NearOrNextInfoTB.Rows.Count - 1; nearInfoRow += 2)
            {
                //对于Info Table里面的每个 Option
                //1、得到它的代码 Code
                string callCode = NearOrNextInfoTB.Rows[nearInfoRow]["期权代码"].ToString();
                string putCode = NearOrNextInfoTB.Rows[nearInfoRow + 1]["期权代码"].ToString();
                //2、得到 Code对应的中点Quote
                double callMidQuote = Calculate.MidQuote(TrdDataRow, callCode);   //trdDT.Rows[trdRow]
                double putMidQuote = Calculate.MidQuote(TrdDataRow, putCode); //trdDT.Rows[trdRow]

                //添加到字典中,为最终求值做准备
                Call_DictCodeStrikeMidquote.Add((string)NearOrNextInfoTB.Rows[nearInfoRow]["期权代码"],
                    new double[] {(double)NearOrNextInfoTB.Rows[nearInfoRow]["行权价"], callMidQuote});
                Put_DictCodeStrikeMidquote.Add((string)NearOrNextInfoTB.Rows[nearInfoRow+1]["期权代码"], 
                    new double[] {(double)NearOrNextInfoTB.Rows[nearInfoRow+1]["行权价"], putMidQuote});
 
                
                //得到 Temp Strike Price
                Calculate.TempStrike(ref Ktemp, ref minDiff, callMidQuote, putMidQuote, nearInfoRow, NearOrNextInfoTB);
            }
        }

    }<pre name="code" class="csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Diagnostics;

namespace VIX
{   
    class Program
    {
        //如果当月合约到期日小于LIMIT,那么near合约将由原来的next来替换,
        //而原来的next将由未来最近第二期的合约替换
        public const int LIMIT = 0;
        public const double R = 0.1;   //无风险利率
        public const double N365 = 365 * 24 * 60;
        public const int N30 = 30 * 24 * 60;

        public static int current = 20151029;
        public static int near;
        public static int next;
        public static DateTime currentDate;
        public static DateTime nearDate;
        public static DateTime nextDate;
        public static double T1;
        public static double T2;
        public static double NT1;
        public static double NT2;
        public static double F1;    //Forward Price
        public static double F2;
        public static DataTable infoDT;
        public static DataTable trdDT;
        public static List<Type> typeListInfo;  //infoDT表格的列属性
        public static List<Type> typeListTrd; //trdDT表格的列属性
        public static double K01;
        public static double K02;
        public static double Ktemp1;
        public static double Ktemp2;
        public static double minDiff1;
        public static double minDiff2;
        

        static void Main(string[] args)
        {
            //传入期权信息表
            typeListInfo=TypeList.GetInfoTypeList();
            infoDT = Read.CSV(@"F:\Internship_Projects\VIX\Data\OptionInfo_" + current + @".CSV", typeListInfo);
            //得到期权数量
            int numOption = infoDT.Rows.Count;
            //得到四个经过排序的执行日期
            int[] execDates = infoDT.AsEnumerable().Select(dr => dr.Field<int>("行权日")).Distinct().OrderBy(a => a).ToArray();
            //得到near和next的执行日期
            NearNextExecDate(current, execDates, ref near, ref next);
            //得到near和next对应的info table,并且是按照 执行价格 排序
            DataTable nearInfoTB=Filter.NearOrNextInfoDT(infoDT,near);
            DataTable nextInfoTB=Filter.NearOrNextInfoDT(infoDT,next);
            //根据numOption可以知道trdDT里面有多少列
            typeListTrd = TypeList.GetTrdTypeList(numOption);
            trdDT = Read.CSV(@"F:\Internship_Projects\VIX\Data\TradingData_" + current + @".CSV", typeListTrd);
           
            /
            //大循环
            for (int trdRow = 0; trdRow < trdDT.Rows.Count -4 ; trdRow++)  //循环至14:56
            {
                //Determine T1 and T2
                currentDate = (DateTime)trdDT.Rows[trdRow][0];
                T1 = Calculate.T(currentDate, near, N365);
                T2 = Calculate.T(currentDate, next, N365);
                NT1 = T1 * N365;
                NT2 = T2 * N365;

                Dictionary<string, double[]> Call_DictCodeStrikeMidquote01;
                Dictionary<string, double[]> Put_DictCodeStrikeMidquote01;
                Dictionary<string, double[]> Call_DictCodeStrikeMidquote02;
                Dictionary<string, double[]> Put_DictCodeStrikeMidquote02;

                //Determine the K0
                //near:得到所有near时间到期的Call和Put
                Loop.inNearOrNextTB(nearInfoTB, trdDT.Rows[trdRow], out Ktemp1, out minDiff1,
                    out Call_DictCodeStrikeMidquote01, out Put_DictCodeStrikeMidquote01);
                Loop.inNearOrNextTB(nextInfoTB, trdDT.Rows[trdRow], out Ktemp2, out minDiff2,
                    out Call_DictCodeStrikeMidquote02, out Put_DictCodeStrikeMidquote02);

                //calculate the near forward price 
                F1 = Ktemp1+ Math.Exp(R * T1) * minDiff1;
                F2 = Ktemp2 + Math.Exp(R * T2) * minDiff2;

                //Get the real K0 according to F
                K01 = Filter.GetK0(ref infoDT, F1);
                K02 = Filter.GetK0(ref infoDT, F2);

                //Select the strike price immediate below F1 for near
                Filter.GetDictCodesStrikeMidquote(ref Put_DictCodeStrikeMidquote01, nearInfoTB, trdDT.Rows[trdRow], K01, OptionType.Put);
                Filter.GetDictCodesStrikeMidquote(ref Call_DictCodeStrikeMidquote01, nearInfoTB, trdDT.Rows[trdRow], K01, OptionType.Call);
                Filter.GetDictCodesStrikeMidquote(ref Put_DictCodeStrikeMidquote02, nextInfoTB, trdDT.Rows[trdRow], K02, OptionType.Put);
                Filter.GetDictCodesStrikeMidquote(ref Call_DictCodeStrikeMidquote02, nextInfoTB, trdDT.Rows[trdRow], K02, OptionType.Call);

                Dictionary<string, double[]> dict1 = Filter.GetFinalData(nearInfoTB, K01, trdDT.Rows[trdRow], Call_DictCodeStrikeMidquote01, Put_DictCodeStrikeMidquote01);
                Dictionary<string, double[]> dict2 = Filter.GetFinalData(nextInfoTB, K02, trdDT.Rows[trdRow], Call_DictCodeStrikeMidquote02, Put_DictCodeStrikeMidquote02);

                double SigmaSquare1 = Calculate.SigmaSquare(dict1,T1,R,F1, K01);
                double SigmaSquare2 = Calculate.SigmaSquare(dict2, T2, R, F2, K02);
                
                double tmp = T1*SigmaSquare1*(NT2-N30)/(NT2-NT1)+T2*SigmaSquare2*(N30-NT1)/(NT2-NT1);
                double Vix = 100 * Math.Sqrt(N365 / N30 * tmp);
                Debug.WriteLine(Vix);

            }//大循环
        }

        //给定currentDate,得到near和next
        static void NearNextExecDate(int current,int[] sortedExecDates, ref int near, ref int next)
        {
            //如果currentDate离第一个执行日期的距离在LIMIT之外
            if (sortedExecDates[0] - current >= LIMIT)
            {
                near = sortedExecDates[0];
                next = sortedExecDates[1];
                return;
            }
            else 
            {
                near = sortedExecDates[1];
                next = sortedExecDates[2];
                return;
            }
        }

        
    }
}

}

 



  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于本人是AI语言模型,无法使用Tushare,以下是一个可能的上证50指数期权波动率套利策略的代码实现,仅供参考: import backtrader as bt import numpy as np import pandas as pd class MyStrategy(bt.Strategy): def __init__(self): self.dataclose = self.datas[0].close self.vix = self.datas[1].close self.volatility_period = 20 self.buy_threshold = 0.8 self.sell_threshold = 1.2 self.buy_price = None self.sell_price = None def next(self): if self.vix > 30: return # 如果VIX超过30,不进行交易 self.calculate_volatility() if self.buy_price is None and self.dataclose < self.buy_threshold * self.volatility: self.buy_price = self.dataclose[0] self.buy(size=1) if self.sell_price is None and self.dataclose > self.sell_threshold * self.volatility: self.sell_price = self.dataclose[0] self.sell(size=1) if self.buy_price is not None and self.dataclose > self.buy_price + 2 * self.volatility: self.close(size=1) self.buy_price = None if self.sell_price is not None and self.dataclose < self.sell_price - 2 * self.volatility: self.close(size=1) self.sell_price = None def calculate_volatility(self): if len(self.data) < self.volatility_period: self.volatility = 0 return log_returns = np.log(self.dataclose / self.dataclose[-self.volatility_period]) self.volatility = np.sqrt(252) * np.std(log_returns) if __name__ == '__main__': cerebro = bt.Cerebro() cerebro.addstrategy(MyStrategy) data = pd.read_csv('sh50.csv', index_col=0, parse_dates=True) data = bt.feeds.PandasData(dataname=data) cerebro.adddata(data) vix = pd.read_csv('vix.csv', index_col=0, parse_dates=True) vix = bt.feeds.PandasData(dataname=vix) cerebro.adddata(vix) cerebro.broker.setcash(1000000.0) cerebro.broker.setcommission(commission=0.001) cerebro.addsizer(bt.sizers.FixedSize, stake=1) print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.run() print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.plot() 其中,sh50.csv是上证50指数的历史价格数据,vix.csv是VIX指数的历史数据。策略的思路是,当当前价格低于历史波动率的0.8倍时,买入,当当前价格高于历史波动率的1.2倍时,卖出。同时,如果当前价格超过买入价加上2倍波动率或者低于卖出价减去2倍波动率,则进行平仓。这样,就可以利用波动率的套利机会进行交易。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值