def test_exit_action_bytradename(mocker):
    with MockCalc(mocker):

        start_date = date(2021, 12, 6)
        end_date = date(2021, 12, 10)

        # Define trade
        irswap1 = IRSwap(PayReceive.Receive,
                         '10y',
                         Currency.USD,
                         notional_amount=1e5,
                         name='swap1')
        irswap2 = IRSwap(PayReceive.Pay,
                         '5y',
                         Currency.USD,
                         notional_amount=1e5,
                         name='swap2')

        trig_req_add = PeriodicTriggerRequirements(start_date=start_date,
                                                   end_date=end_date,
                                                   frequency='1b')
        trig_req_exit = PeriodicTriggerRequirements(start_date=start_date,
                                                    end_date=end_date,
                                                    frequency='2b')
        actions_add = AddTradeAction([irswap1, irswap2])
        actions_exit = ExitTradeAction('swap1')

        triggers = [
            PeriodicTrigger(trig_req_add, actions_add),
            PeriodicTrigger(trig_req_exit, actions_exit)
        ]
        strategy = Strategy(None, triggers)

        # run backtest daily
        engine = GenericEngine()
        # backtest = engine.run_backtest(strategy, start=start_date, end=end_date, frequency='1b', show_progress=True)
        backtest = engine.run_backtest(strategy,
                                       states=[
                                           date(2021, 12, 6),
                                           date(2021, 12, 7),
                                           date(2021, 12, 8),
                                           date(2021, 12, 9),
                                           date(2021, 12, 10)
                                       ],
                                       end=end_date,
                                       show_progress=True)

        trade_ledger = backtest.trade_ledger().to_dict('index')

        assert trade_ledger['Action1_swap1_2021-12-06']['Open'] == date(
            2021, 12, 6)
        assert trade_ledger['Action1_swap1_2021-12-06']['Close'] == date(
            2021, 12, 6)
        assert trade_ledger['Action1_swap1_2021-12-07']['Open'] == date(
            2021, 12, 7)
        assert trade_ledger['Action1_swap1_2021-12-07']['Close'] == date(
            2021, 12, 8)
        assert trade_ledger['Action1_swap2_2021-12-06']['Status'] == 'open'
        assert trade_ledger['Action1_swap2_2021-12-07']['Status'] == 'open'
        assert trade_ledger['Action1_swap2_2021-12-10']['Status'] == 'open'
def test_eq_vol_engine_result(mocker):
    # 1. setup strategy

    start_date = dt.date(2019, 2, 18)
    end_date = dt.date(2019, 2, 20)

    option = EqOption('.STOXX50E', expirationDate='3m', strikePrice='ATM', optionType=OptionType.Call,
                      optionStyle=OptionStyle.European)

    action = EnterPositionQuantityScaledAction(priceables=option, trade_duration='1m')
    trigger = PeriodicTrigger(
        trigger_requirements=PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='1m'),
        actions=action)
    hedgetrigger = PeriodicTrigger(
        trigger_requirements=PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='B'),
        actions=HedgeAction(EqDelta, priceables=option, trade_duration='B'))
    strategy = Strategy(initial_portfolio=None, triggers=[trigger, hedgetrigger])

    # 2. setup mock api response

    mock_api_response(mocker, api_mock_data())

    # 3. when run backtest

    set_session()
    backtest_result = EquityVolEngine.run_backtest(strategy, start_date, end_date)

    # 4. assert response

    df = pd.DataFrame(api_mock_data().risks[FlowVolBacktestMeasure.PNL.value])
    df.date = pd.to_datetime(df.date)
    expected_pnl = df.set_index('date').value

    assert expected_pnl.equals(backtest_result.get_measure_series(FlowVolBacktestMeasure.PNL))
def test_engine_mapping_trade_quantity_nav(mocker):
    # 1. setup strategy

    start_date = dt.date(2019, 2, 18)
    end_date = dt.date(2019, 2, 20)

    option = EqOption('.STOXX50E', expirationDate='3m', strikePrice='ATM', optionType=OptionType.Call,
                      optionStyle=OptionStyle.European)

    action = EnterPositionQuantityScaledAction(priceables=option, trade_duration='1m', trade_quantity=12345,
                                               trade_quantity_type=BacktestTradingQuantityType.NAV)
    trigger = PeriodicTrigger(
        trigger_requirements=PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='1m'),
        actions=action)
    hedgetrigger = PeriodicTrigger(
        trigger_requirements=PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='B'),
        actions=HedgeAction(EqDelta, priceables=option, trade_duration='B'))
    strategy = Strategy(initial_portfolio=None, triggers=[trigger, hedgetrigger])

    # 2. setup mock api response

    mock_api_response(mocker, api_mock_data())

    # 3. when run backtest

    set_session()
    EquityVolEngine.run_backtest(strategy, start_date, end_date)

    # 4. assert API call

    backtest_parameter_args = {
        'trading_parameters': BacktestTradingParameters(
            quantity=12345,
            quantity_type=BacktestTradingQuantityType.NAV.value,
            trade_in_method=TradeInMethod.FixedRoll.value,
            roll_frequency='1m'),
        'underliers': [BacktestStrategyUnderlier(
            instrument=option,
            notional_percentage=100,
            hedge=BacktestStrategyUnderlierHedge(risk_details=DeltaHedgeParameters(frequency='Daily')),
            market_model='SFK')
        ],
        'index_initial_value': 12345,
        "measures": [FlowVolBacktestMeasure.ALL_MEASURES]
    }
    backtest_parameters = VolatilityFlowBacktestParameters.from_dict(backtest_parameter_args)

    backtest = Backtest(name="Flow Vol Backtest",
                        mq_symbol="Flow Vol Backtest",
                        parameters=backtest_parameters,
                        start_date=start_date,
                        end_date=end_date,
                        type='Volatility Flow',
                        asset_class=AssetClass.Equity,
                        currency=Currency.USD,
                        cost_netting=False)

    mocker.assert_called_with(backtest, None)
def test_hedge_action_risk_trigger(mocker):
    with MockCalc(mocker):
        start_date = date(2021, 12, 1)
        # end_date = date(2021, 12, 3)

        # Define trade
        call = FXOption(buy_sell='Buy',
                        option_type='Call',
                        pair='USDJPY',
                        strike_price='ATMF',
                        notional_amount=1e5,
                        expiration_date='2y',
                        name='2y_call')

        hedge_risk = FXDelta(aggregation_level='Type')
        fwd_hedge = FXForward(pair='USDJPY',
                              settlement_date='2y',
                              notional_amount=1e5,
                              name='2y_forward')

        trig_req = RiskTriggerRequirements(risk=hedge_risk,
                                           trigger_level=0,
                                           direction=TriggerDirection.ABOVE)
        action_hedge = HedgeAction(hedge_risk, fwd_hedge, '2b')

        triggers = StrategyRiskTrigger(trig_req, action_hedge)

        with PricingContext(pricing_date=start_date):
            fut = call.resolve(in_place=False)

        call = fut.result()

        strategy = Strategy(call, triggers)

        # run backtest daily
        engine = GenericEngine()
        # backtest = engine.run_backtest(strategy, start=start_date, end=end_date, frequency='1b', show_progress=True)
        backtest = engine.run_backtest(
            strategy,
            states=[date(2021, 12, 1),
                    date(2021, 12, 2),
                    date(2021, 12, 3)],
            show_progress=True)
        summary = backtest.result_summary
        assert len(summary) == 3
        assert round(summary[hedge_risk].sum()) == 0
        assert round(summary['Cumulative Cash'].sum()) == -7090
        assert Price in summary.columns
def test_mkt_trigger_data_sources(mocker):
    with MockCalc(mocker):
        s = pd.Series({
            date(2021, 10, 1): 0.984274,
            date(2021, 10, 4): 1.000706,
            date(2021, 10, 5): 1.044055,
            date(2021, 10, 6): 1.095361,
            date(2021, 10, 7): 1.129336,
            date(2021, 10, 8): 1.182954,
            date(2021, 10, 12): 1.200108,
            date(2021, 10, 13): 1.220607,
            date(2021, 10, 14): 1.172837,
            date(2021, 10, 15): 1.163660,
            date(2021, 10, 18): 1.061084,
            date(2021, 10, 19): 1.025012,
            date(2021, 10, 20): 1.018035,
            date(2021, 10, 21): 1.080751,
            date(2021, 10, 22): 1.069340,
            date(2021, 10, 25): 1.033413
        })

        action = AddTradeAction(
            IRSwaption(notional_currency='USD',
                       expiration_date='1y',
                       termination_date='1y'), 'expiration_date')
        data_source = GenericDataSource(s, MissingDataStrategy.fill_forward)
        mkt_trigger = MktTrigger(
            MktTriggerRequirements(data_source, 1.1, TriggerDirection.ABOVE),
            action)
        strategy = Strategy(None, mkt_trigger)

        engine = GenericEngine()

        # backtest = engine.run_backtest(strategy, start=date(2021, 10, 1), end=date(2021, 10, 25), frequency='1b',
        #                                show_progress=True)
        backtest = engine.run_backtest(strategy,
                                       states=s.index,
                                       show_progress=True)

        summary = backtest.result_summary
        ledger = backtest.trade_ledger()
        assert len(summary) == 12
        assert len(ledger) == 6
        assert round(summary[Price].sum()) == 25163614
        assert round(summary['Cumulative Cash'].sum()) == -2153015
def test_backtest_predefined_timezone_aware():
    tz = 'Europe/London'
    start_dt = '2021-01-01T08:00'
    end_dt = '2021-12-31T17:00'

    states = pd.bdate_range(start_dt, end_dt,
                            freq='1H', tz=tz).to_series().between_time(
                                '08:00', '17:00').index.tolist()
    trigger_dates = pd.bdate_range(
        start_dt, end_dt, freq='1H',
        tz=tz).to_series().at_time('17:00').index.tolist()
    data = np.random.randn(len(states))
    s_rt = pd.Series(index=states, data=data)
    s_eod = s_rt.at_time('17:00')
    s_eod.index = s_eod.index.date

    generic_bond_future = IRBondFuture(currency='EUR')
    add_trade_action = AddTradeAction(generic_bond_future)
    simple_date_trigger_requirement = DateTriggerRequirements(
        dates=trigger_dates)
    simple_date_trigger = DateTrigger(
        trigger_requirements=simple_date_trigger_requirement,
        actions=[add_trade_action])

    data_manager = DataManager()
    data_manager.add_data_source(pd.Series(index=states, data=data),
                                 DataFrequency.REAL_TIME, generic_bond_future,
                                 ValuationFixingType.PRICE)
    data_manager.add_data_source(s_eod, DataFrequency.DAILY,
                                 generic_bond_future,
                                 ValuationFixingType.PRICE)

    # instantiate a new strategy
    strategy = Strategy(None, triggers=simple_date_trigger)

    engine = PredefinedAssetEngine(data_mgr=data_manager,
                                   tz=timezone(tz),
                                   calendars='Weekend')
    backtest = engine.run_backtest(strategy=strategy,
                                   start=states[0],
                                   end=states[-1],
                                   states=states)
    assert len(backtest.trade_ledger()) == 364
def test_generic_engine_simple(mocker):
    with MockCalc(mocker):

        start_date = date(2021, 12, 1)
        # end_date = date(2021, 12, 3)

        # Define trade
        call = FXOption(buy_sell='Buy',
                        option_type='Call',
                        pair='USDJPY',
                        strike_price='ATMF',
                        notional_amount=1e5,
                        expiration_date='2y',
                        name='2y_call')

        # Periodic trigger: based on frequency
        freq = '1m'

        # trig_req = PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency=freq)
        trig_req = DateTriggerRequirements(dates=[start_date])
        actions = AddTradeAction(call, freq)

        # starting with empty portfolio (first arg to Strategy), apply actions on trig_req
        triggers = DateTrigger(trig_req, actions)

        strategy = Strategy(None, triggers)

        # run backtest daily
        engine = GenericEngine()
        # backtest = engine.run_backtest(strategy, start=start_date, end=end_date, frequency='1b', show_progress=True)
        backtest = engine.run_backtest(
            strategy,
            states=[date(2021, 12, 1),
                    date(2021, 12, 2),
                    date(2021, 12, 3)],
            show_progress=True)
        summary = backtest.result_summary
        assert len(summary) == 3
        assert round(summary[Price].sum()) == 2424
        assert round(summary['Cumulative Cash'].sum()) == 0
def test_backtest_predefined():
    # Test simple MOC order
    trigger = ExampleTestTrigger()
    strategy = Strategy(initial_portfolio=None, triggers=[trigger])
    start = dt.date(2021, 1, 4)
    mid = dt.date(2021, 1, 5)
    end = dt.date(2021, 1, 6)

    # these mocks are needed as the date functions need a GSSession
    gs_quant.backtests.predefined_asset_engine.is_business_day = mock.Mock(
        return_value=True)
    gs_quant.backtests.predefined_asset_engine.business_day_offset = mock.Mock(
        return_value=mid)

    data_mgr = DataManager()
    underlying = Security(ric='TestRic')
    close_prices = pd.Series(dtype=float)
    close_prices[start] = 1
    close_prices[mid] = 1.5
    close_prices[end] = 2
    data_mgr.add_data_source(close_prices, DataFrequency.DAILY, underlying,
                             ValuationFixingType.PRICE)

    engine = PredefinedAssetEngine(data_mgr=data_mgr)

    backtest = engine.run_backtest(strategy, start=start, end=end)
    perf = backtest.performance
    holdings = backtest.historical_holdings
    cash_asset = backtest.cash_asset

    # 100 on the initial date
    assert perf[start] == 100
    # 100 on the next day as we traded MOC
    assert perf[mid] == 100
    assert holdings[mid][cash_asset] == 100 - 1.5
    assert holdings[mid][underlying] == 1
    # 100.5 = 98.5 (cash) + 2 ( test asset)
    assert holdings[end][cash_asset] == 100 - 1.5
    assert holdings[end][underlying] == 1
    assert perf[end] == 100.5

    # Test TWAP orders with no ON positions
    twap_entry_mid = 16
    twap_exit_mid = 25

    twap_entry_end = 30
    twap_exit_end = 40

    data_twap = {
        dt.datetime.combine(mid, dt.time(10, 30)): twap_entry_mid,
        dt.datetime.combine(mid, dt.time(14, 30)): twap_exit_mid,
        dt.datetime.combine(end, dt.time(10, 30)): twap_entry_end,
        dt.datetime.combine(end, dt.time(14, 30)): twap_exit_end
    }

    data_mgr.add_data_source(pd.Series(data_twap), DataFrequency.REAL_TIME,
                             underlying, ValuationFixingType.PRICE)
    trigger = FuturesExample()
    strategy = Strategy(initial_portfolio=None, triggers=[trigger])
    engine = PredefinedAssetEngine(data_mgr=data_mgr,
                                   tz=timezone('Europe/London'))
    backtest = engine.run_backtest(strategy, start=start, end=end)
    perf = backtest.performance
    holdings = backtest.historical_holdings
    weights = backtest.historical_weights
    cash_asset = backtest.cash_asset

    # start: 100
    assert perf[start] == 100
    # mid: 100 + (twap_exit - twap_entry)
    assert perf[mid] == 100 - twap_entry_mid + twap_exit_mid
    assert holdings[mid][cash_asset] == perf[mid]
    assert underlying not in holdings[mid]
    assert weights[mid][cash_asset] == 1
    assert underlying not in weights[mid]
    # 100.5 = 98.5 (cash) + 2 ( test asset)
    assert perf[
        end] == 100 - twap_entry_mid + twap_exit_mid - twap_entry_end + twap_exit_end
    assert holdings[end][cash_asset] == perf[end]
    assert underlying not in holdings[end]
    assert weights[end][cash_asset] == 1
    assert underlying not in weights[end]
def test_supports_strategy():

    # 1. Valid strategy

    start_date = dt.date(2019, 2, 18)
    end_date = dt.date(2019, 2, 20)

    option = EqOption('.STOXX50E',
                      expirationDate='3m',
                      strikePrice='ATM',
                      optionType=OptionType.Call,
                      optionStyle=OptionStyle.European)

    action = AddTradesQuantityScaledAction(priceables=option,
                                           trade_duration='1m')
    trigger = PeriodicTrigger(trigger_requirements=PeriodicTriggerRequirements(
        start_date=start_date, end_date=end_date, frequency='1m'),
                              actions=action)
    hedge_trigger = PeriodicTrigger(
        trigger_requirements=PeriodicTriggerRequirements(start_date=start_date,
                                                         end_date=end_date,
                                                         frequency='B'),
        actions=HedgeAction(EqDelta, priceables=option, trade_duration='B'))
    strategy = Strategy(initial_portfolio=None,
                        triggers=[trigger, hedge_trigger])

    assert EquityVolEngine.supports_strategy(strategy)

    # 2. Invalid - no trade action

    trigger = PeriodicTrigger(trigger_requirements=PeriodicTriggerRequirements(
        start_date=start_date, end_date=end_date, frequency='1m'),
                              actions=None)
    strategy = Strategy(initial_portfolio=None, triggers=[trigger])
    assert not EquityVolEngine.supports_strategy(strategy)

    # 3. Invalid - no trade quantity

    action = AddTradesQuantityScaledAction(priceables=option,
                                           trade_duration='1m',
                                           trade_quantity=None)
    trigger = PeriodicTrigger(trigger_requirements=PeriodicTriggerRequirements(
        start_date=start_date, end_date=end_date, frequency='1m'),
                              actions=action)
    strategy = Strategy(initial_portfolio=None, triggers=[trigger])
    assert not EquityVolEngine.supports_strategy(strategy)

    # 4. Invalid - no trade quantity type

    action = AddTradesQuantityScaledAction(priceables=option,
                                           trade_duration='1m',
                                           trade_quantity_type=None)
    trigger = PeriodicTrigger(trigger_requirements=PeriodicTriggerRequirements(
        start_date=start_date, end_date=end_date, frequency='1m'),
                              actions=action)
    strategy = Strategy(initial_portfolio=None, triggers=[trigger])
    assert not EquityVolEngine.supports_strategy(strategy)

    # 5. Invalid - mismatch trade duration and trigger period

    action = AddTradesQuantityScaledAction(priceables=option,
                                           trade_duration='2m',
                                           trade_quantity_type=None)
    trigger = PeriodicTrigger(trigger_requirements=PeriodicTriggerRequirements(
        start_date=start_date, end_date=end_date, frequency='1m'),
                              actions=action)
    strategy = Strategy(initial_portfolio=None, triggers=[trigger])
    assert not EquityVolEngine.supports_strategy(strategy)

    # 6. Invalid - mismatch hedge trade duration and trigger period

    action = AddTradesQuantityScaledAction(priceables=option,
                                           trade_duration='1m',
                                           trade_quantity_type=None)
    trigger = PeriodicTrigger(trigger_requirements=PeriodicTriggerRequirements(
        start_date=start_date, end_date=end_date, frequency='1m'),
                              actions=action)
    hedge_trigger = PeriodicTrigger(
        trigger_requirements=PeriodicTriggerRequirements(start_date=start_date,
                                                         end_date=end_date,
                                                         frequency='B'),
        actions=HedgeAction(EqDelta, priceables=option, trade_duration='M'))
    strategy = Strategy(initial_portfolio=None,
                        triggers=[trigger, hedge_trigger])
    assert not EquityVolEngine.supports_strategy(strategy)

    # 6. Invalid - non-daily hedge trade

    action = AddTradesQuantityScaledAction(priceables=option,
                                           trade_duration='1m',
                                           trade_quantity_type=None)
    trigger = PeriodicTrigger(trigger_requirements=PeriodicTriggerRequirements(
        start_date=start_date, end_date=end_date, frequency='1m'),
                              actions=action)
    hedge_trigger = PeriodicTrigger(
        trigger_requirements=PeriodicTriggerRequirements(start_date=start_date,
                                                         end_date=end_date,
                                                         frequency='M'),
        actions=HedgeAction(EqDelta, priceables=option, trade_duration='M'))
    strategy = Strategy(initial_portfolio=None,
                        triggers=[trigger, hedge_trigger])
    assert not EquityVolEngine.supports_strategy(strategy)
def test_engine_mapping_with_signals(mocker):
    # 1. setup strategy

    start_date = dt.date(2019, 2, 18)
    end_date = dt.date(2019, 2, 27)

    option = EqOption('.STOXX50E',
                      expirationDate='3m',
                      strikePrice='ATM',
                      optionType=OptionType.Call,
                      optionStyle=OptionStyle.European)

    entry_action = EnterPositionQuantityScaledAction(
        priceables=option,
        trade_duration='1m',
        trade_quantity=12345,
        trade_quantity_type=BacktestTradingQuantityType.notional)

    entry_signal_series = pd.Series(data={dt.date(2019, 2, 19): 1})
    entry_dates = entry_signal_series[entry_signal_series > 0].keys()

    entry_trigger = AggregateTrigger(triggers=[
        DateTrigger(trigger_requirements=DateTriggerRequirements(
            dates=entry_dates),
                    actions=entry_action),
        PortfolioTrigger(trigger_requirements=PortfolioTriggerRequirements(
            'len', 0, TriggerDirection.EQUAL))
    ])

    exit_signal_series = pd.Series(data={dt.date(2019, 2, 20): 1})
    exit_dates = exit_signal_series[exit_signal_series > 0].keys()

    exit_trigger = AggregateTrigger(triggers=[
        DateTrigger(trigger_requirements=DateTriggerRequirements(
            dates=exit_dates),
                    actions=ExitPositionAction()),
        PortfolioTrigger(trigger_requirements=PortfolioTriggerRequirements(
            'len', 0, TriggerDirection.ABOVE))
    ])

    strategy = Strategy(initial_portfolio=None,
                        triggers=[entry_trigger, exit_trigger])

    # 2. setup mock api response

    mock_api_response(mocker, api_mock_data())

    # 3. when run backtest

    set_session()
    EquityVolEngine.run_backtest(strategy, start_date, end_date)

    # 4. assert API call

    backtest_parameter_args = {
        'trading_parameters':
        BacktestTradingParameters(
            quantity=12345,
            quantity_type=BacktestTradingQuantityType.notional.value,
            trade_in_method=TradeInMethod.FixedRoll.value,
            roll_frequency='1m',
            trade_in_signals=list(
                map(lambda x: BacktestSignalSeriesItem(x[0], int(x[1])),
                    zip(entry_signal_series.index,
                        entry_signal_series.values))),
            trade_out_signals=list(
                map(lambda x: BacktestSignalSeriesItem(x[0], int(x[1])),
                    zip(exit_signal_series.index,
                        exit_signal_series.values)))),
        'underliers': [
            BacktestStrategyUnderlier(instrument=option,
                                      notional_percentage=100,
                                      hedge=BacktestStrategyUnderlierHedge(),
                                      market_model='SFK',
                                      expiry_date_mode='otc')
        ],
        'index_initial_value':
        0.0,
        "measures": [FlowVolBacktestMeasure.ALL_MEASURES]
    }
    backtest_parameters = VolatilityFlowBacktestParameters.from_dict(
        backtest_parameter_args)

    backtest = Backtest(name="Flow Vol Backtest",
                        mq_symbol="Flow Vol Backtest",
                        parameters=backtest_parameters,
                        start_date=start_date,
                        end_date=end_date,
                        type='Volatility Flow',
                        asset_class=AssetClass.Equity,
                        currency=Currency.USD,
                        cost_netting=False)

    mocker.assert_called_with(backtest, None)