增加交易策略、交易指标、量化库代码等文件夹
This commit is contained in:
378
1.交易策略/2.价差策略/1.松鼠SF14_商品股指套利策略/使用文稿/VNPY/VIP14.py
Normal file
378
1.交易策略/2.价差策略/1.松鼠SF14_商品股指套利策略/使用文稿/VNPY/VIP14.py
Normal file
@@ -0,0 +1,378 @@
|
||||
from typing import List, Dict
|
||||
from datetime import datetime
|
||||
|
||||
from vnpy_portfoliostrategy import (
|
||||
StrategyEngine,
|
||||
StrategyTemplate,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
ArrayManager,
|
||||
)
|
||||
|
||||
from vnpy.trader.utility import Interval
|
||||
from vnpy_portfoliostrategy.utility import PortfolioBarGenerator
|
||||
import numpy as np
|
||||
import talib as ta
|
||||
|
||||
#使用portfoliostrategy模块编写的交易策略,无参数优化,请自行调试优化参数。
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
class VIP14(StrategyTemplate):
|
||||
""""""
|
||||
|
||||
author = "松鼠quant"
|
||||
|
||||
N = 2
|
||||
M = 5
|
||||
X = 5
|
||||
TRS=5
|
||||
|
||||
fixed_size=1
|
||||
|
||||
am_1=0
|
||||
am_2=0
|
||||
current_bar=0
|
||||
|
||||
parameters = [
|
||||
"N",
|
||||
"M",
|
||||
"X",
|
||||
"fixed_size",
|
||||
"TRS"
|
||||
]
|
||||
variables = []
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
strategy_engine: StrategyEngine,
|
||||
strategy_name: str,
|
||||
vt_symbols: List[str],
|
||||
setting: dict
|
||||
):
|
||||
""""""
|
||||
super().__init__(strategy_engine, strategy_name, vt_symbols, setting)
|
||||
|
||||
self.KG=0
|
||||
self.high_after_entry=0
|
||||
self.low_after_entry=0
|
||||
self.dliq_point=0
|
||||
self.kliq_point=0
|
||||
self.curbar_h = np.zeros(100) # 例如,初始化为长度为 100 的数组
|
||||
self.curbar_l = np.zeros(100)
|
||||
# 初始化 hd 属性
|
||||
self.hd = np.zeros(100) # 例如,初始化为长度为 100 的数组
|
||||
self.ld = np.zeros(100) # 例如,初始化为长度为 100 的数组
|
||||
self.targets: Dict[str, int] = {}
|
||||
self.last_tick_time: datetime = None
|
||||
|
||||
# Obtain contract info
|
||||
self.ams: Dict[str, ArrayManager] = {}
|
||||
for vt_symbol in self.vt_symbols:
|
||||
self.ams[vt_symbol] = ArrayManager(100)
|
||||
self.targets[vt_symbol] = 0
|
||||
|
||||
self.pbg = PortfolioBarGenerator(self.on_bars, 2, self.on_2hour_bars, Interval.HOUR)
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
Callback when strategy is inited.
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
|
||||
self.load_bars(10)
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
Callback when strategy is started.
|
||||
"""
|
||||
self.write_log("策略启动")
|
||||
|
||||
def on_stop(self):
|
||||
"""
|
||||
Callback when strategy is stopped.
|
||||
"""
|
||||
self.write_log("策略停止")
|
||||
|
||||
def on_tick(self, tick: TickData):
|
||||
"""
|
||||
Callback of new tick data update.
|
||||
"""
|
||||
self.pbg.update_tick(tick)
|
||||
|
||||
def on_bars(self, bars: Dict[str, BarData]):
|
||||
"""
|
||||
Callback of new bars data update.
|
||||
"""
|
||||
self.pbg.update_bars(bars)
|
||||
|
||||
|
||||
def on_2hour_bars(self, bars: Dict[str, BarData]):
|
||||
""""""
|
||||
self.cancel_all()
|
||||
# 初始化
|
||||
nx = 0
|
||||
|
||||
# 获取合约的数据
|
||||
for vt_symbol, bar in bars.items():
|
||||
nx += 1
|
||||
# 更新 ArrayManager
|
||||
am: ArrayManager = self.ams[vt_symbol]
|
||||
am.update_bar(bar)
|
||||
#print(vt_symbol)
|
||||
# 如果 ArrayManager 还未初始化完毕,则跳过
|
||||
if not am.inited:
|
||||
return
|
||||
# 将第一个和第二个合约的 ArrayManager 分别保存下来
|
||||
if nx == 1:
|
||||
self.am_1 = am
|
||||
elif nx == 2:
|
||||
self.am_2 = am
|
||||
|
||||
am_1=self.am_1
|
||||
am_2=self.am_2
|
||||
|
||||
self.current_bar=self.current_bar+1 #Bar线计数
|
||||
# 计算相关指标
|
||||
c_jb = am_1.close_array / am_2.close_array # 计算价比
|
||||
copen = am_1.open_array / am_2.open_array # 计算开比
|
||||
chigh = np.maximum(np.maximum(am_1.high_array / am_2.high_array, copen), c_jb)
|
||||
clow = np.minimum(np.minimum(am_1.low_array / am_2.low_array, copen), c_jb)
|
||||
|
||||
# 计算多个指标的序列
|
||||
var1 = self.highest(chigh, self.N) - self.lowest(clow, self.N)
|
||||
var2 = self.highest(chigh, self.N) - c_jb[-self.N:]
|
||||
var3 = c_jb[-self.N:] - self.lowest(clow, self.N)
|
||||
var4 = var2 / var1 * 100 - 70
|
||||
var5 = (c_jb[-self.N:] - self.lowest(clow, self.N)) / var1 * 100
|
||||
var6 = (2 * c_jb + chigh + clow) / 4
|
||||
var7 = self.sma(var3 / var1 * 100, 3, 1)
|
||||
var8 = self.lowest(clow, self.M)
|
||||
var9 = self.sma(var7, 3, 1) - self.sma(var4, 9, 1)
|
||||
vara = self.highest(chigh, self.M)
|
||||
bd1 = self.xaverage((var6[-self.M:] - var8) / (vara - var8) * 100, self.M)
|
||||
bd2 = self.xaverage(bd1, self.X)
|
||||
ma1 = self.xaverage(c_jb, self.X)
|
||||
# 检查金叉 (BD1 从小于 BD2 变为大于 BD2)
|
||||
dk_up = bd1[-1] > bd2[-1] and bd1[-2] <= bd2[-1]
|
||||
|
||||
# 检查死叉 (BD1 从大于 BD2 变为小于 BD2)
|
||||
kk_dn = bd1[-1] < bd2[-1] and bd1[-2] >= bd2[-1]
|
||||
|
||||
# 确保 dk_up 和 kk_dn 不为空
|
||||
if dk_up:
|
||||
self.curbar_h = self.current_bar # 在最后一次金叉发生时更新 curbar_h
|
||||
period = max(self.curbar_h - self.curbar_l, self.M)
|
||||
|
||||
# 确保 period 不超过 chigh 的长度
|
||||
if len(chigh) >= period:
|
||||
Highup = max(chigh[-period:-1])
|
||||
else:
|
||||
Highup = max(chigh[-self.M:-1]) # 如果 period 超出范围,则取整个序列的最大值
|
||||
|
||||
if kk_dn:
|
||||
self.curbar_l = self.current_bar # 在最后一次死叉发生时更新 curbar_l
|
||||
period = max(self.curbar_l - self.curbar_h, self.M)
|
||||
|
||||
# 确保 period 不超过 clow 的长度
|
||||
if len(clow) >= period:
|
||||
Lowdown = min(clow[-period:-1])
|
||||
else:
|
||||
Lowdown = min(clow[-self.M:-1]) # 如果 period 超出范围,则取整个序列的最小值
|
||||
|
||||
#print(f'bd1[-1]{bd1[-1] },bd2[-1] {bd2[-1]},chigh[-1]{chigh[-1]},Highup{Highup},c_jb[-1]{c_jb[-1]},ma1[-1]{ma1[-1]}')
|
||||
# 开仓条件
|
||||
cond_dk = (bd1[-1] > bd2[-1]) & (chigh[-1] >= Highup) & (c_jb[-1] > ma1[-1])
|
||||
cond_kk = (bd1[-1] < bd2[-1]) & (clow[-1] <= Lowdown) & (c_jb[-1] < ma1[-1])
|
||||
|
||||
self.KG = np.where(cond_dk, 1, np.where(cond_kk, -1, 0))
|
||||
|
||||
#print(self.KG)
|
||||
#开仓部分
|
||||
nx=0
|
||||
for vt_symbol, bar in bars.items():
|
||||
nx+=1
|
||||
am: ArrayManager = self.ams[vt_symbol]
|
||||
if not am.inited:
|
||||
return
|
||||
price = bar.close_price
|
||||
current_pos = self.get_pos(vt_symbol)
|
||||
#print(nx,current_pos,self.KG)
|
||||
#数据源1:
|
||||
if nx==1:
|
||||
#开多
|
||||
if self.KG>0 :
|
||||
|
||||
if current_pos<0:
|
||||
self.cover(vt_symbol, price, self.fixed_size)
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
if current_pos==0:
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
#开空
|
||||
if self.KG<0 :
|
||||
if current_pos > 0:
|
||||
self.sell(vt_symbol, price, self.fixed_size)
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
if current_pos == 0:
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
#数据源2
|
||||
elif nx==2:
|
||||
#开多
|
||||
if self.KG>0:
|
||||
if current_pos < 0:
|
||||
self.cover(vt_symbol, price, self.fixed_size)
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
if current_pos == 0:
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
#开空
|
||||
if self.KG<0 and current_pos >= 0 :
|
||||
if current_pos > 0 :
|
||||
self.sell(vt_symbol, price, self.fixed_size)
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
if current_pos == 0 :
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
#移动出场
|
||||
if self.KG == 0:
|
||||
self.high_after_entry = chigh[-1]
|
||||
self.low_after_entry = clow[-1]
|
||||
elif self.KG != 0:
|
||||
self.high_after_entry = min(self.high_after_entry, chigh[-1])
|
||||
self.low_after_entry = max(self.low_after_entry, clow[-1])
|
||||
|
||||
# Calculate liquidation points
|
||||
if self.KG > 0:
|
||||
self.dliq_point = self.low_after_entry - (copen[-1] * self.TRS / 1000)
|
||||
elif self.KG < 0:
|
||||
self.kliq_point = self.high_after_entry + (copen[-1] * self.TRS / 1000)
|
||||
|
||||
nx=0
|
||||
# print("多头",c_jb[-1],self.dliq_point)
|
||||
# print("空头",c_jb[-1],self.kliq_point)
|
||||
for vt_symbol, bar in bars.items():
|
||||
nx+=1
|
||||
am: ArrayManager = self.ams[vt_symbol]
|
||||
if not am.inited:
|
||||
return
|
||||
price = bar.close_price
|
||||
current_pos = self.get_pos(vt_symbol)
|
||||
|
||||
#数据源1
|
||||
if nx==1:
|
||||
|
||||
if self.KG > 0 and c_jb[-1]<= self.dliq_point and self.dliq_point > 0 and current_pos>0:
|
||||
self.sell(vt_symbol, price, current_pos)
|
||||
if self.KG < 0 and c_jb[-1] >= self.kliq_point and self.kliq_point > 0 and current_pos<0:
|
||||
self.cover(vt_symbol, price, current_pos)
|
||||
#数据源2
|
||||
if nx==2:
|
||||
if self.KG > 0 and c_jb[-1]<= self.dliq_point and self.dliq_point > 0 and current_pos<0:
|
||||
self.cover(vt_symbol, price, current_pos)
|
||||
if self.KG < 0 and c_jb[-1] >= self.kliq_point and self.kliq_point > 0 and current_pos>0:
|
||||
self.sell(vt_symbol, price, current_pos)
|
||||
|
||||
self.put_event()
|
||||
|
||||
|
||||
def highest(self, data, period):
|
||||
"""
|
||||
计算序列最后 period 个元素中的最高值,返回一个序列
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素中的最高值序列
|
||||
"""
|
||||
result = []
|
||||
start_index = max(0, len(data) - period)
|
||||
|
||||
for i in range(start_index, len(data)):
|
||||
result.append(max(data[max(0, i - period + 1):i + 1]))
|
||||
|
||||
return np.array(result)
|
||||
|
||||
|
||||
def lowest(self, data, period):
|
||||
"""
|
||||
计算序列最后 period 个元素中的最小值,返回一个序列
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素中的最小值序列
|
||||
"""
|
||||
result = []
|
||||
start_index = max(0, len(data) - period)
|
||||
|
||||
for i in range(start_index, len(data)):
|
||||
result.append(min(data[max(0, i - period + 1):i + 1]))
|
||||
|
||||
return np.array(result)
|
||||
|
||||
|
||||
def sma(self, data, period, weight=1):
|
||||
"""
|
||||
计算简单移动平均(SMA)的序列,仅计算最后 period 个元素
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:param weight: 权重
|
||||
:return: SMA序列
|
||||
"""
|
||||
if len(data) < period:
|
||||
return np.convolve(data, np.ones((len(data),))/len(data), mode='valid')
|
||||
else:
|
||||
return np.convolve(data[-period:], np.ones((period,))/period, mode='valid')
|
||||
|
||||
|
||||
|
||||
|
||||
def xaverage(self, data, period):
|
||||
"""
|
||||
计算加权平均(WMA)的序列
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: WMA序列
|
||||
"""
|
||||
if len(data) < period:
|
||||
weights = np.arange(1, len(data) + 1)
|
||||
return np.convolve(data, weights/weights.sum(), mode='full')[-len(data):]
|
||||
else:
|
||||
weights = np.arange(1, period + 1)
|
||||
return np.convolve(data[-period:], weights/weights.sum(), mode='full')[-period:]
|
||||
|
||||
|
||||
|
||||
def HHV(self, data, period):
|
||||
"""
|
||||
计算给定数据序列中最后 period 个元素的最大值
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素的最大值
|
||||
"""
|
||||
if len(data) == 0:
|
||||
return None # 如果数据为空,返回 None 或者抛出异常
|
||||
|
||||
if period > len(data):
|
||||
period = len(data) # 如果 period 超出数据长度,则取数据长度
|
||||
|
||||
return max(data[-period:])
|
||||
|
||||
|
||||
def LLV(self, data, period):
|
||||
"""
|
||||
计算给定数据序列中最后 period 个元素的最小值
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素的最小值
|
||||
"""
|
||||
if len(data) == 0:
|
||||
return None # 如果数据为空,返回 None 或者抛出异常
|
||||
|
||||
if period > len(data):
|
||||
period = len(data) # 如果 period 超出数据长度,则取数据长度
|
||||
|
||||
return min(data[-period:])
|
||||
Reference in New Issue
Block a user