491 lines
19 KiB
Python
491 lines
19 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
20万本金 × 按季度投资 × 多算法对比回测 (v7.0 最优交易时点)
|
||
|
||
条件(v7.0: 最优时点 + 完全无人为限制):
|
||
- 本金:¥200,000(唯一约束)
|
||
- 单只上限:无
|
||
- 最大持仓:无
|
||
- 每笔股数:动态(总资金 × position_pct%,每笔等金额)
|
||
- 股价区间:无
|
||
- 每日最多买入:无
|
||
- 买入时间:09:35(最优,网格搜索验证)
|
||
- 卖出时间:13:40(最优,网格搜索验证)
|
||
- 回测区间:2025-01-01 ~ 最新,按季度分段
|
||
"""
|
||
import sys
|
||
import os
|
||
import time
|
||
from datetime import date, datetime
|
||
from collections import defaultdict
|
||
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
|
||
from backtest_recommend import (
|
||
get_db_conn, run_backtest, START_DATE, SELL_COOLDOWN_DAYS,
|
||
)
|
||
|
||
# ─── 20万本金配置(v5.2: 完全无人为限制 + 动态仓位) ─────────────────
|
||
CAPITAL = 200_000 # 总本金(唯一约束)
|
||
POSITION_PCT = 5 # 单笔仓位 = 总资金的5%(¥10,000/笔,约可持20只)
|
||
|
||
# ─── 季度定义 ─────────────────────
|
||
QUARTERS = [
|
||
("2025-Q1", date(2025, 1, 2), date(2025, 3, 31)),
|
||
("2025-Q2", date(2025, 4, 1), date(2025, 6, 30)),
|
||
("2025-Q3", date(2025, 7, 1), date(2025, 9, 30)),
|
||
("2025-Q4", date(2025, 10, 1), date(2025, 12, 31)),
|
||
("2026-Q1", date(2026, 1, 5), date(2026, 2, 25)),
|
||
# 完整区间
|
||
("全期间", date(2025, 1, 2), date(2026, 2, 25)),
|
||
]
|
||
|
||
# ─── 算法配置 ─────────────────────
|
||
ALGORITHMS = [
|
||
# 名称, 参数字典
|
||
("v3|基线(TP10+SL8)", {
|
||
"take_profit_pct": 10, "stop_loss_pct": 8,
|
||
}),
|
||
("v4|触发≥2+TP10+SL8", {
|
||
"min_buy_triggered": 2, "take_profit_pct": 10, "stop_loss_pct": 8,
|
||
}),
|
||
("v4.2|延迟2天+TP10+SL8", {
|
||
"sell_confirm_days": 2, "take_profit_pct": 10, "stop_loss_pct": 8,
|
||
}),
|
||
("v4|忽略卖出+TP10+SL8", {
|
||
"ignore_sell_signal": True, "take_profit_pct": 10, "stop_loss_pct": 8,
|
||
}),
|
||
("v4|触发≥2+TP15+SL8", {
|
||
"min_buy_triggered": 2, "take_profit_pct": 15, "stop_loss_pct": 8,
|
||
}),
|
||
("v4|触发≥2+TP10+SL5", {
|
||
"min_buy_triggered": 2, "take_profit_pct": 10, "stop_loss_pct": 5,
|
||
}),
|
||
("v4.2|延迟2天+触发≥2+TP10+SL8", {
|
||
"sell_confirm_days": 2, "min_buy_triggered": 2,
|
||
"take_profit_pct": 10, "stop_loss_pct": 8,
|
||
}),
|
||
("v4.1|跟踪止盈8/3+SL5", {
|
||
"ignore_sell_signal": True, "trailing_start_pct": 8, "trailing_gap_pct": 3,
|
||
"stop_loss_pct": 5,
|
||
}),
|
||
("v4.1|跟踪止盈8/3+触发≥2+SL5", {
|
||
"ignore_sell_signal": True, "trailing_start_pct": 8, "trailing_gap_pct": 3,
|
||
"stop_loss_pct": 5, "min_buy_triggered": 2,
|
||
}),
|
||
("v4|触发≥2+TP10+SL8+持仓≤30天", {
|
||
"min_buy_triggered": 2, "take_profit_pct": 10, "stop_loss_pct": 8,
|
||
"max_hold_days": 30,
|
||
}),
|
||
# ── v5.2: 信号加权仓位 (强信号1.5倍/较强1.2倍) ──
|
||
("v5.2|延迟2天+触发≥2+TP10+SL8+信号加权", {
|
||
"sell_confirm_days": 2, "min_buy_triggered": 2,
|
||
"take_profit_pct": 10, "stop_loss_pct": 8,
|
||
"signal_weight": True,
|
||
}),
|
||
("v5.2|忽略卖出+TP10+SL8+信号加权", {
|
||
"ignore_sell_signal": True, "take_profit_pct": 10, "stop_loss_pct": 8,
|
||
"signal_weight": True,
|
||
}),
|
||
("v5.2|跟踪止盈8/3+SL5+信号加权", {
|
||
"ignore_sell_signal": True, "trailing_start_pct": 8, "trailing_gap_pct": 3,
|
||
"stop_loss_pct": 5, "signal_weight": True,
|
||
}),
|
||
]
|
||
|
||
|
||
def main():
|
||
conn = get_db_conn()
|
||
|
||
# 检查数据覆盖
|
||
with conn.cursor() as cur:
|
||
cur.execute("SELECT min(scan_date), max(scan_date), count(DISTINCT scan_date) FROM stock_scan_history")
|
||
scan_min, scan_max, scan_days = cur.fetchone()
|
||
print(f"📊 扫描数据: {scan_min} ~ {scan_max} ({scan_days}天)")
|
||
|
||
cur.execute("SELECT min(trade_date), max(trade_date), count(DISTINCT trade_date) FROM stock_kline_daily")
|
||
kline_min, kline_max, kline_days = cur.fetchone()
|
||
print(f"📊 K线数据: {kline_min} ~ {kline_max} ({kline_days}天)")
|
||
|
||
print(f"\n{'='*120}")
|
||
print(f" 💰 20万本金 按季度投资 × {len(ALGORITHMS)}种算法 对比回测 (v7.0 最优时点)")
|
||
print(f"{'='*120}")
|
||
print(f" 本金: ¥{CAPITAL:,} (唯一约束) | 单笔仓位: {POSITION_PCT}%=¥{int(CAPITAL*POSITION_PCT/100):,}/笔(动态)")
|
||
print(f" 限制: 单只上限=无 | 最大持仓=无 | 股价区间=无 | 每日买入=无 | 冷却期: {SELL_COOLDOWN_DAYS}天")
|
||
print(f" 时点: 买入@09:35 | 卖出@13:40 (v7.0 网格搜索最优)")
|
||
print(f"{'='*120}\n")
|
||
|
||
# results[quarter_name][algo_name] = stats_dict
|
||
results = {}
|
||
|
||
total_runs = len(QUARTERS) * len(ALGORITHMS)
|
||
run_idx = 0
|
||
|
||
for q_name, q_start, q_end in QUARTERS:
|
||
results[q_name] = {}
|
||
for algo_name, algo_params in ALGORITHMS:
|
||
run_idx += 1
|
||
print(f" [{run_idx}/{total_runs}] {q_name} | {algo_name}", end="", flush=True)
|
||
t0 = time.time()
|
||
|
||
result = run_backtest(
|
||
conn,
|
||
start_date=q_start, end_date=q_end,
|
||
total_capital=CAPITAL, # v5.1: 总资金约束模式
|
||
position_pct=POSITION_PCT, # v5.2: 动态仓位(每笔=总资金×5%)
|
||
use_5min_prices=True, # v7: 使用5分钟实时价格
|
||
buy_time='09:35', # v7: 最优买入时间
|
||
sell_time='13:40', # v7: 最优卖出时间
|
||
verbose=False,
|
||
**algo_params,
|
||
)
|
||
|
||
elapsed = time.time() - t0
|
||
if result and result.get('stats'):
|
||
s = result['stats']
|
||
results[q_name][algo_name] = s
|
||
print(f" → ¥{s['profit']:>+10,.0f} 收益{s['capital_pct']:>+6.1f}% "
|
||
f"年化{s['capital_ann_pct']:>+7.1f}% 胜率{s['win_rate']:>5.1f}% "
|
||
f"({elapsed:.1f}s)", flush=True)
|
||
else:
|
||
results[q_name][algo_name] = None
|
||
print(f" → 无数据 ({elapsed:.1f}s)", flush=True)
|
||
|
||
conn.close()
|
||
|
||
# ─── 输出结果 ─────────────────────
|
||
|
||
# 1. 控制台大表
|
||
print(f"\n{'='*160}")
|
||
print(f" 📊 20万本金 × 按季度投资 完整对比表 (v5.2 动态仓位: 每笔={POSITION_PCT}%=¥{int(CAPITAL*POSITION_PCT/100):,})")
|
||
print(f"{'='*160}")
|
||
|
||
# 表头
|
||
header = f"{'算法':<36}"
|
||
for q_name, _, _ in QUARTERS:
|
||
header += f" | {q_name:>14}"
|
||
print(header)
|
||
print("-" * 160)
|
||
|
||
# 盈亏行
|
||
print("\n 📈 盈亏(元):")
|
||
print("-" * 160)
|
||
for algo_name, _ in ALGORITHMS:
|
||
row = f" {algo_name:<34}"
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
row += f" | {s['profit']:>+13,.0f}"
|
||
else:
|
||
row += f" | {'N/A':>13}"
|
||
print(row)
|
||
|
||
# 真实收益率行
|
||
print(f"\n 📊 真实收益率(%):")
|
||
print("-" * 160)
|
||
for algo_name, _ in ALGORITHMS:
|
||
row = f" {algo_name:<34}"
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
row += f" | {s['capital_pct']:>+12.1f}%"
|
||
else:
|
||
row += f" | {'N/A':>13}"
|
||
print(row)
|
||
|
||
# 年化收益率行
|
||
print(f"\n 📊 年化收益率(%):")
|
||
print("-" * 160)
|
||
for algo_name, _ in ALGORITHMS:
|
||
row = f" {algo_name:<34}"
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
method = s.get('capital_ann_method', '?')
|
||
tag = '(S)' if method == 'simple' else '(C)'
|
||
row += f" | {s['capital_ann_pct']:>+9.1f}%{tag}"
|
||
else:
|
||
row += f" | {'N/A':>13}"
|
||
print(row)
|
||
|
||
# 胜率行
|
||
print(f"\n 📊 胜率(%):")
|
||
print("-" * 160)
|
||
for algo_name, _ in ALGORITHMS:
|
||
row = f" {algo_name:<34}"
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
row += f" | {s['win_rate']:>12.1f}%"
|
||
else:
|
||
row += f" | {'N/A':>13}"
|
||
print(row)
|
||
|
||
# 盈亏比行
|
||
print(f"\n 📊 盈亏比:")
|
||
print("-" * 160)
|
||
for algo_name, _ in ALGORITHMS:
|
||
row = f" {algo_name:<34}"
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
row += f" | {s['profit_factor']:>13.2f}"
|
||
else:
|
||
row += f" | {'N/A':>13}"
|
||
print(row)
|
||
|
||
# 交易笔数行
|
||
print(f"\n 📊 交易笔数:")
|
||
print("-" * 160)
|
||
for algo_name, _ in ALGORITHMS:
|
||
row = f" {algo_name:<34}"
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
row += f" | {s['trade_count']:>13}"
|
||
else:
|
||
row += f" | {'N/A':>13}"
|
||
print(row)
|
||
|
||
print(f"\n{'='*160}")
|
||
|
||
# ─── 找出各季度最优算法 ─────────
|
||
print(f"\n 🏆 各季度最优算法:")
|
||
print("-" * 100)
|
||
for q_name, _, _ in QUARTERS:
|
||
best_algo = None
|
||
best_profit = -float('inf')
|
||
best_ann = -float('inf')
|
||
for algo_name, _ in ALGORITHMS:
|
||
s = results[q_name].get(algo_name)
|
||
if s and s['profit'] > best_profit:
|
||
best_profit = s['profit']
|
||
best_ann = s['capital_ann_pct']
|
||
best_algo = algo_name
|
||
best_stats = s
|
||
if best_algo:
|
||
print(f" {q_name:<14} → 🏆 {best_algo:<36} "
|
||
f"盈利 ¥{best_profit:>+10,.0f} 收益{best_stats['capital_pct']:>+6.1f}% "
|
||
f"年化{best_ann:>+7.1f}% 胜率{best_stats['win_rate']:.1f}% "
|
||
f"盈亏比{best_stats['profit_factor']:.2f}")
|
||
else:
|
||
print(f" {q_name:<14} → 无数据")
|
||
|
||
# ─── 输出到 Markdown ─────────
|
||
out_path = os.path.join(os.path.dirname(__file__), "docs", "backtest_quarterly_200k.md")
|
||
os.makedirs(os.path.dirname(out_path), exist_ok=True)
|
||
|
||
with open(out_path, "w", encoding="utf-8") as f:
|
||
f.write("# 💰 20万本金 × 按季度投资 × 多算法对比回测\n\n")
|
||
f.write(f"> 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n\n")
|
||
f.write("## 回测配置 (v5.2 动态仓位)\n\n")
|
||
f.write(f"| 参数 | 值 |\n|------|----|\n")
|
||
f.write(f"| 本金 | ¥{CAPITAL:,} (唯一约束) |\n")
|
||
f.write(f"| 单只上限 | 无(受总资金约束) |\n")
|
||
f.write(f"| 最大持仓 | 无(受总资金约束) |\n")
|
||
f.write(f"| 每笔仓位 | 动态: 总资金×{POSITION_PCT}% = ¥{int(CAPITAL*POSITION_PCT/100):,}/笔 |\n")
|
||
f.write(f"| 每笔股数 | 动态(根据股价自动计算,取整到100股) |\n")
|
||
f.write(f"| 股价区间 | 无 |\n")
|
||
f.write(f"| 每日最多买入 | 无 |\n")
|
||
f.write(f"| 冷却期 | {SELL_COOLDOWN_DAYS}天 |\n")
|
||
f.write(f"| 年化方法 | <90天用简单(S),≥90天用复利CAGR(C) |\n\n")
|
||
|
||
# 算法说明
|
||
f.write("## 算法说明\n\n")
|
||
f.write("| # | 算法 | 参数说明 |\n|---|------|--------|\n")
|
||
for i, (name, params) in enumerate(ALGORITHMS, 1):
|
||
param_str = ", ".join(f"{k}={v}" for k, v in params.items())
|
||
f.write(f"| {i} | {name} | {param_str} |\n")
|
||
f.write("\n")
|
||
|
||
# 盈亏对比表
|
||
f.write("## 一、盈亏对比(元)\n\n")
|
||
f.write(f"| 算法 |")
|
||
for q_name, _, _ in QUARTERS:
|
||
f.write(f" {q_name} |")
|
||
f.write("\n|------|")
|
||
for _ in QUARTERS:
|
||
f.write("--------|")
|
||
f.write("\n")
|
||
for algo_name, _ in ALGORITHMS:
|
||
f.write(f"| {algo_name} |")
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
val = f"{s['profit']:+,.0f}"
|
||
f.write(f" {val} |")
|
||
else:
|
||
f.write(" N/A |")
|
||
f.write("\n")
|
||
f.write("\n")
|
||
|
||
# 真实收益率
|
||
f.write("## 二、真实收益率(%)\n\n")
|
||
f.write(f"| 算法 |")
|
||
for q_name, _, _ in QUARTERS:
|
||
f.write(f" {q_name} |")
|
||
f.write("\n|------|")
|
||
for _ in QUARTERS:
|
||
f.write("--------|")
|
||
f.write("\n")
|
||
for algo_name, _ in ALGORITHMS:
|
||
f.write(f"| {algo_name} |")
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
f.write(f" {s['capital_pct']:+.1f}% |")
|
||
else:
|
||
f.write(" N/A |")
|
||
f.write("\n")
|
||
f.write("\n")
|
||
|
||
# 年化收益率
|
||
f.write("## 三、年化收益率(%)\n\n")
|
||
f.write("> (S)=简单年化(<90天),(C)=复利CAGR(≥90天)\n\n")
|
||
f.write(f"| 算法 |")
|
||
for q_name, _, _ in QUARTERS:
|
||
f.write(f" {q_name} |")
|
||
f.write("\n|------|")
|
||
for _ in QUARTERS:
|
||
f.write("--------|")
|
||
f.write("\n")
|
||
for algo_name, _ in ALGORITHMS:
|
||
f.write(f"| {algo_name} |")
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
method = s.get('capital_ann_method', '?')
|
||
tag = '(S)' if method == 'simple' else '(C)'
|
||
f.write(f" {s['capital_ann_pct']:+.1f}%{tag} |")
|
||
else:
|
||
f.write(" N/A |")
|
||
f.write("\n")
|
||
f.write("\n")
|
||
|
||
# 胜率
|
||
f.write("## 四、胜率(%)\n\n")
|
||
f.write(f"| 算法 |")
|
||
for q_name, _, _ in QUARTERS:
|
||
f.write(f" {q_name} |")
|
||
f.write("\n|------|")
|
||
for _ in QUARTERS:
|
||
f.write("--------|")
|
||
f.write("\n")
|
||
for algo_name, _ in ALGORITHMS:
|
||
f.write(f"| {algo_name} |")
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
f.write(f" {s['win_rate']:.1f}% |")
|
||
else:
|
||
f.write(" N/A |")
|
||
f.write("\n")
|
||
f.write("\n")
|
||
|
||
# 盈亏比
|
||
f.write("## 五、盈亏比\n\n")
|
||
f.write(f"| 算法 |")
|
||
for q_name, _, _ in QUARTERS:
|
||
f.write(f" {q_name} |")
|
||
f.write("\n|------|")
|
||
for _ in QUARTERS:
|
||
f.write("--------|")
|
||
f.write("\n")
|
||
for algo_name, _ in ALGORITHMS:
|
||
f.write(f"| {algo_name} |")
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
f.write(f" {s['profit_factor']:.2f} |")
|
||
else:
|
||
f.write(" N/A |")
|
||
f.write("\n")
|
||
f.write("\n")
|
||
|
||
# 最大占用资金
|
||
f.write("## 六、最大占用资金(元)\n\n")
|
||
f.write(f"| 算法 |")
|
||
for q_name, _, _ in QUARTERS:
|
||
f.write(f" {q_name} |")
|
||
f.write("\n|------|")
|
||
for _ in QUARTERS:
|
||
f.write("--------|")
|
||
f.write("\n")
|
||
for algo_name, _ in ALGORITHMS:
|
||
f.write(f"| {algo_name} |")
|
||
for q_name, _, _ in QUARTERS:
|
||
s = results[q_name].get(algo_name)
|
||
if s:
|
||
f.write(f" ¥{s['max_capital']:,.0f} |")
|
||
else:
|
||
f.write(" N/A |")
|
||
f.write("\n")
|
||
f.write("\n")
|
||
|
||
# 各季度最优算法
|
||
f.write("## 七、🏆 各季度最优算法\n\n")
|
||
f.write("| 季度 | 最优算法 | 盈利(元) | 真实收益 | 年化 | 胜率 | 盈亏比 |\n")
|
||
f.write("|------|---------|---------|---------|------|------|--------|\n")
|
||
for q_name, _, _ in QUARTERS:
|
||
best_algo = None
|
||
best_profit = -float('inf')
|
||
for algo_name, _ in ALGORITHMS:
|
||
s = results[q_name].get(algo_name)
|
||
if s and s['profit'] > best_profit:
|
||
best_profit = s['profit']
|
||
best_algo = algo_name
|
||
best_s = s
|
||
if best_algo:
|
||
method = best_s.get('capital_ann_method', '?')
|
||
tag = '(S)' if method == 'simple' else '(C)'
|
||
f.write(f"| {q_name} | **{best_algo}** | {best_profit:+,.0f} | "
|
||
f"{best_s['capital_pct']:+.1f}% | {best_s['capital_ann_pct']:+.1f}%{tag} | "
|
||
f"{best_s['win_rate']:.1f}% | {best_s['profit_factor']:.2f} |\n")
|
||
else:
|
||
f.write(f"| {q_name} | N/A | - | - | - | - | - |\n")
|
||
f.write("\n")
|
||
|
||
# 算法总盈利排名
|
||
f.write("## 八、算法全期间总收益排名\n\n")
|
||
algo_totals = []
|
||
for algo_name, _ in ALGORITHMS:
|
||
s = results["全期间"].get(algo_name)
|
||
if s:
|
||
algo_totals.append((algo_name, s))
|
||
algo_totals.sort(key=lambda x: x[1]['profit'], reverse=True)
|
||
|
||
f.write("| 排名 | 算法 | 全期间盈利 | 真实收益 | 年化(CAGR) | 胜率 | 盈亏比 | 最大回撤 | 占用资金 |\n")
|
||
f.write("|------|------|----------|---------|-----------|------|--------|---------|--------|\n")
|
||
for rank, (algo_name, s) in enumerate(algo_totals, 1):
|
||
medal = "🏆" if rank == 1 else ("🥈" if rank == 2 else ("🥉" if rank == 3 else f"#{rank}"))
|
||
f.write(f"| {medal} | {algo_name} | {s['profit']:+,.0f} | {s['capital_pct']:+.1f}% | "
|
||
f"{s['capital_ann_pct']:+.1f}% | {s['win_rate']:.1f}% | {s['profit_factor']:.2f} | "
|
||
f"{s['max_drawdown_pct']:.1f}% | ¥{s['max_capital']:,.0f} |\n")
|
||
f.write("\n")
|
||
|
||
# 分析结论
|
||
f.write("## 九、分析结论\n\n")
|
||
if algo_totals:
|
||
best_name, best_s = algo_totals[0]
|
||
f.write(f"### 🏆 全期间最优算法: {best_name}\n\n")
|
||
f.write(f"- 总盈利: **¥{best_s['profit']:+,.0f}**\n")
|
||
f.write(f"- 真实收益率: **{best_s['capital_pct']:+.1f}%**\n")
|
||
f.write(f"- 年化收益率: **{best_s['capital_ann_pct']:+.1f}%**\n")
|
||
f.write(f"- 胜率: **{best_s['win_rate']:.1f}%**\n")
|
||
f.write(f"- 盈亏比: **{best_s['profit_factor']:.2f}**\n")
|
||
f.write(f"- 最大回撤: **{best_s['max_drawdown_pct']:.1f}%**\n")
|
||
f.write(f"- 最大占用资金: **¥{best_s['max_capital']:,.0f}**({best_s['max_capital']/CAPITAL*100:.0f}%本金利用率)\n")
|
||
f.write(f"\n### 回报对比\n\n")
|
||
f.write(f"| 投资方式 | 年化收益 | 20万本金一年收益 |\n")
|
||
f.write(f"|---------|---------|----------------|\n")
|
||
f.write(f"| 银行定存 | 2.5% | ¥5,000 |\n")
|
||
f.write(f"| 余额宝 | 1.8% | ¥3,600 |\n")
|
||
f.write(f"| **本算法** | **{best_s['capital_ann_pct']:+.1f}%** | **¥{best_s['profit']:+,.0f}**(实际) |\n")
|
||
f.write(f"\n")
|
||
|
||
print(f"\n📝 结果已写入 {out_path}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|