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