{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\"Open" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# uncomment to install in colab\n", "# !pip install -e git+https://github.com/enzoampil/fastquant.git@master#egg=fastquant" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Company disclosures-based trading strategy" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Pulling JFC disclosures summary...\n", "3 pages detected!\n", "Found 145 disclosures between 01-01-2015 & 07-15-2020 with 16 types:\n", "['Material Information/Transactions'\n", " \"Notice of Annual or Special Stockholders' Meeting\" 'Press Release'\n", " \"Notice of Analysts'/Investors' Briefing\" 'Declaration of Cash Dividends'\n", " 'Joint Ventures' 'Change in Corporate Contact Details and/or Website'\n", " 'Substantial Acquisitions' 'Clarification of News Reports'\n", " \"Results of Annual or Special Stockholders' Meeting\"\n", " 'Results of Organizational Meeting of Board of Directors'\n", " 'Acquisition or Disposition of Shares of Another Corporation'\n", " 'Change in Directors and/or Officers (Resignation, Removal or Appointment, Election and/or Promotion)'\n", " 'Amendments to By-Laws' 'Amendments to Articles of Incorporation'\n", " 'Update on Corporate Actions/Material Transactions/Agreements']\n", "Pulling details in all JFC disclosures...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 1/1 [00:00<00:00, 6.50it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Loaded: /home/jgendrinal/rprojects/fastquant/examples/src/fastquant/python/fastquant/data/JFC_disclosures_01-01-2015_07-15-2020.csv\n" ] } ], "source": [ "from fastquant import DisclosuresPSE\n", "\n", "dpse = DisclosuresPSE(\"JFC\", start_date=\"01-01-2015\")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Company NameTemplate NamePSE Form NumberAnnounce Date and TimeCircular Numberedge_nourldisclosure_tableBackground/Description of the DisclosureSubject of the Disclosure
0Jollibee Foods Corporation[Amend-1]Material Information/Transactions4-302020-06-25 07:37:00C04444-202068f4aa3616d6457c0de8473cebbd6407https://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"Common\", \"Subject of ...Jollibee Foods Corporation (JFC, the “Guaranto...JFC to Issue US$ 600 Million 5.5 and 10 Year N...
1Jollibee Foods CorporationMaterial Information/Transactions4-302020-06-19 08:01:00C04295-20200be4ceaf09ea28090de8473cebbd6407https://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"COMMON\", \"Subject of ...Golden Plate Pte. Ltd. (GPPL), a wholly owned ...Jollibee Foods Corporation (JFC) and Dim Sum P...
2Jollibee Foods CorporationMaterial Information/Transactions4-302020-06-17 10:39:00C04227-2020d1a0237ac4559bda0de8473cebbd6407https://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"COMMON\", \"Subject of ...Jollibee Foods Corporation (JFC) announced tha...Cash dividend declaration
3Jollibee Foods CorporationNotice of Annual or Special Stockholders' Meeting7-12020-05-28 07:23:00C03757-20203d7278efdfc3720b0de8473cebbd6407https://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"COMMON\", \"Subject of ...In compliance with National Telecommunication ...Update of Corporate Contact Details
4Jollibee Foods CorporationPress Release4-312020-05-28 07:17:00C03749-202055ee488e963620db0de8473cebbd6407https://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"COMMON\", \"Subject of ...Further to its May 8, 2018 disclosure, JFC dis...JFC to increase investment in Buyer of Tim Ho ...
.................................
140Jollibee Foods CorporationDeclaration of Cash Dividends6-12015-04-08 08:39:00C01729-201549a8d6ba7f7608fcb15effbf9088d1abhttps://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"Common\", \"Subject of ...Jollibee Worldwide Pte. Ltd., (the “Issuer”), ...JFC Mandates Banks for U.S.$ Senior Unsecured ...
141Jollibee Foods CorporationPress Release4-312015-02-23 13:41:00C00789-2015929338ec12042f71b15effbf9088d1abhttps://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"COMMON\", \"Subject of ...Notice of annual stockholders' meeting for the...Notice of annual stockholders' meeting for the...
142Jollibee Foods CorporationClarification of News Reports4-132015-01-22 14:28:00C00256-20157578082745af0a94b15effbf9088d1abhttps://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"Common\", \"Subject of ...JFC Announces First Quarter 2020 Financial Res...Press Release: 2020 1st Quarter Financial Results
143Jollibee Foods CorporationUpdate on Corporate Actions/Material Transacti...16-12015-01-09 13:24:00C00092-201509cd76ec59b47b6db15effbf9088d1abhttps://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"Common\", \"Subject of ...Jollibee Foods Corporation, one of Asia's larg...JFC to Spend Php7 Billion for Business Transfo...
144Jollibee Foods CorporationUpdate on Corporate Actions/Material Transacti...16-12015-01-06 07:51:00C00024-201556a1e05383b8fec9b15effbf9088d1abhttps://edge.pse.com.ph/openDiscViewer.do?edge...{\"Title of Each Class\": \"common \", \"Subject of...Jollibee Foods Corporation will do an Earnings...JFC’s Earnings Call for its First Quarter 2020...
\n", "

145 rows × 10 columns

\n", "
" ], "text/plain": [ " Company Name \\\n", "0 Jollibee Foods Corporation \n", "1 Jollibee Foods Corporation \n", "2 Jollibee Foods Corporation \n", "3 Jollibee Foods Corporation \n", "4 Jollibee Foods Corporation \n", ".. ... \n", "140 Jollibee Foods Corporation \n", "141 Jollibee Foods Corporation \n", "142 Jollibee Foods Corporation \n", "143 Jollibee Foods Corporation \n", "144 Jollibee Foods Corporation \n", "\n", " Template Name PSE Form Number \\\n", "0 [Amend-1]Material Information/Transactions 4-30 \n", "1 Material Information/Transactions 4-30 \n", "2 Material Information/Transactions 4-30 \n", "3 Notice of Annual or Special Stockholders' Meeting 7-1 \n", "4 Press Release 4-31 \n", ".. ... ... \n", "140 Declaration of Cash Dividends 6-1 \n", "141 Press Release 4-31 \n", "142 Clarification of News Reports 4-13 \n", "143 Update on Corporate Actions/Material Transacti... 16-1 \n", "144 Update on Corporate Actions/Material Transacti... 16-1 \n", "\n", " Announce Date and Time Circular Number edge_no \\\n", "0 2020-06-25 07:37:00 C04444-2020 68f4aa3616d6457c0de8473cebbd6407 \n", "1 2020-06-19 08:01:00 C04295-2020 0be4ceaf09ea28090de8473cebbd6407 \n", "2 2020-06-17 10:39:00 C04227-2020 d1a0237ac4559bda0de8473cebbd6407 \n", "3 2020-05-28 07:23:00 C03757-2020 3d7278efdfc3720b0de8473cebbd6407 \n", "4 2020-05-28 07:17:00 C03749-2020 55ee488e963620db0de8473cebbd6407 \n", ".. ... ... ... \n", "140 2015-04-08 08:39:00 C01729-2015 49a8d6ba7f7608fcb15effbf9088d1ab \n", "141 2015-02-23 13:41:00 C00789-2015 929338ec12042f71b15effbf9088d1ab \n", "142 2015-01-22 14:28:00 C00256-2015 7578082745af0a94b15effbf9088d1ab \n", "143 2015-01-09 13:24:00 C00092-2015 09cd76ec59b47b6db15effbf9088d1ab \n", "144 2015-01-06 07:51:00 C00024-2015 56a1e05383b8fec9b15effbf9088d1ab \n", "\n", " url \\\n", "0 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "1 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "2 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "3 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "4 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", ".. ... \n", "140 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "141 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "142 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "143 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "144 https://edge.pse.com.ph/openDiscViewer.do?edge... \n", "\n", " disclosure_table \\\n", "0 {\"Title of Each Class\": \"Common\", \"Subject of ... \n", "1 {\"Title of Each Class\": \"COMMON\", \"Subject of ... \n", "2 {\"Title of Each Class\": \"COMMON\", \"Subject of ... \n", "3 {\"Title of Each Class\": \"COMMON\", \"Subject of ... \n", "4 {\"Title of Each Class\": \"COMMON\", \"Subject of ... \n", ".. ... \n", "140 {\"Title of Each Class\": \"Common\", \"Subject of ... \n", "141 {\"Title of Each Class\": \"COMMON\", \"Subject of ... \n", "142 {\"Title of Each Class\": \"Common\", \"Subject of ... \n", "143 {\"Title of Each Class\": \"Common\", \"Subject of ... \n", "144 {\"Title of Each Class\": \"common \", \"Subject of... \n", "\n", " Background/Description of the Disclosure \\\n", "0 Jollibee Foods Corporation (JFC, the “Guaranto... \n", "1 Golden Plate Pte. Ltd. (GPPL), a wholly owned ... \n", "2 Jollibee Foods Corporation (JFC) announced tha... \n", "3 In compliance with National Telecommunication ... \n", "4 Further to its May 8, 2018 disclosure, JFC dis... \n", ".. ... \n", "140 Jollibee Worldwide Pte. Ltd., (the “Issuer”), ... \n", "141 Notice of annual stockholders' meeting for the... \n", "142 JFC Announces First Quarter 2020 Financial Res... \n", "143 Jollibee Foods Corporation, one of Asia's larg... \n", "144 Jollibee Foods Corporation will do an Earnings... \n", "\n", " Subject of the Disclosure \n", "0 JFC to Issue US$ 600 Million 5.5 and 10 Year N... \n", "1 Jollibee Foods Corporation (JFC) and Dim Sum P... \n", "2 Cash dividend declaration \n", "3 Update of Corporate Contact Details \n", "4 JFC to increase investment in Buyer of Tim Ho ... \n", ".. ... \n", "140 JFC Mandates Banks for U.S.$ Senior Unsecured ... \n", "141 Notice of annual stockholders' meeting for the... \n", "142 Press Release: 2020 1st Quarter Financial Results \n", "143 JFC to Spend Php7 Billion for Business Transfo... \n", "144 JFC’s Earnings Call for its First Quarter 2020... \n", "\n", "[145 rows x 10 columns]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dpse.disclosures_combined" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "#!pip install nltk" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package vader_lexicon to\n", "[nltk_data] /home/jgendrinal/nltk_data...\n", "[nltk_data] Package vader_lexicon is already up-to-date!\n" ] } ], "source": [ "import nltk\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "from nltk.sentiment.vader import SentimentIntensityAnalyzer\n", "nltk.download('vader_lexicon')\n", "\n", "sia = SentimentIntensityAnalyzer()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# nltk.download('punkt')\n", "# nltk.download('averaged_perceptron_tagger')" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "date_sentiments = {}\n", "for idx,row in dpse.disclosures_combined.iterrows():\n", " date = row['Announce Date and Time']\n", " paragraph = row['Background/Description of the Disclosure']\n", " if paragraph is not None:\n", " #split paragraph into sentences \n", " # sentences = nltk.sent_tokenize(paragraph)\n", " # for sentence in sentences: \n", " # sentiment = sia.polarity_scores(sentence)['compound']\n", " sentiment = sia.polarity_scores(paragraph)['compound']\n", " date_sentiments[date] = sentiment" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{Timestamp('2020-06-25 07:37:00'): 0.9538,\n", " Timestamp('2020-06-19 08:01:00'): 0.8176,\n", " Timestamp('2020-06-17 10:39:00'): 0.8658,\n", " Timestamp('2020-05-28 07:23:00'): 0.6908,\n", " Timestamp('2020-05-28 07:17:00'): 0.936,\n", " Timestamp('2020-05-22 11:03:00'): 0.7096,\n", " Timestamp('2020-04-15 07:41:00'): 0.9796,\n", " Timestamp('2020-04-07 10:16:00'): 0.8807,\n", " Timestamp('2020-03-20 07:16:00'): 0.8074,\n", " Timestamp('2020-03-16 12:49:00'): 0.4939,\n", " Timestamp('2020-03-13 15:49:00'): 0.0,\n", " Timestamp('2020-02-18 15:26:00'): 0.0,\n", " Timestamp('2020-02-04 09:34:00'): 0.0,\n", " Timestamp('2020-01-24 07:35:00'): 0.0,\n", " Timestamp('2020-01-17 08:17:00'): 0.4019,\n", " Timestamp('2020-01-09 14:03:00'): 0.4767,\n", " Timestamp('2019-11-14 11:38:00'): 0.0,\n", " Timestamp('2019-11-14 08:06:00'): 0.4767,\n", " Timestamp('2019-11-11 14:52:00'): 0.91,\n", " Timestamp('2019-10-07 12:42:00'): 0.0,\n", " Timestamp('2019-10-02 15:46:00'): 0.7351,\n", " Timestamp('2019-09-24 15:43:00'): 0.4767,\n", " Timestamp('2019-09-16 11:10:00'): 0.0,\n", " Timestamp('2019-09-06 15:50:00'): 0.7964,\n", " Timestamp('2019-09-06 15:17:00'): 0.4215,\n", " Timestamp('2019-08-05 15:01:00'): 0.6588,\n", " Timestamp('2019-08-01 09:48:00'): 0.7351,\n", " Timestamp('2019-07-08 07:32:00'): 0.4404,\n", " Timestamp('2019-07-01 13:15:00'): 0.8176,\n", " Timestamp('2019-06-28 15:59:00'): 0.0,\n", " Timestamp('2019-06-28 15:14:00'): 0.0,\n", " Timestamp('2019-06-28 15:11:00'): 0.0,\n", " Timestamp('2019-06-28 07:41:00'): 0.4215,\n", " Timestamp('2019-05-29 11:41:00'): 0.8316,\n", " Timestamp('2019-05-15 14:32:00'): 0.0,\n", " Timestamp('2019-04-22 12:36:00'): 0.0,\n", " Timestamp('2019-04-11 07:58:00'): 0.0,\n", " Timestamp('2019-04-08 14:08:00'): 0.0,\n", " Timestamp('2019-04-08 13:58:00'): 0.0,\n", " Timestamp('2019-02-14 15:00:00'): 0.0,\n", " Timestamp('2019-02-14 14:47:00'): 0.4767,\n", " Timestamp('2018-12-21 13:37:00'): 0.4404,\n", " Timestamp('2018-12-14 10:51:00'): 0.4767,\n", " Timestamp('2018-12-03 08:28:00'): 0.9953,\n", " Timestamp('2018-11-19 11:55:00'): 0.6124,\n", " Timestamp('2018-11-12 13:07:00'): 0.9862,\n", " Timestamp('2018-11-09 14:45:00'): 0.8221,\n", " Timestamp('2018-10-15 13:58:00'): 0.4404,\n", " Timestamp('2018-10-15 13:52:00'): 0.7351,\n", " Timestamp('2018-09-07 08:34:00'): 0.9459,\n", " Timestamp('2018-08-14 07:59:00'): 0.0,\n", " Timestamp('2018-07-05 11:41:00'): 0.4404,\n", " Timestamp('2018-07-05 11:35:00'): 0.8977,\n", " Timestamp('2018-07-02 07:45:00'): 0.4767,\n", " Timestamp('2018-06-29 15:43:00'): 0.6705,\n", " Timestamp('2018-06-29 15:42:00'): 0.8176,\n", " Timestamp('2018-06-29 15:41:00'): 0.4767,\n", " Timestamp('2018-05-09 15:31:00'): 0.0,\n", " Timestamp('2018-05-09 15:11:00'): 0.7783,\n", " Timestamp('2018-04-17 10:01:00'): 0.0,\n", " Timestamp('2018-04-17 08:38:00'): 0.0,\n", " Timestamp('2018-04-11 13:54:00'): 0.4404,\n", " Timestamp('2018-04-06 14:38:00'): 0.9774,\n", " Timestamp('2018-04-05 08:04:00'): 0.0,\n", " Timestamp('2018-03-08 10:23:00'): 0.7351,\n", " Timestamp('2018-02-14 08:14:00'): 0.4767,\n", " Timestamp('2018-02-13 15:18:00'): 0.0,\n", " Timestamp('2018-02-13 14:29:00'): 0.0,\n", " Timestamp('2017-12-26 11:09:00'): 0.9627,\n", " Timestamp('2017-12-05 12:47:00'): 0.0,\n", " Timestamp('2017-11-10 15:00:00'): 0.4404,\n", " Timestamp('2017-11-10 14:36:00'): 0.9747,\n", " Timestamp('2017-11-02 14:02:00'): 0.985,\n", " Timestamp('2017-10-09 13:30:00'): 0.5106,\n", " Timestamp('2017-09-19 12:31:00'): 0.0,\n", " Timestamp('2017-08-14 11:49:00'): 0.9971,\n", " Timestamp('2017-07-05 13:52:00'): 0.4939,\n", " Timestamp('2017-07-03 14:14:00'): 0.0,\n", " Timestamp('2017-06-30 15:21:00'): 0.8176,\n", " Timestamp('2017-05-12 15:49:00'): 0.0,\n", " Timestamp('2017-05-11 15:46:00'): 0.0,\n", " Timestamp('2017-04-06 07:52:00'): 0.0,\n", " Timestamp('2017-04-05 14:56:00'): 0.4939,\n", " Timestamp('2017-04-03 08:37:00'): 0.4939,\n", " Timestamp('2017-03-29 12:04:00'): 0.3182,\n", " Timestamp('2017-03-14 14:45:00'): 0.4767,\n", " Timestamp('2017-02-16 07:39:00'): 0.296,\n", " Timestamp('2017-02-14 14:47:00'): 0.0,\n", " Timestamp('2017-01-03 07:38:00'): 0.836,\n", " Timestamp('2016-12-23 12:56:00'): 0.9882,\n", " Timestamp('2016-12-15 09:16:00'): 0.0,\n", " Timestamp('2016-12-13 08:12:00'): 0.4404,\n", " Timestamp('2016-11-23 12:13:00'): 0.807,\n", " Timestamp('2016-11-21 07:58:00'): 0.807,\n", " Timestamp('2016-11-17 07:44:00'): 0.4019,\n", " Timestamp('2016-11-15 09:19:00'): 0.6705,\n", " Timestamp('2016-11-11 14:49:00'): 0.8176,\n", " Timestamp('2016-08-09 12:22:00'): 0.8016,\n", " Timestamp('2016-07-22 16:04:00'): 0.8478,\n", " Timestamp('2016-07-22 16:03:00'): 0.7003,\n", " Timestamp('2016-05-25 08:28:00'): 0.0,\n", " Timestamp('2016-05-25 08:19:00'): 0.4939,\n", " Timestamp('2016-05-13 12:06:00'): 0.4404,\n", " Timestamp('2016-05-12 14:42:00'): 0.0,\n", " Timestamp('2016-04-22 11:18:00'): 0.0,\n", " Timestamp('2016-04-11 11:49:00'): 0.4767,\n", " Timestamp('2016-04-06 15:20:00'): 0.3182,\n", " Timestamp('2016-02-24 08:34:00'): 0.4404,\n", " Timestamp('2016-02-15 11:53:00'): 0.6124,\n", " Timestamp('2016-02-09 15:24:00'): 0.5423,\n", " Timestamp('2016-01-14 08:26:00'): 0.6597,\n", " Timestamp('2015-11-23 15:27:00'): 0.8176,\n", " Timestamp('2015-11-09 14:43:00'): 0.0,\n", " Timestamp('2015-11-09 14:42:00'): 0.7845,\n", " Timestamp('2015-11-02 10:12:00'): 0.9838,\n", " Timestamp('2015-10-27 08:22:00'): 0.9334,\n", " Timestamp('2015-10-13 08:31:00'): 0.4215,\n", " Timestamp('2015-10-02 13:49:00'): 0.8176,\n", " Timestamp('2015-09-18 09:47:00'): 0.0,\n", " Timestamp('2015-08-26 09:16:00'): 0.9081,\n", " Timestamp('2015-08-07 13:31:00'): 0.3182,\n", " Timestamp('2015-06-29 08:10:00'): 0.9792,\n", " Timestamp('2015-06-26 16:03:00'): 0.9792,\n", " Timestamp('2015-05-14 08:30:00'): 0.802,\n", " Timestamp('2015-05-13 14:57:00'): 0.9538,\n", " Timestamp('2015-04-08 08:41:00'): 0.9538,\n", " Timestamp('2015-04-08 08:39:00'): 0.7184,\n", " Timestamp('2015-02-23 13:41:00'): 0.4767,\n", " Timestamp('2015-01-22 14:28:00'): -0.3182,\n", " Timestamp('2015-01-09 13:24:00'): 0.8625,\n", " Timestamp('2015-01-06 07:51:00'): 0.6124}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "date_sentiments" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting Portfolio Value: 100000.00\n" ] }, { "ename": "TypeError", "evalue": "must be real number, not LineBuffer", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 141\u001b[0m \u001b[0mcerebro\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbroker\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetcommission\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcommission\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.001\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 142\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Starting Portfolio Value: %.2f'\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mcerebro\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbroker\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetvalue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 143\u001b[0;31m \u001b[0mcerebro\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 144\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Final Portfolio Value: %.2f'\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mcerebro\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbroker\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetvalue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 145\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/cerebro.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m 1125\u001b[0m \u001b[0;31m# let's skip process \"spawning\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1126\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0miterstrat\u001b[0m \u001b[0;32min\u001b[0m \u001b[0miterstrats\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1127\u001b[0;31m \u001b[0mrunstrat\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrunstrategies\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miterstrat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1128\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrunstrats\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrunstrat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1129\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_dooptimize\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/cerebro.py\u001b[0m in \u001b[0;36mrunstrategies\u001b[0;34m(self, iterstrat, predata)\u001b[0m\n\u001b[1;32m 1291\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_runonce_old\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrunstrats\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1292\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1293\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_runonce\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrunstrats\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1294\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1295\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moldsync\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/cerebro.py\u001b[0m in \u001b[0;36m_runonce\u001b[0;34m(self, runstrats)\u001b[0m\n\u001b[1;32m 1650\u001b[0m '''\n\u001b[1;32m 1651\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstrat\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrunstrats\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1652\u001b[0;31m \u001b[0mstrat\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_once\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1653\u001b[0m \u001b[0mstrat\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# strat called next by next - reset lines\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1654\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/lineiterator.py\u001b[0m in \u001b[0;36m_once\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 295\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 296\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mindicator\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_lineiterators\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mLineIterator\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mIndType\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 297\u001b[0;31m \u001b[0mindicator\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_once\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 298\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mobserver\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_lineiterators\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mLineIterator\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mObsType\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/lineiterator.py\u001b[0m in \u001b[0;36m_once\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 315\u001b[0m \u001b[0;31m# indicators are each called with its min period\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 316\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpreonce\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_minperiod\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 317\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moncestart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_minperiod\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_minperiod\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 318\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0monce\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_minperiod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuflen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 319\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/indicator.py\u001b[0m in \u001b[0;36moncestart_via_nextstart\u001b[0;34m(self, start, end)\u001b[0m\n\u001b[1;32m 122\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 123\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madvance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 124\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnextstart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 125\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 126\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0monce_via_next\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstart\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/lineiterator.py\u001b[0m in \u001b[0;36mnextstart\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 345\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 346\u001b[0m \u001b[0;31m# Called once for 1st full calculation - defaults to regular next\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 347\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 348\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 349\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m\u001b[0m in \u001b[0;36mnext\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdate\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdate_sentiments\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msentiment\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdate_sentiments\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mdate\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 25\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msentiment\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msentiment\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 26\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 27\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/.local/lib/python3.8/site-packages/backtrader/linebuffer.py\u001b[0m in \u001b[0;36m__setitem__\u001b[0;34m(self, ago, value)\u001b[0m\n\u001b[1;32m 220\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvariable\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0mto\u001b[0m \u001b[0mbe\u001b[0m \u001b[0mset\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 221\u001b[0m '''\n\u001b[0;32m--> 222\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0midx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mago\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 223\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mbinding\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbindings\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 224\u001b[0m \u001b[0mbinding\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mago\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: must be real number, not LineBuffer" ] } ], "source": [ "from __future__ import (absolute_import, division, print_function,\n", " unicode_literals)\n", "import matplotlib.pyplot as pl\n", "pl.style.use(\"default\")\n", "\n", "import backtrader as bt\n", "import backtrader.indicators as btind\n", "from datetime import datetime\n", "import os.path\n", "import sys\n", "\n", "class Sentiment(bt.Indicator):\n", " lines = ('sentiment',)\n", " plotinfo = dict(\n", " plotymargin=0.15,\n", " plothlines=[0],\n", " plotyticks=[1.0, 0, -1.0])\n", " \n", " def next(self):\n", " self.date = self.data.datetime\n", " date = bt.num2date(self.date[0]).date()\n", " prev_sentiment = self.sentiment\n", " if date in date_sentiments:\n", " self.sentiment = date_sentiments[date]\n", " self.lines.sentiment[0] = self.sentiment\n", "\n", "\n", "class SentimentStrat(bt.Strategy):\n", " params = (\n", " ('period', 15),\n", " ('printlog', True),\n", " )\n", "\n", " def log(self, txt, dt=None, doprint=False):\n", " ''' Logging function for this strategy'''\n", " if self.params.printlog or doprint:\n", " dt = dt or self.datas[0].datetime.date(0)\n", " print('%s, %s' % (dt.isoformat(), txt))\n", "\n", " def __init__(self):\n", " # Keep a reference to the \"close\" line in the data[0] dataseries\n", " self.dataclose = self.datas[0].close\n", " # Keep track of pending orders\n", " self.order = None\n", " self.buyprice = None\n", " self.buycomm = None\n", " self.sma = bt.indicators.SimpleMovingAverage(\n", " self.datas[0], period=self.params.period)\n", " self.date = self.data.datetime\n", " self.sentiment = None\n", " Sentiment(self.data)\n", " \n", " def notify_order(self, order):\n", " if order.status in [order.Submitted, order.Accepted]:\n", " # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n", " return\n", " \n", " # Check if an order has been completed\n", " # Attention: broker could reject order if not enough cash\n", " if order.status in [order.Completed]:\n", " if order.isbuy():\n", " self.log(\n", " 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n", " (order.executed.price,\n", " order.executed.value,\n", " order.executed.comm))\n", " self.buyprice = order.executed.price\n", " self.buycomm = order.executed.comm\n", " else: # Sell\n", " self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n", " (order.executed.price,\n", " order.executed.value,\n", " order.executed.comm))\n", " \n", " self.bar_executed = len(self) \n", " \n", " elif order.status in [order.Canceled, order.Margin, order.Rejected]:\n", " self.log('Order Canceled/Margin/Rejected')\n", " \n", " # Write down: no pending order\n", " self.order = None\n", " \n", " def notify_trade(self, trade):\n", " if not trade.isclosed:\n", " return\n", "\n", " self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %\n", " (trade.pnl, trade.pnlcomm))\n", " \n", " ### Main Strat ###\n", " def next(self):\n", " # log closing price of the series from the reference\n", " self.log('Close, %.2f' % self.dataclose[0])\n", " \n", " date = bt.num2date(self.date[0]).date()\n", " prev_sentiment = self.sentiment\n", " if date in date_sentiments:\n", " self.sentiment = date_sentiments[date]\n", " \n", " # Check if an order is pending. if yes, we cannot send a 2nd one\n", " if self.order:\n", " return\n", " print(self.sentiment)\n", " # If not in the market and previous sentiment not none\n", " if not self.position and prev_sentiment:\n", " # buy if current close more than sma AND sentiment increased by >= 0.5\n", " if self.dataclose[0] > self.sma[0] and self.sentiment - prev_sentiment >= 0.5:\n", " self.log('BUY CREATE, %.2f' % self.dataclose[0])\n", " self.order = self.buy()\n", " \n", " # Already in the market and previous sentiment not none\n", " elif prev_sentiment:\n", " # sell if current close less than sma AND sentiment decreased by >= 0.5\n", " if self.dataclose[0] < self.sma[0] and self.sentiment - prev_sentiment <= -0.5:\n", " self.log('SELL CREATE, %.2f' % self.dataclose[0])\n", " self.order = self.sell()\n", "\n", " def stop(self):\n", " self.log('(MA Period %2d) Ending Value %.2f' %\n", " (self.params.period, self.broker.getvalue()), doprint=True)\n", " \n", "\n", "if __name__ == '__main__':\n", " cerebro = bt.Cerebro()\n", " \n", " # Strategy\n", " cerebro.addstrategy(SentimentStrat)\n", "\n", " # Data Feed\n", " data = bt.feeds.YahooFinanceData(\n", " dataname = 'JBFCF',\n", " fromdate = min(date_sentiments.keys()),\n", " todate = datetime.now().date(),\n", " reverse = False\n", " )\n", " \n", " cerebro.adddata(data)\n", "\n", " cerebro.broker.setcash(100000.0)\n", " cerebro.addsizer(bt.sizers.FixedSize, stake=10)\n", " cerebro.broker.setcommission(commission=0.001)\n", " print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n", " cerebro.run()\n", " print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())\n", " \n", " cerebro.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## rappler news sentiment" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from urllib.request import urlopen\n", "from bs4 import BeautifulSoup\n", "from datetime import datetime\n", "import time\n", "\n", "import nltk\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "from nltk.sentiment.vader import SentimentIntensityAnalyzer\n", "# nltk.download('vader_lexicon')\n", "\n", "sia = SentimentIntensityAnalyzer()\n", "\n", "base_url = \"http://rappler.com\"\n", "\n", "page = urlopen(base_url+'/previous-articles?filterMeta=Jollibee').read()\n", "soup = BeautifulSoup(page, features=\"html.parser\")\n", "posts = soup.findAll(\"div\", {\"class\": \"col-xs-12 col-sm-8\"})\n", "\n", "date_sentiments = {}\n", "for post in posts[:10]: #default up to 50 posts\n", " time.sleep(1)\n", " url = post.a['href']\n", " #date = post.time.text\n", " date_string = post.span.text.split('-')[0].strip()\n", " date = datetime.strptime(date_string, '%b %d, %Y').date()\n", " print(date, base_url+url)\n", " try:\n", " link_page = urlopen(base_url+url).read()\n", " except:\n", " url = url[:-2]\n", " link_page = urlopen(url).read()\n", " link_soup = BeautifulSoup(link_page)\n", " sentences = link_soup.findAll(\"p\")\n", " passage = \"\"\n", " for sentence in sentences:\n", " passage += sentence.text\n", " sentiment = sia.polarity_scores(passage)['compound']\n", " date_sentiments[date] = sentiment" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "date_sentiments" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from __future__ import (absolute_import, division, print_function,\n", " unicode_literals)\n", "import matplotlib.pyplot as pl\n", "pl.style.use(\"default\")\n", "\n", "import backtrader as bt\n", "import backtrader.indicators as btind\n", "from datetime import datetime\n", "import os.path\n", "import sys\n", "\n", "class Sentiment(bt.Indicator):\n", " lines = ('sentiment',)\n", " plotinfo = dict(\n", " plotymargin=0.15,\n", " plothlines=[0],\n", " plotyticks=[1.0, 0, -1.0])\n", " \n", " def next(self):\n", " self.date = self.data.datetime\n", " date = bt.num2date(self.date[0]).date()\n", " prev_sentiment = self.sentiment\n", " if date in date_sentiments:\n", " print(date_sentiments[date])\n", " self.sentiment = date_sentiments[date]\n", " self.lines.sentiment[0] = self.sentiment\n", "\n", "\n", "class SentimentStrat(bt.Strategy):\n", " params = (\n", " ('period', 15),\n", " ('printlog', True),\n", " )\n", "\n", " def log(self, txt, dt=None, doprint=False):\n", " ''' Logging function for this strategy'''\n", " if self.params.printlog or doprint:\n", " dt = dt or self.datas[0].datetime.date(0)\n", " print('%s, %s' % (dt.isoformat(), txt))\n", "\n", " def __init__(self):\n", " # Keep a reference to the \"close\" line in the data[0] dataseries\n", " self.dataclose = self.datas[0].close\n", " # Keep track of pending orders\n", " self.order = None\n", " self.buyprice = None\n", " self.buycomm = None\n", " self.sma = bt.indicators.SimpleMovingAverage(\n", " self.datas[0], period=self.params.period)\n", " self.date = self.data.datetime\n", " self.sentiment = None\n", " Sentiment(self.data)\n", " \n", " def notify_order(self, order):\n", " if order.status in [order.Submitted, order.Accepted]:\n", " # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n", " return\n", " \n", " # Check if an order has been completed\n", " # Attention: broker could reject order if not enough cash\n", " if order.status in [order.Completed]:\n", " if order.isbuy():\n", " self.log(\n", " 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n", " (order.executed.price,\n", " order.executed.value,\n", " order.executed.comm))\n", " self.buyprice = order.executed.price\n", " self.buycomm = order.executed.comm\n", " else: # Sell\n", " self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n", " (order.executed.price,\n", " order.executed.value,\n", " order.executed.comm))\n", " \n", " self.bar_executed = len(self) \n", " \n", " elif order.status in [order.Canceled, order.Margin, order.Rejected]:\n", " self.log('Order Canceled/Margin/Rejected')\n", " \n", " # Write down: no pending order\n", " self.order = None\n", " \n", " def notify_trade(self, trade):\n", " if not trade.isclosed:\n", " return\n", "\n", " self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %\n", " (trade.pnl, trade.pnlcomm))\n", " \n", " ### Main Strat ###\n", " def next(self):\n", " # log closing price of the series from the reference\n", " self.log('Close, %.2f' % self.dataclose[0])\n", " \n", " date = bt.num2date(self.date[0]).date()\n", " prev_sentiment = self.sentiment\n", " if date in date_sentiments:\n", " self.sentiment = date_sentiments[date]\n", " \n", " # Check if an order is pending. if yes, we cannot send a 2nd one\n", " if self.order:\n", " return\n", " print(self.sentiment)\n", " # If not in the market and previous sentiment not none\n", " if not self.position and prev_sentiment:\n", " # buy if current close more than sma AND sentiment increased by >= 0.5\n", " if self.dataclose[0] > self.sma[0] and self.sentiment - prev_sentiment >= 0.5:\n", " self.log('BUY CREATE, %.2f' % self.dataclose[0])\n", " self.order = self.buy()\n", " \n", " # Already in the market and previous sentiment not none\n", " elif prev_sentiment:\n", " # sell if current close less than sma AND sentiment decreased by >= 0.5\n", " if self.dataclose[0] < self.sma[0] and self.sentiment - prev_sentiment <= -0.5:\n", " self.log('SELL CREATE, %.2f' % self.dataclose[0])\n", " self.order = self.sell()\n", "\n", " def stop(self):\n", " self.log('(MA Period %2d) Ending Value %.2f' %\n", " (self.params.period, self.broker.getvalue()), doprint=True)\n", " \n", "if __name__ == '__main__':\n", " cerebro = bt.Cerebro()\n", " \n", " # Strategy\n", " cerebro.addstrategy(SentimentStrat)\n", "\n", " # Data Feed\n", " data = bt.feeds.YahooFinanceData(\n", " dataname = 'JBFCF',\n", " fromdate = min(date_sentiments.keys()),\n", " todate = datetime.now().date(),\n", " reverse = False\n", " )\n", " \n", " cerebro.adddata(data)\n", "\n", " cerebro.broker.setcash(100000.0)\n", " cerebro.addsizer(bt.sizers.FixedSize, stake=10)\n", " cerebro.broker.setcommission(commission=0.001)\n", " print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n", " cerebro.run()\n", " print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())\n", " \n", " cerebro.plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.2" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }