Example #1
0
    def __execute_bulk(stmt: str, models: Any = None):
        LogUtils.debug('START')

        try:
            LogUtils.debug(stmt)
            conn = mysql.connector.connect(host=app_config.DB_HOST,
                                           user=app_config.DB_USER_NAME,
                                           passwd=app_config.DB_PASSWORD,
                                           database=app_config.DB_SCHEMA)
            cur = conn.cursor()
            if models:
                LogUtils.debug('{} records.'.format(len(models)))
                cur.executemany(stmt, models)
            else:
                cur.execute(stmt)
            conn.commit()
        except Exception as e:
            LogUtils.error(e)
        finally:
            if cur:
                cur.close()
            if conn:
                conn.close()

        LogUtils.debug('END')
Example #2
0
    def get_no_of_shares(self,
                         capital: float,
                         pct_risk_per_trade: float,
                         volume_limit: float,
                         price: Series,
                         slippage: int = None,
                         is_slip_up: bool = True) -> int:
        if not AppConsts.PRICE_COL_OPEN in price.index \
                or not AppConsts.CUSTOM_COL_ADV in price.index:
            return 0
        open_price: float = price.loc[AppConsts.PRICE_COL_OPEN]
        if slippage:
            if is_slip_up:
                open_price = NumberUtils.round(open_price + (
                    open_price * AppConsts.BASIS_POINT * slippage))
            else:
                open_price = NumberUtils.round(open_price - (
                    open_price * AppConsts.BASIS_POINT * slippage))

        no_of_shares: int = NumberUtils.to_floor(capital * pct_risk_per_trade /
                                                 100 / open_price)
        adv: float = price.loc[AppConsts.CUSTOM_COL_ADV] if price.loc[
            AppConsts.CUSTOM_COL_ADV] > 0 else price.loc[
                AppConsts.PRICE_COL_VOLUME]
        max_volume: float = NumberUtils.to_int(adv * volume_limit / 100)
        if no_of_shares > max_volume:
            LogUtils.warning(
                'Capping max_volume adv={0}, no_of_shares={1}, max_volume={2}'.
                format(adv, no_of_shares, max_volume))
            no_of_shares = max_volume
        return no_of_shares
Example #3
0
 def is_tmrw_valid(self) -> bool:
     tmrw = date.today() + timedelta(days=1)
     tmrw_as_string: str = DateUtils.to_string(tmrw)
     calendars: List[Calendar] = self.__api.get_calendar(
         tmrw_as_string, tmrw_as_string)
     LogUtils.debug(calendars)
     return (calendars[0].date.to_pydatetime().date() == tmrw)
Example #4
0
 def parse_as_list(file_path: str) -> List[List[str]]:
     try:
         LogUtils.debug('START')
         if StringUtils.isNullOrWhitespace(file_path):
             return []
         with open(file_path) as f:
             return list(csv.reader(f)) if f else []
     finally:
         LogUtils.debug('END')
Example #5
0
    def setUpClass(cls: type) -> None:

        # This runs once.
        if not TestInitObj.is_init:
            LogUtils.debug('START-Unit Tests')
            TestInitObj.init()

        # This runs before each test*.py
        LogUtils.debug('START-Unit Test for {0}'.format(cls.__name__))
Example #6
0
    def get(self, path: str) -> Any:
        if not path:
            return None
        req = requests.get(path)
        LogUtils.debug('status code = {0}, path = {1}'.format(
            req.status_code, path))

        if req.status_code != http.HTTPStatus.OK:
            return None
        return req.json()
Example #7
0
    def cancel_order(self, order_id: str) -> None:
        try:

            LogUtils.debug('Submit request = {0}'.format(order_id))

            self.__api.cancel_order(order_id)

        except Exception as ex:
            LogUtils.error(
                'Submit Error for {0}'.format(StringUtils.to_json(order)), ex)
            raise ex
Example #8
0
 def get_date(datestr: str, fmt: str) -> date:
     if not isinstance(datestr, str) \
             or not isinstance(fmt, str) \
             or StringUtils.isNullOrWhitespace(datestr) \
             or StringUtils.isNullOrWhitespace(fmt):
         return None
     try:
         return datetime.strptime(datestr, fmt)
     except Exception as ex:
         LogUtils.warning('Invalid date inputs={0} {1}'.format(
             datestr, fmt))
         return None
Example #9
0
    def get_html_table(self, url: str, element_id: str,
                       index_cols: List[str]) -> DataFrame:

        LogUtils.debug('Url={0}, Element Id={1}'.format(url, element_id))

        table: List[Any] = pd.read_html(url,
                                        attrs={'id': element_id},
                                        index_col=index_cols)
        df: DataFrame = table[0] if table else None

        LogUtils.debug('Df={0}'.format(df))

        return df
Example #10
0
 def import_from_csv_symbols(self) -> str:
     files: List[str] = FileUtils.get_files(AppConsts.STOCK_PRICE_FOLDER)
     for file in files:
         symbol: str = FileUtils.get_wo_ext(file)
         org_symbol: SM = self.__stock_service.get_symbol(
             symbol, AppConsts.INSTRUMENT_STOCK)
         if org_symbol:
             continue
         LogUtils.debug('Inserting {0}.'.format(symbol))
         BaseService._insert(
             SM(symbol=symbol,
                name='',
                status=AppConsts.SYMBOL_STATUS_INIT,
                instrument=AppConsts.INSTRUMENT_STOCK))
     return "1"
Example #11
0
 def __get_symbols__(self,
                     req: TradeSuggestionsRequest) -> List[SymbolMaster]:
     if not req or req.test_limit_symbol <= 0:
         raise BadRequestException()
     symbols: List[SymbolMaster] = self.__stock_service.get_symbols(
         AppConsts.INSTRUMENT_STOCK, [
             AppConsts.SYMBOL_STATUS_EXCLUDE_TRADE,
             AppConsts.SYMBOL_STATUS_ARCHIVED
         ])
     if not symbols:
         raise DbConnectionException()
     shuffle(symbols)
     symbols = symbols[:req.test_limit_symbol]
     LogUtils.debug('Symbol Count={0}'.format(len(symbols)))
     return symbols
Example #12
0
 def __get_prices__(self, symbols: List[SymbolMaster], date_from: date,
                    date_to: date) -> DataFrame:
     if not symbols:
         raise BadRequestException()
     price_items: List[Any] = []
     for symbol in symbols:
         temp: List[
             StockPriceDaily] = self.__stock_service.get_stock_prices_daily(
                 symbol.id, date_from, date_to)
         if temp:
             price_items.extend(temp)
     prices: DataFrame = self.__stock_service.get_price_dataframe(
         price_items)
     LogUtils.debug('Prices Init Shape={0}'.format(prices.shape))
     return prices
Example #13
0
def handle(ex: Exception) -> str:
    response: ResponseModel = ResponseModel()
    try:
        LogUtils.error('Handle error', ex)

        msg: str = str(ex)
        response.http_status_code = http.HTTPStatus.INTERNAL_SERVER_ERROR
        response.error_message = msg if msg else 'Error'
        if isinstance(ex, BadRequestException):
            response.http_status_code = http.HTTPStatus.BAD_REQUEST
        if isinstance(ex, NotFoundException):
            response.http_status_code = http.HTTPStatus.NOT_FOUND
        if isinstance(ex, DbConnectionException):
            response.http_status_code = http.HTTPStatus.INTERNAL_SERVER_ERROR
    except Exception as e:
        response.http_status_code = http.HTTPStatus.INTERNAL_SERVER_ERROR
        response.error_message = 'Error'
    return StringUtils.to_json(response)
Example #14
0
 def __get_symbols__(self, req: BackTestRunRequest) -> List[SymbolMaster]:
     if not req or req.test_limit_symbol <= 0:
         raise BadRequestException()
     symbols: List[SymbolMaster] = self.__stock_service.get_symbols(
         AppConsts.INSTRUMENT_STOCK,
         [AppConsts.SYMBOL_STATUS_EXCLUDE_TRADE])
     if not symbols:
         raise DbConnectionException()
     shuffle(symbols)
     symbols = symbols[:req.test_limit_symbol]
     if req.benchmark_etfs:
         for benchmark_etf in req.benchmark_etfs:
             etf_symbol: SymbolMaster = self.__stock_service.get_symbol(
                 benchmark_etf, AppConsts.INSTRUMENT_ETF)
             if not etf_symbol:
                 continue
             symbols.append(etf_symbol)
     LogUtils.debug('Symbol Count={0}'.format(len(symbols)))
     return symbols
Example #15
0
  def import_company_profiles(self) -> int:
    symbol_masters: List[SymbolMaster] = self.__stock_service.get_symbols(instrument='', exclude_status=[AppConsts.SYMBOL_STATUS_ARCHIVED])
    symbols: List[str] = [s.symbol for s in symbol_masters]
    company_profiles: List[Dict] = self.__iex_cloud_client.get_company_profiles(symbols)

    for profile in company_profiles:
      symbol: str = profile.get('symbol')
      name: str = profile.get('companyName')
      sector: str = profile.get('sector')
      industry: str = profile.get('industry')
      org: SymbolMaster = self.__stock_service.get_symbol(symbol, AppConsts.INSTRUMENT_STOCK)
      if not org:
        LogUtils.warning('{0} not found.'.format(symbol))
      else:
        LogUtils.debug('Updating {0}'.format(symbol))
        org.name = name if name != '' else org.name
        org.sector = sector if sector != '' else org.sector
        org.industry = industry if industry != '' else org.industry
        BaseService._update()
Example #16
0
 def delete_old_prices(self) -> int:
     error: Exception = None
     try:
         thirty_years_ago: datetime = datetime.now() - timedelta(days=365 *
                                                                 30)
         LogUtils.debug(
             'Deleting stock price older than {0}'.format(thirty_years_ago))
         db.session.query(SPD).filter(
             SPD.price_date <= thirty_years_ago).delete()
         db.session.commit()
     except Exception as ex:
         error = ex
     finally:
         self.__email_client.send_html(
             subject=AppConsts.EMAIL_SUBJECT_DELETE_PRICES,
             template_path=AppConsts.TEMPLATE_PATH_DELETE_PRICES,
             model={'errors': [error] if error else []})
         if error:
             LogUtils.error('Delete Price Error', error)
     return 1
Example #17
0
    def get_company_profiles(self, symbols: List[str]) -> List[Dict]:
        """
    https://iexcloud.io/docs/api/#company
    """
        ret: List[Dict] = []
        if not symbols:
            return ret

        for symbol in symbols:
            path: str = '/stock/{0}/company'.format(symbol)
            r = requests.get(self.__get_full_path__(path))
            if r.status_code != http.HTTPStatus.OK:
                LogUtils.debug('{0}={1}', symbol, r.status_code)
                continue
            profile: Dict = r.json()
            if not profile:
                LogUtils.debug('{0} empty', symbol)
            else:
                ret.append(profile)
        return ret
Example #18
0
 def import_from_csv_companynames(self) -> str:
     file: str = FileUtils.get_file(AppConsts.INCOME_STMT_FILE)
     records: List[Dict] = CsvUtils.parse_as_dict(file)
     curr_symbol: str = ''
     for record in records:
         symbol: str = record.get(AppConsts.INTRINIO_KEY_INC_STMT_TICKER)
         company_name: str = record.get(
             AppConsts.INTRINIO_KEY_INC_STMT_NAME)
         if symbol == curr_symbol or StringUtils.isNullOrWhitespace(
                 company_name):
             continue
         curr_symbol = symbol
         org_symbol: SM = self.__stock_service.get_symbol(
             curr_symbol, AppConsts.INSTRUMENT_STOCK)
         if not org_symbol:
             continue
         LogUtils.debug('Updating {0}.'.format(company_name))
         org_symbol.name = company_name
         BaseService._update()
     return "1"
Example #19
0
  def import_prices(self, limit: int = 100) -> int:
    results: Dict = {
        'stock_prices': [],
        'etf_prices': [],
        'missing_symbols': [],
        'errors': []
    }
    try:
      symbol_masters: List[SymbolMaster] = self.__stock_service.get_symbols(instrument='', exclude_status=[AppConsts.SYMBOL_STATUS_ARCHIVED])
      symbols: List[str] = [s.symbol for s in symbol_masters]
      price_set: BarSet = self.__alpaca_client.get_prices(symbols, limit)

      if not price_set:
        raise NotFoundException('BarSet', 'symbols', '')

      for symbol in symbol_masters:
        LogUtils.debug('start {0}'.format(symbol.symbol))

        prices: Bars = price_set[symbol.symbol]
        if not prices:
          LogUtils.warning('{0} price not found.'.format(symbol))
          results['missing_symbols'].append(symbol.symbol)
          continue

        for price in prices:
          price_date: datetime = price.t.to_pydatetime()
          data: tuple = (
              symbol.id,
              price_date,
              NumberUtils.to_float(price.o),
              NumberUtils.to_float(price.h),
              NumberUtils.to_float(price.l),
              NumberUtils.to_float(price.c),
              NumberUtils.to_int(price.v),
          )
          if symbol.instrument == AppConsts.INSTRUMENT_STOCK:
            org: StockPriceDaily = self.__stock_service.get_single_stock_price_daily(symbol.id, price_date)
            if not org:
              results['stock_prices'].append(data)
          else:
            org: EtfPriceDaily = self.__stock_service.get_single_etf_price_daily(symbol.id, price_date)
            if not org:
              results['etf_prices'].append(data)
      if results['stock_prices']:
        BaseService._insert_bulk(StockPriceDaily, results['stock_prices'])
      if results['etf_prices']:
        BaseService._insert_bulk(EtfPriceDaily, results['etf_prices'])
    except NotFoundException as ex:
      results['errors'].append(ex)
    except Exception as ex:
      results['errors'].append(ex)
    finally:
      self.__email_client.send_html(
          subject=AppConsts.EMAIL_SUBJECT_IMPORT_PRICES,
          template_path=AppConsts.TEMPLATE_PATH_IMPORT_PRICES,
          model=results)
      if results['errors']:
        for error in results['errors']:
          LogUtils.error('Import Price Error', error)
      return 1
Example #20
0
    def submit_order(
            self,
            symbol: str,
            qty: int,
            action: str,
            order_type: str = AppConsts.ORDER_TYPE_MARKET,
            time_in_force: str = AppConsts.TIME_IN_FORCE_DAY) -> Order:
        ret: Order = None
        try:

            ret = self.__api.submit_order(symbol=symbol,
                                          qty=qty,
                                          side=action,
                                          type=order_type,
                                          time_in_force=time_in_force)

            LogUtils.debug('Submit response = {0}'.format(ret))

        except Exception as ex:
            LogUtils.error('Submit Error for {0}'.format(symbol), ex)
            raise ex
        return ret
Example #21
0
    def __get_prices__(self, req: BackTestRunRequest,
                       symbols: List[SymbolMaster]) -> DataFrame:
        if not req or not symbols:
            raise BadRequestException()

        price_items: List[Any] = []
        for symbol in symbols:
            temp: List[Any] = []
            if symbol.instrument == AppConsts.INSTRUMENT_STOCK:
                temp = self.__stock_service.get_stock_prices_daily(
                    symbol.id, req.date_from_obj, req.date_to_obj)
            elif symbol.instrument == AppConsts.INSTRUMENT_ETF:
                temp = self.__stock_service.get_etf_prices_daily(
                    symbol.id, req.date_from_obj, req.date_to_obj)
            if temp:
                price_items.extend(temp)

        prices: DataFrame = self.__stock_service.get_price_dataframe(
            price_items)

        LogUtils.debug('Prices Init Shape={0}'.format(prices.shape))
        return prices
Example #22
0
  def send(self,
           subject: str,
           body: str,
           content_type: str,
           to: List[str] = app_config.DFLT_EMAIL_TO,
           sender: str = app_config.GMAIL_USERNAME,
           pw: str = app_config.GMAIL_PW,
           server: str = app_config.GMAIL_SERVER,
           port: int = app_config.GMAIL_PORT) -> None:
    try:
      msg: MIMEMultipart = MIMEMultipart()
      msg['From'] = sender
      msg['To'] = ','.join(to)
      msg['Subject'] = '{0}{1}'.format(app_config.EMAIL_SUBJECT_PREFIX, subject)
      msg.attach(MIMEText(body, content_type))

      LogUtils.debug(msg)

      with SMTP_SSL(server, port, context=ssl.create_default_context()) as server:
        server.ehlo()
        server.login(sender, pw)
        server.sendmail(sender, to, msg.as_string())
    except Exception as ex:
      LogUtils.error('EMAIL ERROR!', ex)
Example #23
0
    def get_all_suggestions(self) -> int:
        error: Exception = None
        try:

            accnt: Account = self.get_account()
            capital: float = NumberUtils.to_floor(
                NumberUtils.to_float(accnt._raw['buying_power']) /
                2)  # 2 to trade everyday

            # Double Bottoms
            req: TradeSuggestionsRequest = TradeSuggestionsRequest()
            req.is_job = True
            req.current_capital = capital
            req.pct_risk_per_trade = 2.5
            req.volume_limit = 0.01
            req.test_limit_symbol = 800
            req.adv_min = AppConsts.ADV_MIN_DFLT
            req.adpv_min = AppConsts.ADPV_MIN_DFLT
            req.strategy_type = AppConsts.STRATEGY_DOUBLE_BOTTOMS
            req.strategy_request = {}
            req.strategy_request['exponential_smoothing_alpha'] = 0.8
            req.strategy_request['exponential_smoothing_max_min_diff'] = 0.7
            req.strategy_request['double_bottoms_diff'] = 1
            LogUtils.debug(StringUtils.to_json(req))
            self.get_suggestions(req)

            # Double Tops
            req: TradeSuggestionsRequest = TradeSuggestionsRequest()
            req.is_job = True
            req.current_capital = capital
            req.pct_risk_per_trade = 2.5
            req.volume_limit = 0.01
            req.test_limit_symbol = 800
            req.adv_min = AppConsts.ADV_MIN_DFLT
            req.adpv_min = AppConsts.ADPV_MIN_DFLT
            req.strategy_type = AppConsts.STRATEGY_DOUBLE_TOPS
            req.strategy_request = {}
            req.strategy_request['exponential_smoothing_alpha'] = 0.8
            req.strategy_request['exponential_smoothing_max_min_diff'] = 0.7
            req.strategy_request['double_tops_diff'] = 1
            LogUtils.debug(StringUtils.to_json(req))
            self.get_suggestions(req)

        except Exception as ex:
            error = ex
        finally:
            self.__email_client.send_html(
                subject=AppConsts.EMAIL_SUBJECT_GET_SUGGESTIONS,
                template_path=AppConsts.TEMPLATE_PATH_GET_SUGGESTIONS,
                model={'errors': [error] if error else []})
            if error:
                LogUtils.error('Get Suggestions Error', error)
            return 1
Example #24
0
    def close_position(self, symbol: str) -> Order:
        ret: Order = None
        try:
            LogUtils.debug('Close Position for = {0}'.format(symbol))

            ret = self.__api.close_position(symbol)

            LogUtils.debug('Close Position response = {0}'.format(ret))

        except Exception as ex:
            LogUtils.error('Close Position Error for {0}'.format(symbol), ex)
            raise ex
        return ret
Example #25
0
import os
from flask import Flask
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from apis.back_test_api import back_test_api
from apis.import_api import import_api
from apis.stock_api import stock_api
from apis.trade_api import trade_api
from app_consts import AppConsts
from app_db import db
from app_utils.log_utils import LogUtils
from middlewares.error_handler import error_handler
from middlewares.request_handler import request_handler

LogUtils.debug('----- APP START -----')
flask_env: str = os.environ.get('FLASK_ENV', AppConsts.FLASK_ENV_STG)
LogUtils.debug('FLASK_ENV = {0}'.format(flask_env))

app: Flask = Flask(__name__, static_folder=AppConsts.STATIC_FOLDER)
app.config.from_object('config_prod.ConfigProd' if flask_env ==
                       AppConsts.FLASK_ENV_PROD else 'config_stg.ConfigStg')
app.register_blueprint(error_handler)
app.register_blueprint(request_handler)
app.register_blueprint(back_test_api, url_prefix=AppConsts.ROUTE_BACK_TEST)
app.register_blueprint(stock_api, url_prefix=AppConsts.ROUTE_STOCK)
app.register_blueprint(import_api, url_prefix=AppConsts.ROUTE_IMPORT)
app.register_blueprint(trade_api, url_prefix=AppConsts.ROUTE_TRADE)
CORS(app)

db.init_app(app)
Example #26
0
    def close_positions(self) -> int:
        errors: List[Exception] = []
        try:
            is_tmrw_valid: bool = self.__alpaca_client.is_tmrw_valid()
            if not is_tmrw_valid:
                LogUtils.warning('Tmrw is not a valid trade date')
                raise BadRequestException('Date',
                                          DateUtils.to_string(date.today()))

            req: GetTradeOrdersRequest = GetTradeOrdersRequest()
            req.exact_status = AppConsts.ORDER_STATUS_IN_POSITION
            orders: List[TradeOrderCustom] = self.get_trade_orders(req)

            if not orders:
                LogUtils.debug('No positions')

            for order in orders:
                try:
                    LogUtils.debug('Check position for = {0}'.format(
                        order.symbol_master.symbol))
                    spd: StockPriceDaily = self.__stock_service.get_last_single_stock_price_daily(
                        order.symbol_master.id)
                    if not spd:
                        LogUtils.warning('No Stock Price Found')
                        raise NotFoundException('SPD', 'symbol_id',
                                                order.symbol_master.id)

                    LogUtils.debug('Last close price = {0}'.format(
                        spd.close_price))
                    # is_exit: bool = (spd.close_price > order.trade_order.target_price
                    #                  or spd.close_price < order.trade_order.stop_loss)
                    if True:  # To-do: fix this to use strategy service. is_exit:

                        LogUtils.debug('Close position for = {0}'.format(
                            order.symbol_master.symbol))

                        resp: Order = self.__alpaca_client.submit_order(
                            symbol=order.symbol_master.symbol,
                            qty=order.trade_order.actual_qty,
                            action=AppConsts.ACTION_SELL
                            if order.trade_order.action == AppConsts.ACTION_BUY
                            else AppConsts.ACTION_BUY)
                        if resp:
                            org: TradeOrder = BaseService._get_by_id(
                                TradeOrder, order.trade_order.id)
                            if not org:
                                LogUtils.warning('Order not found.')
                                raise NotFoundException(
                                    'TradeOrder', 'id', order.trade_order.id)
                            org.exit_alpaca_id = resp.id
                            org.status = AppConsts.ORDER_STATUS_SUBMITTED_EXIT
                            org.modified = datetime.now()
                            BaseService._update()
                        else:
                            raise Exception('Close Position Error.')

                except Exception as ex:
                    LogUtils.error('Close Position Error', ex)
                    errors.append(ex)

        except Exception as ex:
            LogUtils.error('Close Position Error', ex)
            errors.append(ex)
        finally:
            self.__email_client.send_html(
                subject=AppConsts.EMAIL_SUBJECT_CLOSE_POSITIONS,
                template_path=AppConsts.TEMPLATE_PATH_CLOSE_POSITIONS,
                model={'errors': errors})
            return 1
Example #27
0
    def get_sample_prices_for_charts(self, req: CR) -> List[List[SPC]]:
        LogUtils.debug('START')
        ret: List[List[SPC]] = []
        if not req or not req.is_valid_model():
            raise BadRequestException()

        # region Init Symbols
        symbols: List[SymbolMaster] = self.get_symbols(
            AppConsts.INSTRUMENT_STOCK)
        if req.is_random_symbols:
            shuffle(symbols)
            symbols = symbols[:req.no_of_charts * 5]
        else:
            symbols = [s for s in symbols if s.symbol == req.symbol.upper()]
        if not symbols:
            return ret
        symbol_ids: List[int] = [symbol.id for symbol in symbols]
        # endregion

        LogUtils.debug('Symbols count={0}'.format(len(symbol_ids)))

        # region Init Prices
        prices = self.get_vw_symbol_spd_as_df(
            symbol_ids=symbol_ids,
            date_from=(req.date_from_obj -
                       timedelta(days=300)),  # for sma (will filter later)
            date_to=req.date_to_obj)
        # endregion

        LogUtils.debug('Prices Init Shape={0}'.format(prices.shape))

        prices[AppConsts.CUSTOM_COL_PV] = prices[
            AppConsts.PRICE_COL_CLOSE] * prices[AppConsts.PRICE_COL_VOLUME]
        prices[AppConsts.CUSTOM_COL_ADPV] = 0

        for symbol in symbols:
            symbol_prices: DataFrame = prices.loc[[symbol.id]]
            if symbol_prices.empty:
                continue

            # region ADPV (50)
            adpvs: Series = symbol_prices[AppConsts.CUSTOM_COL_PV].rolling(
                window=50).mean()
            prices[AppConsts.CUSTOM_COL_ADPV].update(adpvs)
            adpv: float = adpvs.tail(1)[0]
            if adpv < AppConsts.ADPV_MIN_DFLT:
                continue
            symbol_prices = prices.loc[[symbol.id]]
            # endregion

            LogUtils.debug('START-Symbol={0},ADPV={1}'.format(
                symbol.symbol, adpv))

            # SMA
            prices = self.__calc_service.append_sma(
                prices=prices,
                index=[symbol.id],
                sma_period=req.sma_period_1,
                sma_column_name=AppConsts.CUSTOM_COL_SMA_PERIOD_1)
            prices = self.__calc_service.append_sma(
                prices=prices,
                index=[symbol.id],
                sma_period=req.sma_period_2,
                sma_column_name=AppConsts.CUSTOM_COL_SMA_PERIOD_2)

            # Exponential Price Smoothing
            prices = self.__calc_service.append_exponential_smoothing_price(
                prices=prices,
                index=[symbol.id],
                alpha=req.exponential_smoothing_alpha)

            # Exponential Price Smoothing Max/Min
            prices = self.__calc_service.append_exponential_smoothing_max_min(
                prices=prices,
                index=[symbol.id],
                exponential_smoothing_max_min_diff=req.
                exponential_smoothing_max_min_diff)

            # Double Bottom
            prices = self.__calc_service.append_double_bottoms(
                prices=prices,
                index=[symbol.id],
                price_column=AppConsts.PRICE_COL_CLOSE,
                smooth_price_column=AppConsts.
                CUSTOM_COL_EXPONENTIAL_SMOOTHING_PRICE,
                max_column=AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MAX,
                min_column=AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MIN,
                double_bottoms_diff=5)

            # ABZ
            prices = self.__calc_service.append_abz(
                prices=prices,
                index=[symbol.id],
                abz_er_period=req.abz_er_period,
                abz_std_distance=req.abz_std_distance,
                abz_constant_k=req.abz_constant_k)

            symbol_prices = prices.loc[[symbol.id]]
            ret.append([
                SPC(i, row) for i, row in
                symbol_prices[symbol_prices.index.get_level_values(
                    AppConsts.PRICE_COL_DATE) >= req.date_from_obj].iterrows()
            ])
            if len(ret) == req.no_of_charts:
                return ret

        LogUtils.debug('END')
        return ret
Example #28
0
def handle_before_app_request() -> None:
  try:
    LogUtils.debug('START-Request={0}'.format(json.dumps(request.json, indent=2)))
  finally:
    pass
Example #29
0
def handle_after_app_request(resp: Any) -> Any:
  try:
    LogUtils.debug('END-Request')
  finally:
    pass
  return resp
Example #30
0
    def run(self, req: BackTestRunRequest) -> BackTestRunResponse:
        if not req or not req.is_valid_model():
            raise BadRequestException()
        response: BackTestRunResponse = BackTestRunResponse(req)

        # Init Symbols
        symbols: List[SymbolMaster] = self.__get_symbols__(req)

        # Init Prices
        prices: DataFrame = self.__get_prices__(req, symbols)

        # Do Base Preparation
        prices[AppConsts.CUSTOM_COL_PV] = prices[
            AppConsts.PRICE_COL_CLOSE] * prices[AppConsts.PRICE_COL_VOLUME]
        for s in symbols:
            prices = self.__calc_service.append_sma(
                prices=prices,
                index=[s.id],
                sma_period=AppConsts.ADV_PERIOD_DFLT,
                sma_column_name=AppConsts.CUSTOM_COL_ADV,
                target_column=AppConsts.PRICE_COL_VOLUME)
            prices = self.__calc_service.append_sma(
                prices=prices,
                index=[s.id],
                sma_period=AppConsts.ADPV_PERIOD_DFLT,
                sma_column_name=AppConsts.CUSTOM_COL_ADPV,
                target_column=AppConsts.CUSTOM_COL_PV)

        LogUtils.debug('Prices shape after base preparation={0}'.format(
            prices.shape))

        # region Init Service
        strategy_service: Any = self.__stock_service.get_strategy_service(
            req.strategy_type, req.strategy_request, symbols, prices)
        if not strategy_service or not strategy_service._is_valid_request():
            raise BadRequestException()
        strategy_service._do_preparations()

        LogUtils.debug('Prices shape after strategy preparation={0}'.format(
            prices.shape))
        # endregion

        LogUtils.debug(prices.info())

        # region Init Dates
        start_date: date = DateUtils.add_business_days(req.date_from_obj, -1)
        start_date = DateUtils.add_business_days(start_date, 1)
        start_date_str: str = DateUtils.to_string(start_date)
        end_date: date = DateUtils.add_business_days(req.date_to_obj, -1)
        dates: DataFrame = self.__stock_service.get_dates(
            prices, start_date, end_date)

        LogUtils.debug(
            'Dates actual_start={0}, actual_end={1}, shape={2}'.format(
                start_date, end_date, dates.shape))
        # endregion

        # region Loop Dates
        strategy_item: BackTestResultItem = next(
            b for b in response.back_test_result_items
            if b.target == req.strategy_type)
        strategy_item.capital[start_date_str] = req.start_capital
        strategy_item.capital_available[start_date_str] = req.start_capital
        portfolio: Dict = {}

        for i, date_row in dates.iterrows():
            current_date = date_row[AppConsts.PRICE_COL_DATE]
            current_date_str: str = DateUtils.to_string(current_date)
            next_date = date_row[AppConsts.CUSTOM_COL_NEXT_DATE]
            next_date_str = DateUtils.to_string(next_date)
            next_next_date = date_row[AppConsts.CUSTOM_COL_NEXT_NEXT_DATE]

            shuffle(symbols)
            for symbol in symbols:
                has_price: bool = (symbol.id, current_date) in prices.index
                if not has_price:
                    continue
                price: Series = prices.loc[symbol.id, current_date]

                if symbol.instrument == AppConsts.INSTRUMENT_ETF:

                    # region Benchmark
                    b_result_item: BackTestResultItem = next(
                        b for b in response.back_test_result_items
                        if b.target == symbol.symbol)
                    if not b_result_item:
                        continue

                    if not b_result_item.transactions:
                        no_of_shares: int = self.__stock_service.get_no_of_shares(
                            req.start_capital, req.pct_risk_per_trade,
                            req.volume_limit, price, req.slippage)
                        if no_of_shares == 0:
                            LogUtils.warning('0 shares for ETF={0}'.format(
                                symbol.symbol))
                            continue

                        b_transaction: Transaction = Transaction()
                        b_transaction.symbol_master = symbol
                        b_transaction.action = AppConsts.ACTION_BUY
                        b_transaction.start_date = current_date
                        b_transaction.start_price = price.loc[
                            AppConsts.PRICE_COL_OPEN]
                        b_transaction.no_of_shares = no_of_shares
                        b_result_item.transactions.append(b_transaction)
                        b_result_item.capital[
                            current_date_str] = req.start_capital
                    else:
                        b_transaction: Transaction = b_result_item.transactions[
                            0]
                        b_transaction.end_date = current_date
                        b_transaction.end_price = price.loc[
                            AppConsts.PRICE_COL_CLOSE]
                        b_transaction.set_readonly_props()
                        b_result_item.capital[
                            current_date_str] = self.__calc_benchmark_capital(
                                req, b_transaction.start_price,
                                b_transaction.end_price,
                                b_transaction.no_of_shares)
                    b_result_item.ttl_no_days += 1
                    # endregion

                else:

                    # region Strategy
                    strategy_service._do_calculations(symbol.id, current_date)
                    action: str = strategy_service._get_action()

                    is_in_position: bool = symbol.id in portfolio
                    if not is_in_position:

                        if len(portfolio
                               ) == req.portfolio_max:  # todo: prioritize?
                            continue
                        if current_date == end_date or next_date >= end_date:
                            continue
                        has_next_price: bool = (symbol.id,
                                                next_date) in prices.index
                        has_next_next_price: bool = (
                            symbol.id, next_next_date) in prices.index
                        if not has_next_price or not has_next_next_price:
                            continue
                        adv: float = price.loc[
                            AppConsts.CUSTOM_COL_ADV] if price.loc[
                                AppConsts.CUSTOM_COL_ADV] > 0 else price.loc[
                                    AppConsts.PRICE_COL_VOLUME]
                        if adv < req.adv_min:
                            continue
                        adpv: float = price.loc[
                            AppConsts.CUSTOM_COL_ADPV] if price.loc[
                                AppConsts.CUSTOM_COL_ADPV] > 0 else price.loc[
                                    AppConsts.CUSTOM_COL_PV]
                        if adpv < req.adpv_min:
                            continue

                        next_price: Series = prices.loc[symbol.id, next_date]

                        has_entry_conditions: bool = strategy_service._has_entry_conditions(
                            symbol.id, current_date)
                        if has_entry_conditions:

                            no_of_shares: int = self.__stock_service.get_no_of_shares(
                                strategy_item.
                                capital_available[current_date_str],
                                req.pct_risk_per_trade, req.volume_limit,
                                next_price, req.slippage,
                                action == AppConsts.ACTION_BUY)
                            if no_of_shares == 0:
                                continue

                            trans: Transaction = Transaction()
                            trans.symbol_master = symbol
                            trans.action = action
                            trans.start_date = next_date
                            trans.start_price = next_price.loc[
                                AppConsts.PRICE_COL_OPEN]
                            trans.no_of_shares = no_of_shares

                            trans_amount: float = NumberUtils.round(
                                trans.start_price * no_of_shares)
                            strategy_item.capital_available[
                                current_date_str] -= trans_amount

                            # Add to portfolio
                            portfolio[symbol.id] = trans

                    elif is_in_position:

                        has_exit_conditions: bool = strategy_service._has_exit_conditions(
                            symbol.id, current_date)
                        has_next_next_price: bool = (
                            symbol.id, next_next_date) in prices.index
                        if next_date == end_date or not has_next_next_price or has_exit_conditions:

                            next_price: Series = prices.loc[symbol.id,
                                                            next_date]
                            next_open_price: float = next_price.loc[
                                AppConsts.PRICE_COL_OPEN]
                            slippage_price: float = 0
                            if action == AppConsts.ACTION_BUY:
                                slippage_price: float = NumberUtils.round(
                                    next_open_price -
                                    (next_open_price * AppConsts.BASIS_POINT *
                                     req.slippage))
                            else:
                                slippage_price: float = NumberUtils.round(
                                    next_open_price +
                                    (next_open_price * AppConsts.BASIS_POINT *
                                     req.slippage))
                            trans: Transaction = portfolio.get(symbol.id)
                            trans.end_date = next_date
                            trans.end_price = slippage_price
                            trans.set_readonly_props()
                            strategy_item.transactions.append(trans)

                            if action == AppConsts.ACTION_BUY:
                                trans_amount = NumberUtils.round(
                                    trans.end_price * trans.no_of_shares)
                                strategy_item.capital_available[
                                    current_date_str] += trans_amount
                            else:
                                init_trans_amount = NumberUtils.round(
                                    trans.start_price * trans.no_of_shares)
                                strategy_item.capital_available[
                                    current_date_str] += init_trans_amount
                                strategy_item.capital_available[
                                    current_date_str] += trans.change_in_capital

                            # Remove from portfolio
                            portfolio.pop(symbol.id, None)

                    # endregion

            # capital = capital available + capital in portfolio
            capital: float = strategy_item.capital_available[current_date_str]
            for key, val in portfolio.items():
                price: Series = prices.loc[key, current_date]
                capital += price.loc[
                    AppConsts.PRICE_COL_CLOSE] * val.no_of_shares
            strategy_item.capital[current_date_str] = NumberUtils.round(
                capital)
            strategy_item.ttl_no_days += 1
            strategy_item.capital[next_date_str] = strategy_item.capital[
                current_date_str]
            strategy_item.capital_available[
                next_date_str] = strategy_item.capital_available[
                    current_date_str]

        # endregion

        for result_item in response.back_test_result_items:
            result_item.set_readonly_props()
        return response