コード例 #1
0
ファイル: test_goal.py プロジェクト: dan0712/django-app
    def test_calculate_portfolio_complete(self):
        # tickers for testing portfolio calculations in goals endpoint
        # otherwise, No valid instruments found
        TickerFactory.create(symbol='IAGG', asset_class=self.bonds_asset_class)
        TickerFactory.create(symbol='ITOT', asset_class=self.stocks_asset_class)
        TickerFactory.create(symbol='IPO')
        fund = TickerFactory.create(symbol='rest')

        self.portfolio_set.asset_classes.add(fund.asset_class)

        # Set the markowitz bounds for today
        self.m_scale = MarkowitzScaleFactory.create()

        # populate the data needed for the optimisation
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())

        account = ClientAccountFactory.create(primary_owner=Fixture1.client1())
        # setup some inclusive goal settings
        goal_settings = GoalSettingFactory.create()
        # Create a risk score metric for the settings
        GoalMetricFactory.create(group=goal_settings.metric_group, type=GoalMetric.METRIC_TYPE_RISK_SCORE)
        goal = GoalFactory.create(account=account, selected_settings=goal_settings, portfolio_set=self.portfolio_set,
                                  active_settings=goal_settings)
        goal_settings.completion_date = timezone.now().date() - timedelta(days=365)
        serializer = GoalSettingSerializer(goal_settings)
        url = '/api/v1/goals/{}/calculate-all-portfolios?setting={}'.format(goal.id, json.dumps(serializer.data))
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

        self.client.force_authenticate(user=Fixture1.client1().user)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
コード例 #2
0
    def test_add_goal_complete(self):
        # tickers for testing portfolio calculations in goals endpoint
        # otherwise, No valid instruments found
        self.bonds_index = MarketIndexFactory.create()
        self.stocks_index = MarketIndexFactory.create()
        self.bonds_ticker = TickerFactory.create(
            asset_class=self.bonds_asset_class, benchmark=self.bonds_index)
        self.stocks_ticker = TickerFactory.create(
            asset_class=self.stocks_asset_class, benchmark=self.stocks_index)

        # Set the markowitz bounds for today
        self.m_scale = MarkowitzScaleFactory.create()

        # populate the data needed for the optimisation
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())

        self.portfolio_provider = PortfolioProvider.objects.create(
            name='Krane', type=PORTFOLIO_PROVIDER_TYPE_KRANE)

        url = '/api/v1/goals'
        self.client.force_authenticate(user=Fixture1.client1().user)
        account = ClientAccountFactory.create(primary_owner=Fixture1.client1())
        goal_settings = GoalSettingFactory.create()
        goal_metric = GoalMetricFactory.create(
            group=goal_settings.metric_group)
        ser = GoalCreateSerializer(
            data={
                'account': account.id,
                'name': 'Zero Goal Target',
                'type': GoalTypeFactory().id,
                'target': 500,
                'completion': timezone.now().date(),
                'initial_deposit': 0,
                'ethical': True,
                'portfolio_provider': self.portfolio_provider.id
            })

        self.assertEqual(ser.is_valid(),
                         True,
                         msg="Serializer has errors %s" % ser.errors)
        response = self.client.post(url, ser.data)

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['selected_settings']['target'], 500)
        # "OnTrack" is false because the 500 deposit is still pending
        self.assertEqual(response.data['on_track'], False)

        goal = Goal.objects.get(pk=response.data['id'])
        goal.cash_balance += 500
        goal.save()

        response = self.client.get('%s/%s' % (url, goal.id))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['selected_settings']['target'], 500)
        self.assertEqual(response.data['on_track'], True)
コード例 #3
0
    def test_create_goal(self):
        self.bonds_index = MarketIndexFactory.create()
        self.stocks_index = MarketIndexFactory.create()

        self.bonds_asset_class = AssetClassFactory.create(
            investment_type=InvestmentType.Standard.BONDS.get())
        self.stocks_asset_class = AssetClassFactory.create(
            investment_type=InvestmentType.Standard.STOCKS.get())
        # Add the asset classes to the portfolio set
        self.portfolio_set = PortfolioSetFactory.create()
        self.portfolio_set.asset_classes.add(self.bonds_asset_class,
                                             self.stocks_asset_class)
        self.bonds_ticker = TickerFactory.create(
            asset_class=self.bonds_asset_class, benchmark=self.bonds_index)
        self.stocks_ticker = TickerFactory.create(
            asset_class=self.stocks_asset_class, benchmark=self.stocks_index)

        self.portfolio_provider = PortfolioProvider.objects.create(
            name='Krane', type=PORTFOLIO_PROVIDER_TYPE_KRANE)

        # Set the markowitz bounds for today
        self.m_scale = MarkowitzScaleFactory.create()
        # populate the data needed for the optimisation
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())
        account = ClientAccountFactory.create(primary_owner=Fixture1.client1())
        # setup some inclusive goal settings
        goal_settings = GoalSettingFactory.create()
        goal_type = GoalTypeFactory.create()
        url = '/api/v1/goals'
        data = {
            'account': account.id,
            'name': 'Fancy new goal',
            'type': goal_type.id,
            'target': 15000.0,
            'completion': "2016-01-01",
            'initial_deposit': 5000,
            'ethical': True,
            'portfolio_provider': self.portfolio_provider.id
        }

        # unauthenticated 403
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

        # authenticated 200
        self.client.force_authenticate(account.primary_owner.user)
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertDictEqual(
            response.data['portfolio_provider'], {
                'id': self.portfolio_provider.id,
                'name': self.portfolio_provider.name,
                'type': PORTFOLIO_PROVIDER_TYPE_KRANE
            })
コード例 #4
0
    def test_calculate_portfolio(self):
        """
        expects the setting parameter to be a json dump
        of the goal settings to use for the portfolio calculation
        """
        # tickers for testing portfolio calculations in goals endpoint
        # otherwise, No valid instruments found

        TickerFactory.create(symbol='IAGG', asset_class=self.bonds_asset_class)
        TickerFactory.create(symbol='AGG', asset_class=self.bonds_asset_class)
        TickerFactory.create(symbol='ITOT',
                             asset_class=self.stocks_asset_class)
        TickerFactory.create(symbol='GRFXX',
                             asset_class=self.stocks_asset_class)
        TickerFactory.create(symbol='IPO')
        fund = TickerFactory.create(symbol='rest')

        self.portfolio_set.asset_classes.add(fund.asset_class)

        # Set the markowitz bounds for today
        self.m_scale = MarkowitzScaleFactory.create()

        # populate the data needed for the optimisation
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())

        account = ClientAccountFactory.create(primary_owner=Fixture1.client1())
        # setup some inclusive goal settings
        goal_settings = GoalSettingFactory.create()
        # Create a risk score metric for the settings
        GoalMetricFactory.create(group=goal_settings.metric_group,
                                 type=GoalMetric.METRIC_TYPE_RISK_SCORE)

        portfolio_provider = PortfolioProvider(
            type=constants.PORTFOLIO_PROVIDER_TYPE_KRANE)
        portfolio_provider.save()
        goal = GoalFactory.create(account=account,
                                  portfolio_provider=portfolio_provider,
                                  selected_settings=goal_settings,
                                  portfolio_set=self.portfolio_set)
        serializer = GoalSettingSerializer(goal_settings)
        url = '/api/v1/goals/{}/calculate-portfolio?setting={}'.format(
            goal.id, json.dumps(serializer.data))
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

        self.client.force_authenticate(user=Fixture1.client1().user)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
コード例 #5
0
    def test_calculate_portfolio_old(self):
        fund0 = TickerFactory.create(symbol='IAGG')
        fund1 = TickerFactory.create(symbol='ITOT')
        fund5 = TickerFactory.create(symbol='GRFXX')
        fund2 = TickerFactory.create(symbol='VEA')
        fund0 = TickerFactory.create(symbol='IPO')
        fund3 = TickerFactory.create(symbol='EEM')
        fund4 = TickerFactory.create(symbol='AGG')

        AssetFeatureValueFactory.create(
            assets=[fund1, fund2, fund3, fund4, fund5])
        ps1 = PortfolioSetFactory \
            .create(asset_classes=[fund1.asset_class, fund2.asset_class, fund3.asset_class, fund4.asset_class, fund5.asset_class])

        # Create a settings object with a metric for a feature with no instruments in the current portfolio set.
        feature = AssetFeatureValueFactory.create()
        settings = GoalSettingFactory.create()
        risk_metric = GoalMetricFactory.create(group=settings.metric_group)
        mix_metric = GoalMetricFactory.create(
            group=settings.metric_group,
            type=GoalMetric.METRIC_TYPE_PORTFOLIO_MIX,
            feature=feature,
            comparison=GoalMetric.METRIC_COMPARISON_MAXIMUM,
            configured_val=.3)
        goal = GoalFactory.create(selected_settings=settings,
                                  portfolio_set=ps1)

        # The below fund has the desired feature, but is not in the goal's portfolio set.

        feature.assets.add(fund1)

        # Create some instrument data for the two assets
        self.m_scale = MarkowitzScaleFactory.create()
        # populate the data needed for the prediction
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        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_old(settings=settings,
                                         data_provider=data_provider,
                                         execution_provider=execution_provider,
                                         idata=idata)
        self.assertTrue(True)
コード例 #6
0
    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)
コード例 #7
0
ファイル: test_goal.py プロジェクト: dan0712/django-app
    def test_add_goal_0_target(self):
        # tickers for testing portfolio calculations in goals endpoint
        # otherwise, No valid instruments found
        self.bonds_index = MarketIndexFactory.create()
        self.stocks_index = MarketIndexFactory.create()
        self.bonds_ticker = TickerFactory.create(asset_class=self.bonds_asset_class, benchmark=self.bonds_index)
        self.stocks_ticker = TickerFactory.create(asset_class=self.stocks_asset_class, benchmark=self.stocks_index)

        # Set the markowitz bounds for today
        self.m_scale = MarkowitzScaleFactory.create()

        # populate the data needed for the optimisation
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())

        url = '/api/v1/goals'
        self.client.force_authenticate(user=Fixture1.client1().user)
        account = ClientAccountFactory.create(primary_owner=Fixture1.client1())
        goal_settings = GoalSettingFactory.create()
        goal_metric = GoalMetricFactory.create(group=goal_settings.metric_group)
        ser = GoalCreateSerializer(data={
            'account': account.id,
            'name': 'Zero Goal Target',
            'type': GoalTypeFactory().id,
            'target': 0,
            'completion': timezone.now().date() + timedelta(days=7),
            'initial_deposit': 0,
            'ethical': True,
        })
        self.assertEqual(ser.is_valid(), True, msg="Serializer has errors %s"%ser.errors)
        response = self.client.post(url, ser.data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['selected_settings']['target'], 0)
        self.assertEqual(response.data['on_track'], True)
コード例 #8
0
    def test_calc_opt_inputs_no_assets_for_constraint(self):
        """
        Makes sure when we have no assets filling a constraint, we behave appropriately.
        """

        # This fund has a different feature to the one in the mix metric, but it is in the correct portfolio set.
        fund1 = TickerFactory.create()
        AssetFeatureValueFactory.create(assets=[fund1])
        ps1 = PortfolioSetFactory.create(asset_classes=[fund1.asset_class])

        # Create a settings object with a metric for a feature with no instruments in the current portfolio set.
        feature = AssetFeatureValueFactory.create()
        settings = GoalSettingFactory.create()
        risk_metric = GoalMetricFactory.create(group=settings.metric_group)
        mix_metric = GoalMetricFactory.create(
            group=settings.metric_group,
            type=GoalMetric.METRIC_TYPE_PORTFOLIO_MIX,
            feature=feature,
            comparison=GoalMetric.METRIC_COMPARISON_MAXIMUM,
            configured_val=.3)
        goal = GoalFactory.create(selected_settings=settings,
                                  portfolio_set=ps1)

        # The below fund has the desired feature, but is not in the goal's portfolio set.
        fund2 = TickerFactory.create()
        feature.assets.add(fund2)

        # Create some instrument data for the two assets
        self.m_scale = MarkowitzScaleFactory.create()
        # populate the data needed for the prediction
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        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()
        idata = build_instruments(data_provider)

        execution_provider = ExecutionProviderDjango()

        # Get the opt inputs, there should be no constraint for the max for the feature with no funds.
        result = calc_opt_inputs(settings=settings,
                                 idata=idata,
                                 data_provider=data_provider,
                                 execution_provider=execution_provider)
        xs, lam, constraints, settings_instruments, settings_symbol_ixs, lcovars = result
        self.assertEqual(len(constraints), 3)  # All positive, and sum to 1

        # Then create a fund in the portfolio I want. We should get a constraint for the maximum for the feature.
        fund3 = TickerFactory.create(asset_class=fund1.asset_class)
        feature.assets.add(fund3)
        delete_data()
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())
        idata = build_instruments(data_provider)
        result = calc_opt_inputs(settings=settings,
                                 idata=idata,
                                 data_provider=data_provider,
                                 execution_provider=execution_provider)
        xs, lam, constraints, settings_instruments, settings_symbol_ixs, lcovars = result
        self.assertEqual(len(constraints),
                         4)  # All positive, sum to 1, and the max constraint
コード例 #9
0
    def test_retirement_plan_calculate(self):
        plan = RetirementPlanFactory.create(income=100000,
                                            desired_income=81000,
                                            btc=4000,
                                            retirement_home_price=250000,
                                            paid_days=1,
                                            retirement_age=67,
                                            selected_life_expectancy=85)

        # some tickers for portfolio
        bonds_asset_class = AssetClassFactory.create(
            name='US_TOTAL_BOND_MARKET')
        stocks_asset_class = AssetClassFactory.create(name='HEDGE_FUNDS')

        TickerFactory.create(symbol='IAGG', asset_class=bonds_asset_class)
        TickerFactory.create(symbol='ITOT', asset_class=stocks_asset_class)
        TickerFactory.create(symbol='IPO')
        fund = TickerFactory.create(symbol='rest')

        # Add the asset classes to the advisor's default portfolio set
        plan.client.advisor.default_portfolio_set.asset_classes.add(
            bonds_asset_class, stocks_asset_class, fund.asset_class)

        # Set the markowitz bounds for today
        self.m_scale = MarkowitzScaleFactory.create()

        # populate the data needed for the optimisation
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())
        populate_inflation(asof=mocked_now.date())

        self.assertIsNone(plan.goal_setting)
        old_settings = GoalSetting.objects.all().count()
        old_mgroups = GoalMetricGroup.objects.all().count()
        old_metrics = GoalMetric.objects.all().count()
        url = '/api/v1/clients/{}/retirement-plans/{}/calculate'.format(
            plan.client.id, plan.id)
        self.client.force_authenticate(user=plan.client.user)

        # First try and calculate without a client date of birth. Make sure we get the correct 400
        old_dob = plan.client.date_of_birth
        plan.client.date_of_birth = None
        plan.client.save()
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        # Now set the date of birth
        plan.client.date_of_birth = old_dob
        plan.client.save()

        # We should be ready to calculate properly
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue('portfolio' in response.data)
        self.assertTrue('projection' in response.data)
        self.assertEqual(len(response.data['projection']), 50)
        # Make sure the goal_setting is now populated.
        plan.refresh_from_db()
        self.assertIsNotNone(plan.goal_setting.portfolio)
        self.assertEqual(old_settings + 1, GoalSetting.objects.all().count())
        self.assertEqual(old_mgroups + 1,
                         GoalMetricGroup.objects.all().count())
        self.assertEqual(old_metrics + 1, GoalMetric.objects.all().count())
        old_id = plan.goal_setting.id

        # Recalculate and make sure the number of settings, metric groups and metrics in the system is the same
        # Also make sure the setting object is different
        response = self.client.get(url)
        plan.refresh_from_db()
        self.assertEqual(old_settings + 1, GoalSetting.objects.all().count())
        self.assertEqual(old_mgroups + 1,
                         GoalMetricGroup.objects.all().count())
        self.assertEqual(old_metrics + 1, GoalMetric.objects.all().count())
        self.assertNotEqual(old_id, plan.goal_setting.id)
コード例 #10
0
ファイル: test_rebalance.py プロジェクト: dan0712/django-app
 def setup_performance_history(self):
     populate_prices(400, asof=mocked_now)
     populate_cycle_obs(400, asof=mocked_now)
     populate_cycle_prediction(asof=mocked_now)
コード例 #11
0
    def test_transactions_check(self):
        """
        Create an Goal with RecurringTransaction
        Execute transactions_check and make sure Transactions are
        created for the Recurring Transactions.
        """
        # Bring an invite key, get logged in as a new user
        invite = EmailInviteFactory.create(status=EmailInvite.STATUS_SENT)

        url = reverse('api:v1:client-user-register')
        data = {
            'first_name': invite.first_name,
            'last_name': invite.last_name,
            'invite_key': invite.invite_key,
            'password': '******',
            'question_one': 'what is the first answer?',
            'question_one_answer': 'answer one',
            'question_two': 'what is the second answer?',
            'question_two_answer': 'answer two',
        }

        # Accept an invitation and create a user
        response = self.client.post(url, data)
        lookup_invite = EmailInvite.objects.get(pk=invite.pk)
        invite_detail_url = reverse('api:v1:invite-detail',
                                    kwargs={'invite_key': invite.invite_key})

        self.assertEqual(EmailInvite.STATUS_ACCEPTED, lookup_invite.status)

        # New user must be logged in too
        self.assertIn('sessionid', response.cookies)

        # GET: /api/v1/invites/:key
        # If a session is logged in, return 200 with data
        response = self.client.get(invite_detail_url)
        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK,
            msg='/api/v1/invites/:key should be valid during invitation')
        self.assertEqual(
            response.data['invite_key'],
            invite.invite_key,
            msg='/api/v1/invites/:key should have invitation data')
        self.assertEqual(
            response.data['status'],
            EmailInvite.STATUS_ACCEPTED,
            msg='/api/v1/invites/:key should have invitation status ACCEPTED')
        self.assertEqual(
            'onboarding_data' in response.data,
            True,
            msg='/api/v1/invites/:key should show onboarding_data to user')
        self.assertEqual(response.data['risk_profile_group'],
                         self.risk_group.id)

        # Make sure the user now has access to the risk-profile-groups endpoint
        rpg_url = reverse('api:v1:settings-risk-profile-groups-list')
        response = self.client.get(rpg_url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data[0]['id'], self.risk_group.id)

        # PUT: /api/v1/invites/:key
        # Submit with onboarding_data
        onboarding = {'onboarding_data': {'foo': 'bar'}}
        response = self.client.put(invite_detail_url, data=onboarding)

        lookup_invite = EmailInvite.objects.get(pk=invite.pk)
        self.assertEqual(response.status_code,
                         status.HTTP_200_OK,
                         msg='Onboarding must accept json objects')
        self.assertEqual(response.data['status'],
                         EmailInvite.STATUS_ACCEPTED,
                         msg='invitation status ACCEPTED')
        self.assertEqual(lookup_invite.onboarding_data['foo'],
                         'bar',
                         msg='should save onboarding_file')

        # PUT: /api/v1/invites/:key
        # Tax transcript upload and parsing
        with open(os.path.join(settings.BASE_DIR, 'pdf_parsers', 'samples',
                               'sample_2006.pdf'),
                  mode="rb") as tax_transcript:
            data = {'tax_transcript': tax_transcript}
            response = self.client.put(invite_detail_url,
                                       data,
                                       format='multipart')
        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK,
            msg='Updating onboarding with tax_transcript PDF returns OK')
        self.assertNotEqual(
            response.data['tax_transcript_data'],
            None,
            msg='tax_transcript_data is in the response and not None')
        self.assertEqual(response.data['tax_transcript_data'],
                         self.expected_tax_transcript_data,
                         msg='Parsed tax_transcript_data matches expected')

        with open(os.path.join(settings.BASE_DIR, 'pdf_parsers', 'samples',
                               'ssa-7005-sm-si_wanda_worker_young.pdf'),
                  mode="rb") as ss_statement:
            data = {'social_security_statement': ss_statement}
            response = self.client.put(invite_detail_url,
                                       data,
                                       format='multipart')
        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK,
            msg='Updating onboarding with tax_transcript PDF returns OK')
        self.assertNotEqual(
            response.data['social_security_statement'],
            None,
            msg='social_security_statement_data is in the response and not None'
        )
        self.assertEqual(
            response.data['social_security_statement_data'],
            self.expected_social_security_statement_data,
            msg='Parsed social_security_statement_data matches expected')

        with open(os.path.join(settings.BASE_DIR, 'pdf_parsers', 'samples',
                               'ssa-7005-sm-si_wanda_worker_young.pdf'),
                  mode="rb") as ss_statement:
            data = {'partner_social_security_statement': ss_statement}
            response = self.client.put(invite_detail_url,
                                       data,
                                       format='multipart')
        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK,
            msg='Updating onboarding with tax_transcript PDF returns OK')
        self.assertNotEqual(
            response.data['partner_social_security_statement'],
            None,
            msg=
            'partner_social_security_statement_data is in the response and not None'
        )
        self.assertEqual(
            response.data['partner_social_security_statement_data'],
            self.expected_social_security_statement_data,
            msg='Parsed partner_social_security_statement_data matches expected'
        )

        self.assertEqual(
            response._headers['content-type'],
            ('Content-Type', 'application/json'),
            msg='Response content type is application/json after upload')

        lookup_invite = EmailInvite.objects.get(pk=invite.pk)
        self.assertEqual(response.data['status'],
                         EmailInvite.STATUS_ACCEPTED,
                         msg='invitation status ACCEPTED')

        # re-upload tax transcript
        with open(os.path.join(settings.BASE_DIR, 'pdf_parsers', 'samples',
                               'sample_2006.pdf'),
                  mode="rb") as tax_transcript:
            data = {'tax_transcript': tax_transcript}
            response = self.client.put(invite_detail_url,
                                       data,
                                       format='multipart')
        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK,
            msg='Updating onboarding with tax_transcript PDF returns OK')
        self.assertNotEqual(
            response.data['tax_transcript_data'],
            None,
            msg='tax_transcript_data is in the response and not None')
        self.assertEqual(response.data['tax_transcript_data'],
                         self.expected_tax_transcript_data,
                         msg='Parsed tax_transcript_data matches expected')

        # test stripe photo_verification upload goes through ok
        with open(os.path.join(settings.BASE_DIR, 'tests', 'success.png'),
                  mode="rb") as photo_verification:
            data = {'photo_verification': photo_verification}
            response = self.client.put(invite_detail_url,
                                       data,
                                       format='multipart')
        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK,
            msg='Updating onboarding with photo verification returns OK')
        self.assertNotEqual(
            response.data['photo_verification'],
            None,
            msg='photo_verification is in the response and not None')

        # create client and make sure tax_transcript data is carried over properly
        url = reverse('api:v1:client-list')
        address = {
            "address": "123 My Street\nSome City",
            "post_code": "112233",
            "region": {
                "name": "New South Wales",
                "country": "AU",
                "code": "NSW",
            }
        }
        regional_data = {
            'ssn': '555-55-5555',
        }
        data = {
            "advisor_agreement": True,
            "betasmartz_agreement": True,
            "date_of_birth": date(2016, 9, 21),
            "employment_status": EMPLOYMENT_STATUS_EMMPLOYED,
            "gender": GENDER_MALE,
            "income": 1234,
            "politically_exposed": True,
            "phone_num": "+1-234-234-2342",
            "residential_address": address,
            "regional_data": json.dumps(regional_data)
        }
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        regional_data_load = json.loads(response.data['regional_data'])
        self.assertNotEqual(regional_data_load['tax_transcript'], None)
        self.assertEqual(
            regional_data_load['tax_transcript_data']['filing_status'],
            self.expected_tax_transcript_data['filing_status'],
            msg='Parsed tax_transcript_data filing_status parsed successfully')

        lookup_invite = EmailInvite.objects.get(pk=invite.pk)

        # create goal
        self.bonds_index = MarketIndexFactory.create()
        self.stocks_index = MarketIndexFactory.create()

        self.bonds_asset_class = AssetClassFactory.create(
            investment_type=InvestmentType.Standard.BONDS.get())
        self.stocks_asset_class = AssetClassFactory.create(
            investment_type=InvestmentType.Standard.STOCKS.get())
        # Add the asset classes to the portfolio set
        self.portfolio_set = PortfolioSetFactory.create()
        self.portfolio_set.asset_classes.add(self.bonds_asset_class,
                                             self.stocks_asset_class)
        self.bonds_ticker = TickerFactory.create(
            asset_class=self.bonds_asset_class, benchmark=self.bonds_index)
        self.stocks_ticker = TickerFactory.create(
            asset_class=self.stocks_asset_class, benchmark=self.stocks_index)

        # Set the markowitz bounds for today
        self.m_scale = MarkowitzScaleFactory.create()
        # populate the data needed for the optimisation
        # We need at least 500 days as the cycles go up to 70 days and we need at least 7 cycles.
        populate_prices(500, asof=mocked_now.date())
        populate_cycle_obs(500, asof=mocked_now.date())
        populate_cycle_prediction(asof=mocked_now.date())
        account = ClientAccount.objects.get(
            primary_owner=lookup_invite.user.client)
        # setup some inclusive goal settings
        goal_settings = GoalSettingFactory.create()
        goal = GoalFactory.create(selected_settings=goal_settings)

        # plaid user with access_token needed for deposits
        plaid_user = PlaidUserFactory.create(user=account.primary_owner.user)
        resp = create_public_token()
        plaid_user.access_token = resp['access_token']
        plaid_user.save()
        accounts = get_accounts(plaid_user.user)

        tx1 = self.rt = RecurringTransactionFactory.create(
            setting=goal_settings,
            begin_date=timezone.now().date(),
            amount=10,
            growth=0.0005,
            schedule='RRULE:FREQ=DAILY;INTERVAL=1',
            account_id=accounts[0]['account_id'],
            enabled=True,
        )
        lookup_transactions = Transaction.objects.all()
        self.assertEqual(len(lookup_transactions), 0)
        tx1.setting.goal.approve_selected()

        execute_recurring_transactions_today()

        lookup_transactions = Transaction.objects.all()
        self.assertEqual(len(lookup_transactions), 1)

        trans = Transaction.objects.first()
        self.assertEqual(trans.to_goal.state, Goal.State.ACTIVE.value)
        self.assertEqual(trans.status, Transaction.STATUS_PENDING)