利用螺纹钢期货对钢材采购成本做对冲_2020_10_24

由于钢材市场价格波动较大,因此公司在采购钢材时面临较大的风险。通过螺纹钢期货可以提前锁定钢材价格,从而为公司管理决策减少未知和不确定性。

import openpyxl
import pandas as pd
pd.set_option('display.max_columns', None)# 显示所有列
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression
plt.rcParams['font.sans-serif']=['simhei']#用于正常显示中文标签
plt.rcParams['axes.unicode_minus']=False#用于正常显示负号
import matplotlib
matplotlib.use('Agg')
import datetime,time
import numpy as np
import re
import os
list_type_iron=['20MM','12MM','8MM','average']#所有螺纹钢类型

#读取期货价格数据
data_future=pd.read_excel('原始数据/期货价格数据.xlsx',index_col='时间').ix[:,'收盘价']#读取期货数据

#读取钢铁价格数据
dict_type_price_iron={
    
    }#按照螺纹钢类型保存的钢铁价格数据字典
for type_iron in list_type_iron:
    data_price_iron=pd.read_excel('原始数据\钢铁价格数据.xlsx',sheet_name=type_iron,index_col='指标名称').dropna()
    dict_type_price_iron[type_iron]=data_price_iron


class HedgeFunc():

    def __init__(self,type_iron,periods_hedge,data_order):
        self.data_order = data_order
        self.ph=periods_hedge#对冲间隔天数
        self.ti=type_iron#对冲所用螺纹钢类型
        self.__data()
        self.__basic_parameters()

    #数据处理
    def __data(self):
        self.data_price_iron=dict_type_price_iron[self.ti]#获得指定类型的螺纹钢价格数据
        self.data_price_iron = self.__combine(self.data_price_iron, data_future,col='期货价格')  # 将期货价格拼接至钢铁价格后面

        self.data_future_diff=data_future.diff().dropna()#计算期货价格变动值
        self.data_price_iron_diff=self.data_price_iron.diff().dropna()#计算钢铁价格变动值
        self.data_price_diff=self.__combine(self.data_price_iron_diff,self.data_future_diff,col='期货价格变化')#将期货价格变化拼接至钢铁价格后面

    #定义基本变量
    def __basic_parameters(self):
        self.list_date = list(self.data_price_iron.index)#钢铁价格数据有效日期,用于全局使用的日期列表
        self.list_date_all = pd.date_range(str(data_future.index.min()), str(data_future.index.max())).astype(str).tolist()  #所有日期,用于检索订单日数据
        self.cities=list(data_price_iron.columns)#所有城市名称
        self.ratio_promise=0.05#期货合约价值保证金比例
        self.rate=0.04#保证金利息率
        self.T_hedge=15#计算对冲比率的期间

    #绘制两个变量的散点图
    def __scatter_diff(self,y1,y2,xlabel='',ylabel=''):
        plt.figure(figsize=(10,10))
        plt.scatter(y1,y2)
        plt.xlabel=xlabel
        plt.ylabel=ylabel

    #将时间序列data2按照时间维度拼接到数据框data1中,缺失的地方用data2中下一个数补上
    def __combine(self,data1,data2,col):
        """
        self.data_price_diff=self.__combine(self.data_price_iron_diff,self.data_future_diff,col='期货价格变化')#将期货价格变化拼接至钢铁价格后面
        """
        data1[col]=0.000000
        for date in data1.index:
            if date not in data2.index:
                data1.ix[date,col]=data2[data2[data2.index > date].index[0]]
            else:
                data1.ix[date,col]=data2[date]
        return data1

    #通过最小二乘法计算数据data的T期最佳对冲比率h
    def __h(self,data):
        """
        self.__h(self.data_price_diff,40)
        """
        T=self.T_hedge
        data_h=data[self.cities]#生成一个表格用于存放生成的回归系数
        data.index=range(len(data))
        for i in data.index[T:]:
            y=data.ix[(i-T):i,'期货价格变化']
            for city in self.cities:
                x=data.ix[(i-T):i,city]
                coef=self.__OLS(x,y)#回归系数
                data_h.ix[i,city]=coef
                print([i,city])
        return data_h

     #通过最小二乘法计算两个变量的一元回归系数
    def __OLS(self,x,y):
            """
            self.__OLS(data_price_diff['期货价格'],data_price_diff['杭州'])
            """
            x=np.array(x).reshape(-1, 1)
            y=np.array(y).reshape(-1, 1)
            model = LinearRegression(fit_intercept=False).fit(x, y)  # 拟合模型
            return   model.coef_[0,0]  # 查看拟合系数

    #将采购日现货价格添加至订单数据
    def __add_future(self):
        self.data_order[self.ti + '现货价格'] = 0.000000
        for i in self.data_order.index:
            date = self.data_order.ix[i, '结算单日期']  # 采购日期
            if date not in self.list_date:#如果日期不再目标列表,将取下一个值
                date=self.data_price_iron.ix[self.data_price_iron.index>date].index[0]
            city = self.data_order.ix[i, '采购地']
            self.data_order.ix[i,self.ti + '现货价格']=self.data_price_iron.ix[date,city]
        #计算每笔订单的采购数量(吨)
        self.data_order[self.ti + '采购吨数'] =self.data_order['税前结算金额']/self.data_order[self.ti + '现货价格']

    #将订单日的现货价格,期货价格,对冲比率添加至订单数据data_order
    def __add_order(self):
        """
        self.__add_hedge(data_order)
        """
        self.data_order[self.ti + '订单日现货价格' + str(self.ph)] = 0.000000
        self.data_order[self.ti + '订单日期货价格' + str(self.ph)] = 0.000000
        self.data_order[self.ti + '订单日对冲比率' + str(self.ph)]=0.0000

        for i in self.data_order.index:
            date=self.data_order.ix[i,'结算单日期']#采购日期
            date_order=self.list_date_all[self.list_date_all.index(date)-self.ph]#订单日日期

            if date_order not in self.list_date:#如果日期不再目标列表,将取下一个值
                date_order=self.data_price_iron.ix[self.data_price_iron.index>date_order].index[0]

            city = self.data_order.ix[i, '采购地']
            self.data_order.ix[i, self.ti + '订单日期货价格' + str(self.ph)] = self.data_price_iron.ix[date_order, '期货价格']  # 将螺纹钢订单日期货价格加入订单数据表
            self.data_order.ix[i, self.ti + '订单日现货价格' + str(self.ph)] = self.data_price_iron.ix[date_order, city]#将螺纹钢订单日现货价格加入订单数据表
            self.data_order.ix[i, self.ti + '订单日对冲比率' + str(self.ph)]=self.h.ix[date_order,city]#将订单日对冲比率加入订单数据表格

            print([date, city])

        return self.data_order

    #计算对冲详细过程
    def __hedge_data(self):
        #价格变化幅度
        self.data_order[self.ti + '价格变化幅度' + str(self.ph)]=(self.data_order[self.ti + '现货价格']-self.data_order[self.ti + '订单日现货价格' + str(self.ph)])/self.data_order[self.ti + '订单日现货价格' + str(self.ph)]
        
        self.data_order[self.ti + '期货合约数量' + str(self.ph)]=self.data_order[self.ti + '订单日对冲比率' + str(self.ph)]*self.data_order[self.ti + '采购吨数']#计算所需期货合约数量
        self.data_order[self.ti + '期货合约总额' + str(self.ph)] =self.data_order[self.ti + '期货合约数量' + str(self.ph)]*self.data_order[self.ti + '订单日期货价格' + str(self.ph)]#
        self.data_order[self.ti + '保证金' + str(self.ph)] =self.data_order[self.ti + '期货合约总额' + str(self.ph)]*self.ratio_promise
        self.data_order[self.ti + '保证金利息' + str(self.ph)] =self.data_order[self.ti + '保证金' + str(self.ph)]*self.rate*self.ph/365

        self.data_order['期货价格变化' + str(self.ph)]=self.data_order['期货价格']-self.data_order[self.ti + '订单日期货价格' + str(self.ph)]
        self.data_order['期货收益' + str(self.ph)]=self.data_order['期货价格变化' + str(self.ph)]*self.data_order[self.ti + '期货合约数量' + str(self.ph)]
        self.data_order[self.ti + '总收益' + str(self.ph)]=self.data_order['期货收益' + str(self.ph)]-self.data_order[self.ti + '保证金利息' + str(self.ph)]

        self.data_order[self.ti + '真实结算金额' + str(self.ph)]=self.data_order['税前结算金额']-self.data_order[self.ti + '总收益' + str(self.ph)]#用期货的收益来抵消购买成本
        self.data_order[self.ti + '真实单价' + str(self.ph)] =self.data_order[self.ti + '真实结算金额' + str(self.ph)]/self.data_order[self.ti + '采购吨数']
        
        #对冲幅度
        self.data_order[self.ti + '对冲幅度' + str(self.ph)] =(self.data_order[self.ti + '真实单价' + str(self.ph)]-self.data_order[self.ti + '现货价格'])/self.data_order[self.ti + '现货价格']
        #data_order.fillna(method='bfill',inplace=True)

    #选取指定范围的数据
    def select_range(self,data,col,quantile,type='上'):
        """
        col:列标题;quantile:分位数;type:大于或小于
        col='20MM对冲幅度60'
        """
        number_quantile=np.quantile(data[col].tolist(),quantile)
        if type=='上':return data.ix[data[col]>number_quantile]
        elif type=='下':return data.ix[data[col]<number_quantile]

    #对冲结果分析
    def results_analyse(self,data):

        #计算在价格上涨的日子成功降价的比率
        self.data_up=data.ix[data[self.ti + '订单日现货价格' + str(self.ph)]<data[self.ti + '现货价格']]#价格上涨的样本数据
        self.len_up=len(self.data_up)#价格上涨的样本数据的个数
        self.data_up_sucess=self.data_up.ix[self.data_up[self.ti + '真实单价' + str(self.ph)] < self.data_up[self.ti + '现货价格']]#在价格上涨中对冲成功的数据
        self.len_up_sucess=len(self.data_up.ix[self.data_up[self.ti + '真实单价' + str(self.ph)]<self.data_up[self.ti + '现货价格']])#价格上涨的样本数据对冲成功的个数
        self.prob_up_sucess=self.len_up_sucess/self.len_up

        # 计算在价格下降的日子成功降价的比率
        self.data_down=data.ix[data[self.ti + '订单日现货价格' + str(self.ph)]>data[self.ti + '现货价格']]#价格下降的样本数据
        self.len_down=len(self.data_down)#价格下降的样本数据的个数
        self.data_down_sucess = self.data_down.ix[self.data_down[self.ti + '真实单价' + str(self.ph)] > self.data_down[self.ti + '现货价格']]  # 在价格上涨中对冲成功的数据
        self.len_down_sucess=len(self.data_down.ix[self.data_down[self.ti + '真实单价' + str(self.ph)]>self.data_down[self.ti + '现货价格']])#价格下降的样本数据对冲成功的个数
        self.prob_down_sucess=self.len_down_sucess/self.len_down
        
        return pd.DataFrame([[self.len_up,self.len_up_sucess,self.prob_up_sucess],
                             [self.len_down,self.len_down_sucess,self.prob_down_sucess]],columns=['个数','成功个数','成功比率'],index=['上涨','下降'])


    #展示常用的数据
    def print_summary(self,data):
        self.cols_summary=[self.ti + '订单日现货价格' + str(self.ph),self.ti + '现货价格',self.ti + '真实单价' + str(self.ph),
                           self.ti + '订单日期货价格' + str(self.ph),'期货价格',self.ti + '订单日对冲比率' + str(self.ph)]
        return data[self.cols_summary]

    #绘制对冲结果图:输入任意个列标题名称,绘制图形
    def  plot_hedge(self,data,title,*cols):
        """
        col1=self.ti + '订单日现货价格' + str(self.ph)
        col2=self.ti + '现货价格'
        col3=self.ti + '真实单价' + str(self.ph)
        self.plot_hedge(self.data_up_sucess,col1,col2,col3)
        """
        plt.figure(figsize=(20, 8))
        for col in cols:
            plt.plot(data[col], label=col)
        plt.legend()
        plt.xticks(rotation=270)
        plt.xlabel('订单号',fontsize=10)
        plt.ylabel('价格',fontsize=10)
        plt.grid(alpha=0.3,color='g')
        plt.title(title,fontsize=15)
        plt.savefig('生成数据/生成图片/{}.jpg'.format(title))

    # 计算不同的对冲比率期限下的成功对冲概率
    @classmethod
    def select_T_periods(cls):
        data_T_hedge = pd.DataFrame(index=['上涨个数', '下跌个数', '上涨成功个数', '下跌成功个数', '上涨时期成功概率', '下跌时期成功概率'])
        for T_hedge in np.arange(10, 90, step=10):
            hedge = cls('20MM', 60)
            hedge.T_hedge = T_hedge
            hedge.run()
            data_prob = hedge.data_prob
            data_T_hedge[T_hedge] = [data_prob.ix[0, 0], data_prob.ix[1, 0], data_prob.ix[0, 1], data_prob.ix[1, 1],
                                     data_prob.ix[0, 2], data_prob.ix[1, 2]]
        data_T_hedge.to_excel('生成数据/各种对冲比率期限下的对冲成功概率.xlsx')


    #运行函数
    def run(self):
        self.h = self.__h(self.data_price_diff)
        self.__add_future()
        self.__add_order()
        self.__hedge_data()
        self.data_prob=self.results_analyse(self.data_order)#在价格上涨时期和下跌时期对冲成功的结果分析

    #绘制上涨时期或下跌时期的对冲结果图
    def plot_up_down(self):
        #绘制在价格上涨时期成功对冲的订单图
        col1=self.ti + '订单日现货价格' + str(self.ph)
        col2=self.ti + '现货价格'
        col3=self.ti + '真实单价' + str(self.ph)
        col4=self.ti + '订单日期货价格' + str(self.ph)
        col5='期货价格'
        col=self.ti + '对冲幅度' + str(self.ph)

        data_up = self.select_range(self.data_up_sucess,col=col, quantile=0.1, type='下')
        data_down = self.select_range(self.data_down_sucess,col=col, quantile=0.9, type='上')
        self.plot_hedge(data_up,'在价格上涨时期{}{}对冲图'.format(self.ti,self.ph),col1,col2,col3,col4,col5)
        self.plot_hedge(data_down, '在价格下跌时期{}{}对冲图'.format(self.ti,self.ph), col1, col2, col3,col4,col5)


    #各种示例
    def example(self):
        self.__scatter_diff(self.data_price_diff['期货价格变化'],self.data_price_diff['杭州'],'期货价格变化','杭州')#绘制杭州钢铁价格变化和期货价格变化之间散点图
        self.__scatter_diff(self.data_price_diff['期货价格变化'], self.data_price_diff['南京'], '期货价格变化', '南京')
        self.__OLS(self.data_price_diff['期货价格变化'],self.data_price_diff['杭州'])
        self.h=self.__h(self.data_price_diff,40)
        self.h = self.__h(self.data_price_diff)
        self.__add_future()
        self.__add_order()
        self.__hedge_data()
        self.data_prob=self.results_analyse(self.data_order)#在价格上涨时期和下跌时期对冲成功的结果分析
        self.plot_up_down()


def main():
    for type_iron in list_type_iron:
        for periods_hedge in range(30, 100, 10):
            # 读取订单数据
            data_order = pd.read_excel('原始数据/订单数据.xlsx', sheet_name='20MM')[
                ['结算单日期', '税前结算金额', '采购地', '星期', '期货价格']]
            data_order = data_order.ix[data_order['税前结算金额'] > 1]

            hedge = HedgeFunc(type_iron, periods_hedge,data_order)
            hedge.run()

            pd.DataFrame().to_excel('生成数据\对冲结果{}{}.xlsx'.format(type_iron, periods_hedge))  # 创建空白表格,准备填入数据
            wb = openpyxl.load_workbook('生成数据\对冲结果{}{}.xlsx'.format(type_iron, periods_hedge))  # 将空白表格指定为wb
            writer = pd.ExcelWriter('生成数据\对冲结果{}{}.xlsx'.format(type_iron, periods_hedge),
                                    engine='openpyxl')  # 准备往空白表格写入数据
            writer.book = wb  # 执行写入目标为空白表格

            hedge.data_order.to_excel(writer, sheet_name='{}螺纹钢{}对冲总结果'.format(type_iron, periods_hedge))  # 将总对冲结果填入
            hedge.data_up.to_excel(writer, sheet_name='{}螺纹钢{}上涨时期数据'.format(type_iron, periods_hedge))  # 将总对冲结果填入
            hedge.data_down.to_excel(writer,
                                     sheet_name='{}螺纹钢{}下跌时期数据'.format(type_iron, periods_hedge))  # 将总对冲结果填入
            hedge.data_up_sucess.to_excel(writer, sheet_name='{}螺纹钢{}上涨时期对冲成功数据'.format(type_iron,
                                                                                        periods_hedge))  # 将总对冲结果填入
            hedge.data_down_sucess.to_excel(writer, sheet_name='{}螺纹钢{}下跌时期对冲成功数据'.format(type_iron,
                                                                                          periods_hedge))  # 将总对冲结果填入
            hedge.data_prob.to_excel(writer,
                                     sheet_name='{}螺纹钢{}对冲结果统计'.format(type_iron, periods_hedge))  # 将总对冲结果填入
            hedge.print_summary(hedge.data_up_sucess).to_excel(writer,
                                                               sheet_name='{}螺纹钢{}上涨时期对冲成功简要数据'.format(type_iron,
                                                                                                       periods_hedge))  # 将总对冲结果填入
            hedge.print_summary(hedge.data_down_sucess).to_excel(writer,
                                                                 sheet_name='{}螺纹钢{}下跌时期对冲成功简要数据'.format(type_iron,
                                                                                                         periods_hedge))  # 将总对冲结果填入

            writer.save()
            writer.close()

            hedge.plot_up_down()
            print(type_iron, periods_hedge)

if __name__ =='__main__':
        main()





猜你喜欢

转载自blog.csdn.net/weixin_45590329/article/details/109265829