增加交易策略、交易指标、量化库代码等文件夹

This commit is contained in:
Win_home
2025-04-27 15:54:09 +08:00
parent ca3b209096
commit f57150dae8
589 changed files with 854346 additions and 1757 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
from scipy import stats
from math import log, pow, sqrt, exp
from typing import Tuple
cdf = stats.norm.cdf
pdf = stats.norm.pdf
def calculate_d1(
s: float,
k: float,
r: float,
t: float,
v: float
) -> float:
"""Calculate option D1 value"""
d1: float = (log(s / k) + (0.5 * pow(v, 2)) * t) / (v * sqrt(t))
return d1
def calculate_price(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option price"""
if v <= 0 or not t:
return max(0, cp * (s - k))
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
price: float = cp * (s * cdf(cp * d1) - k * cdf(cp * d2)) * exp(-r * t)
return price
def calculate_delta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option delta"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
delta: float = cp * exp(-r * t) * cdf(cp * d1)
return delta
def calculate_gamma(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option gamma"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
gamma: float = exp(-r * t) * pdf(d1) / (s * v * sqrt(t))
return gamma
def calculate_theta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option theta"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
theta: float = -s * exp(-r * t) * pdf(d1) * v / (2 * sqrt(t)) \
+ cp * r * s * exp(-r * t) * cdf(cp * d1) \
- cp * r * k * exp(-r * t) * cdf(cp * d2)
return theta
def calculate_vega(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option vega"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
vega: float = s * exp(-r * t) * pdf(d1) * sqrt(t)
return vega
def calculate_greeks(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int
) -> Tuple[float, float, float, float, float]:
"""Calculate option price and greeks"""
d1: float = calculate_d1(s, k, r, t, v)
price: float = calculate_price(s, k, r, t, v, cp, d1)
delta: float = calculate_delta(s, k, r, t, v, cp, d1)
gamma: float = calculate_gamma(s, k, r, t, v, d1)
theta: float = calculate_theta(s, k, r, t, v, cp, d1)
vega: float = calculate_vega(s, k, r, t, v, d1)
return price, delta, gamma, theta, vega
def calculate_impv(
price: float,
s: float,
k: float,
r: float,
t: float,
cp: int
):
"""Calculate option implied volatility"""
# Check option price must be positive
if price <= 0 or not t:
return 0
# Check if option price meets minimum value (exercise value)
meet: bool = False
if cp == 1 and (price > (s - k) * exp(-r * t)):
meet = True
elif cp == -1 and (price > k * exp(-r * t) - s):
meet = True
# If minimum value not met, return 0
if not meet:
return 0
# Calculate implied volatility with Newton's method
v: float = 0.01 # Initial guess of volatility
for i in range(50):
# Caculate option price and vega with current guess
p: float = calculate_price(s, k, r, t, v, cp)
vega: float = calculate_vega(s, k, r, t, v, cp)
# Break loop if vega too close to 0
if not vega:
break
# Calculate error value
dx: float = (price - p) / vega
# Check if error value meets requirement
if abs(dx) < 0.00001:
break
# Calculate guessed implied volatility of next round
v += dx
# Check end result to be non-negative
if v <= 0:
return 0
# Round to 4 decimal places
v = round(v, 4)
return v

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
from scipy import stats
from math import log, pow, sqrt, exp
from typing import Tuple
cdf = stats.norm.cdf
pdf = stats.norm.pdf
def calculate_d1(
s: float,
k: float,
r: float,
t: float,
v: float
) -> float:
"""Calculate option D1 value"""
d1: float = (log(s / k) + (0.5 * pow(v, 2)) * t) / (v * sqrt(t))
return d1
def calculate_price(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option price"""
if v <= 0 or not t:
return max(0, cp * (s - k))
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
price: float = cp * (s * cdf(cp * d1) - k * cdf(cp * d2)) * exp(-r * t)
return price
def calculate_delta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option delta"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
delta: float = cp * exp(-r * t) * cdf(cp * d1)
return delta
def calculate_gamma(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option gamma"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
gamma: float = exp(-r * t) * pdf(d1) / (s * v * sqrt(t))
return gamma
def calculate_theta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option theta"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
theta: float = -s * exp(-r * t) * pdf(d1) * v / (2 * sqrt(t)) \
+ cp * r * s * exp(-r * t) * cdf(cp * d1) \
- cp * r * k * exp(-r * t) * cdf(cp * d2)
return theta
def calculate_vega(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option vega"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
vega: float = s * exp(-r * t) * pdf(d1) * sqrt(t)
return vega
def calculate_greeks(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int
) -> Tuple[float, float, float, float, float]:
"""Calculate option price and greeks"""
d1: float = calculate_d1(s, k, r, t, v)
price: float = calculate_price(s, k, r, t, v, cp, d1)
delta: float = calculate_delta(s, k, r, t, v, cp, d1)
gamma: float = calculate_gamma(s, k, r, t, v, d1)
theta: float = calculate_theta(s, k, r, t, v, cp, d1)
vega: float = calculate_vega(s, k, r, t, v, d1)
return price, delta, gamma, theta, vega
def calculate_impv(
price: float,
s: float,
k: float,
r: float,
t: float,
cp: int
):
"""Calculate option implied volatility"""
# Check option price must be positive
if price <= 0 or not t:
return 0
# Check if option price meets minimum value (exercise value)
meet: bool = False
if cp == 1 and (price > (s - k) * exp(-r * t)):
meet = True
elif cp == -1 and (price > k * exp(-r * t) - s):
meet = True
# If minimum value not met, return 0
if not meet:
return 0
# Calculate implied volatility with Newton's method
v: float = 0.01 # Initial guess of volatility
for i in range(50):
# Caculate option price and vega with current guess
p: float = calculate_price(s, k, r, t, v, cp)
vega: float = calculate_vega(s, k, r, t, v, cp)
# Break loop if vega too close to 0
if not vega:
break
# Calculate error value
dx: float = (price - p) / vega
# Check if error value meets requirement
if abs(dx) < 0.00001:
break
# Calculate guessed implied volatility of next round
v += dx
# Check end result to be non-negative
if v <= 0:
return 0
# Round to 4 decimal places
v = round(v, 4)
return v

View File

@@ -0,0 +1,997 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "1fee51d2-8d41-468e-b416-a58bed1ddbb9",
"metadata": {},
"source": [
"数据字典地址http://docs.thinktrader.net/pages/7c0936/"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "e63c7fc2-2500-4d50-a5af-8af4aaa91c1b",
"metadata": {},
"outputs": [],
"source": [
"# Token连接若通过客户端连接则无需运行此单元格\n",
"from vnpy.trader.utility import TEMP_DIR\n",
"\n",
"from xtquant import xtdatacenter as xtdc\n",
"\n",
"# 设置迅投研令牌\n",
"xtdc.set_token(\"4aff6f3b0dcfc990ec9476213ba784e17c34e757\") # 换成自己的Token\n",
"\n",
"# 设置临时文件目录\n",
"xtdc.set_data_home_dir(str(TEMP_DIR) + \"\\\\xt\")\n",
"\n",
"# 初始化连接\n",
"xtdc.init()"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "cc8d3d17-28bb-42a9-aea7-95879d9a5994",
"metadata": {},
"outputs": [],
"source": [
"from xtquant.xtdata import (\n",
" download_history_data,\n",
" download_financial_data,\n",
" get_local_data,\n",
" get_stock_list_in_sector,\n",
" get_instrument_detail,\n",
" get_financial_data,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7a04439d-b14f-48a9-9269-420a0c7c2c58",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'90000001.SZO': Empty DataFrame\n",
" Columns: [time, open, high, low, close, volume, amount, settelementPrice, openInterest, preClose, suspendFlag]\n",
" Index: []}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 期权数据1d也支持\n",
"download_history_data(\"90000001.SZO\", \"1m\", \"20200101\", \"20200112\")\n",
"data1 = get_local_data([], [\"90000001.SZO\"], \"1m\", \"20200101\", \"20200112\")\n",
"data1"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "a9347593-3ed2-42a6-93e2-534ffe5fbc09",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>time</th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>volume</th>\n",
" <th>amount</th>\n",
" <th>settelementPrice</th>\n",
" <th>openInterest</th>\n",
" <th>preClose</th>\n",
" <th>suspendFlag</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
"Empty DataFrame\n",
"Columns: [time, open, high, low, close, volume, amount, settelementPrice, openInterest, preClose, suspendFlag]\n",
"Index: []"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data1[\"90000001.SZO\"]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "32f8a0db",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'rb2401.SF': time open high low close volume \\\n",
" 20231026090100 1698282060000 3665.0 3669.0 3665.0 3667.0 7834 \n",
" 20231026090200 1698282120000 3668.0 3668.0 3666.0 3668.0 3807 \n",
" 20231026090300 1698282180000 3668.0 3669.0 3665.0 3666.0 4264 \n",
" 20231026090400 1698282240000 3666.0 3669.0 3666.0 3668.0 2269 \n",
" 20231026090500 1698282300000 3668.0 3668.0 3666.0 3667.0 1518 \n",
" ... ... ... ... ... ... ... \n",
" 20231030225600 1698677760000 3723.0 3724.0 3722.0 3724.0 2955 \n",
" 20231030225700 1698677820000 3723.0 3724.0 3720.0 3721.0 4641 \n",
" 20231030225800 1698677880000 3720.0 3723.0 3720.0 3723.0 3093 \n",
" 20231030225900 1698677940000 3722.0 3723.0 3721.0 3722.0 2247 \n",
" 20231030230000 1698678000000 3721.0 3723.0 3720.0 3722.0 5145 \n",
" \n",
" amount settelementPrice openInterest preClose \\\n",
" 20231026090100 287264340.0 0.0 1762615 3665.0 \n",
" 20231026090200 139616890.0 0.0 1762851 3667.0 \n",
" 20231026090300 156379480.0 0.0 1763036 3668.0 \n",
" 20231026090400 83222740.0 0.0 1763346 3666.0 \n",
" 20231026090500 55658560.0 0.0 1763165 3668.0 \n",
" ... ... ... ... ... \n",
" 20231030225600 110019310.0 0.0 1690326 3722.0 \n",
" 20231030225700 172706690.0 0.0 1689474 3724.0 \n",
" 20231030225800 115105860.0 0.0 1688648 3721.0 \n",
" 20231030225900 83633400.0 0.0 1688086 3723.0 \n",
" 20231030230000 191473920.0 0.0 1686985 3722.0 \n",
" \n",
" suspendFlag \n",
" 20231026090100 0 \n",
" 20231026090200 0 \n",
" 20231026090300 0 \n",
" 20231026090400 0 \n",
" 20231026090500 0 \n",
" ... ... \n",
" 20231030225600 0 \n",
" 20231030225700 0 \n",
" 20231030225800 0 \n",
" 20231030225900 0 \n",
" 20231030230000 0 \n",
" \n",
" [1455 rows x 11 columns]}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 期货数据1d也支持\n",
"download_history_data(\"rb2401.SF\", \"1m\", \"20230601\", \"20231030\")\n",
"data1 = get_local_data([], [\"rb2401.SF\"], \"1m\", \"20231026\", \"20231030\")\n",
"data1"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "1ade0d1d-1905-4316-b1b4-ebd62cc3834c",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'rb2309.SF': Empty DataFrame\n",
" Columns: [time, lastPrice, open, high, low, lastClose, amount, volume, pvolume, stockStatus, openInt, lastSettlementPrice, askPrice, bidPrice, askVol, bidVol, settlementPrice, transactionNum]\n",
" Index: []}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# L1 Tick数据\n",
"download_history_data(\"rb2309.SF\", \"tick\", \"20230821\", \"20230822\")\n",
"data2 = get_local_data([], [\"rb2309.SF\"], \"tick\", \"20230821\", \"20230822\")\n",
"data2"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "a6c481e8",
"metadata": {},
"outputs": [],
"source": [
"# 下载历史合约信息\n",
"download_history_data(\"\", \"historycontract\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4aea61d2-aab4-4189-b19f-862ec22d6a34",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4807\n"
]
}
],
"source": [
"# 期权过期合约查询\n",
"l = get_stock_list_in_sector(\"过期中金所\")\n",
"# get_stock_list_in_sector(\"过期上证期权\")\n",
"# get_stock_list_in_sector(\"过期深证期权\")\n",
"print(len(l))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "681ffa29-c6e5-46cb-a519-7461425cc73f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['00.IF', 'HO2301-C-2325.IF', 'HO2301-C-2350.IF']"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l[:3]"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "1f76b380-9d30-4dde-a2b6-913193b49b60",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'ExchangeID': 'SZO',\n",
" 'InstrumentID': '90000001',\n",
" 'InstrumentName': '300ETF购1月3700',\n",
" 'Abbreviation': '300ETFG1Y3700',\n",
" 'ProductID': '300ETF(159919)',\n",
" 'ProductName': '',\n",
" 'UnderlyingCode': '',\n",
" 'ExtendName': '',\n",
" 'ExchangeCode': '',\n",
" 'RzrkCode': '',\n",
" 'UniCode': '',\n",
" 'CreateDate': '20191223',\n",
" 'OpenDate': '20191223',\n",
" 'ExpireDate': '20200122',\n",
" 'PreClose': 0.4652,\n",
" 'SettlementPrice': 0.4714,\n",
" 'UpStopPrice': 0.8885,\n",
" 'DownStopPrice': 0.0001,\n",
" 'FloatVolume': 0.0,\n",
" 'TotalVolume': 0.0,\n",
" 'AccumulatedInterest': 1.7976931348623157e+308,\n",
" 'LongMarginRatio': 0.0,\n",
" 'ShortMarginRatio': 0.0,\n",
" 'PriceTick': 0.0001,\n",
" 'VolumeMultiple': 10000,\n",
" 'MainContract': 0,\n",
" 'MaxMarketOrderVolume': 10,\n",
" 'MinMarketOrderVolume': 1,\n",
" 'MaxLimitOrderVolume': 50,\n",
" 'MinLimitOrderVolume': 1,\n",
" 'MaxMarginSideAlgorithm': 49,\n",
" 'DayCountFromIPO': 2147483647,\n",
" 'LastVolume': 239,\n",
" 'InstrumentStatus': 0,\n",
" 'IsTrading': False,\n",
" 'IsRecent': False,\n",
" 'IsContinuous': False,\n",
" 'bNotProfitable': False,\n",
" 'bDualClass': False,\n",
" 'ContinueType': 0,\n",
" 'secuCategory': 0,\n",
" 'secuAttri': 0,\n",
" 'MaxMarketSellOrderVolume': 2147483647,\n",
" 'MinMarketSellOrderVolume': 0,\n",
" 'MaxLimitSellOrderVolume': 2147483647,\n",
" 'MinLimitSellOrderVolume': 0,\n",
" 'MaxFixedBuyOrderVol': 2147483647,\n",
" 'MinFixedBuyOrderVol': 0,\n",
" 'MaxFixedSellOrderVol': 2147483647,\n",
" 'MinFixedSellOrderVol': 0,\n",
" 'HSGTFlag': 0,\n",
" 'BondParValue': 1.7976931348623157e+308,\n",
" 'QualifiedType': 2147483647,\n",
" 'PriceTickType': 2147483647,\n",
" 'tradingStatus': '',\n",
" 'ExtendInfo': {'OptUnit': 1.7976931348623157e+308,\n",
" 'MarginUnit': 9719.2,\n",
" 'OptUndlCode': '159919',\n",
" 'OptUndlMarket': 'SZ',\n",
" 'OptLotSize': 0,\n",
" 'OptExercisePrice': 3.7,\n",
" 'NeeqExeType': 0,\n",
" 'OptExchFixedMargin': 1.7976931348623157e+308,\n",
" 'OptExchMiniMargin': 1.7976931348623157e+308,\n",
" 'Ccy': '',\n",
" 'IbSecType': '',\n",
" 'OptUndlRiskFreeRate': 0.0,\n",
" 'OptUndlHistoryRate': 0.0,\n",
" 'EndDelivDate': '20200122',\n",
" 'RegisteredCapital': 2147483647,\n",
" 'MaxOrderPriceRange': 1.7976931348623157e+308,\n",
" 'MinOrderPriceRange': 1.7976931348623157e+308,\n",
" 'VoteRightRatio': 1.0,\n",
" 'm_nMinRepurchaseDaysLimit': 2147483647,\n",
" 'm_nMaxRepurchaseDaysLimit': 2147483647,\n",
" 'DeliveryYear': 0,\n",
" 'DeliveryMonth': 0,\n",
" 'ContractType': 0,\n",
" 'ProductTradeQuota': 0,\n",
" 'ContractTradeQuota': 0,\n",
" 'ProductOpenInterestQuota': 0,\n",
" 'ContractOpenInterestQuota': 0}}"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 获取期权合约信息\n",
"get_instrument_detail(\"90000001.SZO\", True)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "7269450a-ea0c-4652-9d0d-bd31b572067a",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'600519.SH': {'Balance': m_timetag m_anntime internal_shoule_recv fixed_capital_clearance \\\n",
" 0 20010630 20010822 NaN NaN \n",
" 1 20011231 20020417 NaN 0.0 \n",
" 2 20020331 20020429 NaN 0.0 \n",
" 3 20020630 20020814 NaN 0.0 \n",
" 4 20020930 20021029 NaN 0.0 \n",
" .. ... ... ... ... \n",
" 107 20221231 20230803 NaN NaN \n",
" 108 20221231 20231021 NaN NaN \n",
" 109 20230331 20230426 NaN NaN \n",
" 110 20230630 20230803 NaN NaN \n",
" 111 20230930 20231021 NaN NaN \n",
" \n",
" should_pay_money settlement_payment receivable_premium \\\n",
" 0 NaN 0.0 NaN \n",
" 1 NaN 0.0 NaN \n",
" 2 NaN 0.0 NaN \n",
" 3 NaN 0.0 NaN \n",
" 4 NaN 0.0 NaN \n",
" .. ... ... ... \n",
" 107 NaN NaN NaN \n",
" 108 NaN NaN NaN \n",
" 109 NaN NaN NaN \n",
" 110 NaN NaN NaN \n",
" 111 NaN NaN NaN \n",
" \n",
" accounts_receivable_reinsurance reinsurance_contract_reserve \\\n",
" 0 NaN 0.0 \n",
" 1 NaN 0.0 \n",
" 2 NaN 0.0 \n",
" 3 NaN 0.0 \n",
" 4 NaN 0.0 \n",
" .. ... ... \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" 110 NaN NaN \n",
" 111 NaN NaN \n",
" \n",
" dividends_payable ... m_guaranteeInvestmentFunds \\\n",
" 0 NaN ... NaN \n",
" 1 0.0 ... NaN \n",
" 2 0.0 ... NaN \n",
" 3 0.0 ... NaN \n",
" 4 0.0 ... NaN \n",
" .. ... ... ... \n",
" 107 NaN ... NaN \n",
" 108 NaN ... NaN \n",
" 109 NaN ... NaN \n",
" 110 NaN ... NaN \n",
" 111 NaN ... NaN \n",
" \n",
" m_premiumsReceivedAdvance m_insuranceLiabilities \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" 110 NaN NaN \n",
" 111 NaN NaN \n",
" \n",
" m_liabilitiesIndependentAccounts m_liabilitiesVicariousBusiness \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" 110 NaN NaN \n",
" 111 NaN NaN \n",
" \n",
" m_otherLiablities m_capitalPremium m_petainedProfit \\\n",
" 0 NaN NaN NaN \n",
" 1 NaN NaN NaN \n",
" 2 NaN NaN NaN \n",
" 3 NaN NaN NaN \n",
" 4 NaN NaN NaN \n",
" .. ... ... ... \n",
" 107 NaN NaN NaN \n",
" 108 NaN NaN NaN \n",
" 109 NaN NaN NaN \n",
" 110 NaN NaN NaN \n",
" 111 NaN NaN NaN \n",
" \n",
" m_provisionTransactionRisk m_otherReserves \n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" 110 NaN NaN \n",
" 111 NaN NaN \n",
" \n",
" [112 rows x 146 columns],\n",
" 'Income': m_timetag m_anntime revenue_inc earned_premium \\\n",
" 0 20010630 20010822 8.738635e+08 NaN \n",
" 1 20011231 20020417 1.618047e+09 NaN \n",
" 2 20020331 20020429 7.180149e+08 NaN \n",
" 3 20020630 20020814 9.556542e+08 NaN \n",
" 4 20020930 20021029 1.363886e+09 NaN \n",
" .. ... ... ... ... \n",
" 105 20220930 20221017 8.716023e+10 NaN \n",
" 106 20221231 20230331 1.240998e+11 NaN \n",
" 107 20230331 20230426 3.875581e+10 NaN \n",
" 108 20230630 20230803 6.957602e+10 NaN \n",
" 109 20230930 20231021 1.032684e+11 NaN \n",
" \n",
" real_estate_sales_income total_operating_cost real_estate_sales_cost \\\n",
" 0 NaN NaN NaN \n",
" 1 NaN 1.009061e+09 NaN \n",
" 2 NaN 3.808385e+08 NaN \n",
" 3 NaN 5.545605e+08 NaN \n",
" 4 NaN 8.382068e+08 NaN \n",
" .. ... ... ... \n",
" 105 NaN 2.789089e+10 NaN \n",
" 106 NaN 3.974831e+10 NaN \n",
" 107 NaN 1.071560e+10 NaN \n",
" 108 NaN 2.117930e+10 NaN \n",
" 109 NaN 3.223569e+10 NaN \n",
" \n",
" research_expenses surrender_value net_payments ... \\\n",
" 0 NaN NaN NaN ... \n",
" 1 NaN NaN NaN ... \n",
" 2 NaN NaN NaN ... \n",
" 3 NaN NaN NaN ... \n",
" 4 NaN NaN NaN ... \n",
" .. ... ... ... ... \n",
" 105 1.149345e+08 NaN NaN ... \n",
" 106 1.351857e+08 NaN NaN ... \n",
" 107 2.295621e+07 NaN NaN ... \n",
" 108 7.277068e+07 NaN NaN ... \n",
" 109 1.011260e+08 NaN NaN ... \n",
" \n",
" m_paymentsInsuranceClaims m_amortizedCompensationExpenses \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 105 NaN NaN \n",
" 106 NaN NaN \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" \n",
" m_netReserveInsuranceLiability m_policyReserve \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 105 NaN NaN \n",
" 106 NaN NaN \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" \n",
" m_amortizeInsuranceReserve m_nsuranceFeesCommissionExpenses \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 105 NaN NaN \n",
" 106 NaN NaN \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" \n",
" m_operationAdministrativeExpense m_amortizedReinsuranceExpenditure \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 105 NaN NaN \n",
" 106 NaN NaN \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" \n",
" m_netProfitLossdisposalNonassets m_otherItemsAffectingNetProfit \n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 105 NaN NaN \n",
" 106 NaN NaN \n",
" 107 NaN NaN \n",
" 108 NaN NaN \n",
" 109 NaN NaN \n",
" \n",
" [110 rows x 71 columns],\n",
" 'CashFlow': m_timetag m_anntime cash_received_ori_ins_contract_pre \\\n",
" 0 20010630 20010822 NaN \n",
" 1 20011231 20020417 NaN \n",
" 2 20020630 20020814 NaN \n",
" 3 20021231 20030326 NaN \n",
" 4 20030331 20030425 NaN \n",
" .. ... ... ... \n",
" 86 20220930 20221017 NaN \n",
" 87 20221231 20230331 NaN \n",
" 88 20230331 20230426 NaN \n",
" 89 20230630 20230803 NaN \n",
" 90 20230930 20231021 NaN \n",
" \n",
" net_cash_received_rei_ope net_increase_insured_funds \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 86 NaN NaN \n",
" 87 NaN NaN \n",
" 88 NaN NaN \n",
" 89 NaN NaN \n",
" 90 NaN NaN \n",
" \n",
" net_increase_in_disposal cash_for_interest \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 86 NaN 2.494929e+09 \n",
" 87 NaN 3.247615e+09 \n",
" 88 NaN 8.549059e+08 \n",
" 89 NaN 1.762971e+09 \n",
" 90 NaN 2.334503e+09 \n",
" \n",
" net_increase_in_repurchase_funds cash_for_payment_original_insurance \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 86 NaN NaN \n",
" 87 NaN NaN \n",
" 88 NaN NaN \n",
" 89 NaN NaN \n",
" 90 NaN NaN \n",
" \n",
" cash_payment_policy_dividends ... m_netReductionDeposInveFunds \\\n",
" 0 NaN ... NaN \n",
" 1 NaN ... NaN \n",
" 2 NaN ... NaN \n",
" 3 NaN ... NaN \n",
" 4 NaN ... NaN \n",
" .. ... ... ... \n",
" 86 NaN ... NaN \n",
" 87 NaN ... NaN \n",
" 88 NaN ... NaN \n",
" 89 NaN ... NaN \n",
" 90 NaN ... NaN \n",
" \n",
" m_netIncreaseUnwindingFunds m_netReductionAmountBorrowedFunds \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 86 NaN NaN \n",
" 87 NaN NaN \n",
" 88 NaN NaN \n",
" 89 NaN NaN \n",
" 90 NaN NaN \n",
" \n",
" m_netReductionSaleRepurchaseProceeds m_investmentPaidInCash \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 86 NaN NaN \n",
" 87 NaN NaN \n",
" 88 NaN NaN \n",
" 89 NaN NaN \n",
" 90 NaN NaN \n",
" \n",
" m_paymentOtherCashRelated m_cashOutFlowsInvesactivities \\\n",
" 0 NaN NaN \n",
" 1 NaN NaN \n",
" 2 NaN NaN \n",
" 3 NaN NaN \n",
" 4 NaN NaN \n",
" .. ... ... \n",
" 86 NaN NaN \n",
" 87 NaN NaN \n",
" 88 NaN NaN \n",
" 89 NaN NaN \n",
" 90 NaN NaN \n",
" \n",
" m_absorbCashEquityInv m_otherImpactsOnCash m_addOperatingReceivableItems \n",
" 0 NaN NaN NaN \n",
" 1 NaN NaN NaN \n",
" 2 NaN NaN NaN \n",
" 3 NaN NaN NaN \n",
" 4 NaN NaN NaN \n",
" .. ... ... ... \n",
" 86 NaN NaN NaN \n",
" 87 NaN NaN NaN \n",
" 88 NaN NaN NaN \n",
" 89 NaN NaN NaN \n",
" 90 NaN NaN NaN \n",
" \n",
" [91 rows x 110 columns],\n",
" 'Capital': m_timetag m_anntime total_capital circulating_capital \\\n",
" 0 20010827 20010822 2.500000e+08 7.150000e+07 \n",
" 1 20020726 20020718 2.750000e+08 7.865000e+07 \n",
" 2 20030715 20030708 3.025000e+08 8.651500e+07 \n",
" 3 20040702 20040625 3.932500e+08 1.124695e+08 \n",
" 4 20050808 20050730 4.719000e+08 1.349634e+08 \n",
" 5 20060509 20060509 4.719000e+08 1.349634e+08 \n",
" 6 20060525 20060522 9.438000e+08 3.023180e+08 \n",
" 7 20070525 20070518 9.438000e+08 4.079310e+08 \n",
" 8 20080526 20080520 9.438000e+08 4.551210e+08 \n",
" 9 20090525 20090519 9.438000e+08 9.438000e+08 \n",
" 10 20090630 20090807 9.438000e+08 9.438000e+08 \n",
" 11 20100630 20100812 9.438000e+08 9.438000e+08 \n",
" 12 20101231 20110321 9.438000e+08 9.438000e+08 \n",
" 13 20110630 20110831 9.438000e+08 9.438000e+08 \n",
" 14 20110704 20110627 1.038180e+09 1.038180e+09 \n",
" 15 20111231 20120411 1.038180e+09 1.038180e+09 \n",
" 16 20121231 20130329 1.038180e+09 1.038180e+09 \n",
" 17 20130630 20130831 1.038180e+09 1.038180e+09 \n",
" 18 20130930 20131016 1.038180e+09 1.038180e+09 \n",
" 19 20140626 20140618 1.141998e+09 1.141998e+09 \n",
" 20 20140630 20140829 1.141998e+09 1.141998e+09 \n",
" 21 20150331 20150421 1.141998e+09 1.141998e+09 \n",
" 22 20150630 20150828 1.141998e+09 1.141998e+09 \n",
" 23 20150720 20150710 1.256198e+09 1.256198e+09 \n",
" 24 20150930 20151023 1.256198e+09 1.256198e+09 \n",
" 25 20151231 20160324 1.256198e+09 1.256198e+09 \n",
" 26 20160630 20160827 1.256198e+09 1.256198e+09 \n",
" 27 20160930 20161029 1.256198e+09 1.256198e+09 \n",
" 28 20170331 20170425 1.256198e+09 1.256198e+09 \n",
" 29 20170630 20170728 1.256198e+09 1.256198e+09 \n",
" 30 20191231 20200422 1.256198e+09 1.256198e+09 \n",
" 31 20200331 20200428 1.256198e+09 1.256198e+09 \n",
" 32 20201230 20201231 1.256198e+09 1.256198e+09 \n",
" 33 20230210 20230211 1.256198e+09 1.256198e+09 \n",
" 34 20230331 20230426 1.256198e+09 1.256198e+09 \n",
" 35 20230626 20230628 1.256198e+09 1.256198e+09 \n",
" 36 20230630 20230803 1.256198e+09 1.256198e+09 \n",
" \n",
" restrict_circulating_capital freeFloatCapital \n",
" 0 0.0 0.0 \n",
" 1 0.0 0.0 \n",
" 2 0.0 0.0 \n",
" 3 0.0 0.0 \n",
" 4 0.0 0.0 \n",
" 5 0.0 0.0 \n",
" 6 0.0 0.0 \n",
" 7 0.0 0.0 \n",
" 8 0.0 0.0 \n",
" 9 0.0 0.0 \n",
" 10 0.0 330036900.0 \n",
" 11 0.0 328861200.0 \n",
" 12 0.0 332114800.0 \n",
" 13 0.0 331784800.0 \n",
" 14 0.0 364963280.0 \n",
" 15 0.0 366660300.0 \n",
" 16 0.0 396490000.0 \n",
" 17 0.0 366224100.0 \n",
" 18 0.0 364309100.0 \n",
" 19 0.0 434023700.0 \n",
" 20 0.0 434023000.0 \n",
" 21 0.0 404739300.0 \n",
" 22 0.0 434023000.0 \n",
" 23 0.0 477425300.0 \n",
" 24 0.0 477425800.0 \n",
" 25 0.0 477425845.0 \n",
" 26 0.0 449613757.0 \n",
" 27 0.0 477425845.0 \n",
" 28 0.0 449613757.0 \n",
" 29 0.0 477425845.0 \n",
" 30 0.0 527665845.0 \n",
" 31 0.0 499853757.0 \n",
" 32 0.0 550093757.0 \n",
" 33 0.0 549945427.0 \n",
" 34 0.0 549253243.0 \n",
" 35 0.0 549140936.0 \n",
" 36 0.0 549136536.0 ,\n",
" 'HolderNum': declareDate endDate shareholder shareholderA shareholderB \\\n",
" 0 20030331 20030331 36706.0 36706.0 0.0 \n",
" 1 20030630 20030630 38846.0 38846.0 0.0 \n",
" 2 20030930 20030930 37305.0 37305.0 0.0 \n",
" 3 20040326 20031231 32349.0 32349.0 0.0 \n",
" 4 20040429 20040331 21736.0 21736.0 0.0 \n",
" .. ... ... ... ... ... \n",
" 78 20230331 20221231 167516.0 167516.0 0.0 \n",
" 79 20230331 20230228 159541.0 159541.0 0.0 \n",
" 80 20230426 20230331 156190.0 156190.0 0.0 \n",
" 81 20230803 20230630 161750.0 161750.0 0.0 \n",
" 82 20231021 20230930 150025.0 150025.0 0.0 \n",
" \n",
" shareholderH shareholderFloat shareholderOther \n",
" 0 0.0 NaN NaN \n",
" 1 0.0 38838.0 8.0 \n",
" 2 0.0 NaN NaN \n",
" 3 0.0 NaN NaN \n",
" 4 0.0 NaN NaN \n",
" .. ... ... ... \n",
" 78 0.0 167516.0 0.0 \n",
" 79 0.0 0.0 0.0 \n",
" 80 0.0 0.0 0.0 \n",
" 81 0.0 0.0 0.0 \n",
" 82 0.0 0.0 0.0 \n",
" \n",
" [83 rows x 8 columns],\n",
" 'Top10Holder': declareDate endDate name type quantity reason ratio nature \\\n",
" 0 20010726 20010827 0.0 0.0 161706052.0 0.0 64.680 0.0 \n",
" 1 20010726 20010827 0.0 0.0 338000.0 0.0 0.135 0.0 \n",
" 2 20010726 20010827 0.0 0.0 10000000.0 0.0 4.000 0.0 \n",
" 3 20010726 20010827 0.0 0.0 1500000.0 0.0 0.600 0.0 \n",
" 4 20010726 20010827 0.0 0.0 1443804.0 0.0 0.580 0.0 \n",
" .. ... ... ... ... ... ... ... ... \n",
" 589 20230331 20221231 0.0 0.0 5379160.0 0.0 0.430 0.0 \n",
" 590 20230426 20230331 0.0 0.0 679099269.0 0.0 54.060 0.0 \n",
" 591 20230628 20230626 0.0 0.0 679211576.0 0.0 54.070 0.0 \n",
" 592 20230803 20230630 0.0 0.0 679211576.0 0.0 54.070 0.0 \n",
" 593 20231021 20230930 0.0 0.0 679211576.0 0.0 54.070 0.0 \n",
" \n",
" rank \n",
" 0 1.0 \n",
" 1 10.0 \n",
" 2 2.0 \n",
" 3 3.0 \n",
" 4 4.0 \n",
" .. ... \n",
" 589 10.0 \n",
" 590 1.0 \n",
" 591 1.0 \n",
" 592 1.0 \n",
" 593 1.0 \n",
" \n",
" [594 rows x 9 columns],\n",
" 'Top10FlowHolder': declareDate endDate name type quantity reason ratio nature \\\n",
" 0 20040326 20031231 0.0 0.0 1280917.0 0.0 0.4230 0.0 \n",
" 1 20040326 20031231 0.0 0.0 1189829.0 0.0 0.3930 0.0 \n",
" 2 20040326 20031231 0.0 0.0 1000000.0 0.0 0.3310 0.0 \n",
" 3 20040326 20031231 0.0 0.0 3342834.0 0.0 1.1050 0.0 \n",
" 4 20040326 20031231 0.0 0.0 907407.0 0.0 0.3000 0.0 \n",
" .. ... ... ... ... ... ... ... ... \n",
" 668 20230331 20221231 0.0 0.0 5445803.0 0.0 0.4335 0.0 \n",
" 669 20230331 20221231 0.0 0.0 5379160.0 0.0 0.4282 0.0 \n",
" 670 20230426 20230331 0.0 0.0 679099269.0 0.0 54.0600 0.0 \n",
" 671 20230803 20230630 0.0 0.0 679211576.0 0.0 54.0700 0.0 \n",
" 672 20231021 20230930 0.0 0.0 679211576.0 0.0 54.0700 0.0 \n",
" \n",
" rank \n",
" 0 5.0 \n",
" 1 6.0 \n",
" 2 8.0 \n",
" 3 1.0 \n",
" 4 9.0 \n",
" .. ... \n",
" 668 9.0 \n",
" 669 10.0 \n",
" 670 1.0 \n",
" 671 1.0 \n",
" 672 1.0 \n",
" \n",
" [673 rows x 9 columns],\n",
" 'PershareIndex': m_timetag m_anntime s_fa_ocfps s_fa_bps s_fa_eps_basic \\\n",
" 0 20070331 20070428 0.4032 6.1700 0.57 \n",
" 1 20070630 20070817 0.7267 24.0100 0.90 \n",
" 2 20070930 20071023 1.6405 7.2800 1.69 \n",
" 3 20071231 20090325 1.8471 8.7200 3.00 \n",
" 4 20080331 20080422 1.0518 9.6500 0.93 \n",
" .. ... ... ... ... ... \n",
" 68 20220930 20221017 7.4871 164.5511 35.34 \n",
" 69 20221231 20230331 29.2140 157.2258 49.93 \n",
" 70 20230331 20230426 4.1751 173.7590 16.55 \n",
" 71 20230630 20230803 24.1898 159.9399 28.64 \n",
" 72 20230930 20231021 39.8042 173.3872 42.09 \n",
" \n",
" s_fa_eps_diluted s_fa_undistributedps s_fa_surpluscapitalps \\\n",
" 0 NaN 2.3815 1.4566 \n",
" 1 0.90 2.7022 1.4567 \n",
" 2 1.69 3.4870 1.4568 \n",
" 3 3.00 5.3793 1.4568 \n",
" 4 NaN 6.3047 1.4569 \n",
" .. ... ... ... \n",
" 68 35.34 136.9667 1.0945 \n",
" 69 49.93 128.4049 1.0945 \n",
" 70 16.55 144.9397 1.0945 \n",
" 71 28.64 129.9407 1.0945 \n",
" 72 42.09 143.3907 1.0945 \n",
" \n",
" adjusted_earnings_per_share du_return_on_equity ... equity_roe \\\n",
" 0 NaN 9.6551 ... NaN \n",
" 1 0.899 14.8845 ... 14.88 \n",
" 2 1.680 26.1630 ... NaN \n",
" 3 3.000 39.5989 ... 39.30 \n",
" 4 NaN 10.0724 ... NaN \n",
" .. ... ... ... ... \n",
" 68 NaN 22.4101 ... 21.91 \n",
" 69 49.990 32.4077 ... 30.26 \n",
" 70 NaN 10.0028 ... 10.00 \n",
" 71 28.620 18.0614 ... 16.70 \n",
" 72 NaN 25.4632 ... 24.82 \n",
" \n",
" net_roe total_roe gross_profit net_profit actual_tax_rate \\\n",
" 0 9.2101 NaN 85.0268 35.3655 NaN \n",
" 1 13.8530 NaN 85.1958 34.0022 NaN \n",
" 2 23.1357 NaN 85.1373 37.4401 NaN \n",
" 3 34.3793 NaN 87.9565 40.9821 NaN \n",
" 4 9.5894 NaN 89.9267 46.0020 NaN \n",
" .. ... ... ... ... ... \n",
" 68 21.4794 NaN 91.8744 53.1366 NaN \n",
" 69 31.7541 NaN 91.8667 52.6795 NaN \n",
" 70 9.5269 NaN 92.5956 55.5393 NaN \n",
" 71 17.9082 NaN 91.7981 53.6564 NaN \n",
" 72 24.2765 NaN 91.7075 53.0919 NaN \n",
" \n",
" pre_pay_operate_income sales_cash_flow gear_ratio inventory_turnover \n",
" 0 1.4417 1.2356 40.4210 0.1192 \n",
" 1 0.9290 1.2867 39.1859 0.1898 \n",
" 2 0.4992 1.1884 31.7955 0.3247 \n",
" 3 0.1555 1.0277 20.1557 0.4068 \n",
" 4 0.6517 1.2842 19.6501 0.0856 \n",
" .. ... ... ... ... \n",
" 68 NaN 1.1017 13.8484 0.2066 \n",
" 69 NaN 1.1337 19.4210 0.2795 \n",
" 70 NaN 0.9228 12.3562 0.0728 \n",
" 71 NaN 0.9928 13.3925 0.1432 \n",
" 72 NaN 1.0773 14.1400 0.2146 \n",
" \n",
" [73 rows x 28 columns]}}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 财务数据\n",
"download_financial_data([\"600519.SH\"])\n",
"data3 = get_financial_data([\"600519.SH\"])\n",
"data3"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5259491f-6e9c-41f5-b599-fd4ea5968fa7",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,199 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# 配置迅投研数据服务\n",
"from vnpy.trader.setting import SETTINGS\n",
"\n",
"SETTINGS[\"datafeed.name\"] = \"xt\"\n",
"SETTINGS[\"datafeed.username\"] = \"token\"\n",
"SETTINGS[\"datafeed.password\"] = \"4aff6f3b0dcfc990ec9476213ba784e17c34e757\""
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# 加载功能模块\n",
"from datetime import datetime\n",
"\n",
"from vnpy.trader.datafeed import get_datafeed\n",
"from vnpy.trader.object import HistoryRequest, Exchange, Interval\n",
"\n",
"from vnpy_sqlite import Database as SqliteDatabase\n",
"from elite_database import Database as EliteDatabase"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 初始化数据服务\n",
"datafeed = get_datafeed()\n",
"datafeed.init()"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"数据长度 51143\n"
]
}
],
"source": [
"# 查询期权历史数据\n",
"req = HistoryRequest(\n",
" symbol=\"m2205-C-3000\",\n",
" exchange=Exchange.DCE,#SZSE\n",
" start=datetime(2017, 1, 1),\n",
" end=datetime.now(),\n",
" interval=Interval.MINUTE\n",
")\n",
"\n",
"bars = datafeed.query_bar_history(req)\n",
"print(\"数据长度\", len(bars))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"ename": "IndexError",
"evalue": "list index out of range",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[9], line 2\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# 查看K线数据内容\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m \u001b[43mbars\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\n",
"\u001b[1;31mIndexError\u001b[0m: list index out of range"
]
}
],
"source": [
"# 查看K线数据内容\n",
"bars[0]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 创建SQLite数据库实例并写入数据\n",
"db1 = SqliteDatabase()\n",
"db1.save_bar_data(bars)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 测试SQLite数据查询性能\n",
"%timeit bars = db1.load_bar_data(req.symbol, req.exchange, req.interval, req.start, req.end)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 重新查询一次数据\n",
"bars = datafeed.query_bar_history(req)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 创建Elite数据库实例并写入数据\n",
"db2 = EliteDatabase()\n",
"db2.save_bar_data(bars)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 测试Elite数据查询性能\n",
"%timeit bars = db2.load_bar_data(req.symbol, req.exchange, req.interval, req.start, req.end)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"153/6.25"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
},
"vscode": {
"interpreter": {
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
from multiprocessing import Process
from datetime import datetime
from vnpy.trader.database import BarOverview
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.object import ContractData, BarData, HistoryRequest
from vnpy.trader.constant import Exchange, Product, OptionType, Interval
from vnpy.trader.setting import SETTINGS
from elite_database import EliteDatabase
# 配置迅投研数据服务
SETTINGS["datafeed.name"] = "xt"
SETTINGS["datafeed.username"] = "token"
SETTINGS["datafeed.password"] = "4aff6f3b0dcfc990ec9476213ba784e17c34e757"
# 交易所映射关系
EXCHANGE_XT2VT = {
"SH": Exchange.SSE,
"SZ": Exchange.SZSE,
"BJ": Exchange.BSE,
"SF": Exchange.SHFE,
"IF": Exchange.CFFEX,
"INE": Exchange.INE,
"DF": Exchange.DCE,
"ZF": Exchange.CZCE,
"GF": Exchange.GFEX
}
def update_history_data() -> None:
"""更新历史合约信息"""
# 在子进程中加载xtquant
from xtquant.xtdata import download_history_data
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 下载历史合约信息
download_history_data("", "historycontract")
print("xtquant历史合约信息下载完成")
def update_contract_data(sector_name: str) -> None:
"""更新合约数据"""
# 在子进程中加载xtquant
from xtquant.xtdata import (
get_stock_list_in_sector,
get_instrument_detail
)
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 查询中金所历史合约代码
vt_symbols: list[str] = get_stock_list_in_sector(sector_name)
# 遍历列表查询合约信息
contracts: list[ContractData] = []
for xt_symbol in vt_symbols:
# 拆分XT代码
symbol, xt_exchange = xt_symbol.split(".")
# 筛选期权合约合约
if "-" in symbol:
data: dict = get_instrument_detail(xt_symbol, True)
type_str = data["InstrumentID"].split("-")[1]
if type_str == "C":
option_type = OptionType.CALL
elif type_str == "P":
option_type = OptionType.PUT
contract: ContractData = ContractData(
symbol=data["InstrumentID"],
exchange=EXCHANGE_XT2VT[xt_exchange.replace("O", "")],
name=data["InstrumentName"],
product=Product.OPTION,
size=data["VolumeMultiple"],
pricetick=data["PriceTick"],
min_volume=data["MinLimitOrderVolume"],
option_strike=data["ExtendInfo"]["OptExercisePrice"],
option_listed=datetime.strptime(data["OpenDate"], "%Y%m%d"),
option_expiry=datetime.strptime(data["ExpireDate"], "%Y%m%d"),
option_portfolio=data["ProductID"],
option_index=str(data["ExtendInfo"]["OptExercisePrice"]),
option_type=option_type,
gateway_name="XT"
)
contracts.append(contract)
# 保存合约信息到数据库
database: EliteDatabase = EliteDatabase()
database.save_contract_data(contracts)
print("合约信息更新成功", len(contracts))
def update_bar_data() -> None:
"""更新K线数据"""
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 获取当前时间戳
now: datetime = datetime.now()
# 获取合约信息
database: EliteDatabase = EliteDatabase()
contracts: list[ContractData] = database.load_contract_data()
# 获取数据汇总
data: list[BarOverview] = database.get_bar_overview()
overviews: dict[str, BarOverview] = {}
for o in data:
# 只保留分钟线数据
if o.interval != Interval.MINUTE:
continue
vt_symbol: str = f"{o.symbol}.{o.exchange.value}"
overviews[vt_symbol] = o
# 遍历所有合约信息
for contract in contracts:
# 如果没有到期时间,则跳过
if not contract.option_expiry:
continue
# 查询数据汇总
overview: BarOverview = overviews.get(contract.vt_symbol, None)
# 如果已经到期,则跳过
if overview and contract.option_expiry < now:
continue
# 初始化查询开始的时间中金所期权最早在2019-12-13商品期权最早为豆粕期权2017年3月31日
start: datetime = datetime(2017, 3, 1)
# 实现增量查询
if overview:
start = overview.end
# 执行数据查询和更新入库
req: HistoryRequest = HistoryRequest(
symbol=contract.symbol,
exchange=contract.exchange,
start=start,
end=datetime.now(),
interval=Interval.MINUTE
)
bars: list[BarData] = datafeed.query_bar_history(req)
if bars:
database.save_bar_data(bars)
start_dt: datetime = bars[0].datetime
end_dt: datetime = bars[-1].datetime
msg: str = f"{contract.vt_symbol}数据更新成功,{start_dt} - {end_dt}"
print(msg)
if __name__ == "__main__":
# 使用子进程更新历史合约信息
process: Process = Process(target=update_history_data)
process.start()
process.join() # 等待子进程执行完成
# 更新合约信息
update_contract_data("中金所")
update_contract_data("过期中金所")
# 更新历史数据
update_bar_data()
# "IF":"过期中金所",
# "SF":"过期上期所",不在此更新第26课
# "DF":"过期大商所",不在此更新第26课
# "ZF":"过期郑商所",未完成不在此更新第26课
# "INE":"过期能源中心",
# "SHO":"过期上证期权",未完成不在此更新第26课
# "SZO":"过期深证期权",未完成不在此更新第26课

View File

@@ -0,0 +1,176 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# 加载功能模块\n",
"from datetime import datetime\n",
"\n",
"from vnpy.trader.constant import Interval\n",
"\n",
"from elite_optionstrategy import BacktestingEngine\n",
"\n",
"from buy_option_strategy import BuyOptionStrategy"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# 创建回测引擎\n",
"engine = BacktestingEngine()\n",
"\n",
"engine.set_parameters(\n",
" interval=Interval.MINUTE,\n",
" start=datetime(2023, 1, 1),\n",
" end=datetime(2023, 6, 30),\n",
" rate=0,\n",
" slippage=0.6 + (16 / 100),\n",
")\n",
"\n",
"engine.add_strategy(BuyOptionStrategy, {})"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" 1%| | 2/180 [00:00<00:16, 10.53it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"2024-03-09 09:42:27.361177\t触发异常回测终止\n",
"2024-03-09 09:42:27.362176\tTraceback (most recent call last):\n",
" File \"C:\\veighna_elite_simulation\\lib\\site-packages\\elite_optionstrategy\\backtesting.py\", line 114, in run_backtesting\n",
" File \"C:\\veighna_elite_simulation\\lib\\site-packages\\elite_optionstrategy\\backtesting.py\", line 397, in _run_intraday\n",
" File \"C:\\veighna_elite_simulation\\lib\\site-packages\\elite_optionstrategy\\backtesting.py\", line 442, in _new_bars\n",
" File \"D:\\Gitee_Code\\trading_strategy\\VNPY_Code\\Option_spread_strategy\\使用文档\\19\\buy_option_strategy.py\", line 66, in on_bars\n",
" portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)\n",
" File \"C:\\veighna_elite_simulation\\lib\\site-packages\\elite_optionstrategy\\template.py\", line 247, in get_portfolio\n",
"KeyError: 'IO'\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"# 历史数据回放\n",
"engine.run_backtesting()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2024-03-09 09:28:32.115670\t开始计算逐日盯市盈亏\n",
"2024-03-09 09:28:32.115670\t逐日盯市盈亏计算完成\n"
]
}
],
"source": [
"# 计算每日盈亏\n",
"engine.calculate_result()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 统计绩效结果\n",
"result = engine.calculate_statistics()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 显示资金图表\n",
"engine.show_chart()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 打印逐笔成交\n",
"for trade in engine.all_trades.values():\n",
" print(trade.datetime, trade.vt_symbol, trade.direction.value, trade.offset.value, trade.volume, \"@\", trade.price)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for result in engine.daily_results.values():\n",
" print(result.today_close_data[\"IO2201-C-4900.CFFEX\"])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
},
"vscode": {
"interpreter": {
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,152 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class BuyOptionStrategy(StrategyTemplate):
"""基于均线信号买入期权的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(20) # 快速均线周期
slow_window: int = Parameter(120) # 慢速均线周期
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
ma_signal: int = Variable(0) # 当前信号多空
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 10, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做多平值call
atm_call = front_chain.get_option_by_level(cp=1, level=0)
self.set_target(atm_call.vt_symbol, self.fixed_size)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做多平值put
atm_put = front_chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_put.vt_symbol, self.fixed_size)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal

View File

@@ -0,0 +1,139 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# 配置迅投研数据服务\n",
"from vnpy.trader.setting import SETTINGS\n",
"\n",
"SETTINGS[\"datafeed.name\"] = \"xt\"\n",
"SETTINGS[\"datafeed.username\"] = \"token\"\n",
"SETTINGS[\"datafeed.password\"] = \"4aff6f3b0dcfc990ec9476213ba784e17c34e757\""
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# 加载功能模块\n",
"from datetime import datetime\n",
"\n",
"from vnpy.trader.datafeed import get_datafeed\n",
"from vnpy.trader.object import HistoryRequest, Exchange, Interval\n",
"\n",
"from vnpy_sqlite import Database as SqliteDatabase\n",
"from elite_database import Database as EliteDatabase"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 初始化数据服务\n",
"datafeed = get_datafeed()\n",
"datafeed.init()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"数据长度 174959\n"
]
}
],
"source": [
"# 查询期权历史数据\n",
"req = HistoryRequest(\n",
" symbol=\"IFJQ00\", # 加权指数 \n",
" # symbol=\"IF00\", # 主力连续(未平滑)\n",
" exchange=Exchange.CFFEX,\n",
" start=datetime(2015, 1, 1),\n",
" end=datetime.now(),\n",
" interval=Interval.MINUTE\n",
")\n",
"\n",
"bars = datafeed.query_bar_history(req)\n",
"print(\"数据长度\", len(bars))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 创建Elite数据库实例并写入数据\n",
"db2 = EliteDatabase()\n",
"db2.save_bar_data(bars)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
},
"vscode": {
"interpreter": {
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,184 @@
from multiprocessing import Process
from datetime import datetime
from vnpy.trader.database import BarOverview
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.object import ContractData, BarData, HistoryRequest
from vnpy.trader.constant import Exchange, Product, OptionType, Interval
from vnpy.trader.setting import SETTINGS
from elite_database import EliteDatabase
# 配置迅投研数据服务
SETTINGS["datafeed.name"] = "xt"
SETTINGS["datafeed.username"] = "token"
SETTINGS["datafeed.password"] = "4aff6f3b0dcfc990ec9476213ba784e17c34e757"
# 交易所映射关系
EXCHANGE_XT2VT = {
"SH": Exchange.SSE,
"SZ": Exchange.SZSE,
"BJ": Exchange.BSE,
"SF": Exchange.SHFE,
"IF": Exchange.CFFEX,
"INE": Exchange.INE,
"DF": Exchange.DCE,
"ZF": Exchange.CZCE,
"GF": Exchange.GFEX
}
def update_history_data() -> None:
"""更新历史合约信息"""
# 在子进程中加载xtquant
from xtquant.xtdata import download_history_data
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 下载历史合约信息
download_history_data("", "historycontract")
print("xtquant历史合约信息下载完成")
def update_contract_data(sector_name: str) -> None:
"""更新合约数据"""
# 在子进程中加载xtquant
from xtquant.xtdata import (
get_stock_list_in_sector,
get_instrument_detail
)
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 查询中金所历史合约代码
vt_symbols: list[str] = get_stock_list_in_sector(sector_name)
# 遍历列表查询合约信息
contracts: list[ContractData] = []
for xt_symbol in vt_symbols:
# 拆分XT代码
symbol, xt_exchange = xt_symbol.split(".")
# 筛选期权合约合约
if "-" in symbol:
data: dict = get_instrument_detail(xt_symbol, True)
type_str = data["InstrumentID"].split("-")[1]
if type_str == "C":
option_type = OptionType.CALL
elif type_str == "P":
option_type = OptionType.PUT
option_underlying: str = data["InstrumentID"].split("-")[0]
contract: ContractData = ContractData(
symbol=data["InstrumentID"],
exchange=EXCHANGE_XT2VT[xt_exchange.replace("O", "")],
name=data["InstrumentName"],
product=Product.OPTION,
size=data["VolumeMultiple"],
pricetick=data["PriceTick"],
min_volume=data["MinLimitOrderVolume"],
option_strike=data["ExtendInfo"]["OptExercisePrice"],
option_listed=datetime.strptime(data["OpenDate"], "%Y%m%d"),
option_expiry=datetime.strptime(data["ExpireDate"], "%Y%m%d"),
option_underlying=option_underlying,
option_portfolio=data["ProductID"],
option_index=str(data["ExtendInfo"]["OptExercisePrice"]),
option_type=option_type,
gateway_name="XT"
)
contracts.append(contract)
# 保存合约信息到数据库
database: EliteDatabase = EliteDatabase()
database.save_contract_data(contracts)
print("合约信息更新成功", len(contracts))
def update_bar_data() -> None:
"""更新K线数据"""
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 获取当前时间戳
now: datetime = datetime.now()
# 获取合约信息
database: EliteDatabase = EliteDatabase()
contracts: list[ContractData] = database.load_contract_data()
# 获取数据汇总
data: list[BarOverview] = database.get_bar_overview()
overviews: dict[str, BarOverview] = {}
for o in data:
# 只保留分钟线数据
if o.interval != Interval.MINUTE:
continue
vt_symbol: str = f"{o.symbol}.{o.exchange.value}"
overviews[vt_symbol] = o
# 遍历所有合约信息
for contract in contracts:
# 如果没有到期时间,则跳过
if not contract.option_expiry:
continue
# 查询数据汇总
overview: BarOverview = overviews.get(contract.vt_symbol, None)
# 如果已经到期,则跳过
if overview and contract.option_expiry < now:
continue
# 初始化查询开始的时间
start: datetime = datetime(2018, 1, 1)
# 实现增量查询
if overview:
start = overview.end
# 执行数据查询和更新入库
req: HistoryRequest = HistoryRequest(
symbol=contract.symbol,
exchange=contract.exchange,
start=start,
end=datetime.now(),
interval=Interval.MINUTE
)
bars: list[BarData] = datafeed.query_bar_history(req)
if bars:
database.save_bar_data(bars)
start_dt: datetime = bars[0].datetime
end_dt: datetime = bars[-1].datetime
msg: str = f"{contract.vt_symbol}数据更新成功,{start_dt} - {end_dt}"
print(msg)
if __name__ == "__main__":
# 使用子进程更新历史合约信息
process: Process = Process(target=update_history_data)
process.start()
process.join() # 等待子进程执行完成
# 更新合约信息
update_contract_data("中金所")
update_contract_data("过期中金所")
# 更新历史数据
# update_bar_data()

View File

@@ -0,0 +1,318 @@
from abc import ABC
from typing import Union, Type, TYPE_CHECKING
from collections import defaultdict
from vnpy.trader.constant import Interval, Direction, Offset
from vnpy.trader.object import BarData, TickData, OrderData, TradeData, ContractData
from vnpy.trader.utility import virtual
from .object import PortfolioData
if TYPE_CHECKING:
from .engine import StrategyEngine
class StrategyTemplate(ABC):
"""期权策略模板"""
author: str = ""
def __init__(self, engine: "StrategyEngine", name: str) -> None:
"""构造函数"""
self.engine: "StrategyEngine" = engine
self.name: str = name
self.vt_symbols: set[str] = set()
self.inited: bool = False
self.trading: bool = False
# 初始化变量和参数列表
if not hasattr(self, "parameters"):
self.parameters: list[str] = []
if not hasattr(self, "variables"):
self.variables: list[str] = []
self.variables = ["inited", "trading", "pos_data", "target_data"] + self.variables
# 委托缓存数据
self.orders: dict[str, OrderData] = {}
self.active_orderids: set[str] = set()
# 持仓和目标数据
self.pos_data: dict[str, int] = defaultdict(int) # 实际持仓
self.target_data: dict[str, int] = defaultdict(int) # 目标持仓
# 期权组合
self.portfolios: dict[str, PortfolioData] = {}
def update_setting(self, setting: dict) -> None:
"""更新策略参数"""
for name in self.parameters:
if name in setting:
setattr(self, name, setting[name])
def get_parameters(self) -> dict:
"""查询策略参数"""
strategy_parameters: dict = {}
for name in self.parameters:
strategy_parameters[name] = getattr(self, name)
return strategy_parameters
def get_variables(self) -> dict:
"""查询策略变量"""
strategy_variables: dict = {}
for name in self.variables:
strategy_variables[name] = getattr(self, name)
return strategy_variables
def get_data(self) -> dict:
"""查询策略状态数据"""
strategy_data: dict = {
"strategy_name": self.name,
"vt_symbols": self.vt_symbols,
"class_name": self.__class__.__name__,
"author": self.author,
"parameters": self.get_parameters(),
"variables": self.get_variables(),
}
return strategy_data
@virtual
def on_init(self) -> None:
"""策略初始化"""
pass
@virtual
def on_start(self) -> None:
"""策略启动"""
pass
@virtual
def on_stop(self) -> None:
"""策略停止"""
pass
@virtual
def on_tick(self, tick: TickData) -> None:
"""Tick推送"""
pass
@virtual
def on_bars(self, bars: dict[str, BarData]) -> None:
"""K线推送"""
pass
def update_trade(self, trade: TradeData) -> None:
"""更新成交数据"""
if trade.direction == Direction.LONG:
self.pos_data[trade.vt_symbol] += trade.volume
else:
self.pos_data[trade.vt_symbol] -= trade.volume
def update_order(self, order: OrderData) -> None:
"""更新委托数据"""
self.orders[order.vt_orderid] = order
if not order.is_active() and order.vt_orderid in self.active_orderids:
self.active_orderids.remove(order.vt_orderid)
def buy(self, vt_symbol: str, price: float, volume: float) -> list[str]:
"""买入开仓"""
return self.send_order(vt_symbol, Direction.LONG, Offset.OPEN, price, volume)
def sell(self, vt_symbol: str, price: float, volume: float) -> list[str]:
"""卖出平仓"""
return self.send_order(vt_symbol, Direction.SHORT, Offset.CLOSE, price, volume)
def short(self, vt_symbol: str, price: float, volume: float) -> list[str]:
"""卖出开仓"""
return self.send_order(vt_symbol, Direction.SHORT, Offset.OPEN, price, volume)
def cover(self, vt_symbol: str, price: float, volume: float) -> list[str]:
"""买入平仓"""
return self.send_order(vt_symbol, Direction.LONG, Offset.CLOSE, price, volume)
def send_order(
self,
vt_symbol: str,
direction: Direction,
offset: Offset,
price: float,
volume: float,
) -> list[str]:
"""委托下单"""
if not self.trading:
return []
vt_orderids = self.engine.send_order(
self,
vt_symbol,
direction,
offset,
price,
volume
)
self.active_orderids.update(vt_orderids)
return vt_orderids
def cancel_order(self, vt_orderid: str) -> None:
"""委托撤单"""
if not self.trading:
return
self.engine.cancel_order(self, vt_orderid)
def cancel_all(self) -> None:
"""全撤委托"""
for vt_orderid in list(self.active_orderids):
self.cancel_order(vt_orderid)
def get_pos(self, vt_symbol: str) -> int:
"""查询当前持仓"""
return self.pos_data.get(vt_symbol, 0)
def get_target(self, vt_symbol: str) -> int:
"""查询目标仓位"""
return self.target_data[vt_symbol]
def set_target(self, vt_symbol: str, target: int) -> None:
"""设置目标仓位"""
self.target_data[vt_symbol] = target
def clear_targets(self) -> None:
"""清空目标仓位"""
self.target_data.clear()
def execute_trading(self, price_data: dict[str, float], percent_add: float) -> None:
"""基于目标执行调仓交易"""
self.cancel_all()
# 只发出当前K线切片有行情的合约的委托
for vt_symbol, price in price_data.items():
# 计算仓差
target: int = self.get_target(vt_symbol)
pos: int = self.get_pos(vt_symbol)
diff: int = target - pos
# 多头
if diff > 0:
# 计算多头委托价
order_price: float = price * (1 + percent_add)
# 计算买平和买开数量
cover_volume: int = 0
buy_volume: int = 0
if pos < 0:
cover_volume = min(diff, abs(pos))
buy_volume = diff - cover_volume
else:
buy_volume = diff
# 发出对应委托
if cover_volume:
self.cover(vt_symbol, order_price, cover_volume)
if buy_volume:
self.buy(vt_symbol, order_price, buy_volume)
# 空头
elif diff < 0:
# 计算空头委托价
order_price: float = price * (1 - percent_add)
# 计算卖平和卖开数量
sell_volume: int = 0
short_volume: int = 0
if pos > 0:
sell_volume = min(abs(diff), pos)
short_volume = abs(diff) - sell_volume
else:
short_volume = abs(diff)
# 发出对应委托
if sell_volume:
self.sell(vt_symbol, order_price, sell_volume)
if short_volume:
self.short(vt_symbol, order_price, short_volume)
def write_log(self, msg: str) -> None:
"""输出日志"""
self.engine.write_log(msg, self)
def get_portfolio(self, portfolio_name: str) -> PortfolioData:
"""获取期权组合"""
return self.portfolios[portfolio_name]
def load_bars(self, vt_symbol: str, days: int, interval: Interval) -> list[BarData]:
"""加载历史K线数据"""
return self.engine.load_bars(vt_symbol, days, interval)
def init_portfolio(self, portfolio_name: str) -> bool:
"""查询期权合约"""
# 避免重复订阅
if portfolio_name in self.portfolios:
return True
# 向引擎发起查询
contracts: list[ContractData] = self.engine.init_portfolio(self, portfolio_name)
# 如果有返回合约则创建PortfolioData对象
if contracts:
portfolio: PortfolioData = PortfolioData(contracts[0])
self.portfolios[portfolio_name] = portfolio
for contract in contracts:
self.vt_symbols.add(contract.vt_symbol)
portfolio.add_contract(contract)
return True
else:
return False
def subscribe_options(self, portfolio_name: str) -> bool:
"""订阅期权行情"""
# 避免重复订阅
if portfolio_name in self.portfolios:
return True
# 向引擎发起订阅
contracts: list[ContractData] = self.engine.subscribe_options(self, portfolio_name)
# 如果有返回合约则创建PortfolioData对象
if contracts:
portfolio: PortfolioData = PortfolioData(contracts[0])
self.portfolios[portfolio_name] = portfolio
for contract in contracts:
self.vt_symbols.add(contract.vt_symbol)
portfolio.add_contract(contract)
return True
else:
return False
def subscribe_data(self, vt_symbol: str) -> bool:
"""订阅标的行情"""
n: bool = self.engine.subscribe_data(self, vt_symbol)
if n:
self.vt_symbols.add(vt_symbol)
return n
def put_event(self) -> None:
"""推送策略数据更新事件"""
if self.inited:
self.engine.put_strategy_event(self)
def send_email(self, msg: str) -> None:
"""发送邮件信息"""
if self.inited:
self.engine.send_email(msg, self)
def sync_data(self):
"""同步策略状态数据到文件"""
if self.trading:
self.engine.sync_strategy_data(self)

View File

@@ -0,0 +1,85 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "470f6407-4081-4da1-af10-893e6f2d4d7b",
"metadata": {},
"outputs": [],
"source": [
"from elite_database import EliteDatabase"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "0cc34541-a8ce-4d0f-8939-43bb4efc303b",
"metadata": {},
"outputs": [],
"source": [
"db = EliteDatabase()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "fdafd1d3-b3c7-48cf-910b-c38f8784bf31",
"metadata": {},
"outputs": [],
"source": [
"contracts = db.load_contract_data()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "772229fb-e7cc-470f-9bf0-4ebe2db534e4",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"contract = contracts[0]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1fdf863d-a16d-4ed0-9383-bccbffff6ce7",
"metadata": {},
"outputs": [],
"source": [
"print(contract)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74e1a672-dfb4-434d-92e3-41906da04584",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,136 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 加载功能模块\n",
"from datetime import datetime\n",
"\n",
"from vnpy.trader.constant import Interval\n",
"\n",
"from elite_optionstrategy import BacktestingEngine\n",
"\n",
"from buy_option_strategy import BuyOptionStrategy"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 创建回测引擎\n",
"engine = BacktestingEngine()\n",
"\n",
"engine.set_parameters(\n",
" interval=Interval.MINUTE,\n",
" start=datetime(2022, 1, 1),\n",
" end=datetime(2022, 1, 28),\n",
" rate=0,\n",
" slippage=0.6 + (16 / 100),\n",
")\n",
"\n",
"engine.add_strategy(BuyOptionStrategy, {})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# 历史数据回放\n",
"engine.run_backtesting()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 获取引擎内最新交易日的策略对象\n",
"strategy = engine.strategy"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 获取策略内部对象\n",
"portfolio = strategy.get_portfolio(\"IO\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 查看ChainData\n",
"chain = portfolio.get_chain_by_level(0)\n",
"chain"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"chain.calculate_synthetic()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 查看OptionData\n",
"option = chain.get_option_by_level(cp=1, level=0)\n",
"option"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
},
"vscode": {
"interpreter": {
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,159 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
ChainData,
OptionData,
)
class BuyOptionStrategy(StrategyTemplate):
"""基于均线信号买入期权的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(20) # 快速均线周期
slow_window: int = Parameter(120) # 慢速均线周期
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
ma_signal: int = Variable(0) # 当前信号多空
def on_init(self) -> None:
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor: MaFactor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars: list[BarData] = self.load_bars(self.underlying_symbol, 10, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self) -> None:
"""策略启动"""
self.write_log("策略启动")
def on_stop(self) -> None:
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData) -> None:
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]) -> None:
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain: ChainData = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做多平值call
atm_call: OptionData = front_chain.get_option_by_level(cp=1, level=0)
self.set_target(atm_call.vt_symbol, self.fixed_size)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做多平值put
atm_put: OptionData = front_chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_put.vt_symbol, self.fixed_size)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.fast_ma: float = 0
self.slow_ma: float = 0
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal

View File

@@ -0,0 +1,92 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from update_etf_data import *"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"xtquant历史合约信息下载完成\n"
]
}
],
"source": [
"update_history_data()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"合约信息更新成功 446\n"
]
}
],
"source": [
"update_contract_data(\"深证期权\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"合约信息更新成功 2681\n"
]
}
],
"source": [
"update_contract_data(\"过期深证期权\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,186 @@
from multiprocessing import Process
from datetime import datetime
from vnpy.trader.database import BarOverview
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.object import ContractData, BarData, HistoryRequest
from vnpy.trader.constant import Exchange, Product, OptionType, Interval
from vnpy.trader.setting import SETTINGS
from elite_database import EliteDatabase
# 配置迅投研数据服务
SETTINGS["datafeed.name"] = "xt"
SETTINGS["datafeed.username"] = "token"
SETTINGS["datafeed.password"] = "37b023f6623f9ddcb474dd210c70582d1b1e67d2"
# 交易所映射关系
EXCHANGE_XT2VT = {
"SH": Exchange.SSE,
"SZ": Exchange.SZSE,
"BJ": Exchange.BSE,
"SF": Exchange.SHFE,
"IF": Exchange.CFFEX,
"INE": Exchange.INE,
"DF": Exchange.DCE,
"ZF": Exchange.CZCE,
"GF": Exchange.GFEX
}
def update_history_data() -> None:
"""更新历史合约信息"""
# 在子进程中加载xtquant
from xtquant.xtdata import download_history_data
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 下载历史合约信息
download_history_data("", "historycontract")
print("xtquant历史合约信息下载完成")
def update_contract_data(sector_name: str) -> None:
"""更新合约数据"""
# 在子进程中加载xtquant
from xtquant.xtdata import (
get_stock_list_in_sector,
get_instrument_detail
)
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 查询历史合约代码
vt_symbols: list[str] = get_stock_list_in_sector(sector_name)
# 遍历列表查询合约信息
contracts: list[ContractData] = []
for xt_symbol in vt_symbols:
# 拆分XT代码
symbol, xt_exchange = xt_symbol.split(".")
# 筛选期权合约合约ETF期权代码为8位
if len(symbol) == 8:
data: dict = get_instrument_detail(xt_symbol, True)
name: str = data["InstrumentName"]
if "" in name:
option_type = OptionType.CALL
elif "" in name:
option_type = OptionType.PUT
else:
continue
contract: ContractData = ContractData(
symbol=data["InstrumentID"],
exchange=EXCHANGE_XT2VT[xt_exchange.replace("O", "")],
name=data["InstrumentName"],
product=Product.OPTION,
size=data["VolumeMultiple"],
pricetick=data["PriceTick"],
min_volume=data["MinLimitOrderVolume"],
option_strike=data["ExtendInfo"]["OptExercisePrice"],
option_listed=datetime.strptime(data["OpenDate"], "%Y%m%d"),
option_expiry=datetime.strptime(data["ExpireDate"], "%Y%m%d"),
option_portfolio=data["ProductID"],
option_index=str(data["ExtendInfo"]["OptExercisePrice"]),
option_type=option_type,
gateway_name="XT"
)
contracts.append(contract)
# 保存合约信息到数据库
database: EliteDatabase = EliteDatabase()
database.save_contract_data(contracts)
print("合约信息更新成功", len(contracts))
def update_bar_data() -> None:
"""更新K线数据"""
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 获取当前时间戳
now: datetime = datetime.now()
# 获取合约信息
database: EliteDatabase = EliteDatabase()
contracts: list[ContractData] = database.load_contract_data()
# 获取数据汇总
data: list[BarOverview] = database.get_bar_overview()
overviews: dict[str, BarOverview] = {}
for o in data:
# 只保留分钟线数据
if o.interval != Interval.MINUTE:
continue
vt_symbol: str = f"{o.symbol}.{o.exchange.value}"
overviews[vt_symbol] = o
# 遍历所有合约信息
for contract in contracts:
# 如果没有到期时间,则跳过
if not contract.option_expiry:
continue
# 查询数据汇总
overview: BarOverview = overviews.get(contract.vt_symbol, None)
# 如果已经到期,则跳过
if overview and contract.option_expiry < now:
continue
# 初始化查询开始的时间
start: datetime = datetime(2018, 1, 1)
# 实现增量查询
if overview:
start = overview.end
# 执行数据查询和更新入库
req: HistoryRequest = HistoryRequest(
symbol=contract.symbol,
exchange=contract.exchange,
start=start,
end=datetime.now(),
interval=Interval.MINUTE
)
bars: list[BarData] = datafeed.query_bar_history(req)
if bars:
database.save_bar_data(bars)
start_dt: datetime = bars[0].datetime
end_dt: datetime = bars[-1].datetime
msg: str = f"{contract.vt_symbol}数据更新成功,{start_dt} - {end_dt}"
print(msg)
if __name__ == "__main__":
# 使用子进程更新历史合约信息
process: Process = Process(target=update_history_data)
process.start()
process.join() # 等待子进程执行完成
# 更新合约信息
update_contract_data("上证期权")
update_contract_data("过期上证期权")
update_contract_data("深证期权")
update_contract_data("过期深证期权")
# 更新历史数据
update_bar_data()

View File

@@ -0,0 +1,102 @@
#encoding:gbk
import re
def init(C):
option_code_list1 = get_option_code(C,"IF",data_type = 0) # 获取中金所当前可交易期权合约
option_code_list2 = get_option_code(C,"SHO",data_type = 1) # 获取上交所已退市可交易期权合约
option_code_list3 = get_option_code(C,"IF",data_type = 2) # 获取 中金所 所有期权(包含历史)合约
print(option_code_list1[:5])
print("="*20)
print(option_code_list2[:5])
print("="*20)
print(option_code_list3[:5])
# 可通过C.get_option_detail_data()查看合约具体信息
def hanldbar(C):
return
def get_option_code(C,market,data_type = 0):
'''
ToDo:取出指定market的期权合约
Args:
market: 目标市场,比如中金所填 IF
data_type: 返回数据范围,可返回已退市合约,默认仅返回当前
0: 仅当前
1: 仅历史
2: 历史 + 当前
'''
_history_sector_dict = {
"IF":"过期中金所",
"SF":"过期上期所",
"DF":"过期大商所",
"ZF":"过期郑商所",
"INE":"过期能源中心",
"SHO":"过期上证期权",
"SZO":"过期深证期权",
}
# _now_secotr_dict = {
# "IF":"中金所",
# "SF":"上期所",
# "DF":"大商所",
# "ZF":"郑商所",
# "INE":"能源中心",
# "SHO":"上证期权",
# "SZO":"深证期权",
# }
_sector = _history_sector_dict.get(market)
# _now_sector = _now_secotr_dict.get(market)
if _sector == None:
raise KeyError(f"不存在该市场:{market}")
_now_sector = _sector[2:]
# 过期上证和过期深证有专门的板块,不需要处理
if market == "SHO" or market == "SZO":
if data_type == 0:
_list = C.get_stock_list_in_sector(_now_sector)
elif data_type == 1:
_list = C.get_stock_list_in_sector(_sector)
elif data_type == 2:
_list = C.get_stock_list_in_sector(_sector) + C.get_stock_list_in_sector(_now_sector)
else:
raise KeyError(f"data_type参数错误:{data_type}")
return _list
# 期货期权需要额外处理
if data_type == 0:
all_list = C.get_stock_list_in_sector(_now_sector)
elif data_type == 1:
all_list = C.get_stock_list_in_sector(_sector)
elif data_type == 2:
all_list = C.get_stock_list_in_sector(_sector) + C.get_stock_list_in_sector(_now_sector)
else:
raise KeyError(f"data_type参数错误:{data_type}")
_list = []
pattern1 = r'^[A-Z]{2}\d{4}-[A-Z]-\d{4}\.[A-Z]+$'
pattern2 = r'^[a-zA-Z]+\d+[a-zA-Z]\d+\.[A-Z]+$'
pattern3 = r'^[a-zA-Z]+\d+-[a-zA-Z]-\d+\.[A-Z]+$'
for i in all_list:
if re.match(pattern1,i):
_list.append(i)
elif re.match(pattern2,i):
_list.append(i)
elif re.match(pattern3,i):
_list.append(i)
# _list =[i for i in all_list if re.match(pattern, i)]
return _list

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
from multiprocessing import Process
from datetime import datetime
from vnpy.trader.database import BarOverview
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.object import ContractData, BarData, HistoryRequest
from vnpy.trader.constant import Exchange, Product, OptionType, Interval
from vnpy.trader.setting import SETTINGS
from elite_database import EliteDatabase
# 配置迅投研数据服务
SETTINGS["datafeed.name"] = "xt"
SETTINGS["datafeed.username"] = "token"
SETTINGS["datafeed.password"] = "4aff6f3b0dcfc990ec9476213ba784e17c34e757"
# 交易所映射关系
EXCHANGE_XT2VT = {
"SH": Exchange.SSE,
"SZ": Exchange.SZSE,
"BJ": Exchange.BSE,
"SF": Exchange.SHFE,
"IF": Exchange.CFFEX,
"INE": Exchange.INE,
"DF": Exchange.DCE,
"ZF": Exchange.CZCE,
"GF": Exchange.GFEX
}
def update_history_data() -> None:
"""更新历史合约信息"""
# 在子进程中加载xtquant
from xtquant.xtdata import download_history_data
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 下载历史合约信息
download_history_data("", "historycontract")
print("xtquant历史合约信息下载完成")
def update_contract_data(sector_name: str) -> None:
"""更新合约数据"""
# 在子进程中加载xtquant
from xtquant.xtdata import (
get_stock_list_in_sector,
get_instrument_detail
)
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 查询交易所历史合约代码
vt_symbols: list[str] = get_stock_list_in_sector(sector_name)
# 遍历列表查询合约信息
contracts: list[ContractData] = []
for xt_symbol in vt_symbols:
# 筛选期权合约合约
data: dict = get_instrument_detail(xt_symbol, True)
option_strike: float = data["ExtendInfo"]["OptExercisePrice"]
if not option_strike:
continue
# 拆分XT代码
symbol, xt_exchange = xt_symbol.split(".")
# 移除产品前缀
for ix, w in enumerate(symbol):
if w.isdigit():
break
suffix: str = symbol[ix:]
# 判断期权类型
if "C" in suffix:
option_type = OptionType.CALL
elif "P" in suffix:
option_type = OptionType.PUT
else:
continue
contract: ContractData = ContractData(
symbol=data["InstrumentID"],
exchange=EXCHANGE_XT2VT[xt_exchange.replace("O", "")],
name=data["InstrumentName"],
product=Product.OPTION,
size=data["VolumeMultiple"],
pricetick=data["PriceTick"],
min_volume=data["MinLimitOrderVolume"],
option_strike=data["ExtendInfo"]["OptExercisePrice"],
option_listed=datetime.strptime(data["OpenDate"], "%Y%m%d"),
option_expiry=datetime.strptime(data["ExpireDate"], "%Y%m%d"),
option_portfolio=data["ProductID"],
option_index=str(data["ExtendInfo"]["OptExercisePrice"]),
option_type=option_type,
gateway_name="XT"
)
contracts.append(contract)
# 保存合约信息到数据库
database: EliteDatabase = EliteDatabase()
database.save_contract_data(contracts)
print("合约信息更新成功", len(contracts))
return contracts
def update_bar_data() -> None:
"""更新K线数据"""
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 获取当前时间戳
now: datetime = datetime.now()
# 获取合约信息
database: EliteDatabase = EliteDatabase()
contracts: list[ContractData] = database.load_contract_data()
# 获取数据汇总
data: list[BarOverview] = database.get_bar_overview()
overviews: dict[str, BarOverview] = {}
for o in data:
# 只保留分钟线数据
if o.interval != Interval.MINUTE:
continue
vt_symbol: str = f"{o.symbol}.{o.exchange.value}"
overviews[vt_symbol] = o
# 遍历所有合约信息
for contract in contracts:
# 如果没有到期时间,则跳过
if not contract.option_expiry:
continue
# 查询数据汇总
overview: BarOverview = overviews.get(contract.vt_symbol, None)
# 如果已经到期,则跳过
if overview and contract.option_expiry < now:
continue
# 初始化查询开始的时间
start: datetime = datetime(2017, 1, 1)
# 实现增量查询
if overview:
start = overview.end
# 执行数据查询和更新入库
req: HistoryRequest = HistoryRequest(
symbol=contract.symbol,
exchange=contract.exchange,
start=start,
end=datetime.now(),
interval=Interval.MINUTE
)
bars: list[BarData] = datafeed.query_bar_history(req)
if bars:
database.save_bar_data(bars)
start_dt: datetime = bars[0].datetime
end_dt: datetime = bars[-1].datetime
msg: str = f"{contract.vt_symbol}数据更新成功,{start_dt} - {end_dt}"
print(msg)
if __name__ == "__main__":
# 使用子进程更新历史合约信息
process: Process = Process(target=update_history_data)
process.start()
process.join() # 等待子进程执行完成
# 更新合约信息
update_contract_data("郑商所")
update_contract_data("过期郑商所")
# 更新历史数据
update_bar_data()

View File

@@ -0,0 +1,69 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from update_rqdata_data import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"contracts = update_contract_data(Exchange.CFFEX)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(contracts[0])\n",
"print(contracts[-1])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"update_bar_data()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,154 @@
from datetime import datetime
import rqdatac
import pandas as pd
from vnpy.trader.database import BarOverview
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.object import ContractData, BarData, HistoryRequest
from vnpy.trader.constant import Exchange, Product, OptionType, Interval
from elite_database import EliteDatabase
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 交易所映射关系
EXCHANGE_RQ2VT = {
"XSHG": Exchange.SSE,
"XSHE": Exchange.SZSE,
"SHFE": Exchange.SHFE,
"CFFEX": Exchange.CFFEX,
"INE": Exchange.INE,
"DCE": Exchange.DCE,
"CZCE": Exchange.CZCE,
"GFEX": Exchange.GFEX
}
def update_contract_data(exchange: Exchange) -> None:
"""更新合约数据"""
# 查询期权信息
df: pd.DataFrame = rqdatac.all_instruments(type="Option")
# 转换合约对象
contracts: list[ContractData] = []
for tp in df.itertuples():
# 交易所过滤
if exchange != EXCHANGE_RQ2VT[tp.exchange]:
continue
# 确认期权类型
if tp.option_type == "C":
option_type = OptionType.CALL
else:
option_type = OptionType.PUT
# 获取最小价格变动
pricetick: float = rqdatac.instruments(tp.order_book_id).tick_size()
# 创建期权对象
contract = ContractData(
symbol=tp.trading_code,
exchange=exchange,
name=tp.trading_code,
product=Product.OPTION,
size=tp.contract_multiplier,
pricetick=pricetick,
min_volume=tp.round_lot,
option_strike=tp.strike_price,
option_listed=datetime.strptime(tp.listed_date, "%Y-%m-%d"),
option_expiry=datetime.strptime(tp.maturity_date, "%Y-%m-%d"),
option_portfolio=tp.underlying_symbol,
option_index=str(tp.strike_price),
option_underlying=tp.trading_code.split("-")[0],
option_type=option_type,
gateway_name="RQ"
)
contracts.append(contract)
# 保存合约信息到数据库
database: EliteDatabase = EliteDatabase()
database.save_contract_data(contracts)
print("合约信息更新成功", len(contracts))
return contracts
def update_bar_data() -> None:
"""更新K线数据"""
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 获取当前时间戳
now: datetime = datetime.now()
# 获取合约信息
database: EliteDatabase = EliteDatabase()
contracts: list[ContractData] = database.load_contract_data()
# 获取数据汇总
data: list[BarOverview] = database.get_bar_overview()
overviews: dict[str, BarOverview] = {}
for o in data:
# 只保留分钟线数据
if o.interval != Interval.MINUTE:
continue
vt_symbol: str = f"{o.symbol}.{o.exchange.value}"
overviews[vt_symbol] = o
# 遍历所有合约信息
for contract in contracts:
# 如果没有到期时间,则跳过
if not contract.option_expiry:
continue
# 查询数据汇总
overview: BarOverview = overviews.get(contract.vt_symbol, None)
# 如果已经到期,则跳过
if overview and contract.option_expiry < now:
continue
# 初始化查询开始的时间
start: datetime = datetime(2018, 1, 1)
# 实现增量查询
if overview:
start = overview.end
# 执行数据查询和更新入库
req: HistoryRequest = HistoryRequest(
symbol=contract.symbol,
exchange=contract.exchange,
start=start,
end=datetime.now(),
interval=Interval.MINUTE
)
bars: list[BarData] = datafeed.query_bar_history(req)
if bars:
database.save_bar_data(bars)
start_dt: datetime = bars[0].datetime
end_dt: datetime = bars[-1].datetime
msg: str = f"{contract.vt_symbol}数据更新成功,{start_dt} - {end_dt}"
print(msg)
if __name__ == "__main__":
# 更新合约信息
update_contract_data(Exchange.CFFEX)
# 更新历史数据
update_bar_data()

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,152 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class BuyOptionStrategy(StrategyTemplate):
"""基于均线信号买入期权的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(20) # 快速均线周期
slow_window: int = Parameter(120) # 慢速均线周期
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
ma_signal: int = Variable(0) # 当前信号多空
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 10, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做多平值call
atm_call = front_chain.get_option_by_level(cp=1, level=0)
self.set_target(atm_call.vt_symbol, self.fixed_size)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做多平值put
atm_put = front_chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_put.vt_symbol, self.fixed_size)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Parameter,
PortfolioData,
)
class BuyStraddleStrategy(StrategyTemplate):
"""持续做多跨式价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 如果已有仓位则不操作(持有到期)
if self.pos_data:
return
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 买入跨式价差
atm_call = front_chain.get_option_by_level(cp=1, level=0)
atm_put = front_chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_call.vt_symbol, self.fixed_size)
self.set_target(atm_put.vt_symbol, self.fixed_size)
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)

View File

@@ -0,0 +1,86 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from update_etf_data import *"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"合约信息更新成功 642\n"
]
}
],
"source": [
"contracts = update_contract_data(\"上证期权\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ContractData(gateway_name='XT', extra=None, symbol='10005765', exchange=<Exchange.SSE: 'SSE'>, name='50ETF购3月2411A', product=<Product.OPTION: '期权'>, size=10161, pricetick=0.0001, min_volume=1, stop_supported=False, net_position=False, history_data=False, option_strike=2.411, option_underlying='510050-2403', option_type=<OptionType.CALL: '看涨期权'>, option_listed=datetime.datetime(2023, 7, 27, 0, 0), option_expiry=datetime.datetime(2024, 3, 27, 0, 0), option_portfolio='510050', option_index='2.411')\n",
"ContractData(gateway_name='XT', extra=None, symbol='10006712', exchange=<Exchange.SSE: 'SSE'>, name='300ETF沽6月3700', product=<Product.OPTION: '期权'>, size=10000, pricetick=0.0001, min_volume=1, stop_supported=False, net_position=False, history_data=False, option_strike=3.7, option_underlying='510300-2406', option_type=<OptionType.PUT: '看跌期权'>, option_listed=datetime.datetime(2024, 1, 19, 0, 0), option_expiry=datetime.datetime(2024, 6, 26, 0, 0), option_portfolio='510300', option_index='3.7')\n"
]
}
],
"source": [
"print(contracts[0])\n",
"print(contracts[-1])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"update_bar_data()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,86 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from update_rqdata_data import *"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"合约信息更新成功 4808\n"
]
}
],
"source": [
"contracts = update_contract_data(Exchange.CFFEX)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ContractData(gateway_name='RQ', extra=None, symbol='HO2301-C-2325', exchange=<Exchange.CFFEX: 'CFFEX'>, name='HO2301-C-2325', product=<Product.OPTION: '期权'>, size=100.0, pricetick=0.2, min_volume=1.0, stop_supported=False, net_position=False, history_data=False, option_strike=2325.0, option_underlying='HO2301', option_type=<OptionType.CALL: '看涨期权'>, option_listed=datetime.datetime(2022, 12, 21, 0, 0), option_expiry=datetime.datetime(2023, 1, 20, 0, 0), option_portfolio='HO', option_index='2325.0')\n",
"ContractData(gateway_name='RQ', extra=None, symbol='MO2412-P-6600', exchange=<Exchange.CFFEX: 'CFFEX'>, name='MO2412-P-6600', product=<Product.OPTION: '期权'>, size=100.0, pricetick=0.2, min_volume=1.0, stop_supported=False, net_position=False, history_data=False, option_strike=6600.0, option_underlying='MO2412', option_type=<OptionType.PUT: '看跌期权'>, option_listed=datetime.datetime(2023, 12, 18, 0, 0), option_expiry=datetime.datetime(2024, 12, 20, 0, 0), option_portfolio='MO', option_index='6600.0')\n"
]
}
],
"source": [
"print(contracts[0])\n",
"print(contracts[-1])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"update_bar_data()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,198 @@
from multiprocessing import Process
from datetime import datetime
from vnpy.trader.database import BarOverview
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.object import ContractData, BarData, HistoryRequest
from vnpy.trader.constant import Exchange, Product, OptionType, Interval
from vnpy.trader.setting import SETTINGS
from elite_database import EliteDatabase
# 配置迅投研数据服务
SETTINGS["datafeed.name"] = "xt"
SETTINGS["datafeed.username"] = "token"
SETTINGS["datafeed.password"] = ""
# 交易所映射关系
EXCHANGE_XT2VT = {
"SH": Exchange.SSE,
"SZ": Exchange.SZSE,
"BJ": Exchange.BSE,
"SF": Exchange.SHFE,
"IF": Exchange.CFFEX,
"INE": Exchange.INE,
"DF": Exchange.DCE,
"ZF": Exchange.CZCE,
"GF": Exchange.GFEX
}
def update_history_data() -> None:
"""更新历史合约信息"""
# 在子进程中加载xtquant
from xtquant.xtdata import download_history_data
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 下载历史合约信息
download_history_data("", "historycontract")
print("xtquant历史合约信息下载完成")
def update_contract_data(sector_name: str) -> None:
"""更新合约数据"""
# 在子进程中加载xtquant
from xtquant.xtdata import (
get_stock_list_in_sector,
get_instrument_detail
)
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 查询历史合约代码
vt_symbols: list[str] = get_stock_list_in_sector(sector_name)
# 遍历列表查询合约信息
contracts: list[ContractData] = []
for xt_symbol in vt_symbols:
# 拆分XT代码
symbol, xt_exchange = xt_symbol.split(".")
# 筛选期权合约合约ETF期权代码为8位
if len(symbol) == 8:
data: dict = get_instrument_detail(xt_symbol, True)
#print(data)
#raise Exception("fuck")
name: str = data["InstrumentName"]
if "" in name:
option_type = OptionType.CALL
elif "" in name:
option_type = OptionType.PUT
else:
continue
# 获取期权组合
option_portfolio = data["ExtendInfo"]["OptUndlCode"]
# 获取期权链(标的)
option_expiry = datetime.strptime(data["ExpireDate"], "%Y%m%d")
option_underlying = option_portfolio + "-" + option_expiry.strftime("%y%m")
# 生成合约对象
contract: ContractData = ContractData(
symbol=data["InstrumentID"],
exchange=EXCHANGE_XT2VT[xt_exchange.replace("O", "")],
name=data["InstrumentName"],
product=Product.OPTION,
size=data["VolumeMultiple"],
pricetick=data["PriceTick"],
min_volume=data["MinLimitOrderVolume"],
option_strike=data["ExtendInfo"]["OptExercisePrice"],
option_listed=datetime.strptime(data["OpenDate"], "%Y%m%d"),
option_expiry=datetime.strptime(data["ExpireDate"], "%Y%m%d"),
option_portfolio=option_portfolio,
option_underlying=option_underlying,
option_index=str(data["ExtendInfo"]["OptExercisePrice"]),
option_type=option_type,
gateway_name="XT"
)
contracts.append(contract)
# 保存合约信息到数据库
database: EliteDatabase = EliteDatabase()
database.save_contract_data(contracts)
print("合约信息更新成功", len(contracts))
return contracts
def update_bar_data() -> None:
"""更新K线数据"""
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 获取当前时间戳
now: datetime = datetime.now()
# 获取合约信息
database: EliteDatabase = EliteDatabase()
contracts: list[ContractData] = database.load_contract_data()
# 获取数据汇总
data: list[BarOverview] = database.get_bar_overview()
overviews: dict[str, BarOverview] = {}
for o in data:
# 只保留分钟线数据
if o.interval != Interval.MINUTE:
continue
vt_symbol: str = f"{o.symbol}.{o.exchange.value}"
overviews[vt_symbol] = o
# 遍历所有合约信息
for contract in contracts:
# 如果没有到期时间,则跳过
if not contract.option_expiry:
continue
# 查询数据汇总
overview: BarOverview = overviews.get(contract.vt_symbol, None)
# 如果已经到期,则跳过
if overview and contract.option_expiry < now:
continue
# 初始化查询开始的时间
start: datetime = datetime(2018, 1, 1)
# 实现增量查询
if overview:
start = overview.end
# 执行数据查询和更新入库
req: HistoryRequest = HistoryRequest(
symbol=contract.symbol,
exchange=contract.exchange,
start=start,
end=datetime.now(),
interval=Interval.MINUTE
)
bars: list[BarData] = datafeed.query_bar_history(req)
if bars:
database.save_bar_data(bars)
start_dt: datetime = bars[0].datetime
end_dt: datetime = bars[-1].datetime
msg: str = f"{contract.vt_symbol}数据更新成功,{start_dt} - {end_dt}"
print(msg)
if __name__ == "__main__":
# 使用子进程更新历史合约信息
process: Process = Process(target=update_history_data)
process.start()
process.join() # 等待子进程执行完成
# 更新合约信息
update_contract_data("上证期权")
update_contract_data("过期上证期权")
update_contract_data("深证期权")
update_contract_data("过期深证期权")
# 更新历史数据
update_bar_data()

View File

@@ -0,0 +1,163 @@
from datetime import datetime
import rqdatac
import pandas as pd
from vnpy.trader.database import BarOverview
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.object import ContractData, BarData, HistoryRequest
from vnpy.trader.constant import Exchange, Product, OptionType, Interval
from elite_database import EliteDatabase
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 交易所映射关系
EXCHANGE_RQ2VT = {
"XSHG": Exchange.SSE,
"XSHE": Exchange.SZSE,
"SHFE": Exchange.SHFE,
"CFFEX": Exchange.CFFEX,
"INE": Exchange.INE,
"DCE": Exchange.DCE,
"CZCE": Exchange.CZCE,
"GFEX": Exchange.GFEX
}
def update_contract_data(exchange: Exchange) -> None:
"""更新合约数据"""
# 查询期权信息
df: pd.DataFrame = rqdatac.all_instruments(type="Option")
# 转换合约对象
contracts: list[ContractData] = []
for tp in df.itertuples():
# 交易所过滤
if exchange != EXCHANGE_RQ2VT[tp.exchange]:
continue
# 确认期权类型
if tp.option_type == "C":
option_type = OptionType.CALL
else:
option_type = OptionType.PUT
# 获取最小价格变动
pricetick: float = rqdatac.instruments(tp.order_book_id).tick_size()
# 获取期权链(标的)
if "-" in tp.trading_code:
option_underlying = tp.trading_code.split("-")[0]
else:
suffix = tp.trading_code.replace(tp.underlying_symbol, "")
ix = suffix.index(tp.option_type)
time_str = suffix[:ix]
option_underlying = tp.underlying_symbol + time_str
# 创建期权对象
contract = ContractData(
symbol=tp.trading_code,
exchange=exchange,
name=tp.trading_code,
product=Product.OPTION,
size=tp.contract_multiplier,
pricetick=pricetick,
min_volume=tp.round_lot,
option_strike=tp.strike_price,
option_listed=datetime.strptime(tp.listed_date, "%Y-%m-%d"),
option_expiry=datetime.strptime(tp.maturity_date, "%Y-%m-%d"),
option_portfolio=tp.underlying_symbol,
option_index=str(tp.strike_price),
option_underlying=option_underlying,
option_type=option_type,
gateway_name="RQ"
)
contracts.append(contract)
# 保存合约信息到数据库
database: EliteDatabase = EliteDatabase()
database.save_contract_data(contracts)
print("合约信息更新成功", len(contracts))
return contracts
def update_bar_data() -> None:
"""更新K线数据"""
# 初始化数据服务
datafeed = get_datafeed()
datafeed.init()
# 获取当前时间戳
now: datetime = datetime.now()
# 获取合约信息
database: EliteDatabase = EliteDatabase()
contracts: list[ContractData] = database.load_contract_data()
# 获取数据汇总
data: list[BarOverview] = database.get_bar_overview()
overviews: dict[str, BarOverview] = {}
for o in data:
# 只保留分钟线数据
if o.interval != Interval.MINUTE:
continue
vt_symbol: str = f"{o.symbol}.{o.exchange.value}"
overviews[vt_symbol] = o
# 遍历所有合约信息
for contract in contracts:
# 如果没有到期时间,则跳过
if not contract.option_expiry:
continue
# 查询数据汇总
overview: BarOverview = overviews.get(contract.vt_symbol, None)
# 如果已经到期,则跳过
if overview and contract.option_expiry < now:
continue
# 初始化查询开始的时间
start: datetime = datetime(2018, 1, 1)
# 实现增量查询
if overview:
start = overview.end
# 执行数据查询和更新入库
req: HistoryRequest = HistoryRequest(
symbol=contract.symbol,
exchange=contract.exchange,
start=start,
end=datetime.now(),
interval=Interval.MINUTE
)
bars: list[BarData] = datafeed.query_bar_history(req)
if bars:
database.save_bar_data(bars)
start_dt: datetime = bars[0].datetime
end_dt: datetime = bars[-1].datetime
msg: str = f"{contract.vt_symbol}数据更新成功,{start_dt} - {end_dt}"
print(msg)
if __name__ == "__main__":
# 更新合约信息
update_contract_data(Exchange.CFFEX)
# 更新历史数据
update_bar_data()

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,100 @@
from datetime import datetime, timedelta
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Parameter,
PortfolioData,
ChainData,
OptionData
)
class BuyStraddleStrategy(StrategyTemplate):
"""持续做多跨式价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.05) # 委托超价比例
rolling_days: int = Parameter(5) # 移仓剩余天数
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 默认交易当月期权链
chain_level: int = 0
# 如果已有目标则检查是否要移仓
if self.target_data:
# 获取期权到期时间
for vt_symbol in self.target_data.keys():
option: OptionData = portfolio.get_option(vt_symbol)
expiry: datetime = option.contract.option_expiry
break
# 获取当前时间
for bar in bars.values():
now: datetime = bar.datetime.replace(tzinfo=None) # 移除时区
break
# 计算剩余时间
time_left: timedelta = expiry - now
# 如果尚未到时间,则继续持仓
if time_left.days > self.rolling_days:
return
# 需要交易次月期权链
chain_level = 1
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取目标期权链
chain: ChainData = portfolio.get_chain_by_level(chain_level)
if not chain:
self.write_log("无法获取对应期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
chain.calculate_atm()
# 清空当前目标
self.clear_targets()
# 买入跨式价差
atm_call = chain.get_option_by_level(cp=1, level=0)
atm_put = chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_call.vt_symbol, self.fixed_size)
self.set_target(atm_put.vt_symbol, self.fixed_size)
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Parameter,
PortfolioData,
)
class ShortStraddleStrategy(StrategyTemplate):
"""持续做空跨式价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 如果已有目标则不操作(持有到期)
if self.pos_data:
return
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 卖出跨式价差
atm_call = front_chain.get_option_by_level(cp=1, level=0)
atm_put = front_chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_call.vt_symbol, -self.fixed_size)
self.set_target(atm_put.vt_symbol, -self.fixed_size)
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,68 @@
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Parameter,
PortfolioData,
)
class ShortStraddleStrategy(StrategyTemplate):
"""持续做空跨式价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 如果目标为空(尚未开仓或已经到期),则执行开仓
if not self.target_data:
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 卖出跨式价差
atm_call = front_chain.get_option_by_level(cp=1, level=0)
atm_put = front_chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_call.vt_symbol, -self.fixed_size)
self.set_target(atm_put.vt_symbol, -self.fixed_size)
# 执行具体的委托交易每次on_bars都执行避免某一轮委托未能成交导致偏差
self.execute_trading(price_data, self.percent_add)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,87 @@
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Parameter,
PortfolioData,
ChainData,
OptionData
)
class ShortRestrikeStrategy(StrategyTemplate):
"""带调仓的做空跨式价差策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
restrike_diff: float = Parameter(500) # ReStrike的偏差值
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
chain: ChainData = portfolio.get_chain_by_level(0)
if not chain:
self.write_log("无法获取对应期权链,请检查是否正确添加了期权合约")
return
# 计算平值行权价
chain.calculate_atm()
# 如果已有目标则检查是否要ReStrike
if self.target_data:
# 获取持仓的行权价
for vt_symbol in self.target_data.keys():
option: OptionData = portfolio.get_option(vt_symbol)
option_strike: float = option.strike
break
# 获取ATM的行权价
atm_strike: float = chain.atm_strike
# 如果偏差在可接受范围内,则无需调仓
if abs(option_strike - atm_strike) < self.restrike_diff:
return
# 清空当前目标
self.clear_targets()
# 做空跨式价差
atm_call = chain.get_option_by_level(cp=1, level=0)
atm_put = chain.get_option_by_level(cp=-1, level=0)
self.set_target(atm_call.vt_symbol, -self.fixed_size)
self.set_target(atm_put.vt_symbol, -self.fixed_size)
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,69 @@
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Parameter,
PortfolioData,
)
class ShortStrangleStrategy(StrategyTemplate):
"""持续做空宽跨式价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
otm_level: int = Parameter(2) # 宽跨式虚值档位
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 如果目标为空(尚未开仓或已经到期),则执行开仓
if not self.target_data:
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 卖出宽跨式价差
otm_call = front_chain.get_option_by_level(cp=1, level=self.otm_level)
otm_put = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
self.set_target(otm_call.vt_symbol, -self.fixed_size)
self.set_target(otm_put.vt_symbol, -self.fixed_size)
# 执行具体的委托交易每次on_bars都执行避免某一轮委托未能成交导致偏差
self.execute_trading(price_data, self.percent_add)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,166 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class BullBearStrategy(StrategyTemplate):
"""基于均线信号买入牛熊价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(20) # 快速均线周期
slow_window: int = Parameter(120) # 慢速均线周期
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
long_level: int = Parameter(0) # 做多腿档位
short_level: int = Parameter(2) # 做空腿档位
ma_signal: int = Variable(0) # 当前信号多空
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做多Bull Spread
long_option = front_chain.get_option_by_level(cp=1, level=self.long_level)
short_option = front_chain.get_option_by_level(cp=1, level=self.short_level)
self.set_target(long_option.vt_symbol, self.fixed_size)
self.set_target(short_option.vt_symbol, -self.fixed_size)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做多Bear Spread
long_option = front_chain.get_option_by_level(cp=-1, level=self.long_level)
short_option = front_chain.get_option_by_level(cp=-1, level=self.short_level)
self.set_target(long_option.vt_symbol, self.fixed_size)
self.set_target(short_option.vt_symbol, -self.fixed_size)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.bg.update_bar(bar)
def update_window_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,166 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class BullBearStrategy(StrategyTemplate):
"""基于均线信号买入牛熊价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(20) # 快速均线周期
slow_window: int = Parameter(120) # 慢速均线周期
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
long_level: int = Parameter(0) # 做多腿档位
short_level: int = Parameter(2) # 做空腿档位
ma_signal: int = Variable(0) # 当前信号多空
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做多Bull Spread
long_option = front_chain.get_option_by_level(cp=1, level=self.long_level)
short_option = front_chain.get_option_by_level(cp=1, level=self.short_level)
self.set_target(long_option.vt_symbol, self.fixed_size)
self.set_target(short_option.vt_symbol, -self.fixed_size)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做多Bear Spread
long_option = front_chain.get_option_by_level(cp=-1, level=self.long_level)
short_option = front_chain.get_option_by_level(cp=-1, level=self.short_level)
self.set_target(long_option.vt_symbol, self.fixed_size)
self.set_target(short_option.vt_symbol, -self.fixed_size)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.bg.update_bar(bar)
def update_window_bar(self, bar: BarData) -> None:
"""周期K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,172 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class BullBearStrategy(StrategyTemplate):
"""基于均线信号买入牛熊价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(20) # 快速均线周期
slow_window: int = Parameter(120) # 慢速均线周期
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
long_level: int = Parameter(0) # 做多腿档位
short_level: int = Parameter(2) # 做空腿档位
ma_signal: int = Variable(0) # 当前信号多空
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
data = load_json("bull_bear_data.json")
self.ma_signal = data.get("ma_signal", 0)
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
data = {"ma_signal": self.ma_signal}
save_json("bull_bear_data.json", data)
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做多Bull Spread
long_option = front_chain.get_option_by_level(cp=1, level=self.long_level)
short_option = front_chain.get_option_by_level(cp=1, level=self.short_level)
self.set_target(long_option.vt_symbol, self.fixed_size)
self.set_target(short_option.vt_symbol, -self.fixed_size)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做多Bear Spread
long_option = front_chain.get_option_by_level(cp=-1, level=self.long_level)
short_option = front_chain.get_option_by_level(cp=-1, level=self.short_level)
self.set_target(long_option.vt_symbol, self.fixed_size)
self.set_target(short_option.vt_symbol, -self.fixed_size)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.bg.update_bar(bar)
def update_window_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,173 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class AdvancedSpreadStrategy(StrategyTemplate):
"""基于均线信号做空复合价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(5) # 快速均线周期
slow_window: int = Parameter(60) # 慢速均线周期
fixed_size: int = Parameter(1) # 交易的手数
percent_add: float = Parameter(0.02) # 委托超价比例
otm_level: int = Parameter(0) # 做空期权档位
leg1_ratio: int = Parameter(4) # 顺势腿的比例
leg2_ratio: int = Parameter(1) # 逆势腿的比例
ma_signal: int = Variable(0) # 当前信号多空
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
data = load_json("bull_bear_data.json")
self.ma_signal = data.get("ma_signal", 0)
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
data = {"ma_signal": self.ma_signal}
save_json("bull_bear_data.json", data)
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
call = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
self.set_target(put.vt_symbol, -self.fixed_size * self.leg1_ratio)
self.set_target(call.vt_symbol, -self.fixed_size * self.leg2_ratio)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
call = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
self.set_target(call.vt_symbol, -self.fixed_size * self.leg1_ratio)
self.set_target(put.vt_symbol, -self.fixed_size * self.leg2_ratio)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.bg.update_bar(bar)
def update_window_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal

View File

@@ -0,0 +1,118 @@
from vnpy_ctastrategy import (
CtaTemplate,
StopOrder,
TickData,
BarData,
TradeData,
OrderData,
BarGenerator,
ArrayManager,
)
class DoubleMaStrategy(CtaTemplate):
author = "用Python的交易员"
fast_window = 5
slow_window = 60
fast_ma0 = 0.0
fast_ma1 = 0.0
slow_ma0 = 0.0
slow_ma1 = 0.0
parameters = ["fast_window", "slow_window"]
variables = ["fast_ma0", "fast_ma1", "slow_ma0", "slow_ma1"]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
""""""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
self.bg = BarGenerator(self.on_bar, 30, self.on_window_bar)
self.am = ArrayManager(self.slow_window + 10)
def on_init(self):
"""
Callback when strategy is inited.
"""
self.write_log("策略初始化")
self.load_bar(10)
def on_start(self):
"""
Callback when strategy is started.
"""
self.write_log("策略启动")
self.put_event()
def on_stop(self):
"""
Callback when strategy is stopped.
"""
self.write_log("策略停止")
self.put_event()
def on_tick(self, tick: TickData):
"""
Callback of new tick data update.
"""
self.bg.update_tick(tick)
def on_bar(self, bar: BarData):
"""
Callback of new bar data update.
"""
self.bg.update_bar(bar)
def on_window_bar(self, bar: BarData):
am = self.am
am.update_bar(bar)
if not am.inited:
return
fast_ma = am.sma(self.fast_window, array=True)
self.fast_ma0 = fast_ma[-1]
self.fast_ma1 = fast_ma[-2]
slow_ma = am.sma(self.slow_window, array=True)
self.slow_ma0 = slow_ma[-1]
self.slow_ma1 = slow_ma[-2]
cross_over = self.fast_ma0 > self.slow_ma0 and self.fast_ma1 < self.slow_ma1
cross_below = self.fast_ma0 < self.slow_ma0 and self.fast_ma1 > self.slow_ma1
if cross_over:
if self.pos == 0:
self.buy(bar.close_price, 1)
elif self.pos < 0:
self.cover(bar.close_price, 1)
self.buy(bar.close_price, 1)
elif cross_below:
if self.pos == 0:
self.short(bar.close_price, 1)
elif self.pos > 0:
self.sell(bar.close_price, 1)
self.short(bar.close_price, 1)
self.put_event()
def on_order(self, order: OrderData):
"""
Callback of new order data update.
"""
pass
def on_trade(self, trade: TradeData):
"""
Callback of new trade data update.
"""
self.put_event()
def on_stop_order(self, stop_order: StopOrder):
"""
Callback of stop order update.
"""
pass

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,199 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class AdvancedSpreadStrategy(StrategyTemplate):
"""基于均线信号做空符合价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("i_o") # 期权产品代码沪深300IO;
underlying_symbol: str = Parameter("iJQ00.DCE") # 标的合约代码,沪深300IFJQ00;
fast_window: int = Parameter(5) # 快速均线周期
slow_window: int = Parameter(60) # 慢速均线周期
risk_level: int = Parameter(40) # 开仓风险度
percent_add: float = Parameter(0.02) # 委托超价比例
otm_level: int = Parameter(0) # 做空期权档位
leg1_ratio: int = Parameter(4) # 顺势腿的比例
leg2_ratio: int = Parameter(1) # 逆势腿的比例
ma_signal: int = Variable(0) # 当前信号多空
trading_size: int = Variable(1) # 当前交易数量
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = MaFactor(
self.underlying_symbol,
self.fast_window,
self.slow_window
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
data = load_json("bull_bear_data.json")
self.ma_signal = data.get("ma_signal", 0)
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
data = {"ma_signal": self.ma_signal}
save_json("bull_bear_data.json", data)
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做空Put
call = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
atr_value: float = self.factor.get_atr()
if atr_value:
self.trading_size = int(self.risk_level / atr_value)
self.trading_size = max(self.trading_size, 1)
self.trading_size = 1 # 强制开启最小交易
else:
self.trading_size = 1
self.set_target(put.vt_symbol, -self.trading_size * self.leg1_ratio)
self.set_target(call.vt_symbol, -self.trading_size * self.leg2_ratio)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做空Call
call = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
atr_value: float = self.factor.get_atr()
if atr_value:
self.trading_size = int(self.risk_level / atr_value)
self.trading_size = max(self.trading_size, 1)
self.trading_size = 1 # 强制开启最小交易
else:
self.trading_size = 1
self.set_target(call.vt_symbol, -self.trading_size * self.leg1_ratio)
self.set_target(put.vt_symbol, -self.trading_size * self.leg2_ratio)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
fast_window: int,
slow_window: int
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar, 5, self.update_window_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.bg.update_bar(bar)
def update_window_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal
def get_atr(self) -> float:
"""获取ATR风险度"""
if self.am.inited:
return self.am.atr(self.slow_window)
else:
return 0

View File

@@ -0,0 +1,192 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
)
class AdvancedSpreadStrategy(StrategyTemplate):
"""基于均线信号做空符合价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("MO") # 期权产品代码
underlying_symbol: str = Parameter("IMJQ00.CFFEX") # 标的合约代码,IMJQ00.CFFEX
aroon_window: int = Parameter(5) # AROON周期
risk_level: int = Parameter(40) # 开仓风险度
percent_add: float = Parameter(0.02) # 委托超价比例
otm_level: int = Parameter(0) # 做空期权档位
leg1_ratio: int = Parameter(2) # 顺势腿的比例
leg2_ratio: int = Parameter(1) # 逆势腿的比例
ma_signal: int = Variable(0) # 当前信号多空
trading_size: int = Variable(1) # 当前交易数量
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor = AroonFactor(
self.underlying_symbol,
self.aroon_window,
)
# 加载标的历史数据初始化
bars = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
data = load_json("bull_bear_data.json")
self.ma_signal = data.get("ma_signal", 0)
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
data = {"ma_signal": self.ma_signal}
save_json("bull_bear_data.json", data)
def on_tick(self, tick: TickData):
"""Tick推送"""
pass
def on_bars(self, bars: dict[str, BarData]):
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 清空之前的目标
self.clear_targets()
# 做空Put
call = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
atr_value: float = self.factor.get_atr()
if atr_value:
self.trading_size = int(self.risk_level / atr_value)
self.trading_size = max(self.trading_size, 1)
else:
self.trading_size = 1
self.set_target(put.vt_symbol, -self.trading_size * self.leg1_ratio)
self.set_target(call.vt_symbol, -self.trading_size * self.leg2_ratio)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 清空之前的目标
self.clear_targets()
# 做空Call
call = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
atr_value: float = self.factor.get_atr()
if atr_value:
self.trading_size = int(self.risk_level / atr_value)
self.trading_size = max(self.trading_size, 1)
else:
self.trading_size = 1
self.set_target(call.vt_symbol, -self.trading_size * self.leg1_ratio)
self.set_target(put.vt_symbol, -self.trading_size * self.leg2_ratio)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
class AroonFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(
self,
vt_symbol: str,
aroon_window: int,
) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.aroon_window: int = aroon_window
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
self.am: ArrayManager = ArrayManager(aroon_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.bg.update_bar(bar)
def update_window_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.aroon_up, self.aroon_down = self.am.aroon(self.aroon_window)
# 判断信号
if self.aroon_up > self.aroon_down: # and (self.aroon_up > 50)
self.signal = 1
elif self.aroon_up < self.aroon_down: # and (self.aroon_down < 50)
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal
def get_atr(self) -> float:
"""获取ATR风险度"""
if self.am.inited:
return self.am.atr(self.aroon_window)
else:
return 0

View File

@@ -0,0 +1,203 @@
from vnpy.trader.constant import Interval
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
from vnpy.trader.object import TickData, BarData
from elite_optionstrategy import (
StrategyTemplate,
Variable,
Parameter,
PortfolioData,
ChainData,
OptionData,
OptionBarGenerator,
)
class AdvancedSpreadStrategy(StrategyTemplate):
"""基于均线信号做空符合价差的策略"""
author: str = "用Python的交易员"
option_portfolio: str = Parameter("IO") # 期权产品代码
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
fast_window: int = Parameter(5) # 快速均线周期
slow_window: int = Parameter(60) # 慢速均线周期
risk_level: int = Parameter(40) # 开仓风险度
percent_add: float = Parameter(0.02) # 委托超价比例
otm_level: int = Parameter(0) # 做空期权档位
leg1_ratio: int = Parameter(4) # 顺势腿的比例
leg2_ratio: int = Parameter(1) # 逆势腿的比例
ma_signal: int = Variable(0) # 当前信号多空
trading_size: int = Variable(1) # 当前交易数量
atm_strike: float = Variable(0) # 当前平值行权价
def on_init(self) -> None:
"""策略初始化"""
self.write_log("策略初始化")
# K线截面合成器
self.obg: OptionBarGenerator = OptionBarGenerator(self.on_bars)
# 订阅行情
self.subscribe_options(self.option_portfolio)
self.subscribe_data(self.underlying_symbol)
# 标的信号对象
self.factor: MaFactor = MaFactor(self.underlying_symbol, self.fast_window, self.slow_window)
# 加载标的历史数据初始化
bars: list[BarData] = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
for bar in bars:
self.factor.update_bar(bar)
# 缓存文件名称
self.data_filename: str = f"{self.name}_data.json"
def on_start(self) -> None:
"""策略启动"""
self.write_log("策略启动")
data: dict = load_json(self.data_filename)
self.ma_signal = data.get("ma_signal", 0)
def on_stop(self) -> None:
"""策略停止"""
self.write_log("策略停止")
data: dict = {"ma_signal": self.ma_signal}
save_json(self.data_filename, data)
def on_tick(self, tick: TickData) -> None:
"""Tick推送"""
self.obg.update_tick(tick)
def on_bars(self, bars: dict[str, BarData]) -> None:
"""K线推送"""
# 回测首先计算标的信号
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
if underlying_bar:
self.factor.update_bar(underlying_bar)
# 获取期权组合对象
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
# 更新最新期权价格到组合
price_data: dict[str, float] = {}
for bar in bars.values():
price_data[bar.vt_symbol] = bar.close_price
portfolio.update_price(price_data)
# 获取当月期权链
front_chain: ChainData = portfolio.get_chain_by_level(0)
if not front_chain:
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
return
# 计算平值期权
front_chain.calculate_atm()
self.atm_strike = front_chain.atm_strike
# 获取当前均线多空信号
ma_signal: int = self.factor.get_signal()
# 如果均线多头排列,且尚未做多
if ma_signal > 0 and self.ma_signal <= 0:
# 做空Put
call: OptionData = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put: OptionData = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
# 清空之前的目标
self.clear_targets()
atr_value: float = self.factor.get_atr()
if atr_value:
self.trading_size = int(self.risk_level / atr_value)
self.trading_size = max(self.trading_size, 1)
else:
self.trading_size = 1
self.set_target(put.vt_symbol, -self.trading_size * self.leg1_ratio)
self.set_target(call.vt_symbol, -self.trading_size * self.leg2_ratio)
# 如果均线空头排列,且尚未做空
elif ma_signal < 0 and self.ma_signal >= 0:
# 做空Call
call: OptionData = front_chain.get_option_by_level(cp=1, level=self.otm_level)
put: OptionData = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
if call and put:
# 清空之前的目标
self.clear_targets()
atr_value: float = self.factor.get_atr()
if atr_value:
self.trading_size = int(self.risk_level / atr_value)
self.trading_size = max(self.trading_size, 1)
else:
self.trading_size = 1
self.set_target(call.vt_symbol, -self.trading_size * self.leg1_ratio)
self.set_target(put.vt_symbol, -self.trading_size * self.leg2_ratio)
# 缓存均线多空信号
self.ma_signal = ma_signal
# 执行具体的委托交易
self.execute_trading(price_data, self.percent_add)
# 推送UI事件更新
self.put_event()
class MaFactor:
"""标的物均线因子(基于均线输出多空信号)"""
def __init__(self, vt_symbol: str, fast_window: int, slow_window: int) -> None:
"""构造函数"""
self.vt_symbol: str = vt_symbol
self.fast_window: int = fast_window
self.slow_window: int = slow_window
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
self.am: ArrayManager = ArrayManager(slow_window + 10)
self.signal: int = 0
def update_tick(self, tick: TickData) -> None:
"""Tick更新"""
self.bg.update_tick(tick)
def update_bar(self, bar: BarData) -> None:
"""K线更新"""
self.bg.update_bar(bar)
def update_window_bar(self, bar: BarData) -> None:
"""K线更新"""
self.am.update_bar(bar)
if not self.am.inited:
return
# 计算均线
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
# 判断信号
if self.fast_ma > self.slow_ma:
self.signal = 1
elif self.fast_ma < self.slow_ma:
self.signal = -1
else:
self.signal = 0
def get_signal(self) -> int:
"""获取当前多空信号"""
return self.signal
def get_atr(self) -> float:
"""获取ATR风险度"""
if self.am.inited:
return self.am.atr(self.slow_window)
else:
return 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

View File

@@ -0,0 +1,67 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"id": "3da22414-bb0e-4b6d-af13-8f58ad4467f6",
"metadata": {},
"outputs": [],
"source": [
"def test(a, b):\n",
" a *= 2\n",
" b -= 5\n",
" return a + b"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "1dfec6ed-af61-466c-9b24-5d4254fd4008",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"9"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"test(4, 6)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3cc923b0-95a4-4371-a4a0-028a63dea32b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,70 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from black_76 import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 数据截取自\n",
"f = 3559.2 # IF2311价格\n",
"k = 3600\n",
"r = 0.02\n",
"t = 15/365\n",
"v = 0.148 # 隐含波动率14.8%\n",
"cp = 1\n",
"price = 25.4 # 期权价格"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 从波动率计算价格和希腊值\n",
"calculate_greeks(f, k, r, t, v, cp)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 从价格计算波动率\n",
"calculate_impv(price, f, k, r, t, cp)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,196 @@
from scipy import stats
from math import log, pow, sqrt, exp
from typing import Tuple
cdf = stats.norm.cdf
pdf = stats.norm.pdf
def calculate_d1(
s: float,
k: float,
r: float,
t: float,
v: float
) -> float:
"""Calculate option D1 value"""
d1: float = (log(s / k) + (0.5 * pow(v, 2)) * t) / (v * sqrt(t))
return d1
def calculate_price(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option price"""
# Return option space value if volatility not positive
if v <= 0:
return max(0, cp * (s - k))
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
price: float = cp * (s * cdf(cp * d1) - k * cdf(cp * d2)) * exp(-r * t)
return price
def calculate_delta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option delta"""
if v <= 0:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
delta: float = cp * exp(-r * t) * cdf(cp * d1)
return delta
def calculate_gamma(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option gamma"""
if v <= 0:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
gamma: float = exp(-r * t) * pdf(d1) / (s * v * sqrt(t))
return gamma
def calculate_theta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option theta"""
if v <= 0:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
theta: float = -s * exp(-r * t) * pdf(d1) * v / (2 * sqrt(t)) \
+ cp * r * s * exp(-r * t) * cdf(cp * d1) \
- cp * r * k * exp(-r * t) * cdf(cp * d2)
return theta
def calculate_vega(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option vega"""
if v <= 0:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
vega: float = s * exp(-r * t) * pdf(d1) * sqrt(t)
return vega
def calculate_greeks(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int
) -> Tuple[float, float, float, float, float]:
"""Calculate option price and greeks"""
d1: float = calculate_d1(s, k, r, t, v)
price: float = calculate_price(s, k, r, t, v, cp, d1)
delta: float = calculate_delta(s, k, r, t, v, cp, d1)
gamma: float = calculate_gamma(s, k, r, t, v, d1)
theta: float = calculate_theta(s, k, r, t, v, cp, d1)
vega: float = calculate_vega(s, k, r, t, v, d1)
return price, delta, gamma, theta, vega
def calculate_impv(
price: float,
s: float,
k: float,
r: float,
t: float,
cp: int
):
"""Calculate option implied volatility"""
# Check option price must be positive
if price <= 0:
return 0
# Check if option price meets minimum value (exercise value)
meet: bool = False
if cp == 1 and (price > (s - k) * exp(-r * t)):
meet = True
elif cp == -1 and (price > k * exp(-r * t) - s):
meet = True
# If minimum value not met, return 0
if not meet:
return 0
# Calculate implied volatility with Newton's method
v: float = 0.01 # Initial guess of volatility
for i in range(50):
# Caculate option price and vega with current guess
p: float = calculate_price(s, k, r, t, v, cp)
vega: float = calculate_vega(s, k, r, t, v, cp)
# Break loop if vega too close to 0
if not vega:
break
# Calculate error value
dx: float = (price - p) / vega
# Check if error value meets requirement
if abs(dx) < 0.00001:
break
# Calculate guessed implied volatility of next round
v += dx
# Check end result to be non-negative
if v <= 0:
return 0
# Round to 4 decimal places
v = round(v, 4)
return v

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
from scipy import stats
from math import log, pow, sqrt, exp
from typing import Tuple
cdf = stats.norm.cdf
pdf = stats.norm.pdf
def calculate_d1(
s: float,
k: float,
r: float,
t: float,
v: float
) -> float:
"""Calculate option D1 value"""
d1: float = (log(s / k) + (0.5 * pow(v, 2)) * t) / (v * sqrt(t))
return d1
def calculate_price(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option price"""
if v <= 0 or not t:
return max(0, cp * (s - k))
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
price: float = cp * (s * cdf(cp * d1) - k * cdf(cp * d2)) * exp(-r * t)
return price
def calculate_delta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option delta"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
delta: float = cp * exp(-r * t) * cdf(cp * d1)
return delta
def calculate_gamma(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option gamma"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
gamma: float = exp(-r * t) * pdf(d1) / (s * v * sqrt(t))
return gamma
def calculate_theta(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int,
d1: float = 0.0
) -> float:
"""Calculate option theta"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
d2: float = d1 - v * sqrt(t)
theta: float = -s * exp(-r * t) * pdf(d1) * v / (2 * sqrt(t)) \
+ cp * r * s * exp(-r * t) * cdf(cp * d1) \
- cp * r * k * exp(-r * t) * cdf(cp * d2)
return theta
def calculate_vega(
s: float,
k: float,
r: float,
t: float,
v: float,
d1: float = 0.0
) -> float:
"""Calculate option vega"""
if v <= 0 or not t:
return 0
if not d1:
d1: float = calculate_d1(s, k, r, t, v)
vega: float = s * exp(-r * t) * pdf(d1) * sqrt(t)
return vega
def calculate_greeks(
s: float,
k: float,
r: float,
t: float,
v: float,
cp: int
) -> Tuple[float, float, float, float, float]:
"""Calculate option price and greeks"""
d1: float = calculate_d1(s, k, r, t, v)
price: float = calculate_price(s, k, r, t, v, cp, d1)
delta: float = calculate_delta(s, k, r, t, v, cp, d1)
gamma: float = calculate_gamma(s, k, r, t, v, d1)
theta: float = calculate_theta(s, k, r, t, v, cp, d1)
vega: float = calculate_vega(s, k, r, t, v, d1)
return price, delta, gamma, theta, vega
def calculate_impv(
price: float,
s: float,
k: float,
r: float,
t: float,
cp: int
):
"""Calculate option implied volatility"""
# Check option price must be positive
if price <= 0 or not t:
return 0
# Check if option price meets minimum value (exercise value)
meet: bool = False
if cp == 1 and (price > (s - k) * exp(-r * t)):
meet = True
elif cp == -1 and (price > k * exp(-r * t) - s):
meet = True
# If minimum value not met, return 0
if not meet:
return 0
# Calculate implied volatility with Newton's method
v: float = 0.01 # Initial guess of volatility
for i in range(50):
# Caculate option price and vega with current guess
p: float = calculate_price(s, k, r, t, v, cp)
vega: float = calculate_vega(s, k, r, t, v, cp)
# Break loop if vega too close to 0
if not vega:
break
# Calculate error value
dx: float = (price - p) / vega
# Check if error value meets requirement
if abs(dx) < 0.00001:
break
# Calculate guessed implied volatility of next round
v += dx
# Check end result to be non-negative
if v <= 0:
return 0
# Round to 4 decimal places
v = round(v, 4)
return v