Esempio n. 1
0
def is_valid_trading_day(trading_day: arrow.arrow.Arrow) -> bool:
    """Decides if a given trading day is valid and returns a boolean."""
    trading_day_formatted = trading_day.format("YYYY-MM-DD")
    is_valid = False
    nyse = ec.get_calendar("NYSE")
    nasdaq = ec.get_calendar("NASDAQ")
    if nyse.is_session(trading_day_formatted) and nasdaq.is_session(
            trading_day_formatted):
        is_valid = True
    return is_valid
Esempio n. 2
0
    def read(cls, rootdir):
        path = cls.metadata_path(rootdir)
        with open(path) as fp:
            raw_data = json.load(fp)

            try:
                version = raw_data['version']
            except KeyError:
                # Version was first written with version 1, assume 0,
                # if version does not match.
                version = 0

            default_ohlc_ratio = raw_data['ohlc_ratio']

            if version >= 1:
                minutes_per_day = raw_data['minutes_per_day']
            else:
                # version 0 always assumed US equities.
                minutes_per_day = US_EQUITIES_MINUTES_PER_DAY

            if version >= 2:
                calendar = get_calendar(raw_data['calendar_name'])
                start_session = pd.Timestamp(raw_data['start_session'],
                                             tz='UTC')
                end_session = pd.Timestamp(raw_data['end_session'], tz='UTC')
            else:
                # No calendar info included in older versions, so
                # default to NYSE.
                calendar = get_calendar('XNYS')

                start_session = pd.Timestamp(raw_data['first_trading_day'],
                                             tz='UTC')
                end_session = calendar.minute_to_session_label(
                    pd.Timestamp(raw_data['market_closes'][-1],
                                 unit='m',
                                 tz='UTC'))

            if version >= 3:
                ohlc_ratios_per_sid = raw_data['ohlc_ratios_per_sid']
                if ohlc_ratios_per_sid is not None:
                    ohlc_ratios_per_sid = keymap(int, ohlc_ratios_per_sid)
            else:
                ohlc_ratios_per_sid = None

            return cls(
                default_ohlc_ratio,
                ohlc_ratios_per_sid,
                calendar,
                start_session,
                end_session,
                minutes_per_day,
                version=version,
            )
Esempio n. 3
0
class HistoricalPriceRecord(
        collections.namedtuple(
            "HistoricalPriceRecord",
            ["time", "open", "high", "low", "close", "volume"])):

    __slots__ = ()

    _krx_timezone = get_calendar("XKRX").tz

    @classmethod
    def from_tuple(cls, tup):
        if "일자" in tup._fields:
            dt = datetime.datetime.strptime(tup.일자, "%Y%m%d")
            dt = cls._krx_timezone.localize(dt)
            time = dt.timestamp() * (10**6)  # pylint: disable=redefined-outer-name
        elif "체결시간" in tup._fields:
            dt = datetime.datetime.strptime(tup.체결시간, "%Y%m%d%H%M%S")
            dt = cls._krx_timezone.localize(dt)
            time = dt.timestamp() * (10**6)
        else:
            raise KiwoomOpenApiPlusError("Cannot specify time")
        open = abs(float(tup.시가))  # pylint: disable=redefined-builtin
        high = abs(float(tup.고가))
        low = abs(float(tup.저가))
        close = abs(float(tup.현재가))
        volume = abs(float(tup.거래량))
        return cls(time, open, high, low, close, volume)

    @classmethod
    def records_from_dataframe(cls, df):
        return [cls.from_tuple(tup) for tup in df[::-1].itertuples()]

    @classmethod
    def dict_records_from_dataframe(cls, df):
        return [msg._asdict() for msg in cls.records_from_dataframe(df)]
Esempio n. 4
0
def parse_args(argv):
    usage = "usage: %s CALENDAR [[[DAY] MONTH] YEAR]" % argv[0]

    if len(argv) == 1 or "--help" in argv or "-h" in argv:
        error(usage)

    if len(argv) > 1:
        from exchange_calendars import get_calendar

        try:
            calendar = get_calendar(argv[1])
        except Exception as e:
            error(str(e))

    if len(argv) == 2:
        import datetime

        now = datetime.datetime.now()
        year = now.year
        month = now.month
    elif len(argv) == 3:
        year = _int_arg(argv[2], "YEAR")
        month = None
    elif len(argv) == 4:
        month = _int_arg(argv[2], "MONTH")
        year = _int_arg(argv[3], "YEAR")
    else:
        error(usage)

    return calendar, year, month
Esempio n. 5
0
def is_currently_in_session():
    krx_calendar = get_calendar('XKRX')
    local_timezone = get_localzone()
    now = Timestamp.now(tz=local_timezone)
    previous_open = krx_calendar.previous_open(now)
    next_close = krx_calendar.next_close(previous_open)
    return previous_open <= now <= next_close
Esempio n. 6
0
def get_last_krx_close_datetime():
    krx_calendar = get_calendar('XKRX')
    local_timezone = get_localzone()
    now = Timestamp.now(tz=local_timezone)
    last_close = krx_calendar.previous_close(now)
    last_close = last_close.astimezone(local_timezone)
    last_close = last_close.to_pydatetime()
    return last_close
Esempio n. 7
0
    def get_trading_days(self, start, end):
        nyse = tc.get_calendar("NYSE")
        df = nyse.sessions_in_range(pd.Timestamp(start, tz=pytz.UTC),
                                    pd.Timestamp(end, tz=pytz.UTC))
        trading_days = []
        for day in df:
            trading_days.append(str(day)[:10])

        return trading_days
    def __init__(self, filename, library=None):
        self._loader = KrxHistoricalDailyPriceDataLoader(filename)
        self._store = SQLiteStore(filename)
        self._calendar = get_calendar("XKRX")

        if library is None:
            library = "XKRX-ALL-BACKTEST"

        self._library = self._store.get_or_create_library(library)
Esempio n. 9
0
    def __init__(self): # pylint: disable=super-init-not-called
        self._calendar = self.p.calendar # pylint: disable=no-member

        if isinstance(self._calendar, string_types):
            from exchange_calendars import get_calendar
            self._calendar = get_calendar(self._calendar)

        self.dcache = DatetimeIndex([0.0])
        self.idcache = DataFrame(index=DatetimeIndex([0.0]))
        self.csize = datetime.timedelta(days=self.p.cachesize) # pylint: disable=no-member
Esempio n. 10
0
def test_is_open_on_minute(benchmark):
    xhkg = get_calendar("XHKG")
    timestamps = [
        pd.Timestamp("2019-10-11 01:20:00", tz=UTC),  # pre open
        pd.Timestamp("2019-10-11 01:30:00", tz=UTC),  # open
        pd.Timestamp("2019-10-11 01:31:00", tz=UTC),  # first minute
        pd.Timestamp("2019-10-11 04:31:00", tz=UTC),  # in break
        pd.Timestamp("2019-10-11 08:00:00", tz=UTC),  # close
        pd.Timestamp("2019-10-11 08:01:00", tz=UTC),  # post close
    ]
    benchmark(is_open_on_minute_bench, xhkg, timestamps)
Esempio n. 11
0
    def __init__(self, filename, library=None, library_delisted=None):
        self._downloader = KrxHistoricalDailyPriceDataDownloader()
        self._store = SQLiteStore(filename)
        self._calendar = get_calendar("XKRX")

        if library is None:
            library = "XKRX"
        if library_delisted is None:
            library_delisted = "XKRX-DELISTED"

        self._library = self._store.get_or_create_library(library)
        self._library_delisted = self._store.get_or_create_library(
            library_delisted)
Esempio n. 12
0
    def __init__(self):
        super().__init__()

        self._calendar = self.p.calendar

        if isinstance(self._calendar, string_types):
            from exchange_calendars import get_calendar

            self._calendar = get_calendar(self._calendar)

        self.dcache = DatetimeIndex([0.0])
        self.idcache = DataFrame(index=DatetimeIndex([0.0]))
        self.csize = datetime.timedelta(days=self.p.cachesize)
    def __init__(self):
        self._headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36",
        }

        self._stocks = None
        self._stocks_delisted = None

        self._bld = "dbms/MDC/STAT/standard/MDCSTAT01701"

        self._calendar = get_calendar("XKRX")
        self._start_date = self._calendar.first_session.astimezone(
            self._calendar.tz
        ).normalize()
Esempio n. 14
0
    def sessions(self):
        if 'calendar' in self._table.attrs.attrs:
            # backwards compatibility with old formats, will remove
            return DatetimeIndex(self._table.attrs['calendar'], tz='UTC')
        else:
            cal = get_calendar(self._table.attrs['calendar_name'])
            start_session_ns = self._table.attrs['start_session_ns']
            start_session = Timestamp(start_session_ns, tz='UTC')

            end_session_ns = self._table.attrs['end_session_ns']
            end_session = Timestamp(end_session_ns, tz='UTC')

            sessions = cal.sessions_in_range(start_session, end_session)

            return sessions
Esempio n. 15
0
def check_holidays(
    holiday_key_path,
    calendar_column,
    holiday_column,
    date_format,
    delimiter,
    strip_x_from_cal_name,
    answer_key_calendar_name,
    min_date,
    calendars,
):
    data = pd.read_csv(
        holiday_key_path,
        sep=delimiter,
        parse_dates=[holiday_column],
        date_parser=partial(pd.to_datetime, format=date_format, utc=True),
    )

    for calendar_name in default_calendar_names:
        if calendars and calendar_name not in calendars:
            continue

        cal = get_calendar(calendar_name)

        if answer_key_calendar_name:
            csv_cal_code = answer_key_calendar_name
        elif strip_x_from_cal_name:
            # Convert the calendar name to the format expected in the CSV.
            csv_cal_code = calendar_name.lstrip("X")
        else:
            csv_cal_code = calendar_name

        holidays = set(
            data[holiday_column][data[calendar_column] == csv_cal_code])

        if holidays:
            start = max(cal.first_session, min(holidays), min_date)
            end = min(cal.last_session, max(holidays))

            _check_range(start, end, holidays, cal, calendar_name)
        else:
            click.secho(
                "No holidays found for {} in the holiday key.".format(
                    calendar_name, ),
                fg="yellow",
            )

        click.echo()
Esempio n. 16
0
def exchange_open_check(symbol: str):
    """Check if the exchange for the given symbol is open today or not."""
    sub = next(item for item in db if item['symbol'] == symbol)
    exchange = sub['exchange_iso']
    time_zone = sub['time_zone']
    now = pd.Timestamp.today(tz=time_zone)

    if exchange:
        calendar = ecals.get_calendar(exchange)
        exchange_open = calendar.is_open_on_minute(now)
        print(
            f'{symbol} - {exchange} - {time_zone} - {now} - Open Now?: {exchange_open}'
        )
        return exchange_open
    else:
        return False
Esempio n. 17
0
def eligible_for_rebalance(trading_day: Arrow) -> bool:
    """Decides if a given trading day should trigger a rebalance and returns a boolean."""
    is_eligible = False
    eligible_months = [2, 5, 8, 11]
    nyse = ec.get_calendar("NYSE")

    def _is_last_trading_day_in_month(_trading_day):
        last_date_in_month = pd.Timestamp(_trading_day.ceil("month").datetime)

        return nyse.previous_close(
            last_date_in_month).date() == _trading_day.date()

    if trading_day.month in eligible_months and _is_last_trading_day_in_month(
            trading_day):
        is_eligible = True

    return is_eligible
Esempio n. 18
0
    def test_detect_non_market_minutes(self):
        cal = get_calendar("NYSE")
        # NOTE: This test is here instead of being on the base class for all
        # calendars because some of our calendars are 24/7, which means there
        # aren't any non-market minutes to find.
        day0 = cal.minutes_for_sessions_in_range(
            pd.Timestamp("2013-07-03", tz=UTC),
            pd.Timestamp("2013-07-03", tz=UTC),
        )
        for minute in day0:
            self.assertTrue(cal.is_open_on_minute(minute))

        day1 = cal.minutes_for_sessions_in_range(
            pd.Timestamp("2013-07-05", tz=UTC),
            pd.Timestamp("2013-07-05", tz=UTC),
        )
        for minute in day1:
            self.assertTrue(cal.is_open_on_minute(minute))

        def NYSE_timestamp(s):
            return pd.Timestamp(s, tz="America/New_York").tz_convert(UTC)

        non_market = [
            # After close.
            NYSE_timestamp("2013-07-03 16:01"),
            # Holiday.
            NYSE_timestamp("2013-07-04 10:00"),
            # Before open.
            NYSE_timestamp("2013-07-05 9:29"),
        ]
        for minute in non_market:
            self.assertFalse(cal.is_open_on_minute(minute), minute)

            input_ = pd.to_datetime(
                np.hstack([day0.values, minute.asm8, day1.values]),
                utc=True,
            )
            with self.assertRaises(ValueError) as e:
                cal.minute_index_to_session_labels(input_)

            exc_str = str(e.exception)
            self.assertIn("First Bad Minute: {}".format(minute), exc_str)
 def _make_sure_dates_are_initialized_properly(self, dtbegin, dtend,
                                               granularity):
     """
     dates may or may not be specified by the user.
     when they do, they are probably don't include NY timezome data
     also, when granularity is minute, we want to make sure we get data when
     market is opened. so if it doesn't - let's set end date to be last
     known minute with opened market.
     this nethod takes care of all these issues.
     :param dtbegin:
     :param dtend:
     :param granularity:
     :return:
     """
     if not dtend:
         dtend = pd.Timestamp('now', tz=NY)
     else:
         dtend = pd.Timestamp(pytz.timezone('UTC').localize(dtend)) if \
           not dtend.tzname() else dtend
     if granularity == Granularity.Minute:
         calendar = exchange_calendars.get_calendar(name='NYSE')
         while not calendar.is_open_on_minute(dtend.ceil(freq='T')):
             dtend = dtend.replace(hour=15,
                                   minute=59,
                                   second=0,
                                   microsecond=0)
             dtend -= timedelta(days=1)
     if not dtbegin:
         days = 30 if granularity == Granularity.Daily else 3
         delta = timedelta(days=days)
         dtbegin = dtend - delta
     else:
         dtbegin = pd.Timestamp(pytz.timezone('UTC').localize(dtbegin)) if \
           not dtbegin.tzname() else dtbegin
     while dtbegin > dtend:
         # if we start the script during market hours we could get this
         # situation. this resolves that.
         dtbegin -= timedelta(days=1)
     return dtbegin.astimezone(NY), dtend.astimezone(NY)
class KiwoomOpenApiPlusPriceEventChannel(Logging):

    _krx_timezone = get_calendar("XKRX").tz

    def __init__(self, stub):
        self._stub = stub
        self._fid_list = KiwoomOpenApiPlusRealType.get_fids_by_realtype_name(
            "주식시세")

        self._request_observer = QueueBasedIterableObserver()
        self._request_iterator = iter(self._request_observer)
        self._response_iterator = self._stub.BidirectionalRealCall(
            self._request_iterator)
        self._response_subject = Subject()
        self._response_scheduler_max_workers = 8
        self._response_scheduler = ThreadPoolScheduler(
            self._response_scheduler_max_workers)
        self._buffered_response_iterator = QueueBasedBufferedIterator(
            self._response_iterator)
        self._response_observable = rx.from_iterable(
            self._buffered_response_iterator, self._response_scheduler)
        self._response_subscription = self._response_observable.subscribe(
            self._response_subject)

        self._subjects_by_code = {}

        self.initialize()

    def close(self):
        for _code, (_subject, subscription) in self._subjects_by_code.items():
            subscription.dispose()
        self._response_subscription.dispose()
        self._buffered_response_iterator.stop()
        self._response_iterator.cancel()

    def __del__(self):
        self.close()

    def initialize(self):
        request = KiwoomOpenApiPlusService_pb2.BidirectionalRealRequest()
        request.initialize_request.fid_list.extend(self._fid_list)  # pylint: disable=no-member
        self._request_observer.on_next(request)

    def register_code(self, code):
        request = KiwoomOpenApiPlusService_pb2.BidirectionalRealRequest()
        code_list = [code]
        fid_list = KiwoomOpenApiPlusRealType.get_fids_by_realtype_name("주식시세")
        request.register_request.code_list.extend(code_list)  # pylint: disable=no-member
        request.register_request.fid_list.extend(fid_list)  # pylint: disable=no-member
        self._request_observer.on_next(request)
        self.logger.debug("Registering code %s for real events", code)

    def is_for_code(self, response, code):
        return response.arguments[0].string_value == code

    def filter_for_code(self, code):
        return ops.filter(lambda response: self.is_for_code(response, code))

    def is_valid_price_event(self, response):
        return all(name in response.single_data.names
                   for name in ["20", "27", "28"])

    def filter_price_event(self):
        return ops.filter(self.is_valid_price_event)

    def time_to_timestamp(self, fid20):
        dt = datetime.datetime.now(self._krx_timezone).date()
        tm = datetime.datetime.strptime(fid20, "%H%M%S").time()
        dt = datetime.datetime.combine(dt, tm)
        dt = self._krx_timezone.localize(dt)
        return dt.timestamp() * (10**6)

    def event_to_dict(self, response):
        single_data = dict(
            zip(response.single_data.names, response.single_data.values))
        result = {
            "time": self.time_to_timestamp(single_data["20"]),
            "bid": abs(float(single_data["28"])),
            "ask": abs(float(single_data["27"])),
        }
        return result

    def convert_to_dict(self):
        return ops.map(self.event_to_dict)

    def get_observable_for_code(self, code):
        self.register_code(code)
        if code not in self._subjects_by_code:
            subject = Subject()
            subscription = self._response_subject.pipe(
                self.filter_for_code(code),
                self.filter_price_event(),
                self.convert_to_dict(),
            ).subscribe(subject)
            self._subjects_by_code[code] = (subject, subscription)
        subject, subscription = self._subjects_by_code[code]
        return subject
Esempio n. 21
0
    # single holiday
    schedule = cal.schedule('2018-01-01', '2018-01-01')
    assert_frame_equal(schedule, empty)

    # weekend and holiday
    schedule = cal.schedule('2017-12-30', '2018-01-01')
    assert_frame_equal(schedule, empty)

############################################
# TESTS FOR EXCHANGE_CALENDAR INTEGRATION  #
############################################


mcal_iepa = get_calendar("IEPA")
ecal_iepa = ecal.get_calendar("IEPA")

start, end = ecal_iepa._closes[[0, -1]]

# No exchange_calendar has a close_offset so this class implements it for testing
class _TstExchangeCalendar:
    def __init__(self):
        self.a_test_var = "initialized"
    @property
    def open_offset(self):
        return -2
    @property
    def close_offset(self):
        return 3

Esempio n. 22
0
import datetime
import logging

from exchange_calendars import get_calendar

from koapy import KiwoomOpenApiPlusEntrypoint

logging.basicConfig(
    format="%(asctime)s [%(levelname)s] %(message)s - %(filename)s:%(lineno)d",
    level=logging.DEBUG,
)

krx_calendar = get_calendar("XKRX")

with KiwoomOpenApiPlusEntrypoint() as context:
    # 로그인 처리
    context.EnsureConnected()

    # 종목코드
    code = "005930"

    # 가장 최근 날짜
    start_date = datetime.datetime.now()

    # 날짜 값은 datetime.datetime 타입도 되고 문자열도 됨
    # 문자열 포맷은 구체적으로, 일봉위로는 YYYYMMDD 포맷, 분봉아래로는 YYYYMMDDhhmmss 포맷 지원

    # 가장 오래된 날짜 (거래소 개장일 기준 30일 전)
    end_date = start_date - 30 * krx_calendar.day

    # 참고로 end_date 은 주어진 시간보다 큰 (같지 않은) 레코드만 가져오도록 구현되어있기 때문에 설정에 주의
Esempio n. 23
0
def calendar() -> abc.Iterator[ExchangeCalendar]:
    yield get_calendar("XHKG")
Esempio n. 24
0
def get_krx_timezone():
    krx_calendar = get_calendar('XKRX')
    return krx_calendar.tz
Esempio n. 25
0
    def run(self):
        """
        Iterates through each day of the portfolio time period.

        Add record to positions dataframe and returns series identify if action should be taken if so.
        pull data for equity universe and identify top n stocks
        calculate position size for each stock and number of stocks to purchase
        liquidate all existing positions (add to transactions dataframe)
        enter new positions (add to transactions dataframe)
        Rebalance quarterly, before quarter ending months: end of February, May, August, and November.
        """
        day_0 = self.start_date
        # ensure that day_0 is a valid trading day, bump day forward until this is true
        while True:
            if qm.equities.historical.is_valid_trading_day(day_0):
                # day_0 is a valid trading day
                break
            day_0 = day_0.shift(days=+1)
        day_1 = day_0.shift(days=+1)

        # purchase shares, regardless of eligibility for rebalance
        print(f"Purchasing initial shares. {day_0=}")
        self._transactions = purchase_new_shares(
            self.s3_client,
            self.s3_bucket,
            day_0,
            self.capital_allocation,
            self.num_holdings,
            self.weighting,
            self.market_cap_percentile,
        )
        # updates available cash, this looks odd because the txn_dollars amount is negative if we have purchased a stock
        # it's positive when we've sold a stock
        self.available_cash = self.capital_allocation + self._transactions.loc[
            day_0.datetime]["txn_dollars"].sum()
        # initialize positions based on first day's transactions
        updated_positions = update_positions(self.s3_client, self.s3_bucket,
                                             self._transactions, day_0,
                                             self.available_cash)
        # assign rows to the `day_0` self._positions index
        self._positions = self._positions.append(updated_positions)
        nyse = ec.get_calendar("NYSE")
        for trading_day in trading_days_through_period(day_1, self.end_date):
            if eligible_for_rebalance(trading_day):
                print(
                    f"Rebalancing portfolio...{trading_day.format('YYYY-MM-DD')}"
                )
                self._transactions = self._transactions.append(
                    liquidate_shares(self.s3_client, self.s3_bucket,
                                     trading_day, self._transactions))
                # filters out any purchased shares from this day
                self.available_cash = (
                    self.available_cash +
                    self._transactions.loc[trading_day.datetime].query(
                        "txn_dollars>0")["txn_dollars"].sum())
                self._transactions = self._transactions.append(
                    purchase_new_shares(
                        self.s3_client,
                        self.s3_bucket,
                        trading_day,
                        self.available_cash,
                        self.num_holdings,
                        self.weighting,
                        self.market_cap_percentile,
                    ))
                # filters out any sold shares from this day
                self.available_cash = (
                    self.available_cash +
                    self._transactions.loc[trading_day.datetime].query(
                        "txn_dollars<0")["txn_dollars"].sum())

            updated_positions = update_positions(self.s3_client,
                                                 self.s3_bucket,
                                                 self._transactions,
                                                 trading_day,
                                                 self.available_cash)
            self._positions = self._positions.append(updated_positions)
        print("Calculating returns")
        self._returns = calculate_returns(self._positions)
"""
This script can be used to generate the CSV files used in the exchange calendar
tests. The CSVs include a calendar's sessions, open times, and close times.

This script can be run from the root of the repository with:
    $ python etc/make_exchange_calendar_test_csv.py <calendar_iso_code>
"""
import sys
from os.path import abspath, dirname, join, normpath

import pandas as pd

from exchange_calendars import get_calendar

cal_name = sys.argv[1]
cal = get_calendar(cal_name.upper())

df = pd.DataFrame(
    list(zip(cal.opens, cal.closes, cal.break_starts, cal.break_ends)),
    columns=["market_open", "market_close", "break_start", "break_end"],
    index=cal.closes.index,
)

destination = normpath(
    join(
        abspath(dirname(__file__)),
        "../tests/resources/{}.csv".format(cal_name.lower()),
    ), )
print("Writing test CSV file to {}".format(destination))

df.to_csv(destination, date_format="%Y-%m-%dT%H:%M:%SZ")
Esempio n. 27
0
class API:

    # 우선은 최대한 기존 Oanda 구현을 유지한채로 맞춰서 동작할 수 있도록 구현해놓고
    # 추후 동작이 되는게 확인되면 천천히 하단 API 에 맞게 최적화를 하는 방향으로 작업하는 것으로...

    _krx_timezone = get_calendar("XKRX").tz

    def __init__(self, context):
        self._context = context
        self._codes = self._context.GetCodeListByMarketAsList()

    def __getattr__(self, name):
        return getattr(self._context, name)

    def get_instruments_original(self, account,
                                 instruments):  # TODO: 계좌에 따라 시장이 다를 수 있음
        instruments = self.GetStockInfoAsDataFrame(instruments)
        instruments = [
            tup._asdict() for tup in instruments.itertuples(index=False)
        ]
        response = {"instruments": instruments}
        return response

    def get_instruments(self, account, instruments):
        if isinstance(instruments, str):
            instruments = [instruments]
        instruments = [inst for inst in instruments if inst in self._codes]
        response = {"instruments": instruments}
        return response

    def get_history(self, trcode, inputs, dtbegin=None, dtend=None):
        if trcode == "opt10079":
            code = inputs["종목코드"]
            interval = inputs["틱범위"]
            adjusted_price = inputs.get("수정주가구분") == "1"
            df = self.GetTickStockDataAsDataFrame(
                code, interval, dtend, dtbegin, adjusted_price=adjusted_price)
        elif trcode == "opt10080":
            code = inputs["종목코드"]
            interval = inputs["틱범위"]
            adjusted_price = inputs.get("수정주가구분") == "1"
            df = self.GetMinuteStockDataAsDataFrame(
                code, interval, dtend, dtbegin, adjusted_price=adjusted_price)
        elif trcode == "opt10081":
            code = inputs["종목코드"]
            adjusted_price = inputs.get("수정주가구분") == "1"
            df = self.GetDailyStockDataAsDataFrame(
                code, dtend, dtbegin, adjusted_price=adjusted_price)
        elif trcode == "opt10082":
            code = inputs["종목코드"]
            adjusted_price = inputs.get("수정주가구분") == "1"
            df = self.GetWeeklyStockDataAsDataFrame(
                code, dtend, dtbegin, adjusted_price=adjusted_price)
        elif trcode == "opt10083":
            code = inputs["종목코드"]
            adjusted_price = inputs.get("수정주가구분") == "1"
            df = self.GetMonthlyStockDataAsDataFrame(
                code, dtend, dtbegin, adjusted_price=adjusted_price)
        elif trcode == "opt10094":
            code = inputs["종목코드"]
            adjusted_price = inputs.get("수정주가구분") == "1"
            df = self.GetYearlyStockDataAsDataFrame(
                code, dtend, dtbegin, adjusted_price=adjusted_price)
        else:
            raise KiwoomOpenApiPlusError("Unexpected trcode %s" % trcode)

        candles = HistoricalPriceRecord.dict_records_from_dataframe(df)
        response = {"candles": candles}
        return response

    def get_positions(self, account):
        _summary, foreach = self.GetAccountEvaluationStatusAsSeriesAndDataFrame(
            account)
        positions = [{
            "instrument": tup.종목코드,
            "side": "buy",
            "units": float(tup.보유수량),
            "avgPrice": float(tup.매입금액) / float(tup.보유수량),
        } for tup in foreach.itertuples()]
        response = {"positions": positions}
        return response

    def get_account(self, account):
        summary, _foreach = self.GetAccountEvaluationStatusAsSeriesAndDataFrame(
            account)
        response = {
            "marginAvail": float(summary["D+2추정예수금"]),
            "balance": float(summary["추정예탁자산"]),
        }
        return response

    def create_order(self, account, **kwargs):
        request_name = "create_order(%s, %s)" % (account, kwargs)
        screen_no = ""
        account_no = account
        order_type = {
            "buy": 1,
            "sell": 2,
        }[kwargs["side"]]
        code = kwargs["instrument"]
        quantity = kwargs["units"]
        price = kwargs.get("price", 0)
        quote_type = {
            "limit": "00",
            "market": "03",
        }[kwargs["type"]]
        original_order_no = ""
        responses = self.OrderCall(
            request_name,
            screen_no,
            account_no,
            order_type,
            code,
            quantity,
            price,
            quote_type,
            original_order_no,
        )
        _msg = next(responses)
        _tr = next(responses)
        accept = next(responses)
        accept_data = dict(
            zip(accept.single_data.names, accept.single_data.values))
        result = {"orderOpened": {"id": accept_data["주문번호"]}}
        return result

    def close_order(self, account, oid, size, dataname):
        request_name = "close_order(%s, %s, %s, %s)" % (account, oid, size,
                                                        dataname)
        screen_no = ""
        account_no = account
        order_type = 3 if size >= 0 else 4
        code = dataname
        quantity = 0
        price = 0
        quote_type = ""
        original_order_no = oid
        responses = self.OrderCall(
            request_name,
            screen_no,
            account_no,
            order_type,
            code,
            quantity,
            price,
            quote_type,
            original_order_no,
        )
        _msg = next(responses)
        _tr = next(responses)
        _accept = next(responses)
        confirm = next(responses)
        confirm_data = dict(
            zip(confirm.single_data.names, confirm.single_data.values))
        result = {"orderOpened": {"id": confirm_data["주문번호"]}}
        return result

    def get_today_quotes_by_code(self, codes=None):
        if codes is None:
            codes = self._codes
        df = self.GetStockQuoteInfoAsDataFrame(codes)
        dt = pd.to_datetime(df["일자"].str.cat(df["체결시간"]),
                            format="%Y%m%d%H%M%S").dt.tz_localize(
                                self._krx_timezone)
        dt = dt.astype(np.int64) // 10**3
        df = pd.DataFrame({
            "dataname": df["종목코드"],
            "time": dt,
            "open": df["시가"].astype(float).abs(),
            "high": df["고가"].astype(float).abs(),
            "low": df["저가"].astype(float).abs(),
            "close": df["종가"].astype(float).abs(),
            "volume": df["거래량"].astype(float).abs(),
        })
        msgs = {
            tup.dataname: tup._asdict()
            for tup in df.itertuples(index=False)
        }
        return msgs
Esempio n. 28
0
# -*- coding: utf-8 -*-
from __future__ import division

import collections
from datetime import datetime
import os
import shutil

import numpy as np
import pandas as pd

from util import util
from util import performance

import exchange_calendars as tc
xnys = tc.get_calendar('XNYS')


def compute_stats(strategy_name, underlying, risk_capital, exclude=[], start_date=datetime(2004, 1, 1).date()):

  # start computing stats
  equity_curve = collections.OrderedDict()
  results_table = collections.OrderedDict()
  j = 0

  path = os.getcwd()
  strategy_path = path + '/results/' + strategy_name
  single_results_df = pd.read_csv(
      strategy_path + '/single_results.csv', parse_dates=['entry_date'])
  single_results = single_results_df.to_dict(orient='index')
Esempio n. 29
0
# -*- coding: utf-8 -*-
from __future__ import division
from datetime import timedelta, datetime
import pandas as pd
import exchange_calendars as tc
import pytz

from util import util

xnys = tc.get_calendar("XNYS")


def checkMinIV(combo, miniv):
    for position in combo.getPositions():
        if position is not None:
            iv = util.connector.select_iv(position.option.entry_date,
                                          position.option.underlying,
                                          position.option.expiration,
                                          position.option.type,
                                          position.option.strike)
            if (iv < miniv):
                print("IV below minimum IV")
                return False
    return True


def fly(strategy, underlying, risk_capital, quantity, entrydate, expiration):

    flying = True
    daily_pnls_dict = {}
    previouspnl = 0
Esempio n. 30
0
    def GetStockDataAsDataFrame(self,
                                code,
                                chart_type,
                                interval,
                                start_date=None,
                                end_date=None,
                                adjusted_price=False,
                                adjustement_only=False):
        """
        http://cybosplus.github.io/cpsysdib_rtf_1_/stockchart.htm
        """
        chart = self.CpSysDib.StockChart

        calendar = get_calendar('XKRX')
        tz = calendar.tz or pytz.timezone('Asia/Seoul')

        needs_time = chart_type in ['m', 'T']

        if len(code) == 6 and not code.startswith('A'):
            code = 'A' + code

        fids = [0]

        if needs_time:
            fids += [1]

        if not adjustement_only:
            fids += [2, 3, 4, 5, 8, 9]
        else:
            assert chart_type == 'D'
            adjusted_price = True

        if adjusted_price and chart_type == 'D':
            fids += [18, 19]

        num_fids = len(fids)
        sorted_fids = sorted(fids)
        field_indexes = [sorted_fids.index(i) for i in fids]

        maximum_value_count = 20000
        expected_count = math.floor(maximum_value_count / num_fids) - 1
        request_count = expected_count + 1

        date_format_arg = '%Y%m%d%H%M%S'
        date_format_input = '%Y%m%d'

        if start_date is None:
            start_date = calendar.previous_close(
                pd.Timestamp.now()).astimezone(tz).to_pydatetime()
        if isinstance(start_date, str):
            start_date_len = len(start_date)
            if start_date_len == 14:
                start_date = datetime.datetime.strptime(
                    start_date, date_format_arg)
            elif start_date_len == 8:
                start_date = datetime.datetime.strptime(
                    start_date, date_format_input)
            else:
                raise ValueError
        if not isinstance(start_date, datetime.datetime):
            raise ValueError

        start_date = start_date.astimezone(tz)

        internal_start_date = start_date

        if end_date is not None:
            if isinstance(end_date, str):
                end_date_len = len(end_date)
                if end_date_len == 14:
                    end_date = datetime.datetime.strptime(
                        end_date, date_format_arg)
                elif end_date_len == 8:
                    end_date = datetime.datetime.strptime(
                        end_date, date_format_input)
                else:
                    raise ValueError
            if not isinstance(end_date, datetime.datetime):
                raise ValueError

            end_date = end_date.astimezone(tz)

        should_stop = False
        dataframes = []

        while not should_stop:
            chart.SetInputValue(0, code)
            chart.SetInputValue(1, ord('2'))
            chart.SetInputValue(
                2, int(internal_start_date.strftime(date_format_input)))
            chart.SetInputValue(4, request_count)
            chart.SetInputValue(5, fids)
            chart.SetInputValue(6, ord(chart_type))
            chart.SetInputValue(7, int(interval))
            chart.SetInputValue(
                9,
                ord('1') if adjusted_price else ord('0'))  # 기본으로 수정주가 적용하지 않음

            self.logger.debug('Requesting from %s', internal_start_date)
            chart.RateLimitedBlockRequest()

            received_count = chart.GetHeaderValue(3)
            self.logger.debug('Requested %d, received %d records',
                              request_count, received_count)

            num_fields = chart.GetHeaderValue(1)
            assert num_fields == num_fids

            names = chart.GetHeaderValue(2)
            names = [names[i] for i in field_indexes]

            records = []
            for i in range(received_count):
                record = [chart.GetDataValue(j, i) for j in field_indexes]
                records.append(record)

            df = pd.DataFrame.from_records(records, columns=names)

            if df.shape[0] != 0:
                from_date = datetime.datetime.strptime(
                    df.iloc[0]['날짜'].astype(int).astype(str),
                    date_format_input)
                last_date = datetime.datetime.strptime(
                    df.iloc[-1]['날짜'].astype(int).astype(str),
                    date_format_input)

                from_date = from_date.astimezone(tz)
                last_date = last_date.astimezone(tz)

                self.logger.debug('Received data from %s to %s for code %s',
                                  from_date, last_date, code)

            should_stop = received_count < expected_count

            if not should_stop and end_date is not None:
                should_stop = last_date <= end_date

            if not should_stop:
                self.logger.debug('More data to request remains')
                internal_start_date = last_date
                nrows_before_truncate = df.shape[0]
                datetimes = pd.to_datetime(df['날짜'].astype(int).astype(str),
                                           format='%Y%m%d')
                datetimes = datetimes.dt.tz_localize(tz)
                condition = datetimes > last_date
                df = df.loc[condition]
                nrows_after_truncate = df.shape[0]
                self.logger.debug('Trailing rows truncated: %d',
                                  nrows_before_truncate - nrows_after_truncate)
            else:
                self.logger.debug('No more data to request')
                if end_date is not None:
                    nrows_before_truncate = df.shape[0]
                    dates = df['날짜'].astype(int).astype(str)
                    if needs_time:
                        times = df['시간'].astype(int).astype(str).str.ljust(
                            6, '0')
                        datetimes = dates.str.cat(times)
                        datetimes = pd.to_datetime(datetimes,
                                                   format='%Y%m%d%H%M%S')
                    else:
                        datetimes = dates
                        datetimes = pd.to_datetime(datetimes, format='%Y%m%d')
                    datetimes = datetimes.dt.tz_localize(tz)
                    condition = datetimes > end_date
                    df = df.loc[condition]
                    nrows_after_truncate = df.shape[0]
                    self.logger.debug(
                        'Trailing rows truncated: %d',
                        nrows_before_truncate - nrows_after_truncate)

            dataframes.append(df)

        df = pd.concat(dataframes)
        df.reset_index(drop=True, inplace=True)

        return df