钢材生产流程大致分为三个阶段,分别为炼铁、炼钢和轧钢,生产成本主要包括原料成本、能源成本、人工成本、折旧和财务成本等。从原材料上来看, 生产1吨螺纹钢大致需要1.6吨铁矿石和0.5吨焦炭。因此,我们可以通过期货市场来大致模拟钢厂的生产过程,通过结合相关品种在期货市场的利润以及现货市场供需基本面数据,把握品种间的套利机会。通过成本估算,当前阶段:
螺纹钢成本≈1.6×铁矿石+0.5×焦炭+加工成本
利润≈螺纹钢期货价格-螺纹钢成本
炼铁、炼钢和轧钢三个阶段的总加工费大概在1100元左右。下图是2013.10-2016.7虚拟钢厂利润走势,在-200~300的范围内波动,利润中枢在100左右。
从价格走势图来看,利润走势不可能持续维持在高位或低位。这是因为,当利润达到极高值时,一方面钢厂会加速生产,供给增加则价格降低,另一方面铁矿石,焦炭等原材料价格会上涨,从而压缩利润空间;当利润达到极低值时,一方面钢厂会因持续亏损而减产,产量减少将支撑价格,另一方面上游企业议价能力更弱,原材料价格承压走低,从而使利润逐渐回升。当盘面利润超过300元时,可以做空利润,即卖螺纹,买铁矿石和焦炭,当盘面利润低于200元时,又可以做多利润,即买螺纹,卖铁矿石和焦炭。
首先,从产业结构计算产出比,比如螺纹:铁矿:焦炭=1吨:1.6吨:0.5吨;其次,按照合约规定的实际数量的1:1来确定,当前螺纹:铁矿:焦炭=1:10:10(螺纹期货合约为10吨/手,铁矿和焦炭期货合约均为100吨/手。最后,确定套利比例也需要考虑各品种的波动率的不同。综合上述因素,这样大致可以确定螺纹、铁矿、焦炭三者的手数配比为20:3:1。初始资金设为15w。
合约选取:
螺纹,铁矿,焦炭主力合约
策略逻辑:
当盘面利润高于300时,做空螺纹,做多铁矿石与焦炭;当盘面利润低于100时,若螺纹有空仓,则平掉所有仓位。
当盘面利润低于-200时,做多螺纹,做空铁矿石与焦炭;当盘面利润高于100时,若螺纹有多仓,则平掉所有仓位。
当主力切换时,平掉所有仓位
初始资金:
30w
回测时间:
自铁矿石品种上市以来
universe = [‘RBM0’, ‘IM0’, ‘JM0’] # 策略期货合约
start = ‘2013-11-01’ # 回测开始时间
end = ‘2016-11-18’ # 回测结束时间
capital_base = 300000 # 初试可用资金
refresh_rate = 1 # 调仓周期
freq = ‘d’ # 调仓频率:m-> 分钟;d-> 日
marg
diff = deque([], maxlen=10)
def initialize(futures_account): # 初始化虚拟期货账户,一般用于设置计数器,回测辅助变量等。
futures_account.symbol0 = ‘RB1405’
futures_account.symbol1 = ‘I1405’
futures_account.symbol2 = ‘J1405’
pass
def handle_data(futures_account): # 回测调仓逻辑,每个调仓周期运行一次,可在此函数内实现信号生产,生成调仓指令。
long_position_0 = futures_account.position.get(futures_account.symbol0, dict()).get(‘long_position’, 0)
short_position_0 = futures_account.position.get(futures_account.symbol0, dict()).get(‘short_position’, 0)
long_position_1 = futures_account.position.get(futures_account.symbol1, dict()).get('long_position', 0)
short_position_1 = futures_account.position.get(futures_account.symbol1, dict()).get('short_position', 0)
long_position_2 = futures_account.position.get(futures_account.symbol2, dict()).get('long_position', 0)
short_position_2 = futures_account.position.get(futures_account.symbol2, dict()).get('short_position', 0)
if get_symbol(universe[0]) != futures_account.symbol0 or get_symbol(universe[1]) != futures_account.symbol1 or get_symbol(universe[2]) != futures_account.symbol2:
if long_position_0 != 0:
# print futures_account.current_date, '主力更换, 平仓'
order(futures_account.symbol0, -long_position_0, 'close')
order(futures_account.symbol1, short_position_1, 'close')
order(futures_account.symbol2, short_position_2, 'close')
if short_position_0 != 0:
# print futures_account.current_date, '主力更换, 平仓'
order(futures_account.symbol0, short_position_0, 'close')
order(futures_account.symbol1, -long_position_1, 'close')
order(futures_account.symbol2, -long_position_2, 'close')
futures_account.symbol0 = get_symbol(universe[0])
futures_account.symbol1 = get_symbol(universe[1])
futures_account.symbol2 = get_symbol(universe[2])
else:
futures_account.symbol0 = get_symbol(universe[0])
futures_account.symbol1 = get_symbol(universe[1])
futures_account.symbol2 = get_symbol(universe[2])
close_hist0 = get_symbol_history(symbol=futures_account.symbol0, field=['closePrice'], time_range=1)
close_hist1 = get_symbol_history(symbol=futures_account.symbol1, field=['closePrice'], time_range=1)
close_hist2 = get_symbol_history(symbol=futures_account.symbol2, field=['closePrice'], time_range=1)
close0 = np.array(close_hist0[futures_account.symbol0]['closePrice'])[-1]
close1 = np.array(close_hist1[futures_account.symbol1]['closePrice'])[-1]
close2 = np.array(close_hist2[futures_account.symbol2]['closePrice'])[-1]
if short_position_0 == 0 and close0 - 1.6 * close1 - 0.5 * close2 - 1100 > 300:
# print futures_account.current_date, '做空螺纹'
# print close0 - 1.6 * close1 - 0.5 * close2 - 1100
order(futures_account.symbol0, -20, 'open')
order(futures_account.symbol1, 3, 'open')
order(futures_account.symbol2, 1, 'open')
if long_position_0 == 0 and close0 - 1.6 * close1 - 0.5 * close2 - 1100 < -250:
# print futures_account.current_date, '做多螺纹'
# print close0 - 1.6 * close1 - 0.5 * close2 - 1100
order(futures_account.symbol0, 20, 'open')
order(futures_account.symbol1, -3, 'open')
order(futures_account.symbol2, -1, 'open')
# 平仓
if close0 - 1.6 * close1 - 0.5 * close2 - 1100 < 100:
if short_position_0 != 0:
# print futures_account.current_date, '平仓'
# print close0 - 1.6 * close1 - 0.5 * close2 - 1100
order(futures_account.symbol0, short_position_0, 'close')
if long_position_1 != 0:
order(futures_account.symbol1, -long_position_1, 'close')
if long_position_2 != 0:
order(futures_account.symbol2, -long_position_2, 'close')
if close0 - 1.6 * close1 - 0.5 * close2 - 1100 > 100:
if long_position_0 != 0:
# print futures_account.current_date, '平仓'
# print close0 - 1.6 * close1 - 0.5 * close2 - 1100
order(futures_account.symbol0, short_position_0, 'close')
if short_position_1 != 0:
order(futures_account.symbol1, -long_position_1, 'close')
if short_position_2 != 0:
order(futures_account.symbol2, -long_position_2, 'close')