|
|
@@ -5,7 +5,7 @@ from statistics import mean
|
|
|
import os
|
|
|
from datetime import datetime
|
|
|
|
|
|
-def analyze_options(*options):
|
|
|
+def analyze_options(*options, mark_prices=None):
|
|
|
"""
|
|
|
统一的期权分析方法
|
|
|
|
|
|
@@ -17,6 +17,8 @@ def analyze_options(*options):
|
|
|
premium: 权利金
|
|
|
strike_price: 行权价
|
|
|
quantity: 数量
|
|
|
+ mark_prices: 可选,需要标注的标的价格列表,如 [1.35, 1.52]
|
|
|
+ 可以作为关键字参数或最后一个位置参数传递
|
|
|
|
|
|
示例:
|
|
|
# 单个期权
|
|
|
@@ -24,17 +26,34 @@ def analyze_options(*options):
|
|
|
|
|
|
# 期权组合
|
|
|
analyze_options(('buy', 'call', 0.08, 2.9, 1), ('sell', 'call', 0.03, 3.1, 1))
|
|
|
+
|
|
|
+ # 标注指定价格点(关键字参数方式)
|
|
|
+ analyze_options(('buy', 'call', 0.05, 3.0, 1), mark_prices=[2.8, 3.2])
|
|
|
+
|
|
|
+ # 标注指定价格点(位置参数方式)
|
|
|
+ analyze_options(('buy', 'call', 0.05, 3.0, 1), [2.8, 3.2])
|
|
|
"""
|
|
|
|
|
|
if not options:
|
|
|
raise ValueError("请至少提供一个期权")
|
|
|
|
|
|
+ # 检查最后一个参数是否是 mark_prices(列表类型且不是期权格式)
|
|
|
+ actual_options = list(options)
|
|
|
+ if (mark_prices is None and len(actual_options) > 0 and
|
|
|
+ isinstance(actual_options[-1], list) and
|
|
|
+ len(actual_options[-1]) != 5):
|
|
|
+ # 最后一个参数是列表且不是期权格式,将其视为 mark_prices
|
|
|
+ mark_prices = actual_options.pop()
|
|
|
+
|
|
|
+ if not actual_options:
|
|
|
+ raise ValueError("请至少提供一个期权")
|
|
|
+
|
|
|
# 解析期权数据
|
|
|
option_list = []
|
|
|
all_strikes = []
|
|
|
|
|
|
- for i, opt in enumerate(options):
|
|
|
- if len(opt) != 5:
|
|
|
+ for i, opt in enumerate(actual_options):
|
|
|
+ if not isinstance(opt, (tuple, list)) or len(opt) != 5:
|
|
|
raise ValueError(f"期权{i+1}格式错误,应为(direction, option_type, premium, strike_price, quantity)")
|
|
|
|
|
|
direction, option_type, premium, strike_price, quantity = opt
|
|
|
@@ -76,10 +95,10 @@ def analyze_options(*options):
|
|
|
results['combined'] = combined_profits
|
|
|
|
|
|
# 绘制图表
|
|
|
- _plot_results(results, option_list, prices)
|
|
|
+ _plot_results(results, option_list, prices, mark_prices=mark_prices)
|
|
|
|
|
|
# 打印分析报告
|
|
|
- _print_report(results, option_list, prices)
|
|
|
+ _print_report(results, option_list, prices, mark_prices=mark_prices)
|
|
|
|
|
|
return pd.DataFrame(results)
|
|
|
|
|
|
@@ -123,7 +142,7 @@ def _calculate_profit(option, price):
|
|
|
return 0
|
|
|
|
|
|
|
|
|
-def _plot_results(results, option_list, prices):
|
|
|
+def _plot_results(results, option_list, prices, mark_prices=None):
|
|
|
"""绘制分析图表"""
|
|
|
plt.figure(figsize=(14, 10))
|
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
@@ -151,9 +170,17 @@ def _plot_results(results, option_list, prices):
|
|
|
|
|
|
# 找到并标注关键点(盈亏平衡点、最大收益/损失边界点)
|
|
|
if 'combined' in results:
|
|
|
- _mark_key_points(results['combined'], prices, 'Portfolio')
|
|
|
+ profits_for_marking = results['combined']
|
|
|
+ _mark_key_points(profits_for_marking, prices, 'Portfolio')
|
|
|
elif len(option_list) == 1:
|
|
|
- _mark_key_points(results['opt1'], prices, 'Option')
|
|
|
+ profits_for_marking = results['opt1']
|
|
|
+ _mark_key_points(profits_for_marking, prices, 'Option')
|
|
|
+ else:
|
|
|
+ profits_for_marking = None
|
|
|
+
|
|
|
+ # 标注指定的价格点
|
|
|
+ if mark_prices and profits_for_marking is not None:
|
|
|
+ _mark_specified_prices(mark_prices, profits_for_marking, prices, option_list)
|
|
|
|
|
|
plt.xlabel('Assest Price', fontsize=12)
|
|
|
plt.ylabel('Profit/Loss', fontsize=12)
|
|
|
@@ -226,6 +253,40 @@ def _mark_key_points(profits, prices, label_prefix):
|
|
|
fontsize=10, color='magenta', weight='bold')
|
|
|
|
|
|
|
|
|
+def _mark_specified_prices(mark_prices, profits, prices, option_list):
|
|
|
+ """标注指定的标的价格点及其对应的损益"""
|
|
|
+ if not mark_prices:
|
|
|
+ return
|
|
|
+
|
|
|
+ # 计算每个指定价格对应的损益
|
|
|
+ for mark_price in mark_prices:
|
|
|
+ # 找到最接近的价格索引
|
|
|
+ price_idx = np.argmin(np.abs(prices - mark_price))
|
|
|
+ actual_price = prices[price_idx]
|
|
|
+ profit_value = profits[price_idx]
|
|
|
+
|
|
|
+ # 绘制标记点(使用黄色星形标记)
|
|
|
+ plt.plot(actual_price, profit_value, '*', color='gold', markersize=15,
|
|
|
+ markeredgecolor='darkorange', markeredgewidth=2, zorder=5)
|
|
|
+
|
|
|
+ # 添加标注
|
|
|
+ price_range = prices.max() - prices.min()
|
|
|
+ profit_range = max(profits) - min(profits) if len(profits) > 0 else 1
|
|
|
+
|
|
|
+ # 根据损益值的正负决定标注位置
|
|
|
+ if profit_value >= 0:
|
|
|
+ y_offset = profit_range * 0.15
|
|
|
+ else:
|
|
|
+ y_offset = -profit_range * 0.15
|
|
|
+
|
|
|
+ plt.annotate(f'Price: {actual_price:.4f}\nP/L: {profit_value:.4f}',
|
|
|
+ xy=(actual_price, profit_value),
|
|
|
+ xytext=(actual_price + price_range * 0.05, profit_value + y_offset),
|
|
|
+ arrowprops=dict(arrowstyle='->', color='darkorange', lw=1.5),
|
|
|
+ fontsize=10, color='darkorange', weight='bold',
|
|
|
+ bbox=dict(boxstyle='round,pad=0.5', facecolor='wheat', alpha=0.8))
|
|
|
+
|
|
|
+
|
|
|
def _find_boundary_points(profits, prices, extreme_value, _extreme_type):
|
|
|
"""找到最大收益或最大损失的边界点"""
|
|
|
boundary_points = []
|
|
|
@@ -268,7 +329,7 @@ def _find_boundary_points(profits, prices, extreme_value, _extreme_type):
|
|
|
return boundary_points
|
|
|
|
|
|
|
|
|
-def _print_report(results, option_list, prices):
|
|
|
+def _print_report(results, option_list, prices, mark_prices=None):
|
|
|
"""打印分析报告"""
|
|
|
print("=" * 60)
|
|
|
print("期权分析报告")
|
|
|
@@ -314,6 +375,16 @@ def _print_report(results, option_list, prices):
|
|
|
else:
|
|
|
print("无盈亏平衡点")
|
|
|
|
|
|
+ # 打印指定价格点的损益信息
|
|
|
+ if mark_prices:
|
|
|
+ print(f"\n【指定价格点分析】")
|
|
|
+ for mark_price in mark_prices:
|
|
|
+ # 找到最接近的价格索引
|
|
|
+ price_idx = np.argmin(np.abs(prices - mark_price))
|
|
|
+ actual_price = prices[price_idx]
|
|
|
+ profit_value = profits[price_idx]
|
|
|
+ print(f"标的价格: {actual_price:.4f} -> 损益: {profit_value:.4f} (一单: {profit_value * 10000:.2f}元)")
|
|
|
+
|
|
|
print("=" * 60)
|
|
|
|
|
|
-analyze_options(('sell', 'call', 0.066, 3.0, 1), ('buy', 'call', 0.2017, 2.8, 1))
|
|
|
+analyze_options(('buy', 'put', 0.1636, 1.55, 2), ('sell', 'put', 0.084, 1.45, 1), [1.314, 1.35, 1.52])
|