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
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, )
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)]
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
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
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
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)
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
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)
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)
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()
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
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()
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
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
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
# 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
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 은 주어진 시간보다 큰 (같지 않은) 레코드만 가져오도록 구현되어있기 때문에 설정에 주의
def calendar() -> abc.Iterator[ExchangeCalendar]: yield get_calendar("XHKG")
def get_krx_timezone(): krx_calendar = get_calendar('XKRX') return krx_calendar.tz
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")
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
# -*- 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')
# -*- 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
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