class POSITION: def __init__(self, platform, instrument_id, time_frame): self.__platform = platform self.__instrument_id = instrument_id self.__time_frame = time_frame self.__market = MARKET(self.__platform, self.__instrument_id, self.__time_frame) def direction(self): """获取当前持仓方向""" result = self.__platform.get_position()['direction'] return result def amount(self): """获取当前持仓数量""" result = self.__platform.get_position()['amount'] return result def price(self): """获取当前的持仓价格""" result = self.__platform.get_position()['price'] return result def coverlong_profit(self): """计算平多的单笔交易利润""" self.__value = self.__market.contract_value() # 合约面值 result = (self.__market.last() - self.price()) * (self.amount() * self.__value) return result def covershort_profit(self): """计算平空的单笔交易利润""" self.__value = self.__market.contract_value() # 合约面值 result = (self.price() - self.__market.last()) * (self.amount() * self.__value) return result
class POSITION: def __init__(self, platform, instrument_id, time_frame): self.__platform = platform self.__instrument_id = instrument_id self.__time_frame = time_frame self.__market = MARKET(self.__platform, self.__instrument_id, self.__time_frame) def direction(self): """获取当前持仓方向""" if config.backtest is False: # 实盘模式下实时获取账户实际持仓方向,仅支持单向持仓模式下的查询 result = self.__platform.get_position()['direction'] return result else: # 回测模式下从数据库中读取持仓方向 result = storage.read_mysql_datas(0, "回测", self.__instrument_id.split("-")[0].lower() + "_" + self.__time_frame, "总资金", ">")[-1][6] return result def amount(self, mode=None, side=None): """获取当前持仓数量""" if config.backtest is False: # 实盘模式下实时获取账户实际持仓数量 if mode == "both": # 如果传入参数"both",查询双向持仓模式的持仓数量 result = self.__platform.get_position(mode=mode) if side == "long": long_amount = result["long"]["amount"] return long_amount elif side == "short": short_amount = result["short"]["amount"] return short_amount else: result = self.__platform.get_position()['amount'] return result else: # 回测模式下从数据库中读取持仓数量 result = storage.read_mysql_datas(0, "回测", self.__instrument_id.split("-")[0].lower() + "_" + self.__time_frame, "总资金", ">")[-1][7] return result def price(self, mode=None, side=None): """获取当前的持仓价格""" if config.backtest is False: # 实盘模式下实时获取账户实际持仓价格 if mode == "both": # 如果传入参数"both",查询双向持仓模式的持仓价格 result = self.__platform.get_position(mode=mode) if side == "long": long_price = result["long"]["price"] return long_price elif side == "short": short_price = result["short"]["price"] return short_price else: result = self.__platform.get_position()['price'] return result else: # 回测模式下从数据库中读取持仓价格 result = storage.read_mysql_datas(0, "回测", self.__instrument_id.split("-")[0].lower() + "_" + self.__time_frame, "总资金", ">")[-1][5] return result def coverlong_profit(self, market_type=None, last=None): """ 计算平多的单笔交易利润 :param market_type: 默认是USDT合约,可填"usd_contract"(币本位合约)或者"spot"(现货) :param last: 回测模式可以传入最新成交价 :return: 返回计算出的利润结果 """ if market_type == "usd_contract": # 如果是币本位合约 self.__value = self.__market.contract_value() # 合约面值 if config.backtest and last is not None: # 如果是回测模式且传入了last最新成交价 result = (last - self.price()) * ((self.amount() * self.__value) / self.price()) # 利润=价差*(合约张数*面值)/持仓价格 else: # 如果是实盘模式 result = (self.__market.last() - self.price()) * ((self.amount() * self.__value) / self.price()) # 利润=价差*(合约张数*面值)/持仓价格 elif market_type == "spot": # 如果是现货 if config.backtest and last is not None: # 如果是回测模式且传入了last最新成交价 result = (last - self.price()) * self.amount() # 利润=价差*持仓数量 else: # 如果是实盘模式 result = (self.__market.last() - self.price()) * self.amount() else: # 默认是usdt合约 self.__value = self.__market.contract_value() # 合约面值 if config.backtest and last is not None: # 如果是回测模式且传入了last最新成交价 result = (last - self.price()) * (self.amount() * self.__value) # 利润=价差*(持仓数量*面值) else: # 如果是实盘模式 result = (self.__market.last() - self.price()) * (self.amount() * self.__value) return result def covershort_profit(self, market_type=None, last=None): """ 计算平空的单笔交易利润 :param market_type: 默认是USDT合约,可填"usd_contract"(币本位合约)或者"spot"(现货) :param last: 回测模式可以传入最新成交价 :return: 返回计算出的利润结果 """ if market_type == "usd_contract": # 如果是币本位合约 self.__value = self.__market.contract_value() # 合约面值 if config.backtest and last is not None: # 如果是回测模式且传入了last最新成交价 result = (self.price() - last) * ((self.amount() * self.__value) / self.price()) # 利润=价差*(合约张数*面值)/持仓价格 else: # 如果是实盘模式 result = (self.price() - self.__market.last()) * ( (self.amount() * self.__value) / self.price()) # 利润=价差*(合约张数*面值)/持仓价格 elif market_type == "spot": # 如果是现货 if config.backtest and last is not None: # 如果是回测模式且传入了last最新成交价 result = (self.price() - last) * self.amount() # 利润=价差*持仓数量 else: # 如果是实盘模式 result = (self.price() - self.__market.last()) * self.amount() else: # 默认是usdt合约 self.__value = self.__market.contract_value() # 合约面值 if config.backtest and last is not None: # 如果是回测模式且传入了last最新成交价 result = (self.price() - last) * (self.amount() * self.__value) # 利润=价差*(持仓数量*面值) else: # 如果是实盘模式 result = (self.price() - self.__market.last()) * (self.amount() * self.__value) return result
class SIGNALIZE: """实盘时根据从交易所获取的k线数据绘制k线图、成交量图及指标""" def __init__(self, platform, symbol, time_frame): self.__platform = platform self.__symbol = symbol self.__time_frame = time_frame self.__market = MARKET(self.__platform, self.__symbol, self.__time_frame) # pull some data self.__indicators = INDICATORS(self.__platform, self.__symbol, self.__time_frame) self.__kline = platform.get_kline(self.__time_frame) self.__kline.reverse() # format it in pandas try: # dataframe有7列的情况 self.__df = pd.DataFrame(self.__kline, columns=[ 'time', 'open', 'high', 'low', 'close', 'volume', 'currency_volume' ]) self.__df = self.__df.astype({ 'time': 'datetime64[ns]', 'open': 'float64', 'close': 'float64', 'high': 'float64', 'low': 'float64', 'volume': 'float64', 'currency_volume': 'float64' }) except: # dataframe只有6列的情况,如okex的现货k线数据 self.__df = pd.DataFrame( self.__kline, columns=['time', 'open', 'high', 'low', 'close', 'volume']) self.__df = self.__df.astype({ 'time': 'datetime64[ns]', 'open': 'float64', 'close': 'float64', 'high': 'float64', 'low': 'float64', 'volume': 'float64' }) # create three plot 创建三层图纸,第一层画k线,第二层画成交量,第三层画一些适宜于副图显示的指标 fplt.foreground = '#FFFFFF' # 前景色 fplt.background = '#333333' # 背景色 fplt.odd_plot_background = '#333333' # 第二层图纸的背景色 fplt.cross_hair_color = "#FFFFFF" # 准星的颜色 self.__ax, self.__ax2, self.__ax3 = fplt.create_plot(symbol, rows=3) # plot candle sticks candles = self.__df[['time', 'open', 'close', 'high', 'low']] fplt.candlestick_ochl(candles, ax=self.__ax) # overlay volume on the plot volumes = self.__df[['time', 'open', 'close', 'volume']] fplt.volume_ocv(volumes, ax=self.__ax2) fplt.add_legend("VOLUME", self.__ax2) # 增加"VOLUME"图例 """ plot indicators """ def show(self): """最后必须调用此函数以显示图像""" fplt.show() def plot_last(self, color=None): """在图上画出最新成交价这根横线,便于观察""" last = self.__market.last() array = np.empty(len(self.__kline)) array.fill(last) color = color if color is not None else "#CD7F32" # 默认设置为红色 fplt.plot(self.__df['time'], array, color=color, ax=self.__ax, legend="LAST {}".format(last)) def plot_array(self, array, ax, legend, color=None): """ 绘制任意的数组成线性 :param array: 传入一个数组 :param ax: 加载在第几行的图上 :param legend: 图例名称 :param color: 颜色 :return: """ if ax == 1: ax = self.__ax elif ax == 2: ax = self.__ax2 elif ax == 3: ax = self.__ax3 color = color if color is not None else "#FF0000" # 默认设置为红色 fplt.plot(self.__df['time'], array, color=color, ax=ax, legend=legend) def plot_atr(self, length, color=None): """ 在图上画出ATR :param length: ATR指标参数 :param color: 线的颜色 :return: """ color = color if color is not None else "#FF0000" # 默认设置为红色 fplt.plot(self.__df['time'], self.__indicators.ATR(length), color=color, ax=self.__ax3, legend='ATR({})'.format(length)) def plot_boll(self, length, color1=None, color2=None, color3=None): """ 在图上画出布林通道的上轨、中轨、下轨 :param length: BOLL指标参数 :param upperband_color: 上轨颜色 :param middleband_color: 中轨颜色 :param lowerband_color: 下轨颜色 :return: """ color1 = color1 if color1 is not None else "#FF0000" # 默认设置为红色 color2 = color2 if color2 is not None else "#00FF00" # 默认设置为绿色 color3 = color3 if color3 is not None else "#0000FF" # 默认设置为蓝色 upperband_array = self.__indicators.BOLL(length)['upperband'] middleband_array = self.__indicators.BOLL(length)["middleband"] lowerband_array = self.__indicators.BOLL(length)["lowerband"] fplt.plot(self.__df['time'], upperband_array, color=color1, ax=self.__ax, legend='BOLL({})-UPPERBAND'.format(length)) fplt.plot(self.__df['time'], middleband_array, color=color2, ax=self.__ax, legend='BOLL({})-MIDDLEBAND'.format(length)) fplt.plot(self.__df['time'], lowerband_array, color=color3, ax=self.__ax, legend='BOLL({})-LOWERBAND'.format(length)) # 副图上也加载 fplt.plot(self.__df['time'], upperband_array, color=color1, ax=self.__ax3, legend='BOLL({})-UPPERBAND'.format(length)) fplt.plot(self.__df['time'], middleband_array, color=color2, ax=self.__ax3, legend='BOLL({})-MIDDLEBAND'.format(length)) fplt.plot(self.__df['time'], lowerband_array, color=color3, ax=self.__ax3, legend='BOLL({})-LOWERBAND'.format(length)) def plot_highest(self, length, color=None): """ 在图上画出最高价 :param length: HIGHEST指标参数 :param color: 线的颜色 :return: """ color = color if color is not None else "#FF0000" # 默认设置红黑色 fplt.plot(self.__df['time'], self.__indicators.HIGHEST(length), color=color, ax=self.__ax, legend='HIGHEST({})'.format(length)) # 副图也加载 fplt.plot(self.__df['time'], self.__indicators.HIGHEST(length), color=color, ax=self.__ax3, legend='HIGHEST({})'.format(length)) def plot_ma(self, length, color=None): """ 在图上画出移动平均线 :param length: 简单移动平均线参数 :param color: 线的颜色 :return: """ color = color if color is not None else "#FF0000" # 默认设置为红色 # 主图与副图加载指标 fplt.plot(self.__df['time'], self.__indicators.MA(length), color=color, ax=self.__ax, legend='MA({})'.format(length)) fplt.plot(self.__df['time'], self.__indicators.MA(length), color=color, ax=self.__ax3, legend='MA({})'.format(length)) def plot_macd(self, fastperiod, slowperiod, signalperiod, color1=None, color2=None, color3=None): """ 在图上画出MACD指标 :param fastperiod: :param slowperiod: :param signalperiod: :param color1: :param color2: :param color3: :return: """ color1 = color1 if color1 is not None else "#FF0000" # 默认设置为红色 color2 = color2 if color2 is not None else "#00FF00" # 默认设置为绿色 color3 = color3 if color3 is not None else "#0000FF" # 默认设置为蓝色 dif = self.__indicators.MACD(fastperiod, slowperiod, signalperiod)['DIF'] dea = self.__indicators.MACD(fastperiod, slowperiod, signalperiod)["DEA"] macd = self.__indicators.MACD(fastperiod, slowperiod, signalperiod)["MACD"] fplt.plot(self.__df['time'], dif, color=color1, ax=self.__ax3, legend='MACD({}, {}, {})-DIF'.format(fastperiod, slowperiod, signalperiod)) fplt.plot(self.__df['time'], dea, color=color2, ax=self.__ax3, legend='MACD({}, {}, {})-DEA'.format(fastperiod, slowperiod, signalperiod)) fplt.plot(self.__df['time'], macd, color=color3, ax=self.__ax3, legend='MACD({}, {}, {})-MACD'.format( fastperiod, slowperiod, signalperiod)) def plot_ema(self, length, color=None): """ 在图上画出EMA指标 :param length: :param color: :return: """ color = color if color is not None else "#FF0000" # 默认设置为红色 fplt.plot(self.__df['time'], self.__indicators.EMA(length), color=color, ax=self.__ax, legend='EMA({})'.format(length)) # 副图也加载 fplt.plot(self.__df['time'], self.__indicators.EMA(length), color=color, ax=self.__ax3, legend='EMA({})'.format(length)) def plot_kama(self, length, color=None): """在图上画出KAMA指标""" color = color if color is not None else "#FF0000" # 默认设置为红色 fplt.plot(self.__df['time'], self.__indicators.KAMA(length), color=color, ax=self.__ax, legend='KAMA({})'.format(length)) # 副图也加载 fplt.plot(self.__df['time'], self.__indicators.KAMA(length), color=color, ax=self.__ax3, legend='KAMA({})'.format(length)) def plot_kdj(self, fastk_period, slowk_period, slowd_period, color1=None, color2=None): """ 在图上画出KDJ指标 :param fastk_period: :param slowk_period: :param slowd_period: :param color1: :param color2: :param color3: :return: """ color1 = color1 if color1 is not None else "#FF0000" # 默认设置为红色 color2 = color2 if color2 is not None else "#00FF00" # 默认设置为绿色 k = self.__indicators.KDJ(fastk_period, slowk_period, slowd_period)['k'] d = self.__indicators.KDJ(fastk_period, slowk_period, slowd_period)["d"] # 仅副图加载 fplt.plot(self.__df['time'], k, color=color1, ax=self.__ax3, legend='KDJ({}, {}, {})-K'.format(fastk_period, slowk_period, slowd_period)) fplt.plot(self.__df['time'], d, color=color2, ax=self.__ax3, legend='KDJ({}, {}, {})-D'.format(fastk_period, slowk_period, slowd_period)) def plot_lowest(self, length, color=None): """LOWEST""" color = color if color is not None else "#FF0000" # 默认设置红黑色 fplt.plot(self.__df['time'], self.__indicators.LOWEST(length), color=color, ax=self.__ax, legend='LOWEST({})'.format(length)) # 副图也加载 fplt.plot(self.__df['time'], self.__indicators.LOWEST(length), color=color, ax=self.__ax3, legend='LOWEST({})'.format(length)) def plot_obv(self, color=None): """OBV""" color = color if color is not None else "#FF0000" # 默认设置红黑色 # 仅副图加载 fplt.plot(self.__df['time'], self.__indicators.OBV(), color=color, ax=self.__ax3, legend='OBV') def plot_rsi(self, length, color=None): """RSI""" color = color if color is not None else "#FF0000" # 默认设置为红色 # 仅副图加载 fplt.plot(self.__df['time'], self.__indicators.RSI(length), color=color, ax=self.__ax3, legend='RSI({})'.format(length)) def plot_roc(self, length, color=None): """ROC""" color = color if color is not None else "#FF0000" # 默认设置为红色 # 仅副图加载 fplt.plot(self.__df['time'], self.__indicators.ROC(length), color=color, ax=self.__ax3, legend='ROC({})'.format(length)) def plot_stochrsi(self, timeperiod, fastk_period, fastd_period, color1=None, color2=None): """STOCHRSI""" color1 = color1 if color1 is not None else "#FF0000" # 默认设置为红色 color2 = color2 if color2 is not None else "#00FF00" # 默认设置为绿色 stochrsi = self.__indicators.STOCHRSI(timeperiod, fastk_period, fastd_period)['stochrsi'] fastk = self.__indicators.STOCHRSI(timeperiod, fastk_period, fastd_period)["fastk"] # 仅副图加载 fplt.plot(self.__df['time'], stochrsi, color=color1, ax=self.__ax3, legend='STOCHRSI({}, {}, {})-STOCHRSI'.format( timeperiod, fastk_period, fastd_period)) fplt.plot(self.__df['time'], fastk, color=color2, ax=self.__ax3, legend='STOCHRSI({}, {}, {})-FASTK'.format( timeperiod, fastk_period, fastd_period)) def plot_sar(self, color=None): """ 在图上画出SAR :param length: SAR指标参数 :param color: 线的颜色 :return: """ color = color if color is not None else "#FF0000" # 默认设置为红色 # 主副图均加载 fplt.plot(self.__df['time'], self.__indicators.SAR(), color=color, ax=self.__ax, legend='SAR') fplt.plot(self.__df['time'], self.__indicators.SAR(), color=color, ax=self.__ax3, legend='SAR') def plot_stddev(self, length, color=None): """STDDEV""" color = color if color is not None else "#FF0000" # 默认设置为红色 # 仅副图加载 fplt.plot(self.__df['time'], self.__indicators.STDDEV(length), color=color, ax=self.__ax3, legend='STDDEV({})'.format(length)) def plot_trix(self, length, color=None): """STDDEV""" color = color if color is not None else "#FF0000" # 默认设置为红色 # 仅副图加载 fplt.plot(self.__df['time'], self.__indicators.TRIX(length), color=color, ax=self.__ax3, legend='TRIX({})'.format(length)) def plot_volume(self, color=None): """VOLUME""" color = color if color is not None else "#FF0000" # 默认设置为红色 # 仅副图均加载 fplt.plot(self.__df['time'], self.__indicators.VOLUME(), color=color, ax=self.__ax3, legend='VOLUME')
class SYNCHRONIZE: """持仓同步""" def __init__(self, databank, database, data_sheet, exchange, instrument_id, time_frame): print("{} {} 持仓同步功能已启动!".format(get_localtime(), instrument_id)) self.__databank = databank self.__database = database self.__datasheet = data_sheet self.__exchange = exchange self.__instrument_id = instrument_id self.__time_frame = time_frame self.__position = POSITION(self.__exchange, self.__instrument_id, self.__time_frame) self.__market = MARKET(self.__exchange, self.__instrument_id, self.__time_frame) self.__overprice_range = config.overprice_range def save_strategy_position(self, strategy_direction, strategy_amount): """下单后将仓位信息保存至数据库.""" if self.__databank == "mysql": storage.mysql_save_strategy_position(self.__database, self.__datasheet, strategy_direction, strategy_amount) elif self.__databank == "mongodb": storage.mongodb_save(data={"时间": get_localtime(), "strategy_direction": strategy_direction, "strategy_amount": strategy_amount}, database=self.__database, collection=self.__datasheet) else: raise DataBankError def match(self): # 获取当前账户持仓信息 account_direction = self.__position.direction() account_amount = self.__position.amount() # 获取当前策略应持仓位信息 if self.__databank == "mysql": strategy_direction = storage.read_mysql_datas(0, self.__database, self.__datasheet, "amount", ">=")[-1][-2] strategy_amount = storage.read_mysql_datas(0, self.__database, self.__datasheet, "amount", ">=")[-1][-1] elif self.__databank == "mongodb": strategy_direction = storage.mongodb_read_data(self.__database, self.__datasheet)[-1][0]["strategy_direction"] strategy_amount = int(storage.mongodb_read_data(self.__database, self.__datasheet)[-1][0]["strategy_amount"]) else: strategy_direction = None strategy_amount = None raise DataBankError # 比较账户持仓与策略持仓,如不匹配则同步之 if strategy_direction == "long" and account_direction == "long": if account_amount < strategy_amount: receipt = self.__exchange.buy(self.__market.last() * (1 + self.__overprice_range), strategy_amount - account_amount, 0) return "当前持多,当前实际持仓小于策略应持仓位数量,自动同步结果:{}".format(receipt) elif account_amount > strategy_amount: receipt = self.__exchange.sell(self.__market.last() * (1 - self.__overprice_range), account_amount - strategy_amount, 0) return "当前持多,当前实际持仓大于策略应持仓位数量,自动同步结果:{}".format(receipt) if strategy_direction == "short" and account_direction == "short": # 策略与账户均持空时 if account_amount < strategy_amount: receipt = self.__exchange.sellshort(self.__market.last() * (1 - self.__overprice_range), strategy_amount - account_amount, 0) return "当前持空,当前实际持仓小于策略应持仓位数量,自动同步结果:{}".format(receipt) elif account_amount > strategy_amount: receipt = self.__exchange.buytocover(self.__market.last() * (1 + self.__overprice_range), account_amount - strategy_amount, 0) return "当前持空,当前实际持仓大于策略应持仓位数量,自动同步结果:{}".format(receipt) if strategy_direction == "long" and account_direction == "short": # 策略持多,账户却持空时 receipt1 = self.__exchange.buytocover(self.__market.last() * (1 + self.__overprice_range), account_amount, 0) if "完全成交" not in receipt1: return "策略应持多,当前实际持空,自动同步结果:{}".format(receipt1) else: receipt2 = self.__exchange.buy(self.__market.last() * (1 + self.__overprice_range), strategy_amount, 0) return "策略应持多,当前实际持空,自动同步结果:{}".format(receipt1 + receipt2) if strategy_direction == "short" and account_direction == "long": # 策略持空,账户却持多时 receipt1 = self.__exchange.sell(self.__market.last() * (1 - self.__overprice_range), account_amount, 0) if "完全成交" not in receipt1: return "策略应持空,当前实际持多,自动同步结果:{}".format(receipt1) else: receipt2 = self.__exchange.sellshort(self.__market.last() * (1 - self.__overprice_range), strategy_amount, 0) return "策略应持空,当前实际持多,自动同步结果:{}".format(receipt1 + receipt2) if strategy_direction == "none" and account_direction == "long": # 策略无持仓,账户却持多时 receipt = self.__exchange.sell(self.__market.last() * (1 - self.__overprice_range), account_amount, 0) return "策略应无持仓,当前实际持多,自动同步结果:{}".format(receipt) if strategy_direction == "none" and account_direction == "short": # 策略无持仓,账户却持空时 receipt = self.__exchange.buytocover(self.__market.last() * (1 + self.__overprice_range), account_amount, 0) return "策略应无持仓,当前实际持空,自动同步结果:{}".format(receipt) if account_direction == "none" and strategy_direction == "long": # 账户无持仓,策略却应持多时 receipt = self.__exchange.buy(self.__market.last() * (1 + self.__overprice_range), strategy_amount, 0) return "策略应持多仓,当前实际无持仓,自动同步结果:{}".format(receipt) if account_direction == "none" and strategy_direction == "short": # 账户无持仓,策略却应持空 receipt = self.__exchange.sellshort(self.__market.last() * (1 - self.__overprice_range), strategy_amount, 0) return "策略应持空仓,当前实际无持仓,自动同步结果:{}".format(receipt) if account_amount == strategy_amount and account_direction == strategy_direction: dict = {"策略持仓方向": strategy_direction, "策略持仓数量": strategy_amount, "账户实际持仓方向": account_direction, "账户实际持仓数量": account_amount} return "策略持仓与账户持仓匹配! {}".format(dict) else: raise MatchError