最近在尝试均值回归策略,使用无限易,感觉挺好用的。策略是面向苯乙烯,每次交易只有一手,和大家分享一下量化代码,策略还有很多不足,欢迎交流指正。
代码分解主要包括tick属性与on_order属性、其余部分大同小异,没有什么特别的地方。
在开始正式分解代码之前,介绍一下代码中会使用到的超参数与状态参数。
class Params(BaseParams): """参数映射模型""" exchange: str = Field(default="DCE", title="交易所代码") instrument_id: str = Field(default="eb2408", title="合约代码") order_volume: int = Field(default=1, title="下手单量") stop: int | float = Field(default=20, title="止损") holdstate: str = Field(default="wait", title="初始化当前状态")
class State(BaseState): """状态映射模型""" order_id: int | None = Field(default=None, title="报单编号") close: int | float = Field(default=0 , title="最近价") volume: int | float = Field(default=0, title="成交量") holdstate: str = Field(default="wait", title="当前状态") target_price: int | float = Field(default=465 , title="当前状态价") high_price: int | float = Field(default=0, title="当前最高价") low_price: int | float = Field(default=0, title="当前最低价") status: str = Field(default="状态正常", title="订单交易状态") ar10000:int | float = Field(default=0, title="ar10000") var10000: int | float = Field(default=0, title="var10000")
首先是on_order,当我们进行一次开平仓后会调用这个函数,但下单可能会面临延迟问题,或者报单失败问题。所以我再这里设置了success与finish两个状态变量,如果报单处理流程结束则finish赋值为1(方便判断是否收到了报单结果,因为这个过程会有延迟。)如果下单成功则success转换为1(若报单不成功则根据情况重新下单或保持等待)。
def on_order(self, order: OrderData) -> None: super().on_order(order) self.output("报单信息:", order) if order.traded_volume>0: self.success=1 if order.status=='全部成交': self.finish=1
接下来是主体on_tick部分def on_tick(self, tick: TickData) -> None: """收到行情 tick 推送""" super().on_tick(tick) # 过滤涨跌停和集合竞价 # self.output(tick.last_price,tick.last_volume,tick.datetime) if tick.last_price == 0 or tick.ask_price1 == 0 or tick.bid_price1 == 0 : return # self.output(self.state_map.holdstate) self.update_status_bar() self.close.append(tick.last_price) self.close = self.close[1:] self.ar10000 = self.close[-10000:] self.state_map.ar10000 = np.mean(self.ar10000) self.state_map.var10000 = np.std(self.ar10000)
on_tick回调一开始会过滤集合竞价与涨跌停,但实操下来发现,这套官方的方法并不能很好的过滤集合竞价。在on_tick的开始阶段不断收级最近的成交价,保存到self.close中,并且不断计算过去10000此成交价的均值与标准差。为完成回归策略做准备。
self.state_map.high_price=self.state_map.ar10000+40#3*self.state_map.var10000 self.state_map.low_price = self.state_map.ar10000-40
上面定义了压力位和支撑位,这里定义的是高于或低于均线40个点即进入压力位或支撑位,当然也可以像注释中一样使用标准差进行定义。
self.signal1 = tick.last_price <= self.state_map.low_price self.signal1_1 = tick.last_price > self.state_map.low_price+2 and tick.ask_price1 - tick.bid_price1 < 4 self.signal2 = tick.last_price >= self.state_map.high_price self.signal2_1 = tick.last_price < self.state_map.high_price-2 and tick.ask_price1 - tick.bid_price1 < 4
上为两阶段交易信号,以做多为例,当价格低于low_price,则满足第一阶段,当价格再次高于low_price则完成一次做多信号。在信号中使用价差进行了滑点处理,减少止损止盈误差。
if self.state_map.holdstate=='station': # self.output(self.finish,self.success) if self.finish: if self.success: self.state_map.holdstate = self.next_state self.success=0 else: self.state_map.holdstate = self.current_state self.finish=0 else: self.state_map.holdstate='station'
策略使用状态来描述当前状况,station状态是一个下单后的“中转站”,如果下单后的结果未返回则保持在station等待(self.finish来实现),如果下单未成功则返回下单前的一个状态,反之则进入下单后的状态(self.success来实现)。下面以做多为例分解代码,做空与做多完全对称。
elif self.state_map.holdstate == 'wait': if self.signal1: self.state_map.holdstate = 'poswait' elif self.signal2: self.state_map.holdstate = 'negwait'
等待状态,判断当前的成交价是否满足第一阶段。若此时满足signal1则进入poswait状态。
elif self.state_map.holdstate=='poswait': if self.signal1_1: self.state_map.order_id = self.send_order( exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.ask_price1, order_direction="buy" ) self.state_map.target_price = tick.ask_price1 self.current_state='poswait' self.next_state='poshold' self.state_map.holdstate = 'station'
进入poswait状态后若价格再次回到支撑位以上,则开多单,并进入station状态判断是否下单成功,若成功则进入poshold状态。
elif self.state_map.holdstate == 'poshold': if tick.last_price>=self.state_map.ar10000: self.state_map.order_id = self.auto_close_position(exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.bid_price1, order_direction="sell" ) self.current_state = 'poshold' self.next_state = 'wait' self.state_map.holdstate = 'station' elif tick.last_price<=self.state_map.target_price-self.params_map.stop: self.state_map.order_id = self.auto_close_position(exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.bid_price1, order_direction="sell") self.current_state = 'poshold' self.next_state = 'wait+' self.state_map.holdstate = 'station'
在多单持有状态中,若成交价成功回到均线,则进行止盈,并且状态回到’wait‘。此外,若成交价不仅没有回到均线,反而跌破了止损价,则进入一个“wait+”状态,当前的突破有可能发生了大的行情趋势,“wait+”状态的功能在于当价格回归到一个相对稳定的区间后才再次进行交易,否则进入一个等待状态。
elif self.state_map.holdstate == 'wait+': if tick.last_price>self.state_map.low_price: self.state_map.holdstate='wait'
最后,附一下完整代码from typing import Literal import pandas as pd import numpy as np import csv from pythongo.base import BaseParams, BaseState, BaseStrategy, Field from pythongo.classdef import OrderData,TickData # from pythongo.classdef import KLineData, TickData class Params(BaseParams): """参数映射模型""" exchange: str = Field(default="DCE", title="交易所代码") instrument_id: str = Field(default="eb2408", title="合约代码") order_volume: int = Field(default=1, title="下手单量") stop: int | float = Field(default=20, title="止损价格") holdstate: str = Field(default="wait", title="初始化当前状态") target_price: int | float = Field(default=9698, title="初始化当前状态价") class State(BaseState): """状态映射模型""" order_id: int | None = Field(default=None, title="报单编号") close: int | float = Field(default=0 , title="最近价") volume: int | float = Field(default=0, title="成交量") holdstate: str = Field(default="wait", title="当前状态") target_price: int | float = Field(default=465 , title="当前状态价") high_price: int | float = Field(default=0, title="当前最高价") low_price: int | float = Field(default=0, title="当前最低价") status: str = Field(default="状态正常", title="订单交易状态") ar10000:int | float = Field(default=0, title="ar10000") var10000: int | float = Field(default=0, title="var10000") stop: int | float = Field(default=20, title="止损") class VARIANCE(BaseStrategy): def __init__(self) -> None: super().__init__() self.params_map = Params() self.state_map = State() df = pd.read_csv('C:/Users/lantian/Desktop/tick数据/eb2408.csv').iloc[-10100:, :] self.close = list(df['last_price']) self.ar10000 = self.close[-10000:] self.success = 0 self.finish=0 # self.open=1 # self.askcut = np.mean(self.askar)-np.mean(self.bidar) def on_order(self, order: OrderData) -> None: super().on_order(order) self.output("报单信息:", order) if order.traded_volume>0: self.success=1 if order.status=='全部成交': self.finish=1 def on_start(self) -> None: super().on_start() self.state_map.holdstate = self.params_map.holdstate self.state_map.target_price = self.close[-1] self.state_map.high_price = np.mean(self.ar10000)+3*np.std(self.ar10000) self.state_map.low_price = np.mean(self.ar10000)-3*np.std(self.ar10000) self.update_status_bar() self.state_map.ar10000 = np.mean(self.ar10000) self.state_map.var10000 = np.std(self.ar10000) def on_tick(self, tick: TickData) -> None: """收到行情 tick 推送""" super().on_tick(tick) # 过滤涨跌停和集合竞价 # self.output(tick.last_price,tick.last_volume,tick.datetime) if tick.last_price == 0 or tick.ask_price1 == 0 or tick.bid_price1 == 0 : return # self.output(self.state_map.holdstate) self.update_status_bar() self.close.append(tick.last_price) self.close = self.close[1:] self.ar10000 = self.close[-10000:] self.state_map.ar10000 = np.mean(self.ar10000) self.state_map.var10000 = np.std(self.ar10000) self.state_map.high_price=self.state_map.ar10000+40#3*self.state_map.var10000 self.state_map.low_price = self.state_map.ar10000-40#3*self.state_map.var10000 self.state_map.close = tick.last_price self.signal1 = tick.last_price <= self.state_map.low_price self.signal1_1 = tick.last_price > self.state_map.low_price+2 and tick.ask_price1 - tick.bid_price1 < 4 self.signal2 = tick.last_price >= self.state_map.high_price self.signal2_1 = tick.last_price < self.state_map.high_price-2 and tick.ask_price1 - tick.bid_price1 < 4 if self.state_map.holdstate=='station': # self.output(self.finish,self.success) if self.finish: if self.success: self.state_map.holdstate = self.next_state self.success=0 else: self.state_map.holdstate = self.current_state self.finish=0 else: self.state_map.holdstate='station' elif self.state_map.holdstate == 'wait': if self.signal1: self.state_map.holdstate = 'poswait' elif self.signal2: self.state_map.holdstate = 'negwait' #初始做多 elif self.state_map.holdstate=='poswait': if self.signal1_1: self.state_map.order_id = self.send_order( exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.ask_price1, order_direction="buy" ) self.state_map.target_price = tick.ask_price1 self.current_state='poswait' self.next_state='poshold' self.state_map.holdstate = 'station' # self.success=0 elif self.state_map.holdstate == 'negwait': if self.signal2_1 : self.state_map.order_id = self.send_order( exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.bid_price1, order_direction="sell" ) self.state_map.target_price = tick.bid_price1 self.current_state = 'negwait' self.next_state = 'neghold' self.state_map.holdstate = 'station' elif self.state_map.holdstate == 'poshold': if tick.last_price>=self.state_map.ar10000: self.state_map.order_id = self.auto_close_position(exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.bid_price1, order_direction="sell" ) self.current_state = 'poshold' self.next_state = 'wait' self.state_map.holdstate = 'station' elif tick.last_price<=self.state_map.target_price-self.params_map.stop: self.state_map.order_id = self.auto_close_position(exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.bid_price1, order_direction="sell" ) self.current_state = 'poshold' self.next_state = 'wait+' self.state_map.holdstate = 'station' elif self.state_map.holdstate == 'neghold': if tick.last_price<=self.state_map.ar10000: self.state_map.order_id = self.auto_close_position(exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.ask_price1, order_direction="buy" ) self.current_state = 'neghold' self.next_state = 'wait' self.state_map.holdstate = 'station' elif tick.last_price >= self.state_map.target_price + self.params_map.stop: self.state_map.order_id = self.auto_close_position(exchange=self.params_map.exchange, instrument_id=self.params_map.instrument_id, volume=self.params_map.order_volume, price=tick.ask_price1, order_direction="buy" ) self.current_state = 'neghold' self.next_state = 'wait-' self.state_map.holdstate = 'station' elif self.state_map.holdstate == 'wait+': if tick.last_price>self.state_map.low_price: self.state_map.holdstate='wait' elif self.state_map.holdstate == 'wait-': if tick.last_price<self.state_map.high_price: self.state_map.holdstate='wait' def on_stop(self) -> None: super().on_stop() self.output("我的第一个策略暂停了")