商品期货基本面分析交易方法

一、摘要

在期货交易市场,无论是私募机构还是普通散户,争论技术分析和基本面分析谁好谁坏,从来就没有停止过。技术分析者认为价格已经包含了一切,相信未来价格还以趋势方式演变,只关心图表上价格行为本身的变化,判断它能卖多少钱。基本面分析者认为真正价值最终将会反映在价格上,并不需要关心短期价格走势,更多的是分析影响价格背后的因素,判断它值多少钱。

二、基本面分析的难点

之前听过这样一句话:“散户可以完全无视基本面分析,只需要关注技术分析即可。”当时不以为然,但随着时间的推移,和大量实盘的磨砺,现在回过头看,也渐渐接受了这种观点,因为传统的基本面分析相对于散户来说,门槛真的很高,想要分析某些事合力后会发生什么结果,最起码获取与之关联的数据是全面的、准确的。否则,再怎么分析,也只是片面的。

影响期货基本面分析的因素

  • 宏观
    • 宏观政策
    • 产业政策
    • 政治因素
    • 外汇汇率
    • 经济周期
    • 货币政策
  • 品种
    • 升水贴水
    • 供需关系
    • 商品库存
    • 产业利润
  • 其他
    • 季节因素
    • 天气因素
    • 新闻事件
    • 市场情绪

如上面这个列表,与商品期货基本面分析有关的三大因素,林林总总多达数十项,往细了分,更有几十项之多,并且这些数据是在不停变化的。单个散户想要获取这些庞大的数据已经是力所不及的事了,更不用提客观分析。所以从这一点来讲,在机构和散户共存的市场中,散户在起跑线上就已经处于劣势中。尽管机构也不是近乎完美,但相对于散户,有更多的优势去获取更多更准确的信息,以及素质更高的分析团队。所以,在只论输赢的交易市场,散户的赢面很小。那么有没有合适散户的基本面分析法呢?

三、期货基本分析的核心

其实,期货的基本面分析并不是想象中那么难,只需要抓住基本面分析核心要素,就能剥丝抽茧从错综复杂的信息中找出规律。我们知道影响商品期货的三大因素中包括:宏观、品种、其他。宏观经济数据复杂多变,每天每时每刻,地球上有太多的经济数据公布,各国政界、央行、投行,官方的和非官方的。除了政治和经济危机外,宏观分析是聊天的好材料,实用性不大。美国著名的基金管理专家彼得·林奇曾发表看法:“我每年花在经济大势上的分析时间不超出十五分钟”。

另外,在季节因素、天气因素、新闻事件、市场情绪中,很多都是突发事件,本身就是无法分析预测的。所以散户只需要把精力放在品种上即可,因为期货和现货的价格都是公开的,可以计算出升水还是贴水。现货的库存数据在一些网站还是比较容易获取的,可以预判出相对的供需关系等等,从而判断期货未来的大概价值。

升水贴水

同样一个商品品种,在现货市场与期货市场的价格差,叫做基差。如果期货价格大于现货价格,我们称之为期货升水;如果期货价格小于现货价格,我们称之为期货贴水。无论是升水还是贴水,随着交割日期的临近,现货价格与期货价格都会趋于一致,一种是期货向现货回归,另一种是现货向期货回归。从期货市场很难判断基差究竟会以哪种方式回归,所以只能从现货市场中寻找蛛丝马迹。

供需关系
影响商品现货价格的因素虽然有很多,但最终大都体现在供需关系上。如果买者多于卖者,价格就会上涨;如果卖者多于买者,价格就会下跌。国内的商品期货大致上可以分为:农产品和工业品。期货圈子中流传着这样一句话:“农产品看供给,工业品看需求。”农产品是刚需,需求是相对稳定的,决定价格主要看供给;工业品是下游需求带动的,再者国内基本都产能过剩,决定价格主要看需求。

虽然,在实际操作中我们很难获取工业品的需求数据,也很难计算出农产品的供给数据。但是价格波动依存于供给与需求的相互作用,这种相互作用的结果就是库存。如果库存处于低位,说明市场供不应求,需求的力量大于供给的力量,未来价格看涨;如果库存处于高位,说明市场供大于求,供给的力量大于需求的力量,未来价格看跌。

仓单
所谓的仓单就是交易所的交割仓库入库现货后开具的标准仓单,它反映的是交易所公布的库存数量。当期货价格较高时,现货商就是注册仓单然后在市场上销售,所以根据这个原理我们可以反推出在期货中的交易方向。

  • 期货多头:如果仓单大量减少,说明期货价格低于现货价格,应该做多。
  • 期货空头:如果仓单大量增加,说明期货价格高于现货价格,应该做空。

另外,还可以利用仓单来判断库存。仓单既可以注册也可以注销,当期货主力想要价格上涨时,会把持有的注册仓单注销掉,改变交易所公布的库存数量,来达到交割货物不足的假象,进而影响期货价格上涨的预期。当期货主力想要价格下跌时,会注册仓单,造成交割货物增多的假象,使得被动影响期货价格下跌。


到这里,基本面分析三大因素:库存、基差、仓单就已经凑齐了,有些做基本面分析的朋友可能还会加上产业利润、技术分析等等,增加窥视市场的维度,理论上两者相加是大于二的,因为能知道越多信息,越多的角度去观察市场,才能做出更好的决策。那么我们的基本面交易策略可以为一下条件:

  • 多头:贴水 + 低库存 + 仓单减少
  • 空头:升水 + 高库存 + 仓单增加

四、获取数据

接下来,我们就利用发明者量化交易平台,来获取这些数据吧!

'''backtest
start: 2019-01-01 00:00:00
end: 2019-08-09 00:00:00
period: 1d
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''


# 导入库
import requests
import re
from bs4 import BeautifulSoup
import time
import datetime
import random
import json
import threading


# 请求头文件
request_headers = {
    'Connection': 'keep-alive',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'accept-encoding': 'gzip, deflate',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
    'Referer': 'http://www.100ppi.com/sf2/day-2019-01-02.html'
}


# ip代理
proxies = {
    'http': 'http://182.35.82.128:9999', 'https': 'https://182.35.82.128:9999'
}


# 全局变量
diff_data = 0  # 存储基差数据
reserve_data = 0  # 存储库存数据
receipt_data = 0  # 存储仓单数据
profit_data = 0  # 存储虚拟利润数据


# 日期转时间戳
def to_timestamp(date_str):
    times = date_str + " 00:00:00"
    time_array = time.strptime(times, "%Y-%m-%d %H:%M:%S")
    return int(round(time.mktime(time_array) * 1000))


# 返回日期数组
def date_arr(year, month, day):
    begin = datetime.date(year, month, day)
    end = datetime.date.today()
    arr = []
    for i in range((end - begin).days + 1):
        day = begin + datetime.timedelta(days=i)
        arr.append([str(day).replace('-', ''), str(day),
                    day.weekday() + 1, to_timestamp(str(day))])
    return arr


# 获取基差
def spot_futures_diff_data(date, futures_name):
    global diff_data
    url = f"http://www.100ppi.com/sf2/day-{date}.html"  # 获取生意社的基差数据
    try:
        url_text = requests.get(
            url, headers=request_headers).text
    except BaseException:
        Log('获取基差数据失败')
        return int(diff_data)
    soup = BeautifulSoup(url_text, "html5lib")
    if len(soup.select("#fdata")) > 0:
        results = soup.select("#fdata")[0]
        for i in results.find_all('tr'):
            if len(i.find_all('td', text=futures_name)) > 0:
                data = i.find_all('font')[0].text
                if data is not None:
                    diff_data = data
    return int(diff_data)


# 获取库存
def spot_reserve_data(date, futures_name):
    global reserve_data
    # 获取上期所的库存数据
    url = f'http://www.shfe.com.cn/data/dailydata/{date}weeklystock.dat'
    try:
        url_text = requests.get(url, headers=request_headers).text
    except BaseException:
        print('获取库存数据失败')
        return reserve_data
    total = 0
    count = 0
    if url_text[0] == '{':
        for i in json.loads(url_text)['o_cursor']:
            if futures_name in i['VARNAME']:
                if '合计' not in i['WHABBRNAME'] and '总计' not in i['WHABBRNAME']:
                    try:
                        inventory = int(i['WHSTOCKS'])
                    except BaseException:
                        return reserve_data
                    if inventory > 0:
                        total = total + inventory
                        count = count + 1
        if count > 0:
            reserve_data = int(total / count)
    return reserve_data


# 获取仓单
def spot_receipt_data(date, futures_name):
    global receipt_data
    # 获取上期所的仓单数据
    url = f'http://www.shfe.com.cn/data/dailydata/{date}dailystock.dat'
    try:
        url_text = requests.get(url, headers=request_headers).text
    except BaseException:
        print('获取库存数据失败')
        return receipt_data
    total = 0
    count = 0
    if url_text[0] == '{':
        for i in json.loads(url_text)['o_cursor']:
            if futures_name in i['VARNAME']:
                if '合计' not in i['WHABBRNAME'] and '总计' not in i['WHABBRNAME']:
                    try:
                        inventory = int(i['WRTWGHTS'])
                    except BaseException:
                        return receipt_data
                    if inventory > 0:
                        total = total + inventory
                        count = count + 1
        if count > 0:
            receipt_data = int(total / count)
    return receipt_data


# 虚拟利润
def get_profit_data():
    global profit_data
    profit_data = profit_data + random.randint(-100, 150)
    return profit_data


# 程序入口
def main():
    # threading.Thread(target=spot_futures_diff_arr, args=('天然橡胶',)).start()
    # threading.Thread(target=commodity_inventory_arr, args=('天然橡胶',)).start()
    # threading.Thread(target=commodity_warehouse_receipt_arr, args=('天然橡胶',)).start()

    # 基差图表
    cfgA = {
        "extension": {
            "layout": 'single',
            "col": 6,
            "height": "500px",
        },
        "title": {
            "text": "基差图表"
        },
        "xAxis": {
            "type": "datetime"
        },
        "series": [{
            "name": "基差",
            "data": [],
        }]
    }

    # 库存图表
    cfgB = {
        "extension": {
            "layout": 'single',
            "col": 6,
            "height": "500px",
        },
        "title": {
            "text": "库存图表"
        },
        "xAxis": {
            "type": "datetime"
        },
        "series": [{
            "name": "库存",
            "data": [],
        }]
    }

    # 仓单图表
    cfgC = {
        "extension": {
            "layout": 'single',
            "col": 6,
            "height": "500px",
        },
        "title": {
            "text": "仓单图表"
        },
        "xAxis": {
            "type": "datetime"
        },
        "series": [{
            "name": "仓单",
            "data": [],
        }]
    }

    # 虚拟利润图表
    cfgD = {
        "extension": {
            "layout": 'single',
            "col": 6,
            "height": "500px",
        },
        "title": {
            "text": "虚拟利润图表"
        },
        "xAxis": {
            "type": "datetime"
        },
        "series": [{
            "name": "利润",
            "data": [],
        }]
    }
    LogReset()  # 清除日志
    chart = Chart([cfgA, cfgB, cfgC, cfgD])  # 创建图表
    chart.reset()  # 初始清空图表
    for i in date_arr(2018, 1, 1):  # 从2018年1月1日开始获取历史数据
        diff = spot_futures_diff_data(i[1], '天然橡胶')  # 获取基差数据
        reserve = spot_reserve_data(i[0], '天然橡胶')  # 获取库存数据
        receipt = spot_receipt_data(i[0], '天然橡胶')  # 获取仓单数据
        profit = get_profit_data()  # 虚拟利润
        if diff != 0 and reserve != 0 and receipt != 0:  # 如果所有的数据都不为0
            chart.add(0, [i[3], diff])  # 绘制基差数据
            chart.add(1, [i[3], reserve])  # 绘制库存数据
            chart.add(2, [i[3], receipt])  # 绘制仓单数据
            chart.add(3, [i[3], profit])  # 绘制虚拟利润数据
            chart.update([cfgA, cfgB, cfgC, cfgD])  # 重置图表配置
            time.sleep(1)  # 休眠1秒
            Log(f'基差:{diff} 库存:{reserve} 仓单:{receipt} 日期:{i[1]}')  # 输出信息到日志

完整的代码已经分享到发明者量化交易平台 https://www.fmz.com/strategy/161412 可以直接复制策略无需设置,直接创建机器人即可。

五、实盘运行

六、总结

回到本篇的开场,基本面分析和技术分析并不存在孰优孰劣,它们探究的是同一个市场,只是站的角度不同。没有人可以仅凭一个角度分析,就能窥视市场全部。我相信两者相加是大于二的,因为能知道越多信息,越多的角度去观察市场,才能做出更好的决策。

你可能感兴趣的:(商品期货基本面分析交易方法)