# uncomment to install in colab
# !pip install -e git+https://github.com/enzoampil/fastquant.git@master#egg=fastquant
from fastquant import DisclosuresPSE
dpse = DisclosuresPSE("JFC", start_date="01-01-2015")
Pulling JFC disclosures summary... 3 pages detected! Found 145 disclosures between 01-01-2015 & 07-15-2020 with 16 types: ['Material Information/Transactions' "Notice of Annual or Special Stockholders' Meeting" 'Press Release' "Notice of Analysts'/Investors' Briefing" 'Declaration of Cash Dividends' 'Joint Ventures' 'Change in Corporate Contact Details and/or Website' 'Substantial Acquisitions' 'Clarification of News Reports' "Results of Annual or Special Stockholders' Meeting" 'Results of Organizational Meeting of Board of Directors' 'Acquisition or Disposition of Shares of Another Corporation' 'Change in Directors and/or Officers (Resignation, Removal or Appointment, Election and/or Promotion)' 'Amendments to By-Laws' 'Amendments to Articles of Incorporation' 'Update on Corporate Actions/Material Transactions/Agreements'] Pulling details in all JFC disclosures...
100%|██████████| 1/1 [00:00<00:00, 6.50it/s]
Loaded: /home/jgendrinal/rprojects/fastquant/examples/src/fastquant/python/fastquant/data/JFC_disclosures_01-01-2015_07-15-2020.csv
dpse.disclosures_combined
Company Name | Template Name | PSE Form Number | Announce Date and Time | Circular Number | edge_no | url | disclosure_table | Background/Description of the Disclosure | Subject of the Disclosure | |
---|---|---|---|---|---|---|---|---|---|---|
0 | Jollibee Foods Corporation | [Amend-1]Material Information/Transactions | 4-30 | 2020-06-25 07:37:00 | C04444-2020 | 68f4aa3616d6457c0de8473cebbd6407 | https://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... |
1 | Jollibee Foods Corporation | Material Information/Transactions | 4-30 | 2020-06-19 08:01:00 | C04295-2020 | 0be4ceaf09ea28090de8473cebbd6407 | https://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... |
2 | Jollibee Foods Corporation | Material Information/Transactions | 4-30 | 2020-06-17 10:39:00 | C04227-2020 | d1a0237ac4559bda0de8473cebbd6407 | https://edge.pse.com.ph/openDiscViewer.do?edge... | {"Title of Each Class": "COMMON", "Subject of ... | Jollibee Foods Corporation (JFC) announced tha... | Cash dividend declaration |
3 | Jollibee Foods Corporation | Notice of Annual or Special Stockholders' Meeting | 7-1 | 2020-05-28 07:23:00 | C03757-2020 | 3d7278efdfc3720b0de8473cebbd6407 | https://edge.pse.com.ph/openDiscViewer.do?edge... | {"Title of Each Class": "COMMON", "Subject of ... | In compliance with National Telecommunication ... | Update of Corporate Contact Details |
4 | Jollibee Foods Corporation | Press Release | 4-31 | 2020-05-28 07:17:00 | C03749-2020 | 55ee488e963620db0de8473cebbd6407 | https://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 ... |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
140 | Jollibee Foods Corporation | Declaration of Cash Dividends | 6-1 | 2015-04-08 08:39:00 | C01729-2015 | 49a8d6ba7f7608fcb15effbf9088d1ab | https://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 ... |
141 | Jollibee Foods Corporation | Press Release | 4-31 | 2015-02-23 13:41:00 | C00789-2015 | 929338ec12042f71b15effbf9088d1ab | https://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... |
142 | Jollibee Foods Corporation | Clarification of News Reports | 4-13 | 2015-01-22 14:28:00 | C00256-2015 | 7578082745af0a94b15effbf9088d1ab | https://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 |
143 | Jollibee Foods Corporation | Update on Corporate Actions/Material Transacti... | 16-1 | 2015-01-09 13:24:00 | C00092-2015 | 09cd76ec59b47b6db15effbf9088d1ab | https://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... |
144 | Jollibee Foods Corporation | Update on Corporate Actions/Material Transacti... | 16-1 | 2015-01-06 07:51:00 | C00024-2015 | 56a1e05383b8fec9b15effbf9088d1ab | https://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... |
145 rows × 10 columns
#!pip install nltk
import nltk
import warnings
warnings.filterwarnings('ignore')
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')
sia = SentimentIntensityAnalyzer()
[nltk_data] Downloading package vader_lexicon to [nltk_data] /home/jgendrinal/nltk_data... [nltk_data] Package vader_lexicon is already up-to-date!
# nltk.download('punkt')
# nltk.download('averaged_perceptron_tagger')
date_sentiments = {}
for idx,row in dpse.disclosures_combined.iterrows():
date = row['Announce Date and Time']
paragraph = row['Background/Description of the Disclosure']
if paragraph is not None:
#split paragraph into sentences
# sentences = nltk.sent_tokenize(paragraph)
# for sentence in sentences:
# sentiment = sia.polarity_scores(sentence)['compound']
sentiment = sia.polarity_scores(paragraph)['compound']
date_sentiments[date] = sentiment
date_sentiments
{Timestamp('2020-06-25 07:37:00'): 0.9538, Timestamp('2020-06-19 08:01:00'): 0.8176, Timestamp('2020-06-17 10:39:00'): 0.8658, Timestamp('2020-05-28 07:23:00'): 0.6908, Timestamp('2020-05-28 07:17:00'): 0.936, Timestamp('2020-05-22 11:03:00'): 0.7096, Timestamp('2020-04-15 07:41:00'): 0.9796, Timestamp('2020-04-07 10:16:00'): 0.8807, Timestamp('2020-03-20 07:16:00'): 0.8074, Timestamp('2020-03-16 12:49:00'): 0.4939, Timestamp('2020-03-13 15:49:00'): 0.0, Timestamp('2020-02-18 15:26:00'): 0.0, Timestamp('2020-02-04 09:34:00'): 0.0, Timestamp('2020-01-24 07:35:00'): 0.0, Timestamp('2020-01-17 08:17:00'): 0.4019, Timestamp('2020-01-09 14:03:00'): 0.4767, Timestamp('2019-11-14 11:38:00'): 0.0, Timestamp('2019-11-14 08:06:00'): 0.4767, Timestamp('2019-11-11 14:52:00'): 0.91, Timestamp('2019-10-07 12:42:00'): 0.0, Timestamp('2019-10-02 15:46:00'): 0.7351, Timestamp('2019-09-24 15:43:00'): 0.4767, Timestamp('2019-09-16 11:10:00'): 0.0, Timestamp('2019-09-06 15:50:00'): 0.7964, Timestamp('2019-09-06 15:17:00'): 0.4215, Timestamp('2019-08-05 15:01:00'): 0.6588, Timestamp('2019-08-01 09:48:00'): 0.7351, Timestamp('2019-07-08 07:32:00'): 0.4404, Timestamp('2019-07-01 13:15:00'): 0.8176, Timestamp('2019-06-28 15:59:00'): 0.0, Timestamp('2019-06-28 15:14:00'): 0.0, Timestamp('2019-06-28 15:11:00'): 0.0, Timestamp('2019-06-28 07:41:00'): 0.4215, Timestamp('2019-05-29 11:41:00'): 0.8316, Timestamp('2019-05-15 14:32:00'): 0.0, Timestamp('2019-04-22 12:36:00'): 0.0, Timestamp('2019-04-11 07:58:00'): 0.0, Timestamp('2019-04-08 14:08:00'): 0.0, Timestamp('2019-04-08 13:58:00'): 0.0, Timestamp('2019-02-14 15:00:00'): 0.0, Timestamp('2019-02-14 14:47:00'): 0.4767, Timestamp('2018-12-21 13:37:00'): 0.4404, Timestamp('2018-12-14 10:51:00'): 0.4767, Timestamp('2018-12-03 08:28:00'): 0.9953, Timestamp('2018-11-19 11:55:00'): 0.6124, Timestamp('2018-11-12 13:07:00'): 0.9862, Timestamp('2018-11-09 14:45:00'): 0.8221, Timestamp('2018-10-15 13:58:00'): 0.4404, Timestamp('2018-10-15 13:52:00'): 0.7351, Timestamp('2018-09-07 08:34:00'): 0.9459, Timestamp('2018-08-14 07:59:00'): 0.0, Timestamp('2018-07-05 11:41:00'): 0.4404, Timestamp('2018-07-05 11:35:00'): 0.8977, Timestamp('2018-07-02 07:45:00'): 0.4767, Timestamp('2018-06-29 15:43:00'): 0.6705, Timestamp('2018-06-29 15:42:00'): 0.8176, Timestamp('2018-06-29 15:41:00'): 0.4767, Timestamp('2018-05-09 15:31:00'): 0.0, Timestamp('2018-05-09 15:11:00'): 0.7783, Timestamp('2018-04-17 10:01:00'): 0.0, Timestamp('2018-04-17 08:38:00'): 0.0, Timestamp('2018-04-11 13:54:00'): 0.4404, Timestamp('2018-04-06 14:38:00'): 0.9774, Timestamp('2018-04-05 08:04:00'): 0.0, Timestamp('2018-03-08 10:23:00'): 0.7351, Timestamp('2018-02-14 08:14:00'): 0.4767, Timestamp('2018-02-13 15:18:00'): 0.0, Timestamp('2018-02-13 14:29:00'): 0.0, Timestamp('2017-12-26 11:09:00'): 0.9627, Timestamp('2017-12-05 12:47:00'): 0.0, Timestamp('2017-11-10 15:00:00'): 0.4404, Timestamp('2017-11-10 14:36:00'): 0.9747, Timestamp('2017-11-02 14:02:00'): 0.985, Timestamp('2017-10-09 13:30:00'): 0.5106, Timestamp('2017-09-19 12:31:00'): 0.0, Timestamp('2017-08-14 11:49:00'): 0.9971, Timestamp('2017-07-05 13:52:00'): 0.4939, Timestamp('2017-07-03 14:14:00'): 0.0, Timestamp('2017-06-30 15:21:00'): 0.8176, Timestamp('2017-05-12 15:49:00'): 0.0, Timestamp('2017-05-11 15:46:00'): 0.0, Timestamp('2017-04-06 07:52:00'): 0.0, Timestamp('2017-04-05 14:56:00'): 0.4939, Timestamp('2017-04-03 08:37:00'): 0.4939, Timestamp('2017-03-29 12:04:00'): 0.3182, Timestamp('2017-03-14 14:45:00'): 0.4767, Timestamp('2017-02-16 07:39:00'): 0.296, Timestamp('2017-02-14 14:47:00'): 0.0, Timestamp('2017-01-03 07:38:00'): 0.836, Timestamp('2016-12-23 12:56:00'): 0.9882, Timestamp('2016-12-15 09:16:00'): 0.0, Timestamp('2016-12-13 08:12:00'): 0.4404, Timestamp('2016-11-23 12:13:00'): 0.807, Timestamp('2016-11-21 07:58:00'): 0.807, Timestamp('2016-11-17 07:44:00'): 0.4019, Timestamp('2016-11-15 09:19:00'): 0.6705, Timestamp('2016-11-11 14:49:00'): 0.8176, Timestamp('2016-08-09 12:22:00'): 0.8016, Timestamp('2016-07-22 16:04:00'): 0.8478, Timestamp('2016-07-22 16:03:00'): 0.7003, Timestamp('2016-05-25 08:28:00'): 0.0, Timestamp('2016-05-25 08:19:00'): 0.4939, Timestamp('2016-05-13 12:06:00'): 0.4404, Timestamp('2016-05-12 14:42:00'): 0.0, Timestamp('2016-04-22 11:18:00'): 0.0, Timestamp('2016-04-11 11:49:00'): 0.4767, Timestamp('2016-04-06 15:20:00'): 0.3182, Timestamp('2016-02-24 08:34:00'): 0.4404, Timestamp('2016-02-15 11:53:00'): 0.6124, Timestamp('2016-02-09 15:24:00'): 0.5423, Timestamp('2016-01-14 08:26:00'): 0.6597, Timestamp('2015-11-23 15:27:00'): 0.8176, Timestamp('2015-11-09 14:43:00'): 0.0, Timestamp('2015-11-09 14:42:00'): 0.7845, Timestamp('2015-11-02 10:12:00'): 0.9838, Timestamp('2015-10-27 08:22:00'): 0.9334, Timestamp('2015-10-13 08:31:00'): 0.4215, Timestamp('2015-10-02 13:49:00'): 0.8176, Timestamp('2015-09-18 09:47:00'): 0.0, Timestamp('2015-08-26 09:16:00'): 0.9081, Timestamp('2015-08-07 13:31:00'): 0.3182, Timestamp('2015-06-29 08:10:00'): 0.9792, Timestamp('2015-06-26 16:03:00'): 0.9792, Timestamp('2015-05-14 08:30:00'): 0.802, Timestamp('2015-05-13 14:57:00'): 0.9538, Timestamp('2015-04-08 08:41:00'): 0.9538, Timestamp('2015-04-08 08:39:00'): 0.7184, Timestamp('2015-02-23 13:41:00'): 0.4767, Timestamp('2015-01-22 14:28:00'): -0.3182, Timestamp('2015-01-09 13:24:00'): 0.8625, Timestamp('2015-01-06 07:51:00'): 0.6124}
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import matplotlib.pyplot as pl
pl.style.use("default")
import backtrader as bt
import backtrader.indicators as btind
from datetime import datetime
import os.path
import sys
class Sentiment(bt.Indicator):
lines = ('sentiment',)
plotinfo = dict(
plotymargin=0.15,
plothlines=[0],
plotyticks=[1.0, 0, -1.0])
def next(self):
self.date = self.data.datetime
date = bt.num2date(self.date[0]).date()
prev_sentiment = self.sentiment
if date in date_sentiments:
self.sentiment = date_sentiments[date]
self.lines.sentiment[0] = self.sentiment
class SentimentStrat(bt.Strategy):
params = (
('period', 15),
('printlog', True),
)
def log(self, txt, dt=None, doprint=False):
''' Logging function for this strategy'''
if self.params.printlog or doprint:
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# Keep track of pending orders
self.order = None
self.buyprice = None
self.buycomm = None
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.period)
self.date = self.data.datetime
self.sentiment = None
Sentiment(self.data)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else: # Sell
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm))
### Main Strat ###
def next(self):
# log closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
date = bt.num2date(self.date[0]).date()
prev_sentiment = self.sentiment
if date in date_sentiments:
self.sentiment = date_sentiments[date]
# Check if an order is pending. if yes, we cannot send a 2nd one
if self.order:
return
print(self.sentiment)
# If not in the market and previous sentiment not none
if not self.position and prev_sentiment:
# buy if current close more than sma AND sentiment increased by >= 0.5
if self.dataclose[0] > self.sma[0] and self.sentiment - prev_sentiment >= 0.5:
self.log('BUY CREATE, %.2f' % self.dataclose[0])
self.order = self.buy()
# Already in the market and previous sentiment not none
elif prev_sentiment:
# sell if current close less than sma AND sentiment decreased by >= 0.5
if self.dataclose[0] < self.sma[0] and self.sentiment - prev_sentiment <= -0.5:
self.log('SELL CREATE, %.2f' % self.dataclose[0])
self.order = self.sell()
def stop(self):
self.log('(MA Period %2d) Ending Value %.2f' %
(self.params.period, self.broker.getvalue()), doprint=True)
if __name__ == '__main__':
cerebro = bt.Cerebro()
# Strategy
cerebro.addstrategy(SentimentStrat)
# Data Feed
data = bt.feeds.YahooFinanceData(
dataname = 'JBFCF',
fromdate = min(date_sentiments.keys()),
todate = datetime.now().date(),
reverse = False
)
cerebro.adddata(data)
cerebro.broker.setcash(100000.0)
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
cerebro.broker.setcommission(commission=0.001)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot()
Starting Portfolio Value: 100000.00
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-9-a24ff364b1c0> in <module> 141 cerebro.broker.setcommission(commission=0.001) 142 print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) --> 143 cerebro.run() 144 print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue()) 145 ~/.local/lib/python3.8/site-packages/backtrader/cerebro.py in run(self, **kwargs) 1125 # let's skip process "spawning" 1126 for iterstrat in iterstrats: -> 1127 runstrat = self.runstrategies(iterstrat) 1128 self.runstrats.append(runstrat) 1129 if self._dooptimize: ~/.local/lib/python3.8/site-packages/backtrader/cerebro.py in runstrategies(self, iterstrat, predata) 1291 self._runonce_old(runstrats) 1292 else: -> 1293 self._runonce(runstrats) 1294 else: 1295 if self.p.oldsync: ~/.local/lib/python3.8/site-packages/backtrader/cerebro.py in _runonce(self, runstrats) 1650 ''' 1651 for strat in runstrats: -> 1652 strat._once() 1653 strat.reset() # strat called next by next - reset lines 1654 ~/.local/lib/python3.8/site-packages/backtrader/lineiterator.py in _once(self) 295 296 for indicator in self._lineiterators[LineIterator.IndType]: --> 297 indicator._once() 298 299 for observer in self._lineiterators[LineIterator.ObsType]: ~/.local/lib/python3.8/site-packages/backtrader/lineiterator.py in _once(self) 315 # indicators are each called with its min period 316 self.preonce(0, self._minperiod - 1) --> 317 self.oncestart(self._minperiod - 1, self._minperiod) 318 self.once(self._minperiod, self.buflen()) 319 ~/.local/lib/python3.8/site-packages/backtrader/indicator.py in oncestart_via_nextstart(self, start, end) 122 123 self.advance() --> 124 self.nextstart() 125 126 def once_via_next(self, start, end): ~/.local/lib/python3.8/site-packages/backtrader/lineiterator.py in nextstart(self) 345 346 # Called once for 1st full calculation - defaults to regular next --> 347 self.next() 348 349 def next(self): <ipython-input-9-a24ff364b1c0> in next(self) 23 if date in date_sentiments: 24 self.sentiment = date_sentiments[date] ---> 25 self.lines.sentiment[0] = self.sentiment 26 27 ~/.local/lib/python3.8/site-packages/backtrader/linebuffer.py in __setitem__(self, ago, value) 220 value (variable): value to be set 221 ''' --> 222 self.array[self.idx + ago] = value 223 for binding in self.bindings: 224 binding[ago] = value TypeError: must be real number, not LineBuffer
from urllib.request import urlopen
from bs4 import BeautifulSoup
from datetime import datetime
import time
import nltk
import warnings
warnings.filterwarnings('ignore')
from nltk.sentiment.vader import SentimentIntensityAnalyzer
# nltk.download('vader_lexicon')
sia = SentimentIntensityAnalyzer()
base_url = "http://rappler.com"
page = urlopen(base_url+'/previous-articles?filterMeta=Jollibee').read()
soup = BeautifulSoup(page, features="html.parser")
posts = soup.findAll("div", {"class": "col-xs-12 col-sm-8"})
date_sentiments = {}
for post in posts[:10]: #default up to 50 posts
time.sleep(1)
url = post.a['href']
#date = post.time.text
date_string = post.span.text.split('-')[0].strip()
date = datetime.strptime(date_string, '%b %d, %Y').date()
print(date, base_url+url)
try:
link_page = urlopen(base_url+url).read()
except:
url = url[:-2]
link_page = urlopen(url).read()
link_soup = BeautifulSoup(link_page)
sentences = link_soup.findAll("p")
passage = ""
for sentence in sentences:
passage += sentence.text
sentiment = sia.polarity_scores(passage)['compound']
date_sentiments[date] = sentiment
date_sentiments
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import matplotlib.pyplot as pl
pl.style.use("default")
import backtrader as bt
import backtrader.indicators as btind
from datetime import datetime
import os.path
import sys
class Sentiment(bt.Indicator):
lines = ('sentiment',)
plotinfo = dict(
plotymargin=0.15,
plothlines=[0],
plotyticks=[1.0, 0, -1.0])
def next(self):
self.date = self.data.datetime
date = bt.num2date(self.date[0]).date()
prev_sentiment = self.sentiment
if date in date_sentiments:
print(date_sentiments[date])
self.sentiment = date_sentiments[date]
self.lines.sentiment[0] = self.sentiment
class SentimentStrat(bt.Strategy):
params = (
('period', 15),
('printlog', True),
)
def log(self, txt, dt=None, doprint=False):
''' Logging function for this strategy'''
if self.params.printlog or doprint:
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# Keep track of pending orders
self.order = None
self.buyprice = None
self.buycomm = None
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.period)
self.date = self.data.datetime
self.sentiment = None
Sentiment(self.data)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else: # Sell
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm))
### Main Strat ###
def next(self):
# log closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
date = bt.num2date(self.date[0]).date()
prev_sentiment = self.sentiment
if date in date_sentiments:
self.sentiment = date_sentiments[date]
# Check if an order is pending. if yes, we cannot send a 2nd one
if self.order:
return
print(self.sentiment)
# If not in the market and previous sentiment not none
if not self.position and prev_sentiment:
# buy if current close more than sma AND sentiment increased by >= 0.5
if self.dataclose[0] > self.sma[0] and self.sentiment - prev_sentiment >= 0.5:
self.log('BUY CREATE, %.2f' % self.dataclose[0])
self.order = self.buy()
# Already in the market and previous sentiment not none
elif prev_sentiment:
# sell if current close less than sma AND sentiment decreased by >= 0.5
if self.dataclose[0] < self.sma[0] and self.sentiment - prev_sentiment <= -0.5:
self.log('SELL CREATE, %.2f' % self.dataclose[0])
self.order = self.sell()
def stop(self):
self.log('(MA Period %2d) Ending Value %.2f' %
(self.params.period, self.broker.getvalue()), doprint=True)
if __name__ == '__main__':
cerebro = bt.Cerebro()
# Strategy
cerebro.addstrategy(SentimentStrat)
# Data Feed
data = bt.feeds.YahooFinanceData(
dataname = 'JBFCF',
fromdate = min(date_sentiments.keys()),
todate = datetime.now().date(),
reverse = False
)
cerebro.adddata(data)
cerebro.broker.setcash(100000.0)
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
cerebro.broker.setcommission(commission=0.001)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot()