detail.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. {% extends 'base.html' %}
  2. {% block title %}交易记录详情 - 期货数据管理系统{% endblock %}
  3. {% block content %}
  4. <div class="container-fluid">
  5. <div class="row mb-3">
  6. <div class="col">
  7. <h2>交易记录详情 - ID: {{ transaction.id }}</h2>
  8. </div>
  9. <div class="col-auto">
  10. <a href="{{ url_for('transaction.index') }}" class="btn btn-secondary">
  11. <i class="fas fa-arrow-left"></i> 返回列表
  12. </a>
  13. <a href="{{ url_for('transaction.edit', id=transaction.id) }}" class="btn btn-warning">
  14. <i class="fas fa-edit"></i> 编辑
  15. </a>
  16. </div>
  17. </div>
  18. <div class="card">
  19. <div class="card-header">
  20. <h5 class="card-title mb-0">交易信息</h5>
  21. </div>
  22. <div class="card-body">
  23. <table class="table table-bordered table-striped table-sm">
  24. <tbody>
  25. <tr>
  26. <th style="width: 200px">ID</th>
  27. <td>{{ transaction.id }}</td>
  28. </tr>
  29. <tr>
  30. <th>交易 ID (trade_id)</th>
  31. <td>{{ transaction.trade_id or 'N/A' }}</td>
  32. </tr>
  33. <tr>
  34. <th>成交时间</th>
  35. <td>{{ transaction.transaction_time }}</td>
  36. </tr>
  37. <tr>
  38. <th>合约代码</th>
  39. <td>{{ transaction.contract_code or 'N/A' }}</td>
  40. </tr>
  41. <tr>
  42. <th>名称</th>
  43. <td>{{ transaction.name or 'N/A' }}</td>
  44. </tr>
  45. <tr>
  46. <th>账户</th>
  47. <td>{{ transaction.account or 'N/A' }}</td>
  48. </tr>
  49. <tr>
  50. <th>操作策略</th>
  51. <td>{{ transaction.strategy_name or 'N/A' }}</td>
  52. </tr>
  53. <tr>
  54. <th>多空仓位</th>
  55. <td id="detail-position_type">{{ transaction.position_type }}</td>
  56. </tr>
  57. <tr>
  58. <th>K线形态</th>
  59. <td>{{ transaction.candle_pattern or 'N/A' }}</td>
  60. </tr>
  61. <tr>
  62. <th>成交价格</th>
  63. <td id="detail-price">{{ transaction.price }}</td>
  64. </tr>
  65. <tr>
  66. <th>成交手数</th>
  67. <td id="detail-volume">{{ transaction.volume }}</td>
  68. </tr>
  69. <tr>
  70. <th>成交金额</th>
  71. <td id="detail-amount">{{ transaction.amount }}</td>
  72. </tr>
  73. <tr>
  74. <th>保证金</th>
  75. <td id="detail-margin">{{ transaction.margin }}</td>
  76. </tr>
  77. <tr>
  78. <th>交易类别</th>
  79. <td id="detail-trade_type">{{ transaction.trade_type }}</td>
  80. </tr>
  81. <tr>
  82. <th>交易状态</th>
  83. <td id="detail-trade_status">{{ transaction.trade_status }}</td>
  84. </tr>
  85. <tr>
  86. <th>最新价格</th>
  87. <td id="detail-latest_price">{{ transaction.latest_price }}</td>
  88. </tr>
  89. <tr>
  90. <th>实际收益率</th>
  91. <td id="detail-actual_profit_rate" class="profit-loss">{{ transaction.actual_profit_rate }}</td>
  92. </tr>
  93. <tr>
  94. <th>实际收益</th>
  95. <td id="detail-actual_profit" class="profit-loss">{{ transaction.actual_profit }}</td>
  96. </tr>
  97. <tr>
  98. <th>止损价格</th>
  99. <td id="detail-stop_loss_price">{{ transaction.stop_loss_price }}</td>
  100. </tr>
  101. <tr>
  102. <th>止损比例</th>
  103. <td id="detail-stop_loss_rate" class="profit-loss">{{ transaction.stop_loss_rate }}</td>
  104. </tr>
  105. <tr>
  106. <th>止损收益</th>
  107. <td id="detail-stop_loss_profit" class="profit-loss">{{ transaction.stop_loss_profit }}</td>
  108. </tr>
  109. <tr>
  110. <th>操作时间</th>
  111. <td>{{ transaction.operation_time }}</td>
  112. </tr>
  113. <tr>
  114. <th>信心指数</th>
  115. <td id="detail-confidence_index">{{ transaction.confidence_index }}</td>
  116. </tr>
  117. <tr>
  118. <th>相似度评估</th>
  119. <td id="detail-similarity_evaluation">{{ transaction.similarity_evaluation }}</td>
  120. </tr>
  121. <tr>
  122. <th>长期趋势名称</th>
  123. <td>{{ transaction.long_trend_name or 'N/A' }}</td>
  124. </tr>
  125. <tr>
  126. <th>中期趋势名称</th>
  127. <td>{{ transaction.mid_trend_name or 'N/A' }}</td>
  128. </tr>
  129. <!-- 移除 BRD 未提及的字段 -->
  130. <!--
  131. <tr>
  132. <th>是否平今</th>
  133. <td>{{ '是' if transaction.is_close_today else '否' }}</td>
  134. </tr>
  135. <tr>
  136. <th>关联开仓记录ID</th>
  137. <td>{{ transaction.related_open_id or 'N/A' }}</td>
  138. </tr>
  139. <tr>
  140. <th>备注</th>
  141. <td>{{ transaction.notes or '无' }}</td>
  142. </tr>
  143. <tr>
  144. <th>创建时间</th>
  145. <td>{{ transaction.created_at.strftime('%Y-%m-%d %H:%M:%S') if transaction.created_at else 'N/A' }}</td>
  146. </tr>
  147. <tr>
  148. <th>更新时间</th>
  149. <td>{{ transaction.updated_at.strftime('%Y-%m-%d %H:%M:%S') if transaction.updated_at else 'N/A' }}</td>
  150. </tr>
  151. -->
  152. </tbody>
  153. </table>
  154. </div>
  155. </div>
  156. </div>
  157. {% endblock %}
  158. {% block scripts %}
  159. <script>
  160. // 复用 index 页面的格式化函数
  161. function formatNumber(num, precision = 3) {
  162. if (num === null || num === undefined || isNaN(num)) {
  163. return 'N/A'; // 详情页用 N/A
  164. }
  165. let fixedNum = Number(parseFloat(num).toFixed(precision));
  166. return fixedNum.toString();
  167. }
  168. function formatPercentage(num, precision = 2) {
  169. if (num === null || num === undefined || isNaN(num)) {
  170. return 'N/A';
  171. }
  172. let percentage = parseFloat(num) * 100;
  173. return formatNumber(percentage, precision) + '%';
  174. }
  175. function getPositionTypeText(type) {
  176. const types = { 0: '开多', 1: '平多', 2: '开空', 3: '平空' };
  177. return types[type] !== undefined ? types[type] : '未知';
  178. }
  179. function getTradeTypeText(type) {
  180. return type === 1 ? '真实交易' : (type === 0 ? '模拟交易' : '未知');
  181. }
  182. function getTradeStatusText(status) {
  183. const statuses = { 0: '进行中', 1: '已暂停', 2: '暂停进行', 3: '已结束' };
  184. return statuses[status] !== undefined ? statuses[status] : '未知';
  185. }
  186. document.addEventListener('DOMContentLoaded', function() {
  187. // 将 applyProfitLossClass 移到内部,确保在 DOM 加载后可用
  188. function applyProfitLossClass(elementId) {
  189. const element = document.getElementById(elementId);
  190. if (!element) return;
  191. const rawValue = element.textContent.replace('%', ''); // 去掉百分号(如果存在)
  192. if (rawValue === 'N/A' || isNaN(parseFloat(rawValue))) {
  193. element.classList.remove('text-danger', 'text-success');
  194. return;
  195. }
  196. const num = parseFloat(rawValue);
  197. element.classList.remove('text-danger', 'text-success'); // 先移除旧样式
  198. if (num > 0) element.classList.add('text-danger');
  199. else if (num < 0) element.classList.add('text-success');
  200. }
  201. // 获取原始数据 - 不再需要,将从 DOM 读取
  202. // const transactionData = {{ transaction | tojson | safe }};
  203. // 格式化显示 - 直接读取并更新 DOM 元素内容
  204. const positionTypeElement = document.getElementById('detail-position_type');
  205. if (positionTypeElement) positionTypeElement.textContent = getPositionTypeText(parseFloat(positionTypeElement.textContent));
  206. const priceElement = document.getElementById('detail-price');
  207. if (priceElement) priceElement.textContent = formatNumber(parseFloat(priceElement.textContent), 3);
  208. const volumeElement = document.getElementById('detail-volume');
  209. if (volumeElement) volumeElement.textContent = formatNumber(parseFloat(volumeElement.textContent), 0);
  210. const amountElement = document.getElementById('detail-amount');
  211. if (amountElement) amountElement.textContent = formatNumber(parseFloat(amountElement.textContent), 2);
  212. const marginElement = document.getElementById('detail-margin');
  213. if (marginElement) marginElement.textContent = formatNumber(parseFloat(marginElement.textContent), 2);
  214. const tradeTypeElement = document.getElementById('detail-trade_type');
  215. if (tradeTypeElement) tradeTypeElement.textContent = getTradeTypeText(parseFloat(tradeTypeElement.textContent));
  216. const tradeStatusElement = document.getElementById('detail-trade_status');
  217. if (tradeStatusElement) tradeStatusElement.textContent = getTradeStatusText(parseFloat(tradeStatusElement.textContent));
  218. const latestPriceElement = document.getElementById('detail-latest_price');
  219. if (latestPriceElement) latestPriceElement.textContent = formatNumber(parseFloat(latestPriceElement.textContent), 3);
  220. const actualProfitRateElement = document.getElementById('detail-actual_profit_rate');
  221. if (actualProfitRateElement) actualProfitRateElement.textContent = formatPercentage(parseFloat(actualProfitRateElement.textContent), 2);
  222. const actualProfitElement = document.getElementById('detail-actual_profit');
  223. if (actualProfitElement) actualProfitElement.textContent = formatNumber(parseFloat(actualProfitElement.textContent), 2);
  224. const stopLossPriceElement = document.getElementById('detail-stop_loss_price');
  225. if (stopLossPriceElement) stopLossPriceElement.textContent = formatNumber(parseFloat(stopLossPriceElement.textContent), 3);
  226. const stopLossRateElement = document.getElementById('detail-stop_loss_rate');
  227. if (stopLossRateElement) stopLossRateElement.textContent = formatPercentage(parseFloat(stopLossRateElement.textContent), 2);
  228. const stopLossProfitElement = document.getElementById('detail-stop_loss_profit');
  229. if (stopLossProfitElement) stopLossProfitElement.textContent = formatNumber(parseFloat(stopLossProfitElement.textContent), 2);
  230. const confidenceIndexElement = document.getElementById('detail-confidence_index');
  231. if (confidenceIndexElement) confidenceIndexElement.textContent = formatNumber(parseFloat(confidenceIndexElement.textContent), 0);
  232. const similarityEvaluationElement = document.getElementById('detail-similarity_evaluation');
  233. if (similarityEvaluationElement) similarityEvaluationElement.textContent = formatPercentage(parseFloat(similarityEvaluationElement.textContent), 1);
  234. // 应用盈亏颜色
  235. applyProfitLossClass('detail-actual_profit_rate');
  236. applyProfitLossClass('detail-actual_profit');
  237. applyProfitLossClass('detail-stop_loss_rate');
  238. applyProfitLossClass('detail-stop_loss_profit');
  239. });
  240. </script>
  241. {% endblock %}