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

以CTP接口为例
1.object.py修改如下:

class AccountData(BaseData):
    """
    Account data contains information about balance, frozen and
    available.
    """

    accountid: str = ""                 #账户ID
    margin: float  = 0                  #占用保证金
    balance: float = 0                 #总资金:margin + available = balance
    available: float = 0                #可用资金
    frozen: float = 0                   #冻结资金
    pre_balance: float = 0          #上个交易日总资金
    commission: float = 0           #手续费
    close_profit: float = 0             #平仓利润
    position_profit: float = 0          #持仓利润
    datetime: datetime = None
    #--------------------------------------------------------------------------------------------------
    def __post_init__(self):
        """"""
        self.vt_accountid = f"{self.gateway_name}_{self.accountid}"
        #资金使用率
        if self.available:
            self.percent = (self.balance - self.available) / self.balance * 100  
        else:
            if self.balance:
                self.percent = self.margin /  self.balance * 100
            else:
                self.percent = 0

2.ctp_gateway.py修改如下

from pytz import timezone
TZ_INFO = timezone("Asia/Shanghai")   #使用东八区时区
class CtpTdApi(TdApi):
    """"""

    def __init__(self, gateway):
        """Constructor"""
        super(CtpTdApi, self).__init__()
        self.account_date = None    #账户日期
    #-------------------------------------------------------------------------------------------------
    def onRspQryTradingAccount(self, data: dict, error: dict, reqid: int,
                               last: bool):
        """"""
        account = AccountData(
            accountid=data["AccountID"] + "_" + self.gateway_name,
            balance=round(data["Balance"],3),
            available = round(data["Available"],3),                   #可用资金
            pre_balance = round(data["PreBalance"],3),                #上个交易日总资金
            commission = round(data["Commission"],3),                #手续费
            margin = round(data["CurrMargin"] ,3),                    #账户保证金
            close_profit = round(data["CloseProfit"],3),              #平仓盈亏
            position_profit = round(data["PositionProfit"],3),        #持仓盈亏
            frozen=data["FrozenMargin"] + data["FrozenCash"] + data["FrozenCommission"],        #冻结资金
            datetime = datetime.now(TZ_INFO),
            gateway_name=self.gateway_name)
        self.gateway.on_account(account)

        recording = True
        #周六周日不写入数据
        if datetime.today().weekday() == 5 or datetime.today().weekday() == 6:
            recording = False
        if recording:
            #通过CTP接口查询账户资金
            account_info = account.__dict__
            ctp_account_path = "C:\\ProgramData\\Anaconda3\\Lib\\site-packages\\vnpy-2.1.0-py3.7.egg\\vnpy\\app\\cta_strategy\\account_info\\ctp_account"+".csv"
            if not os.path.exists(ctp_account_path): # 如果文件不存在,需要写header
                with open(ctp_account_path, 'w',newline="") as f1:#newline=""不自动换行
                    w1 = csv.DictWriter(f1, account_info.keys())
                    w1.writeheader()
                    w1.writerow(account_info)
            else: # 文件存在,不需要写header
                if self.account_date and self.account_date != account.datetime.date():        #一天写入一次账户信息
                    with open(ctp_account_path,'a',newline="") as f1:  #a二进制追加形式写入
                        w1 = csv.DictWriter(f1, account_info.keys())
                        w1.writerow(account_info)
            self.account_date = account.datetime.date()
Member
avatar
加入于:
帖子: 141
声望: 57

3.新建ctp_account_info.py复制如下代码:

# encoding: UTF-8
import numpy as np
import pandas as pd
import scipy.stats as scs
import csv
from datetime import datetime
from dateutil.relativedelta import relativedelta
from time import sleep
from pyecharts.charts import (Bar,Line,Graph,Gauge,Page)#柱状图,折线图,关系图,仪表盘,多图同表
from pyecharts import options as opts
from empyrical import (sortino_ratio,omega_ratio,annual_volatility,cagr,conditional_value_at_risk,downside_risk,stability_of_timeseries,tail_ratio,value_at_risk,calmar_ratio)
#年总交易日
TRADING_DAY = 252
def output(msg):
    print(f'{datetime.now()}\t{msg}')
#----------------------------------------------------------------------
def statistics_status(array):
    """返回array均值,标准差,偏度,峰度"""
    stats = scs.describe(array)
    return stats[2],np.sqrt(stats[3]),stats[4],stats[5]  
#------------------------------------------------------------------
def plot_account(total_date:int = 0):
    ctp_account_path = "C:\\ProgramData\\Anaconda3\\Lib\\site-packages\\vnpy-2.1.0-py3.7.egg\\vnpy\\app\\cta_strategy\\account_info\\ctp_account"+".csv"    #ctp账户csv数据路径
    df = pd.DataFrame()
    account_day = []     #交易日
    account_balance = []     #总资金
    account_commission = []  #手续费
    net_value_list = []   #净值
    net_value = 0
    #月度盈亏参数
    last_month_date = ""
    month_pnl = 0
    month_dict = {}
    #------------------------------------------------------------------
    with open(ctp_account_path,"r",encoding="utf-8") as f1:
        csvreader = csv.DictReader((line.replace("\0",'') for line in f1),delimiter=",") #line.replace移除NULL值       
        ctp_account = [rowdata for rowdata in csvreader]
    for index in range(len(ctp_account)):
        account_day.append(ctp_account[index]["datetime"])  #交易日
        account_balance.append(float(ctp_account[index]["balance"]))    
        account_commission.append(float(ctp_account[index]["commission"]))
    if total_date:
        df["balance"] = account_balance[-total_date:]
        df["commission"] = account_commission[-total_date:]
        df["datetime"] = account_day[-total_date:]
    else:
        df["balance"] = account_balance
        df["commission"] = account_commission
        df["datetime"] = account_day
    for index in range(len(df["balance"])):
        if index == 0:
            net_pnl = 1
        else:
            net_pnl = df["balance"][index]/df["balance"][index-1]-1
        net_value += net_pnl
        net_value_list.append((float(net_value)))
    df["highlevel"] = df["balance"].rolling(min_periods=1,window=len(df),center=False).max()    #净值高点
    df["drawdown"] = df["balance"] - df["highlevel"]    #回撤资金
    df["drawdown_percent"] = (df["balance"] - df["highlevel"])/df["highlevel"]         #回撤百分比
    total_return = (df["balance"].values[-1]/df["balance"].values[0]-1)*100  #总收益率
    total_days = len(df) / 2        
    annual_return = total_return / total_days * TRADING_DAY         #年化收益率  
    max_drawdown = df["drawdown"].min()      #最大回撤资金
    mad_dd_percent = df["drawdown_percent"].min()*100        #最大回撤率

    df["return"] = (np.log(df["balance"]) - np.log(df["balance"].shift(1))).fillna(0)   #净收益率
    df['net_pnl'] = df["balance"] - df["balance"].shift(1)  #日盈亏  
    df['net_pnl'].fillna(0,inplace = True)
    df.set_index("datetime",inplace=True)

    #收益率均值,标准差,偏度,峰度
    return_mean,return_std,return_skew, return_kurt = statistics_status(df["return"].values) 
    if return_std:
        sharpe_ratio = df["return"].mean() * 100 / (return_std * 100) * np.sqrt(252)
    else:
        sharpe_ratio = 0    
    #sortino_info
    sortino_info = sortino_ratio(df['return'])
    omega_info = omega_ratio(df['return'])
    #calmar_info
    calmar_info = calmar_ratio(df['return'],period="yearly")
    #年化波动率
    annual_volatility_info = annual_volatility(df['return'])
    #年化复合增长率
    cagr_info = cagr(df['return'])
    annual_downside_risk = downside_risk(df['return'],required_return = 0.2,period = 'yearly')       
    c_var = conditional_value_at_risk(df['return'])
    var_info = value_at_risk(df['return'])
    stability_return = stability_of_timeseries(df['return'])
    #尾部比率0.25 == 1/4,收益1,风险4
    tail_ratio_info = tail_ratio(df['return'])
    #--------------------------------------------------------------------------------------------
    output("-"*70)
    output(f"账户:{ctp_account[-1]['accountid']},总资金:{float(ctp_account[-1]['balance']):,.3f},可用资金剩余:{float(ctp_account[-1]['available']):,.3f},rmb总资金:{float(ctp_account[-1]['pre_balance']):,.3f}")
    output(f"总盈亏:{df['balance'].values[-1]-df['balance'].values[0]:,.3f},总收益率:{total_return:,.3f}%,复利净值:{net_value_list[-1]:,.3f}")
    output(f"sharpe_ratio:\t{sharpe_ratio:,.3f}")
    output(f"calmar_info:\t{calmar_info:,.3f}")
    output(f"sortino_info:\t{sortino_info:,.3f}")
    output(f"omega_info:\t{omega_info:,.3f}")
    output(f"年化波动率:\t{annual_volatility_info:,.3f}")
    output(f"年复合增长率:\t{cagr_info:,.3f}")
    output(f"年化下行风险率:\t{annual_downside_risk:,.3f}")
    output(f"c_var:\t{c_var:,.3f}")
    output(f"var_info:\t{var_info:,.3f}")
    output(f"收益稳定率:\t{stability_return:,.3f}")
    output(f"尾部比率:\t{tail_ratio_info:,.3f}")
    output(f"持仓盈亏:{float(ctp_account[-1]['position_profit']):,.3f}")
    output(f"最大回撤率:{mad_dd_percent:,.3f}%")
    output(f"收益回撤比:{-np.sum(df['net_pnl'])/max_drawdown:,.3f}")
    output(f"收益率回撤率比:{-total_return/mad_dd_percent:,.3f}")
    output(f"总手续费:{df['commission'].sum():,.3f}")   
    output(f"年化收益率:\t{annual_return:,.3f}%")
    output(f"日均盈亏:{np.mean(df['net_pnl']):,.3f}\t日盈利最大值:{max(df['net_pnl']):,.3f}\t日亏损最大值:{min(df['net_pnl']):,.3f}")
    output(f"日均收益率:{return_mean*100:,.3f}%,收益率标准差:{return_std*100:,.3f}%,收益率偏度:{return_skew:,.3f},收益率峰度:{return_kurt:,.3f}")
    output("-"*70)
    #--------------------------------------------------------------------------------------------
    page = Page()
    bar_1 = Line()
    bar_1.add_xaxis(df['balance'].index.tolist())
    bar_1.add_yaxis(f"账户:{ctp_account[-1]['accountid']}\n\n总资金\n\n起止时间:{df['balance'].index[0]}至{df['balance'].index[-1]}",df['balance'].tolist())      #主标题
    bar_1.set_global_opts(opts.TitleOpts(title=f"资金\n\n总收益率:{total_return:,.3f}%"),toolbox_opts=opts.ToolboxOpts())  #副标题,ToolboxOpts设置工具箱配置项 
    bar_1.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项 

    bar_2 = Bar()
    bar_2.add_xaxis(df.index.tolist())
    bar_2.add_yaxis(f"复利净值最高点:{max(net_value_list):,.3f}\t复利净值最低点:{min(net_value_list):,.3f}",net_value_list)      #主标题
    bar_2.set_global_opts(opts.TitleOpts(title="复利净值"),toolbox_opts=opts.ToolboxOpts())  #副标题,ToolboxOpts设置工具箱配置项 
    bar_2.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项 

    bar_3 = Bar()
    bar_3.add_xaxis(df.index[1:].tolist())
    bar_3.add_yaxis(f"日盈利最大值:{max(df['net_pnl']):,.3f}\n\n日亏损最大值:{min(df['net_pnl']):,.3f}",df['net_pnl'].tolist())      #主标题
    bar_3.set_global_opts(opts.TitleOpts(title="日收益"),toolbox_opts=opts.ToolboxOpts())  #副标题,ToolboxOpts设置工具箱配置项 
    bar_3.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项 
    for pnl_index in df['net_pnl'].index:
        month_date = pnl_index[:7]
        if not isinstance(df['net_pnl'][pnl_index],np.float64):
            continue
        if month_date == last_month_date:
            month_pnl += df['net_pnl'][pnl_index]
        else:
            #月份减一保存实际月份收益
            month_dict.update({month_date:month_pnl})
            for key,value in list(month_dict.items()):
                if isinstance(key,datetime):
                    continue
                key = datetime.strptime(key,"%Y-%m") - relativedelta(months = 1)
                month_dict.update({key:value})
            #month_dict删除原始的str键值对
            for key,value in list(month_dict.items()):
                if isinstance(key,str):
                    month_dict.pop(key)
            month_pnl = df['net_pnl'][pnl_index]
        last_month_date = month_date
    month_dict.pop(list(month_dict.keys())[0])
    #兼容月度数据不足
    try:
        max_month_pnl = max(month_dict.values())
        min_month_pnl = min(min(month_dict.values()),0)
    except ValueError:
        max_month_pnl = 0
        min_month_pnl = 0
    bar_4 = Bar()
    bar_4.add_xaxis(list(month_dict.keys()))
    bar_4.add_yaxis(f"月盈亏\n\n最大月盈利:{max_month_pnl:,.3f}\n\n最大月亏损:{min_month_pnl:,.3f}",list(month_dict.values()))
    bar_4.set_global_opts(opts.TitleOpts(title="月盈亏"),toolbox_opts=opts.ToolboxOpts())  #设置工具箱配置项   
    bar_4.set_series_opts(label_opts=opts.LabelOpts(is_show=False)
    )  #系列配置项 

    bar_5 = Bar()
    bar_5.add_xaxis(df["drawdown"].index.tolist())
    bar_5.add_yaxis(f"回撤资金\n\n最大回撤资金:{max_drawdown:,.3f}",df["drawdown"].tolist())      #主标题
    bar_5.set_global_opts(opts.TitleOpts(title="资金"),toolbox_opts=opts.ToolboxOpts())  #副标题,ToolboxOpts设置工具箱配置项 
    bar_5.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项 

    bar_6 = Bar()
    bar_6.add_xaxis(df["drawdown_percent"].index.tolist())
    bar_6.add_yaxis(f"回撤百分比\n\n最大回撤率:{mad_dd_percent:,.3f}%",df["drawdown_percent"].tolist())      #主标题
    bar_6.set_global_opts(opts.TitleOpts(title="回撤百分比"),toolbox_opts=opts.ToolboxOpts())  #副标题,ToolboxOpts设置工具箱配置项 
    bar_6.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项 

    hist,bin_edges= np.histogram(df['net_pnl'], bins=50)
    bar_7 = Bar()
    bar_7.add_xaxis(bin_edges[1:].tolist())
    bar_7.add_yaxis("日盈亏分布直方图",hist.tolist())      #主标题
    bar_7.set_global_opts(opts.TitleOpts(title="频数"),toolbox_opts=opts.ToolboxOpts())  #副标题,ToolboxOpts设置工具箱配置项 
    bar_7.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项 


    bar_8 = Bar()
    bar_8.add_xaxis(df.index.tolist())
    bar_8.add_yaxis(f"日最高手续费:{df['commission'].max()}",df["commission"].tolist())      #主标题
    bar_8.set_global_opts(opts.TitleOpts(title="手续费"),toolbox_opts=opts.ToolboxOpts())  #副标题,ToolboxOpts设置工具箱配置项 
    bar_8.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项 

    page.add(bar_1)
    page.add(bar_2)
    page.add(bar_3)
    page.add(bar_4)
    page.add(bar_5)
    page.add(bar_6)
    page.add(bar_7)
    page.add(bar_8)
    page.render(ctp_account_path.replace(".csv",".html"))#保存为html
#--------------------------------------------------------------------------------------------
if __name__ == '__main__':
    while True:
        plot_account(total_date=360)#统计最近total_date日数据
        sleep(60)
        break
Member
avatar
加入于:
帖子: 141
声望: 57

月度盈亏应该没有写错吧,大概😅

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

给月总点赞!

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

这个很实用,多谢l月总。

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

谢谢大佬分享

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

点赞

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

谢谢大佬分享

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

ctp_account_info.py中的函数:

def output(self, msg):
def statistics_status(self,array):

参数有self,貌似是从哪个类中截取出来的代码,普通函数是没有self参数的,而且这个"scs."模块是什么引用的什么模块也没有交代。

恳请楼主上弦之月 重新分享完整的ctp_account_info.py文件。

不管怎样都谢谢楼主啦!

Member
avatar
加入于:
帖子: 41
声望: 3

点赞点赞

Member
avatar
加入于:
帖子: 141
声望: 57

@hxxjava 已修复 你再试下

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

点赞

Member
加入于:
帖子: 50
声望: 2

按顺序弄好了 怎么使用呢···

Member
avatar
加入于:
帖子: 141
声望: 57

@回测猛如虎 交易程序运行后,每天0点会写入一次账户信息到csv,运行ctp_account_info.py会显示策略收益统计信息并画图到账户csv目录(account_info目录)

Member
加入于:
帖子: 50
声望: 2

上弦之月 wrote:

@回测猛如虎 交易程序运行后,每天0点会写入一次账户信息到csv,运行ctp_account_info.py会显示策略收益统计信息并画图到账户csv目录(account_info目录)
哦哦 明白了。 那这样的话 vnpy 的程序一直运行到0点? 能每天早上9点前 打开vnpy 把昨天账户信息写入到csv呢?

还有一个问题是

 #通过CTP接口查询账户资金
            account_info = account.__dict__
            ctp_account_path = "C:\\ProgramData\\Anaconda3\\Lib\\site-packages\\vnpy-2.1.0-py3.7.egg\\vnpy\\app\\cta_strategy\\account_info\\ctp_account"+".csv"
            if not os.path.exists(ctp_account_path): # 如果文件不存在,需要写header

这个 ctp_account_path 的地址 要改成自己 vnpy 所装的地方吗?比如

description

Member
avatar
加入于:
帖子: 141
声望: 57

@回测猛如虎 是的

Member
加入于:
帖子: 50
声望: 2

上弦之月 wrote:

@回测猛如虎 是的
谢谢

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

我这老是报文件的PEMISSION ERROR啊

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

请求上弦之月大佬解答,我这个CSV文件读取老是权限错误,找不到问题所在

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

@上弦之月

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

沪公网安备 31011502017034号

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