├── Utilities ├── __init__.py ├── calculate.py ├── Analysis.py ├── utilities.py ├── admin_write_util.py └── admin_util.py ├── back_test ├── __init__.py ├── model │ ├── __init__.py │ ├── abstract_account.py │ ├── abstract_base_product_set.py │ ├── abstract_base_product.py │ ├── base_future.py │ └── base_instrument.py ├── tests │ ├── __init__.py │ ├── base_product_test.py │ ├── base_instrument_test.py │ ├── test.py │ ├── base_option_set_test.py │ ├── test_baseAccount.py │ ├── constant_test.py │ ├── option_account_test.py │ └── sample_strategy.py ├── deprecated │ └── __init__.py └── tmp_test.py ├── data_access ├── __init__.py ├── db_utilities.py ├── craw_data_hist1.py ├── metrics_moving_average.py ├── craw_data_check.py ├── crew_data_stocks_run.py ├── craw_data_hist2.py ├── craw_data_contractsinfo.py ├── metrics_calculate_run.py ├── export_data.py ├── spider_api_czce.py └── metrics_optiondata_goldencopy.py ├── PricingLibrary ├── __init__.py ├── tests │ ├── __init__.py │ ├── delta_calculate_example.py │ ├── binomial_example.py │ ├── quantlib_american_options.py │ └── test_binomialTree.py ├── OptionPricingUtil.py ├── AbstractOptionPricingEngine.py ├── Evaluation.py ├── svimodel.py ├── Util.py ├── Options.py ├── BlackFormular.py ├── BinomialModel.py ├── OptionMetrics.py └── BlackCalculator.py ├── regular_reports ├── __init__.py ├── cu_daily.py ├── commodity_option_pricing.py ├── otc_index_option_histvol.py ├── otc_index_future_basis.py └── otc_index_option_histvolts.py ├── OptionStrategyLib ├── __init__.py ├── Indexing │ ├── __init__.py │ └── IntradayImpVol.py ├── Optimization │ ├── __init__.py │ └── svi_neldermead.py ├── OptionMetrics │ └── __init__.py ├── OptionReplication │ ├── __init__.py │ ├── .DS_Store │ └── synthetic_option.py ├── OptionStrategy │ ├── __init__.py │ ├── butterfly │ │ └── __init__.py │ ├── protective_put │ │ └── __init__.py │ ├── put_call_parity │ │ └── __init__.py │ ├── short_straddle │ │ └── __init__.py │ ├── historical_statistics │ │ ├── __init__.py │ │ ├── vol_historical.py │ │ ├── vol_kde.py │ │ ├── pcp_arbitrage.py │ │ ├── yield_distribution.py │ │ ├── index_with_alpha.py │ │ ├── implied_vol_curves.py │ │ └── vol_filtration.py │ ├── option_market_capacity.py │ ├── bkt_strategy_vol_surface.py │ └── analysis_greeks.py ├── VolatilityModel │ ├── __init__.py │ ├── kernel_density.py │ ├── historical_volatility.py │ └── historical_volatility_test.py ├── .DS_Store ├── example_option_pricing.py ├── example_delta.py ├── calibration.py ├── example_svi_calibration.py └── example_blackvolsurface.py ├── .gitignore ├── .DS_Store ├── data └── morning_report.txt ├── README.md └── morning_report.txt /Utilities/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /back_test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data_access/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /PricingLibrary/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /back_test/model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /back_test/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /regular_reports/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /PricingLibrary/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /back_test/deprecated/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/Indexing/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/Optimization/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionMetrics/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionReplication/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/VolatilityModel/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/butterfly/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/protective_put/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/put_call_parity/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/short_straddle/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea/ 3 | *.csv 4 | *.png 5 | *.xlsx 6 | *.xls -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinhuli/OptionStrategy/HEAD/.DS_Store -------------------------------------------------------------------------------- /data/morning_report.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinhuli/OptionStrategy/HEAD/data/morning_report.txt -------------------------------------------------------------------------------- /OptionStrategyLib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinhuli/OptionStrategy/HEAD/OptionStrategyLib/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BackTest 2 | 3 | This project aimed to construct a option trading specific back test framework. 4 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionReplication/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinhuli/OptionStrategy/HEAD/OptionStrategyLib/OptionReplication/.DS_Store -------------------------------------------------------------------------------- /back_test/tmp_test.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_option_set import BaseOptionSet 2 | from data_access.get_data import get_50option_mktdata, get_comoption_mktdata 3 | import back_test.model.constant as c 4 | import datetime 5 | from PricingLibrary.BinomialModel import BinomialTree 6 | import Utilities.admin_write_util as admin 7 | import numpy as np 8 | 9 | c.OptionM.get_mdate_by_contractid() -------------------------------------------------------------------------------- /back_test/tests/base_product_test.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_product import BaseProduct 2 | from data_access.get_data import get_index_intraday 3 | 4 | import datetime 5 | 6 | """Back Test Settings""" 7 | # start_date = datetime.date(2015, 3, 1) 8 | start_date = datetime.date(2017, 10, 1) 9 | end_date = datetime.date(2017, 11, 1) 10 | 11 | """Collect Mkt Date""" 12 | 13 | df_index_metrics = get_index_intraday(start_date, end_date, 'index_50etf') 14 | product = BaseProduct(df_index_metrics) 15 | product.execute_order(None) -------------------------------------------------------------------------------- /PricingLibrary/OptionPricingUtil.py: -------------------------------------------------------------------------------- 1 | import QuantLib as ql 2 | 3 | 4 | def get_engine(bsmprocess, engineType): 5 | if engineType == 'AnalyticEuropeanEngine': 6 | engine = ql.AnalyticEuropeanEngine(bsmprocess) 7 | elif engineType == 'BinomialVanillaEngine': 8 | engine = ql.BinomialVanillaEngine(bsmprocess, 'crr', 801) 9 | elif engineType == 'AnalyticBarrierEngine': 10 | engine = ql.AnalyticBarrierEngine(bsmprocess) 11 | elif engineType == 'BinomialBarrierEngine': 12 | engine = ql.BinomialBarrierEngine(bsmprocess, 'crr', 801) 13 | else: 14 | engine = None 15 | return engine 16 | -------------------------------------------------------------------------------- /back_test/model/abstract_account.py: -------------------------------------------------------------------------------- 1 | from pandas import Series 2 | from abc import ABC, abstractmethod 3 | 4 | """ 5 | AbstractBaseProduct: an abstract class of base product. 6 | """ 7 | 8 | 9 | class AbstractAccount(ABC): 10 | """ 11 | initialize the AbstractBaseProduct class 12 | """ 13 | 14 | def __init__(self) -> None: 15 | super().__init__() 16 | 17 | """ 18 | init: initialize product after object is constructed. 19 | """ 20 | @abstractmethod 21 | def init(self) -> None: 22 | pass 23 | 24 | # """ 25 | # open_position: 26 | # """ 27 | # @abstractmethod 28 | # def add_record(self) -> bool: 29 | # pass -------------------------------------------------------------------------------- /back_test/tests/base_instrument_test.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_instrument import BaseInstrument 2 | import datetime 3 | from data_access.get_data import get_index_intraday 4 | from back_test.model.constant import FrequentType 5 | 6 | """Back Test Settings""" 7 | # start_date = datetime.date(2015, 3, 1) 8 | start_date = datetime.date(2017, 10, 1) 9 | end_date = datetime.date(2017, 11, 1) 10 | 11 | """Collect Mkt Date""" 12 | 13 | df_index_metrics = get_index_intraday(start_date, end_date, 'index_50etf') 14 | 15 | a = BaseInstrument(df_index_metrics, frequency=FrequentType.MINUTE) 16 | a.init() 17 | a.set_date(datetime.date(2017,10,10)) 18 | a.set_date(datetime.date(2017,1,1)) 19 | print(a) 20 | -------------------------------------------------------------------------------- /PricingLibrary/AbstractOptionPricingEngine.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class AbstractOptionPricingEngine(ABC): 4 | 5 | def __init__(self) -> None: 6 | super().__init__() 7 | 8 | 9 | @abstractmethod 10 | def NPV(self) -> float: 11 | pass 12 | 13 | @abstractmethod 14 | def reset_vol(self, vol): 15 | pass 16 | 17 | @abstractmethod 18 | def estimate_vol(self, price: float, presion: float = 0.00001, max_vol: float = 2.0): 19 | pass 20 | 21 | @abstractmethod 22 | def Delta(self,implied_vol:float) -> float: 23 | pass 24 | 25 | @abstractmethod 26 | def Gamma(self,implied_vol:float) -> float: 27 | pass -------------------------------------------------------------------------------- /back_test/tests/test.py: -------------------------------------------------------------------------------- 1 | import back_test.model.constant as c 2 | from back_test.model.constant import OptionM 3 | 4 | d = [2400.0, 2450.0, 2500.0, 2550.0, 2600.0, 2650.0, 2700.0, 2750.0, 2800.0, 2850.0, 2900.0, 2950.0, 3000.0, 3050.0, 5 | 3100.0, 3150.0] 6 | spot = 2010 7 | res = OptionM.get_strike_monenyes_rank_dict_nearest_strike(spot, d, c.OptionType.CALL) 8 | print(spot) 9 | print(d) 10 | print(res) 11 | spot = 3300 12 | res = OptionM.get_strike_monenyes_rank_dict_nearest_strike(spot, d, c.OptionType.CALL) 13 | print(spot) 14 | print(d) 15 | print(res) 16 | spot = 2010 17 | res = OptionM.get_strike_monenyes_rank_dict_nearest_strike(spot, d, c.OptionType.PUT) 18 | print(spot) 19 | print(d) 20 | print(res) 21 | spot = 3300 22 | res = OptionM.get_strike_monenyes_rank_dict_nearest_strike(spot, d, c.OptionType.PUT) 23 | print(spot) 24 | print(d) 25 | print(res) 26 | -------------------------------------------------------------------------------- /back_test/tests/base_option_set_test.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from data_access.get_data import get_50option_mktdata, get_50option_minute_with_underlying 3 | from back_test.model.base_option_set import BaseOptionSet 4 | 5 | start_date = datetime.date(2017, 11, 28) 6 | end_date = datetime.date(2017, 12, 31) 7 | df_option_metrics = get_50option_mktdata(start_date, end_date) 8 | bkt_optionset = BaseOptionSet(df_option_metrics) 9 | bkt_optionset.init() 10 | bkt_optionset.set_date(datetime.date(2017, 12, 5)) 11 | bkt_optionset.set_date(datetime.date(2017, 12, 1)) 12 | bkt_optionset.set_date(datetime.date(2017, 11, 30)) 13 | bkt_optionset.set_date(datetime.date(2017, 12, 7)) 14 | print(bkt_optionset) 15 | df_50_metrics = get_50option_minute_with_underlying(start_date, end_date) 16 | etf_optionset = BaseOptionSet(df_50_metrics) 17 | etf_optionset.init() 18 | etf_optionset.set_date(datetime.date(2017,12,5)) 19 | etf_optionset.set_date(datetime.date(2017,12,1)) 20 | -------------------------------------------------------------------------------- /morning_report.txt: -------------------------------------------------------------------------------- 1 | 2 | 2018年7月20日,豆粕期权1809合约总成交量为121018手,较前一交易日增加72508手,总持仓量为746322手,较前一交易日减少14114手。其中,认购期权总成交量为78934手,认沽期权总成交量为42084手,成交量认沽认购比(PCR)为0.53,认购期权总持仓量为463160手,认沽期权总持仓量为283162手,持仓量认沽认购比(PCR)为0.61。此外1901合约当日认购与认沽合约总成交1810手, 总持仓为14082手。最大持仓点位方面,1809合约认沽期权最大持仓对应行权价3000点,其次为2950点,持仓量分别为56460与30646手,认购期权最大持仓点位为3600点,其次为3200点,持仓量分别为109374与57010手。 3 | 4 | 2018年7月20日,白糖期权1809合约总成交量为39386手,较前一交易日增加15556手,总持仓量为196110手,较前一交易日减少4346手。其中,认购期权总成交量为20816手,认沽期权总成交量为18570手,成交量认沽认购比(PCR)为0.89,认购期权总持仓量为148592手,认沽期权总持仓量为47518手,持仓量认沽认购比(PCR)为0.32。此外1901合约当日认购与认沽合约总成交2648手, 总持仓为19662手。最大持仓点位方面,1809合约认沽期权最大持仓对应行权价4800点,其次为4700点,持仓量分别为10500与7750手,认购期权最大持仓点位为6000点,其次为5300点,持仓量分别为14150与11560手。 5 | 6 | 隐含波动率方面,豆粕期权1809合约隐含波动率较前一交易日整体有所提升,其中,认沽期权平值合约隐含波动率为15.78%,较上一交易日(13.28%)增加约2.5%,认购期权平值合约隐含波动率为14.08%,较上一交易日(15.12%)减少约1.0%。期货主力合约近1M历史波动率为14.53%,近3M历史波动率为18.33%,平值期权隐含波动率较近1M历史波动率偏高,较近3M历史波动率偏低。白糖期权1809合约隐含波动率较前一交易日整体有所下降,平值合约隐含波动率为14.76%,较上一交易日(17.13%)减少约2.4%。期货主力合约近1M历史波动率为13.66%,近3M历史波动率为12.63%,平值期权隐含波动率较近1M历史波动率偏高,较近3M历史波动率亦偏高。 7 | 8 | 9 | -------------------------------------------------------------------------------- /back_test/model/abstract_base_product_set.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | """ 4 | AbstractBaseProductSet: an abstract class of base product set. 5 | """ 6 | 7 | 8 | class AbstractBaseProductSet(ABC): 9 | """ 10 | initialize the AbstractBaseProduct class 11 | """ 12 | 13 | def __init__(self) -> None: 14 | super().__init__() 15 | 16 | """ 17 | init: initialize product set after object is constructed. 18 | """ 19 | @abstractmethod 20 | def init(self) -> None: 21 | pass 22 | 23 | 24 | """ 25 | pre_process: pre process data to populate option set 26 | """ 27 | @abstractmethod 28 | def pre_process(self) -> None: 29 | pass 30 | 31 | """ 32 | next: move forward to next tick for each element in product set 33 | """ 34 | @abstractmethod 35 | def next(self) -> None: 36 | pass 37 | 38 | """ 39 | has_next: return whether has next iter 40 | """ 41 | @abstractmethod 42 | def has_next(self) -> bool: 43 | pass -------------------------------------------------------------------------------- /PricingLibrary/tests/delta_calculate_example.py: -------------------------------------------------------------------------------- 1 | import math 2 | import datetime 3 | from PricingLibrary.BlackFormular import BlackFormula 4 | from PricingLibrary.EngineQuantlib import QlBlackFormula 5 | from PricingLibrary.BlackCalculator import BlackCalculator 6 | import back_test.model.constant as c 7 | 8 | dt_eval = datetime.date(2017, 1, 1) 9 | dt_maturity = datetime.date(2017, 4, 1) 10 | spot_price = 120 11 | strike_price = 100 12 | volatility = 0.3 # the historical vols or implied vols 13 | dividend_rate = 0 14 | risk_free_rate = 0.03 15 | option_price = 30.0 16 | 17 | black_formula = BlackFormula(dt_eval, dt_maturity, c.OptionType.CALL,spot_price,strike_price,option_price) 18 | iv1 = black_formula.ImpliedVolApproximation() 19 | estimated_price1 = BlackCalculator(dt_eval,dt_maturity,strike_price,c.OptionType.CALL,spot_price,iv1).NPV() 20 | ql_black = QlBlackFormula(dt_eval, dt_maturity, c.OptionType.CALL,spot_price,strike_price) 21 | iv2, estimated_price2 = ql_black.estimate_vol(option_price) 22 | 23 | print(iv1,estimated_price1) 24 | print(iv2,estimated_price2) -------------------------------------------------------------------------------- /data_access/db_utilities.py: -------------------------------------------------------------------------------- 1 | 2 | def code_map_dce(): 3 | map = {'豆一':'a', 4 | '豆二':'b', 5 | '胶合板':'bb', 6 | '玉米':'c', 7 | '玉米淀粉':'cs', 8 | '纤维板':'fb', 9 | '铁矿石': 'i', 10 | '焦炭': 'j', 11 | '鸡蛋': 'jd', 12 | '焦煤': 'jm', 13 | '聚乙烯': 'l', 14 | '豆粕': 'm', 15 | '棕榈油': 'p', 16 | '聚丙烯': 'pp', 17 | '聚氯乙烯': 'v', 18 | '豆油': 'y', 19 | } 20 | return map 21 | 22 | def get_codename(name): 23 | map = code_map_dce() 24 | 25 | return map.get(name) 26 | 27 | def key_map_sfe(): 28 | map = { 29 | 'codename':u'PRODUCTID', 30 | 'contractmonth':u'DELIVERYMONTH', 31 | 'amt_last_close':u'CLOSEPRICE', 32 | 'amt_last_settlement':u'PRESETTLEMENTPRICE', 33 | 'amt_open':u'OPENPRICE', 34 | 'amt_high':u'HIGHESTPRICE', 35 | 'amt_low':u'LOWESTPRICE', 36 | 'amt_close':u'CLOSEPRICE', 37 | 'amt_settlement':u'SETTLEMENTPRICE', 38 | 'amt_trading_volume': u'VOLUME', 39 | 'amt_holding_volume': u'OPENINTEREST', 40 | } 41 | return map -------------------------------------------------------------------------------- /PricingLibrary/Evaluation.py: -------------------------------------------------------------------------------- 1 | import QuantLib as ql 2 | 3 | class Evaluation: 4 | 5 | def __init__(self,evalDate,daycounter,calendar): 6 | self.evalDate = evalDate 7 | self.daycounter = daycounter 8 | self.calendar = calendar 9 | ql.Settings.instance().evaluationDate = evalDate 10 | 11 | def get_bsmprocess(self,rf, spot_price,vol_surface): 12 | yield_ts = ql.YieldTermStructureHandle(ql.FlatForward(self.evalDate, rf, self.daycounter)) 13 | dividend_ts = ql.YieldTermStructureHandle(ql.FlatForward(self.evalDate, 0.0, self.daycounter)) 14 | vol_ts = ql.BlackVolTermStructureHandle(vol_surface) 15 | process = ql.BlackScholesMertonProcess(ql.QuoteHandle(ql.SimpleQuote(spot_price)), dividend_ts,yield_ts, vol_ts) 16 | return process 17 | 18 | def get_bsmprocess_cnstvol(self,rf,spot_price,vol): 19 | yield_ts = ql.YieldTermStructureHandle(ql.FlatForward(self.evalDate, rf, self.daycounter)) 20 | dividend_ts = ql.YieldTermStructureHandle(ql.FlatForward(self.evalDate, 0.0, self.daycounter)) 21 | vol_ts = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(self.evalDate, self.calendar, vol, self.daycounter)) 22 | process = ql.BlackScholesMertonProcess(ql.QuoteHandle(ql.SimpleQuote(spot_price)), dividend_ts,yield_ts, vol_ts) 23 | return process 24 | -------------------------------------------------------------------------------- /data_access/craw_data_hist1.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | import datetime 4 | from WindPy import w 5 | from data_access import spider_api_dce as dce 6 | from data_access import spider_api_sfe as sfe 7 | from data_access import spider_api_czce as czce 8 | from data_access.db_data_collection import DataCollection 9 | from Utilities import admin_write_util as admin 10 | import pandas as pd 11 | import numpy as np 12 | 13 | """ 14 | SH300 TR 15 | wind data 16 | """ 17 | 18 | w.start() 19 | 20 | conn = admin.conn_mktdata() 21 | conn_intraday = admin.conn_intraday() 22 | 23 | index_daily = admin.table_indexes_mktdata() 24 | 25 | 26 | dc = DataCollection() 27 | 28 | 29 | today = datetime.date.today() 30 | beg_date = datetime.date(2015, 1, 1) 31 | # beg_date = datetime.date(2018, 9, 1) 32 | end_date = datetime.date.today() 33 | 34 | date_range = w.tdays(beg_date, end_date, "").Data[0] 35 | date_range = sorted(date_range,reverse=True) 36 | for dt in date_range: 37 | windcode = "H00300.CSI" 38 | id_instrument = 'index_300sh_total_return' 39 | datestr = dt.strftime("%Y-%m-%d") 40 | db_data = dc.table_index().wind_data_index(windcode, datestr, id_instrument) 41 | # print(db_data) 42 | try: 43 | conn.execute(index_daily.insert(), db_data) 44 | print('index_300sh_total_return -- inserted into data base succefully') 45 | except Exception as e: 46 | print(e) 47 | 48 | -------------------------------------------------------------------------------- /back_test/tests/test_baseAccount.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from back_test.model.base_future_coutinuous import BaseFutureCoutinuous 3 | from back_test.model.base_account import BaseAccount 4 | from data_access.get_data import get_dzqh_cf_minute,get_dzqh_cf_daily 5 | import datetime 6 | from back_test.model.trade import Trade 7 | from back_test.model.constant import TradeType, Util, FrequentType 8 | class TestBaseAccount(TestCase): 9 | @classmethod 10 | def setUpClass(cls): 11 | start_date = datetime.date(2017, 10, 1) 12 | end_date = datetime.date(2017, 11, 21) 13 | df_cf_minute = get_dzqh_cf_minute(start_date, end_date, 'if') 14 | df_cf = get_dzqh_cf_daily(start_date, end_date, 'if') 15 | cls.future = BaseFutureCoutinuous(df_cf_minute, df_cf, frequency=FrequentType.MINUTE) 16 | cls.future.init() 17 | cls.account = BaseAccount(Util.BILLION) 18 | cls.trading_desk = Trade() 19 | 20 | def test_over_all_account(self): 21 | while self.future.has_next(): 22 | order = self.account.create_trade_order(self.future, TradeType.OPEN_LONG,10) 23 | execution_res = self.future.execute_order(order) 24 | self.account.add_record(execution_res, self.future) 25 | self.assertGreater(self.account.cash, 0, 26 | "account cash lower than 0 on order {}".format("order")) 27 | self.trading_desk.add_pending_order(order) 28 | self.future.next() 29 | 30 | -------------------------------------------------------------------------------- /OptionStrategyLib/example_option_pricing.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import QuantLib as ql 3 | from PricingLibrary.OptionMetrics import OptionMetrics 4 | from PricingLibrary.Options import OptionPlainEuropean 5 | from PricingLibrary.Evaluation import Evaluation 6 | 7 | 8 | ################################################################################################## 9 | evalDate = datetime.date(2018,1,2) 10 | ql_evalDate = ql.Date(evalDate.day,evalDate.month,evalDate.year) 11 | rf = 0.03 12 | calendar = ql.China() 13 | daycounter = ql.ActualActual() 14 | evaluation = Evaluation(ql_evalDate,daycounter,calendar) 15 | engineType = 'AnalyticEuropeanEngine' 16 | ################################################################################################## 17 | spot_price = 2.907 18 | option_price = 0.08 19 | strike = 2.9 20 | ql_mdt = ql.Date(28,2,2018) 21 | ################################################################################################## 22 | option = OptionPlainEuropean(strike,ql_mdt,ql.Option.Call) 23 | option_metrics = OptionMetrics(option) 24 | iv = option_metrics.implied_vol(evaluation,rf, spot_price, option_price) 25 | delta = option_metrics.delta(evaluation,rf, spot_price, option_price,engineType) 26 | theta = option_metrics.theta(evaluation,rf, spot_price, option_price,engineType) 27 | vega = option_metrics.vega(evaluation,rf, spot_price, option_price,engineType) 28 | # eff_delta = option_metrics.effective_delta(evaluation,rf ,spot_price, option_price, engineType) 29 | print('implied vol : ', iv) 30 | print('delta : ', delta) 31 | print('theta : ', theta) 32 | print('vega : ', vega) 33 | 34 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/vol_historical.py: -------------------------------------------------------------------------------- 1 | from data_access import get_data 2 | import back_test.model.constant as c 3 | import datetime 4 | from Utilities.PlotUtil import PlotUtil 5 | import matplotlib.pyplot as plt 6 | from OptionStrategyLib.VolatilityModel.historical_volatility import HistoricalVolatilityModels as Histvol 7 | from Utilities import timebase 8 | import numpy as np 9 | 10 | pu = PlotUtil() 11 | start_date = datetime.date(2010, 1, 1) 12 | end_date = datetime.date.today() 13 | dt_histvol = start_date - datetime.timedelta(days=40) 14 | # min_holding = 18 15 | 16 | 17 | name_code = name_code_option = c.Util.STR_CU 18 | # df_metrics = get_data.get_comoption_mktdata(start_date, end_date,name_code) 19 | df_future_c1_daily = get_data.get_mktdata_future_c1_daily(dt_histvol, end_date, name_code) 20 | # df_future_c1_daily = get_data.get_index_mktdata(dt_histvol, end_date, name_code) 21 | 22 | 23 | """ 历史波动率 """ 24 | vol_10 = Histvol.hist_vol(df_future_c1_daily[c.Util.AMT_CLOSE],n=10) 25 | vol_20 = Histvol.hist_vol(df_future_c1_daily[c.Util.AMT_CLOSE],n=20) 26 | vol_30 = Histvol.hist_vol(df_future_c1_daily[c.Util.AMT_CLOSE],n=30) 27 | vol_60 = Histvol.hist_vol(df_future_c1_daily[c.Util.AMT_CLOSE],n=60) 28 | vol_90 = Histvol.hist_vol(df_future_c1_daily[c.Util.AMT_CLOSE],n=90) 29 | 30 | 31 | df = df_future_c1_daily[[c.Util.DT_DATE]] 32 | df['hist_vol_10'] = vol_10 33 | df['hist_vol_20'] = vol_20 34 | df['hist_vol_30'] = vol_30 35 | df['hist_vol_60'] = vol_60 36 | df['hist_vol_90'] = vol_90 37 | df = df.dropna() 38 | df = df.sort_values(c.Util.DT_DATE,ascending=False) 39 | print(df) 40 | df.to_csv('../../../data/'+name_code+'_histvol.csv') -------------------------------------------------------------------------------- /OptionStrategyLib/VolatilityModel/kernel_density.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.stats import norm 3 | from matplotlib import pylab as plt 4 | from sklearn.neighbors import KernelDensity 5 | from scipy.stats import gaussian_kde 6 | from statsmodels.nonparametric.kde import KDEUnivariate 7 | from statsmodels.nonparametric.kernel_density import KDEMultivariate 8 | import pandas as pd 9 | import back_test.model.constant as c 10 | from Utilities.PlotUtil import PlotUtil 11 | 12 | 13 | def kde_scipy(x, x_grid, bandwidth=0.2, **kwargs): 14 | """Kernel Density Estimation with Scipy""" 15 | # Note that scipy weights its bandwidth by the covariance of the 16 | # input data. To make the results comparable to the other methods, 17 | # we divide the bandwidth by the sample standard deviation here. 18 | kde = gaussian_kde(x, bw_method=bandwidth / x.std(ddof=1), **kwargs) 19 | return kde.evaluate(x_grid) 20 | 21 | 22 | def kde_statsmodels_u(x, x_grid, bandwidth=0.2, **kwargs): 23 | """Univariate Kernel Density Estimation with Statsmodels""" 24 | kde = KDEUnivariate(x) 25 | kde.fit(bw=bandwidth, **kwargs) 26 | return kde.evaluate(x_grid) 27 | 28 | 29 | def kde_statsmodels_m(x, x_grid, bandwidth=0.2, **kwargs): 30 | """Multivariate Kernel Density Estimation with Statsmodels""" 31 | kde = KDEMultivariate(x, bw=bandwidth * np.ones_like(x), 32 | var_type='c', **kwargs) 33 | return kde.pdf(x_grid) 34 | 35 | 36 | def kde_sklearn(x, x_grid, bandwidth=0.2, **kwargs): 37 | """Kernel Density Estimation with Scikit-learn""" 38 | kde_skl = KernelDensity(bandwidth=bandwidth, **kwargs) 39 | # kde_skl = KernelDensity() 40 | kde_skl.fit(x[:, np.newaxis]) 41 | # score_samples() returns the log-likelihood of the samples 42 | log_pdf = kde_skl.score_samples(x_grid[:, np.newaxis]) 43 | return np.exp(log_pdf) 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /PricingLibrary/svimodel.py: -------------------------------------------------------------------------------- 1 | from PricingLibrary.BlackCalculator import BlackCalculator 2 | import numpy as np 3 | import math 4 | class svimodel: 5 | 6 | def __init__(self,ttm,params = [0.02,0.05,-0.8,-0.2,0.1]): 7 | # 虽然params有defalt值,但必须先对当天数据进行SVI模型校准,直接用default值计算结果肯定是不对的。 8 | a_star, b_star, rho_star, m_star, sigma_star = params 9 | self.a = a_star 10 | self.b = b_star 11 | self.rho = rho_star 12 | self.m = m_star 13 | self.sigma = sigma_star 14 | self.ttm = ttm 15 | 16 | def svi_iv_function(self,x_svi): 17 | return np.sqrt( self.a + self.b * (self.rho * (x_svi - self.m) + 18 | np.sqrt((x_svi - self.m) ** 2 + self.sigma ** 2))) 19 | 20 | # 计算隐含波动率对行权价的一阶倒数(dSigma_dK),用于全Delta计算 21 | def calculate_dSigma_dK(self,strike,forward,ttm): 22 | x = math.log(strike / forward, math.e) 23 | temp = np.sqrt((x-self.m)**2+self.sigma**2) 24 | temp1 = (x-self.m)/temp 25 | temp2 = np.sqrt(self.a+self.b*(self.rho*(x-self.m)+temp)) 26 | dVol_dX = self.b*(self.rho+temp1)/(2*temp2) 27 | dX_dK = 1/strike 28 | dSigma_dK = dVol_dX*dX_dK 29 | return dSigma_dK 30 | 31 | # 计算Effective Delta 32 | def calculate_effective_delta(self, spot, dS, strike, discount, iscall): 33 | s_plus = spot + dS 34 | s_minus = spot - dS 35 | f_plus = s_plus / discount 36 | f_minus = s_minus / discount 37 | stdDev_plus = self.svi_iv_function(math.log(strike / f_plus, math.e)) * math.sqrt(self.ttm) 38 | stdDev_minus = self.svi_iv_function(math.log(strike / f_plus, math.e)) * math.sqrt(self.ttm) 39 | black_splus = BlackCalculator(strike, f_plus, stdDev_plus, discount, iscall) 40 | black_sminus = BlackCalculator(strike, f_minus, stdDev_minus, discount, iscall) 41 | delta_eff = (black_splus.value() - black_sminus.value()) / (s_plus - s_minus) 42 | return delta_eff -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/vol_kde.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib import pylab as plt 3 | import pandas as pd 4 | import back_test.model.constant as c 5 | from Utilities.PlotUtil import PlotUtil 6 | from OptionStrategyLib.VolatilityModel.kernel_density import kde_sklearn 7 | 8 | pu = PlotUtil() 9 | df_data = pd.read_csv('../../../data/df_data.csv') 10 | # df_data = pd.read_csv('../../data/df_underlying.csv') 11 | dates = list(df_data[c.Util.DT_DATE]) 12 | histvols = np.array(df_data[c.Util.AMT_HISTVOL+'_20']) 13 | garman_klass_vols = np.array(df_data[c.Util.AMT_GARMAN_KLASS+'_20']) 14 | parkinson_vols = np.array(df_data[c.Util.AMT_PARKINSON_NUMBER+'_20']) 15 | logvols = np.log(histvols) 16 | 17 | x_grid = np.linspace(min(histvols), max(histvols), 1000) 18 | 19 | # plt.figure(1) 20 | pdf_cc = kde_sklearn(histvols, x_grid, bandwidth=0.03) 21 | # plt.plot(x_grid, pdf_cc,'r--', label='Estimate') 22 | # plt.hist(histvols, bins=30, normed=True, color=(0,.5,0,1), label='Histogram') 23 | 24 | # plt.figure(2) 25 | pdf_gk = kde_sklearn(garman_klass_vols, x_grid, bandwidth=0.03) 26 | # plt.plot(x_grid, pdf_gk,'r--', label='Estimate') 27 | # plt.hist(garman_klass_vols, bins=30, normed=True, color=(0,.5,0,1), label='Histogram') 28 | 29 | 30 | # plt.figure(3) 31 | pdf_p = kde_sklearn(parkinson_vols, x_grid, bandwidth=0.03) 32 | # plt.plot(x_grid, pdf_p,'r--', label='Estimate') 33 | # plt.hist(parkinson_vols, bins=30, normed=True, color=(0,.5,0,1), label='Histogram') 34 | 35 | plt.figure(1) 36 | pu.plot_line_chart(x_grid,[pdf_cc,pdf_gk,pdf_p],['Close-to-close','Garman_Klass','Parkinson']) 37 | 38 | 39 | plt.figure(2) 40 | 41 | vols_1w = np.array(df_data[c.Util.AMT_HISTVOL+'_5']) 42 | pdf_1w = kde_sklearn(vols_1w, x_grid, bandwidth=0.03) 43 | vols_1m = np.array(df_data[c.Util.AMT_HISTVOL+'_20']) 44 | pdf_1m = kde_sklearn(vols_1m, x_grid, bandwidth=0.03) 45 | vols_3m = np.array(df_data[c.Util.AMT_HISTVOL+'_60']) 46 | pdf_3m = kde_sklearn(vols_3m, x_grid, bandwidth=0.03) 47 | pu.plot_line_chart(x_grid,[pdf_1w,pdf_1m,pdf_3m],['Weekly kde','Monthly kde','Quarterly kde']) 48 | 49 | 50 | plt.show() 51 | -------------------------------------------------------------------------------- /Utilities/calculate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.stats as ss 3 | import datetime 4 | import pandas as pd 5 | 6 | 7 | 8 | def calculate_histvol(data, n): 9 | datas = np.log(data) 10 | df = datas.diff() 11 | vol = df.rolling(window=n).std() * np.sqrt(252) 12 | return vol 13 | 14 | 15 | def simulate(S0, sigma, N, mu=0.05): 16 | dt = 1.0 / (365 * 1440.0) # 1min 17 | t = 1 18 | S = S0 19 | path = [S] 20 | seeds = list(np.random.normal(0, 1, N)) 21 | while t < N: 22 | normal = seeds[t - 1] 23 | ds = mu * S * dt + sigma * S * normal * np.sqrt(dt) 24 | S += ds 25 | t += 1 26 | path.append(S) 27 | return S, path 28 | 29 | 30 | def montecarlo(S0, sigma, dates, mu=0.05, N=10000): 31 | datetimes = generate_times(dates) 32 | N = len(datetimes) 33 | S, path = simulate(S0, sigma, N) 34 | df = pd.DataFrame() 35 | df['dt_datetime'] = datetimes 36 | df['amt_close'] = path 37 | df['dt_date'] = df['dt_datetime'].apply(lambda x: x.date()) 38 | return df 39 | 40 | 41 | def generate_times(dt_dates): 42 | dt_times = [] 43 | for dt_date in dt_dates: 44 | dt_time = datetime.datetime(dt_date.year, dt_date.month, dt_date.day, 9, 30, 0) 45 | dt_times.append(dt_time) 46 | while dt_time < datetime.datetime(dt_date.year, dt_date.month, dt_date.day, 11, 30, 0): 47 | dt_time = dt_time + datetime.timedelta(minutes=1) 48 | dt_times.append(dt_time) 49 | dt_time = datetime.datetime(dt_date.year, dt_date.month, dt_date.day, 13, 0, 0) 50 | dt_times.append(dt_time) 51 | while dt_time < datetime.datetime(dt_date.year, dt_date.month, dt_date.day, 15, 0, 0): 52 | dt_time = dt_time + datetime.timedelta(minutes=1) 53 | dt_times.append(dt_time) 54 | return dt_times 55 | 56 | 57 | # t = generate_times([datetime.date(2018, 1, 1), datetime.date(2018, 1, 2)]) 58 | # print(t) 59 | montecarlo(3000, 0.2,[datetime.date(2018, 1, 1), datetime.date(2018, 1, 2)]) 60 | # s, path = simulate_minute(3000, 0.2, 30) 61 | # print(s) 62 | # print(path) 63 | -------------------------------------------------------------------------------- /OptionStrategyLib/VolatilityModel/historical_volatility.py: -------------------------------------------------------------------------------- 1 | import math 2 | import pandas as pd 3 | import numpy as np 4 | from back_test.model.constant import Util 5 | 6 | 7 | class HistoricalVolatilityModels: 8 | 9 | @staticmethod 10 | def hist_vol(closes,n=20): 11 | # df_vol = df[[Util.DT_DATE]] 12 | series = np.log(closes).diff() 13 | # df_vol[Util.AMT_HISTVOL+'_'+str(n)] = series.rolling(window=n).std() * math.sqrt(252) 14 | # df_vol = df_vol.dropna().set_index(Util.DT_DATE) 15 | res_series = series.rolling(window=n).std() * math.sqrt(252) 16 | return res_series 17 | 18 | @staticmethod 19 | def parkinson_number(df,n=20): 20 | df_vol = df[[Util.DT_DATE]] 21 | squred_log_h_l = df.apply(HistoricalVolatilityModels.fun_squred_log_high_low, axis=1) 22 | sum_squred_log_h_l = squred_log_h_l.rolling(window=n).sum() 23 | df_vol[Util.AMT_PARKINSON_NUMBER+'_'+str(n)] = sum_squred_log_h_l.apply( 24 | lambda x: math.sqrt(252 * x / (n * 4 * math.log(2)))) 25 | df_vol = df_vol.dropna().set_index(Util.DT_DATE) 26 | return df_vol 27 | 28 | @staticmethod 29 | def garman_klass(df, n=20): 30 | df_vol = df[[Util.DT_DATE]] 31 | tmp = df.apply(HistoricalVolatilityModels.fun_garman_klass, axis=1) 32 | sum_tmp = tmp.rolling(window=n).sum() 33 | df_vol[Util.AMT_GARMAN_KLASS+'_'+str(n)] = sum_tmp.apply(lambda x:math.sqrt(x*252/n)) 34 | df_vol = df_vol.dropna().set_index(Util.DT_DATE) 35 | return df_vol 36 | 37 | @staticmethod 38 | def fun_squred_log_high_low(df: pd.Series) -> float: 39 | return (math.log(df[Util.AMT_HIGH] / df[Util.AMT_LOW])) ** 2 40 | 41 | @staticmethod 42 | def fun_squred_log_close_open(df: pd.Series) -> float: 43 | return (math.log(df[Util.AMT_CLOSE] / df[Util.AMT_OPEN])) ** 2 44 | 45 | @staticmethod 46 | def fun_garman_klass(df: pd.Series) -> float: 47 | return 0.5 * HistoricalVolatilityModels.fun_squred_log_high_low(df) - (2 * math.log(2) - 1) * HistoricalVolatilityModels.fun_squred_log_close_open(df) 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /OptionStrategyLib/VolatilityModel/historical_volatility_test.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_option_set import BaseOptionSet 2 | from data_access import get_data 3 | import back_test.model.constant as c 4 | import datetime 5 | from OptionStrategyLib.VolatilityModel.historical_volatility import HistoricalVolatilityModels as Histvol 6 | from Utilities.PlotUtil import PlotUtil 7 | 8 | 9 | pu = PlotUtil() 10 | start_date = datetime.date(2010, 1, 1) 11 | end_date = datetime.date(2018, 8, 8) 12 | dt_histvol = start_date 13 | 14 | """ 50ETF option """ 15 | name_code = c.Util.STR_IH 16 | name_code_option = c.Util.STR_50ETF 17 | # df_metrics = get_data.get_50option_mktdata(start_date, end_date) 18 | df_future_c1_daily = get_data.get_dzqh_cf_c1_daily(dt_histvol, end_date, name_code) 19 | # df_future_c1_daily = get_data.get_index_mktdata(start_date,end_date,'index_50sh') 20 | """ 历史波动率 """ 21 | df_cc_1m = Histvol.hist_vol(df_future_c1_daily) 22 | # df_p_1m = Histvol.parkinson_number(df_future_c1_daily) 23 | # df_gk_1m = Histvol.garman_klass(df_future_c1_daily) 24 | df_data = df_future_c1_daily.join(df_cc_1m,on=c.Util.DT_DATE,how='left') 25 | # df_data = df_data.join(df_p_1m,on=c.Util.DT_DATE,how='left') 26 | # df_data = df_data.join(df_gk_1m,on=c.Util.DT_DATE,how='left') 27 | 28 | df_cc_1w = Histvol.hist_vol(df_future_c1_daily,n=5) 29 | # df_p_1w = Histvol.parkinson_number(df_future_c1_daily,n=5) 30 | # df_gk_1w = Histvol.garman_klass(df_future_c1_daily,n=5) 31 | df_data = df_data.join(df_cc_1w,on=c.Util.DT_DATE,how='left') 32 | # df_data = df_data.join(df_p_1w,on=c.Util.DT_DATE,how='left') 33 | # df_data = df_data.join(df_gk_1w,on=c.Util.DT_DATE,how='left') 34 | 35 | df_cc_3m = Histvol.hist_vol(df_future_c1_daily,n=60) 36 | # df_p_3m = Histvol.parkinson_number(df_future_c1_daily,n=60) 37 | # df_gk_3m = Histvol.garman_klass(df_future_c1_daily,n=60) 38 | df_data = df_data.join(df_cc_3m,on=c.Util.DT_DATE,how='left') 39 | # df_data = df_data.join(df_p_3m,on=c.Util.DT_DATE,how='left') 40 | # df_data = df_data.join(df_gk_3m,on=c.Util.DT_DATE,how='left') 41 | 42 | df_data = df_data.dropna() 43 | 44 | # df_data.to_csv('../../data/df_data.csv') 45 | df_data.to_csv('../../data/df_underlying.csv') -------------------------------------------------------------------------------- /Utilities/Analysis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 10 17:29:49 2018 4 | 5 | @author: xhlidzqh 6 | """ 7 | import numpy as np 8 | import pandas as pd 9 | 10 | 11 | def get_netvalue_analysis(netvalue, freq='D'): 12 | '''由净值序列进行指标统计,netvalue应为Series''' 13 | if freq == 'D': 14 | oneyear = 252 15 | elif freq == 'W': 16 | oneyear = 50 17 | elif freq == 'M': 18 | oneyear = 12 19 | else: 20 | print('Not Right freq') 21 | # 交易次数 22 | tradeslen = netvalue.shape[0] 23 | # 收益率序列 24 | tmp = netvalue.shift() 25 | tmp[0] = 1 26 | returns = netvalue / tmp - 1 27 | # 累计收益率 28 | totalreturn = netvalue.iloc[-1] - 1 29 | # 年化收益率 30 | return_yr = (1 + totalreturn) ** (oneyear / tradeslen) - 1 31 | # 年化波动率 32 | volatility_yr = np.std(returns, ddof=0) * np.sqrt(oneyear) 33 | # 夏普比率 34 | sharpe = (return_yr - 0.024) / volatility_yr 35 | # 回撤 36 | drawdowns = get_maxdrawdown(netvalue) 37 | # 最大回撤 38 | maxdrawdown = min(drawdowns) 39 | #收益风险比 40 | profit_risk_ratio = return_yr / np.abs(maxdrawdown) 41 | # 盈利次数 42 | win_count = (returns >= 0).sum() 43 | # 亏损次数 44 | lose_count = (returns < 0).sum() 45 | # 胜率 46 | win_rate = win_count / (win_count + lose_count) 47 | # 盈亏比 48 | p_over_l = returns[returns > 0].mean() / np.abs(returns[returns < 0].mean()) 49 | r = pd.Series() 50 | r['累计收益率'] = totalreturn 51 | r['年化收益率'] = return_yr 52 | r['年化波动率'] = volatility_yr 53 | r['最大回撤率'] = maxdrawdown 54 | r['胜率('+freq+')'] = win_rate 55 | r['盈亏比'] = p_over_l 56 | r['夏普比率'] = sharpe 57 | r['Calmar比'] = profit_risk_ratio 58 | 59 | return r 60 | 61 | 62 | def get_maxdrawdown(netvalue): 63 | ''' 64 | 最大回撤率计算 65 | ''' 66 | maxdrawdowns = pd.Series(index=netvalue.index) 67 | for i in np.arange(len(netvalue.index)): 68 | highpoint = netvalue.iloc[0:(i + 1)].max() 69 | if highpoint == netvalue.iloc[i]: 70 | maxdrawdowns.iloc[i] = 0 71 | else: 72 | maxdrawdowns.iloc[i] = netvalue.iloc[i] / highpoint - 1 73 | 74 | 75 | return maxdrawdowns -------------------------------------------------------------------------------- /OptionStrategyLib/Indexing/IntradayImpVol.py: -------------------------------------------------------------------------------- 1 | from data_access.get_data import get_50option_mktdata as option_data, get_50option_intraday as intraday_data 2 | from back_test.model.base_option_set import BaseOptionSet 3 | from back_test.model.constant import Util, OptionUtil 4 | import datetime 5 | import math 6 | import pandas as pd 7 | 8 | 9 | class IntradayImpVol(BaseOptionSet): 10 | def __init__(self, start_date, end_date, min_holding): 11 | df_daily = option_data(start_date, end_date) 12 | df_intraday = intraday_data(start_date, end_date) 13 | super().__init__(df_data=df_intraday,df_daily_data=df_daily, rf=0.03) 14 | self.min_holding = min_holding 15 | 16 | 17 | def get_atm_options(self, nbr_maturity): 18 | maturity = self.select_maturity_date(nbr_maturity, min_holding=self.min_holding) 19 | list_atm_call, list_atm_put = self.get_options_list_by_moneyness_mthd1(moneyness_rank=0, maturity=maturity) 20 | atm_call = self.select_higher_volume(list_atm_call) 21 | atm_put = self.select_higher_volume(list_atm_put) 22 | return atm_call, atm_put 23 | 24 | def get_atm_iv_average(self, nbr_maturity): 25 | atm_call, atm_put = self.get_atm_options(nbr_maturity) 26 | iv_call = atm_call.get_implied_vol() 27 | iv_put = atm_put.get_implied_vol() 28 | iv_avg = (iv_call + iv_put) / 2 29 | return iv_avg 30 | 31 | 32 | start_date = datetime.date.today() - datetime.timedelta(days=10) 33 | end_date = datetime.date.today() 34 | nbr_maturity = 0 35 | min_holding = 8 36 | Impvol = IntradayImpVol(start_date, end_date, min_holding) 37 | Impvol.init() 38 | 39 | # maturity = Impvol.select_maturity_date(nbr_maturity, min_holding=Impvol.min_holding) 40 | # iv_htr = Impvol.get_atm_iv_by_htbr(maturity) 41 | # iv_avg = Impvol.get_atm_iv_average(nbr_maturity) 42 | # print('iv_htr : ', iv_htr) 43 | # print('iv_avg : ', iv_avg) 44 | # 45 | # 46 | # atm_call, atm_put = Impvol.get_atm_options(nbr_maturity) # 基于收盘价的平值期权 47 | # id_atm_call = atm_call.id_instrument() 48 | 49 | maturity = Impvol.select_maturity_date(nbr_maturity=nbr_maturity,min_holding=min_holding) 50 | iv = Impvol.get_atm_iv_by_htbr(maturity) 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /back_test/tests/constant_test.py: -------------------------------------------------------------------------------- 1 | from back_test.model.constant import OptionUtil, OptionType 2 | 3 | dict = { 4 | 2.35:1, 5 | 2.4:1 , 6 | 2.45: -1, 7 | 2.5: 0, 8 | 2.55: 1, 9 | 2.6: 1, 10 | 2.65: 2, 11 | 2.7: 3, 12 | 2.75: 4, 13 | 2.8: 5, 14 | 2.85: 6, 15 | 2.9: 7, 16 | 2.95: 8, 17 | 3.0: 9, 18 | 3.1: 10, 19 | 3.2: 11, 20 | 3.3: 12, 21 | 3.4: 13 22 | } 23 | # print(OptionUtil.get_strike_monenyes_rank_dict_nearest_strike(2.78,dict.keys(),OptionType.CALL)) 24 | # print(OptionUtil.get_strike_by_monenyes_rank_nearest_strike(2.78, 1, dict.keys(), OptionType.CALL)) 25 | # print(OptionUtil.get_strike_by_monenyes_rank_nearest_strike(2.78, 0, dict.keys(), OptionType.CALL)) 26 | # print(OptionUtil.get_strike_by_monenyes_rank_nearest_strike(2.78, -1, dict.keys(), OptionType.CALL)) 27 | # print(OptionUtil.get_strike_monenyes_rank_dict_nearest_strike(2.78,dict.keys(),OptionType.PUT)) 28 | # print(OptionUtil.get_strike_by_monenyes_rank_nearest_strike(2.78, 1, dict.keys(), OptionType.PUT)) 29 | # print(OptionUtil.get_strike_by_monenyes_rank_nearest_strike(2.78, 0, dict.keys(), OptionType.PUT)) 30 | # print(OptionUtil.get_strike_by_monenyes_rank_nearest_strike(2.78, -1, dict.keys(), OptionType.PUT)) 31 | print("ATM moneyness") 32 | print("2.78, call") 33 | print(OptionUtil.get_strike_monenyes_rank_dict_nearest_strike(2.78,dict.keys(),OptionType.CALL)) 34 | print("2.78, put") 35 | print(OptionUtil.get_strike_monenyes_rank_dict_nearest_strike(2.78,dict.keys(),OptionType.PUT)) 36 | print("2.76, call") 37 | print(OptionUtil.get_strike_monenyes_rank_dict_nearest_strike(2.76,dict.keys(),OptionType.CALL)) 38 | print("2.76, put") 39 | print(OptionUtil.get_strike_monenyes_rank_dict_nearest_strike(2.76,dict.keys(),OptionType.PUT)) 40 | print("OTM moneyness") 41 | print("2.78, call") 42 | print(OptionUtil.get_strike_monenyes_rank_dict_otm_strike(2.78,dict.keys(),OptionType.CALL)) 43 | print("2.78, put") 44 | print(OptionUtil.get_strike_monenyes_rank_dict_otm_strike(2.78,dict.keys(),OptionType.PUT)) 45 | print("2.76, call") 46 | print(OptionUtil.get_strike_monenyes_rank_dict_otm_strike(2.76,dict.keys(),OptionType.CALL)) 47 | print("2.76, put") 48 | print(OptionUtil.get_strike_monenyes_rank_dict_otm_strike(2.76,dict.keys(),OptionType.PUT)) 49 | -------------------------------------------------------------------------------- /back_test/model/abstract_base_product.py: -------------------------------------------------------------------------------- 1 | from pandas import Series 2 | from abc import ABC, abstractmethod 3 | from back_test.model.trade import Order 4 | from back_test.model.constant import LongShort,ExecuteType 5 | """ 6 | AbstractBaseProduct: an abstract class of base product. 7 | """ 8 | 9 | 10 | class AbstractBaseProduct(ABC): 11 | """ 12 | initialize the AbstractBaseProduct class 13 | """ 14 | 15 | def __init__(self) -> None: 16 | super().__init__() 17 | 18 | """ 19 | init: initialize product after object is constructed. 20 | """ 21 | @abstractmethod 22 | def init(self) -> None: 23 | pass 24 | 25 | """ 26 | pre_process: pre process data to filter out invalid data or doing other required preprocess job. 27 | """ 28 | @abstractmethod 29 | def pre_process(self) -> None: 30 | pass 31 | 32 | """ 33 | next: move forward to next tick of data 34 | """ 35 | @abstractmethod 36 | def next(self) -> None: 37 | pass 38 | 39 | """ 40 | has_next: return whether has next iter 41 | """ 42 | @abstractmethod 43 | def has_next(self) -> bool: 44 | pass 45 | 46 | """ 47 | update_current_state: update df_metrics with current_index. 48 | """ 49 | 50 | @abstractmethod 51 | def update_current_state(self) -> None: 52 | pass 53 | 54 | """ 55 | get_current_state: method to get current tick data based on index from df_metric as DataFrame(Series). 56 | """ 57 | 58 | @abstractmethod 59 | def get_current_state(self) -> Series: 60 | pass 61 | 62 | 63 | 64 | @abstractmethod 65 | def validate_data(self): 66 | pass 67 | 68 | 69 | @abstractmethod 70 | def execute_order(self, order: Order, slippage:int=0,execute_type:ExecuteType=ExecuteType.EXECUTE_ALL_UNITS) -> bool: 71 | # 执行交易指令 72 | pass 73 | 74 | 75 | @abstractmethod 76 | def get_current_value(self, long_short:LongShort) -> float: 77 | # 保证金交易当前价值为零/基础证券交易不包含保证金current value为当前价格 78 | pass 79 | 80 | @abstractmethod 81 | def is_margin_trade(self, long_short:LongShort) -> bool: 82 | # 标记是否为保证金交易 83 | pass 84 | 85 | @abstractmethod 86 | def is_mtm(self) -> bool: 87 | # 标记该证券是否逐日盯市 88 | pass 89 | -------------------------------------------------------------------------------- /back_test/model/base_future.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from typing import Union 3 | import datetime 4 | from back_test.model.constant import FrequentType, Util,LongShort 5 | from back_test.model.base_product import BaseProduct 6 | 7 | 8 | class BaseFuture(BaseProduct): 9 | """ 10 | BaseFuture: For Independent Future. 11 | """ 12 | 13 | def __init__(self, df_data: pd.DataFrame, df_daily_data: pd.DataFrame = None, 14 | rf: float = 0.03, frequency: FrequentType = FrequentType.DAILY): 15 | super().__init__(df_data, df_daily_data, rf, frequency) 16 | self._multiplier = Util.DICT_CONTRACT_MULTIPLIER[self.name_code()] 17 | 18 | def __repr__(self) -> str: 19 | return 'BaseInstrument(id_instrument: {0},eval_date: {1},frequency: {2})' \ 20 | .format(self.id_instrument(), self.eval_date, self.frequency) 21 | 22 | """ getters """ 23 | 24 | def contract_month(self) -> Union[str, None]: 25 | return self.current_state[Util.NAME_CONTRACT_MONTH] 26 | 27 | def get_initial_margin(self,long_short:LongShort) -> Union[float,None]: 28 | return self.get_maintain_margin(long_short) 29 | 30 | def get_maintain_margin(self,long_short:LongShort) -> Union[float,None]: 31 | margin_rate = Util.DICT_FUTURE_MARGIN_RATE[self.name_code()] 32 | pre_settle_price = self.mktprice_last_settlement() 33 | margin = pre_settle_price * margin_rate * self._multiplier 34 | return margin 35 | 36 | def maturitydt(self) -> Union[datetime.date,None]: 37 | return self.current_state[Util.DT_MATURITY] 38 | 39 | def multiplier(self) -> Union[int,None]: 40 | return self._multiplier 41 | 42 | """ 用于计算杠杆率 :保证金交易,current value为零 """ 43 | def get_current_value(self, long_short): 44 | return 0.0 45 | 46 | def is_margin_trade(self, long_short): 47 | return True 48 | 49 | def is_mtm(self): 50 | return True 51 | 52 | def is_core(self) -> Union[bool,None]: 53 | core_months = Util.DICT_FUTURE_CORE_CONTRACT[self.name_code()] 54 | if core_months == Util.STR_ALL: 55 | return True 56 | else: 57 | month = int(self.contract_month()[-2:]) 58 | if month in core_months: 59 | return True 60 | else: 61 | return False 62 | 63 | -------------------------------------------------------------------------------- /back_test/tests/option_account_test.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from back_test.model.base_account import BaseAccount 4 | from back_test.model.base_future_coutinuous import BaseFutureCoutinuous 5 | from back_test.model.constant import TradeType, Util, FrequentType 6 | from back_test.model.trade import Trade 7 | from data_access.get_data import get_dzqh_cf_minute, get_dzqh_cf_daily 8 | 9 | start_date = datetime.date(2017, 10, 1) 10 | end_date = datetime.date(2017, 11, 21) 11 | 12 | # df_option_metrics = get_50option_mktdata(start_date, end_date) 13 | # df_index_metrics = get_index_mktdata(start_date, end_date, 'index_50etf') 14 | # option_set = BaseOptionSet(df_option_metrics) 15 | # option_set.init() 16 | # index = BaseInstrument(df_index_metrics) 17 | df_cf_minute = get_dzqh_cf_minute(start_date, end_date, 'if') 18 | df_cf = get_dzqh_cf_daily(start_date, end_date, 'if') 19 | future = BaseFutureCoutinuous(df_cf_minute, df_cf, frequency=FrequentType.MINUTE) 20 | future.init() 21 | account = BaseAccount(Util.BILLION) 22 | trading_desk = Trade() 23 | # while future.has_next(): 24 | # for option in option_set.eligible_options: 25 | # TODO: Create and execute order could be implemented in base_product class. 26 | order = account.create_trade_order(future, 27 | TradeType.OPEN_LONG, 28 | 10) 29 | 30 | execution_res = future.execute_order(order) 31 | account.add_record(execution_res, future) 32 | trading_desk.add_pending_order(order) 33 | future.next() 34 | order = account.create_trade_order(future, 35 | TradeType.OPEN_SHORT, 36 | 5) 37 | execution_res = future.execute_order(order) 38 | account.add_record(execution_res, future) 39 | trading_desk.add_pending_order(order) 40 | future.next() 41 | order = account.create_trade_order(future, 42 | TradeType.OPEN_SHORT, 43 | 10) 44 | execution_res = future.execute_order(order) 45 | account.add_record(execution_res, future) 46 | trading_desk.add_pending_order(order) 47 | future.next() 48 | 49 | account.daily_accounting(future.eval_date) 50 | 51 | order = account.create_trade_order(future, 52 | TradeType.CLOSE_SHORT, 53 | ) 54 | execution_res = future.execute_order(order) 55 | account.add_record(execution_res, future) 56 | trading_desk.add_pending_order(order) 57 | -------------------------------------------------------------------------------- /PricingLibrary/Util.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import math 3 | 4 | from back_test.deprecated.BktUtil import BktUtil 5 | 6 | 7 | class PricingUtil: 8 | 9 | @staticmethod 10 | def get_ttm(dt_eval, dt_maturity): 11 | N = (dt_maturity - dt_eval).total_seconds() / 60.0 12 | N365 = 365 * 1440.0 13 | ttm = N / N365 14 | return ttm 15 | 16 | @staticmethod 17 | def get_std(dt_eval, dt_maturity, annualized_vol): 18 | stdDev = annualized_vol * math.sqrt(PricingUtil.get_ttm(dt_eval, dt_maturity)) 19 | return stdDev 20 | 21 | @staticmethod 22 | def get_discount(dt_eval, dt_maturity, rf): 23 | discount = math.exp(-rf * PricingUtil.get_ttm(dt_eval, dt_maturity)) 24 | return discount 25 | 26 | @staticmethod 27 | def get_maturity_metrics(self, dt_date, spot, option): 28 | strike = option.strike 29 | if option.option_type == BktUtil().type_put: 30 | if strike > spot: # ITM 31 | delta = -1.0 32 | elif strike < spot: # OTM 33 | delta = 0.0 34 | else: 35 | delta = 0.5 36 | option_price = max(strike - spot, 0) 37 | else: 38 | if strike < spot: # ITM 39 | delta = 1.0 40 | elif strike > spot: # OTM 41 | delta = 0.0 42 | else: 43 | delta = 0.5 44 | option_price = max(spot - strike, 0) 45 | delta = delta 46 | option_price = option_price 47 | return delta, option_price 48 | 49 | 50 | class Calendar(object): 51 | def leepDates(self, dt1, dt2): 52 | # swap dt1 and dt2 if dt1 is earlier than dt2 53 | if (dt1 - dt2).days < 0: 54 | tmp = dt2 55 | dt2 = dt1 56 | dt1 = tmp 57 | # dt1 > dt2 58 | year1 = dt1.year 59 | year2 = dt2.year 60 | daysWithoutLeap = (year1 + 1 - year2) * 365 61 | daysWithLeap = (datetime.date(year1 + 1, 1, 1) - datetime.date(year2, 1, 1)).days 62 | leapDays = daysWithLeap - daysWithoutLeap 63 | if self.isLeapYear(dt1.year) and (dt1 - datetime.date(year1, 2, 29)).days < 0: 64 | print((dt1 - datetime.date(year1, 2, 29)).days) 65 | leapDays -= 1 66 | if self.isLeapYear(dt2.year) and (dt2 - datetime.date(year2, 2, 29)).days > 0: 67 | leapDays -= 1 68 | return (dt1 - dt2).days - leapDays 69 | 70 | def isLeapYear(self, year): 71 | return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) 72 | 73 | -------------------------------------------------------------------------------- /OptionStrategyLib/example_delta.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import pandas as pd 6 | from OptionStrategyLib.OptionPricing.BlackCalculator import EuropeanOption 7 | from OptionStrategyLib.Util import PricingUtil 8 | 9 | from Utilities.PlotUtil import PlotUtil 10 | from back_test.deprecated.BktUtil import BktUtil 11 | 12 | utl = BktUtil() 13 | pricing_utl = PricingUtil() 14 | strike = 3000 15 | vol = 0.2 16 | vol2 = 0.3 17 | rf = 0.03 18 | spotlist = np.arange(strike - 700, strike + 700, 10) 19 | dt_date = datetime.datetime(2018, 3, 30) 20 | dt_maturity = datetime.datetime(2018, 6, 30) 21 | dt_maturity2 = datetime.datetime(2018, 4, 30) 22 | Option = EuropeanOption(strike, dt_maturity, utl.type_put) 23 | Option2 = EuropeanOption(strike, dt_maturity2, utl.type_put) 24 | OptionCall = EuropeanOption(strike, dt_maturity, utl.type_call) 25 | deltalist_1 = [] 26 | deltalist_2 = [] 27 | gammalist1 = [] 28 | gammalist2 = [] 29 | for spot in spotlist: 30 | delta = pricing_utl.get_blackcalculator(dt_date, spot, Option, rf, vol).Delta() 31 | # gamma = pricing_utl.get_blackcalculator(dt_date, spot, Option, rf, vol).Gamma() 32 | # gammalist1.append(gamma) 33 | deltalist_1.append(delta) 34 | delta2 = pricing_utl.get_blackcalculator(dt_date, spot, Option, 0.1, vol).Delta() 35 | # gamma2 = pricing_utl.get_blackcalculator(dt_date, spot, Option2, rf, vol).Gamma() 36 | deltalist_2.append(delta2) 37 | # gammalist2.append(gamma2) 38 | 39 | plot_utl = PlotUtil() 40 | plot_utl.plot_line_chart(spotlist, [deltalist_2,deltalist_1], ['Delta (rf=0.03)','Delta (rf=0.1)']) 41 | # plot_utl.plot_line_chart(spotlist, [gammalist2,gammalist1], ['Gamma(T=1M)','Gamma(T=3M)']) 42 | plt.show() 43 | df = pd.DataFrame() 44 | df['spot'] = spotlist 45 | df['delta'] = deltalist_1 46 | df.to_excel('../delta.xlsx') 47 | # for spot in spotlist: 48 | # black1 = pricing_utl.get_blackcalculator(dt_date, spot, Option, rf, vol) 49 | # delta1 = black1.Delta() 50 | # gamma1 = black1.Gamma() 51 | # black2 = pricing_utl.get_blackcalculator(dt_date, spot, Option, rf, vol2) 52 | # delta2 = black2.Delta() 53 | # gamma2 = black2.Gamma() 54 | # gammalist1.append(gamma1) 55 | # deltalist_1.append(delta1) 56 | # deltalist_2.append(delta2) 57 | # gammalist2.append(gamma2) 58 | # 59 | # 60 | # plot_utl = PlotUtil() 61 | # plot_utl.plot_line_chart(spotlist, [deltalist_1,deltalist_2], ['Delta vol=0.2','Delta vol=0.3']) 62 | # plot_utl.plot_line_chart(spotlist, [gammalist1,gammalist2], ['Gamma vol=0.2','Gamma vol=0.3']) 63 | # plt.show() 64 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/pcp_arbitrage.py: -------------------------------------------------------------------------------- 1 | from data_access import get_data 2 | from back_test.model.base_option_set import BaseOptionSet 3 | from back_test.model.constant import Util, OptionUtil 4 | from Utilities.PlotUtil import PlotUtil 5 | import matplotlib.pyplot as plt 6 | import datetime 7 | import math 8 | import pandas as pd 9 | 10 | 11 | def get_atm_options(optionset, maturity): 12 | list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1(moneyness_rank=0, maturity=maturity) 13 | atm_call = optionset.select_higher_volume(list_atm_call) 14 | atm_put = optionset.select_higher_volume(list_atm_put) 15 | return atm_call, atm_put 16 | 17 | def get_atm_iv_average(optionset, maturity): 18 | atm_call, atm_put = get_atm_options(optionset,maturity) 19 | iv_call = atm_call.get_implied_vol() 20 | iv_put = atm_put.get_implied_vol() 21 | iv_avg = (iv_call + iv_put) / 2 22 | return iv_avg 23 | 24 | pu = PlotUtil() 25 | start_date = datetime.date(2018,9,12) 26 | end_date = datetime.date(2018,9,13) 27 | nbr_maturity = 0 28 | min_holding = 8 29 | df_daily = get_data.get_comoption_mktdata(start_date, end_date,Util.STR_SR) 30 | optionset = BaseOptionSet(df_data=df_daily,rf=0.015) 31 | optionset.init() 32 | # optionset.go_to(end_date) 33 | 34 | maturity = optionset.select_maturity_date(nbr_maturity, min_holding=min_holding) 35 | iv_htr = optionset.get_atm_iv_by_htbr(maturity) 36 | iv_avg = get_atm_iv_average(optionset, maturity) 37 | 38 | htbr = optionset.get_htb_rate(maturity) 39 | print('iv_htr : ', iv_htr) 40 | print('iv_avg : ', iv_avg) 41 | print('htb rate : ', htbr) 42 | 43 | curve = optionset.get_implied_vol_curves(maturity) 44 | 45 | curve_htbr = optionset.get_implied_vol_curves_htbr(maturity) 46 | curve_otm = optionset.get_otm_implied_vol_curve(maturity) 47 | curve_htbr[Util.PCT_IV_CALL_BY_HTBR] = curve_htbr[Util.PCT_IV_CALL_BY_HTBR].apply(lambda x: None if x<0.05 else x) 48 | curve_htbr[Util.PCT_IV_PUT_BY_HTBR] = curve_htbr[Util.PCT_IV_PUT_BY_HTBR].apply(lambda x: None if x<0.05 else x) 49 | curve_otm[Util.PCT_IV_OTM_BY_HTBR] = curve_otm[Util.PCT_IV_OTM_BY_HTBR].apply(lambda x: None if x<0.05 else x) 50 | 51 | strikes = curve[Util.AMT_APPLICABLE_STRIKE] 52 | pu.plot_line_chart(strikes,[list(curve[Util.PCT_IV_CALL]),list(curve[Util.PCT_IV_PUT])],['CALL IV','PUT iv']) 53 | pu.plot_line_chart(strikes,[list(curve_htbr[Util.PCT_IV_CALL_BY_HTBR]),list(curve_htbr[Util.PCT_IV_PUT_BY_HTBR])],['CALL IV adjusted','PUT IV adjusted']) 54 | pu.plot_line_chart(strikes,[list(curve_otm[Util.PCT_IV_OTM_BY_HTBR])],['IV-'+str(optionset.eval_date)]) 55 | 56 | 57 | plt.show() 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/yield_distribution.py: -------------------------------------------------------------------------------- 1 | from data_access import get_data 2 | import back_test.model.constant as c 3 | import matplotlib 4 | from matplotlib import pylab as plt 5 | import datetime 6 | import numpy as np 7 | from OptionStrategyLib.VolatilityModel.kernel_density import kde_sklearn 8 | from Utilities.PlotUtil import PlotUtil 9 | from scipy import stats 10 | 11 | pu = PlotUtil() 12 | start_date = datetime.date(2010, 1, 1) 13 | end_date = datetime.date.today() 14 | dt_histvol = start_date 15 | 16 | """ 50ETF option """ 17 | name_code = c.Util.STR_CU 18 | name_code_index = c.Util.STR_INDEX_50SH 19 | # df_future_c1_daily = get_data.get_dzqh_cf_c1_daily(dt_histvol, end_date, name_code) 20 | df_future_c1_daily = get_data.get_mktdata_future_c1_daily(dt_histvol, end_date, name_code) 21 | df_index = get_data.get_index_mktdata(start_date, end_date, name_code_index) 22 | 23 | df_future_c1_daily[c.Util.AMT_YIELD] = np.log(df_future_c1_daily[c.Util.AMT_CLOSE]).diff(periods=20) 24 | df_index[c.Util.AMT_YIELD] = np.log(df_index[c.Util.AMT_CLOSE]).diff(periods=20) 25 | df_future_c1_daily = df_future_c1_daily.dropna() 26 | df_index = df_index.dropna() 27 | 28 | r_future = np.array(df_future_c1_daily[c.Util.AMT_YIELD]) 29 | r_index = np.array(df_index[c.Util.AMT_YIELD]) 30 | s = np.random.normal(0, 0.15, 1000) 31 | 32 | m1 = np.mean(r_future) 33 | sk1 = stats.skew(r_future) 34 | std1 = np.std(r_future) 35 | m2 = np.mean(r_index) 36 | sk2 = stats.skew(r_index) 37 | std2 = np.std(r_index) 38 | print(name_code,m1,std1,sk1) 39 | print(name_code_index,m2,std2,sk2) 40 | plt.figure(1) 41 | x_f = np.linspace(min(r_future), max(r_future), 1000) 42 | mu, sigma = stats.norm.fit(r_index) 43 | pdf_norm = stats.norm.pdf(x_f, mu, sigma) 44 | pdf_f = kde_sklearn(r_future, x_f, bandwidth=0.02) 45 | plt.plot(x_f, pdf_norm, 'r--', linewidth=2, label='正态分布') 46 | plt.plot(x_f, pdf_f, 'black', label='kernel density') 47 | plt.hist(r_future, bins=100, normed=True, facecolor="#8C8C8C", label='沪铜期货回报率分布(月)') 48 | plt.legend() 49 | 50 | plt.figure(2) 51 | x_indx = np.linspace(min(r_index), max(r_index), 1000) 52 | 53 | mu, sigma = stats.norm.fit(r_index) 54 | pdf_norm = stats.norm.pdf(x_indx, mu, sigma) 55 | pdf_indx = kde_sklearn(r_index, x_indx, bandwidth=0.02) 56 | plt.plot(x_indx, pdf_norm, 'r--', linewidth=2, label='正态分布') 57 | plt.plot(x_indx, pdf_indx, 'black', label='kernel density') 58 | plt.hist(r_index, bins=100, density=True, facecolor="#8C8C8C", label='上证50指数回报率分布(月)') 59 | # plt.hist(r_index, bins=100, density=True,facecolor="#8C8C8C", label='沪深300指数回报率分布(月)') 60 | plt.legend() 61 | 62 | # plt.figure(3) 63 | # pu.plot_line_chart(x_indx,[pdf_f,pdf_indx],['IH月度回报率 kernel density','50ETF月度回报率 kernel density']) 64 | plt.show() 65 | -------------------------------------------------------------------------------- /back_test/tests/sample_strategy.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from back_test.deprecated.BktOptionStrategy import BktOptionStrategy 4 | 5 | from data_access.get_data import get_50option_mktdata, get_index_mktdata 6 | 7 | 8 | class SampleStrategy(BktOptionStrategy): 9 | 10 | def __init__(self, df_option, money_utilization=0.2, init_fund=100000.0, 11 | cash_reserve_pct=0.20): 12 | BktOptionStrategy.__init__(self, df_option, money_utilization=money_utilization, 13 | init_fund=init_fund) 14 | self.cash_reserve_pct = cash_reserve_pct 15 | 16 | def run(self): 17 | bkt_optionset = self.bkt_optionset 18 | bkt_account = self.bkt_account 19 | inv_fund = (1 - self.cash_reserve_pct) * bkt_account.cash 20 | evalDate = bkt_optionset.eval_date 21 | 22 | """Option: Select Strategy and Open Position""" 23 | # 投资什么条件的期权,这里是第一个到期日,平值 24 | maturity_date = self.get_1st_eligible_maturity(evalDate) 25 | moneyness = 0 26 | 27 | # 根基上述条件选出期权投资组合 28 | portfolio = self.bkt_optionset.get_call(moneyness,maturity_date,self.util.long) 29 | # 根据现在有多少钱,分配投资组合中的每个期权的持仓unit 30 | bkt_account.portfolio_rebalancing_eqlfund(portfolio, 0.0, cd_open_by_price='close', fund=inv_fund) 31 | # 开仓 32 | bkt_account.open_portfolio(evalDate, portfolio) 33 | 34 | self.flag_trade = True 35 | bkt_account.mkm_update_portfolio(evalDate, portfolio) 36 | print(evalDate, bkt_optionset.eval_date, ' , ', bkt_account.npv) 37 | 38 | while bkt_optionset.index < len(bkt_optionset.dt_list): 39 | bkt_optionset.next() 40 | evalDate = bkt_optionset.eval_date 41 | 42 | """ STRATEGY : HOLD TO MATURITY """ 43 | if evalDate == maturity_date: 44 | bkt_account.close_portfolio(evalDate,portfolio,cd_close_by_price='close') 45 | bkt_account.mkm_update_portfolio(evalDate, portfolio) 46 | print(evalDate, ' , ', bkt_account.npv) # npv是组合净值,期初为1 47 | break 48 | """按当日价格调整保证金,计算投资组合盯市价值""" 49 | bkt_account.mkm_update_portfolio(evalDate, portfolio) 50 | print(evalDate, ' , ', bkt_account.npv) 51 | 52 | 53 | """Back Test Settings""" 54 | # start_date = datetime.date(2015, 3, 1) 55 | start_date = datetime.date(2017, 10, 1) 56 | end_date = datetime.date(2018, 1, 21) 57 | 58 | """Collect Mkt Date""" 59 | 60 | df_option_metrics = get_50option_mktdata(start_date, end_date) 61 | df_index_metrics = get_index_mktdata(start_date, end_date, 'index_50etf') 62 | """Run Backtest""" 63 | 64 | bkt_strategy = SampleStrategy(df_option_metrics, df_index_metrics) 65 | bkt_strategy.set_min_holding_days(8) 66 | 67 | bkt_strategy.run() 68 | -------------------------------------------------------------------------------- /PricingLibrary/Options.py: -------------------------------------------------------------------------------- 1 | from back_test.model.constant import OptionType 2 | import datetime 3 | 4 | class Option(object): 5 | 6 | def __init__(self, strike:float, dt_maturity:datetime.date, optionType:OptionType): 7 | self.strike = strike 8 | self.dt_maturity = dt_maturity 9 | self.option_type = optionType 10 | 11 | 12 | class EuropeanOption(Option): 13 | def __init__(self, strike:float, dt_maturity:datetime.date, optionType:OptionType, 14 | dt_issue:datetime.date=None, init_price:float=None): 15 | super().__init__(strike, dt_maturity, optionType) 16 | self.init_price = init_price 17 | self.dt_issue = dt_issue 18 | 19 | 20 | import QuantLib as ql 21 | 22 | 23 | class OptionPlainEuropean(object): 24 | def __init__(self, strike, maturitydt, optionType, init_price=None): 25 | self.strike = strike 26 | self.maturitydt = maturitydt 27 | self.optionType = optionType 28 | exercise = ql.EuropeanExercise(maturitydt) 29 | payoff = ql.PlainVanillaPayoff(optionType, strike) 30 | option = ql.EuropeanOption(payoff, exercise) 31 | self.exercise = exercise 32 | self.payoff = payoff 33 | self.option_ql = option 34 | self.init_price = init_price 35 | # 36 | # 37 | # class OptionPlainAmerican: 38 | # def __init__(self, strike, effectivedt, maturitydt, optionType): 39 | # self.strike = strike 40 | # self.maturitydt = maturitydt 41 | # self.optionType = optionType 42 | # exercise = ql.AmericanExercise(effectivedt, maturitydt) 43 | # payoff = ql.PlainVanillaPayoff(optionType, strike) 44 | # option = ql.VanillaOption(payoff, exercise) 45 | # self.option_ql = option 46 | # 47 | # 48 | # class OptionBarrierEuropean: 49 | # def __init__(self, strike, maturitydt, optionType, barrier, barrierType): 50 | # self.strike = strike 51 | # self.maturitydt = maturitydt 52 | # self.optionType = optionType 53 | # self.barrier = barrier 54 | # self.barrierType = barrierType 55 | # exercise = ql.EuropeanExercise(maturitydt) 56 | # self.exercise = exercise 57 | # payoff = ql.PlainVanillaPayoff(optionType, strike) 58 | # self.payoff = payoff 59 | # barrieroption = ql.BarrierOption(barrierType, barrier, 0.0, payoff, exercise) 60 | # self.option_ql = barrieroption 61 | 62 | 63 | # class OptionPlainAsian: 64 | # def __init__(self, strike,effectivedt, maturitydt, optionType): 65 | # self.strike = strike 66 | # self.maturitydt = maturitydt 67 | # self.optionType = optionType 68 | # exercise = ql.EuropeanExercise(maturitydt) 69 | # payoff = ql.PlainVanillaPayoff(optionType, strike) 70 | # option = ql.DiscreteAveragingAsianOption(payoff, exercise,) 71 | # self.option_ql = option 72 | -------------------------------------------------------------------------------- /data_access/metrics_moving_average.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import datetime 3 | from sqlalchemy import * 4 | from sqlalchemy.orm import sessionmaker 5 | import numpy as np 6 | from Utilities import admin_util as admin 7 | 8 | def average(df): 9 | if df.empty: return -999.0 10 | sum = (df['amt_close'] * df['amt_trading_volume']).sum() 11 | vol = df['amt_trading_volume'].sum() 12 | if vol != 0: 13 | return sum / vol 14 | return -999.0 15 | 16 | def moving_average(df,n): 17 | ma =df['amt_close'].rolling(window = n).mean() 18 | return ma 19 | 20 | def standard_deviation(df,n): 21 | std =df['amt_close'].rolling(window = n).std() 22 | return std 23 | 24 | def percentile(df,n,percent): 25 | return df['amt_close'].rolling(window=n).quantile(percent) 26 | 27 | # beg_date = datetime.date(2014, 6, 1) 28 | date = datetime.date(2015, 1, 1) 29 | 30 | index_mktdata = admin.table_indexes_mktdata() 31 | ma_metrics = admin.table_moving_average() 32 | 33 | 34 | # query_mkt = admin.session_mktdata().query(index_mktdata.c.dt_date,index_mktdata.c.id_instrument, 35 | # index_mktdata.c.amt_close) \ 36 | # .filter(index_mktdata.c.datasource == 'wind')\ 37 | # .filter(index_mktdata.c.id_instrument == 'index_50etf')\ 38 | # .filter(index_mktdata.c.dt_date >= date) 39 | # df_dataset = pd.read_sql_query(query_mkt.statement,query_mkt.session.bind) 40 | 41 | query_mkt = admin.session_metrics().query(ma_metrics.c.dt_date,ma_metrics.c.id_instrument, 42 | ma_metrics.c.amt_close) \ 43 | .filter(ma_metrics.c.cd_period == 'ma_3')\ 44 | .filter(ma_metrics.c.id_instrument == 'index_cvix') 45 | # .filter(ma_metrics.c.dt_date >= date) 46 | df_dataset = pd.read_sql_query(query_mkt.statement,query_mkt.session.bind) 47 | 48 | print('s') 49 | # xl = pd.ExcelFile('../data/VIX_daily.xlsx') 50 | # df_dataset = xl.parse("Sheet1", header=None) 51 | # df_dataset['dt_date']= df_dataset[0] 52 | # df_dataset['amt_close']= df_dataset[1] 53 | # df_dataset = df_dataset[['dt_date','amt_close']] 54 | # df_dataset = df_dataset[df_dataset['dt_date']>datetime.date(2017,6,1)] 55 | # df_dataset['id_instrument'] = 'index_cvix' 56 | 57 | 58 | for p in [0.25,0.5,0.75]: 59 | # ma = moving_average(df_dataset,n) 60 | # std = standard_deviation(df_dataset,n) 61 | pct = percentile(df_dataset,126,p) 62 | df = df_dataset.copy() 63 | df['cd_period']='percentileHY_'+str(int(100*p)) 64 | df['cd_calculation'] = 'percentile_HY' 65 | df['amt_value'] = pct 66 | df = df.dropna() 67 | df = df[df['dt_date']>date] 68 | # df['dt_date'] = df['dt_date'].dt.strftime('%Y-%m-%d') 69 | print(df) 70 | df.to_sql(name='moving_average', con=engine_metrics, if_exists = 'append', index=False) 71 | 72 | 73 | 74 | 75 | # for (idx,row) in df_dataset.iterrows(): 76 | # if idx <= 20 : 77 | # df_dataset.ix[idx,'amt_std'] = np.nan 78 | # else: 79 | # data = df_dataset.loc[0:idx,'amt_close'] 80 | -------------------------------------------------------------------------------- /data_access/craw_data_check.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | import datetime 4 | from WindPy import w 5 | from data_access import spider_api_dce as dce 6 | from data_access import spider_api_sfe as sfe 7 | from data_access import spider_api_czce as czce 8 | from data_access.db_data_collection import DataCollection 9 | from Utilities import admin_write_util as admin 10 | import pandas as pd 11 | import numpy as np 12 | 13 | 14 | w.start() 15 | 16 | conn = admin.conn_mktdata() 17 | conn_intraday = admin.conn_intraday() 18 | 19 | futures_mktdata_daily = admin.table_futures_mktdata() 20 | 21 | 22 | def wind_future_daily(dt,contracts): 23 | datestr = dt.strftime("%Y-%m-%d") 24 | try: 25 | res = w.wss(contracts,"pre_close,open,high,low,close,volume,amt,oi,pre_settle,settle,windcode","tradeDate="+datestr+";priceAdj=U;cycle=D") 26 | d = res.Data 27 | f = res.Fields 28 | df = pd.DataFrame(data=np.transpose(d), columns=f,) 29 | df1 = df.dropna(subset=['CLOSE']) 30 | df1['id_instrument'] = df1['WINDCODE'].apply(lambda x:(x[-len(x):-8]+'_'+x[-8:-4]).lower()) 31 | df1['name_code'] = df1['WINDCODE'].apply(lambda x:x[-len(x):-8].lower()) 32 | df1['cd_exchange'] = df1['WINDCODE'].apply(lambda x:x[-3:].lower()) 33 | df1.loc[:,'datasource'] = 'wind' 34 | df1.loc[:,'timestamp'] = datetime.datetime.today() 35 | df1.loc[:,'dt_date'] = dt 36 | df1=df1.rename(columns={'PRE_CLOSE':'amt_last_close', 37 | 'OPEN':'amt_open', 38 | 'HIGH':'amt_high', 39 | 'LOW':'amt_low', 40 | 'CLOSE':'amt_close', 41 | 'VOLUME':'amt_trading_volume', 42 | 'AMT':'amt_trading_value', 43 | 'OI':'amt_holding_volume', 44 | 'PRE_SETTLE':'amt_last_settlement', 45 | 'SETTLE':'amt_settlement', 46 | 'WINDCODE':'code_instrument' 47 | }) 48 | return df1 49 | except Exception as e: 50 | print(e) 51 | return pd.DataFrame() 52 | 53 | 54 | 55 | today = datetime.date.today() 56 | beg_date = datetime.date(2010, 1, 1) 57 | # end_date = datetime.date(2010, 1, 10) 58 | end_date = datetime.date.today() 59 | 60 | data_contracts = w.wset("futurecc","startdate=2010-01-01;enddate="+today.strftime("%Y-%m-%d")+";wind_code=CU.SHF;field=wind_code,contract_issue_date,last_trade_date,last_delivery_mouth") 61 | df_contracts = pd.DataFrame(data=np.transpose(data_contracts.Data), columns=data_contracts.Fields) 62 | date_range = w.tdays(beg_date, end_date, "").Data[0] 63 | date_range = sorted(date_range,reverse=True) 64 | for dt in date_range: 65 | c_str = "" 66 | contracts = df_contracts[(df_contracts['contract_issue_date'] <=dt)&(df_contracts['last_delivery_mouth'] >=dt)]['wind_code'].values 67 | for c in contracts: 68 | c_str += c +"," 69 | c_str = c_str[0:len(c_str)-2] 70 | # c_str += '\"' 71 | df1 = wind_future_daily(dt,c_str) 72 | try: 73 | df1.to_sql('futures_mktdata', con=admin.engine_gc, if_exists='append', index=False) 74 | except Exception as e: 75 | print(e) 76 | pass 77 | print(dt,' finished.') -------------------------------------------------------------------------------- /back_test/model/base_instrument.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from back_test.model.base_product import BaseProduct 4 | from back_test.model.constant import FrequentType, Util, ExecuteType, LongShort 5 | from back_test.model.trade import Order 6 | 7 | 8 | class BaseInstrument(BaseProduct): 9 | """ 10 | BaseInstrument: STOCK/ETF/INDEX 11 | """ 12 | 13 | def __init__(self, df_data: pd.DataFrame, df_daily_data: pd.DataFrame = None, 14 | rf: float = 0.03, frequency: FrequentType = FrequentType.DAILY): 15 | super().__init__(df_data, df_daily_data, rf, frequency) 16 | self._multiplier = 1.0 17 | self.fee_rate = 0.0 18 | self.fee_per_unit = 0.0 19 | 20 | def __repr__(self) -> str: 21 | return 'BaseInstrument(id_instrument: {0},eval_date: {1},frequency: {2})' \ 22 | .format(self.id_instrument(), self.eval_date, self.frequency) 23 | 24 | """ Long position only in base instrument. """ 25 | 26 | def execute_order(self, order: Order, slippage=0, execute_type: ExecuteType = ExecuteType.EXECUTE_ALL_UNITS): 27 | if order is None: return 28 | if execute_type == ExecuteType.EXECUTE_ALL_UNITS: 29 | order.trade_all_unit(slippage) 30 | elif execute_type == ExecuteType.EXECUTE_WITH_MAX_VOLUME: 31 | order.trade_with_current_volume(int(self.trading_volume()), slippage) 32 | else: 33 | return 34 | execution_record: pd.Series = order.execution_res 35 | # calculate margin requirement 36 | margin_requirement = 0.0 37 | if self.fee_per_unit is None: 38 | # 百分比手续费 39 | transaction_fee = execution_record[Util.TRADE_PRICE] * self.fee_rate * execution_record[ 40 | Util.TRADE_UNIT] * self._multiplier 41 | else: 42 | # 每手手续费 43 | transaction_fee = self.fee_per_unit * execution_record[Util.TRADE_UNIT] 44 | execution_record[Util.TRANSACTION_COST] += transaction_fee 45 | transaction_fee_add_to_price = transaction_fee / (execution_record[Util.TRADE_UNIT] * self._multiplier) 46 | execution_record[Util.TRADE_PRICE] += execution_record[ 47 | Util.TRADE_LONG_SHORT].value * transaction_fee_add_to_price 48 | position_size = order.long_short.value * execution_record[Util.TRADE_PRICE] * execution_record[ 49 | Util.TRADE_UNIT] * self._multiplier 50 | execution_record[ 51 | Util.TRADE_BOOK_VALUE] = position_size # 头寸规模(含多空符号),例如,空一手豆粕(3000点,乘数10)得到头寸规模为-30000,而建仓时点头寸市值为0。 52 | execution_record[Util.TRADE_MARGIN_CAPITAL] = margin_requirement 53 | # execution_record[ 54 | # Util.TRADE_MARKET_VALUE] = position_size # Init value of a future trade is ZERO, except for transaction cost. 55 | return execution_record 56 | 57 | """ 用于计算杠杆率 :基础证券交易不包含保证金current value为当前价格 """ 58 | 59 | def get_current_value(self, long_short): 60 | if long_short == LongShort.LONG: 61 | return self.mktprice_close() 62 | else: 63 | return 64 | 65 | def is_margin_trade(self, long_short): 66 | if long_short == LongShort.LONG: 67 | return False 68 | else: 69 | return 70 | 71 | def is_mtm(self): 72 | return False 73 | 74 | def multiplier(self): 75 | return self._multiplier 76 | -------------------------------------------------------------------------------- /data_access/crew_data_stocks_run.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from sqlalchemy import create_engine, MetaData, Table, Column, TIMESTAMP 4 | import datetime 5 | from WindPy import w 6 | from data_access.db_data_collection import DataCollection 7 | 8 | w.start() 9 | 10 | # date = datetime.date(2018, 4, 4) 11 | # dt_date = date.strftime("%Y-%m-%d") 12 | # print(dt_date) 13 | 14 | engine = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/mktdata', echo=False) 15 | conn = engine.connect() 16 | metadata = MetaData(engine) 17 | stocks = Table('stocks', metadata, autoload=True) 18 | stocks_mktdata = Table('stocks_mktdata', metadata, autoload=True) 19 | 20 | dc = DataCollection() 21 | 22 | 23 | ##################### CONTRACT INFO ######################################### 24 | # option_contracts 25 | 26 | # db_datas = dc.table_stocks().wind_A_shares_total(dt_date) 27 | # for db_data in db_datas: 28 | # try: 29 | # conn.execute(stocks.insert(), db_data) 30 | # except Exception as e: 31 | # print(e) 32 | # continue 33 | 34 | # for dt in date_range: 35 | # print(dt) 36 | 37 | ##################### GET STOCK MKT DATA ######################################### 38 | # date = '2018-04-20' 39 | # 40 | # setcode = w.wset("SectorConstituent", u"date=" + date + ";sector=全部A股") 41 | # 42 | # code = setcode.Data[1] 43 | # 44 | # db_datas = dc.table_stocks().wind_stocks_daily_wss(date,code) 45 | # try: 46 | # conn.execute(stocks_mktdata.insert(), db_datas) 47 | # except Exception as e: 48 | # print(e) 49 | 50 | 51 | ##################### BATCH GET STOCK MKT DATA ######################################### 52 | beg_date = datetime.date(2018, 4, 20) 53 | end_date = datetime.date(2018, 5, 9) 54 | 55 | date_range = w.tdays(beg_date, end_date, "").Data[0] 56 | for dt in date_range: 57 | date = dt.strftime("%Y-%m-%d") 58 | setcode = w.wset("SectorConstituent", u"date=" + date + ";sector=全部A股") 59 | 60 | code = setcode.Data[1] 61 | 62 | db_datas = dc.table_stocks().wind_stocks_daily_wss(date,code) 63 | try: 64 | conn.execute(stocks_mktdata.insert(), db_datas) 65 | except Exception as e: 66 | print(e) 67 | 68 | 69 | 70 | ##################### RECHECK DATA ######################################### 71 | 72 | # df_codes = pd.read_excel('../data/recheck.xls',converters={'code_errors': lambda x: str(x)}) 73 | # code_errors = df_codes['code_errors'].unique() 74 | # 75 | # df = dc.table_stocks().get_A_shares_total() 76 | # 77 | # df['code'] = df['windcode'].apply(lambda x:x.split('.')[0]) 78 | # 79 | # for (i, row) in df.iterrows(): 80 | # if i<4:continue 81 | # code = row['code'] 82 | # if code in code_errors: 83 | # windcode = row['windcode'] 84 | # print(windcode) 85 | # db_datas = dc.table_stocks().wind_stocks_daily(beg_date, end_date, windcode) 86 | # for db_data in db_datas: 87 | # # stocks_mktdata.delete((stocks_mktdata.c.id_instrument == db_data['id_instrument']) 88 | # # & (stocks_mktdata.c.dt_date == db_data['dt_date']) 89 | # # & (stocks_mktdata.c.datasource == db_data['datasource']) 90 | # # ).execute() 91 | # try: 92 | # conn.execute(stocks_mktdata.insert(), db_data) 93 | # except Exception as e: 94 | # print(e) 95 | # continue 96 | -------------------------------------------------------------------------------- /data_access/craw_data_hist2.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | import datetime 4 | from WindPy import w 5 | from data_access import spider_api_dce as dce 6 | from data_access import spider_api_sfe as sfe 7 | from data_access import spider_api_czce as czce 8 | from data_access.db_data_collection import DataCollection 9 | from Utilities import admin_write_util as admin 10 | import pandas as pd 11 | import numpy as np 12 | 13 | """ 14 | CU : 沪铜期货 15 | wind data 16 | """ 17 | 18 | w.start() 19 | 20 | conn = admin.conn_mktdata() 21 | conn_intraday = admin.conn_intraday() 22 | 23 | futures_mktdata_daily = admin.table_futures_mktdata() 24 | 25 | 26 | def wind_future_daily(dt,contracts): 27 | datestr = dt.strftime("%Y-%m-%d") 28 | try: 29 | res = w.wss(contracts,"pre_close,open,high,low,close,volume,amt,oi,pre_settle,settle,windcode","tradeDate="+datestr+";priceAdj=U;cycle=D") 30 | d = res.Data 31 | f = res.Fields 32 | df = pd.DataFrame(data=np.transpose(d), columns=f,) 33 | df1 = df.dropna(subset=['CLOSE']) 34 | df1['id_instrument'] = df1['WINDCODE'].apply(lambda x:(x[-len(x):-8]+'_'+x[-8:-4]).lower()) 35 | df1['name_code'] = df1['WINDCODE'].apply(lambda x:x[-len(x):-8].lower()) 36 | df1['cd_exchange'] = df1['WINDCODE'].apply(lambda x:x[-3:].lower()) 37 | df1.loc[:,'datasource'] = 'wind' 38 | df1.loc[:,'timestamp'] = datetime.datetime.today() 39 | df1.loc[:,'dt_date'] = dt 40 | df1=df1.rename(columns={'PRE_CLOSE':'amt_last_close', 41 | 'OPEN':'amt_open', 42 | 'HIGH':'amt_high', 43 | 'LOW':'amt_low', 44 | 'CLOSE':'amt_close', 45 | 'VOLUME':'amt_trading_volume', 46 | 'AMT':'amt_trading_value', 47 | 'OI':'amt_holding_volume', 48 | 'PRE_SETTLE':'amt_last_settlement', 49 | 'SETTLE':'amt_settlement', 50 | 'WINDCODE':'code_instrument' 51 | }) 52 | return df1 53 | except Exception as e: 54 | print(e) 55 | return pd.DataFrame() 56 | 57 | 58 | 59 | today = datetime.date.today() 60 | beg_date = datetime.date(2018, 9, 10) 61 | # end_date = datetime.date(2018, 9, 14) 62 | end_date = datetime.date.today() 63 | 64 | # data_contracts = w.wset("futurecc","startdate=2010-01-01;enddate="+end_date.strftime("%Y-%m-%d")+";wind_code=CU.SHF;field=wind_code,contract_issue_date,last_trade_date,last_delivery_mouth") 65 | data_contracts = w.wset("futurecc","startdate=2017-09-15;enddate=2018-09-15;wind_code=CU.SHF") 66 | df_contracts = pd.DataFrame(data=np.transpose(data_contracts.Data), columns=data_contracts.Fields) 67 | date_range = w.tdays(beg_date, end_date, "").Data[0] 68 | date_range = sorted(date_range,reverse=True) 69 | for dt in date_range: 70 | c_str = "" 71 | contracts = df_contracts[(df_contracts['contract_issue_date'] <=dt)&(df_contracts['last_delivery_month'] >=dt)]['wind_code'].values 72 | for c in contracts: 73 | c_str += c +"," 74 | c_str = c_str[0:len(c_str)-2] 75 | # c_str += '\"' 76 | df1 = wind_future_daily(dt,c_str) 77 | try: 78 | df1.to_sql('futures_mktdata', con=admin.engine_gc, if_exists='append', index=False) 79 | print(dt, ' finished.') 80 | except Exception as e: 81 | print(e) 82 | pass 83 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/index_with_alpha.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_option_set import BaseOptionSet 2 | from back_test.model.base_account import BaseAccount 3 | from back_test.model.base_instrument import BaseInstrument 4 | from back_test.model.base_option import BaseOption 5 | from data_access import get_data 6 | import back_test.model.constant as c 7 | import datetime 8 | import numpy as np 9 | from Utilities.PlotUtil import PlotUtil 10 | import matplotlib.pyplot as plt 11 | import pandas as pd 12 | import math 13 | 14 | start_date = datetime.date(2015, 2, 1) 15 | end_date = datetime.date(2018, 8, 31) 16 | d1 = start_date 17 | min_holding = 20 18 | nbr_maturity = 1 19 | slippage = 0 20 | pct_underlying_invest = 1.0 21 | 22 | df_metrics = get_data.get_50option_mktdata(start_date, end_date) 23 | df_underlying = get_data.get_index_mktdata(start_date, end_date, c.Util.STR_INDEX_50ETF) 24 | 25 | calendar = c.Calendar(sorted(df_underlying[c.Util.DT_DATE].unique())) 26 | pu = PlotUtil() 27 | 28 | df = pd.DataFrame() 29 | d1 = calendar.firstBusinessDayNextMonth(d1) 30 | d2 = d1 + datetime.timedelta(days=365) 31 | while d2 <= end_date: 32 | df_metrics_1 = df_metrics[(df_metrics[c.Util.DT_DATE] >= d1) & (df_metrics[c.Util.DT_DATE] <= d2)].reset_index( 33 | drop=True) 34 | df_underlying_1 = df_underlying[ 35 | (df_underlying[c.Util.DT_DATE] >= d1) & (df_underlying[c.Util.DT_DATE] <= d2)].reset_index(drop=True) 36 | df_underlying_with_alpha = df_underlying_1[[c.Util.DT_DATE, c.Util.ID_INSTRUMENT, c.Util.AMT_CLOSE]] 37 | 38 | df_underlying_with_alpha.loc[:, 'p_last'] = df_underlying_with_alpha[c.Util.AMT_CLOSE].shift() 39 | df_underlying_with_alpha.loc[:, 'r1'] = (df_underlying_with_alpha.loc[:, c.Util.AMT_CLOSE]-df_underlying_with_alpha.loc[:, 'p_last'])/df_underlying_with_alpha.loc[:, 'p_last'] + 0.1 / 252 40 | df_underlying_with_alpha.loc[:, 'close_alpha'] = None 41 | p0 = df_underlying_with_alpha.loc[0, c.Util.AMT_CLOSE] 42 | for (idx, r) in df_underlying_with_alpha.iterrows(): 43 | if idx == 0: 44 | df_underlying_with_alpha.loc[idx, 'close_alpha'] = df_underlying_with_alpha.loc[0, c.Util.AMT_CLOSE] 45 | else: 46 | df_underlying_with_alpha.loc[idx, 'close_alpha'] = df_underlying_with_alpha.loc[idx - 1, 'close_alpha'] * (1+ df_underlying_with_alpha.loc[idx, 'r1']) 47 | 48 | df_underlying_with_alpha = df_underlying_with_alpha[ 49 | [c.Util.DT_DATE, c.Util.ID_INSTRUMENT, c.Util.AMT_CLOSE, 'close_alpha']].rename( 50 | columns={c.Util.AMT_CLOSE: 'etf_close'}) 51 | df_underlying_with_alpha = df_underlying_with_alpha.rename(columns={'close_alpha': c.Util.AMT_CLOSE}) 52 | alpha = (df_underlying_with_alpha.loc[len(df_underlying_with_alpha) - 1, c.Util.AMT_CLOSE]-p0) / p0 53 | etf = (df_underlying_with_alpha.loc[len(df_underlying_with_alpha) - 1, 'etf_close']-p0) / p0 54 | df_underlying_with_alpha.loc[:, 'npv_50etf'] = df_underlying_with_alpha.loc[:, 'etf_close'] / p0 55 | df_underlying_with_alpha.loc[:, 'npv_50etf_alpha'] = df_underlying_with_alpha.loc[:, c.Util.AMT_CLOSE] / p0 56 | npv_alpha = df_underlying_with_alpha.loc[len(df_underlying_with_alpha) - 1, 'npv_50etf_alpha'] 57 | npv_etf = df_underlying_with_alpha.loc[len(df_underlying_with_alpha) - 1, 'npv_50etf'] 58 | alpha1 = np.log(npv_alpha) 59 | etf1 = np.log(npv_etf) 60 | print(d1, ' ', alpha - etf, alpha1-etf1) 61 | # print('alpha : ', alpha, npv_alpha) 62 | # print('etf : ', etf, npv_etf) 63 | d1 = calendar.firstBusinessDayNextMonth(d1) 64 | d2 = d1 + datetime.timedelta(days=365) 65 | -------------------------------------------------------------------------------- /regular_reports/cu_daily.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import datetime 3 | import QuantLib as ql 4 | from PricingLibrary.EngineQuantlib import QlBlackFormula 5 | import back_test.model.constant as c 6 | import math 7 | 8 | eval_date = datetime.date.today() 9 | spot = 49130 10 | 11 | rf = 0.03 12 | 13 | calendar = ql.China() 14 | df = pd.read_excel('C:/Users/wangd/Documents/Dongli/00_数据/铜期权日度数据-20180921.xlsx', sheetname='analysis') 15 | 16 | 17 | def maturity_date(series): 18 | cm = series['合约月份'] 19 | month = cm - 1900 20 | d1 = ql.Date(1, month, 2019) 21 | mdt = calendar.advance(d1, ql.Period(-5, ql.Days)) 22 | mdt = c.QuantlibUtil.to_dt_date(mdt) 23 | return mdt 24 | 25 | 26 | def iv(series,S, cd_price,option_type=None): 27 | if option_type is None: 28 | if series['期权类型'] == 'C': 29 | option_type = c.OptionType.CALL 30 | elif series['期权类型'] == 'P': 31 | option_type = c.OptionType.PUT 32 | else: 33 | option_type = None 34 | black = QlBlackFormula(eval_date, series['到期日'], option_type, S, series['行权价'], rf=rf) 35 | iv = black.estimate_vol(series[cd_price]) 36 | return iv 37 | 38 | def fun_calculate_iv(series,S,option_type): 39 | if option_type == c.OptionType.PUT: 40 | vol = iv(series,S,c.Util.AMT_PUT_QUOTE,c.OptionType.PUT) 41 | elif option_type == c.OptionType.CALL: 42 | vol = iv(series, S, c.Util.AMT_CALL_QUOTE, c.OptionType.CALL) 43 | else: 44 | return 45 | return vol 46 | 47 | def fun_otm_iv(df_series): 48 | K = df_series['行权价'] 49 | if K <= spot: 50 | return df_series['iv_put'] 51 | else: 52 | return df_series['iv_call'] 53 | 54 | def fun_otm_iv_htbr(df_series): 55 | K = df_series['行权价'] 56 | if K <= spot: 57 | return df_series['iv_put_htbr'] 58 | else: 59 | return df_series['iv_call_htbr'] 60 | 61 | def fun_htb_rate(df_series, rf): 62 | r = -math.log((df_series[c.Util.AMT_CALL_QUOTE] - df_series[c.Util.AMT_PUT_QUOTE] 63 | + df_series['行权价'] * math.exp(-rf * df_series[c.Util.AMT_TTM])) 64 | / spot) / df_series[c.Util.AMT_TTM] 65 | return r 66 | 67 | def fun_ttm(series): 68 | return (series['到期日'] - eval_date).total_seconds() / 60.0 / (365 * 1440.0) 69 | 70 | 71 | df = df[['合约代码', '合约月份', '行权价', '期权类型', '开盘价', '最高价', '最低价', '收盘价', '成交量', '持仓量', '成交额']] 72 | df['到期日'] = df.apply(lambda x: maturity_date(x), axis=1) 73 | df[c.Util.AMT_TTM] = df.apply(lambda x: fun_ttm(x),axis=1) 74 | df['成交量加权均价'] = 10000 * df['成交额'] / df['成交量'] / 5 75 | df['iv_last'] = df.apply(lambda x: iv(x, spot, '收盘价'), axis=1) 76 | df['iv_vw'] = df.apply(lambda x: iv(x, spot, '成交量加权均价'), axis=1) 77 | 78 | df1_call = df[(df['合约月份'] == 1902) & (df['期权类型'] == 'C')][['合约月份','到期日', '行权价', '收盘价', 'iv_last',c.Util.AMT_TTM]].rename( 79 | columns={'iv_last': 'iv_call', '收盘价': c.Util.AMT_CALL_QUOTE}) 80 | df1_put = df[(df['合约月份'] == 1902) & (df['期权类型'] == 'P')][['行权价', '收盘价', 'iv_last']].rename( 81 | columns={'iv_last': 'iv_put', '收盘价': c.Util.AMT_PUT_QUOTE}) 82 | df1 = pd.merge(df1_call, df1_put, on='行权价') 83 | 84 | diff = abs(df1.loc[:, '行权价'] - spot) 85 | htb_r = fun_htb_rate(df1.loc[diff.idxmin()], rf) 86 | spot_htbr = spot * math.exp(-htb_r * df1[c.Util.AMT_TTM].values[0]) 87 | 88 | df1['iv_otm_curve'] = df1.apply(lambda x: fun_otm_iv(x), axis=1) 89 | df1['iv_call_htbr'] = df1.apply(lambda x: fun_calculate_iv(x,spot_htbr,c.OptionType.CALL), axis=1) 90 | df1['iv_put_htbr'] = df1.apply(lambda x: fun_calculate_iv(x,spot_htbr,c.OptionType.PUT), axis=1) 91 | df1['iv_otm_curve_htbr'] = df1.apply(lambda x: fun_otm_iv_htbr(x), axis=1) 92 | 93 | print(df) 94 | -------------------------------------------------------------------------------- /OptionStrategyLib/calibration.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from matplotlib import pyplot as plt 4 | from OptionStrategyLib.Optimization.svi_neldermead import SVINelderMeadOptimization as svinmopt 5 | class SVICalibration(): 6 | 7 | def __init__(self,evalDate,initparams=[0.1, 0.1 ,0.1 ,0.1 ,0.1],sim_no=10): 8 | self.evalDate = evalDate 9 | self.initparams = initparams 10 | self.sim_no = sim_no 11 | 12 | def calibrate_rawsvi(self,strikes,maturity_dates,underlying_prices,option_prices,implied_vols,rfs): 13 | df = pd.DataFrame({'strikes':strikes, 14 | 'maturity_dates':maturity_dates, 15 | 'underlying_prices':underlying_prices, 16 | 'option_prices':option_prices, 17 | 'implied_vols':option_prices, 18 | 'risk_free_rates':rfs}) 19 | ttms = [] 20 | for mdt in maturity_dates: 21 | ttm = (mdt - self.evalDate).days / 365.0 22 | ttms.append(ttm) 23 | df['ttm'] = ttms 24 | df['forwards'] = df['underlying_prices']*np.exp(df['risk_free_rates']) 25 | # df['logmoneyness'] = np.log(df['strikes']/df['forwards']) 26 | df['logmoneyness'] = np.log(df['strikes']/df['underlying_prices']) 27 | df['totalvariance'] = (df['implied_vols']**2)*df['ttm'] 28 | mdates_uqique = df['maturity_dates'].unique() 29 | params_set = [] 30 | for mdate in mdates_uqique: 31 | c1 = df['maturity_dates']==mdate 32 | logmoneyness = df[c1]['logmoneyness'].tolist() 33 | totalvariance = df[c1]['totalvariance'].tolist() 34 | impliedvols = df[c1]['implied_vols'].tolist() 35 | ttm = df[c1]['ttm'].iloc[0] 36 | data = [logmoneyness,totalvariance] 37 | # print('data : ',data) 38 | min_sse = 100 39 | calibrated_params = None 40 | for iter in range(self.sim_no): 41 | ms_0 = self.initparams[0:2] 42 | adc_0 = self.initparams[2:] 43 | nm = svinmopt(data, adc_0, ms_0, 1e-7) 44 | calibrated_params, obj = nm.optimization() 45 | _a_star, _d_star, _c_star, m_star, sigma_star = calibrated_params 46 | sse = 0.0 47 | for i, m in enumerate(logmoneyness): 48 | tv = totalvariance[i] 49 | y_1 = np.divide((m - m_star), sigma_star) 50 | tv_1 = _a_star + _d_star * y_1 + _c_star * np.sqrt(y_1 ** 2 + 1) 51 | sse += (tv - tv_1) ** 2 52 | if sse >= min_sse: continue 53 | min_sse = sse 54 | _a_star, _d_star, _c_star, m_star, sigma_star = calibrated_params 55 | # print(calibrated_params) 56 | a_star = np.divide(_a_star, ttm) 57 | b_star = np.divide(_c_star, (sigma_star * ttm)) 58 | rho_star = np.divide(_d_star, _c_star) 59 | params_set.append({'dt_date': self.evalDate,'dt_maturity':mdate, 60 | 'a':a_star,'b':b_star,'rho':rho_star,'m':m_star,'sigma':sigma_star}) 61 | x_svi = np.arange(min(logmoneyness)-0.005, max(logmoneyness)+0.02, 0.1/100) 62 | vol_svi = np.sqrt( 63 | a_star+b_star*(rho_star*(x_svi-m_star)+np.sqrt((x_svi-m_star)**2+sigma_star**2))) 64 | plt.figure() 65 | plt.plot(logmoneyness, impliedvols, 'ro') 66 | plt.plot(x_svi, vol_svi, 'b--') 67 | plt.show() 68 | params_df = pd.DataFrame(params_set) 69 | return params_df 70 | 71 | -------------------------------------------------------------------------------- /Utilities/utilities.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import QuantLib as ql 3 | import pandas as pd 4 | import numpy as np 5 | import os 6 | 7 | def to_dt_dates(ql_dates): 8 | datetime_dates = [] 9 | for d in ql_dates: 10 | dt = datetime.date(d.year(),d.month(),d.dayOfMonth()) 11 | datetime_dates.append(dt) 12 | return datetime_dates 13 | 14 | def to_ql_dates(datetime_dates): 15 | ql_dates = [] 16 | for d in datetime_dates: 17 | dt = ql.Date(d.day,d.month,d.year) 18 | ql_dates.append(dt) 19 | return ql_dates 20 | 21 | def to_ql_date(datetime_date): 22 | dt = ql.Date(datetime_date.day,datetime_date.month,datetime_date.year) 23 | return dt 24 | 25 | def to_dt_date(ql_date): 26 | dt = datetime.date(ql_date.year(), ql_date.month(), ql_date.dayOfMonth()) 27 | return dt 28 | 29 | def get_curve_treasury_bond(evalDate, daycounter): 30 | datestr = str(evalDate.year()) + "-" + str(evalDate.month()) + "-" + str(evalDate.dayOfMonth()) 31 | try: 32 | curvedata = pd.read_json(os.path.abspath('..') + '\marketdata\curvedata_tb_' + datestr + '.json') 33 | rates = curvedata.values[0] 34 | calendar = ql.China() 35 | dates = [evalDate, 36 | calendar.advance(evalDate, ql.Period(1, ql.Months)), 37 | calendar.advance(evalDate, ql.Period(3, ql.Months)), 38 | calendar.advance(evalDate, ql.Period(6, ql.Months)), 39 | calendar.advance(evalDate, ql.Period(9, ql.Months)), 40 | calendar.advance(evalDate, ql.Period(1, ql.Years))] 41 | krates = np.divide(rates, 100) 42 | curve = ql.ForwardCurve(dates, krates, daycounter) 43 | except Exception as e: 44 | print(e) 45 | print('Error def -- get_curve_treasury_bond in \'svi_read_data\' on date : ', evalDate) 46 | return 47 | return curve 48 | 49 | def get_mdate_by_contractid(commodityType,contractId,calendar): 50 | maturity_date = 0 51 | if commodityType == 'm': 52 | year = '20' + contractId[0: 2] 53 | month = contractId[-2:] 54 | date = ql.Date(1, int(month), int(year)) 55 | maturity_date = calendar.advance(calendar.advance(date, ql.Period(-1, ql.Months)), ql.Period(4, ql.Days)) 56 | elif commodityType == 'sr': 57 | year = '201' + contractId[2] 58 | month = contractId[-2:] 59 | date = ql.Date(1, int(month), int(year)) 60 | maturity_date = calendar.advance(calendar.advance(date, ql.Period(-1, ql.Months)), ql.Period(-5, ql.Days)) 61 | return maturity_date 62 | 63 | def get_rf_tbcurve(evalDate,daycounter,maturitydate): 64 | curve = get_curve_treasury_bond(evalDate, daycounter) 65 | maxdate = curve.maxDate() 66 | #print(maxdate,maturitydate) 67 | if maturitydate > maxdate: 68 | rf = curve.zeroRate(maxdate, daycounter, ql.Continuous).rate() 69 | else: 70 | rf = curve.zeroRate(maturitydate, daycounter, ql.Continuous).rate() 71 | return rf 72 | 73 | def get_yield_ts(evalDate,curve,mdate,daycounter): 74 | maxdate = curve.maxDate() 75 | if mdate > maxdate: 76 | rf = curve.zeroRate(maxdate, daycounter, ql.Continuous).rate() 77 | else: 78 | rf = curve.zeroRate(mdate, daycounter, ql.Continuous).rate() 79 | yield_ts = ql.YieldTermStructureHandle(ql.FlatForward(evalDate, rf, daycounter)) 80 | return yield_ts 81 | 82 | def get_dividend_ts(evalDate,daycounter): 83 | dividend_ts = ql.YieldTermStructureHandle(ql.FlatForward(evalDate, 0.0, daycounter)) 84 | return dividend_ts 85 | 86 | def get_closest_strike(strikes,target): 87 | res = strikes[0] 88 | emin = 100.0 89 | for strike in strikes: 90 | e = strike-target 91 | if e < emin : res = strike 92 | return res -------------------------------------------------------------------------------- /Utilities/admin_write_util.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine, MetaData, Table 2 | from sqlalchemy.orm import sessionmaker 3 | 4 | 5 | engine = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/mktdata', echo=False) 6 | # conn = engine.connect() 7 | metadata = MetaData(engine) 8 | 9 | engine_intraday = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/mktdata_intraday', echo=False) 10 | # conn_intraday = engine_intraday.connect() 11 | metadata_intraday = MetaData(engine_intraday) 12 | 13 | engine_metrics = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/metrics', echo=False) 14 | # conn_metrics = engine_metrics.connect() 15 | metadata_metrics = MetaData(engine_metrics) 16 | 17 | engine_dzqh = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/dzqh', echo=False) 18 | 19 | metadata_dzqh = MetaData(engine_dzqh) 20 | 21 | engine_gc = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/golden_copy', echo=False) 22 | 23 | metadata_gc = MetaData(engine_gc) 24 | 25 | def conn_dzqh(): 26 | return engine_dzqh.connect() 27 | 28 | def conn_mktdata(): 29 | return engine.connect() 30 | 31 | def conn_intraday(): 32 | return engine_intraday.connect() 33 | 34 | def conn_metrics(): 35 | return engine_metrics.connect() 36 | 37 | def session_dzqh(): 38 | Session = sessionmaker(bind=engine_dzqh) 39 | return Session() 40 | 41 | def session_mktdata(): 42 | Session = sessionmaker(bind=engine) 43 | return Session() 44 | 45 | def session_intraday(): 46 | Session = sessionmaker(bind=engine_intraday) 47 | return Session() 48 | 49 | def session_metrics(): 50 | Session = sessionmaker(bind=engine_metrics) 51 | return Session() 52 | 53 | def table_options_mktdata(): 54 | return Table('options_mktdata', metadata, autoload=True) 55 | 56 | def table_futures_mktdata(): 57 | return Table('futures_mktdata', metadata, autoload=True) 58 | 59 | def table_futures_mktdata_gc(): 60 | return Table('futures_mktdata', metadata_gc, autoload=True) 61 | 62 | def table_futures_institution_positions(): 63 | return Table('futures_institution_positions', metadata, autoload=True) 64 | 65 | def table_indexes_mktdata(): 66 | return Table('indexes_mktdata', metadata, autoload=True) 67 | 68 | def table_option_contracts(): 69 | return Table('option_contracts', metadata, autoload=True) 70 | 71 | def table_future_contracts(): 72 | return Table('future_contracts', metadata, autoload=True) 73 | 74 | def table_stocks_mktdata(): 75 | return Table('stocks_mktdata', metadata, autoload=True) 76 | 77 | def table_events(): 78 | return Table('events', metadata, autoload=True) 79 | 80 | def table_index_mktdata_intraday(): 81 | return Table('equity_index_mktdata_intraday', metadata_intraday, autoload=True) 82 | 83 | def table_option_mktdata_intraday(): 84 | return Table('option_mktdata_intraday', metadata_intraday, autoload=True) 85 | 86 | def table_option_metrics(): 87 | return Table('option_metrics', metadata_metrics, autoload=True) 88 | 89 | def table_moving_average(): 90 | return Table('moving_average', metadata_metrics, autoload=True) 91 | 92 | def table_option_iv_by_moneyness(): 93 | return Table('option_iv_by_moneyness', metadata_metrics, autoload=True) 94 | 95 | def table_option_atm_iv(): 96 | return Table('option_atm_iv',metadata_metrics, autoload=True) 97 | 98 | def table_implied_volatilities(): 99 | return Table('implied_volatilities_by_moneyness',metadata_metrics, autoload=True) 100 | 101 | 102 | 103 | def table_cf_minute_1(): 104 | return Table('cf_minute_1',metadata_dzqh, autoload=True) 105 | 106 | def table_cf_daily(): 107 | return Table('cf_day',metadata_dzqh, autoload=True) 108 | -------------------------------------------------------------------------------- /OptionStrategyLib/Optimization/svi_neldermead.py: -------------------------------------------------------------------------------- 1 | from scipy.optimize import minimize 2 | import numpy as np 3 | import math 4 | 5 | 6 | class SVINelderMeadOptimization: 7 | 8 | 9 | def __init__(self,data,init_adc,init_msigma,tol): 10 | self.init_msigma = init_msigma 11 | self.init_adc = init_adc 12 | self.tol = tol 13 | self.data = data 14 | 15 | def outter_fun(self,params): 16 | m,sigma = params 17 | sigma = max(1e-10,sigma) 18 | adc_0 = self.init_adc 19 | def inner_fun(params): 20 | a,d,c = params 21 | sum = 0.0 22 | for i,xi in enumerate(self.data[0]): 23 | yi = (xi - m)/sigma 24 | f_msigma = (a + d*yi + c * math.sqrt((yi**2 + 1)) - self.data[1][i])**2 25 | sum += f_msigma 26 | return sum 27 | #print(m,sigma) 28 | # Constraints: 0 <= c <=4sigma; |d| <= c and |d| <= 4sigma - c; 0 <= a <= max{vi} 29 | #print("m",m,";\tsigma",sigma) 30 | bnds = ((1e-10,max(self.data[1])),(-4*sigma,4*sigma),(1e-10, 4*sigma)) 31 | # bnds = ((-1, max(self.data[1])), (-4 * sigma, 4 * sigma), (0, 4 * sigma)) 32 | #bnds = ((None, None), (-4 * sigma, 4 * sigma), (0, 4 * sigma)) 33 | b = np.array(bnds,float) 34 | cons = ( 35 | {'type':'ineq','fun': lambda x: x[2] - abs(x[1])}, 36 | {'type':'ineq','fun': lambda x: 4*sigma - x[2] - abs(x[1])} 37 | ) 38 | inner_res = minimize(inner_fun,adc_0,method='SLSQP',bounds = bnds,constraints=cons, tol=1e-6) 39 | #inner_res = minimize(inner_fun, adc_0, method='SLSQP', tol=1e-6) 40 | a_star,d_star,c_star = inner_res.x 41 | #global _a_star,_d_star,_c_star 42 | self._a_star, self._d_star, self._c_star = inner_res.x 43 | #print(a_star,d_star,c_star) 44 | sum = 0.0 45 | for i,xi in enumerate(self.data[0]): 46 | yi = (xi - m)/sigma 47 | f_msigma = (a_star + d_star*yi + c_star * math.sqrt((yi**2 + 1)) - self.data[1][i])**2 48 | sum += f_msigma 49 | return sum 50 | 51 | def optimization(self): 52 | outter_res = minimize(self.outter_fun, self.init_msigma, method='Nelder-Mead', tol=self.tol) 53 | m_star,sigma_star = outter_res.x 54 | #print(outter_res.x) 55 | #print(outter_res) 56 | obj = outter_res.fun 57 | #print(_a_star,_d_star,_c_star,m_star,sigma_star) 58 | # SVI parameters: a, b, sigma, rho, m 59 | calibrated_params = [self._a_star, self._d_star, self._c_star,m_star,sigma_star] 60 | return calibrated_params,obj 61 | 62 | 63 | def one_fuction(self,params): 64 | a,d,c,m,sigma = params 65 | sum = 0.0 66 | for i,xi in enumerate(self.data[0]): 67 | yi = (xi - m)/sigma 68 | f_msigma = (a + d*yi + c * math.sqrt((yi**2 + 1)) - self.data[1][i])**2 69 | sum += f_msigma 70 | return sum 71 | 72 | def optimization_SLSQP(self): 73 | [a0,d0,c0,m0,sigma0] = [1,1,1,1,1] 74 | bnds = ((0,max(self.data[1])),(None,None),(0,None),(None,None),(0,None)) 75 | cons = ( 76 | {'type': 'ineq', 'fun': lambda x: 4*x[4] - x[2]}, # 4sigma - c >= 0 77 | {'type': 'ineq', 'fun': lambda x: x[2] - abs(x[1])}, # |d| <= c 78 | {'type': 'ineq', 'fun': lambda x: 4*x[4] - x[2] - abs(x[1])} # |d| <= 4sigma -c 79 | ) 80 | res = minimize(self.one_fuction, np.array([a0,d0,c0,m0,sigma0]), 81 | method='SLSQP',bounds = bnds,constraints=cons, tol=1e-6) 82 | a_star,d_star,c_star, m_star, sigma_star = res.x 83 | print('a_star,d_star,c_star, m_star, sigma_star: ', a_star,d_star,c_star, m_star, sigma_star) 84 | return a_star,d_star,c_star, m_star, sigma_star 85 | -------------------------------------------------------------------------------- /regular_reports/commodity_option_pricing.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_option_set import BaseOptionSet 2 | from data_access import get_data 3 | import back_test.model.constant as c 4 | import datetime 5 | 6 | """""" 7 | name_code = c.Util.STR_M 8 | core_id = 'm_1901' 9 | end_date = datetime.date(2018,9,7) 10 | last_week = datetime.date(2018, 8, 31) 11 | start_date = last_week 12 | dt_histvol = start_date - datetime.timedelta(days=200) 13 | min_holding = 5 14 | 15 | 16 | df_metrics = get_data.get_comoption_mktdata(start_date, end_date, name_code) 17 | 18 | """ T-quote IV """ 19 | optionset = BaseOptionSet(df_metrics,rf=0) 20 | optionset.init() 21 | optionset.go_to(end_date) 22 | dt_maturity = optionset.select_maturity_date(0,min_holding) 23 | call_list, put_list = optionset.get_options_list_by_moneyness_mthd1(0,dt_maturity) 24 | atm_call = optionset.select_higher_volume(call_list) 25 | atm_put = optionset.select_higher_volume(put_list) 26 | print('atm call iv: ',atm_call.get_implied_vol()) 27 | print('atm put iv: ',atm_put.get_implied_vol()) 28 | print('atm strike: ',atm_put.strike()) 29 | 30 | t_quote = optionset.get_T_quotes(dt_maturity) 31 | ivs_c1 = optionset.get_call_implied_vol_curve(dt_maturity) 32 | ivs_p1 = optionset.get_put_implied_vol_curve(dt_maturity) 33 | ivs_c2 = optionset.get_call_implied_vol_curve_htbr(dt_maturity) 34 | ivs_p2 = optionset.get_put_implied_vol_curve_htbr(dt_maturity) 35 | 36 | iv_curve = optionset.get_implied_vol_curves(dt_maturity) 37 | iv_curve_htbr = optionset.get_implied_vol_curves_htbr(dt_maturity) 38 | iv_volume_weighted = optionset.get_volume_weighted_iv(dt_maturity) 39 | iv_volume_weighted_htbr = optionset.get_volume_weighted_iv_htbr(dt_maturity) 40 | htbr = optionset.get_htb_rate(dt_maturity) 41 | print(iv_volume_weighted) 42 | print(iv_volume_weighted_htbr) 43 | print(htbr) 44 | print(iv_curve_htbr) 45 | """ Quantlib """ 46 | # global data 47 | # todaysDate = QuantlibUtil.to_ql_date(end_date) 48 | # Settings.instance().evaluationDate = todaysDate 49 | # settlementDate = todaysDate 50 | # maturityDate = QuantlibUtil.to_ql_date(datetime.date(2018,12,7)) 51 | # dt_settlement = QuantlibUtil.to_dt_date(settlementDate) 52 | # dt_maturity = QuantlibUtil.to_dt_date(maturityDate) 53 | # rf = 0.0 54 | # spot = 3120.0 55 | # strike = 3650.0 56 | # riskFreeRate = FlatForward(settlementDate, rf, ActualActual()) 57 | # # option parameters 58 | # exercise = AmericanExercise(settlementDate, maturityDate) 59 | # payoff = PlainVanillaPayoff(Option.Put, strike) 60 | # 61 | # # market data 62 | # refValue = 538.0 63 | # underlying = SimpleQuote(spot) 64 | # volatility = BlackConstantVol(todaysDate, China(), 0.20, ActualActual()) 65 | # dividendYield = FlatForward(settlementDate, 0.00, ActualActual()) 66 | # 67 | # process = BlackScholesMertonProcess(QuoteHandle(underlying), 68 | # YieldTermStructureHandle(dividendYield), 69 | # YieldTermStructureHandle(riskFreeRate), 70 | # BlackVolTermStructureHandle(volatility)) 71 | # 72 | # option = VanillaOption(payoff, exercise) 73 | # 74 | # ql_baw = QlBAW(dt_settlement,dt_maturity,OptionType.PUT,OptionExerciseType.AMERICAN,spot,strike,rf=rf) 75 | # ql_bonomial = QlBinomial(dt_settlement,dt_maturity,OptionType.PUT,OptionExerciseType.AMERICAN,spot,strike,rf=rf) 76 | # 77 | # option.setPricingEngine(BaroneAdesiWhaleyEngine(process)) 78 | # print('BAW : ',option.impliedVolatility(refValue,process)) 79 | # print('BAW local : ',ql_baw.estimate_vol_ql(refValue)) 80 | # print('BAW local2 : ',ql_baw.estimate_vol(refValue)) 81 | # 82 | # option.setPricingEngine(BinomialVanillaEngine(process,'crr',800)) 83 | # print('Binomial : ',option.impliedVolatility(refValue,process)) 84 | # print('Binomial local : ',ql_bonomial.estimate_vol_ql(refValue)) 85 | # print('Binomial local2 : ',ql_bonomial.estimate_vol(refValue)) 86 | 87 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/option_market_capacity.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_option_set import BaseOptionSet 2 | from data_access import get_data 3 | import back_test.model.constant as c 4 | import datetime 5 | import pandas as pd 6 | 7 | """""" 8 | start_date = datetime.date(2015, 1, 4) 9 | end_date = datetime.date.today() 10 | min_holding = 1 11 | nbr_maturitys = [0,1,2,3] 12 | 13 | df_metrics = get_data.get_50option_mktdata(start_date, end_date) 14 | 15 | list_capacity = [] 16 | 17 | optionset = BaseOptionSet(df_metrics) 18 | optionset.init() 19 | # optionset.go_to(end_date) 20 | # 计算市场容量时直接用nbr_maturity选择相应的到期日,不需考虑min_holding 21 | while optionset.has_next(): 22 | for nbr_maturity in nbr_maturitys: 23 | dt_maturity = optionset.get_maturities_list()[nbr_maturity] 24 | dict_m_options = optionset.get_dict_moneyness_and_options(dt_maturity,c.OptionType.PUT) 25 | otm_put = 0 #[-inf, -1] 26 | atm_put = 0 #[0] 27 | itm_put = 0 #[1, inf] 28 | for m in dict_m_options.keys(): 29 | if m <= -1: 30 | for option in dict_m_options[m]: 31 | otm_put += option.trading_volume() 32 | elif m >= 1: 33 | for option in dict_m_options[m]: 34 | itm_put += option.trading_volume() 35 | else: 36 | for option in dict_m_options[m]: 37 | atm_put += option.trading_volume() 38 | total_put = otm_put+itm_put+atm_put 39 | total_call = 0 40 | dict_m_options_c = optionset.get_dict_moneyness_and_options(dt_maturity,c.OptionType.CALL) 41 | for m in dict_m_options_c.keys(): 42 | for option in dict_m_options_c[m]: 43 | total_call += option.trading_volume() 44 | list_capacity.append({ 45 | '1-dt_date':optionset.eval_date, 46 | '2-nbr_maturity':nbr_maturity, 47 | '3-otm_put':otm_put, 48 | '4-itm_put':itm_put, 49 | '5-atm_put':atm_put, 50 | '6-total_put':total_put, 51 | '7-total_option':total_call+total_put, 52 | }) 53 | # print(optionset.eval_date,nbr_maturity,total_put,total_call) 54 | optionset.next() 55 | 56 | df_capacity = pd.DataFrame(list_capacity) 57 | 58 | df_m0 = df_capacity[df_capacity['2-nbr_maturity']==0].reset_index(drop=True) 59 | df_m1 = df_capacity[df_capacity['2-nbr_maturity']==1].reset_index(drop=True) 60 | df_m2 = df_capacity[df_capacity['2-nbr_maturity']==2].reset_index(drop=True) 61 | df_m3 = df_capacity[df_capacity['2-nbr_maturity']==3].reset_index(drop=True) 62 | 63 | df_m0['8-otm_put_capacity'] = c.Statistics.moving_average(df_m0['3-otm_put'],20)/100.0 64 | df_m0['9-put_capacity'] = c.Statistics.moving_average(df_m0['6-total_put'],20)/100.0 65 | df_m0['10-total_capacity'] = c.Statistics.moving_average(df_m0['7-total_option'],20)/100.0 66 | df_m0.to_csv('../accounts_data/df_m0.csv') 67 | 68 | df_m1['8-otm_put_capacity'] = c.Statistics.moving_average(df_m1['3-otm_put'],20)/100.0 69 | df_m1['9-put_capacity'] = c.Statistics.moving_average(df_m1['6-total_put'],20)/100.0 70 | df_m1['10-total_capacity'] = c.Statistics.moving_average(df_m1['7-total_option'],20)/100.0 71 | df_m1.to_csv('../accounts_data/df_m1.csv') 72 | 73 | df_m2['8-otm_put_capacity'] = c.Statistics.moving_average(df_m2['3-otm_put'],20)/100.0 74 | df_m2['9-put_capacity'] = c.Statistics.moving_average(df_m2['6-total_put'],20)/100.0 75 | df_m2['10-total_capacity'] = c.Statistics.moving_average(df_m2['7-total_option'],20)/100.0 76 | df_m2.to_csv('../accounts_data/df_m2.csv') 77 | 78 | df_m3['8-otm_put_capacity'] = c.Statistics.moving_average(df_m3['3-otm_put'],20)/100.0 79 | df_m3['9-put_capacity'] = c.Statistics.moving_average(df_m3['6-total_put'],20)/100.0 80 | df_m3['10-total_capacity'] = c.Statistics.moving_average(df_m3['7-total_option'],20)/100.0 81 | df_m3.to_csv('../accounts_data/df_m3.csv') -------------------------------------------------------------------------------- /regular_reports/otc_index_option_histvol.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import * 2 | from sqlalchemy.orm import sessionmaker 3 | from mpl_toolkits.mplot3d import Axes3D 4 | import matplotlib as mpl 5 | from matplotlib import cm as plt_cm 6 | import datetime 7 | import pandas as pd 8 | from WindPy import w 9 | from data_access.db_tables import DataBaseTables as dbt 10 | import matplotlib.pyplot as plt 11 | from Utilities.PlotUtil import PlotUtil 12 | from Utilities.calculate import calculate_histvol 13 | 14 | ########################################################################################### 15 | w.start() 16 | pu = PlotUtil() 17 | plt.rcParams['font.sans-serif'] = ['STKaiti'] 18 | plt.rcParams.update({'font.size': 13}) 19 | engine1 = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/mktdata_intraday', echo=False) 20 | engine2 = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/mktdata', echo=False) 21 | metadata1 = MetaData(engine1) 22 | Session1 = sessionmaker(bind=engine1) 23 | sess1 = Session1() 24 | metadata2 = MetaData(engine2) 25 | Session2 = sessionmaker(bind=engine2) 26 | sess2 = Session2() 27 | index_intraday = Table('equity_index_mktdata_intraday', metadata1, autoload=True) 28 | EquityIndexIntraday = dbt.EquityIndexIntraday 29 | IndexMkt = dbt.IndexMkt 30 | ############################################################################################ 31 | # Eval Settings 32 | evalDate = datetime.date(2018, 6, 30) 33 | startDate = datetime.date(2017, 1, 1) 34 | hist_date = datetime.date(2015 ,6, 1) 35 | index_ids = ['index_300sh','index_50sh','index_500sh','index_50etf'] 36 | histvols_3M = [] 37 | realizedvols = [] 38 | dates = [] 39 | mergedvix_df = pd.DataFrame() 40 | query2_1 = sess2.query(IndexMkt.id_instrument, IndexMkt.dt_date, IndexMkt.amt_close) \ 41 | .filter(IndexMkt.dt_date >= startDate) \ 42 | .filter(IndexMkt.dt_date <= evalDate) \ 43 | .filter(IndexMkt.id_instrument == 'index_cvix') 44 | 45 | for indexid in index_ids: 46 | 47 | query2 = sess2.query(IndexMkt.id_instrument, IndexMkt.dt_date, IndexMkt.amt_close) \ 48 | .filter(IndexMkt.dt_date >= hist_date) \ 49 | .filter(IndexMkt.dt_date <= evalDate) \ 50 | .filter(IndexMkt.id_instrument == indexid) 51 | 52 | index_df = pd.read_sql(query2.statement, query2.session.bind) 53 | 54 | index_df['histvol_120'] = calculate_histvol(index_df['amt_close'],120) 55 | index_df['histvol_60'] = calculate_histvol(index_df['amt_close'],60) 56 | index_df['histvol_20'] = calculate_histvol(index_df['amt_close'],20) 57 | index_df['histvol_10'] = calculate_histvol(index_df['amt_close'],10) 58 | index_df['histvol_5'] = calculate_histvol(index_df['amt_close'],5) 59 | 60 | merged_df = index_df 61 | dates = merged_df['dt_date'].tolist() 62 | 63 | vol_set = [ merged_df['histvol_20'].tolist(), 64 | merged_df['histvol_60'].tolist(), 65 | merged_df['histvol_120'].tolist()] 66 | print(dates[-1],indexid,' histvol_120 : ',merged_df['histvol_120'].tolist()[-1]) 67 | print(dates[-1],indexid,' histvol_60 : ',merged_df['histvol_60'].tolist()[-1]) 68 | print(dates[-1],indexid,' histvol_20 : ',merged_df['histvol_20'].tolist()[-1]) 69 | f2, ax2 = plt.subplots() 70 | ldgs = [ '历史波动率1M', '历史波动率3M','历史波动率6M'] 71 | for cont2, y in enumerate(vol_set): 72 | pu.plot_line(ax2, cont2, dates, y, ldgs[cont2], '日期', '波动率(%)') 73 | ax2.legend(bbox_to_anchor=(0., 1.02, 1., .202), loc=3, 74 | ncol=5, mode="expand", borderaxespad=0.) 75 | for tick in ax2.get_xticklabels(): 76 | tick.set_rotation(90) 77 | f2.set_size_inches((14, 6)) 78 | f2.savefig('../save_figure/otc_realizedvol_' + indexid + '_' + str(startDate) + ' - ' + str(evalDate) + '.png', 79 | dpi=300, format='png') 80 | histvols_3M.append(merged_df['histvol_60'].tolist()) 81 | merged_df.to_csv('../data/index_vols'+indexid+'.csv') 82 | 83 | 84 | 85 | plt.show() 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Utilities/admin_util.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine, MetaData, Table 2 | from sqlalchemy.orm import sessionmaker 3 | 4 | 5 | engine = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/mktdata', echo=False) 6 | # conn = engine.connect() 7 | metadata = MetaData(engine) 8 | 9 | engine_intraday = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/mktdata_intraday', echo=False) 10 | # conn_intraday = engine_intraday.connect() 11 | metadata_intraday = MetaData(engine_intraday) 12 | 13 | engine_metrics = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/metrics', echo=False) 14 | # conn_metrics = engine_metrics.connect() 15 | metadata_metrics = MetaData(engine_metrics) 16 | 17 | engine_dzqh = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/dzqh', echo=False) 18 | 19 | metadata_dzqh = MetaData(engine_dzqh) 20 | 21 | engine_gc = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/golden_copy', echo=False) 22 | 23 | metadata_gc = MetaData(engine_gc) 24 | 25 | def conn_dzqh(): 26 | return engine_dzqh.connect() 27 | 28 | def conn_mktdata(): 29 | return engine.connect() 30 | 31 | def conn_intraday(): 32 | return engine_intraday.connect() 33 | 34 | def conn_metrics(): 35 | return engine_metrics.connect() 36 | 37 | def session_dzqh(): 38 | Session = sessionmaker(bind=engine_dzqh) 39 | return Session() 40 | 41 | def session_mktdata(): 42 | Session = sessionmaker(bind=engine) 43 | return Session() 44 | 45 | def session_intraday(): 46 | Session = sessionmaker(bind=engine_intraday) 47 | return Session() 48 | 49 | def session_metrics(): 50 | Session = sessionmaker(bind=engine_metrics) 51 | return Session() 52 | 53 | def session_gc(): 54 | Session = sessionmaker(bind=engine_gc) 55 | return Session() 56 | 57 | def table_options_mktdata(): 58 | return Table('options_mktdata', metadata, autoload=True) 59 | 60 | def table_futures_mktdata(): 61 | return Table('futures_mktdata', metadata, autoload=True) 62 | 63 | def table_futures_mktdata_gc(): 64 | return Table('futures_mktdata', metadata_gc, autoload=True) 65 | 66 | def table_futures_institution_positions(): 67 | return Table('futures_institution_positions', metadata, autoload=True) 68 | 69 | def table_indexes_mktdata(): 70 | return Table('indexes_mktdata', metadata, autoload=True) 71 | 72 | def table_option_contracts(): 73 | return Table('option_contracts', metadata, autoload=True) 74 | 75 | def table_future_contracts(): 76 | return Table('future_contracts', metadata, autoload=True) 77 | 78 | def table_stocks_mktdata(): 79 | return Table('stocks_mktdata', metadata, autoload=True) 80 | 81 | def table_events(): 82 | return Table('events', metadata, autoload=True) 83 | 84 | def table_index_mktdata_intraday(): 85 | return Table('equity_index_mktdata_intraday', metadata_intraday, autoload=True) 86 | 87 | def table_option_mktdata_intraday(): 88 | return Table('option_mktdata_intraday', metadata_intraday, autoload=True) 89 | 90 | def table_option_mktdata_intraday_gc(): 91 | return Table('option_mktdata_intraday', metadata_gc, autoload=True) 92 | 93 | 94 | def table_option_metrics(): 95 | return Table('option_metrics', metadata_metrics, autoload=True) 96 | 97 | def table_moving_average(): 98 | return Table('moving_average', metadata_metrics, autoload=True) 99 | 100 | def table_option_iv_by_moneyness(): 101 | return Table('option_iv_by_moneyness', metadata_metrics, autoload=True) 102 | 103 | def table_option_atm_iv(): 104 | return Table('option_atm_iv',metadata_metrics, autoload=True) 105 | 106 | def table_implied_volatilities(): 107 | return Table('implied_volatilities_by_moneyness',metadata_metrics, autoload=True) 108 | 109 | def table_cf_minute_gc(): 110 | return Table('cf_minute',metadata_gc, autoload=True) 111 | 112 | # def table_cf_daily(): 113 | # return Table('cf_day',metadata_dzqh, autoload=True) 114 | 115 | 116 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/implied_vol_curves.py: -------------------------------------------------------------------------------- 1 | from data_access import get_data 2 | from back_test.model.base_option_set import BaseOptionSet 3 | from back_test.model.constant import Util, OptionUtil 4 | from Utilities.PlotUtil import PlotUtil 5 | import matplotlib.pyplot as plt 6 | import datetime 7 | import math 8 | import pandas as pd 9 | 10 | 11 | def get_atm_options(optionset, maturity): 12 | list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1(moneyness_rank=0, maturity=maturity) 13 | atm_call = optionset.select_higher_volume(list_atm_call) 14 | atm_put = optionset.select_higher_volume(list_atm_put) 15 | return atm_call, atm_put 16 | 17 | def get_atm_iv_average(optionset, maturity): 18 | atm_call, atm_put = get_atm_options(optionset,maturity) 19 | iv_call = atm_call.get_implied_vol() 20 | iv_put = atm_put.get_implied_vol() 21 | iv_avg = (iv_call + iv_put) / 2 22 | return iv_avg 23 | 24 | pu = PlotUtil() 25 | start_date = datetime.date(2018,9,12) 26 | end_date = datetime.date(2018,9,13) 27 | nbr_maturity = 0 28 | min_holding = 8 29 | df_daily = get_data.get_comoption_mktdata(start_date, end_date,Util.STR_SR) 30 | optionset = BaseOptionSet(df_data=df_daily,rf=0.015) 31 | optionset.init() 32 | # optionset.go_to(end_date) 33 | 34 | maturity = optionset.select_maturity_date(nbr_maturity, min_holding=min_holding) 35 | iv_htr = optionset.get_atm_iv_by_htbr(maturity) 36 | iv_avg = get_atm_iv_average(optionset, maturity) 37 | 38 | htbr = optionset.get_htb_rate(maturity) 39 | print('iv_htr : ', iv_htr) 40 | print('iv_avg : ', iv_avg) 41 | print('htb rate : ', htbr) 42 | 43 | # curve = optionset.get_implied_vol_curves(maturity) 44 | 45 | # curve_htbr = optionset.get_implied_vol_curves_htbr(maturity) 46 | curve_otm = optionset.get_otm_implied_vol_curve(maturity) 47 | # curve_htbr[Util.PCT_IV_CALL_BY_HTBR] = curve_htbr[Util.PCT_IV_CALL_BY_HTBR].apply(lambda x: None if x<0.05 else x) 48 | # curve_htbr[Util.PCT_IV_PUT_BY_HTBR] = curve_htbr[Util.PCT_IV_PUT_BY_HTBR].apply(lambda x: None if x<0.05 else x) 49 | curve_otm[Util.PCT_IV_OTM_BY_HTBR] = curve_otm[Util.PCT_IV_OTM_BY_HTBR].apply(lambda x: None if x<0.05 else x) 50 | 51 | strikes = curve_otm[Util.AMT_APPLICABLE_STRIKE] 52 | # pu.plot_line_chart(strikes,[list(curve[Util.PCT_IV_CALL]),list(curve[Util.PCT_IV_PUT])],['CALL IV','PUT iv']) 53 | # pu.plot_line_chart(strikes,[list(curve_htbr[Util.PCT_IV_CALL_BY_HTBR]),list(curve_htbr[Util.PCT_IV_PUT_BY_HTBR])],['CALL IV adjusted','PUT IV adjusted']) 54 | pu.plot_line_chart(strikes,[list(curve_otm[Util.PCT_IV_OTM_BY_HTBR])],['IV : '+str(optionset.eval_date)]) 55 | ivs1 = list(curve_otm[Util.PCT_IV_OTM_BY_HTBR]) 56 | optionset.next() 57 | maturity = optionset.select_maturity_date(nbr_maturity, min_holding=min_holding) 58 | iv_htr = optionset.get_atm_iv_by_htbr(maturity) 59 | iv_avg = get_atm_iv_average(optionset, maturity) 60 | 61 | htbr = optionset.get_htb_rate(maturity) 62 | print('iv_htr : ', iv_htr) 63 | print('iv_avg : ', iv_avg) 64 | print('htb rate : ', htbr) 65 | 66 | # curve = optionset.get_implied_vol_curves(maturity) 67 | 68 | # curve_htbr = optionset.get_implied_vol_curves_htbr(maturity) 69 | curve_otm = optionset.get_otm_implied_vol_curve(maturity) 70 | # curve_htbr[Util.PCT_IV_CALL_BY_HTBR] = curve_htbr[Util.PCT_IV_CALL_BY_HTBR].apply(lambda x: None if x<0.05 else x) 71 | # curve_htbr[Util.PCT_IV_PUT_BY_HTBR] = curve_htbr[Util.PCT_IV_PUT_BY_HTBR].apply(lambda x: None if x<0.05 else x) 72 | curve_otm[Util.PCT_IV_OTM_BY_HTBR] = curve_otm[Util.PCT_IV_OTM_BY_HTBR].apply(lambda x: None if x<0.05 else x) 73 | ivs2 = list(curve_otm[Util.PCT_IV_OTM_BY_HTBR]) 74 | 75 | strikes = curve_otm[Util.AMT_APPLICABLE_STRIKE] 76 | # pu.plot_line_chart(strikes,[list(curve[Util.PCT_IV_CALL]),list(curve[Util.PCT_IV_PUT])],['CALL IV','PUT iv']) 77 | # pu.plot_line_chart(strikes,[list(curve_htbr[Util.PCT_IV_CALL_BY_HTBR]),list(curve_htbr[Util.PCT_IV_PUT_BY_HTBR])],['CALL IV adjusted','PUT IV adjusted']) 78 | pu.plot_line_chart(strikes,[list(curve_otm[Util.PCT_IV_OTM_BY_HTBR])],['IV : '+str(optionset.eval_date)]) 79 | pu.plot_line_chart(strikes,[ivs2,ivs1],['IV : '+str(optionset.eval_date),'IV : last day']) 80 | 81 | plt.show() 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /PricingLibrary/tests/binomial_example.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from PricingLibrary.BinomialModel import BinomialTree 3 | from PricingLibrary.BlackCalculator import BlackCalculator 4 | from back_test.model.constant import OptionType, OptionExerciseType, QuantlibUtil 5 | import datetime 6 | import QuantLib as ql 7 | import numpy as np 8 | from Utilities.PlotUtil import PlotUtil 9 | import matplotlib.pyplot as plt 10 | 11 | 12 | pu = PlotUtil() 13 | dt_eval = datetime.date(2017, 1, 1) 14 | dt_maturity = datetime.date(2017, 4, 1) 15 | spot_price = 120 16 | strike_price = 120 17 | volatility = 0.3 # the historical vols or implied vols 18 | dividend_rate = 0 19 | risk_free_rate = 0.03 20 | # steps = 800 21 | maturity_date = QuantlibUtil.to_ql_date(dt_maturity) 22 | 23 | option_type = ql.Option.Put 24 | 25 | day_count = ql.ActualActual() 26 | calendar = ql.NullCalendar() 27 | 28 | calculation_date = QuantlibUtil.to_ql_date(dt_eval) 29 | # print(day_count.yearFraction(calculation_date, maturity_date)) 30 | ql.Settings.instance().evaluationDate = calculation_date 31 | payoff = ql.PlainVanillaPayoff(option_type, strike_price) 32 | settlement = calculation_date 33 | 34 | am_exercise = ql.AmericanExercise(settlement, maturity_date) 35 | american_option = ql.VanillaOption(payoff, am_exercise) 36 | eu_exercise = ql.EuropeanExercise(maturity_date) 37 | european_option = ql.VanillaOption(payoff, eu_exercise) 38 | 39 | spot_handle = ql.QuoteHandle( 40 | ql.SimpleQuote(spot_price) 41 | ) 42 | flat_ts = ql.YieldTermStructureHandle( 43 | ql.FlatForward(calculation_date, risk_free_rate, day_count) 44 | ) 45 | dividend_yield = ql.YieldTermStructureHandle( 46 | ql.FlatForward(calculation_date, dividend_rate, day_count) 47 | ) 48 | flat_vol_ts = ql.BlackVolTermStructureHandle( 49 | ql.BlackConstantVol(calculation_date, calendar, volatility, day_count) 50 | ) 51 | bsm_process = ql.BlackScholesMertonProcess(spot_handle, 52 | dividend_yield, 53 | flat_ts, 54 | flat_vol_ts) 55 | 56 | 57 | list_bm_euro = [] 58 | list_ql_euro = [] 59 | list_bm_ame = [] 60 | list_ql_ame = [] 61 | x = list(np.arange(5,800,10)) 62 | for steps in x: 63 | steps = int(steps) 64 | print(steps) 65 | american_binomial_tree = BinomialTree(steps, dt_eval, dt_maturity, 66 | OptionType.PUT, OptionExerciseType.AMERICAN, spot_price, strike_price, volatility) 67 | american_binomial_tree.initialize() 68 | american_binomial_tree.step_back(0) 69 | list_bm_ame.append(american_binomial_tree.NPV()) 70 | # print(american_binomial_tree.T) 71 | # print("american binomial_tree price", american_binomial_tree.NPV()) 72 | european_binomial_tree = BinomialTree(steps, dt_eval, dt_maturity, 73 | OptionType.PUT, OptionExerciseType.EUROPEAN, spot_price, strike_price, volatility) 74 | european_binomial_tree.initialize() 75 | european_binomial_tree.step_back(0) 76 | # print("european binomial_tree price", european_binomial_tree.NPV()) 77 | list_bm_euro.append(european_binomial_tree.NPV()) 78 | # black = BlackCalculator(dt_eval, dt_maturity, strike_price, OptionType.PUT, spot_price, volatility) 79 | # print("european blackcalculator price", black.NPV()) 80 | 81 | 82 | binomial_engine = ql.BinomialVanillaEngine(bsm_process, "crr", steps) 83 | black_engine = ql.AnalyticEuropeanEngine(bsm_process) 84 | american_option.setPricingEngine(binomial_engine) 85 | # print("american quantlib price(BinomialVanillaEngine)", american_option.NPV()) 86 | european_option.setPricingEngine(binomial_engine) 87 | # print("european quantlib price(BinomialVanillaEngine)", european_option.NPV()) 88 | # european_option.setPricingEngine(black_engine) 89 | # print("european quantlib price(blackcalculator)", european_option.NPV()) 90 | list_ql_euro.append(european_option.NPV()) 91 | list_ql_ame.append(american_option.NPV()) 92 | 93 | 94 | pu.plot_line_chart(x,[list_bm_euro,list_ql_euro],['bimonial euro','quantpib euro'],'steps') 95 | pu.plot_line_chart(x,[list_bm_ame,list_ql_ame],['bimonial ame','quantpib ame'],'steps') 96 | plt.show() -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/bkt_strategy_vol_surface.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | import QuantLib as ql 4 | import matplotlib.pyplot as plt 5 | from back_test.deprecated.OptionPortfolio import * 6 | from matplotlib import cm 7 | 8 | from back_test.deprecated.BktOptionStrategy import BktOptionStrategy 9 | from data_access.get_data import get_50option_mktdata as get_mktdata 10 | 11 | 12 | class BktStrategyVolsurfae(BktOptionStrategy): 13 | 14 | def __init__(self, df_option_metrics, option_invest_pct=0.1): 15 | 16 | BktOptionStrategy.__init__(self, df_option_metrics,rf = 0.0) 17 | self.moneyness = 0 18 | self.option_invest_pct = option_invest_pct 19 | self.cash_reserve_pct = 0.05 20 | self.delta_neutral = False 21 | self.portfolio = None 22 | 23 | def vol_surface(self): 24 | bkt_optionset = self.bkt_optionset 25 | bkt = self.bkt_account 26 | # bkt2 = BktAccount() 27 | idx_event = 0 28 | while bkt_optionset.index < len(bkt_optionset.dt_list): 29 | 30 | evalDate = bkt_optionset.eval_date 31 | black_var_surface = bkt_optionset.get_volsurface_squre('call') 32 | 33 | plt.rcParams['font.sans-serif'] = ['STKaiti'] 34 | plt.rcParams.update({'font.size': 13}) 35 | 36 | plot_years = np.arange(0.1, 0.4, 0.01) 37 | k_min = min(black_var_surface.minStrike(), black_var_surface.maxStrike()) 38 | k_max = max(black_var_surface.minStrike(), black_var_surface.maxStrike()) 39 | plot_strikes = np.arange(k_min + 0.001, k_max - 0.001, 0.01) 40 | fig = plt.figure() 41 | ax = fig.gca(projection='3d') 42 | X, Y = np.meshgrid(plot_strikes, plot_years) 43 | Z = np.array([black_var_surface.blackVol(y, x) 44 | for xr, yr in zip(X, Y) 45 | for x, y in zip(xr, yr)] 46 | ).reshape(len(X), len(X[0])) 47 | surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, 48 | linewidth=0.2) 49 | ax.set_xlabel('strikes') 50 | ax.set_ylabel('maturities') 51 | fig.colorbar(surf, shrink=0.5, aspect=5) 52 | fig.savefig('../save_results/vol_call_'+str(evalDate)+'.png') 53 | 54 | black_var_surface = bkt_optionset.get_volsurface_squre('put') 55 | 56 | plt.rcParams['font.sans-serif'] = ['STKaiti'] 57 | plt.rcParams.update({'font.size': 13}) 58 | 59 | plot_years = np.arange(0.1, 0.4, 0.01) 60 | k_min = min(black_var_surface.minStrike(), black_var_surface.maxStrike()) 61 | k_max = max(black_var_surface.minStrike(), black_var_surface.maxStrike()) 62 | plot_strikes = np.arange(k_min+0.001, k_max-0.001, 0.01) 63 | fig1 = plt.figure() 64 | ax1 = fig1.gca(projection='3d') 65 | X, Y = np.meshgrid(plot_strikes, plot_years) 66 | Z = np.array([black_var_surface.blackVol(y, x) 67 | for xr, yr in zip(X, Y) 68 | for x, y in zip(xr, yr)] 69 | ).reshape(len(X), len(X[0])) 70 | surf = ax1.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, 71 | linewidth=0.2) 72 | ax1.set_xlabel('strikes') 73 | ax1.set_ylabel('maturities') 74 | fig1.colorbar(surf, shrink=0.5, aspect=5) 75 | fig1.savefig('../save_results/vol_put_'+str(evalDate)+'.png') 76 | plt.show() 77 | 78 | bkt_optionset.next() 79 | 80 | 81 | """Back Test Settings""" 82 | start_date = datetime.date(2017, 11, 11) 83 | end_date = datetime.date(2017, 12, 1) 84 | 85 | calendar = ql.China() 86 | daycounter = ql.ActualActual() 87 | util = BktUtil() 88 | 89 | """Collect Mkt Date""" 90 | # df_events = get_eventsdata(start_date, end_date,1) 91 | 92 | df_option_metrics = get_mktdata(start_date, end_date) 93 | 94 | """Run Backtest""" 95 | 96 | bkt_strategy = BktStrategyVolsurfae(df_option_metrics, option_invest_pct=0.2) 97 | bkt_strategy.set_min_holding_days(20) 98 | 99 | bkt_strategy.vol_surface() 100 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/analysis_greeks.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | import QuantLib as ql 3 | import numpy as np 4 | import pandas as pd 5 | import datetime 6 | from PricingLibrary.Options import OptionPlainEuropean 7 | from PricingLibrary.OptionMetrics import OptionMetrics 8 | from PricingLibrary.Evaluation import Evaluation 9 | from Utilities.PlotUtil import PlotUtil 10 | import matplotlib.pyplot as plt 11 | import math 12 | 13 | 14 | pu = PlotUtil() 15 | eval_date = datetime.date(2018, 3, 1) 16 | mdt = datetime.date(2018, 3, 28) 17 | # spot = 2.8840 # 2018-3-1 50etf close price 18 | spot = 2.9 19 | strike = 2.9 20 | strike2 = 3.0 21 | strike3 = 2.8 22 | vol = 0.3 23 | rf = 0.03 24 | maturitydt = ql.Date(mdt.day, mdt.month, mdt.year) 25 | engineType = 'AnalyticEuropeanEngine' 26 | mkt_call = {3.1: 0.0148, 3.0: 0.0317, 2.95: 0.0461, 2.9: 0.0641, 2.85: 0.095, 2.8: 0.127, 2.75: 0.1622} 27 | mkt_put = {3.1: 0.2247, 3.0: 0.1424, 2.95: 0.1076, 2.9: 0.0751, 2.85: 0.0533, 2.8: 0.037, 2.75: 0.0249} 28 | calendar = ql.China() 29 | daycounter = ql.ActualActual() 30 | evalDate = ql.Date(eval_date.day, eval_date.month, eval_date.year) 31 | evaluation = Evaluation(evalDate, daycounter, calendar) 32 | 33 | # calendar = ql.China() 34 | # daycounter = ql.ActualActual() 35 | # evalDate = ql.Date(eval_date.day, eval_date.month, eval_date.year) 36 | # maturitydt = ql.Date(mdt.day, mdt.month, mdt.year) 37 | # evaluation = Evaluation(evalDate, daycounter, calendar) 38 | # 39 | # option_call = OptionPlainEuropean(strike, maturitydt, ql.Option.Call) 40 | # 41 | # option_put = OptionPlainEuropean(strike, maturitydt, ql.Option.Put) 42 | # option_call2 = OptionPlainEuropean(strike2, maturitydt, ql.Option.Call) 43 | # metrics = OptionMetrics(option_call2, rf, engineType).set_evaluation(evaluation) 44 | # iv = metrics.implied_vol(spot, option_price) 45 | # vega = metrics.vega(spot, vol) 46 | # vega2 = metrics.vega_effective(spot,vol) 47 | # rho = metrics.rho(spot,vol) 48 | # print(vega,vega2,rho) 49 | # theta = metrics.theta(spot, iv) 50 | # 51 | # yield_curve = ql.FlatForward(evalDate, 52 | # rf, 53 | # daycounter) 54 | # T = yield_curve.dayCounter().yearFraction(evalDate, 55 | # maturitydt) 56 | # discount = yield_curve.discount(T) 57 | # strikepayoff = ql.PlainVanillaPayoff(ql.Option.Call, strike) 58 | # stddev = vol * math.sqrt(T) 59 | # black = ql.BlackCalculator(strikepayoff, spot, stddev, discount) 60 | # theta2 = black.theta(spot,T)/252 61 | # print(iv, vega, theta,theta2) 62 | 63 | yield_curve = ql.FlatForward(evalDate, 64 | rf, 65 | daycounter) 66 | 67 | option_call = OptionPlainEuropean(strike, maturitydt, ql.Option.Call, mkt_call[strike]) 68 | option_call2 = OptionPlainEuropean(strike2, maturitydt, ql.Option.Call, mkt_call[strike2]) #虚值 69 | option_call3 = OptionPlainEuropean(strike3,maturitydt,ql.Option.Call,mkt_call[strike3]) #实值 70 | 71 | metricscall = OptionMetrics(option_call, rf, engineType).set_evaluation(evaluation) 72 | metricscall2 = OptionMetrics(option_call2, rf, engineType).set_evaluation(evaluation) 73 | metricscall3 = OptionMetrics(option_call3, rf, engineType).set_evaluation(evaluation) 74 | iv_call = metricscall.implied_vol(spot, mkt_call[strike]) 75 | iv_call2 = metricscall2.implied_vol(spot, mkt_call[strike2]) 76 | iv_call3 = metricscall3.implied_vol(spot, mkt_call[strike3]) 77 | 78 | prices = [] 79 | prices2 = [] 80 | prices3 = [] 81 | x = [] 82 | # t = 0 83 | while evalDate < maturitydt: 84 | p = metricscall.theta(spot,iv_call) 85 | p2 = metricscall2.theta(spot,iv_call2) 86 | p3 = metricscall3.theta(spot,iv_call3) 87 | prices.append(p) 88 | prices2.append(p2) 89 | prices3.append(p3) 90 | T = yield_curve.dayCounter().yearFraction(evalDate,maturitydt)*365.0 91 | x.append(T) 92 | evalDate = calendar.advance(evalDate, ql.Period(1, ql.Days)) 93 | evaluation = Evaluation(evalDate, daycounter, calendar) 94 | metricscall.set_evaluation(evaluation) 95 | metricscall2.set_evaluation(evaluation) 96 | metricscall3.set_evaluation(evaluation) 97 | # t += 1 98 | df_thetas = pd.DataFrame({'t':x,'theta-atm':prices,'theta-otm':prices2,'theta-itm':prices3}).sort_values(by='t') 99 | df_thetas.to_csv('../save_results/df_thetas.csv') 100 | 101 | -------------------------------------------------------------------------------- /data_access/craw_data_contractsinfo.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from sqlalchemy import create_engine, MetaData, Table, Column, TIMESTAMP 4 | import datetime 5 | import pandas as pd 6 | from WindPy import w 7 | import os 8 | 9 | 10 | from data_access.db_data_collection import DataCollection 11 | 12 | w.start() 13 | engine = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/mktdata', 14 | echo=False) 15 | conn = engine.connect() 16 | metadata = MetaData(engine) 17 | option_contracts = Table('option_contracts', metadata, autoload=True) 18 | future_contracts = Table('future_contracts', metadata, autoload=True) 19 | dc = DataCollection() 20 | ##############################option_contracts#################################################### 21 | 22 | db_datas = dc.table_option_contracts().wind_options_50etf() 23 | for db_data in db_datas: 24 | id_instrument = db_data['id_instrument'] 25 | res = option_contracts.select(option_contracts.c.id_instrument == id_instrument).execute() 26 | if res.rowcount > 0: continue 27 | try: 28 | conn.execute(option_contracts.insert(), db_data) 29 | print('option_contracts -- inserted into data base succefully') 30 | except Exception as e: 31 | print(e) 32 | print(db_data) 33 | continue 34 | 35 | db_datas = dc.table_option_contracts().wind_options_m() 36 | for db_data in db_datas: 37 | id_instrument = db_data['id_instrument'] 38 | res = option_contracts.select(option_contracts.c.id_instrument == id_instrument).execute() 39 | if res.rowcount > 0: continue 40 | try: 41 | conn.execute(option_contracts.insert(), db_data) 42 | print('option_contracts -- inserted into data base succefully') 43 | 44 | except Exception as e: 45 | print(e) 46 | print(db_data) 47 | continue 48 | 49 | db_datas = dc.table_option_contracts().wind_options_sr() 50 | for db_data in db_datas: 51 | id_instrument = db_data['id_instrument'] 52 | res = option_contracts.select(option_contracts.c.id_instrument == id_instrument).execute() 53 | if res.rowcount > 0: continue 54 | try: 55 | conn.execute(option_contracts.insert(), db_data) 56 | print('option_contracts -- inserted into data base succefully') 57 | 58 | except Exception as e: 59 | print(e) 60 | print(db_data) 61 | continue 62 | 63 | ##############################future_contracts#################################################### 64 | category_code = "IF.CFE" 65 | nbr_multiplier = 300 66 | db_datas = dc.table_future_contracts().wind_future_contracts(category_code,nbr_multiplier) 67 | for db_data in db_datas: 68 | id_instrument = db_data['id_instrument'] 69 | res = future_contracts.select(future_contracts.c.id_instrument == id_instrument).execute() 70 | if res.rowcount > 0: continue 71 | try: 72 | conn.execute(future_contracts.insert(), db_data) 73 | print('future_contracts -- inserted into data base succefully') 74 | 75 | except Exception as e: 76 | print(e) 77 | print(db_data) 78 | continue 79 | 80 | category_code = "IH.CFE" 81 | nbr_multiplier = 300 82 | db_datas = dc.table_future_contracts().wind_future_contracts(category_code,nbr_multiplier) 83 | for db_data in db_datas: 84 | id_instrument = db_data['id_instrument'] 85 | res = future_contracts.select(future_contracts.c.id_instrument == id_instrument).execute() 86 | if res.rowcount > 0: continue 87 | try: 88 | conn.execute(future_contracts.insert(), db_data) 89 | print('future_contracts -- inserted into data base succefully') 90 | 91 | except Exception as e: 92 | print(e) 93 | print(db_data) 94 | continue 95 | 96 | category_code = "IC.CFE" 97 | nbr_multiplier = 200 98 | db_datas = dc.table_future_contracts().wind_future_contracts(category_code,nbr_multiplier) 99 | for db_data in db_datas: 100 | id_instrument = db_data['id_instrument'] 101 | res = future_contracts.select(future_contracts.c.id_instrument == id_instrument).execute() 102 | if res.rowcount > 0: continue 103 | try: 104 | conn.execute(future_contracts.insert(), db_data) 105 | print('future_contracts -- inserted into data base succefully') 106 | 107 | except Exception as e: 108 | print(e) 109 | print(db_data) 110 | continue -------------------------------------------------------------------------------- /OptionStrategyLib/OptionStrategy/historical_statistics/vol_filtration.py: -------------------------------------------------------------------------------- 1 | from back_test.model.base_option_set import BaseOptionSet 2 | from back_test.model.base_account import BaseAccount 3 | from data_access import get_data 4 | import back_test.model.constant as c 5 | import datetime 6 | import numpy as np 7 | from OptionStrategyLib.OptionReplication.synthetic_option import SytheticOption 8 | from Utilities.PlotUtil import PlotUtil 9 | import matplotlib.pyplot as plt 10 | import pandas as pd 11 | from Utilities.timebase import LLKSR,KALMAN,LLT 12 | 13 | 14 | pu = PlotUtil() 15 | start_date = datetime.date(2015, 1, 1) 16 | end_date = datetime.date(2018, 8, 8) 17 | dt_histvol = start_date - datetime.timedelta(days=90) 18 | min_holding = 15 19 | init_fund = c.Util.BILLION 20 | slippage = 0 21 | m = 1 # 期权notional倍数 22 | 23 | """ commodity option """ 24 | name_code = name_code_option = c.Util.STR_M 25 | df_metrics = get_data.get_comoption_mktdata(start_date, end_date,name_code) 26 | df_future_c1_daily = get_data.get_future_c1_by_option_daily(dt_histvol, end_date, name_code, min_holding) 27 | 28 | """ 50ETF option """ 29 | # name_code = c.Util.STR_IH 30 | # name_code_option = c.Util.STR_50ETF 31 | # df_metrics = get_data.get_50option_mktdata(start_date, end_date) 32 | # df_future_c1_daily = get_data.get_mktdata_cf_c1_daily(dt_histvol, end_date, name_code) 33 | 34 | """ 隐含波动率 """ 35 | df_iv = get_data.get_iv_by_moneyness(dt_histvol,end_date,name_code_option) 36 | df_iv_call = df_iv[df_iv[c.Util.CD_OPTION_TYPE]=='call'] 37 | df_iv_put = df_iv[df_iv[c.Util.CD_OPTION_TYPE]=='put'] 38 | df_data = df_iv_call[[c.Util.DT_DATE,c.Util.PCT_IMPLIED_VOL]].rename(columns={c.Util.PCT_IMPLIED_VOL:'iv_call'}) 39 | df_data = df_data.join(df_iv_put[[c.Util.DT_DATE,c.Util.PCT_IMPLIED_VOL]].set_index(c.Util.DT_DATE),on=c.Util.DT_DATE,how='outer')\ 40 | .rename(columns={c.Util.PCT_IMPLIED_VOL:'iv_put'}) 41 | df_data = df_data.dropna().reset_index(drop=True) 42 | df_data.loc[:,'average_iv'] = (df_data.loc[:,'iv_call'] + df_data.loc[:,'iv_put'])/2 43 | 44 | """ Volatility Statistics """ 45 | df_iv_stats = df_data[[c.Util.DT_DATE, 'average_iv']] 46 | df_iv_stats.loc[:,'std'] = np.log(df_iv_stats['average_iv']).diff() 47 | 48 | """ 1. MA """ 49 | 50 | df_iv_stats.loc[:,'ma_20'] = c.Statistics.moving_average(df_iv_stats['average_iv'], n=20) 51 | df_iv_stats.loc[:,'ma_10'] = c.Statistics.moving_average(df_iv_stats['average_iv'], n=10) 52 | df_iv_stats.loc[:,'ma_5'] = c.Statistics.moving_average(df_iv_stats['average_iv'], n=5) 53 | df_iv_stats['diff_20'] = df_iv_stats['ma_20'].diff() 54 | df_iv_stats['diff_10'] = df_iv_stats['ma_10'].diff() 55 | df_iv_stats['diff_5'] = df_iv_stats['ma_5'].diff() 56 | 57 | """ 2. Filtration :LLKSR """ 58 | df_iv_stats['LLKSR_20'] = LLKSR(df_iv_stats['average_iv'], 20) 59 | df_iv_stats['LLKSR10'] = LLKSR(df_iv_stats['average_iv'], 10) 60 | df_iv_stats['LLKSR_5'] = LLKSR(df_iv_stats['average_iv'], 5) 61 | df_iv_stats['diff_20'] = df_iv_stats['LLKSR_20'].diff() 62 | df_iv_stats['diff_10'] = df_iv_stats['LLKSR10'].diff() 63 | df_iv_stats['diff_5'] = df_iv_stats['LLKSR_5'].diff() 64 | 65 | """ 3. Filtration : KALMAN """ 66 | df_iv_stats['KALMAN_20'] = KALMAN(df_iv_stats['average_iv'], 20) 67 | df_iv_stats['KALMAN_10'] = KALMAN(df_iv_stats['average_iv'], 10) 68 | df_iv_stats['KALMAN_5'] = KALMAN(df_iv_stats['average_iv'], 5) 69 | df_iv_stats['diff_20'] = df_iv_stats['KALMAN_20'].diff() 70 | df_iv_stats['diff_10'] = df_iv_stats['KALMAN_10'].diff() 71 | df_iv_stats['diff_5'] = df_iv_stats['KALMAN_5'].diff() 72 | 73 | """ 4. Filtration : LLT """ 74 | df_iv_stats['LLT_20'] = LLT(df_iv_stats['average_iv'], 20) 75 | df_iv_stats['LLT_10'] = LLT(df_iv_stats['average_iv'], 10) 76 | df_iv_stats['LLT_5'] = LLT(df_iv_stats['average_iv'], 5) 77 | df_iv_stats['diff_20'] = df_iv_stats['LLT_20'].diff() 78 | df_iv_stats['diff_10'] = df_iv_stats['LLT_10'].diff() 79 | df_iv_stats['diff_5'] = df_iv_stats['LLT_5'].diff() 80 | 81 | df_iv_stats = df_iv_stats.set_index(c.Util.DT_DATE) 82 | 83 | pu.plot_line_chart(list(df_iv_stats.index), [list(df_iv_stats['average_iv']),list(df_iv_stats['std'])], 84 | ['iv','chg of vol']) 85 | pu.plot_line_chart(list(df_iv_stats.index), [list(df_iv_stats['ma_5']), list(df_iv_stats['KALMAN_5']), list(df_iv_stats['LLKSR_5']), list(df_iv_stats['LLT_5'])], 86 | ['ma_5','KALMAN_5','LLKSR_5','LLT_5']) 87 | pu.plot_line_chart(list(df_iv_stats.index), [list(df_iv_stats['ma_20']), list(df_iv_stats['KALMAN_20']), list(df_iv_stats['LLKSR_20']), list(df_iv_stats['LLT_20'])], 88 | ['ma_20','KALMAN_20','LLKSR_20','LLT_20']) 89 | plt.show() 90 | -------------------------------------------------------------------------------- /data_access/metrics_calculate_run.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import QuantLib as ql 3 | from back_test.deprecated.BktOptionSet import BktOptionSet 4 | 5 | from OptionStrategyLib.OptionStrategy.bkt_strategy_ivbymoneyness import BktStrategyMoneynessVol 6 | from Utilities import admin_write_util as admin 7 | from back_test.deprecated.BktUtil import BktUtil 8 | from data_access.get_data import get_50option_mktdata, get_comoption_mktdata 9 | 10 | start_date = datetime.date(2018,8,27) 11 | end_date = datetime.date(2018,8,31) 12 | 13 | calendar = ql.China() 14 | daycounter = ql.ActualActual() 15 | util = BktUtil() 16 | 17 | 18 | optionMetrics = admin.table_option_metrics() 19 | conn = admin.conn_metrics() 20 | 21 | name_option = 'sr' 22 | print(name_option) 23 | df_option_metrics = get_comoption_mktdata(start_date,end_date,name_option) 24 | bkt_optionset = BktOptionSet(df_option_metrics) 25 | while bkt_optionset.index <= len(bkt_optionset.dt_list): 26 | evalDate = bkt_optionset.eval_date 27 | option_metrics = bkt_optionset.collect_option_metrics() 28 | try: 29 | for r in option_metrics: 30 | res = optionMetrics.select((optionMetrics.c.id_instrument == r['id_instrument']) 31 | &(optionMetrics.c.dt_date == r['dt_date'])).execute() 32 | if res.rowcount > 0: 33 | optionMetrics.delete((optionMetrics.c.id_instrument == r['id_instrument']) 34 | &(optionMetrics.c.dt_date == r['dt_date'])).execute() 35 | conn.execute(optionMetrics.insert(), r) 36 | print(evalDate, ' : option metrics -- inserted into data base succefully') 37 | except Exception as e: 38 | print(e) 39 | pass 40 | if evalDate == bkt_optionset.end_date: 41 | break 42 | bkt_optionset.next() 43 | 44 | 45 | name_option = 'm' 46 | print(name_option) 47 | df_option_metrics = get_comoption_mktdata(start_date,end_date,name_option) 48 | bkt_optionset = BktOptionSet(df_option_metrics) 49 | 50 | while bkt_optionset.index <= len(bkt_optionset.dt_list): 51 | evalDate = bkt_optionset.eval_date 52 | option_metrics = bkt_optionset.collect_option_metrics() 53 | try: 54 | for r in option_metrics: 55 | res = optionMetrics.select((optionMetrics.c.id_instrument == r['id_instrument']) 56 | &(optionMetrics.c.dt_date == r['dt_date'])).execute() 57 | if res.rowcount > 0: 58 | optionMetrics.delete((optionMetrics.c.id_instrument == r['id_instrument']) 59 | &(optionMetrics.c.dt_date == r['dt_date'])).execute() 60 | conn.execute(optionMetrics.insert(), r) 61 | print(evalDate, ' : option metrics -- inserted into data base succefully') 62 | except Exception as e: 63 | print(e) 64 | pass 65 | if evalDate == bkt_optionset.end_date: 66 | break 67 | bkt_optionset.next() 68 | 69 | print('50etf') 70 | df_option_metrics = get_50option_mktdata(start_date,end_date) 71 | bkt_optionset = BktOptionSet(df_option_metrics) 72 | 73 | while bkt_optionset.index <= len(bkt_optionset.dt_list): 74 | evalDate = bkt_optionset.eval_date 75 | option_metrics = bkt_optionset.collect_option_metrics() 76 | try: 77 | for r in option_metrics: 78 | res = optionMetrics.select((optionMetrics.c.id_instrument == r['id_instrument']) 79 | &(optionMetrics.c.dt_date == r['dt_date'])).execute() 80 | if res.rowcount > 0: 81 | optionMetrics.delete((optionMetrics.c.id_instrument == r['id_instrument']) 82 | &(optionMetrics.c.dt_date == r['dt_date'])).execute() 83 | conn.execute(optionMetrics.insert(), r) 84 | print(evalDate, ' : option metrics -- inserted into data base succefully') 85 | except Exception as e: 86 | print(e) 87 | pass 88 | if evalDate == bkt_optionset.end_date: 89 | break 90 | bkt_optionset.next() 91 | 92 | """ Calculate ATM Implied Volatility""" 93 | print('50 atm iv') 94 | df_option_metrics = get_50option_mktdata(start_date, end_date) 95 | 96 | bkt = BktStrategyMoneynessVol(df_option_metrics) 97 | bkt.set_min_holding_days(8) # TODO: 选择期权最低到期日 98 | res = bkt.ivs_mdt1_run() 99 | table = admin.table_option_atm_iv() 100 | for r in res: 101 | try: 102 | conn.execute(table.insert(), r) 103 | # r.to_sql(name='option_atm_iv', con=engine, if_exists='append', index=False) 104 | except Exception as e: 105 | print(e) 106 | continue -------------------------------------------------------------------------------- /PricingLibrary/tests/quantlib_american_options.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2004, 2005, 2006, 2007 StatPro Italia srl 2 | # 3 | # This file is part of QuantLib, a free-software/open-source library 4 | # for financial quantitative analysts and developers - http://quantlib.org/ 5 | # 6 | # QuantLib is free software: you can redistribute it and/or modify it under the 7 | # terms of the QuantLib license. You should have received a copy of the 8 | # license along with this program; if not, please email 9 | # . The license is also available online at 10 | # . 11 | # 12 | # This program is distributed in the hope that it will be useful, but WITHOUT 13 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | # FOR A PARTICULAR PURPOSE. See the license for more details. 15 | from PricingLibrary.EngineQuantlib import QlBAW,QlBlackFormula,QlBinomial 16 | from QuantLib import * 17 | import datetime 18 | from back_test.model.constant import QuantlibUtil,OptionType,OptionExerciseType 19 | 20 | # global data 21 | todaysDate = Date(17,May,1998) 22 | Settings.instance().evaluationDate = todaysDate 23 | settlementDate = Date(17,May,1998) 24 | riskFreeRate = FlatForward(settlementDate, 0.06, ActualActual()) 25 | 26 | # option parameters 27 | exercise = AmericanExercise(settlementDate, Date(17,May,1999)) 28 | payoff = PlainVanillaPayoff(Option.Put, 40.0) 29 | 30 | # market data 31 | underlying = SimpleQuote(36.0) 32 | volatility = BlackConstantVol(todaysDate, China(), 0.20, ActualActual()) 33 | dividendYield = FlatForward(settlementDate, 0.00, ActualActual()) 34 | 35 | dt_settlement = QuantlibUtil.to_dt_date(settlementDate) 36 | dt_maturity = QuantlibUtil.to_dt_date(Date(17,May,1999)) 37 | spot = 36.0 38 | strike = 40.0 39 | ql_baw = QlBAW(dt_settlement,dt_maturity,OptionType.PUT,OptionExerciseType.AMERICAN,spot,strike,rf=0.06) 40 | ql_bonomial = QlBinomial(dt_settlement,dt_maturity,OptionType.PUT,OptionExerciseType.AMERICAN,spot,strike,rf=0.06) 41 | # report 42 | header = '%19s' % 'method' + ' |' + \ 43 | ' |'.join(['%17s' % tag for tag in ['value', 44 | 'estimated error', 45 | 'actual error' ] ]) 46 | print(header) 47 | print('-'*len(header)) 48 | 49 | refValue = None 50 | def report(method, x, dx = None): 51 | e = '%.4f' % abs(x-refValue) 52 | x = '%.5f' % x 53 | if dx: 54 | dx = '%.4f' % dx 55 | else: 56 | dx = 'n/a' 57 | print('%19s' % method + ' |' + \ 58 | ' |'.join(['%17s' % y for y in [x, dx, e] ])) 59 | 60 | # good to go 61 | 62 | process = BlackScholesMertonProcess(QuoteHandle(underlying), 63 | YieldTermStructureHandle(dividendYield), 64 | YieldTermStructureHandle(riskFreeRate), 65 | BlackVolTermStructureHandle(volatility)) 66 | 67 | option = VanillaOption(payoff, exercise) 68 | 69 | refValue = 4.48667344 70 | report('reference value',refValue) 71 | 72 | # method: analytic 73 | 74 | option.setPricingEngine(BaroneAdesiWhaleyEngine(process)) 75 | # report('Barone-Adesi-Whaley',option.NPV()) 76 | report('Barone-Adesi-Whaley',option.impliedVolatility(refValue,process)) 77 | 78 | # option.setPricingEngine(BjerksundStenslandEngine(process)) 79 | # report('Bjerksund-Stensland',option.NPV()) 80 | 81 | # method: finite differences 82 | timeSteps = 800 83 | gridPoints = 800 84 | 85 | # option.setPricingEngine(FDAmericanEngine(process,timeSteps,gridPoints)) 86 | # report('finite differences',option.NPV()) 87 | 88 | # method: binomial 89 | timeSteps = 800 90 | 91 | # option.setPricingEngine(BinomialVanillaEngine(process,'jr',timeSteps)) 92 | # report('binomial (JR)',option.NPV()) 93 | 94 | option.setPricingEngine(BinomialVanillaEngine(process,'crr',timeSteps)) 95 | # report('binomial (CRR)',option.NPV()) 96 | report('binomial (CRR)',option.impliedVolatility(refValue,process)) 97 | 98 | # option.setPricingEngine(BinomialVanillaEngine(process,'eqp',timeSteps)) 99 | # report('binomial (EQP)',option.NPV()) 100 | 101 | # option.setPricingEngine(BinomialVanillaEngine(process,'trigeorgis',timeSteps)) 102 | # report('bin. (Trigeorgis)',option.NPV()) 103 | 104 | # option.setPricingEngine(BinomialVanillaEngine(process,'tian',timeSteps)) 105 | # report('binomial (Tian)',option.NPV()) 106 | 107 | # option.setPricingEngine(BinomialVanillaEngine(process,'lr',timeSteps)) 108 | # report('binomial (LR)',option.NPV()) 109 | 110 | 111 | report('binomial (self)',ql_bonomial.estimate_vol(refValue)) 112 | report('BAW (self)',ql_baw.estimate_vol(refValue)) -------------------------------------------------------------------------------- /regular_reports/otc_index_future_basis.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import * 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import sessionmaker 4 | from mpl_toolkits.mplot3d import Axes3D 5 | import matplotlib as mpl 6 | from matplotlib import cm as plt_cm 7 | from mpl_toolkits.axes_grid1 import host_subplot 8 | import datetime 9 | import pandas as pd 10 | import numpy as np 11 | from WindPy import w 12 | from data_access.db_tables import DataBaseTables as dbt 13 | import matplotlib.pyplot as plt 14 | from Utilities.PlotUtil import PlotUtil 15 | import QuantLib as ql 16 | 17 | ########################################################################################### 18 | w.start() 19 | pu = PlotUtil() 20 | plt.rcParams['font.sans-serif'] = ['STKaiti'] 21 | plt.rcParams.update({'font.size': 11}) 22 | engine = create_engine('mysql+pymysql://guest:passw0rd@101.132.148.152/mktdata', 23 | echo=False) 24 | conn = engine.connect() 25 | metadata = MetaData(engine) 26 | Session = sessionmaker(bind=engine) 27 | sess = Session() 28 | index_mkt = Table('indexes_mktdata', metadata, autoload=True) 29 | future_mkt = Table('futures_mktdata', metadata, autoload=True) 30 | FutureMkt = dbt.FutureMkt 31 | IndexMkt = dbt.IndexMkt 32 | index_ids = ['index_300sh','index_50sh','index_500sh'] 33 | future_codes = ['IF','IH','IC'] 34 | index_names = ['沪深300指数','上证50指数','中证500指数'] 35 | # indexid = 'index_300sh' 36 | # futurescode = 'IF' 37 | ############################################################################################ 38 | # Eval Settings 39 | eval_date = datetime.date(2018, 1, 29) 40 | evalDate = eval_date.strftime("%Y-%m-%d") 41 | 42 | hist_date = datetime.date(2016, 1, 1) 43 | 44 | ############################################################################################# 45 | for idx_index,indexid in enumerate(index_ids): 46 | futurescode = future_codes[idx_index] 47 | data = w.wsd(futurescode+'.CFE', "trade_hiscode", hist_date.strftime('%Y-%m-%d'), evalDate, "") 48 | id_cores = [] 49 | for idx,dt in enumerate(data.Times): 50 | name = data.Data[0][idx] 51 | id_core = name[0:2] + "_" + name[2:6] 52 | id_cores.append({'dt_date':dt,'id_instrument':id_core}) 53 | future_df = pd.DataFrame(id_cores) 54 | query_index = sess.query(IndexMkt.id_instrument,IndexMkt.dt_date,IndexMkt.amt_close)\ 55 | .filter(IndexMkt.dt_date >= hist_date)\ 56 | .filter(IndexMkt.dt_date <= evalDate) 57 | 58 | query_future = sess.query(FutureMkt.id_instrument,FutureMkt.dt_date,FutureMkt.amt_close)\ 59 | .filter(FutureMkt.dt_date >= hist_date)\ 60 | .filter(FutureMkt.dt_date <= evalDate)\ 61 | .filter(FutureMkt.flag_night == -1)\ 62 | .filter(FutureMkt.datasource == 'wind')\ 63 | .filter(FutureMkt.name_code == futurescode[0:2]) 64 | 65 | 66 | index_df = pd.read_sql(query_index.statement,query_index.session.bind) 67 | futuremkt_df = pd.read_sql(query_future.statement,query_future.session.bind) 68 | # print(index_df) 69 | # print(futuremkt_df) 70 | indexsh_df = index_df[index_df['id_instrument']==indexid].reset_index() 71 | indexsh_df = indexsh_df.rename(columns = {'id_instrument':'id_index','amt_close':'amt_index_price'}) 72 | 73 | future_df = future_df.merge(futuremkt_df,left_on=['id_instrument','dt_date'],right_on=['id_instrument','dt_date'], 74 | how='left') 75 | future_df = future_df.rename(columns = {'id_instrument':'id_future','amt_close':'amt_future_price'}) 76 | 77 | basis_df = future_df.join(indexsh_df.set_index('dt_date'),on='dt_date',how='left') 78 | basis_df['basis'] = basis_df['amt_index_price']-basis_df['amt_future_price'] 79 | basis_df['pct_basis'] = basis_df['basis']/basis_df['amt_index_price'] 80 | 81 | basis_df_c = basis_df[basis_df['dt_date']==eval_date] 82 | print(indexid,' basis : ',basis_df_c) 83 | x = basis_df['dt_date'].tolist() 84 | basis_set = [basis_df['amt_future_price'].tolist(), 85 | basis_df['amt_index_price'].tolist(), 86 | basis_df['basis'].tolist()] 87 | indexname = index_names[idx_index] 88 | ldgs = [futurescode+'主力合约价格', indexname+'价格', '基差(右)'] 89 | 90 | fig1 = plt.figure() 91 | host = host_subplot(111) 92 | par = host.twinx() 93 | host.set_xlabel("日期") 94 | p1, = host.plot(x, basis_set[0], label=ldgs[0],color=pu.colors[0],linestyle=pu.lines[0], linewidth=2) 95 | p2, = host.plot(x, basis_set[1], label=ldgs[1],color=pu.colors[1],linestyle=pu.lines[1], linewidth=2) 96 | # basis_max = max(basis_set[2]) 97 | par.fill_between(x, 0, basis_set[2],label=ldgs[2],color=pu.colors[2]) 98 | 99 | host.legend(bbox_to_anchor=(0., 1.02, 1., .202), loc=3, 100 | ncol=3, mode="expand", borderaxespad=0.) 101 | fig1.set_size_inches((8, 6)) 102 | fig1.savefig('../data/otc_basis_' + indexid + '_' + str(hist_date) + ' - ' + str(evalDate) + '.png', dpi=300, 103 | format='png') 104 | 105 | plt.show() -------------------------------------------------------------------------------- /OptionStrategyLib/example_svi_calibration.py: -------------------------------------------------------------------------------- 1 | import math 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import datetime 6 | import os 7 | import pickle 8 | import QuantLib as ql 9 | from WindPy import w 10 | from sqlalchemy import * 11 | from sqlalchemy.orm import sessionmaker 12 | from data_access.db_tables import DataBaseTables as dbt 13 | from OptionStrategyLib.calibration import SVICalibration 14 | w.start() 15 | 16 | ################################################################################################## 17 | evalDate = datetime.date(2017,12,8) 18 | ql_evalDate = ql.Date(evalDate.day,evalDate.month,evalDate.year) 19 | rf = 0.03 20 | ################################################################################################## 21 | 22 | engine = create_engine('mysql+pymysql://guest:passw0rd@101.132.148.152/mktdata', echo=False) 23 | conn = engine.connect() 24 | metadata = MetaData(engine) 25 | Session = sessionmaker(bind=engine) 26 | sess = Session() 27 | options_mkt = Table('options_mktdata', metadata, autoload=True) 28 | futures_mkt = Table('futures_mktdata', metadata, autoload=True) 29 | options = Table('option_contracts', metadata, autoload=True) 30 | Index_mkt = dbt.IndexMkt 31 | Option_mkt = dbt.OptionMkt 32 | Option_contracts = dbt.Options 33 | svicalibration = SVICalibration(evalDate) 34 | calendar = ql.China() 35 | daycounter = ql.ActualActual() 36 | ql.Settings.instance().evaluationDate = ql_evalDate 37 | ################################################################################################## 38 | query_option = sess.query(Option_mkt.id_instrument, 39 | Option_mkt.cd_option_type, 40 | Option_mkt.amt_strike, 41 | Option_contracts.dt_maturity, 42 | Option_mkt.amt_close, 43 | Option_mkt.pct_implied_vol)\ 44 | .join(Option_contracts,Option_mkt.id_instrument==Option_contracts.id_instrument)\ 45 | .filter(Option_mkt.dt_date == evalDate)\ 46 | .filter(Option_mkt.flag_night == -1)\ 47 | .filter(Option_mkt.datasource == 'wind') 48 | 49 | query_etf = sess.query(Index_mkt.amt_close)\ 50 | .filter(Index_mkt.dt_date == evalDate)\ 51 | .filter(Index_mkt.id_instrument == 'index_50etf') 52 | 53 | df_option = pd.read_sql(query_option.statement,query_option.session.bind) 54 | df_option = df_option[df_option['id_instrument'].str[-1] != 'A'] 55 | df_50etf = pd.read_sql(query_etf.statement,query_etf.session.bind) 56 | df_option['underlying_prices'] = [df_50etf['amt_close'].iloc[0]]*len(df_option) 57 | df_option['risk_free_rates'] = [rf]*len(df_option) 58 | # df_option['add'] = df_option['id_instrument'][-1] 59 | 60 | for (idx,row) in df_option.iterrows(): 61 | optiontype = row['cd_option_type'] 62 | if optiontype == 'call': ql_optiontype = ql.Option.Call 63 | else: ql_optiontype = ql.Option.Put 64 | id = row['id_instrument'] 65 | mdt = row['dt_maturity'] 66 | ql_mdt = ql.Date(mdt.day,mdt.month,mdt.year) 67 | strike = row['amt_strike'] 68 | spot = row['underlying_prices'] 69 | close = row['amt_close'] 70 | exercise = ql.EuropeanExercise(ql_mdt) 71 | payoff = ql.PlainVanillaPayoff(ql_optiontype, strike) 72 | option = ql.EuropeanOption(payoff, exercise) 73 | flat_vol_ts = ql.BlackVolTermStructureHandle( 74 | ql.BlackConstantVol(ql_evalDate, calendar, 0.0, daycounter)) 75 | dividend_ts = ql.YieldTermStructureHandle(ql.FlatForward(ql_evalDate, 0.0, daycounter)) 76 | yield_ts = ql.YieldTermStructureHandle(ql.FlatForward(ql_evalDate, row['risk_free_rates'], daycounter)) 77 | process = ql.BlackScholesMertonProcess(ql.QuoteHandle(ql.SimpleQuote(spot)), dividend_ts, yield_ts, 78 | flat_vol_ts) 79 | option.setPricingEngine(ql.AnalyticEuropeanEngine(process)) 80 | 81 | try: 82 | implied_vol = option.impliedVolatility(close, process, 1.0e-4, 300, 0.0, 10.0) 83 | except RuntimeError: 84 | implied_vol = 0.0 85 | df_option['pct_implied_vol'].loc[idx] = implied_vol 86 | 87 | print(df_option) 88 | df_option = df_option[df_option['pct_implied_vol'] > 0 ] 89 | # c1 = (df_option['cd_option_type'] == 'call') & (df_option['amt_strike']>=df_option['underlying_prices']) 90 | # c2 = (df_option['cd_option_type'] == 'put') & (df_option['amt_strike']<=df_option['underlying_prices']) 91 | # df_option = df_option[c1|c2] 92 | 93 | df_option = df_option[df_option['cd_option_type']=='call'] 94 | params_dict = svicalibration.calibrate_rawsvi(df_option['amt_strike'], 95 | df_option['dt_maturity'], 96 | df_option['underlying_prices'], 97 | df_option['amt_close'], 98 | df_option['pct_implied_vol'], 99 | df_option['risk_free_rates']) 100 | print(params_dict) 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /data_access/export_data.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine, MetaData, Table 2 | from sqlalchemy.orm import sessionmaker 3 | import pandas as pd 4 | import datetime 5 | 6 | 7 | engine = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/mktdata', echo=False) 8 | metadata = MetaData(engine) 9 | 10 | engine_intraday = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/mktdata_intraday', echo=False) 11 | metadata_intraday = MetaData(engine_intraday) 12 | 13 | engine_dzqh = create_engine('mysql+pymysql://readonly:passw0rd@101.132.148.152/dzqh', echo=False) 14 | metadata_dzqh = MetaData(engine_dzqh) 15 | 16 | def conn_dzqh(): 17 | return engine_dzqh.connect() 18 | 19 | def conn_mktdata(): 20 | return engine.connect() 21 | 22 | def conn_intraday(): 23 | return engine_intraday.connect() 24 | 25 | def session_dzqh(): 26 | Session = sessionmaker(bind=engine_dzqh) 27 | return Session() 28 | 29 | def session_mktdata(): 30 | Session = sessionmaker(bind=engine) 31 | return Session() 32 | 33 | def session_intraday(): 34 | Session = sessionmaker(bind=engine_intraday) 35 | return Session() 36 | 37 | def table_options_mktdata(): 38 | return Table('options_mktdata', metadata, autoload=True) 39 | 40 | def table_futures_mktdata(): 41 | return Table('futures_mktdata', metadata, autoload=True) 42 | 43 | def table_futures_institution_positions(): 44 | return Table('futures_institution_positions', metadata, autoload=True) 45 | 46 | def table_indexes_mktdata(): 47 | return Table('indexes_mktdata', metadata, autoload=True) 48 | 49 | def table_option_contracts(): 50 | return Table('option_contracts', metadata, autoload=True) 51 | 52 | def table_future_contracts(): 53 | return Table('future_contracts', metadata, autoload=True) 54 | 55 | def table_stocks_mktdata(): 56 | return Table('stocks_mktdata', metadata, autoload=True) 57 | 58 | def table_events(): 59 | return Table('events', metadata, autoload=True) 60 | 61 | def table_index_mktdata_intraday(): 62 | return Table('equity_index_mktdata_intraday', metadata_intraday, autoload=True) 63 | 64 | def table_option_mktdata_intraday(): 65 | return Table('option_mktdata_intraday', metadata_intraday, autoload=True) 66 | 67 | def table_cf_minute_1(): 68 | return Table('cf_minute_1',metadata_dzqh, autoload=True) 69 | 70 | def table_cf_daily(): 71 | return Table('cf_day',metadata_dzqh, autoload=True) 72 | 73 | 74 | def cf_minute(start_date, end_date, name_code): 75 | name_code = name_code.lower() 76 | table_cf = table_cf_minute_1() 77 | query = session_dzqh().query(table_cf.c.dt_datetime, table_cf.c.id_instrument, table_cf.c.dt_date, 78 | table_cf.c.amt_open, table_cf.c.amt_close, table_cf.c.amt_trading_volume). \ 79 | filter((table_cf.c.dt_date >= start_date)&(table_cf.c.dt_date <= end_date)&(table_cf.c.name_code == name_code)) 80 | df = pd.read_sql(query.statement, query.session.bind) 81 | # df = df[df['id_instrument'].str.contains("_")] 82 | return df 83 | 84 | def get_index_minute(start_date, end_date, id_index): 85 | Index = table_index_mktdata_intraday() 86 | query = session_intraday().query(Index.c.dt_datetime, Index.c.id_instrument, Index.c.amt_close, 87 | Index.c.amt_trading_volume, Index.c.amt_trading_value) \ 88 | .filter(Index.c.dt_datetime >= start_date).filter(Index.c.dt_datetime <= end_date) \ 89 | .filter(Index.c.id_instrument == id_index) 90 | df = pd.read_sql(query.statement, query.session.bind) 91 | return df 92 | 93 | 94 | def get_index_mktdata(start_date, end_date, id_index): 95 | Index_mkt = table_indexes_mktdata() 96 | query_etf = session_mktdata().query(Index_mkt.c.dt_date, Index_mkt.c.amt_close, Index_mkt.c.amt_open, 97 | Index_mkt.c.id_instrument, Index_mkt.c.amt_high,Index_mkt.c.amt_low, 98 | Index_mkt.c.amt_trading_volume,Index_mkt.c.amt_trading_value) \ 99 | .filter(Index_mkt.c.dt_date >= start_date).filter(Index_mkt.c.dt_date <= end_date) \ 100 | .filter(Index_mkt.c.id_instrument == id_index) 101 | df_index = pd.read_sql(query_etf.statement, query_etf.session.bind) 102 | return df_index 103 | 104 | def get_50option_minute(start_date, end_date): 105 | OptionIntra = table_option_mktdata_intraday() 106 | query = session_intraday().query(OptionIntra.c.dt_datetime, 107 | OptionIntra.c.dt_date, 108 | OptionIntra.c.id_instrument, 109 | OptionIntra.c.amt_close, 110 | OptionIntra.c.amt_trading_volume, 111 | OptionIntra.c.amt_trading_value) \ 112 | .filter(OptionIntra.c.dt_date >= start_date).filter(OptionIntra.c.dt_date <= end_date) 113 | df = pd.read_sql(query.statement, query.session.bind) 114 | return df 115 | 116 | dt1 = datetime.date(2018, 5, 5) 117 | dt2 = datetime.date(2018, 5, 7) 118 | # data : 50etf 期权分钟数据 119 | data = get_50option_minute(dt1, dt2) 120 | print(data) 121 | -------------------------------------------------------------------------------- /PricingLibrary/BlackFormular.py: -------------------------------------------------------------------------------- 1 | import math 2 | from scipy.stats import norm 3 | from PricingLibrary.Util import PricingUtil 4 | from back_test.model.constant import * 5 | from PricingLibrary.BlackCalculator import BlackCalculator 6 | 7 | """ 8 | Approximated Black 1976 implied standard deviation, i.e. 9 | volatility*sqrt(timeToMaturity). 10 | It is calculated using Brenner and Subrahmanyan (1988) and Feinstein 11 | (1988) approximation for at-the-money forward option, with the extended 12 | moneyness approximation by Corrado and Miller (1996) 13 | """ 14 | 15 | 16 | class BlackFormula(object): 17 | 18 | def __init__(self, 19 | dt_eval: datetime.date, 20 | dt_maturity: datetime.date, 21 | option_type: OptionType, 22 | spot: float, 23 | strike: float, 24 | black_price: float, 25 | rf: float = 0.03, 26 | displacement: float = 0.0): 27 | discount = PricingUtil.get_discount(dt_eval, dt_maturity, rf) 28 | self.dt_eval = dt_eval 29 | self.dt_maturity = dt_maturity 30 | self.option_type = option_type 31 | self.strike = strike + displacement 32 | self.forward = spot / discount + displacement 33 | self.discount = discount 34 | self.spot = spot 35 | self.displacement = displacement 36 | self.black_price = black_price 37 | 38 | if strike == self.forward: 39 | stddev = black_price / discount * math.sqrt(2.0 * math.pi) / self.forward 40 | else: 41 | moneynessDelta = self.option_type.value * (self.forward - strike) 42 | moneynessDelta_2 = moneynessDelta / 2.0 43 | temp = black_price / discount - moneynessDelta_2 44 | moneynessDelta_PI = moneynessDelta * moneynessDelta / math.pi 45 | temp2 = temp * temp - moneynessDelta_PI 46 | if temp2 < 0.0: 47 | # approximation breaks down, 2 alternatives: 48 | # 1. zero it 49 | temp2 = 0.0 50 | # 2. Manaster-Koehler (1982) efficient Newton-Raphson seed 51 | # return std::fabs(std::log(forward/strike))*std::sqrt(2.0); -- commented out in original C++ 52 | temp2 = math.sqrt(temp2) 53 | temp += temp2 54 | temp *= math.sqrt(2.0 * math.pi) 55 | stddev = temp / (self.forward + strike) 56 | self.stddev = stddev 57 | 58 | def ImpliedVolApproximation(self): 59 | return self.stddev / math.sqrt(PricingUtil.get_ttm(self.dt_eval, self.dt_maturity)) 60 | 61 | def ImpliedVol(self): 62 | return 63 | 64 | """ Black 1976 implied standard deviation, 65 | i.e. volatility*sqrt(timeToMaturity) """ 66 | 67 | 68 | class BlackFormulaImpliedStdDev(object): 69 | 70 | def __init__(self, 71 | dt_eval: datetime.date, 72 | dt_maturity: datetime.date, 73 | strike: float, 74 | type: OptionType, 75 | spot: float, 76 | black_price: float, 77 | guess: float, 78 | accuracy: float, 79 | rf: float = 0.03, 80 | displacement: float = 0.0): 81 | discount = PricingUtil.get_discount(dt_eval, dt_maturity, rf) 82 | self.dt_eval = dt_eval 83 | self.dt_maturity = dt_maturity 84 | self.option_type = type 85 | self.strike = strike + displacement 86 | self.forward = spot / discount + displacement 87 | self.discount = discount 88 | self.spot = spot 89 | self.black_price = black_price 90 | self.guess = guess 91 | self.accuracy = accuracy 92 | self.displacement = displacement 93 | 94 | # TODO: SOLVE 95 | 96 | def ImpliedVol(self): 97 | return 98 | 99 | # a = [ 100 | # -0.0527 , 101 | # -0.0238 , 102 | # -0.0268 , 103 | # -0.0253 , 104 | # -0.0426 , 105 | # -0.0699 , 106 | # -0.0566 , 107 | # -0.0525 , 108 | # -0.1019 , 109 | # -0.0379 , 110 | # -0.0302 , 111 | # -0.0270 , 112 | # -0.0338 , 113 | # -0.0064 , 114 | # -0.0313 , 115 | # -0.0360 , 116 | # -0.0376 , 117 | # -0.0188 , 118 | # -0.0243 , 119 | # -0.0230 , 120 | # -0.0265 , 121 | # -0.0154 , 122 | # -0.0150 , 123 | # -0.0215 , 124 | # -0.0130 , 125 | # -0.0170 , 126 | # -0.0219 , 127 | # -0.0151 , 128 | # -0.0229 , 129 | # -0.0166 , 130 | # -0.0220 , 131 | # -0.0187 , 132 | # -0.0166 , 133 | # -0.0123 , 134 | # -0.0170 , 135 | # -0.0152 , 136 | # -0.0103 , 137 | # -0.0158 , 138 | # -0.0410 , 139 | # -0.0266 , 140 | # -0.0276 141 | # ] 142 | # vols = [] 143 | # dt_eval = datetime.date(2018, 7, 6) 144 | # dt_maturity = dt_eval + datetime.timedelta(days=30) 145 | # strike = 4000 146 | # spot = 4000 147 | # for i in a: 148 | # black_price = spot*(-i) 149 | # type = OptionType.CALL 150 | # black = BlackFormula(dt_eval, dt_maturity, strike, type, spot, black_price) 151 | # std = black.stddev 152 | # vol = std / math.sqrt(PricingUtil.get_ttm(dt_eval, dt_maturity)) 153 | # # print(vol * 100, '%') 154 | # # black = BlackCalculator(dt_eval, dt_maturity, strike, type, spot, vol) 155 | # # print(black.NPV()) 156 | # vols.append(vol) 157 | # 158 | # df = pd.DataFrame() 159 | # df['vol'] = vols 160 | # df.to_csv('../vols.csv') 161 | # print(df) 162 | 163 | 164 | -------------------------------------------------------------------------------- /PricingLibrary/BinomialModel.py: -------------------------------------------------------------------------------- 1 | import typing 2 | import math 3 | import datetime 4 | import back_test.model.constant as constant 5 | 6 | 7 | class OptionPayoff(object): 8 | @staticmethod 9 | def get_payoff(s: float, k: float): 10 | return max(s - k, 0) 11 | 12 | 13 | """ 14 | dt_eval: datetime.date, 15 | dt_maturity: datetime.date, 16 | strike: float, 17 | type: OptionType, 18 | spot: float, 19 | vol: float, 20 | rf: float = 0.03 21 | """ 22 | 23 | 24 | class BinomialTree(object): 25 | 26 | def __init__(self, 27 | dt_eval: datetime.date, 28 | dt_maturity: datetime.date, 29 | option_type: constant.OptionType, 30 | option_exercise_type: constant.OptionExerciseType, 31 | spot: float, 32 | strike: float, 33 | vol: float = 0.2, 34 | rf: float = 0.03, 35 | n: int=800): 36 | 37 | self.values: typing.List[typing.List[float]] = [] 38 | self.asset_values: typing.List[typing.List[float]] = [] 39 | self.exercise_values: typing.List[typing.List[float]] = [] 40 | self.option_type = option_type 41 | self.option_exercise_type = option_exercise_type 42 | self.strike = strike 43 | self.spot = spot 44 | self.rf = rf 45 | self.dt_eval: datetime.date = dt_eval 46 | self.dt_maturity: datetime.date = dt_maturity 47 | self.T: float = (dt_maturity - dt_eval).days / 365.0 48 | self.t: float = self.T/n 49 | self.u = math.exp(vol * math.sqrt(self.t)) 50 | self.d = math.exp(-1 * vol * math.sqrt(self.t)) 51 | self.p_u = (math.exp(rf * self.t) - self.d) / (self.u - self.d) 52 | self.p_d = 1 - self.p_u 53 | self.discount = math.exp(-1 * rf * self.t) 54 | self.n: int = n 55 | 56 | def initialize(self): 57 | self.populate_asset() 58 | 59 | def populate_asset(self): 60 | pre = [] 61 | for i in range(self.n): 62 | if len(pre) == 0: 63 | cur = [self.spot] 64 | else: 65 | cur = [pre[0] * self.u] 66 | for item in pre: 67 | cur.append(item * self.d) 68 | self.asset_values.append(cur) 69 | pre = cur 70 | for asset_value in self.asset_values: 71 | exercise_value = [constant.PricingUtil.payoff(v, self.strike, self.option_type) for v in asset_value] 72 | self.exercise_values.append(exercise_value) 73 | 74 | def disp(self): 75 | print("exercise type =", self.option_exercise_type) 76 | print("u =", self.u) 77 | print("d =", self.d) 78 | print("p_u =", self.p_u) 79 | print("p_d =", self.p_d) 80 | # if len(self.asset_values) != 0: 81 | # print("asset(underlying) tree structure") 82 | # for value in self.asset_values: 83 | # print(value) 84 | # if len(self.exercise_values) != 0: 85 | # print("exercise tree structure") 86 | # for value in self.exercise_values: 87 | # print(value) 88 | # if len(self.values) != 0: 89 | # print("value tree structure") 90 | # for value in self.values: 91 | # print(value) 92 | 93 | def size(self, i: int) -> int: 94 | return i 95 | 96 | def NPV(self) -> float: 97 | self.step_back(0) 98 | return self.values[0][0] 99 | 100 | def step_back(self, step_back_to: int) -> None: 101 | step_back_from = self.n - 1 102 | self.values.insert(0, self.exercise_values[step_back_from]) 103 | for i in range(step_back_from, step_back_to, -1): 104 | cur_value = self.values[0] 105 | pre_value = [] 106 | count = self.size(i) 107 | for j in range(count): 108 | continous_value = (cur_value[j] * self.p_u + cur_value[j + 1] * self.p_d) * self.discount 109 | if self.option_exercise_type == constant.OptionExerciseType.AMERICAN: 110 | exercise_value = self.exercise_values[i - 1][j] 111 | else: 112 | exercise_value = 0 113 | pre_value.append(max(continous_value, exercise_value)) 114 | self.values.insert(0, pre_value) 115 | return None 116 | 117 | def reset(self, vol: float) -> None: 118 | self.asset_values = [] 119 | self.values = [] 120 | self.exercise_values = [] 121 | self.u = math.exp(vol * math.sqrt(self.t)) 122 | self.d = math.exp(-1 * vol * math.sqrt(self.t)) 123 | self.p_u = (math.exp(self.rf * self.t) - self.d) / (self.u - self.d) 124 | self.p_d = 1 - self.p_u 125 | self.discount = math.exp(-1 * self.rf * self.t) 126 | self.populate_asset() 127 | 128 | def estimate_vol(self, price: float, presion:float=0.00001,max_vol:float=2.0): 129 | l = presion 130 | r = max_vol 131 | while l < r and round((r - l), 5) > presion: 132 | m = round(l + (r - l) / 2, 5) 133 | self.reset(m) 134 | p = self.NPV() 135 | if p < price: 136 | l = m 137 | else: 138 | r = m 139 | return m 140 | -------------------------------------------------------------------------------- /regular_reports/otc_index_option_histvolts.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import * 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import sessionmaker 4 | from mpl_toolkits.mplot3d import Axes3D 5 | import matplotlib as mpl 6 | from matplotlib import cm as plt_cm 7 | import datetime 8 | import pandas as pd 9 | import numpy as np 10 | from WindPy import w 11 | from data_access.db_tables import DataBaseTables as dbt 12 | import matplotlib.pyplot as plt 13 | from Utilities.PlotUtil import PlotUtil 14 | import QuantLib as ql 15 | 16 | ########################################################################################### 17 | w.start() 18 | pu = PlotUtil() 19 | plt.rcParams['font.sans-serif'] = ['STKaiti'] 20 | plt.rcParams.update({'font.size': 15}) 21 | engine = create_engine('mysql+pymysql://guest:passw0rd@101.132.148.152/mktdata', 22 | echo=False) 23 | conn = engine.connect() 24 | metadata = MetaData(engine) 25 | Session = sessionmaker(bind=engine) 26 | sess = Session() 27 | index_mkt = Table('indexes_mktdata', metadata, autoload=True) 28 | indexmkt_table = dbt.IndexMkt 29 | index_ids = ['index_300sh','index_50sh','index_500sh'] 30 | ############################################################################################ 31 | # Eval Settings 32 | eval_date = datetime.date(2018, 5, 18) 33 | evalDate = eval_date.strftime("%Y-%m-%d") 34 | 35 | hist_date = datetime.date(2016, 1, 1).strftime("%Y-%m-%d") 36 | 37 | ############################################################################################# 38 | 39 | 40 | 41 | query_index = sess.query(indexmkt_table.id_instrument,indexmkt_table.dt_date,indexmkt_table.amt_close)\ 42 | .filter(indexmkt_table.dt_date >= hist_date)\ 43 | .filter(indexmkt_table.dt_date <= evalDate)\ 44 | 45 | index_df = pd.read_sql(query_index.statement,query_index.session.bind) 46 | 47 | for indexid in index_ids: 48 | 49 | index300sh_df = index_df[index_df['id_instrument']==indexid].reset_index(drop=True) 50 | for (idx,row) in index300sh_df.iterrows(): 51 | if idx == 0: r=0.0 52 | else: 53 | r = np.log(float(row['amt_close']) / float(index300sh_df.loc[idx-1,'amt_close'])) 54 | index300sh_df.loc[idx,'yield'] = r 55 | 56 | for idx_v in range(len(index300sh_df)): 57 | if idx_v >= 120: 58 | index300sh_df.loc[idx_v,'histvol_120'] = np.std(index300sh_df['yield'][idx_v-120:idx_v])*np.sqrt(252)*100 59 | if idx_v >= 60: 60 | index300sh_df.loc[idx_v,'histvol_60'] = np.std(index300sh_df['yield'][idx_v-60:idx_v])*np.sqrt(252)*100 61 | if idx_v >= 20: 62 | index300sh_df.loc[idx_v,'histvol_20'] = np.std(index300sh_df['yield'][idx_v-20:idx_v])*np.sqrt(252)*100 63 | if idx_v >= 10: 64 | index300sh_df.loc[idx_v,'histvol_10'] = np.std(index300sh_df['yield'][idx_v-10:idx_v])*np.sqrt(252)*100 65 | if idx_v >= 5: 66 | index300sh_df.loc[idx_v,'histvol_5'] = np.std(index300sh_df['yield'][idx_v-5:idx_v])*np.sqrt(252)*100 67 | 68 | # print(index300sh_df) 69 | volcone_df = pd.DataFrame() 70 | histvols_60 = index300sh_df['histvol_120'].dropna().tolist() 71 | histvols_30 = index300sh_df['histvol_60'].dropna().tolist() 72 | histvols_20 = index300sh_df['histvol_20'].dropna().tolist() 73 | histvols_10 = index300sh_df['histvol_10'].dropna().tolist() 74 | histvols_5 = index300sh_df['histvol_5'].dropna().tolist() 75 | max_vols = [max(histvols_60),max(histvols_30),max(histvols_20),max(histvols_10),max(histvols_5)] 76 | min_vols = [min(histvols_60), min(histvols_30), min(histvols_20), min(histvols_10),min(histvols_5)] 77 | median_vols = [np.median(histvols_60), np.median(histvols_30), np.median(histvols_20), 78 | np.median(histvols_10),np.median(histvols_5)] 79 | p75_vols = [np.percentile(histvols_60, 75), np.percentile(histvols_30, 75), 80 | np.percentile(histvols_20, 75), np.percentile(histvols_10, 75), np.percentile(histvols_5, 75)] 81 | p25_vols = [np.percentile(histvols_60, 25), np.percentile(histvols_30, 25), 82 | np.percentile(histvols_20, 25), np.percentile(histvols_10, 25), np.percentile(histvols_5, 25)] 83 | # print(evalDate) 84 | index300sh_df_c = index300sh_df[index300sh_df['dt_date']==eval_date] 85 | current_vols = [float(index300sh_df_c['histvol_120']), 86 | float(index300sh_df_c['histvol_60']), 87 | float(index300sh_df_c['histvol_20']), 88 | float(index300sh_df_c['histvol_10']), 89 | float(index300sh_df_c['histvol_5'])] 90 | print(indexid,' current_vols : ', current_vols) 91 | histvolcone = [current_vols, max_vols, min_vols, median_vols, p75_vols, p25_vols] 92 | x = [120, 60, 20, 10, 5] 93 | x_labels = ['1W', '2W', '1M', '3M', '6M'] 94 | 95 | f2, ax2 = plt.subplots() 96 | ldgs = ['当前水平', '最大值', '最小值', '中位数', '75分位数', '25分位数'] 97 | for cont2, y in enumerate(histvolcone): 98 | pu.plot_line(ax2, cont2, x, y, ldgs[cont2], '时间窗口', '波动率(%)') 99 | ax2.legend(bbox_to_anchor=(0., 1.02, 1., .202), loc=3, 100 | ncol=6, mode="expand", borderaxespad=0.,frameon=False) 101 | ax2.set_xticks([5,10,20,60,120]) 102 | ax2.set_xticklabels(x_labels) 103 | f2.set_size_inches((12, 6)) 104 | f2.savefig('../data/otc_histvols_'+indexid+'_' + str(hist_date)+' - '+ str(evalDate) + '.png', dpi=300, format='png') 105 | 106 | 107 | 108 | plt.show() 109 | 110 | -------------------------------------------------------------------------------- /PricingLibrary/tests/test_binomialTree.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from PricingLibrary.BinomialModel import BinomialTree 3 | from PricingLibrary.BlackCalculator import BlackCalculator 4 | from back_test.model.constant import OptionType, OptionExerciseType, QuantlibUtil 5 | import datetime 6 | import QuantLib as ql 7 | from PricingLibrary.EngineQuantlib import QlBinomial 8 | 9 | class TestBinomialTree(TestCase): 10 | @classmethod 11 | def setUpClass(cls): 12 | cls.size = 800 13 | cls.max_index = 9 14 | cls.strike = 20 15 | 16 | def test_step_back(self): 17 | dt_eval = datetime.date(2017, 1, 1) 18 | dt_maturity = datetime.date(2017, 4, 1) 19 | spot_price = 120 20 | strike_price = 120 21 | volatility = 0.3 # the historical vols or implied vols 22 | dividend_rate = 0 23 | risk_free_rate = 0.03 24 | steps = self.size 25 | 26 | american_binomial_tree = BinomialTree(steps, dt_eval, dt_maturity, 27 | OptionType.PUT, OptionExerciseType.AMERICAN, spot_price, strike_price, volatility) 28 | american_binomial_tree.initialize() 29 | american_binomial_tree.step_back(0) 30 | print(american_binomial_tree.T) 31 | print("american binomial_tree price", american_binomial_tree.NPV()) 32 | european_binomial_tree = BinomialTree(steps, dt_eval, dt_maturity, 33 | OptionType.PUT, OptionExerciseType.EUROPEAN, spot_price, strike_price, volatility) 34 | european_binomial_tree.initialize() 35 | european_binomial_tree.step_back(0) 36 | print("european binomial_tree price", european_binomial_tree.NPV()) 37 | 38 | black = BlackCalculator(dt_eval, dt_maturity,strike_price,OptionType.PUT,spot_price,volatility) 39 | print("european blackcalculator price", black.NPV()) 40 | 41 | maturity_date = QuantlibUtil.to_ql_date(dt_maturity) 42 | 43 | option_type = ql.Option.Put 44 | 45 | day_count = ql.ActualActual() 46 | calendar = ql.NullCalendar() 47 | 48 | calculation_date = QuantlibUtil.to_ql_date(dt_eval) 49 | print(day_count.yearFraction(calculation_date,maturity_date)) 50 | ql.Settings.instance().evaluationDate = calculation_date 51 | payoff = ql.PlainVanillaPayoff(option_type, strike_price) 52 | settlement = calculation_date 53 | 54 | am_exercise = ql.AmericanExercise(settlement, maturity_date) 55 | american_option = ql.VanillaOption(payoff, am_exercise) 56 | eu_exercise = ql.EuropeanExercise(maturity_date) 57 | european_option = ql.VanillaOption(payoff, eu_exercise) 58 | 59 | spot_handle = ql.QuoteHandle( 60 | ql.SimpleQuote(spot_price) 61 | ) 62 | flat_ts = ql.YieldTermStructureHandle( 63 | ql.FlatForward(calculation_date, risk_free_rate, day_count) 64 | ) 65 | dividend_yield = ql.YieldTermStructureHandle( 66 | ql.FlatForward(calculation_date, dividend_rate, day_count) 67 | ) 68 | flat_vol_ts = ql.BlackVolTermStructureHandle( 69 | ql.BlackConstantVol(calculation_date, calendar, volatility, day_count) 70 | ) 71 | bsm_process = ql.BlackScholesMertonProcess(spot_handle, 72 | dividend_yield, 73 | flat_ts, 74 | flat_vol_ts) 75 | binomial_engine = ql.BinomialVanillaEngine(bsm_process, "crr", steps) 76 | black_engine = ql.AnalyticEuropeanEngine(bsm_process) 77 | american_option.setPricingEngine(binomial_engine) 78 | print("american quantlib price(BinomialVanillaEngine)", american_option.NPV()) 79 | european_option.setPricingEngine(binomial_engine) 80 | print("european quantlib price(BinomialVanillaEngine)", european_option.NPV()) 81 | european_option.setPricingEngine(black_engine) 82 | print("european quantlib price(blackcalculator)", european_option.NPV()) 83 | 84 | def test_estimate_vol(self): 85 | dt_eval = datetime.date(2018, 8, 7) 86 | dt_maturity = datetime.date(2018, 12, 7) 87 | spot_price = 3224 88 | strike_price = 3200 89 | init_vol = 0.1755 # the historical vols or implied vols 90 | risk_free_rate = 0.03 91 | steps = self.size 92 | american_binomial_tree = BinomialTree(steps,dt_eval,dt_maturity,OptionType.PUT,OptionExerciseType.AMERICAN,spot_price,strike_price,init_vol,risk_free_rate) 93 | american_binomial_tree.initialize() 94 | vol, price = american_binomial_tree.estimate_vol(105.5) 95 | print(vol) 96 | print(price) 97 | american_ql = QlBinomial(steps,dt_eval,dt_maturity,OptionType.PUT,OptionExerciseType.AMERICAN,spot_price,strike_price,init_vol,risk_free_rate) 98 | vol, price = american_ql.estimate_vol(105.5) 99 | print(vol) 100 | print(price) 101 | american_binomial_tree = BinomialTree(steps,dt_eval,dt_maturity,OptionType.CALL,OptionExerciseType.AMERICAN,spot_price,strike_price,init_vol,risk_free_rate) 102 | american_binomial_tree.initialize() 103 | vol, price = american_binomial_tree.estimate_vol(139.5) 104 | print(vol) 105 | print(price) 106 | american_ql = QlBinomial(steps,dt_eval,dt_maturity,OptionType.CALL,OptionExerciseType.AMERICAN,spot_price,strike_price,init_vol,risk_free_rate) 107 | vol, price = american_ql.estimate_vol(139.5) 108 | print(vol) 109 | print(price) 110 | -------------------------------------------------------------------------------- /PricingLibrary/OptionMetrics.py: -------------------------------------------------------------------------------- 1 | import QuantLib as ql 2 | from OptionStrategyLib.OptionPricing import OptionPricingUtil as util 3 | 4 | 5 | class OptionMetrics: 6 | def __init__(self, option, rf, engineType): 7 | self.Option = option 8 | self.rf = rf 9 | self.engineType = engineType 10 | # self.implied_vol = -1.0 11 | 12 | def reset_option(self, option): 13 | self.Option = option 14 | 15 | def set_evaluation(self, evaluation): 16 | self.evaluation = evaluation 17 | return self 18 | 19 | def option_price(self, spot_price, vol): 20 | option = self.Option.option_ql 21 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, vol) 22 | engine = util.get_engine(process, self.engineType) 23 | option.setPricingEngine(engine) 24 | p = option.NPV() 25 | return p 26 | 27 | def implied_vol(self, spot_price, option_price): 28 | option = self.Option.option_ql 29 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, 0.0) 30 | engine = util.get_engine(process, self.engineType) 31 | option.setPricingEngine(engine) 32 | # print('pricing metrics eval date : ',self.evaluation.evalDate) 33 | try: 34 | implied_vol = option.impliedVolatility(option_price, process, 1.0e-3, 300, 0.05, 5.0) 35 | except: 36 | implied_vol = 0.0 37 | # self.implied_vol = implied_vol 38 | return implied_vol 39 | 40 | def delta(self, spot_price, implied_vol): 41 | option = self.Option.option_ql 42 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, implied_vol) 43 | engine = util.get_engine(process, self.engineType) 44 | option.setPricingEngine(engine) 45 | delta = option.delta() 46 | return delta 47 | 48 | def effective_delta(self, spot_price, implied_vol, dS=0.001): 49 | option_ql = self.Option.option_ql 50 | process1 = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price + dS, implied_vol) 51 | process2 = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price - dS, implied_vol) 52 | engine1 = util.get_engine(process1, self.engineType) 53 | engine2 = util.get_engine(process2, self.engineType) 54 | option_ql.setPricingEngine(engine1) 55 | option_price1 = option_ql.NPV() 56 | option_ql.setPricingEngine(engine2) 57 | option_price2 = option_ql.NPV() 58 | delta_eff = (option_price1 - option_price2) / (2 * dS) 59 | return delta_eff 60 | 61 | def theta(self, spot_price,implied_vol): 62 | """The rate of change in the fair value of the option per one day decrease 63 | in the option time when other variables remain constant. 64 | This is the negative of the derivative of the option price 65 | with respect to the option time (in years), divided by 365.""" 66 | option = self.Option.option_ql 67 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, implied_vol) 68 | engine = util.get_engine(process, self.engineType) 69 | option.setPricingEngine(engine) 70 | theta = option.theta()/365.0 71 | return theta 72 | 73 | def vega(self, spot_price, implied_vol): 74 | """The rate of change in the fair value of the option per 1% change in volatility 75 | when other variables remain constant. 76 | This is the derivative of the option price with respect to the volatility, 77 | divided by 100.""" 78 | option = self.Option.option_ql 79 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, implied_vol) 80 | engine = util.get_engine(process, self.engineType) 81 | option.setPricingEngine(engine) 82 | vega = option.vega()/100.0 83 | # price1 = self.option_price(spot_price, implied_vol) 84 | # price2 = self.option_price(spot_price, implied_vol + 0.01) 85 | # vega = price2 - price1 86 | return vega 87 | 88 | def vega_effective(self, spot_price, implied_vol): 89 | # option = self.Option.option_ql 90 | # process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, implied_vol) 91 | # engine = util.get_engine(process, self.engineType) 92 | # option.setPricingEngine(engine) 93 | # vega = option.vega() 94 | price1 = self.option_price(spot_price, implied_vol) 95 | price2 = self.option_price(spot_price, implied_vol + 0.01) 96 | vega = price2 - price1 97 | return vega 98 | def rho(self, spot_price, implied_vol): 99 | option = self.Option.option_ql 100 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, implied_vol) 101 | engine = util.get_engine(process, self.engineType) 102 | option.setPricingEngine(engine) 103 | rho = option.rho() 104 | return rho 105 | 106 | def gamma(self, spot_price, implied_vol): 107 | option = self.Option.option_ql 108 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, implied_vol) 109 | engine = util.get_engine(process, self.engineType) 110 | option.setPricingEngine(engine) 111 | gamma = option.gamma() 112 | return gamma 113 | 114 | def vomma(self, spot_price, implied_vol): 115 | option = self.Option.option_ql 116 | process = self.evaluation.get_bsmprocess_cnstvol(self.rf, spot_price, implied_vol) 117 | engine = util.get_engine(process, self.engineType) 118 | option.setPricingEngine(engine) 119 | vomma = option.vomma() 120 | return vomma 121 | -------------------------------------------------------------------------------- /data_access/spider_api_czce.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | from WindPy import * 5 | import requests 6 | import pandas as pd 7 | import os 8 | import time 9 | 10 | w.start() 11 | 12 | 13 | 14 | def spider_option(firstdate,enddate): 15 | dataset = {} 16 | date_range = w.tdays(firstdate, enddate, "").Data[0] 17 | for i in range(len(date_range)): 18 | time.sleep(0.5) 19 | date = date_range[i] 20 | # print('spider : ',date) 21 | year, month, day = date.year, date.month, date.day 22 | #url = 'http://www.dce.com.cn/publicweb/quotesdata/exportDayQuotesChData.html?dayQuotes.variety='\ 23 | # + codename+'&dayQuotes.trade_type=1&year='+str(year)+'&month='+str(month-1)+'&day='+str(day) 24 | if month < 10: str_month = '0'+str(month) 25 | else: str_month = str(month) 26 | if day < 10: str_day = '0' + str(day) 27 | else: str_day = str(day) 28 | url = 'http://old.czce.com.cn/portal/DFSStaticFiles/Option/'\ 29 | +str(year)+'/'+str(year)+str_month+str_day+'/OptionDataDaily.txt' 30 | #http://www.czce.com.cn/portal/DFSStaticFiles/Future/2017/20170919/FutureDataDailyMA.htm 31 | #http://www.czce.com.cn/portal/DFSStaticFiles/Future/2017/20170915/FutureDataDailySR.txt 32 | #http://www.czce.com.cn/portal/DFSStaticFiles/Option/2017/20171208/OptionDataDaily.txt 33 | res = requests.get(url) 34 | content = res.content 35 | # print(res) 36 | result = content.decode(encoding='GB18030') 37 | rows = result.split('\n') 38 | if len(rows) == 0: 39 | pass 40 | else: 41 | index = [] 42 | index_row = rows[1].split("|") 43 | for item in index_row: 44 | index.extend(item.split()) 45 | data = pd.DataFrame(index=index) 46 | for nbrRow in range(2,len(rows)): 47 | row = rows[nbrRow] 48 | colums_of_row = row.split('|') 49 | if len(colums_of_row) != len(index): 50 | continue 51 | if colums_of_row[0][0:2] == '小计' or colums_of_row[0][0:2] == '总计' \ 52 | or colums_of_row[0][0:4] == 'SR合计' : 53 | continue 54 | data[nbrRow] = colums_of_row 55 | # datestr = str(date.year) + "-" + str(date.month) + "-" + str(date.day) 56 | # data.to_json(os.path.abspath('..')+ '\marketdata\\' + codename + '_mkt_' + datestr + '.json') 57 | dt_date = datetime.date(date) 58 | dataset.update({dt_date: data}) 59 | return dataset 60 | 61 | def spider_future(firstdate,enddate): 62 | dataset = {} 63 | date_range = w.tdays(firstdate, enddate, "").Data[0] 64 | for i in range(len(date_range)): 65 | time.sleep(0.5) 66 | date = date_range[i] 67 | # print(date) 68 | year, month, day = date.year, date.month, date.day 69 | if month < 10: str_month = '0'+str(month) 70 | else: str_month = str(month) 71 | if day < 10: str_day = '0' + str(day) 72 | else: str_day = str(day) 73 | url = 'http://old.czce.com.cn/portal/DFSStaticFiles/Future/'\ 74 | +str(year)+'/'+str(year)+str_month+str_day+'/FutureDataDaily.txt' 75 | #http://www.czce.com.cn/portal/DFSStaticFiles/Future/2017/20170919/FutureDataDailyMA.htm 76 | #http://www.czce.com.cn/portal/DFSStaticFiles/Future/2017/20170915/FutureDataDailySR.txt 77 | #http://www.czce.com.cn/portal/DFSStaticFiles/Option/2017/20170915/OptionDataDaily.txt 78 | res = requests.get(url) 79 | content = res.content 80 | # print(res) 81 | result = content.decode(encoding='GB18030') 82 | rows = result.split('\n') 83 | if len(rows) == 0: 84 | pass 85 | else: 86 | index = [] 87 | index_row = rows[1].split("|") 88 | for item in index_row: 89 | index.extend(item.split()) 90 | data = pd.DataFrame(index=index) 91 | for nbrRow in range(2,len(rows)): 92 | row = rows[nbrRow] 93 | colums_of_row = row.split('|') 94 | if len(colums_of_row) != len(index): 95 | continue 96 | if colums_of_row[0][0:2] == '小计' or colums_of_row[0][0:2] == '总计': 97 | continue 98 | data[nbrRow] = colums_of_row 99 | # datestr = str(date.year) + "-" + str(date.month) + "-" + str(date.day) 100 | # data.to_json(os.path.abspath('..')+ '\marketdata\\' + codename + '_future_mkt_' + datestr + '.json') 101 | dt_date = datetime.date(date) 102 | dataset.update({dt_date: data}) 103 | return dataset 104 | 105 | # def get_data(): 106 | # 107 | # # fd = {'i': '2013/10/18', 'jm': '2013/03/22', 'j': '2011/04/15'} 108 | # fd = { 'sr': '2017-09-10'} 109 | # 110 | # # fd = {'v': '2009/05/25', 'b': '2004/12/22', 'm': ' 2000/07/17', 'a': '1999/01/04', 'y': '2006/01/09', 111 | # # 'jd': '2013/11/08', 'bb': '2013/12/06', 'jm': '2013/03/22', 'j': '2011/04/15', 'pp': '2014/02/28', 112 | # # 'l': '2007/07/31', 'i': '2013/10/18', 'fb': '2013/12/06', 'c': '2004/09/22', 'cs': '2014/12/19', 113 | # # 'p': '2007/10/29'} 114 | # 115 | # for code in fd.keys(): 116 | # print("正在爬取 "+code+' 数据……') 117 | # spider_option(code, fd[code]) 118 | 119 | 120 | # get_data() 121 | 122 | # codename = 'sr' 123 | # datestr = '2017-8-21' 124 | # df = pd.read_json(os.path.abspath('..')+ '\marketdata\\' + codename + '_mkt_' + datestr + '.json') 125 | # print(df) -------------------------------------------------------------------------------- /data_access/metrics_optiondata_goldencopy.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import datetime 3 | from sqlalchemy import * 4 | from sqlalchemy.orm import sessionmaker 5 | 6 | 7 | def average(df): 8 | if df.empty: return -999.0 9 | sum = (df['amt_close'] * df['amt_trading_volume']).sum() 10 | vol = df['amt_trading_volume'].sum() 11 | if vol != 0: 12 | return sum / vol 13 | return -999.0 14 | 15 | beg_date = datetime.date(2018, 4, 20) 16 | end_date = datetime.date(2018, 5, 4) 17 | # dc = DataCollection() 18 | engine = create_engine('mysql+pymysql://guest:passw0rd@101.132.148.152/mktdata', echo=False) 19 | engine_metrics = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/metrics', echo=False) 20 | Session = sessionmaker(bind=engine) 21 | sess = Session() 22 | metadata = MetaData(engine) 23 | options_mktdata = Table('options_mktdata', metadata, autoload=True) 24 | 25 | engine_intraday = create_engine('mysql+pymysql://root:liz1128@101.132.148.152/mktdata_intraday', echo=False) 26 | conn_intraday = engine_intraday.connect() 27 | Session_intraday = sessionmaker(bind=engine_intraday) 28 | sess_intraday = Session_intraday() 29 | metadata_intraday = MetaData(engine_intraday) 30 | option_mktdata_intraday = Table('option_mktdata_intraday', metadata_intraday, autoload=True) 31 | 32 | query_mkt = sess.query(options_mktdata) \ 33 | .filter(options_mktdata.c.datasource == 'wind')\ 34 | .filter(options_mktdata.c.id_underlying == 'index_50etf')\ 35 | .filter(options_mktdata.c.dt_date >= beg_date)\ 36 | .filter(options_mktdata.c.dt_date <= end_date) 37 | 38 | dataset = pd.read_sql_query(query_mkt.statement,query_mkt.session.bind) 39 | dates = dataset['dt_date'].unique() 40 | 41 | for date in dates: 42 | df = dataset[dataset['dt_date'] == date] 43 | 44 | for index, row in df.iterrows(): 45 | cur = row['dt_date'] 46 | next_day = row['dt_date'] + datetime.timedelta(days=1) 47 | id = row['id_instrument'] 48 | windcode = row['code_instrument'] 49 | query_intraday = sess_intraday.query(option_mktdata_intraday) \ 50 | .filter(option_mktdata_intraday.c.datasource == 'wind') \ 51 | .filter(option_mktdata_intraday.c.id_instrument == id) \ 52 | .filter(option_mktdata_intraday.c.dt_datetime >= date) \ 53 | .filter(option_mktdata_intraday.c.dt_datetime <= next_day) 54 | daily_df = pd.read_sql_query(query_intraday.statement,query_intraday.session.bind) 55 | if len(daily_df) < 240: 56 | print('intraday data length shorted : ',date,id,len(daily_df)) 57 | 58 | df_morning_open_15min = daily_df.loc[lambda df: (df.dt_datetime >= datetime.datetime(cur.year, cur.month, cur.day, 9, 30, 0))& 59 | (df.dt_datetime <= datetime.datetime(cur.year,cur.month,cur.day,9,45,0)),:] 60 | df_morning_close_15min = daily_df.loc[lambda df: (df.dt_datetime >= datetime.datetime(cur.year, cur.month, cur.day, 11, 15, 0))& 61 | (df.dt_datetime <= datetime.datetime(cur.year,cur.month,cur.day,11,30,0)),:] 62 | df_afternoon_open_15min = daily_df.loc[lambda df: (df.dt_datetime >= datetime.datetime(cur.year, cur.month, cur.day, 13, 0, 0))& 63 | (df.dt_datetime <= datetime.datetime(cur.year,cur.month,cur.day,13,15,0)),:] 64 | df_afternoon_close_15min = daily_df.loc[lambda df: (df.dt_datetime >= datetime.datetime(cur.year, cur.month, cur.day, 14, 45, 0))& 65 | (df.dt_datetime <= datetime.datetime(cur.year,cur.month,cur.day,15,00,0)),:] 66 | df_morning = daily_df.loc[lambda df: (df.dt_datetime >= datetime.datetime(cur.year, cur.month, cur.day, 9, 30, 0))& 67 | (df.dt_datetime <= datetime.datetime(cur.year,cur.month,cur.day,11,30,0)),:] 68 | df_afternoon = daily_df.loc[lambda df: (df.dt_datetime >= datetime.datetime(cur.year, cur.month, cur.day, 13, 0, 0))& 69 | (df.dt_datetime <= datetime.datetime(cur.year,cur.month,cur.day,15,00,0)),:] 70 | 71 | amt_morning_open_15min = average(df_morning_open_15min) 72 | amt_morning_close_15min = average(df_morning_close_15min) 73 | amt_afternoon_open_15min = average(df_afternoon_open_15min) 74 | amt_afternoon_close_15min = average(df_afternoon_close_15min) 75 | amt_daily_avg = average(daily_df) 76 | amt_morning_avg = average(df_morning) 77 | amt_afternoon_avg = average(df_afternoon) 78 | df.loc[index,'amt_morning_open_15min'] = amt_morning_open_15min 79 | df.loc[index,'amt_morning_close_15min'] = amt_morning_close_15min 80 | df.loc[index,'amt_afternoon_open_15min'] = amt_afternoon_open_15min 81 | df.loc[index,'amt_afternoon_close_15min'] = amt_afternoon_close_15min 82 | df.loc[index,'amt_daily_avg'] = amt_daily_avg 83 | df.loc[index,'amt_morning_avg'] = amt_morning_avg 84 | df.loc[index,'amt_afternoon_avg'] = amt_afternoon_avg 85 | # print(df.loc[index]) 86 | if row['amt_open'] == -999 or row['amt_close'] == -999: 87 | if row['amt_settlement'] == -999.0: 88 | print(row) 89 | print('No settlement and close data, No can do !!!!!') 90 | else: 91 | df.loc[index,'amt_open'] = row['amt_settlement'] 92 | df.loc[index,'amt_close'] = row['amt_settlement'] 93 | df.loc[index,'cd_remark'] = 'no trade volume' 94 | try: 95 | df.to_sql(name='options_mktdata_goldencopy', con=engine_metrics, if_exists = 'append', index=False) 96 | print(date,'inserted into database') 97 | except Exception as e: 98 | print(e) 99 | print(date) 100 | pass 101 | 102 | -------------------------------------------------------------------------------- /OptionStrategyLib/example_blackvolsurface.py: -------------------------------------------------------------------------------- 1 | import math 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from matplotlib import cm 5 | from mpl_toolkits.mplot3d import Axes3D 6 | import numpy as np 7 | import datetime 8 | import os 9 | import pickle 10 | import QuantLib as ql 11 | from WindPy import w 12 | from sqlalchemy import * 13 | from sqlalchemy.orm import sessionmaker 14 | from data_access.db_tables import DataBaseTables as dbt 15 | from OptionStrategyLib.calibration import SVICalibration 16 | from PricingLibrary.Evaluation import Evaluation 17 | from PricingLibrary.OptionMetrics import OptionMetrics 18 | from PricingLibrary.Options import OptionPlainEuropean 19 | w.start() 20 | 21 | ################################################################################################## 22 | evalDate = datetime.date(2015,8,20) 23 | ql_evalDate = ql.Date(evalDate.day,evalDate.month,evalDate.year) 24 | rf = 0.03 25 | engineType = 'AnalyticEuropeanEngine' 26 | dt = 1.0/12 27 | ################################################################################################## 28 | 29 | engine = create_engine('mysql+pymysql://guest:passw0rd@101.132.148.152/mktdata', echo=False) 30 | conn = engine.connect() 31 | metadata = MetaData(engine) 32 | Session = sessionmaker(bind=engine) 33 | sess = Session() 34 | options_mkt = Table('options_mktdata', metadata, autoload=True) 35 | futures_mkt = Table('futures_mktdata', metadata, autoload=True) 36 | options = Table('option_contracts', metadata, autoload=True) 37 | Index_mkt = dbt.IndexMkt 38 | Option_mkt = dbt.OptionMkt 39 | Option_contracts = dbt.Options 40 | svicalibration = SVICalibration(evalDate) 41 | calendar = ql.China() 42 | daycounter = ql.ActualActual() 43 | ql.Settings.instance().evaluationDate = ql_evalDate 44 | evaluation = Evaluation(ql_evalDate,daycounter,calendar) 45 | ################################################################################################## 46 | query_option = sess.query(Option_mkt.id_instrument, 47 | Option_mkt.cd_option_type, 48 | Option_mkt.amt_strike, 49 | Option_contracts.dt_maturity, 50 | Option_mkt.amt_close, 51 | Option_mkt.pct_implied_vol)\ 52 | .join(Option_contracts,Option_mkt.id_instrument==Option_contracts.id_instrument)\ 53 | .filter(Option_mkt.dt_date == evalDate)\ 54 | .filter(Option_mkt.flag_night == -1)\ 55 | .filter(Option_mkt.datasource == 'wind') 56 | 57 | query_etf = sess.query(Index_mkt.amt_close)\ 58 | .filter(Index_mkt.dt_date == evalDate)\ 59 | .filter(Index_mkt.id_instrument == 'index_50etf') 60 | 61 | df_option = pd.read_sql(query_option.statement,query_option.session.bind) 62 | df_option = df_option[df_option['id_instrument'].str[-1] != 'A'] 63 | df_50etf = pd.read_sql(query_etf.statement,query_etf.session.bind) 64 | df_option['underlying_price'] = [df_50etf['amt_close'].iloc[0]]*len(df_option) 65 | df_option['risk_free_rate'] = [rf]*len(df_option) 66 | 67 | for (idx,row) in df_option.iterrows(): 68 | optiontype = row['cd_option_type'] 69 | if optiontype == 'call': ql_optiontype = ql.Option.Call 70 | else: ql_optiontype = ql.Option.Put 71 | id = row['id_instrument'] 72 | mdt = row['dt_maturity'] 73 | ql_mdt = ql.Date(mdt.day,mdt.month,mdt.year) 74 | strike = row['amt_strike'] 75 | spot = row['underlying_price'] 76 | close = row['amt_close'] 77 | euro_option = OptionPlainEuropean(strike,ql_mdt,ql_optiontype) 78 | option_metrics = OptionMetrics(euro_option) 79 | implied_vol = option_metrics.implied_vol(evaluation, rf, spot, close,engineType) 80 | df_option['pct_implied_vol'].loc[idx] = implied_vol 81 | df_option['amt_strike'].loc[idx] = strike 82 | 83 | print(len(df_option)) 84 | df_option = df_option[df_option['pct_implied_vol'] > 0 ].reset_index() 85 | print(len(df_option)) 86 | 87 | 88 | df_option_call = df_option[df_option['cd_option_type'] == 'call' ].reset_index() 89 | print(df_option_call) 90 | ql_maturities = [] 91 | volset = [] 92 | strikes = [] 93 | maturity_dates = df_option_call['dt_maturity'].unique().tolist() 94 | for mdate in maturity_dates: 95 | c1 = df_option_call['dt_maturity'] == mdate 96 | implied_vols = df_option_call[c1]['pct_implied_vol'].tolist() 97 | if len(strikes) == 0 : strikes = df_option_call[c1]['amt_strike'].tolist() 98 | ql_maturities.append(ql.Date(mdate.day,mdate.month,mdate.year)) 99 | volset.append(implied_vols) 100 | matrix = ql.Matrix(len(strikes), len(maturity_dates)) 101 | vol_bvs = [] 102 | for i in range(matrix.rows()): 103 | for j in range(matrix.columns()): 104 | matrix[i][j] = volset[j][i] 105 | 106 | black_var_surface = ql.BlackVarianceSurface(ql_evalDate, calendar,ql_maturities, 107 | strikes,matrix, daycounter) 108 | print(strikes) 109 | vol_t1 = black_var_surface.blackVol(0.03,2.8) 110 | print(vol_t1) 111 | 112 | plt.rcParams['font.sans-serif'] = ['STKaiti'] 113 | plt.rcParams.update({'font.size': 13}) 114 | 115 | plot_years = np.arange(0.1,0.4,0.01) 116 | plot_strikes = np.arange(strikes[0],strikes[-1],0.01) 117 | fig = plt.figure() 118 | ax = fig.gca(projection='3d') 119 | X, Y = np.meshgrid(plot_strikes, plot_years) 120 | Z = np.array([black_var_surface.blackVol(y, x) 121 | for xr, yr in zip(X, Y) 122 | for x, y in zip(xr,yr) ] 123 | ).reshape(len(X), len(X[0])) 124 | surf = ax.plot_surface(X,Y,Z, rstride=1, cstride=1, cmap=cm.coolwarm, 125 | linewidth=0.2) 126 | ax.set_xlabel('strikes') 127 | ax.set_ylabel('maturities') 128 | fig.colorbar(surf, shrink=0.5, aspect=5) 129 | plt.show() 130 | -------------------------------------------------------------------------------- /PricingLibrary/BlackCalculator.py: -------------------------------------------------------------------------------- 1 | import math 2 | import datetime 3 | from scipy.stats import norm 4 | from back_test.model.constant import OptionType,PricingUtil 5 | 6 | """ European Option Pricing and Metrics """ 7 | 8 | 9 | class BlackCalculator(object): 10 | 11 | def __init__(self, 12 | dt_eval: datetime.date, 13 | dt_maturity: datetime.date, 14 | strike: float, 15 | type: OptionType, 16 | spot: float, 17 | vol: float, 18 | rf: float = 0.03): 19 | if type == OptionType.CALL: 20 | self.iscall = True 21 | else: 22 | self.iscall = False 23 | stdDev = PricingUtil.get_std(dt_eval, dt_maturity, vol) 24 | discount = PricingUtil.get_discount(dt_eval, dt_maturity, rf) 25 | self.dt_eval = dt_eval 26 | self.dt_maturity = dt_maturity 27 | self.strike = strike 28 | self.forward = spot / discount 29 | self.stdDev = stdDev 30 | self.discount = discount 31 | self.spot = spot 32 | if stdDev > 0.0: 33 | if self.strike == 0.0: 34 | n_d1 = 0.0 35 | n_d2 = 0.0 36 | cum_d1 = 1.0 37 | cum_d2 = 1.0 38 | D1 = None 39 | D2 = None 40 | else: 41 | D1 = math.log(self.forward / self.strike, math.e) / stdDev + 0.5 * stdDev 42 | D2 = D1 - stdDev 43 | cum_d1 = norm.cdf(D1) 44 | cum_d2 = norm.cdf(D2) 45 | n_d1 = norm.pdf(D1) 46 | n_d2 = norm.pdf(D2) 47 | self.D1 = D1 48 | self.D2 = D2 49 | else: 50 | if self.forward > self.strike: 51 | cum_d1 = 1.0 52 | cum_d2 = 1.0 53 | else: 54 | cum_d1 = 0.0 55 | cum_d2 = 0.0 56 | n_d1 = 0.0 57 | n_d2 = 0.0 58 | 59 | if self.iscall: 60 | alpha = cum_d1 ## N(d1) 61 | dAlpha_dD1 = n_d1 ## n(d1) 62 | beta = -cum_d2 ## -N(d2) 63 | dBeta_dD2 = -n_d2 ## -n(d2) 64 | else: 65 | alpha = -1.0 + cum_d1 ## -N(-d1) 66 | dAlpha_dD1 = n_d1 ## n( d1) 67 | beta = 1.0 - cum_d2 ## N(-d2) 68 | dBeta_dD2 = -n_d2 ## -n( d2) 69 | self.alpha = alpha 70 | self.dAlpha_dD1 = dAlpha_dD1 71 | self.beta = beta 72 | self.dBeta_dD2 = dBeta_dD2 73 | self.x = self.strike 74 | self.dX_dS = 0.0 75 | 76 | def NPV(self): 77 | return self.discount * (self.forward * self.alpha + self.x * self.beta) 78 | 79 | def Alpha(self): 80 | # Replicate portfolio -- component shares of stock, 81 | # N(d1) for call / -N(-d1) for put 82 | return self.alpha 83 | 84 | def Beta(self): 85 | # Replicate portfolio -- component shares of borrowing/lending, 86 | # -N(d2) for call / N(-d2) for put 87 | return self.beta 88 | 89 | def Cash(self): 90 | return self.beta * self.strike * self.discount 91 | 92 | def Delta(self): 93 | if self.spot <= 0.0: 94 | return 95 | elif self.dt_eval == self.dt_maturity: 96 | if self.iscall: 97 | if self.strike < self.spot: 98 | delta = 1.0 99 | elif self.strike > self.spot: 100 | delta = 0.0 101 | else: 102 | delta = 0.5 103 | else: 104 | if self.strike > self.spot: 105 | delta = -1.0 106 | elif self.strike < self.spot: 107 | delta = 0.0 108 | else: 109 | delta = -0.5 110 | else: 111 | DforwardDs = self.forward / self.spot 112 | temp = self.stdDev * self.spot 113 | DalphaDs = self.dAlpha_dD1 / temp 114 | DbetaDs = self.dBeta_dD2 / temp 115 | temp2 = DalphaDs * self.forward + self.alpha * DforwardDs + DbetaDs * self.x \ 116 | + self.beta * self.dX_dS 117 | delta = self.discount * temp2 118 | return delta 119 | 120 | def Gamma(self): 121 | spot = self.spot 122 | if spot <= 0.0: 123 | return 124 | if self.dt_eval == self.dt_maturity: 125 | return 0.0 126 | DforwardDs = self.forward / spot 127 | temp = self.stdDev * spot 128 | DalphaDs = self.dAlpha_dD1 / temp 129 | DbetaDs = self.dBeta_dD2 / temp 130 | D2alphaDs2 = -DalphaDs / spot * (1 + self.D1 / self.stdDev) 131 | D2betaDs2 = -DbetaDs / spot * (1 + self.D2 / self.stdDev) 132 | temp2 = D2alphaDs2 * self.forward + 2.0 * DalphaDs * DforwardDs + D2betaDs2 * self.x \ 133 | + 2.0 * DbetaDs * self.dX_dS 134 | gamma = self.discount * temp2 135 | return gamma 136 | 137 | # 全Delta: dOption/dS = dOption/dS + dOption/dSigma * dSigma/dK 138 | # 根据SVI模型校准得到的隐含波动率的参数表达式,计算隐含波动率对行权价的一阶倒数(dSigma_dK) 139 | # def delta_total(self, dSigma_dK): 140 | # delta = self.Delta() 141 | # return delta + delta * dSigma_dK 142 | 143 | 144 | # mdt = datetime.date.today() + datetime.timedelta(days=30) 145 | # mdt2 = datetime.date.today() + datetime.timedelta(days=30*3) 146 | # p = BlackCalculator(datetime.date.today(),mdt,2.5,OptionType.PUT,2.5,0.25) 147 | # p2 = BlackCalculator(datetime.date.today(),mdt2,2.5,OptionType.PUT,2.5,0.25) 148 | # c = BlackCalculator(datetime.date.today(),mdt,1920,OptionType.CALL,1837,0.13) 149 | 150 | # print(p.NPV()*100/2.5) 151 | # print(p2.NPV()*100/2.5) 152 | # print(c.NPV()) 153 | -------------------------------------------------------------------------------- /OptionStrategyLib/OptionReplication/synthetic_option.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from copy import copy 3 | from PricingLibrary.BlackCalculator import BlackCalculator 4 | from PricingLibrary.Options import EuropeanOption 5 | from back_test.model.base_future_coutinuous import BaseFutureCoutinuous 6 | from back_test.model.constant import Util, FrequentType, DeltaBound, BuyWrite 7 | from PricingLibrary.Util import PricingUtil 8 | import math 9 | import datetime 10 | """ 11 | 基于期货构造合成期权,行权价可根据现货指数点位设定从而包含基差的影响。 12 | """ 13 | 14 | 15 | class SytheticOption(BaseFutureCoutinuous): 16 | def __init__(self, df_c1_data, 17 | df_c1_daily=None, 18 | df_futures_all_daily=None, 19 | df_index_daily=None, 20 | rf=0.03, 21 | frequency=FrequentType.MINUTE 22 | ): 23 | # 将主力合约作为一个产品使用name_code为id, 24 | df_c1_data = df_c1_data.reset_index(drop=True) 25 | name_code = df_c1_data.loc[0, Util.ID_INSTRUMENT].split('_')[0].lower() 26 | df_c1_data = df_c1_data.rename(columns={Util.ID_INSTRUMENT: Util.ID_FUTURE}) 27 | df_c1_data.loc[:, Util.ID_INSTRUMENT] = name_code 28 | if df_futures_all_daily is not None: 29 | df_futures_all_daily = df_futures_all_daily.rename(columns={Util.ID_INSTRUMENT: Util.ID_FUTURE}) 30 | df_futures_all_daily.loc[:, Util.ID_INSTRUMENT] = name_code 31 | 32 | super().__init__(df_future_c1=df_c1_data, df_future_c1_daily=df_c1_daily, 33 | df_futures_all_daily=df_futures_all_daily, df_underlying_index_daily=df_index_daily, 34 | rf=rf, frequency=frequency) 35 | self.synthetic_unit: int = 0 36 | self.amt_option = 0 37 | 38 | def get_c1_with_start_dates(self): 39 | df = self.df_daily_data.drop_duplicates(Util.ID_INSTRUMENT)[[Util.DT_DATE, Util.ID_INSTRUMENT]] 40 | return df 41 | 42 | def get_c1_with_end_dates(self): 43 | df = self.df_daily_data.drop_duplicates(Util.ID_INSTRUMENT, 'last')[[Util.DT_DATE, Util.ID_INSTRUMENT]] 44 | return df 45 | 46 | def get_black_delta(self, option: EuropeanOption, vol: float, spot: float = None): 47 | if spot is None: 48 | spot = self.mktprice_close() 49 | black = BlackCalculator(self.eval_date, option.dt_maturity, option.strike, 50 | option.option_type, spot, vol, self.rf) 51 | delta = black.Delta() 52 | return delta 53 | 54 | # Get synthetic position in trade unit 55 | def get_synthetic_unit(self, delta, buywrite=BuyWrite.BUY) -> int: 56 | trade_unit = np.floor(buywrite.value * delta * self.amt_option / self.multiplier()) 57 | return trade_unit 58 | 59 | # Get hedge position in trade unit 60 | def get_hedge_position(self, delta, buywrite=BuyWrite.BUY): 61 | # hedge_scale : total notional amt to hedge in RMB 62 | return - self.get_synthetic_unit(delta, buywrite) 63 | 64 | def get_hedge_rebalancing_unit(self, delta: float, 65 | buywrite: BuyWrite = BuyWrite.BUY) -> int: 66 | hold_unit = self.synthetic_unit 67 | synthetic_unit = self.get_synthetic_unit(delta, buywrite) 68 | d_unit = -(synthetic_unit - hold_unit) 69 | return d_unit 70 | # if delta_bound == DeltaBound.WHALLEY_WILLMOTT: 71 | # if vol is None or spot is None or gamma is None or dt_maturity is None: return 72 | # bound = self.whalley_wilmott2(self.eval_date, vol, spot, gamma, dt_maturity) * self.amt_option / self.multiplier() 73 | # if abs(d_unit) > bound: 74 | # return d_unit 75 | # else: 76 | # return 0 77 | # else: 78 | # return d_unit 79 | 80 | def get_rebalancing_unit(self, delta: float, 81 | option: EuropeanOption, 82 | vol: float, 83 | spot: float, 84 | delta_bound: DeltaBound, 85 | buywrite: BuyWrite = BuyWrite.BUY) -> int: 86 | hold_unit = self.synthetic_unit 87 | synthetic_unit = self.get_synthetic_unit(delta, buywrite) 88 | d_unit = synthetic_unit - hold_unit 89 | if delta_bound == DeltaBound.WHALLEY_WILLMOTT: 90 | bound = self.whalley_wilmott(self.eval_date, option, vol, spot) * self.amt_option / self.multiplier() 91 | if abs(d_unit) > bound: 92 | return d_unit 93 | else: 94 | return 0 95 | else: 96 | return d_unit 97 | 98 | def whalley_wilmott(self, eval_date, option, vol, spot=None, rho=0.5, fee=6.9 / 10000.0): 99 | if spot is None: 100 | spot = self.mktprice_close() 101 | black = BlackCalculator(self.eval_date, option.dt_maturity, option.strike, 102 | option.option_type, spot, vol, self.rf) 103 | gamma = black.Gamma() 104 | ttm = PricingUtil.get_ttm(eval_date, option.dt_maturity) 105 | H = (1.5 * math.exp(-self.rf * ttm) * fee * spot * (gamma ** 2) / rho) ** (1 / 3) 106 | return H 107 | 108 | def whalley_wilmott2(self, eval_date, vol, spot, gamma,dt_maturity, rho=0.5, fee=6.9 / 10000.0): 109 | ttm = PricingUtil.get_ttm(eval_date, dt_maturity) 110 | H = (1.5 * math.exp(-self.rf * ttm) * fee * spot * (gamma ** 2) / rho) ** (1 / 3) 111 | return H 112 | 113 | def portfolio_exposure(self, hedge_holding_unit): 114 | # TODO 115 | return 116 | 117 | def replicated_option_value(self, option: EuropeanOption, vol): 118 | spot = self.mktprice_close() 119 | black = BlackCalculator(self.eval_date, option.dt_maturity, option.strike, 120 | option.option_type, spot, vol, self.rf) 121 | npv = black.NPV() 122 | return npv 123 | --------------------------------------------------------------------------------