最近,股票市场出现了高波动性,例如,某家知名公司的稳定票据可能因对其管理层实施制裁的消息而立即损失数个百分点,反之亦然,因为积极的报告以及投资者对超利润股利的期望反而飙升。
如何确定给定证券的所有权是带来收入还是仅带来损失和失望?
(来源)在本文中,我将告诉您如何识别和可视化调整后的证券财务结果。
通过使用客户报告Opening Broker的示例,我们将考虑分析和合并针对股票市场的经纪报告,构建云报告系统的架构,并在AWS Quicksight中进行简单,方便的分析。
任务说明
许多培训和教育课程告诉我们有关交易者日记的需求,记录所有交易参数以供进一步分析和总结交易策略。 我同意,在交易所工作的这种方法可以使您对交易者进行纪律处分,提高其意识,但是它也可能使您摆脱繁琐的过程。
我承认,起初我认真尝试遵循日记的建议,在Excel表中仔细记录了每个交易及其参数,构建了一些报告,摘要图表,计划的未来交易,但是……我很快就厌倦了这一切。
为什么手动保存交易者的日志很不方便?- 手动填充日志(即使使用部分自动化,也可以从交易终端卸载日常交易的形式)很快就累了;
- 手动输入有很大的错误或错别字的风险;
- 主动交易者可能成为被动投资者,他越来越少回到该杂志,然后完全忘记它(我的情况); 好,最后
- 我们可以编程,为什么不利用它来使整个过程自动化呢? 所以,走吧!
经纪公司通常是高科技组织,可以为客户提供几乎所有感兴趣的问题的相当高质量的分析。 可以说,每次更新此报告都将越来越好,但是即使是最高级的报告也可能没有苛刻和好奇的客户希望看到的自定义和合并。
例如,Opening Broker允许您以个人帐户接收XML格式的经纪报告,但是如果您在莫斯科证券交易所(MOEX)上拥有IIA和常规经纪帐户,则这将是两个不同的报告,并且如果您在St.彼得斯堡证券交易所(SPB),那么前两个将再增加一个。
总体而言,为了获得投资者的合并日记帐,有必要处理XML格式的三个文件。
上述关于MOEX和SPB的报告的格式略有不同,在实现数据映射的过程中需要考虑这些报告。
正在开发的系统架构
下图显示了正在开发的系统的体系结构模型:
解析器实现
我们将在最大可能的期限内收到有关“个人帐户”中所有三个帐户的报告(每年可分为几份报告),将其保存为XML格式并将其放在一个文件夹中。 作为研究的测试数据,我们将使用虚拟的客户组合,但参数应尽可能接近市场实际情况。
假设正在考虑的投资者X先生拥有五种证券的少量投资组合:
- 有关SPB交换的报告将有两篇论文:Apple和Microsoft;以及Microsoft。
- 关于MOEX交易所(经纪)的报告包含一篇论文:FGC UES;
- MOEX交易所(IIS)上的报告包含两种证券:MMK和OFZ 24019;
根据我们的五种证券,在买卖中可能会发生交易,支付股息和息票,价格可能会发生变化等。 我们希望看到当前的状况,即:财务结果,考虑到所有付款,交易和当前的市场价值。
Python开始发挥作用,我们将所有报告中的信息读入一个数组:
my_files_list = [join('Data/', f) for f in listdir('Data/') if isfile(join('Data/', f))] my_xml_data = []
为了进行分析,从报告中我们需要几个实体,即:
- 证券在投资组合中的头寸;
- 达成交易;
- 非交易业务和其他账户变动;
- 未平仓合约的平均价格
为了准备样本,我们将使用四个字典来描述上述集合。
dict_stocks = {'stock_name': [], 'account': [], 'currency': [], 'current_cost': [], 'current_cost_rub': [], 'saldo' : []} dict_deals = {'stock_name': [], 'account': [], 'date_oper': [], 'type_oper': [], 'quantity': [], 'price': [], 'currency': [], 'brokerage': [], 'result': []} dict_flows = {'stock_name': [], 'account': [], 'date_oper': [], 'type_oper': [], 'result': [], 'currency': []} dict_avg_price = {'stock_name': [], 'account': [], 'avg_open_price' : []}
关于这些词典的全部内容,请说几句话。
Dict_stocks字典需要dict_stocks词典来存储投资组合中的常规信息:
- 纸名(stock_name);
- 帐户名称(SPB,MOEX BROK,MOEX IIS)(帐户);
- 用于本文结算的货币(货币);
- 当前值(在“个人开户经纪人”中生成报告时)(current_cost)。 在此,我想指出,对于需求量过大的客户,有可能在将来进行其他改进,并使用来自交易终端或相应交易所网站的安全报价的动态接收;
- 生成报告时安全位置的当前值(current_cost_rub)
与上述项目类似,在这里您还可以根据需要获取当前时刻的中央银行汇率或汇率。 - 证券当前余额(Saldo)
字典dict_deals需要dict_deals词典来存储有关已完成事务的以下信息:
- 纸名(stock_name);
- 帐户名称(SPB,MOEX BROK,MOEX IIS)(帐户);
- 交易日期,即 T0(date_oper);
- 操作类型(type_oper);
- 参与交易的证券数量(数量);
- 交易执行的价格(价格);
- 进行交易的货币(货币);
- 交易经纪佣金(经纪);
- 交易的财务结果(结果)
Dict_flows字典dict_flows词典反映了客户帐户上资金的流动,并用于存储以下信息:
- 纸名(stock_name);
- 帐户名称(SPB,MOEX BROK,MOEX IIS)(帐户);
- 交易日期,即 T0(date_oper);
- 操作类型(type_oper)。 它可以采用多个值:div,NKD,税;
- 进行交易的货币(货币);
- 运营的财务结果(结果)
字典dict_avg_pricedict_avg_price词典对于以每张纸的平均购买价提供会计信息是必需的:
- 纸名(stock_name);
- 帐户名称(SPB,MOEX BROK,MOEX IIS)(帐户);
- 未平仓合约的平均价格(avg_open_price)
我们处理一系列XML文档,并使用适当的数据填写这些词典:
所有处理都将遍历报告中的所有XML数据。 所有报告中有关交易平台,客户代码的信息都是相同的,因此您可以安全地从相同的标记中提取它,而无需使用映射。
但是随后我们必须使用一种特殊的设计,该设计将根据报告(SPB或MOEX)为标记提供必要的别名,因为 这些报告中本质上相同的数据被称为“不同”。
标签差异- SBP报告中的交易经纪人佣金位于经纪人标签和MOEX报告中-broker_commission ;
- SPB报表中的非交易帐户交易日期是operationdate ,而在MOEX中,它是operation_date等。
标签映射示例 tags_mapping = { 'SPB': { 'current_position': 'briefcase_position', 'deals': 'closed_deal', 'flows': 'nontrade_money_operation', ... 'stock_name_deal': 'issuername', 'paymentcurrency': 'paymentcurrency', 'currency_flows': 'currencycode' }, 'MOEX': { 'current_position': 'spot_assets', 'deals': 'spot_main_deals_conclusion', 'flows': 'spot_non_trade_money_operations', ... 'stock_name_deal': 'security_name', 'paymentcurrency': 'price_currency_code', 'currency_flows': 'currency_code' } }
get_allias函数以交易平台的名称作为输入,返回进行处理所需的标记的名称:
Get_allias函数 def get_allias(exchange_name): return( tags_mapping[exchange_name]['current_position'], tags_mapping[exchange_name]['deals'], tags_mapping[exchange_name]['flows'], ... tags_mapping[exchange_name]['stock_name_deal'], tags_mapping[exchange_name]['paymentcurrency'], tags_mapping[exchange_name]['currency_flows'] )
函数get_briefcase负责处理有关客户组合状态的信息:
Get_briefcase函数 def get_briefcase(XMLdata):
接下来,get_deals函数检索有关事务的信息:
Get_deals函数 def get_deals(XMLdata): stock_name_proc = '' closed_deal = XMLdata.find(deals) if not closed_deal: return
除了使用有关交易参数的信息处理数组外,此处还计算了由PNL使用FIFO方法实现的未平仓头寸的平均价格。 PnlSnapshot类负责此计算,通过稍作修改即可创建该模型,并以此处提供的代码为基础:
损益计算最后,最难实现的功能是获取有关非交易操作的信息的功能
-get_nontrade_operation 。 其复杂性在于以下事实:在用于非交易操作的报告块中,没有关于交易类型和与此操作相关的安全性的明确信息。
非交易操作的付款目的地示例股息或累积的票息收入的支付可以表示如下:
- 支付收入客户<777777>的股利 < APPLE INC-ao>->从SPB报告中支付股利;
- 支付收益客户<777777>的股息 < MICROSOFTCOM ->
- 支付客户收入777777i(NKD 2 OFZ 24019 )预扣税0.00卢布->从MOEX报告中支付优惠券;
- 向客户支付FGC UES 777777的股息-代缴预提税XX.XX卢布->从MOEX报告中支付股息。 等
因此,没有正则表达式将很难做到,因此我们将充分利用它们。 问题的另一面是,公司的名称并不总是与投资组合或交易中的付款名称一致。 因此,从支付目的接收到的发行者的名称必须与词典另外相关。 作为字典,我们将使用一系列交易,因为 有最完整的公司列表。
get_company_from_str函数从注释中检索发行者名称:
Get_company_from_str函数 def get_company_from_str(comment): company_name = ''
如果
get_company_from_briefcase函数在参与交易的公司之间找到匹配项,则会将公司名称引到字典中:
Get_company_from_briefcase函数 def get_company_from_briefcase(company_name): company_name_full = None value_from_dic = df_deals[df_deals['stock_name'].str.contains(company_name)] company_arr = value_from_dic['stock_name'].unique() if len(company_arr) == 1: company_name_full = company_arr[0] return company_name_full
最后,收集非交易操作数据的最终功能是
get_nontrade_operation :
Get_nontrade_operation函数 def get_nontrade_operation(XMLdata): nontrade_money_operation = XMLdata.find(flows) if not nontrade_money_operation: return try: for child in nontrade_money_operation: comment = child.get('comment') type_oper_match = re.search('||^.+.+.+$', comment) if type_oper_match: company_name = get_company_from_str(comment) type_oper = get_type_oper(comment) dict_flows['stock_name'].append(company_name) dict_flows['account'].append(account_name) dict_flows['date_oper'].append(to_dt(child.get(operationdate)).strftime('%Y-%m-%d')) dict_flows['type_oper'].append(type_oper) dict_flows['result'].append(float(child.get('amount'))) dict_flows['currency'].append(child.get(currency_flows)) except Exception as e: print('get_nontrade_operation --> Oops! It seems we have a BUG!', e)
从报告中收集数据的结果将是三个数据框,大致如下:
- 带有未平仓合约平均价格信息的DataFrame:
- 交易数据框:
- 包含有关非交易操作信息的DataFrame:
因此,剩下要做的就是对交易表和投资组合信息表进行外部联合:
df_result = pd.merge(df_deals, df_stocks_avg, how='outer', on=['stock_name', 'account', 'currency']).fillna(0) df_result.sample(10)
最后,处理数据数组的最后一部分是将上一步中获得的数据数组与用于非交易事务的DataFrame合并。
完成工作的结果是一张大的平台,其中包含所有需要分析的信息:
df_result_full = df_result.append(df_flows, ignore_index=True).fillna(0) df_result_full.sample(10).head()
来自DataFrame的结果数据集(最终报告)可以轻松上传到CSV,然后可以在任何BI系统中用于详细分析。
if not exists('OUTPUT'): makedirs('OUTPUT') report_name = 'OUTPUT\my_trader_diary.csv' df_result_full.to_csv(report_name, index = False, encoding='utf-8-sig')
在AWS中上载和处理数据
进展不会停滞不前,现在云服务和无服务器计算模型在数据处理和存储中越来越受欢迎。 这主要是由于这种方法的简单性和廉价性,当您不需要购买昂贵的设备来构建用于复杂计算或处理大数据的系统架构时,您只需在需要的时间租用云中的电源,并以相对较低的费用足够快地部署必要的资源即可。 。
市场上最大和最知名的云提供商之一是亚马逊。 让我们看一下Amazon Web Services(AWS)环境的示例,以构建一个分析系统来处理投资组合中的数据。
AWS有多种工具可供选择,但我们将使用以下工具:
可以使用Amazon的文档,特别是有一篇不错的文章,
《将Athena与AWS Glue一起使用时的最佳做法》 ,其中描述了如何使用AWS Glue创建和使用表和数据。 让我们利用本文的主要思想,并将其应用于创建我们自己的分析报告系统的体系结构。
我们的报告解析器准备的CSV文件将添加到S3存储桶中。 计划在每个星期六(在交易周结束时)对S3上的相应文件夹进行补充,因此,如果不能在报表的形成和处理日期之前对数据进行分区,就无法进行。
除了优化对此类数据的SQL查询操作之外,这种方法还将使我们能够进行其他分析,例如,获取每篇论文的财务结果变化的动态等。
使用Amazon S3- 在S3上创建一个存储桶,将其命名为“ report-parser”;
- 在此存储区“ report-parser”中创建一个名为“ my_trader_diary”的文件夹;
- 在目录“ my_trader_diary”中,创建一个包含当前报告日期的目录,例如,“ date_report = 2018-10-01”,并将CSV文件放入其中;
- 仅出于实验目的和对分区的更好理解,我们将再创建两个目录:“ date_report = 2018-09-27”和“ date_report = 2018-10-08”。 我们将相同的CSV文件放入其中;
- 最终的S3存储桶“报告解析器”应类似于下图所示:
使用AWS Glue总的来说,您只能使用Amazon Athena来根据S3上的数据创建外部表,但是AWS Glue是一种更灵活,更方便的工具。
- 我们进入AWS Glue并创建一个新的Crawler,它将通过报告日期从单独的CSV文件中收集一个表:
- 设置新的Crawler的名称;
- 我们指出从何处获取数据的存储库(s3:// report-parser / my_trader_diary /)
- 我们选择或创建一个新的IAM角色,该角色将有权启动Crawler并访问S3上的指定资源;
- 接下来,您需要设置开始频率。 我们现在按需设置它,但是在将来,我认为这种情况将会改变,并且发布会变成每周一次。
- 保存并等待创建抓取工具。
- 搜寻器进入“就绪”状态时,请启动它!
- 一旦工作,新的my_trader_diary表将出现在AWS Glue:“数据库”->“表”选项卡中:
请更详细地考虑生成的表。
如果单击创建的表的名称,那么我们将转到包含元数据描述的页面。 在底部有一个表格布局,最近的是一列,该列不在源CSV文件中-date_report。 AWS Glue的此列基于源数据的各个部分的定义自动创建(在存储桶S3中,我们特别命名了文件夹date_report = YYYY-MM-DD,这使我们可以将它们用作按日期分隔的部分)。
表分区在右上角的同一页上,有一个“查看分区”按钮,通过单击它,我们可以看到生成的表包括哪些部分:
资料分析
有了我们可以上载的已处理数据,我们可以轻松地开始分析它们。 首先,将Amazon Athena的功能视为执行分析查询的最简单,最快的方法。 为此,请转到Amazon Athena服务,选择我们需要的数据库(财务),然后编写以下SQL代码:
select d.date_report, d.account, d.stock_name, d.currency, sum(d.quantity) as quantity, round(sum(d.result), 2) as result from my_trader_diary d group by d.date_report, d.account, d.stock_name, d.currency order by d.account, d.stock_name, d.date_report;
此请求将向我们显示每个证券在所有报告日期的净财务结果。 因为 我们在不同的日期下载了3次相同的报告,结果不会改变,当然,在实际市场中,结果会有所不同:
但是,如果我们想以灵活的表格或图表的形式可视化接收到的数据怎么办? 在这里,Amazon QuickSight可以助您一臂之力,借助它,您几乎可以像编写SQL查询一样快地配置灵活的分析。 我们将转到Amazon QuickSight服务(如果您尚未在此处注册,则需要注册)。
单击新分析->新数据集按钮,然后在出现的窗口中选择数据集源,然后单击Athena:
我们将为数据源命名,例如“ PNL_analysis”,然后单击“创建数据源”按钮。
接下来,“选择表”窗口打开,您需要在其中选择数据库和数据源表。 我们选择数据库-Financial,并选择其中的表:my_traider_diary。 默认情况下,将使用整个表,但是如果选择“使用自定义SQL”,则可以自定义和微调所需的数据样本。 例如,我们使用整个表格,然后单击“编辑/预览数据”按钮。
将打开一个新页面,您可以在其中进行其他设置并处理现有数据。
现在,我们需要向数据集中添加其他计算字段:运营季度和运营年份。 细心的读者可能会注意到,在将最终报告保存为CSV之前,在解析器方面更容易进行此类操作。 毫无疑问,我现在的目标是实时演示BI系统设置的功能和灵活性。 我们继续通过单击“新建字段”按钮来创建计算字段。
为了突出显示运营年份和季度,使用了简单的公式:
成功创建计算字段并将其添加到选择后,请为我们的数据集命名,例如“ my_pnl_analyze”,然后单击“保存并可视化”按钮。
之后,我们将转移到Amazon QuickSight主板上,我们要做的第一件事是为报告日期设置一个过滤器(考虑到从三个部分收集了相同的数据)。 选择报告日期2018-10-01,然后单击“应用”按钮,然后转到“可视化”选项卡。
现在,我们可以在任何平面上可视化投资组合的结果,例如,针对交易帐户内的每种证券,然后依次按币种划分(因为结果在不同币种中不可比)和操作类型。让我们从所有BI中最强大的工具开始-数据透视表。为了节省空间和显示灵活性,我将货币放在单独的控件中(MS Excel中切片的类似物)上表显示,如果投资者决定现在出售FGC UES的所有股份,他将因此记录亏损,因为 支付的股息为1,509.91便士。它们不支付费用(1,763.36卢布-负汇率差,174卢布-股息个人所得税)。等待并等待交易所上更好的时机是有意义的。
下图是条形图:现在,我们将形成一个表格,该表格将向我们显示我们在每张纸上投资了多少,投资组合中有多少天以及整个拥有期间的盈利能力是多少。为此,添加两个新的计算字段:sum_investment和count_days。字段sum_investmentsum_investment ( ) :
ifelse({stock_name} = ' 24019',{avg_open_price} * quantity * 10,{avg_open_price} * quantity)
, – ( – 1000).
字段count_dayscount_day ( ) :
dateDiff(parseDate({date_oper}),parseDate({date_report}))
最终表显示在下面的屏幕快照中:结论与总结
我们已经与您一起检查了报表解析器的实现以及使用Amazon服务“即时”分析其准备的数据的方法。我们还谈到了投资组合分析的一些业务和基本方面,例如这个主题几乎是巨大的,很难在一篇文章中进行介绍,我认为将其放在单独的出版物或什至一系列出版物中是很有意义的。至于经纪人报告处理工具的使用以及其中涉及的方法和算法,可以使用它们(进行适当的修改)来处理其他经纪人的报告。无论如何,如果您打算使代码适合您的需求,我准备提供一些提示,因此请随时提出问题-我一定会尽力回答。我确信该系统将找到其应用并将进一步发展。例如,计划在投资组合的完整PNL的计算中增加对存入和其他费用(例如,提取资金)以及还清债券等的会计处理。...在下一版解析器中,Quicksight方面的计算字段用于演示目的,所有这些附加列都将移植到Python,并将在解析器端进行计算。作为该解决方案的架构师和主要业务客户,我看到了以下进一步升级:嗯,我不想每次都手动请求这些XML报告!当然,到目前为止,没有其他可能性了,但是带有令牌传输和采样范围的Broker API非常适合接收每周原始报告。随后在亚马逊方面进行全自动处理:从在AWS Glue上触发ETL-job到以Amazon QuickSight中的图形和表格形式获取现成的结果,您将可以完全自动化该过程。完整的源代码可以在我的GitHub存储库中找到