0

I'm trying to make telegram alarm bot but encountered error such as "TypeError: Object of type DataFrame is not JSON serializable"

Here is my code:

import FinanceDataReader as fdr
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import pandas as pd
import datetime
from datetime import date
import sys
fdr.__version__
import schedule
import time
import pytz
import telegram

count = 1

def job():
    # 전역변수 설정
    global count
    count += 1
    # 한국시각, 주말 설정
    now = datetime.datetime.now(pytz.timezone('Asia/Seoul'))
    today = date.today()
    weekend = today.weekday()
    # 예외시간 설정. 9시 이전 및 15시 이후로는 알람을 보내지 않음
    if now.hour >= 15 or now.hour <= 9 or weekend == [5, 6]:
        return
    API_KEY = 'My key'
    bot = telegram.Bot(token=API_KEY)
    bot.get_updates()
#     for i in updates:
#     print(i.message['chat']['id'])
    
    # 코스닥지수
    code = 'KQ11'
    df = fdr.DataReader('KQ11','2022-08').reset_index()
    # 3,5,10 이동평균 딕셔너리에 할당
    df['close_sma3d'] = df['Close'].rolling(3).mean()
    df['close_sma5d'] = df['Close'].rolling(5).mean()
    df['close_sma10d'] = df['Close'].rolling(10).mean()
    # dataframe 재구성
#     df = df.to_dict()
#     df.rename(columns={0: 'Date', 1: 'Close', 2: 'Open', 3: 'High', 4: 'Low', 5: 'Volume', 6: 'Change', 7: 'close_sma3d', 8: 'close_sma5d', 9: 'close_sma10d'}, inplace = True)
    df2 = df.loc[: ,['Date','Close', 'close_sma3d','close_sma5d','close_sma10d']].iloc[-1:]
    alerts = df2[(df2['Close'] > df2['close_sma3d']) | (df2['Close'] > df2['close_sma5d']) | (df2['Close'] > df2['close_sma10d'])]
    
    def display(row):
        print(f" - {row['Date']} Signal 발생! 코스닥_현재가 {row['Close']} 3일이동평균 {row['close_sma3d']:.2f} 5일이동평균 {row['close_sma5d']:.2f} 10일이동평균 {row['close_sma10d']:.2f}")
    Market_timing = alerts.apply(display, axis=1)
    
    if count % 1 == 0:
        bot.sendMessage(chat_id = 'Mykey', text = Market_timing)
    else:
        print('대기 중입니다..')
                        
# 2 시간 마다 실행
schedule.every(1).minutes.do(job)

print('Start App..')

while True:
    schedule.run_pending()
    time.sleep(1)

And here is the error:

TypeError                                 Traceback (most recent call last)
Input In [3], in <cell line: 49>()
     46 print('Start App..')
     48 while True:
---> 49     schedule.run_pending()
     50     time.sleep(1)

File ~\miniconda3\envs\py38\lib\site-packages\schedule\__init__.py:780, in run_pending()
    776 def run_pending() -> None:
    777     """Calls :meth:`run_pending <Scheduler.run_pending>` on the
    778     :data:`default scheduler instance <default_scheduler>`.
    779     """
--> 780     default_scheduler.run_pending()

File ~\miniconda3\envs\py38\lib\site-packages\schedule\__init__.py:100, in Scheduler.run_pending(self)
     98 runnable_jobs = (job for job in self.jobs if job.should_run)
     99 for job in sorted(runnable_jobs):
--> 100     self._run_job(job)

File ~\miniconda3\envs\py38\lib\site-packages\schedule\__init__.py:172, in Scheduler._run_job(self, job)
    171 def _run_job(self, job: "Job") -> None:
--> 172     ret = job.run()
    173     if isinstance(ret, CancelJob) or ret is CancelJob:
    174         self.cancel_job(job)

File ~\miniconda3\envs\py38\lib\site-packages\schedule\__init__.py:661, in Job.run(self)
    658     return CancelJob
    660 logger.debug("Running job %s", self)
--> 661 ret = self.job_func()
    662 self.last_run = datetime.datetime.now()
    663 self._schedule_next_run()

Input In [3], in job()
     35 Market_timing = alerts.apply(display, axis=1)
     37 if count % 1 == 0:
---> 38     bot.sendMessage(chat_id = '1760120639', text = Market_timing)
     39 else:
     40     print('대기 중입니다..')

File ~\miniconda3\envs\py38\lib\site-packages\telegram\bot.py:133, in log.<locals>.decorator(*args, **kwargs)
    130 @functools.wraps(func)
    131 def decorator(*args: object, **kwargs: object) -> RT:  # pylint: disable=W0613
    132     logger.debug('Entering: %s', func.__name__)
--> 133     result = func(*args, **kwargs)
    134     logger.debug(result)
    135     logger.debug('Exiting: %s', func.__name__)

File ~\miniconda3\envs\py38\lib\site-packages\telegram\bot.py:525, in Bot.send_message(self, chat_id, text, parse_mode, disable_web_page_preview, disable_notification, reply_to_message_id, reply_markup, timeout, api_kwargs, allow_sending_without_reply, entities, protect_content)
    522 if entities:
    523     data['entities'] = [me.to_dict() for me in entities]
--> 525 return self._message(  # type: ignore[return-value]
    526     'sendMessage',
    527     data,
    528     disable_notification=disable_notification,
    529     reply_to_message_id=reply_to_message_id,
    530     reply_markup=reply_markup,
    531     allow_sending_without_reply=allow_sending_without_reply,
    532     timeout=timeout,
    533     api_kwargs=api_kwargs,
    534     protect_content=protect_content,
    535 )

File ~\miniconda3\envs\py38\lib\site-packages\telegram\bot.py:339, in Bot._message(self, endpoint, data, reply_to_message_id, disable_notification, reply_markup, allow_sending_without_reply, timeout, api_kwargs, protect_content)
    336     else:
    337         data['media'].parse_mode = None
--> 339 result = self._post(endpoint, data, timeout=timeout, api_kwargs=api_kwargs)
    341 if result is True:
    342     return result

File ~\miniconda3\envs\py38\lib\site-packages\telegram\bot.py:298, in Bot._post(self, endpoint, data, timeout, api_kwargs)
    295 # Drop any None values because Telegram doesn't handle them well
    296 data = {key: value for key, value in data.items() if value is not None}
--> 298 return self.request.post(
    299     f'{self.base_url}/{endpoint}', data=data, timeout=effective_timeout
    300 )

File ~\miniconda3\envs\py38\lib\site-packages\telegram\utils\request.py:364, in Request.post(self, url, data, timeout)
    359     result = self._request_wrapper('POST', url, fields=data, **urlopen_kwargs)
    360 else:
    361     result = self._request_wrapper(
    362         'POST',
    363         url,
--> 364         body=json.dumps(data).encode('utf-8'),
    365         headers={'Content-Type': 'application/json'},
    366         **urlopen_kwargs,
    367     )
    369 return self._parse(result)

File ~\miniconda3\envs\py38\lib\json\__init__.py:231, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    226 # cached encoder
    227 if (not skipkeys and ensure_ascii and
    228     check_circular and allow_nan and
    229     cls is None and indent is None and separators is None and
    230     default is None and not sort_keys and not kw):
--> 231     return _default_encoder.encode(obj)
    232 if cls is None:
    233     cls = JSONEncoder

File ~\miniconda3\envs\py38\lib\json\encoder.py:199, in JSONEncoder.encode(self, o)
    195         return encode_basestring(o)
    196 # This doesn't pass the iterator directly to ''.join() because the
    197 # exceptions aren't as detailed.  The list call should be roughly
    198 # equivalent to the PySequence_Fast that ''.join() would do.
--> 199 chunks = self.iterencode(o, _one_shot=True)
    200 if not isinstance(chunks, (list, tuple)):
    201     chunks = list(chunks)

File ~\miniconda3\envs\py38\lib\json\encoder.py:257, in JSONEncoder.iterencode(self, o, _one_shot)
    252 else:
    253     _iterencode = _make_iterencode(
    254         markers, self.default, _encoder, self.indent, floatstr,
    255         self.key_separator, self.item_separator, self.sort_keys,
    256         self.skipkeys, _one_shot)
--> 257 return _iterencode(o, 0)

File ~\miniconda3\envs\py38\lib\json\encoder.py:179, in JSONEncoder.default(self, o)
    160 def default(self, o):
    161     """Implement this method in a subclass such that it returns
    162     a serializable object for ``o``, or calls the base implementation
    163     (to raise a ``TypeError``).
   (...)
    177 
    178     """
--> 179     raise TypeError(f'Object of type {o.__class__.__name__} '
    180                     f'is not JSON serializable')

TypeError: Object of type DataFrame is not JSON serializable

I tried TypeError: Object of type 'DataFrame' is not JSON serializable this method but had a problem making a column.

Any help would be appreciated.

Joshua Chung
  • 113
  • 9
  • "I tried using the code from another answer but (vague description of problem)" is not answerable. We can't see **what** you tried, or **how**, nor can we see **what went wrong**, nor is that a **question** ("Any help would be appreciated" also [does not qualify](https://meta.stackoverflow.com/questions/284236)). Please read [ask] and [mre]. – Karl Knechtel Sep 01 '22 at 04:25
  • The problem, apparently, is that you cannot just send anything you like using `bot.sendMessage`; you must send something that can be serialized as JSON. Keep in mind that the received message will just be JSON with some particular structure; the code that receives the message will have to *know* that it represents a Dataframe, and create it back from the JSON. – Karl Knechtel Sep 01 '22 at 04:26
  • For example, you can take the Dataframe apart into a list of lists, and send that. – Karl Knechtel Sep 01 '22 at 04:28

0 Answers0