def calculate_portfolio_for_selected(self): from portfolios.providers.data.django import DataProviderDjango from portfolios.providers.execution.django import ExecutionProviderDjango from portfolios.calculation import Unsatisfiable, calculate_portfolio, get_instruments data_provider = DataProviderDjango() execution_provider = ExecutionProviderDjango() idata = get_instruments(data_provider) try: weights, er, stdev = calculate_portfolio(self.selected_settings, data_provider, execution_provider, True, idata) portfolio = Portfolio.objects.create( setting=self.selected_settings, stdev=stdev, er=er, ) items = [ PortfolioItem(portfolio=portfolio, asset=Ticker.objects.get(id=tid), weight=weight, volatility=idata[0].loc[tid, tid]) for tid, weight in weights.iteritems() ] PortfolioItem.objects.bulk_create(items) except Unsatisfiable: # We detect when loading a goal in the allocation screen if there has been no portfolio created # and return a message to the user. It it perfectly reasonable for a goal to be created without a # portfolio. logger.exception( "No suitable portfolio could be found. Leaving empty.")
def test_calculate_portfolio(self): # TODO # constraints -> limit them -> maximum minimum value of 5%, maximum max value of 95% asset_class1 = AssetClassFactory.create(name='US_TOTAL_BOND_MARKET') asset_class2 = AssetClassFactory.create(name='HEDGE_FUNDS') fund0 = TickerFactory.create(symbol='IAGG', asset_class=asset_class1) fund0 = TickerFactory.create(symbol='GRFXX', asset_class=asset_class1) fund1 = TickerFactory.create(symbol='ITOT', asset_class=asset_class2) fund0 = TickerFactory.create(symbol='IPO') fund0 = TickerFactory.create(symbol='AGG', asset_class=asset_class1) fund6 = TickerFactory.create(symbol='rest') ps1 = PortfolioSetFactory \ .create(asset_classes=[asset_class1, asset_class2, fund6.asset_class]) feature = AssetFeatureValueFactory.create() feature.assets.add(fund6) settings = GoalSettingFactory.create() risk_metric = GoalMetricFactory.create( group=settings.metric_group, type=GoalMetric.METRIC_TYPE_RISK_SCORE) mix_metric = GoalMetricFactory.create( group=settings.metric_group, type=GoalMetric.METRIC_TYPE_PORTFOLIO_MIX, feature=feature, comparison=GoalMetric.METRIC_COMPARISON_MINIMUM, configured_val=0.5) goal = GoalFactory.create(selected_settings=settings, portfolio_set=ps1) # Create some instrument data for the two assets self.m_scale = MarkowitzScaleFactory.create() populate_prices(500, asof=mocked_now.date()) populate_cycle_obs(500, asof=mocked_now.date()) populate_cycle_prediction(asof=mocked_now.date()) data_provider = DataProviderDjango() execution_provider = ExecutionProviderDjango() idata = build_instruments(data_provider) result = calculate_portfolio(settings=settings, data_provider=data_provider, execution_provider=execution_provider, idata=idata) self.assertTrue(True)
def calculate_portfolio(self, request, pk=None, **kwargs): """ Called to calculate a portfolio object for a set of supplied settings. """ goal = self.get_object() check_state(Goal.State(goal.state), [Goal.State.ACTIVE, Goal.State.INACTIVE]) setting_str = request.query_params.get('setting', None) if not setting_str: raise ValidationError( "Query parameter 'setting' must be specified and a valid JSON string" ) try: setting = ujson.loads(setting_str) except ValueError: raise ValidationError( "Query parameter 'setting' must be a valid json string") # Create the settings object from the dict serializer = serializers.GoalSettingStatelessSerializer(data=setting) serializer.is_valid(raise_exception=True) settings = serializer.create_stateless(serializer.validated_data, goal) try: data = self.build_portfolio_data( calculate_portfolio( settings=settings, data_provider=DataProviderDjango(), execution_provider=ExecutionProviderDjango())) return Response(data) except Unsatisfiable as e: rdata = {'reason': "No portfolio could be found: {}".format(e)} if e.req_funds is not None: rdata['req_funds'] = e.req_funds return Response({'error': rdata}, status=status.HTTP_400_BAD_REQUEST)
def test_calculate_portfolio(self): goal1 = Fixture1.goal1() goal1.portfolio_set.asset_classes = [ AssetClass.objects.get(name="US_BONDS"), AssetClass.objects.get(name="AU_STOCKS"), AssetClass.objects.get(name="AU_STOCK_MUTUALS") ] goal1.selected_settings.metric_group.metrics = [GoalMetric.objects.create(group=Fixture1.metric_group1(), type=GoalMetric.METRIC_TYPE_RISK_SCORE, rebalance_type="1", configured_val=0.0, comparison=2, rebalance_thr=0.05) ] goal1.selected_settings.SYSTEM_CURRENCY = 'USD' goal1.cash_balance = 1000 idata = get_instruments(self._data_provider) portfolio, er, var = calculate_portfolio(settings=goal1.selected_settings, data_provider=self._data_provider, execution_provider=self._execution_provider, idata=idata) self.assertEqual(len(portfolio), 4)
def test_backtest(self): setup = TestSetup() # actually tickers are created here - we need to set proper asset class for each ticker self.create_goal() setup.create_goal(self.goal) setup.data_provider.initialize_tickers() setup.data_provider.move_date_forward() backtester = Backtester() print("backtesting " + str(setup.data_provider.get_current_date())) build_instruments(setup.data_provider) # optimization fails due to portfolios_stats = calculate_portfolios( setting=setup.goal.selected_settings, data_provider=setup.data_provider, execution_provider=setup.execution_provider) portfolio_stats = calculate_portfolio( settings=setup.goal.selected_settings, data_provider=setup.data_provider, execution_provider=setup.execution_provider) weights, instruments, reason = rebalance( idata=get_instruments(setup.data_provider), goal=setup.goal, data_provider=setup.data_provider, execution_provider=setup.execution_provider) new_positions = build_positions(setup.goal, weights, instruments) # create sell requests first mor, requests = create_request( setup.goal, new_positions, reason, execution_provider=setup.execution_provider, data_provider=setup.data_provider, allowed_side=-1) # process sells backtester.execute(mor) # now create buys - but only use cash to finance proceeds of buys mor, requests = create_request( setup.goal, new_positions, reason, execution_provider=setup.execution_provider, data_provider=setup.data_provider, allowed_side=1) backtester.execute(mor) transaction_cost = np.sum([abs(r.volume) for r in requests]) * 0.005 # So, the rebalance could not be in place if the excecution algo might not determine how much it will cost to rebalance. # this does not work - make it work with Django execution provider - use EtnaOrders #performance = backtester.calculate_performance(execution_provider=setup.execution_provider) self.assertTrue(True)
def create_settings(plan): """ Creates some settings that can be used to create a real retirement goal if desired. :param plan: The retirement plan to create settings for. :return: A GoalSetting object that has been saved in the database. The caller needs to destroy it if it is no longer required :raises Unsatisfiable if no suitable portfolio could be found for the plan. """ metric_group = GoalMetricGroup.objects.create(type=GoalMetricGroup.TYPE_CUSTOM) settings = GoalSetting.objects.create( target=0, completion=timezone.now().date(), hedge_fx=False, metric_group=metric_group, ) risk_metric = GoalMetric.objects.create(group=metric_group, type=GoalMetric.METRIC_TYPE_RISK_SCORE, comparison=GoalMetric.METRIC_COMPARISON_EXACTLY, rebalance_type=GoalMetric.REBALANCE_TYPE_ABSOLUTE, rebalance_thr=0.05, configured_val=plan.desired_risk) # Create a mock goal so we can call calculate_portfolio class MockGoal(object): portfolio_set = plan.client.advisor.default_portfolio_set id = 0 available_balance = 100000 current_balance = 100000 def __str__(self): return "Retiresmartz calculation Goal for plan: {}".format(plan) # Create a dummy settings object for the calculation. # We need to do this because we are using a fake goal, as we don't have a real goal yet. settings_data = GoalSettingSerializer(instance=settings).data calc_settings = GoalSettingStatelessSerializer.create_stateless(settings_data, MockGoal()) data_provider = DataProviderDjango() idata = get_instruments(data_provider) weights, er, stdev = calculate_portfolio( settings=calc_settings, idata=idata, data_provider=data_provider, execution_provider=ExecutionProviderDjango() ) portfolio = Portfolio.objects.create( setting=settings, stdev=stdev, er=er, ) items = [PortfolioItem(portfolio=portfolio, asset=Ticker.objects.get(id=tid), weight=weight, volatility=idata[0].loc[tid, tid]) for tid, weight in weights.items()] PortfolioItem.objects.bulk_create(items) return settings
''' # execute orders from yesterday backtester.place_order(settings=setup.goal, order=requests, data_provider=setup.data_provider, execution_provider=setup.execution_provider) ''' # calculate current portfolio stats portfolios_stats = calculate_portfolios( setting=setup.goal.active_settings, data_provider=setup.data_provider, execution_provider=setup.execution_provider) portfolio_stats = calculate_portfolio( settings=setup.goal.active_settings, data_provider=setup.data_provider, execution_provider=setup.execution_provider) # generate orders for tomorrow #try: requests = rebalance(idata=get_instruments(setup.data_provider), goal=setup.goal, data_provider=setup.data_provider, execution_provider=setup.execution_provider) #except: # print("reblance not succesful") # requests = [setup.execution_provider.create_empty_market_order()] backtester.execute_order(settings=setup.goal, order=requests, data_provider=setup.data_provider,
def create(self, validated_data): """ Override the default create because we need to generate a portfolio. :param validated_data: :return: The created Goal """ account = validated_data['account'] data_provider = DataProviderDjango() execution_provider = ExecutionProviderDjango() idata = get_instruments(data_provider) with transaction.atomic(): metric_group = GoalMetricGroup.objects.create( type=GoalMetricGroup.TYPE_CUSTOM) settings = GoalSetting.objects.create( target=validated_data['target'], completion=validated_data['completion'], hedge_fx=False, metric_group=metric_group, ) portfolio_provider_id = validated_data[ 'portfolio_provider'] if 'portfolio_provider' in validated_data else get_default_provider_id( ) portfolio_set_id = '' portfolio_providers = PortfolioProvider.objects.all() portfolio_provider = get_default_provider() for pp in portfolio_providers: if pp.id == portfolio_provider_id: portfolio_provider = pp if portfolio_provider.type == constants.PORTFOLIO_PROVIDER_TYPE_KRANE: portfolio_set_type = constants.PORTFOLIO_SET_TYPE_KRANE elif portfolio_provider.type == constants.PORTFOLIO_PROVIDER_TYPE_AON: portfolio_set_type = constants.PORTFOLIO_SET_TYPE_AON elif portfolio_provider.type == constants.PORTFOLIO_PROVIDER_TYPE_LEE: portfolio_set_type = constants.PORTFOLIO_SET_TYPE_LEE else: raise Exception('unhandled portfolio_provider_id') portfolio_sets = PortfolioSet.objects.all() portfolio_set = account.default_portfolio_set for ps in portfolio_sets: if ps.type == portfolio_set_type: portfolio_set = ps goal = Goal.objects.create( account=account, name=validated_data['name'], type=validated_data['type'], portfolio_set=portfolio_set, portfolio_provider=portfolio_provider, selected_settings=settings, ) # Based on the risk profile, and whether an ethical profile was specified on creation, set up Metrics. recommended_risk = recommend_risk(settings) GoalMetric.objects.create( group=metric_group, type=GoalMetric.METRIC_TYPE_RISK_SCORE, comparison=GoalMetric.METRIC_COMPARISON_EXACTLY, rebalance_type=GoalMetric.REBALANCE_TYPE_ABSOLUTE, rebalance_thr=0.05, configured_val=recommended_risk) if validated_data['ethical']: GoalMetric.objects.create( group=metric_group, type=GoalMetric.METRIC_TYPE_PORTFOLIO_MIX, feature=AssetFeatureValue.Standard.SRI_OTHER.get_object(), comparison=GoalMetric.METRIC_COMPARISON_EXACTLY, rebalance_type=GoalMetric.REBALANCE_TYPE_ABSOLUTE, rebalance_thr=0.05, configured_val=1 # Start with 100% ethical. ) # Make sure the risk score assigned is appropriate for the goal. try: validate_risk_score(settings) except CVE as verr: raise ValidationError(verr.message) # Add the initial deposit if specified. initial_dep = validated_data.pop('initial_deposit', None) if initial_dep is not None: Transaction.objects.create(reason=Transaction.REASON_DEPOSIT, to_goal=goal, amount=initial_dep) # Calculate the optimised portfolio try: weights, er, stdev = calculate_portfolio( settings, data_provider, execution_provider, True, idata) portfolio = Portfolio.objects.create( setting=settings, stdev=stdev, er=er, ) items = [ PortfolioItem(portfolio=portfolio, asset=Ticker.objects.get(id=tid), weight=weight, volatility=idata[0].loc[tid, tid]) for tid, weight in weights.iteritems() ] PortfolioItem.objects.bulk_create(items) except Unsatisfiable: # We detect when loading a goal in the allocation screen if there has been no portfolio created # and return a message to the user. It it perfectly reasonable for a goal to be created without a # portfolio. logger.exception( "No suitable portfolio could be found. Leaving empty.") return goal