from vnpy.app.cta_strategy import (
CtaTemplate,
StopOrder,
TickData,
BarData,
TradeData,
OrderData,
BarGenerator,
ArrayManager,
)
import sys,os
import json
from pathlib import Path
class GridDemo(CtaTemplate):
Lot = 1 #默认手数
grid_ma_length = 30 #均线周期
grid_step = 5 #止损步长
Lot_max = 233 #最大手数
grid_ma = 0 #均线
parameters = ["Lot",
"grid_ma_length",
"grid_step",
'Lot_max'
]
variables = ["grid_ma"]
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)
self.am = ArrayManager()
self.ask_price = 0.0 #买入价
self.bid_price = 0.0 #卖出价
self.traded = [] #成交记录
self.kai = True #开平
self.fangxiang = '' #买卖
p=Path(os.getcwd()+'\logs')
p.mkdir(exist_ok=True) #新建目录,如果目录已存在,则不会引起错误
#由于文件目录一样,如果采用新策略、新品种,本地成交记录必须先删除,否则会影响初始交易手数
if os.path.isfile(os.getcwd()+'\logs\KaiLog.json') != True: #文件不存在则创建文件,创建完关闭
with open(os.getcwd()+'\logs\KaiLog.json','w+',encoding='utf-8') as Kai_Log:
json.dump([],Kai_Log) #文件不存在创建,序列化为列表,然后关闭
self.Kai_Log=open(os.getcwd()+'\logs\KaiLog.json','r+',encoding='utf-8') #先读后写再次打开新建的文件
else : self.Kai_Log=open(os.getcwd()+'\logs\KaiLog.json','r+',encoding='utf-8') #文件存在则先读后写。文件写入完后要close()关闭才会从缓存保存进文件
if os.path.isfile(os.getcwd()+'\logs\PingLog.json') != True: #文件不存在则创建文件,创建完关闭
with open(os.getcwd()+'\logs\PingLog.json','w+',encoding='utf-8') as Ping_Log:
json.dump([],Ping_Log) #文件不存在创建,序列化为列表,然后关闭
self.Ping_Log=open(os.getcwd()+'\logs\PingLog.json','r+',encoding='utf-8') #先读后写再次打开新建的文件
else : self.Ping_Log=open(os.getcwd()+'\logs\PingLog.json','r+',encoding='utf-8') #文件存在则先读后写。文件写入完后要close()关闭才会从缓存保存进文件
self.kai_list = json.load(self.Kai_Log) #文件读取进列表
self.ping_list = json.load(self.Ping_Log)
def on_init(self):
"""Callback when strategy is inited."""
self.write_log("策略初始化")
self.load_bar(self.grid_ma_length)
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.bg.update_tick(tick)
#self.ask_price = tick.ask_price_1 #买卖价取tick对手价,盘中以最新行情判断交易,则将交易逻辑写在on_tick中
#self.bid_price = tick.bid_price_1
def on_bar(self, bar: BarData): #每分钟K走完调用一次
"""Callback of new bar data update."""

'''下单逻辑:空仓时开仓,有仓时平仓;止损点数=grid_step,止盈点数=2*grid_step;策略分析:价格偏离均值时有向均值回归的倾向,所以:最新价(对手价) - 均值 > grid_step,做空,反之做多若不等K线走完,用最新行情,则把逻辑写在on_tick函数中该策略分析较为简单,可以改成其他策略,但下单逻辑不能改'''

self.cancel_all()
am = self.am #转换为内部变量,增加查询速度
am.update_bar(bar) #m默认生成1分钟数据
if not am.inited:
return
grid_array = am.sma(self.grid_ma_length, array=True) #计算均线
self.grid_ma = grid_array[-1] #取均线最新值
self.ask_price = bar.close_price #买卖价取K线收盘价
self.bid_price = bar.close_price
#self.write_log('price'+str(self.ask_price))
#self.write_log('grid_ma'+str(self.grid_ma))
lots = self.shoushu(Lots=self.Lot) #计算开仓手数
if lots >= self.Lot_max : lots = self.Lot_max
#self.write_log('shoushu: '+str(lots))
#self.write_log('grid_step: '+str(self.grid_step))
#pos配置在本地文件cta_strategy_data.json中,只能通过策略更改,外部下单不会修改
if self.pos == 0 :
if self.grid_ma - self.ask_price >= self.grid_step:
#stop为True是本地停止单,这个停止单并没有发送给交易所,而是存储在内部,使用ctaEngine.sendStopOrder()函数; 否则这直接发送到交易所,使用ctaEngine.sendOrder函数
#对于StopOrder真正触发的交易通常是涨停价或者跌停价发出的市价单(Market price),参数price只是触发条件;而普通sendOrder是真正按照参数price的限价单(Limit price)
vt_orderid = self.buy(price=self.ask_price, volume=lots,stop=False,lock=False)
self.fangxiang = 'Buy'
elif self.bid_price - self.grid_ma >= self.grid_step:
vt_orderid = self.short(price=self.bid_price, volume=lots,stop=False,lock=False)
self.fangxiang = 'Sell'
self.kai = True
elif self.pos < 0 :
if self.ask_price - self.kai_list[-1]['price'] >= self.grid_step :
vt_orderid = self.cover(price=self.ask_price, volume=abs(self.pos),stop=False,lock=False)
elif self.kai_list[-1]['price'] - self.ask_price >= self.grid_step * 2 :
vt_orderid = self.cover(price=self.ask_price, volume=abs(self.pos),stop=False,lock=False)
self.kai = False
elif self.pos > 0 :
if self.bid_price - self.kai_list[-1]['price'] >= self.grid_step * 2 :
vt_orderid = self.sell(price=self.bid_price, volume=self.pos,stop=False,lock=False)
elif self.kai_list[-1]['price'] - self.bid_price >= self.grid_step :
vt_orderid = self.sell(price=self.bid_price, volume=self.pos,stop=False,lock=False)
self.kai = False
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.write_log(trade)
#shoshu = 0
jiage = 0.0
if self.kai:
tr_dic = {'symbol':trade.symbol,'volume':trade.volume,'price':trade.price,'datetime':str(trade.datetime)}
self.kai_list.append(tr_dic)
self.Kai_Log.seek(0) #把操作标记移到0
self.Kai_Log.truncate()
json.dump(self.kai_list, self.Kai_Log) #把当日开仓成交记录写入缓存
self.Kai_Log.flush() #实时写入本地文件
elif not self.kai :
if self.fangxiang == 'Buy' : jiage = trade.price - self.kai_list[-1]['price']
elif self.fangxiang == 'Sell' : jiage = self.kai_list[-1]['price'] - trade.price
tr_dic = {'symbol':trade.symbol,'volume':trade.volume,'price':trade.price,'lirun':jiage,'datetime':str(trade.datetime)}
self.ping_list.append(tr_dic)
self.Ping_Log.seek(0) #把操作标记移到0
self.Ping_Log.truncate()
json.dump(self.ping_list, self.Ping_Log) #把当日开仓成交记录写入缓存
self.Ping_Log.flush()
self.put_event()
def on_stop_order(self, stop_order: StopOrder):
"""Callback of stop order update."""
pass
def shoushu(self,Lots=1): #如果采用新策略、新品种,本地成交记录必须先删除,否则会影响初始交易手数
try:
if self.ping_list[-1]['lirun'] > 0: #前一单盈利,开仓手数默认
Lots = self.Lot
elif self.ping_list[-2]['lirun'] > 0: #前二单盈利,开仓手数默认
Lots = self.Lot
else :
Lots = self.ping_list[-1]['volume'] + self.ping_list[-2]['volume'] #前一二单亏损,开仓手数为两单之和
except IndexError:
Lots = self.Lot #没有平仓记录或只有一个平仓记录且亏损,开仓手数默认
return Lots