利用儲存的成交訂單,生成實盤交易分析報表
利用之前文章寫的儲存成交order,借鑑vnpy已有的回測分析報表,生成一個實盤交易分析報表。
截圖如下。
這裡有個統計按鈕,點選輸出統計介面
因為我的程式碼改動不少,直接拿來用估計不合適,只做參考。
首先要自行修改database的程式碼,新增按照strategy名稱,和時間段讀取儲存到資料的order,按照時許正向返回。
讀取出來是一個order的佇列,下面程式碼可以放在cta_engine.py, 是把開倉價和開倉時間作為屬性附加到平倉單,輸出平倉單佇列。對於沒有成對的,或者跨期的品種配對要拋棄;這裡在做移倉操作時候,要針對策略,生成虛擬的平倉和開新倉的訂單,具體實現程式碼後面再移倉程式碼中說。
def convert_strategy_triggered_order(self,strategy_name,open_date = datetime(2001,10,10), close_date = datetime(2100,10,10)): result = database_manager.load_triggered_stop_order_data(strategy_name,open_date,close_date) if result: matchedOrderList = [] last_order = None for order in result: if order.offset == Offset.OPEN: last_order = copy(order) elif last_order != None and order.offset == Offset.CLOSE: if last_order.vt_symbol != order.vt_symbol: last_order = None continue order.open_datetime= last_order.datetime order.open_price = last_order.average_price matchedOrderList.append(copy(order)) last_order = None return matchedOrderList
然後下面程式碼是處理的平倉單佇列進行分析,計算出各種指標,輸出為DataFrame格式,這裡包括收益金額,收益率,單位收益率,回撤,賬面價值等。
def get_strategy_triggered_order(self,strategy_name): # result = database_manager.load_close_triggered_stop_order_data(strategy_name) result = self.convert_strategy_triggered_order(strategy_name) objectDF = DataFrame(data=None,columns=["開倉日期","平倉日期", "開倉方向", "開倉價", "手數", "平倉價", "收益"],dtype=object) if result: parameters = self.get_strategy_parameters(strategy_name) # 策略中定義的合約價值 HeYueJiaZhi = parameters["HeYueJiaZhi"] # 策略中定義的品種每筆數量 HeYueChengShu = parameters["HeYueChengShu"] for close_data in result: if close_data.direction == Direction.LONG: close_data.direction = Direction.SHORT else: close_data.direction = Direction.LONG objectDF.loc[len(objectDF) + 1] = [close_data.vt_orderids[0].replace(tzinfo=None),close_data.datetime.replace(tzinfo=None), close_data.direction, close_data.open_price,close_data.volume, close_data.average_price,0.0] objectDF["收益"] = objectDF.apply(lambda x: x['開倉價'] - x['平倉價'] if x['開倉方向'] == Direction.SHORT else x['平倉價'] - x['開倉價'], axis=1) objectDF["UnitReturn"] = objectDF["收益"] * 100 / objectDF['開倉價'] objectDF["收益"] = objectDF["收益"]*HeYueChengShu*objectDF["手數"] objectDF["balance"] = objectDF["收益"].cumsum() + HeYueJiaZhi objectDF["return"] = objectDF["收益"]*100/HeYueJiaZhi objectDF.loc[0] = copy(objectDF.iloc[0]) objectDF = objectDF.sort_index() objectDF.loc[0,"balance"] = HeYueJiaZhi objectDF["highlevel"] = ( objectDF["balance"].rolling( min_periods=1, window=len(objectDF), center=False).max() ) objectDF.drop(index=0, inplace=True) objectDF["drawdown"] = objectDF["balance"] - objectDF["highlevel"] objectDF["ddpercent"] = objectDF["drawdown"] / objectDF["highlevel"] * 100 return objectDF
下面程式碼是分析dataframe,計算指標資料
def calculate_statistics(self, objectDF): """ """ data = {} # end_balance = df["balance"].iloc[-1] # max_drawdown = df["drawdown"].min() # max_ddpercent = df["ddpercent"].min() HeYueJiaZhi = self._data["parameters"]["HeYueJiaZhi"] data["capital"] = HeYueJiaZhi data["total_net_pnl"] = objectDF["收益"].sum() data["end_balance"] = objectDF["balance"].iloc[-1] data["total_return"] = data["total_net_pnl"]*100/max(HeYueJiaZhi,1) data["max_drawdown"] = objectDF["drawdown"].min() data["max_ddpercent"] = objectDF["ddpercent"].min() data["total_trade_count"] = len(objectDF) data["winningResult"] = len(objectDF[objectDF["收益"] >0]) data["losingResult"] = len(objectDF[objectDF["收益"] <0]) data["winningRate"] = data["winningResult"] *100/ data["total_trade_count"] data["totalWinning"] = objectDF[objectDF["收益"] >0]["收益"].sum() data["totalLosing"] = objectDF[objectDF["收益"] <0]["收益"].sum() data["averageWinning"] = data["totalWinning"]/max(1,data["winningResult"]) data["averageLosing"] = data["totalLosing"]/max(1,data["losingResult"]) data["perprofitLoss"] = data["total_net_pnl"] / data["total_trade_count"] data["profitLossRatio"] = data["averageWinning"] / max(1,abs(data["averageLosing"])) return data
然後在cta_widget.py 中,加入顯示程式碼,這裡直接使用回撤模組的BacktesterChart
def analyze_strategy(self): objectDF = self.cta_engine.get_strategy_triggered_order(self.strategy_name) if not objectDF.empty: triggerd_statistics_monitor = Triggered_OrderStatisticsMonitor() triggerd_statistics_monitor.set_data(self.calculate_statistics(objectDF)) triggerd_statistics_monitor.setMinimumHeight(400) triggerd_view = TriggeredMonitor(self.cta_manager.main_engine, self.cta_manager.main_engine.event_engine) triggerd_view.set_df(objectDF) triggerd_view.setMinimumHeight(400) triggerd_view.setMinimumWidth(600) objectDF["net_pnl"] = objectDF["收益"] objectDF= objectDF.set_index("平倉日期") chart = BacktesterChart() chart.set_data(objectDF) analyz_dialog = QDialog() analyz_dialog.setWindowTitle(self.strategy_name) analyz_dialog.setWindowModality(Qt.NonModal) analyz_dialog.setWindowFlags(Qt.Dialog | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint) gbox = QtWidgets.QGridLayout() analyz_dialog.resize(1200, 800) gbox.addWidget(triggerd_statistics_monitor,0,0) gbox.addWidget(triggerd_view,1,0) gbox.addWidget(chart,0,1,2,1) analyz_dialog.setLayout(gbox) analyz_dialog.exec_() class PercentCell(BaseCell): def __init__(self, content: Any, data: Any): super(PercentCell, self).__init__(content, data) def set_content(self, content: Any, data: Any) -> None: self.setText(f"{content:,.2f}%") self._data = data class fullDatetimeCell(BaseCell): def __init__(self, content: Any, data: Any): super(fullDatetimeCell, self).__init__(content, data) def set_content(self, content: Any, data: Any) -> None: if content is None: return timestamp = content.strftime("%Y%m%d %H:%M:%S") self.setText(timestamp) self._data = data class TriggeredMonitor(BaseMonitor): event_type = "" data_key = "" sorting = False # ["平倉日期", "方向", "開倉價", "手數", "平倉價", "收益"] headers = { "開倉日期": {"display": "開倉日期", "cell": fullDatetimeCell, "update": False}, "平倉日期": {"display": "平倉日期", "cell": fullDatetimeCell, "update": False}, "開倉方向": {"display": "開倉方向", "cell": DirectionCell, "update": False}, "開倉價": {"display": "開倉價", "cell": BaseCell, "update": False}, "手數": {"display": "手數", "cell": BaseCell, "update": False}, "平倉價": {"display": "平倉價", "cell": BaseCell, "update": False}, "收益": {"display": "收益", "cell": PnlCell, "update": False}, "return": {"display": "收益率", "cell": PercentCell, "update": False}, "UnitReturn": {"display": "單位收益率", "cell": PercentCell, "update": False}, "balance": {"display": "當前資金", "cell": BaseCell, "update": False}, "drawdown": {"display": "回撤", "cell": BaseCell, "update": False} } def set_df(self,objectDF): objectDF_list = objectDF.to_dict(orient='records') for record_item in objectDF_list: self.insert_data(record_item) def insert_data(self, data): self.insertRow(0) for column, header in enumerate(self.headers.keys()): setting = self.headers[header] content = data[header] cell = setting["cell"](content, data) self.setItem(0, column, cell) def __del__(self) -> None: pass class Triggered_OrderStatisticsMonitor(QtWidgets.QTableWidget): KEY_NAME_MAP = { "capital": "策略定義資金", "end_balance": "歷史結算資金", "total_net_pnl": "總盈虧", "total_return": "總收益率", "total_trade_count": "總成交筆數", "winningResult": "盈利次數", "losingResult" : "虧損次數", "winningRate": "筆數勝率", "max_drawdown": "最大回撤", "max_ddpercent": "最大回撤比率", "totalWinning": "總盈利金額", "totalLosing": "總虧損金額", "perprofitLoss": "平均單筆損益", "averageWinning": "盈利平均每筆", "averageLosing" : "虧損平均每筆", "profitLossRatio" : "盈虧比", } def __init__(self): super().__init__() self.cells = {} self.init_ui() def init_ui(self): self.setRowCount(len(self.KEY_NAME_MAP)) self.setVerticalHeaderLabels(list(self.KEY_NAME_MAP.values())) self.setColumnCount(1) self.horizontalHeader().setVisible(False) self.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Stretch ) self.setEditTriggers(self.NoEditTriggers) for row, key in enumerate(self.KEY_NAME_MAP.keys()): cell = QtWidgets.QTableWidgetItem() self.setItem(row, 0, cell) self.cells[key] = cell def clear_data(self): for cell in self.cells.values(): cell.setText("") def set_data(self, data: dict): data["capital"] = f"{data['capital']:,.2f}" data["end_balance"] = f"{data['end_balance']:,.2f}" data["total_net_pnl"] = f"{data['total_net_pnl']:,.2f}" data["total_return"] = f"{data['total_return']:,.2f}%" data["total_trade_count"] = f"{data['total_trade_count']}" data["winningResult"] = f"{data['winningResult']}" data["losingResult"] = f"{data['losingResult']}" data["winningRate"] = f"{data['winningRate']:,.2f}%" data["max_drawdown"] = f"{data['max_drawdown']:,.2f}" data["max_ddpercent"] = f"{data['max_ddpercent']:,.2f}%" data["totalWinning"] = f"{data['totalWinning']:,.2f}" data["totalLosing"] = f"{data['totalLosing']:,.2f}" data["averageWinning"] = f"{data['averageWinning']:,.2f}" data["averageLosing"] = f"{data['averageLosing']:,.2f}" data["perprofitLoss"] = f"{data['perprofitLoss']:,.2f}" data["profitLossRatio"] = f"{data['profitLossRatio']:,.2f}" for key, cell in self.cells.items(): value = data.get(key, "") cell.setText(str(value))
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2919132/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 交易日均千萬訂單的儲存架構設計與實踐架構
- 如何避免SAP訂單儲存後生成的中介軟體CSA inbound queue
- 實現報表資料分庫儲存
- 利用Kubernetes實現容器的持久化儲存持久化
- 使用LocalStorage實現Form表單內容本地儲存ORM
- 小程式-生成海報儲存本地相簿
- 動態表單儲存設計
- 儲存論——經濟訂貨批次的R實現
- 利用VNPY回測引擎分析實盤交易,並用excel和pdf輸出分析結果Excel
- 報表資料分庫儲存
- [20200330]sar報表儲存時間.txt
- 將echarts生成的圖表變為圖片儲存起來Echarts
- 揭秘 | 成立17年的淘寶,萬億級海量交易訂單都儲存在哪?
- 關於訂單庫存扣減的最佳實踐
- Druid:實時分析資料儲存UI
- 利用Node實現HTML5離線儲存HTML
- 如何利用MySQL有效的儲存IP地址MySql
- EMC儲存Raid故障資料分析報告AI
- 自定義hybris生成訂單的ID格式
- Go實現簡單的K-V儲存Go
- 小程式開發-利用canvas實現儲存二維碼海報到本機Canvas
- PostgreSQL:表的儲存屬性SQL
- 利用canvas生成海報Canvas
- [20200402]sar報表儲存時間2.txt
- 利用jQuery實現表單驗證功能jQuery
- 如何實現企業雲盤資料化儲存
- VNPY實盤交易中,出現發單成功但是沒有交易情況
- Java自動生成訂單序列號Java
- PHP 做 RSA 簽名 生成訂單PHP
- Sql儲存過程分頁--臨時表儲存SQL儲存過程
- 一文告訴你Excel融合分析如何利用Smartbi實現儀表盤效果Excel
- SAP CRM銷售訂單UI上的欄位對應的資料庫表儲存欄位:requested start date和end dateUI資料庫
- ORACLE PL/SQL 物件、表資料對比功能儲存過程簡單實現OracleSQL物件儲存過程
- 聊聊mysql的單列多值儲存MySql
- 【提升團隊運營效率】交易履約之訂單中心實踐
- 指標儲存地址分析指標
- etcd-raft-儲存分析Raft
- 線性表的順序儲存C++程式碼實現C++