VeighNa量化社区
你的开源社区量化交易平台
Member
avatar
加入于:
帖子: 419
声望: 171

CTA策略的交易活动包含哪些?

CTA策略的交易活动可以使用下面这些来描述:

  • 策略被接受的停止单(如果使用了停止单);
  • 策略被接受的委托单;
  • 策略收到的成交单;
  • 策略的合约持仓;
  • 策略的权益统计。

CTA策略的交易活动的呈现形式

  • 委托单以粉色的实心上箭头(买开)和空心下箭头(卖平)标识,蓝色的实心下箭头(卖开)和空心上箭头(买平)来标识,标注在发生K线的接受时刻。因为停止单实际上是转化为合约当时的涨停价或跌停价的委托单,所以停止单也是以限价委托单的形式来标注的;
  • 成交单以红色的实心上箭头(买开)和空心下箭头(卖平)标识,绿色的实心下箭头(卖开)和空心上箭头(买平)来标识,标注在发生K线的接受时刻;
  • 策略持仓以标题变量的方式显示在主题的标题栏中,随光标所在K线的变化而变化。包含:多头累积持仓,空头持仓,以及在当根K线时浮动盈亏,冻结仓位等信息;
  • 策略的权益统计也是以标题变量的方式显示在主题的标题栏中,随光标所在K线的变化而变化。包含策略自创建以来的权益变化信息。
  • 所有在主图标题栏中的显示信息,可以在策略中配置的,以便只显示你感兴趣的标题项。

几个概念:

  • 一次完整交易:策略的持仓从零开始到非零,再从非零仓位变成零仓位的过程。
  • 策略权益:初始分配资金+各次已完成交易的盈亏+未完成交易的浮动盈亏。
  • 策略账户:每个策略实例对应一个策略账户。策略账户中包含为策略分配的初始资金、历史委托单、历史成交单、历史持仓。利用这些信息,策略账户可以提供策略账户的当前权益,策略账户可用资金。

需要完成的工作

  • 实现一个策略交易监视器。它是一个rpc_server,具有按策略实例名称的方式对策略的委托单、成交单、持仓进行记录、查询、持仓统计的功能。可以在本地运行,也可以单独运行在远程。
  • 实现一个包含rpc_client的cta_template的扩展模版。它可以为其派生的用户策略提供下面的功能:
    1)策略初始化时创建K线图表窗口
    2)可以为用户策略提供K线主图指标、附图指标
    3)策略运行时,把on_order()、on_trade()收到的OrderData,TradeData等数据保存到策略交易监视器;
    4)把on_order()、on_trade()收到的OrderData,TradeData等数据推送到K线图表
  • 实现一个包含从ChartWidget派生类的K线图表
    1)它的主图和附图是用户策略配置的
    2)配置的主图包含一个K线主图,一个可以显示历史交易的交易主图
Member
avatar
加入于:
帖子: 419
声望: 171

无法统一的合约参数

合约参数中的保证金比例率和手续费是最难搞定的。

  • 保证金率:交易所有统一的保证金率,期货公司可以在交易所的基础上加收保证金,这些东西也是可以谈判的,导致因人而异的保证金率,但这就是现实。
  • 手续费分两种:1)按手数计算的,开仓手续费,平仓手续费和平今仓手续费;2)按交易金额计算的,开仓手续费率,平仓手续费率和平今仓手续费率。
    ## 合约参数中最无法统一的是保证金比例和手续费率。
    虽然交易所在某个时段内会统一发布上线合约的保证金率和手续费(率),可是这些信息对普通投资者没有多大意义,因为不同的期货公司出于安全的考虑,会在交易所发布合约保证金率的基础上加收额外的比例的保证金,另外由于投资者找到的不同经纪人、不同的期货公司,会得到不同的交易手续费,另外即使你找到同一家期货公司的同一个经纪人,也会因为统治者资金实力不同,谈判技巧的不同而造成交易手续费(率)的不同。这导致你不要通话网络从服务器获得交易合约的保证金比例和手续费率,因为他们通常是提供你开户的网址上公布的,或者文件,手机短信的方式通知的。
    这对我们的策略在计算账户剩余可用资金方面带来了巨大的不便!怎么办?方法总是比困难多!

合约参数维护

本贴介绍一种本人研究出来的一种办法:
1)手工创建合约参数文件(格式为json文件),内容包含:合约代码,市场,合约名称,类别,多头保证金率,空头保证金率,开仓手续费(率),平仓手续费(率),平今仓手续费(率)。
2)创建合约参数类,它的功能是从json文件夹中加载合约参数到合约参数字典,然后为外部提供合约参数查询功能。
3)当合约保证金率或者手续费(率)发生变化时,及时手动修改该json的内容就可以。

实现代码

[.vntrader]\contract_param.json文件内容

{
    "rb2010.SHFE": {
        "inverse":false,
        "margin_rate": 0.15,
        "open_fee":{"费率":0.001},
        "close_fee":{"费率":0.001},
        "close_today_fee":{"费率":0.001}  
    },
    "ag2012.SHFE": {
        "inverse":false,
        "margin_rate": 0.15,
        "open_fee":{"费率":0.005},
        "close_fee":{"费率":0.005},
        "close_today_fee":{"费率":0.005}  
    },
    "ap2012.SHFE": {
        "inverse":false,
        "margin_rate": 0.15,
        "open_fee":{"每手":5},
        "close_fee":{"每手":5},
        "close_today_fee":{"每手":5}  
    },
    "al2010.SHFE": {
        "inverse":false,
        "margin_rate": 0.15,
        "open_fee":{"每手":3},
        "close_fee":{"每手":3},
        "close_today_fee":{"每手":0}      
    }
}

vnpy\usertools\contract_param.py文件内容

from dataclasses import dataclass
from enum import Enum
from typing import Tuple,List,Dict,Union,Set,Sequence,Optional
from vnpy.trader.utility import load_json, save_json, extract_vt_symbol

from vnpy.trader.constant import Direction, Exchange, Interval, Offset, Status,Product, OptionType, OrderType

import datetime
import rqdatac as rq
from rqdatac.utils import to_date


''' 获得上市日期 '''
def get_listed_date(symbol:str):
    info = rq.instruments(symbol)
    return to_date(info.listed_date)

''' 获得交割日期 '''
def get_de_listed_date(symbol:str):
    info = rq.instruments(symbol)
    return to_date(info.de_listed_date)


def str2date(date_str:str):
    ''' 
    日期字符串转化到datetme.date() 
    '''
    return datetime.datetime.strptime(date_str,"%Y-%m-%d").date()

class FeeType(Enum):
    """
    Commission fee type.
    """
    LOT = "每手"    # 手续费 :LOT per lot
    RATE = "费率"   # 手续费率 :RATE%


@dataclass
class ContractParameter():
    """
    合约参数,包含:
        合约代码,市场,合约名称,类别,
        多头保证金率,空头保证金率,
        开仓手续费(率),平仓手续费(率),平今仓手续费(率),
    """

    symbol:str                      # 合约代码
    exchange:Exchange               # 交易所
    name:str                        # 合约名称
    product:Product                 # 类别
    listed_date:datetime.date       # 上市日期
    maturity_date:datetime.date     # 到期日期
    de_listed_date:datetime.date    # 交割日期
    size : int                      # 合约乘数

    # 以下来自手工json文件
    inverse:bool                    # 反向合约
    margin_rate:float               # 保证金率(%)    
    # FeeType.LOT: 手续费;FeeType.RATE:手续费率(%)
    open_fee:Tuple[FeeType,float]           # 开仓手续费(率)
    close_fee:Tuple[FeeType,float]          # 平仓手续费(率)
    close_today_fee:Tuple[FeeType,float]    # 平今仓手续费(率)

    def __post_init__(self):
        """  """
        self.vt_symbol = f"{self.symbol}.{self.exchange.value}"


class ContractParameters():

    """ 
    合约参数类,从json文件读取合约参数 
    注意:对包含保证金率和手续费(率)的json文件的处理只有加载操作,没有修改和删除操作。
         修改和删除合约参数操作通过手工操作就可以了。
    """

    contract_parameters_file = "contract_param.json"
    contract_parameters:Dict[str,ContractParameter] = {}

    def __init__(self):
        self.load_contract_paramters()

    def load_contract_paramters(self):
        """ 读取contract_param.json中的合约参数列表 """
        parameters = load_json(self.contract_parameters_file)

        for vt_symbol,param in parameters.items():
            symbol,exchange = extract_vt_symbol(vt_symbol)
            contract = rq.instruments(symbol.upper())

            # print(f"{contract}")

            cparam = ContractParameter(
                        symbol = symbol,
                        exchange = exchange,
                        product = contract.product,
                        name = contract.symbol,
                        listed_date = str2date(contract.listed_date),
                        maturity_date = str2date(contract.maturity_date),
                        de_listed_date = str2date(contract.de_listed_date),
                        size = contract.contract_multiplier,
                        inverse = param["inverse"],
                        margin_rate = param['margin_rate'],
                        open_fee = param['open_fee'],
                        close_fee = param['close_fee'],
                        close_today_fee = param.get("close_today_fee",{})
                    )

            self.contract_parameters[cparam.vt_symbol] = cparam

    def get_paramter(self,vt_symbol:str):
        return self.contract_parameters.get(vt_symbol,{})



if __name__ == "__main__":
    rq.init('18096678138','Rq131466',("rqdatad-pro.ricequant.com",16011))

    contract_parameters = ContractParameters()
    # print(f"{contract_parameters.contract_parameters}")

    vt_symbols = ['rb2010.SHFE','ag2012.SHFE','ap2012.SHFE','al2010.SHFE','cu2010.SHFE']
    for vt_symbol in vt_symbols:
        param = contract_parameters.get_paramter(vt_symbol) 
        print(f"\n{vt_symbol}'s contract parameters: \n{param}")

    # last_trade_date = rq.get_latest_trading_date() 
    # instruments = rq.all_instruments(type='Future',date=last_trade_date,market='cn')

    # print(f"{last_trade_date} {type(instruments)} ")
    # for idx,instrument in instruments.iterrows():
    #     # print(f"{type(instrument)}   {instrument}")
    #     order_book_id = instrument['order_book_id']
    #     symbol = instrument['symbol']
    #     trading_hours = instrument['trading_hours']
    #     # print(f"{order_book_id,symbol,trading_hours}")

    #     if trading_hours.find("09:01-10:15") < 0:
    #         print(f"合约代码:{order_book_id} 名称:{symbol} 交易时间段:{trading_hours}")

    # print(f"finished")

测试结果

contract_param.py文件中自带测试代码,直接运行可以得到如下结果:

rb2010.SHFE's contract parameters: 
ContractParameter(symbol='rb2010', exchange=<Exchange.SHFE: 'SHFE'>, name='螺纹钢2010', product='Commodity', listed_date=datetime.date(2019, 10, 16), maturity_date=datetime.date(2020, 10, 15), de_listed_date=datetime.date(2020, 10, 15), size=10.0, inverse=False, margin_rate=0.15, open_fee={'费率': 0.001}, close_fee={'费率': 0.001}, close_today_fee={'费率': 0.001})

ag2012.SHFE's contract parameters: 
ContractParameter(symbol='ag2012', exchange=<Exchange.SHFE: 'SHFE'>, name='白银2012', product='Commodity', listed_date=datetime.date(2019, 12, 17), maturity_date=datetime.date(2020, 12, 15), de_listed_date=datetime.date(2020, 12, 15), size=15.0, inverse=False, margin_rate=0.15, open_fee={'费率': 0.005}, close_fee={'费率': 0.005}, close_today_fee={'费率': 0.005})

ap2012.SHFE's contract parameters: 
ContractParameter(symbol='ap2012', exchange=<Exchange.SHFE: 'SHFE'>, name='鲜苹果2012', product='Commodity', listed_date=datetime.date(2019, 12, 16), maturity_date=datetime.date(2020, 12, 14), de_listed_date=datetime.date(2020, 12, 14), size=10.0, inverse=False, margin_rate=0.15, open_fee={'每手': 5}, close_fee={'每手': 5}, close_today_fee={'每手': 5})

al2010.SHFE's contract parameters:
ContractParameter(symbol='al2010', exchange=<Exchange.SHFE: 'SHFE'>, name='铝2010', product='Commodity', listed_date=datetime.date(2019, 10, 16), maturity_date=datetime.date(2020, 10, 15), de_listed_date=datetime.date(2020, 10, 15), size=5.0, inverse=False, margin_rate=0.15, open_fee={'每手': 3}, close_fee={'每手': 3}, close_today_fee={'每手': 0})

cu2010.SHFE's contract parameters:
{}
Member
avatar
加入于:
帖子: 50
声望: 1

楼主,有新的进展吗?

Member
avatar
加入于:
帖子: 419
声望: 171

晴空 wrote:

楼主,有新的进展吗?

有了,正在测试中......

Member
avatar
加入于:
帖子: 50
声望: 1

赞一个,期待发布

Member
avatar
加入于:
帖子: 16
声望: 0

期待大佬教程完善哈,在此感谢了

Member
avatar
加入于:
帖子: 20
声望: 2

因为合约参数改变比较小,个人习惯用NamedTuple来保存这些参数

Member
加入于:
帖子: 1
声望: 0

期待大佬的结果

Member
avatar
加入于:
帖子: 7
声望: 0

大佬,弄好了吗?

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】