增加交易策略、交易指标、量化库代码等文件夹
No files matched your search
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJWSVAwOeW3peS9nOWMul/lpJrnqboiLCJkZXNjIjoiIiwiZGF0YSI6InZJK21zcG40TGtVRW56MVJTaGErYUkrbERKVytiRktlT1NQaEQyNS9HR216ZGdYK3lySTBuS09RMW9rem1WNjBGNkQ1Qll6UjZEMk4wUFZaQjFqQXQyMU1XN0FNcE9mc0M1eFcrWXRaV1dBeEwra0tzUW9vUkFRRW95RTRNVlcrOGlHZzZ2dDgrdnFLZ0ppck4vRE9pd2VjOEdIdWZMMEtQM0Vqa2ZoTzI3MEpBVTJJNHplYTVWdjRNeEViTDZhWUxRcmFYRXFreDQzWVVxYWx1UmxSbUZqdERyb05CcVB1K0FlR0lyclordnJoTGN0WEV0dHRETmFCWkFzdThXVjdCQW8yUFZKZlhqa1ZxdVlObk1yd2dVcmFNeWVLZ21aMHFMcGplMFc4UGR6MnhZR1g2bUFDS1Rrb1IyNzVwalNjYmRsYS93QlZZVzh3YWJ1UzFZQThlbjJpWXFvdERSVC9kTHhaQjlGRlhVSWZ3L0J4Q2o2d0s0UXpqdi9FMVpqcFFTMWx3ODFNWG9pYXZLbTZ1ejJ1aHZNdWhlZmRXVjRUQ2JCWWtsVERLSW1WNmdpcnFhUHRSYWFYWStRU3d0UVJKc3htQTVacXZYOTNuMWxyRlBRV0JKVVZSc0YvVHJyenQ2STR3SVlHRDBjekRhblNyZWo5RmgyTUkxSTgyOXZNclpJUmdTQk5mNmsyWjJOTEJSZmlpRFVHMHBhTXRsemE0RVNrazVXdjAxeUF3THYzMWNTWGY1U3dhTmZzQ09BYkZTRUxTZVZFOUIwZTlCZHloMG9RUERsOGtqMmtSNENMR3prdmpyUTFUbFBsOXhwdUdEZlpKNlcwMDBjZWhZck1SY2ZGTXd0WC9EcUZ0V05sY3R3dW83bzBqdDhWdE93L1ZKNmxTaC93S1orbUJzOVFac1QvMG0xNXlxN0cwa2gyU1BFZ1BVWWY2bDBhWmhXNCtFUHVqR3ZOdFdEV3U5NUVoTXVBRHpOMnNoWm0xellwWHY4am8rVXVGSmZXSGFhVjA4KzY2dTFmbHZ5bGk1SEFad3VRblR2RWJ5TEVxOEViamlCUGVQTVovL0dlWGdMS3lVcHNkSDQ2L1pOSEUwN01KaXFQeTVEcWZkM2tKMENDYWRYcDB6YmxVTjBUa0M2WVowSFl6WDducTRkcGpLRjYvdlNrc3VHYmRHQWRJOTladGZQbG5xWUZpMEtFZ1RudHhiUDRYWVZQbXBXbE11TXpQbzhrenFwRmM1cGF6bC9DRXJTbnhSU0pCV0dtMmU2b2pEOVdpK0IwNzhRQzZqVUFDM1ltQ0o0VDNFMXZ4cGhGc25kNWpOMld3K0RYY2gyTnJIQ0xrcUdzY0kwcFpOLzdCOXd3Y041RllmVE5aWW5haUNqVGVydzdicDNCR2MrQ3VITGVrcjhzQjVTT29FV2Q5VC8xc0YyOVA0dU1LRzZwNUl1L2hVR3lJbTBrY1MxNlVrKzFMeklQUkdSUXF1bVFPeVo3ZS81M0xxY0x2WUF3Q0M5Qk9PT3B5Mmk4d0RDSTRoZmRrNFlkbjNkaFRpeXdFbWZ6cFBXSGo5dmY0M3RmajVydWFpQ3lSUEg2aHpVb2JuanBFZ0hQM3JKQWhmSUV0QnpUaHpRVHNyemNReTVDQWdrV3Y3OWRXbjVUOGh0SUt4MkdwRE5XYXhHUDdOV1JkeFo1RWNoOEw1VmZreU5OUzF2cjh6d1JhN2tNNllvWXV1ck9DRFJmbW9rQTNVb1MzanJUWW5hUEVpMXdFRjZONDZNcHVNakhKUzZVS0ZGVzA2UWxpb001azQ2OG1PRXlHOTZNN0JySVNNdk9zREM2Q0xucHYycWt1QXVDOHZyU1UvaGplUUtwajN1NzRxUy9DbVd3Ni82enBTK2pGVFd1aXRUOHgzYjlkZ2MzNU9QRERweGRGQmhkRWpsbnJsZjVYdHJ0UEJTdWJtYjN2QlRBRHhnY2Y4QlBjd2Q4aFd1b29jeG9oVkZ1cng4VTZXM08yOU1nbUNQclVWZnRzT3ZVSitwZjNKNUxnMEdJVVo4ckJxQUpISXVYODZ3RnpSeGtMOVRqaHZuSmFINWlZNzhzRWl5Z3F2eGhjUkRaMkxJblhvUGh3S3ZVblpPdXJ3RHZFRGZMZHdoOHJIUEcrV0RXVWR4enYwRjFoQ1ZRNmt5ZU8xNStRVjd1Wk9zWW9kOWlORExaUlRiNFhMVUlmOFcwYisxNlQyUGdiVmxicUo4UG9ZZ2VSTEpKenNBUlFRNDBiRjBaUTgrWXVBcnZLRDRHYkhHdEduODNKSDNFVVhmRlFqUGJzaTF0ZCtjejR6UFkycWd3S1k0RHk0Vzg5c1I3Y1dSUFJQQWd0UzM0Z1dZWFgrYlZEMHA4R256M0NNVXRLVmlBeUd0NjhwOHB3NVlqdHNJd2xnRGh1QXcxa3BEVEMzTjFndE9vVjhENHRUUVdhMHBZWEt6RzRnL2NYSDZBNVR5S1YxZEVaYkdJQW5ERUs0Y0N3ZFBURjlwMjBzMitJMkhDeFNKVy9pcmJndWxvaDJITFhVdCthTGNZeTJGeWlCbnA0UndIVVJicVJWSHFKQWlnM01NNC8yMmhpVWNZQ05pUHpPVWplVHNYTDI4dnhwSHZzbGgzcnNGNThZRjB1dUZtUTBrT3l5VjVuaVlWVTR5am9Ndlc3MGRHd2tiWTlrS3BNRHAwRVg1QmlHNC9ESVNwZmxSUFpIb3QvRksxQzNKbjhodUhaZWRyWUg2cVZHL0FWNkZPaGpZQjZTOVNzcUJidVl0SzZoUGl3ckh0UlZCamh5U3Rkb1JacDhDRlFkUUszMTE4UEZpN0dWK21IRWFLQU15U2xkb2p0NUZTSVZJdERRN1ArY1ZmeG5GV0RpQzVrd3BYNFpTNy9aN2dtUzB3OHdvY0c0Rnk0Yko4b1pjNjdJNExhQXlRdUxUSXk3QUN3NTFyY2JmMUVhdExpTWFnekNkQVpVTTVGaS9oTmplR3pVVVY0ZjgyY28wc0s0d0diVFFjcFdTaXJKdnRvWWdzNVBoYmp2Uy93eXBJaS9EZWphL3lHVnlVQ1hEc3g4Tk1FeklyL3BVVFZSSVJ4UThTcUppRWREYkovdEc5cnFEUE55Z0k5Y2RMc01ZY0NXOUFWWDJrWFlHN0tTeU1hMFZzUWo2NHVpWVlrWXJBeXFXRU50Z2J0dksrYVVwMkxxemRtUmdsWmswRTREVlZKdTliWWlBOGo3alhIemk4OVNVRm41eUZDU2lJZ0hFNE9OeUE4aGdIN1JObURsNkpFb2FKMzVBai8rVkJHUFdEZWNWNWYvckJ1TU13ekdJLzB3OEhlK2R1V29kanptYjRCc0hnaHVlMTJkaVg5WFdCK2VoWlNZUzdqMlgzMEJJRVg3NXZwVGVzbmF0QUhXVzR1aWI5emEvck04b0dHWnlBZCtEWGFVYXFpbVdpU1krc1JFZG01SFRqSkszZm1PY1FUcVNkY3U1aGxtT1FackRDK2hXcVI4Yk95OWdRM1ZqL25ONVdxU0lhdlFsUWYveVZjRThkb3hWNUg5NlVTNmxRdjdkUVRZMVllcmhGbG5GWjErOWYvcTBnV3NJckVzNUNhYmFQYmtWTHgrYVFSdnF6Sm94d01OMzNPanFrNUJIc0RZMitJWWtTMkRBUUxraXJZVXB5em5mT2VEdjF3V1hqKzJjbGl5ZjdZSHQ1aWUvclBXYUhvNmdzQWdTWitFMWpCS0ZodXNjMisxMWo2M1VYNUk5ZzJmUExuL3FOa1NmbUtzNE1rWkZiRzNGNHdmMG13ZU9TRUl2ZWplZmZxQVZOeUE4MC9DR0ZVVDJzOW1lTnZHem40RTVqaGhwY1p0VjJ3UUpBQ2NsbU91UHBBSFdjWmhGWEI0dVRmdEFLRXlXWG15eUc5RktGUFJoWjJMa1hVV1kxMVRKN2Z4d2thK21OSHFKS2Z5dTJUYUFUMXlOMjY3dFNLbVkva1VXeDN2VEVOZk9aRHNmQjJyaHEydXVrakQwK2FzRWZRS0RSQWhFVWFlSTdPRUFSZGZUa1UyN01aQ2FxeHJNdUkwRm1nd01WT0FkZmhFUVEyeTB6NURyRG9Cb2FGUXN5dzRCSjkzbjN4cTk4eG5VM0ZqeXl6djVIc0RiR1RVR1hMQjhacDIyQWFCVjNvTmcyT3ZpNlBydmlCY2NRN0NKQUwzTmdUZ0pvWEZ5Qi9tWDVVME5GWkZsclloM0s1VzlHTHArckdPZlowcTVndzhXZ3l4YzUwQU84cmR6UWFPYXBpazJyaDcrWWc4Um9uYnI0bDh4MDVYTjRiR0dVQ3ZFZ2ZRYWNEQkZXR2FKTlpVQzRrTGlUNU9wZlhUbW1jTzRURjNqbUpHVDhJSHkxLzl3ZnNkd0R3bjdSTzArNXZPVEFTTWRQcU4xRDdzaHBzOGhVTmJOWVBXVDQ2TFBDalhob3dQRE5INFh1eCtQcWtzYTFYaFVQbVhlSUtHd21pUFh3QVJMQkxJVzk2QW9DOWdid291VnI0UjFFV0FEcHZRS3ZCeGM0SGExcEsrOVNXaDgzVnNDYWQwVUNuK2R1ajExakZoaDFPTlVwZDRBU0R6M1pJcFBGSDQzc2x3dGFHNWlzRG83TUJjY282aC9ibjdiZW91WmdTazJtY3hUVXZnZEVuOGhQUlhsdW1yb2NvbFA5UTVEdTd3UDBBd0ZUWTlCczVRY1lJZll4YVNpVHNCTGd5aVBUNzZBVDRFNVF2R2I1U0ZCSU4zTFlPd1JQWDdzdkJhcTVHSnIxMTBtOWN4RWJxVkNBTUIzTDhZeEZrRnFhSVZvZHFPLytGRUw3Smd6ZFVhWWZtVCs4RFdOcWVEa1EvZVV3MFJ3UUcyN1JKbkE4OXdKdnVLine truncated
|
||||
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ModuleNotFoundError",
|
||||
"evalue": "No module named 'vnpy_ctastrategy.strategies.vip09'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[2], line 5\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mvnpy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mtrader\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01moptimize\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m OptimizationSetting\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mvnpy_ctastrategy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mbacktesting\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m BacktestingEngine\n\u001b[1;32m----> 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mvnpy_ctastrategy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mstrategies\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mvip09\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m vip09\n\u001b[0;32m 6\u001b[0m \u001b[38;5;66;03m# from vip09 import vip09\u001b[39;00m\n",
|
||||
"\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'vnpy_ctastrategy.strategies.vip09'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from vnpy.trader.optimize import OptimizationSetting\n",
|
||||
"from vnpy_ctastrategy.backtesting import BacktestingEngine\n",
|
||||
"# from vnpy_ctastrategy.strategies.vip09 import vip09\n",
|
||||
"from vip09 import vip09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"engine = BacktestingEngine()\n",
|
||||
"engine.set_parameters(\n",
|
||||
" vt_symbol=\"rb00.SHFE\",\n",
|
||||
" interval=\"1m\",\n",
|
||||
" start=datetime(2020, 1, 1),\n",
|
||||
" end=datetime(2024, 3, 21),\n",
|
||||
" rate=1.5/10000,\n",
|
||||
" slippage=1,\n",
|
||||
" size=10,\n",
|
||||
" pricetick=1,\n",
|
||||
" capital=1_000_00,\n",
|
||||
")\n",
|
||||
"engine.add_strategy(vip09, {})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"engine.load_data()\n",
|
||||
"engine.run_backtesting()\n",
|
||||
"df = engine.calculate_result()\n",
|
||||
"engine.calculate_statistics()\n",
|
||||
"engine.show_chart()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"'''用于参数优化'''\n",
|
||||
"# setting = OptimizationSetting()\n",
|
||||
"# setting.set_target(\"sharpe_ratio\")\n",
|
||||
"# setting.add_parameter(\"Length\", 5, 100, 5)\n",
|
||||
"# setting.add_parameter(\"N\", 5, 100, 5)\n",
|
||||
"# setting.add_parameter(\"X\", 1, 20, 1)\n",
|
||||
"# setting.add_parameter(\"TS\", 5, 100, 5)\n",
|
||||
"# from multiprocessing import cpu_count\n",
|
||||
"# # 获取 CPU 核心数量\n",
|
||||
"# num_cores = cpu_count()\n",
|
||||
"# print(f\"获取 CPU 核心数量:\",round(num_cores/2))\n",
|
||||
"# engine.run_ga_optimization(setting, max_workers=round(num_cores/2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#engine.run_bf_optimization(setting)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.9"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import pandas as pd
|
||||
|
||||
class vip09(TargetPosTemplate):
|
||||
""""""
|
||||
author = "松鼠Quant"
|
||||
|
||||
Length = 30
|
||||
N=5
|
||||
X=10
|
||||
TS = 45
|
||||
lots=1
|
||||
|
||||
current_bar = 0
|
||||
|
||||
Highup=0
|
||||
Lowdown=0
|
||||
|
||||
HigherAfterEntry = float('inf')
|
||||
LowerAfterEntry = -float('inf')
|
||||
liQKA = 0
|
||||
DliqPoint = 0
|
||||
KliqPoint = 0
|
||||
OBVValue=[0]
|
||||
MAOBV=[0]
|
||||
M2=[0]
|
||||
|
||||
cond1=[0]*2
|
||||
cond2=[0]*2
|
||||
cond3=[0]*2
|
||||
cond4=[0]*2
|
||||
cond5=[0]*2
|
||||
|
||||
kong_cond1=[0]*2
|
||||
kong_cond2=[0]*2
|
||||
kong_cond3=[0]*2
|
||||
kong_cond4=[0]*2
|
||||
kong_cond5=[0]*2
|
||||
|
||||
MA1=0
|
||||
MA2=0
|
||||
MA3=0
|
||||
|
||||
|
||||
|
||||
parameters = ["Length",'N','X',"TS",'lots']
|
||||
variables = ["HigherAfterEntry","LowerAfterEntry","liQKA","DliqPoint","KliqPoint"]
|
||||
|
||||
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
||||
""""""
|
||||
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
|
||||
|
||||
#df: pd.DataFrame = pd.read_csv(r"index_contract.csv")
|
||||
#self.contracts_sizes = {row.vt_symbol.split('.')[0]: row.contract_size for _, row in df.iterrows()}
|
||||
|
||||
self.bg = BarGenerator(self.on_bar)
|
||||
self.am = ArrayManager(200)
|
||||
self.pos=0
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
Callback when strategy is inited.
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(1)
|
||||
|
||||
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)
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
"""
|
||||
Callback of new bar data update.
|
||||
"""
|
||||
|
||||
am = self.am
|
||||
am.update_bar(bar)
|
||||
if not am.inited:
|
||||
return
|
||||
# 如果历史数据还没有初始化完毕,则直接返回
|
||||
# 注意:am 是交易系统中的一个数据管理器,可能用于管理历史数据
|
||||
# bar 是当前的K线数据,其中包含了开盘价、最高价、最低价、收盘价和成交量等信息
|
||||
|
||||
if self.current_bar == 0:
|
||||
self.OBVValue = [0]
|
||||
else:
|
||||
if am.close_array[-1] > am.close_array[-2]:
|
||||
self.OBVValue += [self.OBVValue[-1] + am.volume_array[-1]]
|
||||
elif am.close_array[-1] < am.close_array[-2]:
|
||||
self.OBVValue += [self.OBVValue[-1] - am.volume_array[-1]]
|
||||
|
||||
# 根据收盘价的涨跌更新 OBV 值
|
||||
# 如果当前收盘价大于昨日收盘价,则 OBV 值加上当前成交量
|
||||
# 如果当前收盘价小于昨日收盘价,则 OBV 值减去当前成交量
|
||||
# 注意:OBV 是一种基于成交量变化来反映买卖力量的技术指标,用于判断市场的买卖压力
|
||||
|
||||
self.current_bar=self.current_bar+1 #Bar线计数
|
||||
#print(bar.datetime,"current_bar:",self.current_bar)
|
||||
|
||||
# 记录当前Bar的计数器值,并打印当前Bar的时间和计数器值
|
||||
|
||||
if(self.current_bar<self.Length*3):
|
||||
return
|
||||
|
||||
# 如果当前Bar的计数器值小于三倍的Length,则直接返回
|
||||
# 这可能是因为需要一定数量的历史数据才能计算出所需的指标值
|
||||
|
||||
# print(f'{self.OBVValue},OBVValue')
|
||||
self.MA1=am.ema(self.Length,array=True)
|
||||
self.MA2=am.ema(self.Length*2,array=True)
|
||||
self.MA3=am.ema(self.Length*3,array=True)
|
||||
|
||||
# 计算三条指定长度的指数移动平均线(EMA)
|
||||
# 分别记为MA1、MA2和MA3
|
||||
|
||||
self.MAOBV += [(sum(self.OBVValue[-self.Length:]) / self.Length)]
|
||||
self.M2 += [(sum(self.OBVValue[-self.X:]) / self.X)]
|
||||
|
||||
# 计算 OBV 和 M2 的移动平均值
|
||||
|
||||
self.cond1=self.MA1 if am.close_array[-1] > self.MA1[-2] else self.cond1
|
||||
self.cond2=self.MA1 if self.M2[-1] > self.M2[-2] else self.cond2
|
||||
self.cond3=self.MA2 if am.close_array[-1] > self.MA2[-1] and self.MAOBV[-1] > self.MAOBV[-2] else self.cond3
|
||||
self.cond4=self.MA2 if am.close_array[-1] > self.MA2[-1] else self.cond4
|
||||
self.cond5=self.MA3 if am.close_array[-1] > self.MA3[-1] and self.MAOBV[-1] > self.MAOBV[-2] else self.cond5
|
||||
|
||||
# 根据条件更新多头条件指标cond1到cond5
|
||||
# 根据条件更新空头条件指标kong_cond1到kong_cond5
|
||||
|
||||
self.kong_cond1=self.MA1 if am.close_array[-1] < self.MA1[-2] else self.kong_cond1
|
||||
self.kong_cond2=self.MA1 if self.M2[-1] < self.M2[-2] else self.kong_cond2
|
||||
self.kong_cond3=self.MA2 if am.close_array[-1] < self.MA2[-1] and self.MAOBV[-1] < self.MAOBV[-2] else self.kong_cond3
|
||||
self.kong_cond4=self.MA2 if am.close_array[-1] < self.MA2[-1] else self.kong_cond4
|
||||
self.kong_cond5=self.MA3 if am.close_array[-1] < self.MA3[-1] and self.MAOBV[-1] < self.MAOBV[-2] else self.kong_cond5
|
||||
|
||||
# 更新空头条件指标kong_cond1到kong_cond5
|
||||
|
||||
self.Highup=max(am.high_array[-self.N:-1])
|
||||
self.Lowdown=min(am.low_array[-self.N:-1])
|
||||
|
||||
# 计算最近N个Bar的最高价和最低价,分别记为Highup和Lowdown
|
||||
|
||||
Long_line=am.close_array[-1]>self.MA3[-1] and self.MAOBV[-1]>self.MAOBV[-2]
|
||||
short_line=am.close_array[-1]<self.MA3[-1] and self.MAOBV[-1]<self.MAOBV[-2]
|
||||
|
||||
#print(f'{self.MAOBV[-1]},MAOBV')
|
||||
#print(f'{Long_line},Long_line')
|
||||
#print(f'{short_line},short_line')
|
||||
if Long_line:
|
||||
if self.cond4[-1]>self.cond5[-2] and self.cond5[-1]>self.cond5[-2] and self.cond4[-1]>self.cond4[-2] and self.cond3[-1]>self.cond3[-2] and self.cond2[-1]>self.cond2[-2] and self.cond1[-1]>self.cond1[-2] :
|
||||
if self.pos!=1 and bar.high_price>=self.Highup :
|
||||
if self.pos == 0:
|
||||
self.buy(bar.close_price, self.lots)
|
||||
elif self.pos < 0:
|
||||
self.cover(bar.close_price, self.lots)
|
||||
self.buy(bar.close_price, self.lots)
|
||||
self.LowerAfterEntry = bar.close_price
|
||||
|
||||
if short_line:
|
||||
if self.kong_cond4[-1]<self.kong_cond5[-1] and self.kong_cond5[-1]<self.kong_cond5[-2] and self.kong_cond4[-1]<self.kong_cond4[-2] and self.kong_cond3[-1]<self.kong_cond3[-2] and self.kong_cond2[-1]<self.kong_cond2[-2] and self.kong_cond1[-1]<self.kong_cond1[-2]:
|
||||
if self.pos!=-1 and bar.low_price<=self.Lowdown :
|
||||
if self.pos == 0:
|
||||
self.short(bar.close_price, self.lots)
|
||||
elif self.pos > 0:
|
||||
self.sell(bar.close_price, self.lots)
|
||||
self.short(bar.close_price, self.lots)
|
||||
|
||||
self.HigherAfterEntry = bar.close_price
|
||||
|
||||
|
||||
#记录入场后的最高价和最低价
|
||||
if self.pos>0:
|
||||
#self.HigherAfterEntry = HigherAfterEntry
|
||||
self.LowerAfterEntry = max(self.LowerAfterEntry, bar.low_price)
|
||||
elif self.pos<0:
|
||||
self.HigherAfterEntry = min(self.HigherAfterEntry, bar.high_price)
|
||||
#self.LowerAfterEntry = LowerAfterEntry
|
||||
|
||||
if self.pos == 0:
|
||||
self.liQKA = 1
|
||||
else:
|
||||
self.liQKA = self.liQKA - 0.1
|
||||
self.liQKA = max(self.liQKA, 0.5)
|
||||
|
||||
if self.pos>0:
|
||||
DliqPoint = self.LowerAfterEntry - ((bar.open_price)*self.TS/1000)*self.liQKA
|
||||
#print(bar.datetime,"DliqPoint",DliqPoint,"low_price",bar.low_price,"self.LowerAfterEntry",self.LowerAfterEntry)
|
||||
if self.pos<0:
|
||||
KliqPoint = self.HigherAfterEntry + ((bar.open_price)*self.TS/1000)*self.liQKA
|
||||
#print(bar.datetime,"KliqPoint",KliqPoint,"high_price",bar.high_price,"self.HigherAfterEntry",self.HigherAfterEntry)
|
||||
|
||||
if (self.pos>0 and bar.low_price <= DliqPoint and DliqPoint>0):
|
||||
#多头出场
|
||||
self.sell(bar.close_price, self.lots)
|
||||
elif (self.pos<0 and bar.high_price > KliqPoint and KliqPoint>0):
|
||||
#空头出场
|
||||
self.cover(bar.close_price, self.lots)
|
||||
|
||||
self.current_bar += 1
|
||||
self.put_event()
|
||||
|
||||
|
||||
def on_trade(self, trade: TradeData):
|
||||
"""
|
||||
Callback of new trade data update.
|
||||
"""
|
||||
self.put_event()
|
||||
|
||||
|
||||
def on_stop_order(self, stop_order: StopOrder):
|
||||
"""
|
||||
Callback of stop order update.
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJ2aXAxMCIsImRlc2MiOiIiLCJkYXRhIjoidkkrbXNwbjRMa1VFbnoxUlNoYSthSStsREpXK2JGS2VtM2hOYnJ5NDhpQy96dFhxNlBGOE1VYXo2djNsUXpQeU1hVVQ5MGkySFJ3aUdHWVY4cDduN1FWUGc1ZGdmeTgxbm4raEpUczFnMVE4a0xFUytMTUNCNkhFWnJGQkFkV1Vwanlid2QxWXZGb3puRXVzRU42bUxpMlk2REp3V25OcTFYYmlMd1loZThPK3l2UE9KRDd3OEtOUzVxMURpY01WZ1kxZVBTblhYSU9iZGViano1NUZ6NG0rZFlHbzhoZk9wUEgyQ0V0UldkWkhjRlo1RG5Ud1MrazJiSzYva2NJVS9ETkhNTHJ2YUp2OHY3KzE2QStXS0ZOeE1jYitDZnUvUHBPZnRTZG9NSm9SZFBlbElsUXZQWUpEajhlcnNlVDc3dmZVVkpudDQ1bUF3VlZsUVRRNjZjNE16eUo3dlVZT1JhaW1PODJRZ1B4bU1JYTdZSzl3Ukt1S016K0MvaVZ6NFBNalNZU0VWQmVOcWc5MnlEM2FsMDZhdFNGNUZhMlIySHZrV2lnYUZLcmhEZ1dmYlU4MnR5cEFCaGhXVjN5czZkSGQyN2lWa05HdWY1NENKMk1kLzdPRmNpcEhvV1pCNVR0US9kbzNnc096aFhJcVI2Rm1RUlpmVDN6cXo3VXhBVjRraG9XTGhQNTJiWGFSZXRrLzhHU1pwY2VCajNZbjc3cnZzNiszMGQ1Q01GczJ4VjhzMHJQcWVCWFJaMnZWTm15ZGpCeGN0R3h1UXVsKzNEQkpsRkFINkpUeW5pWTNqWTh6MmZZVk9IVHRGYmx0MlVqOHVhMTk4ZWlFRFE2TUxCZUE3NVZqWHFlRlQ3T01ZRGRSQnpUaitZOVR3L3pUNDJrUVgyOUQ4ZVI1VXo3UFZWc1M1MEtrQ2t4WHlCcG96SDVsTTdsOTRBWFl6UWRRTm1LS0FBd05Iamk5OXY4cmVQR1BTSjRmb1UvdGl2WjNxNTNZcmFsVkxRRWRSTWFCQWFLZ3B5bGNGamo3eVRKTG9GSk16MjVtUnVnVERacEhxdzA2aFdnQlhJN2VFeklPYjQ5ZlYxOWxuaUZ0eEdBeUs5NmhSdnRySndsa3BmZm9FT21XVXdBcGxuNmtlbW5VbGpzYjZ5MnBNSS8rSmhOdXQ5VmpnOWxQQWM0UnJkbklIaVBDNmFpUVpzYStIUC85ejRPMldUK3FjRW8vNGpRdXNhd21KaERaM2U4dDNNL1liaGN0Qk9PcnloR0hhTkJiWTAxNkx6NXBqU2xod1QwUVFSaGV1S2ZEZ3NuY3laQ203NXZIaDBrVkxwQzZaaC83ZU4xMlJFVTdmazFuZVdYbEtleG43SnJXVWNSbVFNTjNINGFoUDQ1Wkl2YXNKL0d3NnNyY2JPRlBuK1FXbU1DbE4wNXNndnZZNTZCZzJsV0JRZEpkYmFvZVdJaGxKWmtUdjIyRG9UaytEREJZVTZmUjA4RVUwSGpTYXB3aHYwMDJQcVljZWxnNDNDcVBFUWg5ai82Sks5SUdzNDZnVkFraTBaMFd4MjBHSURmMDRxVTFaTEZUamdNQ2FXeUdLV2dqZ2tVY3Fvam1vWEJBWWtLZENhZTZRZlZRWUZaTFE0OXM1QStnUFgzZThkVCtEZHl2Vm51QWU4MEVJb2hYU2I2VmxrNnhRS3dxV1ZFeUpnWDlSL3FIUEZDQjVrTGpJR3BWN0o5UzlEeXpyTzVlcG1PTXAzWGtrem1YbTdmK3RPa0xuMUNBanV6U3REWUVyTitQd0tOazBpNFJwQ2RQMTdVQUdvbC91WHlydW5RYldzWlZ5bzlsZnI3RFl0ckxJUVcwaTlzWEVSNDYvRG1TbGJEaHRVZ3ZwK3NpanN4amd2aTQrK3pJdFR3ZmxhdTRoREVLVHNhcDhhaUsyQ3drZUVTZUgxc3JMRWdFQ1RBVThHN002S3JQVUVmZGMyMlRMeU14Rm9RMzRhWFRHcUU1RU93N28xNmp2Rk9vOGYrUTkzVHdCa1FBREI1Q3lEd0dsOW4xa2NWWmMxZUkvZ2Q1d2x2TlRZZWlpWmI4LzhROFZIcXE0b1ZDVXpiWDdaWGF1NENPOERIS1BHeStETFNzYnZlVG90bWRwZFRWTk5SczZVazBPd3Q1Yi9sV2J4RXFmUGJKQStQam1ic2FDeWl4NGNuZ0J3NGJzOURyMmhVMDNyck1PRnR1K1JRUThoVEVTeERsSzN5UUhOUms0UmU4dlhJVkZqZHd0b2JPOUw4TFVYMXUxT2NJYWRlTFJVSkIyWnowWndpKzkyV1FwMmlOaGplQmRTWEFnbkR4SkV2eXliK2FmTFAwMldxMmdKTnlJbFRadTRXTFNYdlc0R1NsOXNQZC95dGdUdzJTdmVndlpuQ2xibjNSOEsxM21XM0I5U20wUCtLV1R1NXBsU1VNbFMzaDRzRjJRVDVSenkvTmo5UUZoNGpqcEhZajEwbHRrQ2dUQWpKT0RFb1l1VTQ5V2RuT2FSSnJIWDZLOHh3YzYrL1FEWWhaYm1MZzBoalpMOFBZQ1pBZnBsRWo4K0RkK0FmTFptdkdvS2wwUHNuQ1JRMUc4NTVSUHdMSnBESTl2QzlQZ3NQSmNoMGN2dzNyUWJZaFFsN0ppZ2doMjlnczMwR2xxL05nbzBRMDRwaHQ3Z2JNSGMwUWlrN2F2R0FZUTAvUkpNYWNDY2h3OTgrRVhiMzQ5RW5lMFhGUHZveU11M1dXY1VNNnFoeUtHekxuelI1MHdsQ2N0OWFVaGRKVmdGNkRHR1VRWGNDY2c3bi9lNDF5WE1SdkdhQzBZK3V1Uktzb2dOdHNRcnZMcFUzM0dTMDFxNitlbUpDL3NNM01ySnUxckpmZzJiU0h0dG5WTzJKZFQrYVVYNHRnVUFOdG1TRTFReFo0RTlhaTBnbUtNTjhqam5TMjBrYld2ZmFEbmY0WXkzNFRwUUhmbzdnakdsNzkvWW0zOGlMWHByWHRLVTJMZk9mczhyMGNlM1VPeGYram1OcENLMXBSK0VuRFA1R1laYnppSFozOStJcFRNRGlIeDcvekR1aGZqYVBNRVU4TENNcUtjSlBtSjRwazRUOUkwZUxiemk3WitLNlYwMlRJS3NpSlQwQnh3cE5JczJUQisyTHFEQktjbm13Vk84dEs2K1UvbW54bm00cTNOMVNJRjVKN25oSDRFSmcvZi9BOWZ5Mk5DVlFZTUoxK0lHSXR4Z2gwNitUYTI5S3R6VHo1UXVHMFVMdUxVNitZYXhiUHZxMGpuYXZZY09ndDRXRzNwZEFLRzZPdVo2TGROZTBmdGMwV2sraUxlRVZKckNNU25MRGs1a0s1OWJJQ28vbHdlK0RXS1lUcHJXU3pFdFdvckxONStnS0MzbVlub3pjQnJKdXBxU2JodGJycnVVY0xLRTdDOGM1d2JJYi8yT0JjaEN2c1lkZ0VpNmZrZFlnTk5yWE80UHlhSWhzRTl6R1g2YVFGNlRiUlM2MS96dkxGeGFZWEF1UjJRTnZaQmtENm12YTdBOXlqZ212eFVjbXA3QURUdVlOU2lYaW8xT3R6eU1CRnZPZUZLZ0tKalZtWkp1UHV1bWdnZjA2N1RBdlVsZENVYkJtNlV6Y0JucmFGUUlHekN2SUd1eGNqdStCbnFNOEtIeXhSSzJZeGxQNGtDclA0clNPN2JweVFxTXZhKzBuV09HU1RDM2VWY1dXc3B0KzhBaUF1b2lDRjZseEVvU0did0NzVFF3RmJ6ajEwNXRRS1RBMyt6WkZhMGwxYlBMU25kM0NyYU1ta3R3ajY5aVFuNkZQMmFPUXh3akRZNW1lRWc5OE5XMGVXWkR5WDUxelorZit0Y3ZBTTlhc1c4eWowYUE0RFVDbWJPQ2t5YkhiSGx4ZU5aZUY2MHZyWFg0blhaUGsySGhwWWFRRG94bzlsWDJPZ1pHTDE3bDNVNXZWRHR0NVV1SXFQZGJxbWZrakRMNWRXRFlJTlliRFJBM0VPUWl0NFZiOWRnc1dicE1DOXR4bWZ3bk50TUtjMHdLd1YzUitmUmlWYkN0MXZYdFYrcndoNFQ4VVNHY0tmaGxuZWxqNDBmSWhNVisrcDRxR01hM3FZdlZPQnR3K1hyV1lKVkEwU1FqTXg1WVkyZThJNWNSR0RNYXJSQy9HaEVSQk81UUIvQWR6RFArakg3MlBvK3c4UVhVQ3hoSk9TdmZlOUVYY1Y5WVFPU3IvT3dhQ0xpcFExb2lBQ0s3b3JkQ0hNbHlZd0t1MUtNcFpaOFFROE1ITEM4dWZ6SlQ3NWVPa3RoUWp2Y2IyUEx3d3RPSGJnMHFzWDE5MGlWdmZ3bzVZK3B2OGFCWVR4YVUvSitJc2xxTXUrNk5XTW11WmdpYnVNUHZ2aXpodGhLTlc4MkJBTWo1QW5pOUtMbGd5VUg2bEFmLzN1Z0dFUm1DUFBDSWVXVmdCZW5sSXpjOWZQZ1Arc2ltdXhLSktuMWs4YVNJZnFhV0hxZWZyVWtPME5GbUlKT21TTXhtZXBYb1IrallzeHNDNkZmTHBzTURoSDhiQmxuaVZRYVYrYjBaaVN5L1dsYTg5WnFQNUpHUmpWRDBINS9LVDFlcFFpMm8rZTdRa1dFS1lDT1hNdlNEWGJ4Z0hiVVJpaVU0N3lzck9XNWxJM1M1UHdsdElHazBXbDNmUUF1SHJHTGhUZnFYbkZMYmZpYTAySXJhU0krZGFpbnQreS9yOStaR3FCL3djZnl2RFdoRXh4Mjk5OExyZWhzcWJCVWJBci9RSXpCZDBHTWJNaHYrWnMyaWpOYVlNVndBSjMvN3ZGSmNGSm1pRkxiS1VCL0dRVjlCOHB3bVRFRGlIVGdqOUZyWHhCY0ZtcHMwSDI0ekQzd0szZzBLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJ2aXAxMF9ib2xsQkIiLCJkZXNjIjoiIiwiZGF0YSI6InZJK21zcG40TGtVRW56MVJTaGErYUkrbERKVytiRktlbTNoTmJyeTQ4aUJqbGNLVWJSQjMycDdIcVZHQkxEY1A2Y1lQVDVWK1J3dGNZSnFJeWVsWERkQkk2aFhCSnBUNWs3SERDdVY0L0xhNUZCNGRrZG5jRUE5VXQ1eDVhT0hkZ2FuWXkrOVdzd0JKTnZzQWpTU3RkOXM1S055Tlc3SzJJTXdpYzRBVnBtejQ5WnJER2lhT2tUVDluSnI2ZnhHcDNiNmlvRk9tS2xNc1o5R1pPZTNmR2tXNHpXck5rMEU4MXNhd0JGWXN3YVVqWlhrSmRPSHVCZ1FLTmoxU1gxNDVGYXJtRFp6SzhJRksyak1uaW9KbWRLaTZZM3RGdkQzYzlzV0JsK3BnQWlrNUtFZHUrYVkwbkczWld2OEFWV0Z2TUdtN2t0V0FQSHA5b21LcUxRMFUvM1M4V1FmUlJWMUNIOFB3Y1FvK3NDdUVNNDcveE5XWTZVRXRaY1BOVEY2SW1yeXB1cnM5cm9iekxvWG4zVmxlRXdtd1dKSlV3eWlKbGVvSXE2bWo3VVdtbDJQa0VzTFVFU2JNWmdPV2FyMS9kNTlaYXhUMEZnU1ZGVWJCZjA2Njg3ZWlPTUNHQmc5SE13MnAwcTNvL1JZZGpDTlNQTnZieksyU0VZRWdUWCtwTm1kalN3VVg0b2cxQnRLV2pMWmMydUJFcEpPVnI5TmNnTUM3OTlYRWwzK1VzR2pYN0FqZ0d4VWhDMG5sUlBRZEh2UVhjb2RLRUR3NWZKSTlwRWVBaXcxUkpldmoxUFB6MUMrWlpSaEEvOEVlNjFOaTdpVHF0MW14TnU0ejRlZ0RnbUZDVm9wUk9oaVRiMUpZQTRDV3ZhQkkySUpOejNBUWFjRDIvbWhLT2NOVzVUeTVWT1ZPR2w4SVZVM25FQ0Z0TmRLZ1Q0em83RXNrNlg1YU4zVS93bTNJaFFOTkNUTXlYU0hjODc2a1JmUFRqamczeVQvc3p0MDhDNWo4dVNXODlMczN3V3ZMYk1Od29BN2ZLc3E3cG1Lb0VWaE9zZGhEYVRzK1V3VERQSXpvQktoWmwvYWdGVyt5UnkvVFhNRTd1eXdxVk1UTHBodkcrOXFFV2ppVTJ3VjFzR2dlU25qQTB0MXVjYU14TjhvYXRWS2REUTQ5NDM3QUIrZUhLUWRGOUlaTE5SR1hGbXFWYStVWk5KMGpkV3dabDRXb0xtSy9XK21qSlVVOEhXNXY4OXAybEdwWCtxSVZqaHZpQjRVaitwTWZtMTFZeFdOZ0xDOHdlVG5NcTJWaDFURFV5UWZ5Qll1SFhML3IyMmFZOEdvWFhFYTEzN2JOUFl6VU5aSVppbTlxc1BKWnZ3VUhWQi9aWEprYWZ1bGV2ODJqaHRTSkpuOTl0L0ptSnFURjRPTGpUZXl6VWdQb0p6RjJBNTdlMXpoSEo2N2E2MG12OEUrREhjTzluL3kzSDZIbFkrQnNDUE00MGR6emFmeUVYV2VIZm0xeXRDTGxtT3ArWXdZSEJQNFdYSGRhQ2tadjc0UXhkZUtrUjdFV2p4c252UXppQ3JqS2szTDJPYmFIbGtVZFhqaW5pdXZsRmNMYlhmWVdSSXNJWXQzYmlHcDhMNTNvSGNFMmhySE0vRWdnOStRUlhaejRHNFk3QXZNbE56K1pST3E2U2hlaUk0Y3FMSnRuZVpkZGpIb0R2MVhRNHhNRFEvQXkxU1NISnk1OXZSdGlJNFZKeW1RZ3BQbXFVUUh1VUxyZW16aCtmaHdScm8xNjc5Y1RCV1JuaWNRcnVkWkRCZmR1dFRYSGtENnVzT3lkL2dpcjA1bld2UWJRQnkwUDJ1VUJSQ2VvekJYQU16MDB6ZGJSeHY5WDJ3aW1ZanZVV0x1a2IwYnU5RzhiSTBBMnN6RHJlYlBWOUx5Vk8rc2lFTUdRbS9zUGxBRXdMckVMcGxqNzZWa3VWSGp1YnI4WnBBQjN3emFsMjkvaElwTElDN3VmaE9yUGowSFF4ZjBqS3ZXaXNVN0l3cEpxVjRzYkl1VWhtL2dlbXVUOVlIdWM4cWdvcVJ4NVJrVHg2MWFCdEk0MmxCemhSTW1PY05rOFBCVzU1aHFlMG9scW40VnBvVnlXNWdkSDJHRmYzbkl2WFBoSHRYUFBhZnJ6V2FyRVNBMWxpNlZObHZYUi8rTjlHVllabjg4bmZzdjBQeFRIMjE4alZuWk0ycHRXbUo5UitsWG1ObGR4NUJjL3pkVEt1VFZrbDNQS2V6R1dCd1pIMURzVlNJMWlOVzgwUTU0THdFcTRISHMzNGxtT09YY0hjQ3N0cXR3dTdJaGdlOUFZeEJ6bXF2TndOSTZZaTA5YmRJSUpGU1lQeVVHcGZHSEdWa2pJQVZUVkM2WFBKSjIzc0JlNXdMc21iR0FWbjNuT3JKUVZHNWpJbXgxNFN2UkFWRzMxZlkrb2tVWXBKcGl0eDNTS05KaS9zcXhZUGt5eXgweHp2aS9ISFRUd1VoREs4NU40K2FzM1h4OU5CZklyYXJNSkcwLzRvTVlyRHROT1pHcVlLZzUzYTBxRHdoTnBSMCtUMnVvWVMwQkwwQ3V4eG5VbUd0TkdUekhxbndRbnBRNHJjY3VxMUlQU3FVN0tKTzZNaGpFNTQxM3RmNTBHN1dNVDluRzFoei9haWprYjZvdElyUzRCMmJKYm1sbnNNV1pnaTVsMUphekNGNWE0V2NJVGxJYXFvR0lOcjArL3VkbWZ3VHF0TlN3aFkzU0VPaENhbnZ3NHFSSFFYOXJSSS81VUNKTm1mdWlNRTBnRXZjcUFJTkxDRFNQblRoeEJoL2pObk9jZG1DNTUwdjhRMHRFbDhpUyt4cUhkS3R1TGpXTUhMcC96LzBBQUhLcjVGZEdsYzJFRjlyL0dxOHcyZXJobTZURDdaZ0NYYkJMeE5FSHdjQ1lnRGVaRlJrcnRSSGxYT2xlZVg4cXlnallUUVNzYzQwTmt6blFUT2dDWENROVpmWStIdUVBdFhueTV0UWw1a2VONWhDWGkwQVdlWnpzRXNKSU8vWGFsc2tnOWlyaXVRSTMvTXBjMjhubkxORG1nM21RNTJsS0h1bFg5VGtuMFlCeW5hNHVqdGZsNmRNaFBwYWpDMm1SYnBwaVNZQ3JOSzE3ZW8ySHBaTmVlOEJkWmpnM1NLYUtVck1lMXBqdVZhMk9GVFhlRVFiUnhqd2cyZ3NoUExJc05DbWJUVW5HTjVFdTRwQWlOMkdHZUpCeHdkWkZvV1prSE9GU0R4aUtxVTdpbHhHd1B3NFVuVG9sM0FrQVo2QU00N2tFTWtNeFM5eGVZTmR5Z0QyQ2liNkU1WC9xYWUrTU01UHRoWTExdmZPT210RDYwU3pQSGw1cnROcDZ0ZndIZlhDQUxuUC9ySkM0bUFGd0VjWE12b2ovNGh0bWFvcEZMNXJJb1BEc2FQUXMyYSttM0ZLOWRaaEthd0ZoTjNWSTU3Ukc4NUNsNTZJR3ltSHJibFhqSVZWMVcrR2JhL2tCL2s0RnlsU3JDWWp5M2o2RTRwbmdmaHk1Ky9MZVpIZ1Fzd0VpbVJ6bVlPM2d6dEVTNllycmJJM1F5aTlHWkhCbjJlWVN1ZzBNUEwvQU91UEdNOXlCWGhBZU5nNE1icHpkREtDZS9HcFc3cDV1VTQwa0xMeVhXVUNwNnR0MTdmS3MzTjBySk11VFRpblVDSGVaNE43cWJLK21oUFNHeUEzTWxZSjIxYzJZbmFJNGFNeXdoMmtXM2VQTXllTlZKRnR3b3FTU09vYUNScDUwUStyRm94cVlCUUFVWFFFbkdtNm9tQzVNQU5PYWg5YmpyM2VjZGVMS1c2Rjg5ai9CcVFEeTZQb3phRXc4cG5CUmJ4MW9oZ2FZdEl0QlhkMXpkdVMzakwxQ3l0U29IRmV3OEdPWEZDQ2V1bitKTFRtaVVHclovekNlV2ZSL3ZZWHdLMUpKdHROVnlMSFgxOWR5di9kem1RMkdHSVZmbm8wOU1UbVBOb0ZUamxROU1RMlUzR2JqSks0bjVNN2loNkc2RjNBZFBHQW9melFIbWx2K0JMT0hiYnIzWjkxa0FYZ1NjOWUzbW5haFVWUkwxOHlwU0JNMm5Ld08xdXFBZkZuNnlXNnJwTXV3Tlo0SGxqSy96TXpxZ3RteThzY2xQTEs1ZmhqelJHaUJQcGhXZ3V0WmpUeHBEVVpiODYybEZ0OVJpRUVBeDJ3UU5RYlU5aEhUaElJRzlsNUJiak9zaWtPZ292MDRQTzVEdHZvK1kzaFBPQWNhWG1mSG9WejhmNDB3azdpc1VwUWRwU3ZZdjRiK3hINCtxSVpyclZpWUR3RXJZdWFBSHNrTzBYQSt6NHNad2F3Y2QzOXpaU1JTK1d6UmZLQUNqVU1KODBuaHFMVGVtdFVjNE1UV2NhekpSZitJb3ljVk1uZ2c2QmYySmNpS2xwb1hOUjQrTC82blg3SnlYUXJvVVdWSkhsYTNzYmF3eWhhOHIzblF6eUdNSnZjRmFQZm1sSjZIOTBRYVN1d2ZTMkRKUWUvWTdEaHorTnVPdTJnQ0RGc2tDNDNaSlE5TytTN01hNUVoRkJ6bktPVVdlRm9RdGhvMVRxdGxtNlowNkc1MDJiUGdWL3QvQnVQeFhZY3lrc21LS3VCUHhYWi9XdkFLS00ybXJlOFYycDZGaUVQa0U3SkZSWjh4Q2VSV3FhNWNBU1pJWkhCRmt0bkhqZWRBdk9wV2FoenBKYlE3Q1Q0WHhtVXVVb0J4N2FIM04rVG4xYnFIYWxaMm1jZVlQSVkzOWlWU21VU0wzTktoOHhqY2ZBME1QRXhpbXVwUW9rcHdqQTI2UVNiZnh5SEYrZE1uSHpEM1pmbEVoUHRLine truncated
|
||||
@@ -0,0 +1,179 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 简称: vip09
|
||||
// 名称: vip09
|
||||
// 类别: 公式应用
|
||||
// 类型: 用户应用
|
||||
// 输出: Void
|
||||
//------------------------------------------------------------------------
|
||||
Params
|
||||
//默认螺纹888 1小时
|
||||
Numeric Length(20); //周期
|
||||
Numeric Offset(2); //标准差倍数
|
||||
Numeric X(0.5);
|
||||
Numeric SS(0.01);
|
||||
Numeric TRS(75);//移动止损止盈幅度 参数范围及步长: 5-100,5
|
||||
Numeric Fund(20000); //投入保证金;
|
||||
Vars
|
||||
Numeric Lots(0);
|
||||
Series<Numeric> ma1;
|
||||
Series<Numeric> UpLine; //上轨
|
||||
Series<Numeric> DownLine; //下轨
|
||||
Series<Numeric> MidLine; //中间线
|
||||
Series<Numeric> Band;
|
||||
|
||||
Series<Numeric> cond1;
|
||||
Series<Numeric> cond2;
|
||||
Series<Numeric> cond3;
|
||||
Series<Numeric> cond4;
|
||||
|
||||
Series<Numeric> kcond1;
|
||||
Series<Numeric> kcond2;
|
||||
Series<Numeric> kcond3;
|
||||
Series<Numeric> kcond4;
|
||||
|
||||
//出场变量
|
||||
Series<Numeric> MyPrice;//开仓价格
|
||||
Series<Numeric> MyPrice2;//开仓价格
|
||||
Series<Numeric> HighAfterEntry;//开仓后出现的最高价
|
||||
Series<Numeric> LowAfterEntry;//开仓后出现的最低价
|
||||
Series<Numeric> liQKA;
|
||||
Series<Numeric> DliqPoint;
|
||||
Series<Numeric> KliqPoint;
|
||||
Series<Numeric> barcoutN;
|
||||
Series<Numeric> bar_entry_count;
|
||||
Series<Numeric> kaicang_kg;
|
||||
Series<Numeric> BB;
|
||||
Series<Numeric> WIDTH;
|
||||
Plot plt1;
|
||||
Plot plt2;
|
||||
Events
|
||||
//此处实现事件函数
|
||||
|
||||
//初始化事件函数,策略运行期间,首先运行且只有一次,应用在订阅数据等操作
|
||||
OnInit()
|
||||
{
|
||||
//与数据源有关
|
||||
Range[0:DataCount-1]
|
||||
{
|
||||
//=========数据源相关设置==============
|
||||
AddDataFlag(Enum_Data_RolloverBackWard()); //设置后复权
|
||||
|
||||
AddDataFlag(Enum_Data_RolloverRealPrice()); //设置映射真实价格
|
||||
|
||||
AddDataFlag(Enum_Data_AutoSwapPosition()); //设置自动换仓
|
||||
|
||||
//AddDataFlag(Enum_Data_IgnoreSwapSignalCalc()); //设置忽略换仓信号计算
|
||||
plt1.figure(0); //plt_vol单独设置画板,没有指定画板id,系统自动创建
|
||||
plt2.figure(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Bar更新事件函数,参数indexs表示变化的数据源图层ID数组
|
||||
OnBar(ArrayRef<Integer> indexs)
|
||||
{
|
||||
|
||||
Lots = Max(1, Round(Fund/(O*ContractUnit*BigPointValue* MarginRatio/rollover), 0));
|
||||
MidLine = AverageFC(Close,Length);
|
||||
Band = StandardDev(Close,Length,2);
|
||||
UpLine = MidLine + Offset * Band;
|
||||
DownLine = MidLine - Offset * Band;
|
||||
|
||||
PlotNumeric("MidLine",cond1,cond1,White);
|
||||
PlotNumeric("UpLine",cond2,cond2,red);
|
||||
PlotNumeric("DownLine",cond3,cond3,Green);
|
||||
|
||||
//布林极限(BB)=(收盘价-布林下轨价格)/(布林上轨价格-布林下轨价格)
|
||||
BB=((C-DownLine)/(UpLine-DownLine));
|
||||
//WIDTH=(布林上轨值-布林下轨值)/布林平均值
|
||||
WIDTH=((UpLine-DownLine)/MidLine);
|
||||
plt1.line("BB",BB); //画线
|
||||
plt2.line("WIDTH",WIDTH);
|
||||
|
||||
|
||||
if(MarketPosition!=1 and H>=UpLine[1] and C[1]<UpLine[1] and BB[1]>X and WIDTH[1]>SS)
|
||||
{
|
||||
Buy(Lots,max(open,UpLine[1])); // 如果满足开多条件,则买入
|
||||
LowAfterEntry = EntryPrice;//保存多头开仓价格;
|
||||
kaicang_kg=1; // 标记已开仓
|
||||
bar_entry_count=0; // 重置连续未开仓的Bar计数
|
||||
barcoutN=0; //重置liQKA系数的计算
|
||||
liQKA = 1;
|
||||
}
|
||||
|
||||
if(MarketPosition!=-1 and L<=DownLine[1] and C[1]>DownLine[1] and BB[1]<X and WIDTH[1]>SS)
|
||||
{
|
||||
SellShort(Lots,min(open,DownLine[1])); // 如果满足开空条件,则卖空
|
||||
HighAfterEntry = EntryPrice;//保存空头开仓价格;
|
||||
kaicang_kg=1; // 标记已开仓
|
||||
bar_entry_count=0; // 重置连续未开仓的Bar计数
|
||||
barcoutN=0; //重置liQKA系数的计算
|
||||
liQKA = 1;
|
||||
}
|
||||
|
||||
|
||||
if(kaicang_kg[1] ==1) // 如果在本周期内有开仓操作
|
||||
{
|
||||
bar_entry_count=bar_entry_count+1; // 将开仓计数器加一
|
||||
}
|
||||
Else
|
||||
{
|
||||
bar_entry_count=0; // 如果没有开仓,则将开仓计数器重置为 0
|
||||
}
|
||||
|
||||
//移动出场
|
||||
If(MarketPosition == 0) // 自适应参数默认值;
|
||||
{
|
||||
liQKA = 1;
|
||||
barcoutN=0;
|
||||
}Else if(bar_entry_count>barcoutN) //当有持仓的情况下,liQKA会随着持仓时间的增加而逐渐减小,即止损止盈幅度乘数的减少。
|
||||
{
|
||||
liQKA = liQKA - 0.1;
|
||||
liQKA = Max(liQKA,0.3);
|
||||
barcoutN=bar_entry_count;
|
||||
}
|
||||
|
||||
Commentary("kaicang_kg[1]"+text(kaicang_kg[1]));
|
||||
Commentary("bar_entry_count"+text(bar_entry_count));
|
||||
Commentary("barcoutN"+text(barcoutN));
|
||||
Commentary("liQKA"+text(liQKA));
|
||||
// 记录开仓后的最高点和最低点
|
||||
If(bar_entry_count > 0)
|
||||
{
|
||||
HighAfterEntry = Min(HighAfterEntry,High); // 空头止损,更新最低的最高价
|
||||
LowAfterEntry = Max(LowAfterEntry,Low); // 多头止损,更新最高的最低价
|
||||
}
|
||||
|
||||
if(MarketPosition>0)
|
||||
{
|
||||
DliqPoint = LowAfterEntry - (Open*TRS/1000)*liQKA; //经过计算,这根吊灯出场线会随着持仓时间的增加变的越来越敏感;
|
||||
}
|
||||
if(MarketPosition<0)
|
||||
{
|
||||
KliqPoint = HighAfterEntry + (Open*TRS/1000)*liQKA; //经过计算,这根吊灯出场线会随着持仓时间的增加变的越来越敏感;
|
||||
}
|
||||
|
||||
If(KliqPoint[1]>0 and MarketPosition<0)PlotNumeric("KliqPoint[1]",KliqPoint[1]);
|
||||
if(DliqPoint[1]>0 and MarketPosition>0)PlotNumeric("DliqPoint[1]",DliqPoint[1]);
|
||||
|
||||
// 持有多单时
|
||||
If(MarketPosition >0 And bar_entry_count >0 And Low <= DliqPoint[1] and DliqPoint[1]>0 and DliqPoint[1]>0 and bar_entry_count>0)
|
||||
{
|
||||
|
||||
Sell(0,Min(Open,DliqPoint[1]));
|
||||
DliqPoint=0;
|
||||
kaicang_kg=0;
|
||||
}
|
||||
|
||||
// 持有空单时
|
||||
If(MarketPosition <0 And bar_entry_count >0 And High >= KliqPoint[1] and KliqPoint[1]>0 and KliqPoint[1]>0 and bar_entry_count>0)
|
||||
{
|
||||
|
||||
BuyToCover(0,Max(Open,KliqPoint[1]));
|
||||
KliqPoint=0;
|
||||
kaicang_kg=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from vnpy.trader.optimize import OptimizationSetting\n",
|
||||
"from vnpy_ctastrategy.backtesting import BacktestingEngine\n",
|
||||
"# from vnpy_ctastrategy.strategies.vip10 import vip10\n",
|
||||
"from vip10 import vip10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"engine = BacktestingEngine()\n",
|
||||
"engine.set_parameters(\n",
|
||||
" vt_symbol=\"rb888.SHFE\",\n",
|
||||
" interval=\"1h\",\n",
|
||||
" start=datetime(2020, 1, 1),\n",
|
||||
" end=datetime(2024, 3, 21),\n",
|
||||
" rate=1.5/10000,\n",
|
||||
" slippage=1,\n",
|
||||
" size=10,\n",
|
||||
" pricetick=1,\n",
|
||||
" capital=1_000_00,\n",
|
||||
")\n",
|
||||
"engine.add_strategy(vip10, {})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2024-08-25 20:34:47.539362\t开始加载历史数据\n",
|
||||
"2024-08-25 20:34:47.539362\t加载进度:# [0%]\n",
|
||||
"2024-08-25 20:34:47.747392\t加载进度:# [10%]\n",
|
||||
"2024-08-25 20:34:47.748385\t加载进度:## [20%]\n",
|
||||
"2024-08-25 20:34:47.748385\t加载进度:### [30%]\n",
|
||||
"2024-08-25 20:34:47.749381\t加载进度:#### [40%]\n",
|
||||
"2024-08-25 20:34:47.749381\t加载进度:##### [50%]\n",
|
||||
"2024-08-25 20:34:47.750388\t加载进度:###### [60%]\n",
|
||||
"2024-08-25 20:34:47.750388\t加载进度:####### [70%]\n",
|
||||
"2024-08-25 20:34:47.751394\t加载进度:######## [80%]\n",
|
||||
"2024-08-25 20:34:47.751394\t加载进度:######### [90%]\n",
|
||||
"2024-08-25 20:34:47.751394\t加载进度:########## [100%]\n",
|
||||
"2024-08-25 20:34:47.752372\t历史数据加载完成,数据量:0\n",
|
||||
"2024-08-25 20:34:47.752372\t策略初始化完成\n",
|
||||
"2024-08-25 20:34:47.752372\t开始回放历史数据\n",
|
||||
"2024-08-25 20:34:47.752372\t历史数据回放结束\n",
|
||||
"2024-08-25 20:34:47.752372\t开始计算逐日盯市盈亏\n",
|
||||
"2024-08-25 20:34:47.752372\t回测成交记录为空\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "KeyError",
|
||||
"evalue": "\"None of ['date'] are in the columns\"",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[3], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m engine\u001b[38;5;241m.\u001b[39mload_data()\n\u001b[0;32m 2\u001b[0m engine\u001b[38;5;241m.\u001b[39mrun_backtesting()\n\u001b[1;32m----> 3\u001b[0m df \u001b[38;5;241m=\u001b[39m \u001b[43mengine\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcalculate_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 4\u001b[0m engine\u001b[38;5;241m.\u001b[39mcalculate_statistics()\n\u001b[0;32m 5\u001b[0m engine\u001b[38;5;241m.\u001b[39mshow_chart()\n",
|
||||
"File \u001b[1;32mc:\\veighna_studio\\lib\\site-packages\\vnpy_ctastrategy\\backtesting.py:283\u001b[0m, in \u001b[0;36mBacktestingEngine.calculate_result\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 280\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m key, value \u001b[38;5;129;01min\u001b[39;00m daily_result\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__dict__\u001b[39m\u001b[38;5;241m.\u001b[39mitems():\n\u001b[0;32m 281\u001b[0m results[key]\u001b[38;5;241m.\u001b[39mappend(value)\n\u001b[1;32m--> 283\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdaily_df \u001b[38;5;241m=\u001b[39m \u001b[43mDataFrame\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfrom_dict\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresults\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset_index\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mdate\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 285\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moutput(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m逐日盯市盈亏计算完成\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 286\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdaily_df\n",
|
||||
"File \u001b[1;32mc:\\veighna_studio\\lib\\site-packages\\pandas\\core\\frame.py:6109\u001b[0m, in \u001b[0;36mDataFrame.set_index\u001b[1;34m(self, keys, drop, append, inplace, verify_integrity)\u001b[0m\n\u001b[0;32m 6106\u001b[0m missing\u001b[38;5;241m.\u001b[39mappend(col)\n\u001b[0;32m 6108\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m missing:\n\u001b[1;32m-> 6109\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNone of \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmissing\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m are in the columns\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 6111\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m inplace:\n\u001b[0;32m 6112\u001b[0m frame \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\n",
|
||||
"\u001b[1;31mKeyError\u001b[0m: \"None of ['date'] are in the columns\""
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"engine.load_data()\n",
|
||||
"engine.run_backtesting()\n",
|
||||
"df = engine.calculate_result()\n",
|
||||
"engine.calculate_statistics()\n",
|
||||
"engine.show_chart()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"'''用于参数优化'''\n",
|
||||
"# setting = OptimizationSetting()\n",
|
||||
"# setting.set_target(\"sharpe_ratio\")\n",
|
||||
"# setting.add_parameter(\"Length\", 5, 80, 5)\n",
|
||||
"# setting.add_parameter(\"Offset\", 0.5, 3, 0.5)\n",
|
||||
"# setting.add_parameter(\"X\", 1, 3, 1)\n",
|
||||
"# setting.add_parameter(\"TS\", 5, 80, 5)\n",
|
||||
"# from multiprocessing import cpu_count\n",
|
||||
"# # 获取 CPU 核心数量\n",
|
||||
"# num_cores = cpu_count()\n",
|
||||
"# print(f\"获取 CPU 核心数量:\",round(num_cores/2))\n",
|
||||
"# engine.run_ga_optimization(setting, max_workers=round(num_cores/2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# engine.run_bf_optimization(setting)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.9"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import pandas as pd
|
||||
|
||||
class vip10(TargetPosTemplate):
|
||||
""""""
|
||||
author = "松鼠Quant"
|
||||
|
||||
#默认螺纹888 1小时
|
||||
|
||||
Length = 50 #布林周期
|
||||
Offset=2 #标准差倍数倍数
|
||||
X=2 #过滤均线的Length倍数, 范围及步长:1-5,1
|
||||
TS = 60 #移动止损止盈幅度 参数范围及步长: 5-200,5
|
||||
|
||||
|
||||
lots=1 #下单手数
|
||||
|
||||
current_bar = 0
|
||||
|
||||
Highup=0
|
||||
Lowdown=0
|
||||
|
||||
HigherAfterEntry = float('inf')
|
||||
LowerAfterEntry = -float('inf')
|
||||
liQKA = 0
|
||||
DliqPoint = 0
|
||||
KliqPoint = 0
|
||||
OBVValue=[0]
|
||||
MAOBV=[0]
|
||||
M2=[0]
|
||||
|
||||
cond1=[0]*2
|
||||
cond2=[0]*2
|
||||
cond3=[0]*2
|
||||
|
||||
kong_cond1=[0]*2
|
||||
kong_cond2=[0]*2
|
||||
kong_cond3=[0]*2
|
||||
|
||||
|
||||
MA1=0
|
||||
MA2=0
|
||||
MA3=0
|
||||
BB=0
|
||||
WIDTH=0
|
||||
|
||||
|
||||
|
||||
|
||||
parameters = ["Length",'Offset','X',"TS",'lots']
|
||||
variables = ["HigherAfterEntry","LowerAfterEntry","liQKA","DliqPoint","KliqPoint"]
|
||||
|
||||
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
||||
""""""
|
||||
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
|
||||
|
||||
#df: pd.DataFrame = pd.read_csv(r"index_contract.csv")
|
||||
#self.contracts_sizes = {row.vt_symbol.split('.')[0]: row.contract_size for _, row in df.iterrows()}
|
||||
|
||||
self.bg = BarGenerator(self.on_bar)
|
||||
self.am = ArrayManager(300)
|
||||
self.pos=0
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
Callback when strategy is inited.
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(1)
|
||||
|
||||
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)
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
"""
|
||||
Callback of new bar data update.
|
||||
"""
|
||||
self.cancel_all()
|
||||
am = self.am
|
||||
am.update_bar(bar)
|
||||
|
||||
if not am.inited:
|
||||
return
|
||||
# 如果历史数据还没有初始化完毕,则直接返回
|
||||
# 注意:am 是交易系统中的一个数据管理器,可能用于管理历史数据
|
||||
# bar 是当前的K线数据,其中包含了开盘价、最高价、最低价、收盘价和成交量等信息
|
||||
|
||||
self.current_bar=self.current_bar+1 #Bar线计数
|
||||
#print(bar.datetime,"current_bar:",self.current_bar)
|
||||
|
||||
|
||||
# 如果当前Bar的计数器值小于三倍的Length,则直接返回
|
||||
if(self.current_bar<self.Length*self.X):
|
||||
return
|
||||
|
||||
#中轨
|
||||
self.MA1=am.ema(self.Length,array=True)
|
||||
self.MA2=am.ema(self.Length*self.X,array=True)
|
||||
# #上下轨
|
||||
self.boll_up, self.boll_down = am.boll(self.Length, self.Offset,array=True)
|
||||
|
||||
# 多头开仓条件
|
||||
self.cond1=self.MA1 if self.MA1[-1]>self.MA1[-2] and am.close_array[-1] > self.MA1[-2] else self.cond1
|
||||
self.cond2=self.boll_up if self.boll_up[-1] > self.boll_up[-2] and am.high_array[-1] > self.boll_up[-1] else self.cond2
|
||||
self.cond3=self.MA2 if self.MA2[-1] > self.MA2[-2] and am.close_array[-1] > self.MA2[-1] else self.cond3
|
||||
|
||||
|
||||
# 空头开仓条件
|
||||
self.kong_cond1=self.MA1 if self.MA1[-1] < self.MA1[-2] and am.close_array[-1] < self.MA1[-2] else self.kong_cond1
|
||||
self.kong_cond2=self.boll_down if self.boll_down[-1] < self.boll_down[-2] and am.low_array[-1] < self.boll_down[-1] else self.kong_cond2
|
||||
self.kong_cond3=self.MA2 if self.MA2[-1] < self.MA2[-2] and am.close_array[-1] < self.MA2[-1] else self.kong_cond3
|
||||
|
||||
|
||||
#布林极限(BB)=(收盘价-布林下轨价格)/(布林上轨价格-布林下轨价格)
|
||||
self.BB=((am.close_array[-1]-self.boll_down[-1])/(self.boll_up[-1]-self.boll_down[-1]))
|
||||
|
||||
|
||||
#极限宽,WIDTH=(布林上轨值-布林下轨值)/布林平均值,VIP10用不到这个算法,可以自己拓展
|
||||
self.WIDTH=((self.boll_up[-1]-self.boll_down[-1])/self.boll_up[-1])*100
|
||||
|
||||
if self.cond1[-1]>self.cond1[-2] and self.cond2[-1]>self.cond2[-2] and self.cond3[-1]>self.cond3[-2] and self.BB>0.5 :
|
||||
if self.pos!=1 and bar.high_price>=self.cond2[-1] :
|
||||
if self.pos == 0:
|
||||
self.buy(bar.close_price, self.lots)
|
||||
elif self.pos < 0:
|
||||
self.cover(bar.close_price, self.lots)
|
||||
self.buy(bar.close_price, self.lots)
|
||||
self.liQKA = 1
|
||||
self.LowerAfterEntry = bar.close_price
|
||||
|
||||
|
||||
if self.kong_cond1[-1]<self.kong_cond1[-2] and self.kong_cond2[-1]<self.kong_cond2[-2] and self.kong_cond3[-1]<self.kong_cond3[-2] and self.BB<0.5 :
|
||||
if self.pos!=-1 and bar.low_price<=self.kong_cond2[-1] :
|
||||
if self.pos == 0:
|
||||
self.short(bar.close_price, self.lots)
|
||||
elif self.pos > 0:
|
||||
self.sell(bar.close_price, self.lots)
|
||||
self.short(bar.close_price, self.lots)
|
||||
self.liQKA = 1
|
||||
self.HigherAfterEntry = bar.close_price
|
||||
|
||||
|
||||
#记录入场后的最高价和最低价
|
||||
if self.pos>0:
|
||||
#self.HigherAfterEntry = HigherAfterEntry
|
||||
self.LowerAfterEntry = max(self.LowerAfterEntry, bar.low_price)
|
||||
elif self.pos<0:
|
||||
self.HigherAfterEntry = min(self.HigherAfterEntry, bar.high_price)
|
||||
#self.LowerAfterEntry = LowerAfterEntry
|
||||
|
||||
if self.pos == 0:
|
||||
self.liQKA = 1
|
||||
else:
|
||||
self.liQKA = self.liQKA - 0.1
|
||||
self.liQKA = max(self.liQKA, 0.5)
|
||||
|
||||
if self.pos>0:
|
||||
DliqPoint = self.LowerAfterEntry - ((bar.open_price)*self.TS/1000)*self.liQKA
|
||||
#print(bar.datetime,"DliqPoint",DliqPoint,"low_price",bar.low_price,"self.LowerAfterEntry",self.LowerAfterEntry)
|
||||
if self.pos<0:
|
||||
KliqPoint = self.HigherAfterEntry + ((bar.open_price)*self.TS/1000)*self.liQKA
|
||||
#print(bar.datetime,"KliqPoint",KliqPoint,"high_price",bar.high_price,"self.HigherAfterEntry",self.HigherAfterEntry)
|
||||
|
||||
if (self.pos>0 and bar.low_price <= DliqPoint and DliqPoint>0):
|
||||
#多头出场
|
||||
self.sell(bar.close_price, self.lots)
|
||||
elif (self.pos<0 and bar.high_price > KliqPoint and KliqPoint>0):
|
||||
#空头出场
|
||||
self.cover(bar.close_price, self.lots)
|
||||
|
||||
self.current_bar += 1
|
||||
self.put_event()
|
||||
|
||||
|
||||
def on_trade(self, trade: TradeData):
|
||||
"""
|
||||
Callback of new trade data update.
|
||||
"""
|
||||
self.put_event()
|
||||
|
||||
|
||||
def on_stop_order(self, stop_order: StopOrder):
|
||||
"""
|
||||
Callback of stop order update.
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJWSVAxMSjlt6XkvZzljLopIiwiZGVzYyI6IiIsImRhdGEiOiJ2SSttc3BuNExrVUVuejFSU2hhK2FJK2xESlcrYkZLZXRLT1RkOWVpMTFaaXRlejBKU1psRmIvTzFlcm84WHd4UnJQcS9lVkRNL0l4cFJQM1NMWWRIQ0lZWmhYeW51ZnRCVStEbDJCL0x6V2VmNkVsT3pXRFZEeVFzUkw0c3dJSG9jUm1zVUVCMVpTbVBKdkIzVmk4V2pPY1M2d1EzcVl1TFpqb01uQmFjMnJWZHVJdkJpRjd3NzdLODg0a1B2RHdvMUxtclVPSnd4V0JqVjQ5S2RkY2c5VU9qWkdkSDlHRDRFZHMwWFp4MkNjeWwzRFozWDZZRHFUeDlnaExVVm5XUjNCV2VRNTA4RXZwTm15dXY1SENGUHd6UnpDNjcyaWIvTCsvdGVnUGxpaFRjVEhHL2duN3Z6NlRuN1VuYURDYUVYVDNwU0pVTHoyQ1E0L0hxN0hrKys3MzFGU1o3ZU9aZ01GVlpVRTBPdW5PRE04aWU3MUdEa1dvcGp2TmtJRDhaakNHdTJDdmNFU3Jpak0vZ3Y0bGMrRHpJMG1FaEZRWGphb1Bkc2c5MnBkT21yVWhlUld0a2RoNzVGb29HaFNxNFE0Rm4yMVBOcmNxUUFZWVZsZDhyT25SM2R1NGxaRFJybitlQWlkakhmK3poWElxUjZGbVFlVTdVUDNhTjRMRHM0VnlLa2VoWmtFV1gwOTg2cysxTVFGZUpJYUZpNFQrZG0xMmtYclpQL0JrbWFYSGdZOTJKKys2NzdPdnQ5SGVRakJiTnNWZkxOS3o2bmdWMFdkcjFYV3djQ2F2QmVnZkU5VnRVZ0xsRk1Rdk1OMlFkTG1DOXczclRzTUhRNWIrQWNNUGpUTXNJVlM2OGl5d3RMV01QUVlOT2RtUENtSkh0aW5zYThCUXNNOWNjTXFmZUdnMDRqZHhYcWhyaFJSYitqMngzeERwRUg4WkxXemJ0R2RoaTQxTEw2VXJVSWVtb0lUT1hFL2xXK1JRT0NnM3JtMnMxaElqdHpKWW5VLzdaRVNOZWVlSmQ4YndObWloWVU1bHphcXlwc09la3FtdnJHaFJqc1BPdkZkL3dHaHVEWVhBSEFDc3ljTkg1MENRRjNNU2N5ZSt2ckJSRGVwZkdHU3BDZlVTSUxEbmpIZTdRSFB6Z0V5MlJDZzZFODlVTTJ5Tm5RN3lSN2dvVFZaQmllVVBBK1V1cTg4UE8vbS9ac3FHKzZQSTYxTmQreUI4cmF3ODBsYjgzTjlTbmlSTTc2R3QzZk5RYkZkMzdvTjlsWEQwd1F2anJlaHg2aFRjMjgwdTlqY0VTTVJ4cnJCb213YUdXb25wRkVISXROc3JvZjlMUFd6b1VMTlhCeXNkR1BnS2w2MFJRM0JyYmFGZWdYM1Axd2J3akhXTFJCd0RwOEFvcGQ1YWNoZFhJdmk4TFA2eFkzZisvYlBLZmFBaC9aNmczbjBqMDJlMm1XVWFTVk1iK0dQSHliTSs5OTVBRnRBalNHMGhwNTB6UCtDV3JuSlVveVNOaDBXejUwZ0IzUEZpNGlNUWw1TjB6ZlNZNEhSMy9JTklhUTBFV3pJZ1RFS1piRmxFTnViQjJ6azhhSURRL1dKR2RHUFpRb0hPWHdRV0E1QzVjMkI2M0xLa1U1TjcyNmVPR1Q3bHp0SFJEVms2dDNCU0swL1JHZk4xalpzTGZaT2lBMkFJZXI4OHhLNmFsbVBtb2pYaG9EY2xBWHhmL1dmMlRVdnhhRy90NzZTQ2dzQi9rRlIvSEZIVUNDZDZubVVSQWN3NjBoeWlsMld2Q3l0QWFuLytJRTVGdTI5d3hGMjh1ZkZzQ0ZPak1BeUMreTlQa0dMTE55NCtFQ0p3NzRFOGFkSDlOVE5aYjBSZnFmNTZhdklvZ3ZXUSt6ZjJhVU0vQ1k3UEg4ZmtYWWtTUDg3cXhBZ0UrYXIrUzRvV1pvTlI0clRYMlhtMTNjdDkxMHAvbGN6b1B1UXNlbnp1SFpTOW9xVnpVb05zNUw2T1Y3ZDVBUTFNSjhhV1d0NUNqVUZZd1hNR0NUa3Y3ZmhlTktMQ2Q3RHlBSjJPdW83WHV4NjVWUkg2QUpJU1cwVkFEUlpVNTJXTnhnYkUrSkdQV2lSbTdBWG1qWHhFNk9HRE9yZ1ZwTTkzQWVCZUNVUE1PaVdicEZIQ0hGOUpRT1Z2Z3RqSmlFV05xdmhMbzhWcDlwTzQrbkQ0S3dxQWxEWU1oVk9Gd0FIRGdJVlR4QTRKbTYxakJJb3M2ZkFRVGFLcVMyTnRTeG1SVzMxdHMwSklmd3BWSWRwdzN5WGt5QzlqekpFUjFXLzRxMnZsd25WOUR2Uk9YNW9SRVk1a0FnaDhDK0dYQmhmS1BHMFdNeFU5SW9NVzJaTm9pdzdLQ2pVMVN1TFVySXlLWVg3UytJbi9IMDR3amluaUtmMEdRTjFQODZTcU5oblBxTnpPZk4xNTNSZ3RRQk92UGxIcE5uMm45cmJLLzVvTGMvbVZFVlZVdWkxMVp2cEg4cGxtRUx1QXRUL1o0SVFicDhEWnJ0dWs3T1h4bzZmYlphb1o2VHluWGVXQjVROHRDMVliYmNnRXV5YXowTExUTGIzQWRiQndKcThGNkI5NkhtdU9xRXhKTGxUZ2IwQ3cvVjFnUFNhRjBmd1hhL0FCd3crTk15d2hWTHJ5TExDMHRZdzlCZzA1Mlk4S1lrY0tmOG5hTFd4RGwwazJoTFF2VTRtVnkvSTRDYnNBN1A4aVhaaEJCdjByeFNxb0RlSXhZVTVjcmZVR3lnTFJydVE5dzY5VzZ0a2trQ3VMNTNCbGMzSEFmd0hXUWVDRlJUQUpZdzdFamtKWEtjSkFvOTJwZUZCL3pNb0tsU2I0L09xRWovOGEwMGw0SGs5ZE9hRW93K3ZkbU1lU2NjcGV2elVMZG5PR0hPOUZmbTc4MUVKN1BDL0JoTVUxbVU1ZTUycCtKZTA1ZENZY1BYZTNVY1FMMVhQQy9kWU1kbkhGaTVYYWovRUUxY2dJRGdJYXlNOUk3SnFJUzd0TW91VEtoNUZXN3M0WkJvRkdTclExam5oSXc2cktXc2VXSmVrT1pnYjcwYW9wRE1hcTEwS0RnczBEMFZ1dVl1ZGhyVnZPZlZjRXJCZWhNVHU3emVwTEtJUXA3L1NqYlNxeDh2bHJJN0dMK201bUg1T1dzQUlreWk2cnpMekNLUHBpNjloQ0FQN3lVZCtYSFUvc2VNTHp4RE9TeWFBWWxqb0VWVXUwRlhaS2VVOGt6bHpQdVozYllmWmFOQ1FuSU8wYUs5cFhiaVBRRVJoSnlLclB2THpqN0ZXSnoxWGxQTlBnWjFkQ2tmZFBRU1ZBelpDb3QzS1hBTG9acGc0QnN0ZXl6UXFpSEpMZlVnYXgrcGYxSnFpOGZhSmpoYmgrNDY1R2dyaW0rN3NoZlNLZmRvRllhNml0b084WjJsMHBoZnpERWJLREVlN2xYdzJUU2tEZkFURVR4djdHVFNFeEV1VEYvQTFuUGNTZE9xeG9vSUgxU0t2NDJOV1p4aHd6ekdpekwvMHZ0cUhCbnlINVRFUEsvY2ZhWmlVdm12SnN4OHRHWmg1NFVpNzF3NmsxWDJnWlFTSUNpQ2lGZ2RhS3lpWmQxb2ZoQ1Q0TVE3UzZqRHkzU3hNYWlSTHB0RlJMWjZWS0F4RFhDNHpRcVV0cm5UY1h4bDB0aDJWclVObHJGVDhaQkF2TFV5Zm9GWXFybTZab0k3SVRiVkNxQ1l0SldjYWduZThpcHZlbzB1RVF0ZWNkT2JOc1Z3bzliYml4akhsemlCT2RsUXpQMmdETW5mTDNSYSt6OGNtd1pLLzJYT3lURURzeUNQWGxLK2tpeXFYUjQ2WE1MTHZsYXNsZ01ORWZrTjdPbzJjdFp6Qi9LTmxVVEdXVjZXVjFHSmNUckJaTmE4RDNoblZjQm01M2pHRkFTZ293d2RTRTJYNmpYZWV4QnlmR0cwQUxVQ05NYU84K2tRQUpkY1d0WFo2RXFSN2tEVVZqMW5UMW05YTJ3S2ZZeXhIeGJLV1B6ZW5NQ2k5TjYrM0FVbTdmY205ZmtFS1NDUU5kVmZHZC9FK090UGhrNG51VmMrRkMwa3BSaW9HZ2ZoL2FSdlNZK1hYUjdsWXJ1Qk9FNlJoRHc5Z1ErMVZ2a2g0UDhNajZBRkNWbVZGaUZ5N2t5T0ZPdnFPZUQwODVRTllkQTFjdDIzZ0xBVnR1T3NUYVVOdVhDK0h0ei8wTTYwNSsvY0cyby9zaThjVUQvNS9TamEvYUpFbWZhN2UzbHhXM0xOTWVFK05aOUVXdWZkZ3h6T21FN3ZPZ3Z2aTN2YXdTcnJoU2dOZjhjQTRKN3U0cFkyZ2hKUGRVbFU2QUhkQUs3Wm53bEpXWHhoWit2Ui9rUmk0Ny9FSkw0TmlJRkVEL21xN1hGNUw1aUgxSk9yOHdXQVJVaXM3NlU0L29hU0FSTlFheWg4V1dreUg4bkRVREZrZlZYWnRnK0tIQXB4ZEVkMXNkSkZhY0hncXJMVTFQMVlRZTBTM0lPWTBtL3VObzZtODFTQWRUaFVqbmlUeTdzbFRqNGF3eCtOTzBvNHE2MFZzM3FSbElidnRXUVFvNU9ZRkk4WFdhWVNDa2lUbzlQQkFIWWhwZGVmWk1xVGxUZVJWWlJBSnhEK0pQTE85aTVZSjFVMlRiR09VZmp2YkoreUVjSFNjdHNhN0p0WmhvM3R5WmNKd0YrK21kcWxuZkJGMEdsNzREa0h6eGVEVkovWVl1d0o2WUkxTkJZa3Bjem5wTFJXZnc3ZVlOaXNNVG94VExOcFJZcmRQRlVHUzNzZHBJdkwwUWxzOEt0YitmQzdlNzhyYVBHOGRzREtLSWZqdXVvSTlPT1RLine truncated
|
||||
@@ -0,0 +1,263 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import math
|
||||
class vip11(TargetPosTemplate):
|
||||
""""""
|
||||
author = "松鼠Quant"
|
||||
|
||||
#默认螺纹888 1H
|
||||
length = 15
|
||||
M=35
|
||||
smoothingFactor = 0.1
|
||||
TS = 50 #移动止损止盈幅度 参数范围及步长: 5-200,5
|
||||
lots=1 #下单手数
|
||||
|
||||
|
||||
fastLength = 12
|
||||
slowLength = 26
|
||||
MACDLength =9
|
||||
|
||||
current_bar = 0
|
||||
|
||||
Highup=0
|
||||
Lowdown=0
|
||||
|
||||
HigherAfterEntry = float('inf')
|
||||
LowerAfterEntry = -float('inf')
|
||||
liQKA = 0
|
||||
DliqPoint = 0
|
||||
KliqPoint = 0
|
||||
OBVValue=[0]
|
||||
MAOBV=[0]
|
||||
M2=[0]
|
||||
|
||||
cond1=[0]*2
|
||||
cond2=[0]*2
|
||||
cond3=[0]*2
|
||||
|
||||
kong_cond1=[0]*2
|
||||
kong_cond2=[0]*2
|
||||
kong_cond3=[0]*2
|
||||
MACDValue=[0]*3
|
||||
normalizedMACD=[1]*3
|
||||
smoothedMACD=[1]*3
|
||||
smoothedNormalizedMACD=[1]*3
|
||||
STCValue=[1]*3
|
||||
|
||||
|
||||
MA1=0
|
||||
MA2=0
|
||||
MA3=0
|
||||
|
||||
gobro=0
|
||||
|
||||
|
||||
|
||||
parameters = ["length",'M','smoothingFactor',"TS",'lots']
|
||||
variables = ["HigherAfterEntry","LowerAfterEntry","liQKA","DliqPoint","KliqPoint"]
|
||||
|
||||
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
||||
""""""
|
||||
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
|
||||
|
||||
#df: pd.DataFrame = pd.read_csv(r"index_contract.csv")
|
||||
#self.contracts_sizes = {row.vt_symbol.split('.')[0]: row.contract_size for _, row in df.iterrows()}
|
||||
|
||||
self.bg = BarGenerator(self.on_bar)
|
||||
self.am = ArrayManager(300)
|
||||
self.pos=0
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
Callback when strategy is inited.
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(1)
|
||||
|
||||
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)
|
||||
|
||||
def calculate_con(self,Price1, Price2, Length):
|
||||
Matches = 0
|
||||
Price2=Price2-Length
|
||||
Price2list=[]
|
||||
Price2list.extend([Price2 + j for j in range(1, Length)]) # 使用extend将元素添加到列表中
|
||||
# 检查是否有足够的数据进行计算
|
||||
if len(Price1) < Length :
|
||||
return 0
|
||||
else:
|
||||
for i in range(1,Length-1):
|
||||
# 计算Con条件
|
||||
if (Price1[i] >= Price1[i-1] and Price2list[i] >= Price2list[i-1]) or \
|
||||
(Price1[i] < Price1[i-1] and Price2list[i] < Price2list[i-1]):
|
||||
Matches += 1
|
||||
# 计算结果并返回
|
||||
return 2 * Matches / Length - 1
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
"""
|
||||
Callback of new bar data update.
|
||||
"""
|
||||
self.cancel_all()
|
||||
am = self.am
|
||||
am.update_bar(bar)
|
||||
if not am.inited:
|
||||
return
|
||||
# 如果历史数据还没有初始化完毕,则直接返回
|
||||
# 注意:am 是交易系统中的一个数据管理器,可能用于管理历史数据
|
||||
# bar 是当前的K线数据,其中包含了开盘价、最高价、最低价、收盘价和成交量等信息
|
||||
|
||||
self.current_bar=self.current_bar+1 #Bar线计数
|
||||
#print(bar.datetime,"current_bar:",self.current_bar)
|
||||
|
||||
# 如果当前Bar的计数器值小于self.M*2
|
||||
if(self.current_bar<self.M*2):
|
||||
return
|
||||
#均线
|
||||
self.MA1=am.ema(self.M,array=True)
|
||||
self.MA2=am.ema(self.M*2,array=True)
|
||||
|
||||
# 如果当前Bar的计数器值小于self.MACDLength
|
||||
if(self.current_bar<self.MACDLength):
|
||||
return
|
||||
|
||||
a1 = 2 / (self.fastLength + 1)
|
||||
a2 = 2 / (self.slowLength + 1)
|
||||
# 计算相关系数
|
||||
r2 = 0.5 * math.pow(self.calculate_con(am.close_array[-self.MACDLength:], self.current_bar, self.MACDLength), 2) + 0.5
|
||||
kk = r2 * ((1 - a1) * (1 - a2)) + (1 - r2) * ((1 - a1) / (1 - a2))
|
||||
# 计算MACD值
|
||||
self.MACDValue.append((am.close_array[-1] - am.close_array[-2]) * (a1 - a2) + (-a2 - a1 + 2) * self.MACDValue[-1] - kk * self.MACDValue[-2])
|
||||
|
||||
# 如果当前Bar的计数器值小于self.fastLength
|
||||
if(self.current_bar<self.fastLength):
|
||||
return
|
||||
|
||||
lowestMACD = min(self.MACDValue[-self.fastLength:])
|
||||
highestMACD = max(self.MACDValue[-self.fastLength:]) - lowestMACD
|
||||
# 归一化MACD
|
||||
self.normalizedMACD.append((self.MACDValue[-1] - lowestMACD) / highestMACD * 100 if highestMACD > 0 else self.normalizedMACD[-1])
|
||||
# 平滑MACD
|
||||
self.smoothedMACD.append(self.normalizedMACD[-1] if self.smoothedMACD[-1]==1 else self.smoothedMACD[-2] + self.smoothingFactor * (self.normalizedMACD[-1] - self.smoothedMACD[-2]))
|
||||
# 计算平滑后MACD的最低值和最高值
|
||||
lowestSmoothedMACD = min(self.smoothedMACD[-self.fastLength:])
|
||||
highestSmoothedMACD = max(self.smoothedMACD[-self.fastLength:]) - lowestSmoothedMACD
|
||||
# 归一化平滑后MACD
|
||||
self.smoothedNormalizedMACD.append((self.smoothedMACD[-1] - lowestSmoothedMACD) / highestSmoothedMACD * 100 if highestSmoothedMACD > 0 else self.smoothedNormalizedMACD[-1])
|
||||
# 平滑归一化后MACD,得到STC值
|
||||
self.STCValue.append(self.smoothedNormalizedMACD[-1] if self.STCValue[-1]==1 else self.STCValue[-2] + self.smoothingFactor * (self.smoothedNormalizedMACD[-1] - self.STCValue[-2]))
|
||||
|
||||
STCvalue1=round(self.STCValue[-1]-50,0)
|
||||
STCvalue2=round(self.STCValue[-2]-50,0)
|
||||
STCvalue3=round(self.STCValue[-3]-50,0)
|
||||
|
||||
# 如果当前Bar的计数器值小于self.fastLength
|
||||
if(self.current_bar<self.length):
|
||||
return
|
||||
|
||||
Highup=max(am.high_array[-self.length:-1])
|
||||
Lowdown=min(am.low_array[-self.length:-1])
|
||||
|
||||
#/*开平仓条件代码-----------------------------------------------------------------------------------------------------------------------------------*/
|
||||
cond1=STCvalue1>STCvalue2 and STCvalue3>=STCvalue2
|
||||
cond2=STCvalue1<STCvalue2 and STCvalue3<=STCvalue2
|
||||
cond3=self.MA1[-1]>self.MA2[-1] and am.close_array[-1]>self.MA2[-1]
|
||||
cond4=self.MA1[-1]<self.MA2[-1] and am.close_array[-1]<self.MA2[-1]
|
||||
|
||||
if cond1:
|
||||
self.gobro=1
|
||||
|
||||
if cond2:
|
||||
self.gobro=-1
|
||||
|
||||
if self.pos!=1 and self.gobro==1 and bar.high_price>=Highup and STCvalue1<0 and cond3==True:
|
||||
if self.pos == 0:
|
||||
self.buy(bar.close_price, self.lots)
|
||||
elif self.pos < 0:
|
||||
self.cover(bar.close_price, self.lots)
|
||||
self.buy(bar.close_price, self.lots)
|
||||
self.liQKA = 1
|
||||
self.LowerAfterEntry = bar.close_price
|
||||
|
||||
|
||||
if self.pos!=-1 and self.gobro==-1 and bar.low_price<=Lowdown and STCvalue1>0 and cond4==True :
|
||||
if self.pos == 0:
|
||||
self.short(bar.close_price, self.lots)
|
||||
elif self.pos > 0:
|
||||
self.sell(bar.close_price, self.lots)
|
||||
self.short(bar.close_price, self.lots)
|
||||
self.liQKA = 1
|
||||
self.HigherAfterEntry = bar.close_price
|
||||
|
||||
|
||||
#记录入场后的最高价和最低价
|
||||
if self.pos>0:
|
||||
#self.HigherAfterEntry = HigherAfterEntry
|
||||
self.LowerAfterEntry = max(self.LowerAfterEntry, bar.low_price)
|
||||
elif self.pos<0:
|
||||
self.HigherAfterEntry = min(self.HigherAfterEntry, bar.high_price)
|
||||
#self.LowerAfterEntry = LowerAfterEntry
|
||||
|
||||
if self.pos == 0:
|
||||
self.liQKA = 1
|
||||
else:
|
||||
self.liQKA = self.liQKA - 0.1
|
||||
self.liQKA = max(self.liQKA, 0.5)
|
||||
|
||||
if self.pos>0:
|
||||
DliqPoint = self.LowerAfterEntry - ((bar.open_price)*self.TS/1000)*self.liQKA
|
||||
#print(bar.datetime,"DliqPoint",DliqPoint,"low_price",bar.low_price,"self.LowerAfterEntry",self.LowerAfterEntry)
|
||||
if self.pos<0:
|
||||
KliqPoint = self.HigherAfterEntry + ((bar.open_price)*self.TS/1000)*self.liQKA
|
||||
#print(bar.datetime,"KliqPoint",KliqPoint,"high_price",bar.high_price,"self.HigherAfterEntry",self.HigherAfterEntry)
|
||||
|
||||
if (self.pos>0 and bar.low_price <= DliqPoint and DliqPoint>0):
|
||||
#多头出场
|
||||
self.sell(bar.close_price, self.lots)
|
||||
self.gobro=0
|
||||
elif (self.pos<0 and bar.high_price > KliqPoint and KliqPoint>0):
|
||||
#空头出场
|
||||
self.cover(bar.close_price, self.lots)
|
||||
self.gobro=0
|
||||
self.current_bar += 1
|
||||
self.put_event()
|
||||
|
||||
|
||||
def on_trade(self, trade: TradeData):
|
||||
"""
|
||||
Callback of new trade data update.
|
||||
"""
|
||||
self.put_event()
|
||||
|
||||
|
||||
def on_stop_order(self, stop_order: StopOrder):
|
||||
"""
|
||||
Callback of stop order update.
|
||||
"""
|
||||
pass
|
||||
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJ2aXAxM+W3peS9nOWMuiIsImRlc2MiOiIiLCJkYXRhIjoidkkrbXNwbjRMa1VFbnoxUlNoYSthSStsREpXK2JGS2VGQVJjZWN1WjNBMTd2ZUtuU1pHTkNWOFk2UGlrNG9LWWs3MU51NCtQRVF3bU12REZLQ3piWk1LNXQ2RnNqbmZBYlZ0bWRYd3ZEZFpjdHNOOVFVRWRNdnRJUjVQbGRjZXJZQ21MVm1NTGI2RXRDdHBjU3FUSGplVXNOWWpCK3VIdUU0YUNiNEdUMzVPcUw0MnJndHcrMUhiMkpZUWE4eEt2UmlBZXBFWUFveSt1V0hDcWFPTXZwVWpEL0wyM2s4Q2ZqWEI5emJ5d1FLYkhieXRVb2NGTFZrWE4rdGFkWkgyZUMzeTFIcnZqSzNtVENROUlvandGVXE5NDFhN3hUUjRvSVJnNFFPWFIvTElzUlRFTzZCanVFVk1tVlFDdU5QamM4d2tGd2FpN0c4Yy9RSnhrdlZRZWo2QVlhK3RsMTFhYXVSaVJkaVMzMjl1WTN3STFOQ084YU9zdXVuWEFhdU5ScUx4VTlxYk13ZE9nb1pNY2FjTURHNmk4VlBhbXpNSFRvWDRLR2V2SXU2VnJtTlYvcEozL1l0TnljVUVVRm1kK2U3aHVBeWZKNVljL1k2MnNRMnBHSkhvYlBzbU1qT29tRWxSYmkzSUprUnVpV25UZGpjREpjU050NjRhTDErZExNT2lmUWszcXg2MTN5Umw4azE0VHRBZEpoRkZWZ2xXV05ZM0pCdVZnODBZT2QyR05OVnB3ZzlLUUo5NHFVTnd4Q0lrRUFtQ29XTDRiSGhjV2g1blBCSnZ3UVNRV25PVk90Ni9pc0o3eEw5eVdrZEEydFJKTHEzVnk3eUtCVG9kVkQ2ZXZORGJSOWdrNE0rd2ltOGswSmJNYitIbDdrcDV3V0JkNXVRNTJpTk9LSzc5c092TG9VaHVtQXBBWFhiQmZjU2s0R0svaXlkN3k1OVUrc2s4UUxYNVp3UzhuQ05ESGkvWStTd3VLelBha21kM3dYSk9HNWdmWVVoRGhhR2hXbitvSEUvcGRBeldUbllIRFRldFBleUpNdHlpdDhrYVN5RUxCN1hicnRRMkdQbkxtbHRBa0ZtK0tvWkpsSXdmV2lUSzhtTmZ3WVBFZEtLV2Rza1BQaUt1dTJtUnVldnhodU9GL0tPYkFEY2pKNnlndFl2YjZOelpHMkMwd0ZBZ3QzdzFhc0hLNHhZZzRkVGg2bGNJQjU0ZklycFA2T3BRL3BlVXRQK1RZSTVzNStoOVp5STZFR2tMNTVadmxGYjVjSFRNRFpHSUtWN1BYRk93UXlEUE5yRlB6M3FRdUF3aTY3UXFhU1M4NEJCOHFaZDZEbkdKd0hETWJzZDJ4K056dWFTcWQ4eHlxMlZPVWZCOFdoaU9GeEY1MU93Uk1XdXdSL0xMdlNoUkJ6bWRXU1A0RTFaV21xd1hTUEtrY0hpcWRVZWdwRzZIckU5VU5CMDVjM1ZwNFBEY25CRHpoemVOb3ZDSDdaanY5UzVnUkNwQ2ZtbWZkMmNiWkZjK1JVdEpvRzVCL3pZM1AyTVRmN3lrQ2hkd2pPY2cwZi9rN3UxVDlEODhheHdUYXplNHhPODVVeGh6cXdJZ2RnNFpoS1lnMVhDdVRRaHFXSnh1RkpKYzJFUmlrZ3MyK3cyS251MVhERmNTUDNJRGM0SGZQdGpzWHF3V3VRNGdXMHkrMUZFVDl1SkovVUxLMHdOUjkydkxCakQzc3ZCMFRiMW1UenByNHFJRWZhUXF3bXQ1VjliY0lRT0prcTdPQzJvbXl5bGlSbDR1bkc2TWxQb09mTzczVXl2WW9lTGtLUk9sVjhoUUxDam10ZTBLcDFJZmpJTG50OWp4YXB6QnEyU2RhcEFWaWVhcDRET2doZVcydGlRWitqekV3ZENaK0NxNS96ZnVLTGpUWVpvOW1NSmpvT3pEcHhINGpkTTBtdEpHaEYxZzB4VGVHQnRlQklvTWNuRllOcEliMkFoaHJBUUFwSnRGSGhwd0tTTjB1QkFsSTlzR25mNXZKSVhZaXU5WXRmWFZGSXlvenNKUHZPREhaOWdrYWR5azVIZkZZTTJNOGs1VE5NQTNmMGpSZWZJMGpkbFlBeXhWbEx5Ri9HUnkrb0NQU05rY0doUThDTVdWYmszdjVGZVRRWWFVY2kwbVdHc0hjdDFLdWtpcGFUSE5wcjlVVkxZT2NtR29zSVR5V2hLdlhIck1CSkZMTGorOTErR1JrR0xNUzdOanVpVGw0bmJEK0UzM1FRN3R6blJNTEE4Y1Bxdk9OQzRpcEV6U09jK0JBWll5UHAyM1M0alhvcHpMR05QQUxZNVcxNTdpTU4rSzdtZytidUJDd0xONDBVVWpmSGFVL3FLWk1KcU80SVloN2wydkdYZ1IwODVMMytjVVZHV3JSUkxiQ0ZkNFRsdmswNTAxeXBMZkNScUtTUEVTeTlhR2drb0xXWGNXcHY4VHlONUFHaUdrYXg2S29VTGJFVmhPQm1OK3NUSkdqRGZYQUJoZEtJODd4OTA1WXdRTWtsS2t1NlEwZG9BUmpJbjJiMW1nVnBjSzlQTFhVUGVJY1F0TVFpUXN3VmtRTWVvb2lXekx3N1V6NE1BTWludHh3UzY1cXhaQXZHbnJMVHUxaG5wa01qeFQyM0Jnb2JkZ3k0U010RWJSSkVicm1oZU41RGJmU0s5Q1NOSE5uZmJWeGk5emNkNndRSTh1UWRYTmNaSHROaFRYMFRHWHhKcG1kNWlnYThUVlhzN29oZFhybDQxUEZrdGtKSTk2RjRCZVl3SklqT3VUSmNKOGhqY29QMVYzMVNpUTcxdDU5UUVWckFqNFZjdWh6ZWIwUDRoakVsd3pUWC9nU3J4aEZ5SFZvQi9xMzJHS2xCbFNRTldUSFlTZHBWazFLSCtmTVB2T0VUVjBiT0pOWUI4MWphS0VWQlUwK1Frdm1KNmFBRmJMVXJkVk9wT2lQVWEyb3N0aFhPbndFQmdiaTFVWlVyM2NYNzFVREliaWVncnA5S2kyM0dWVDBnUk9uNDFIMmZYRjh5U1pSdkVLbER5WDRHS2JlZjdpTk5VUWhEVWlJaXkrZE5RRSt0bzR0RE5nV2lnYlpES21tZWRoc1ovNGRSdFJQV2pxUkxSQW9pRFJBZGNGc3Q1ZGlyNlljdUI4R0ZPa1J4M3VoVnlRRUhNTVBvNFJ4bEtxclhBOEFRemw0L2IwMHBPd0xPOVFDQksrL05yVmZVK2FkWEkrVFF2WlFpQzFPUC9qdk1XUXZnakpHWlRZeWlyVy9oUGl6emFuVWMzenY4WTF4bzdMWUNySWIyc3N2dXk1K2duQm13djdwbE9YdStJQmJacy9EbVlaeUJ0b0R2Y2YvelZrTWVTNFRuMk43UzdPN0xZdVNiY1VBRHVyTXdLRmd6Y2RiTEtvVC9vaStoMXpvN2x6MndBWnJIc0grcVRyajI1b1Z0VlExV284VlJmQVBjSm5nM3E0Tkd3a2Y0dnM4MlJ3ems5NEpuZ3l5MUJCYzNVbTUwZTJwSU14UjRnTjA5ZTkyUUthbjcxQm0wbEdRMnFBcy84aHNSSjgyTStCaFpZY2VwU2xYMDNiYlZUbkt0QStRU04xbXNJQThrVVlvVDRTVGxHVmhmeFBRY0w3cG5XQ1JBbnk1NDZFdWdzWFp4Q1hWbDNvV1Nuam53TGR6ZUYzVW5zQjVTVHU1WFA0WEVRN2JRbGZTM1NROTZXa1RVdnZxVWc5TCtDc2FuR1ZmQUp3TzhibzFUcEtOajdIMmxuaVEzLzVlOThlcmFIdkNPOEtnSkp3NmF5SUhxSnJuSjlKbXh3VnBQZlVWQVB4SklwSTY4eXJYNGRyVU9HYkZOVWpCYUVNT3U0YkZyM1pFV3BOVGhhZ3dOZGswSFNGYVZTenFzUXFvYm1PSlhVTW52eWl3MFd1UU1ZUFo5ek91dlMzVFo2OWhjMVRoUTdUNjZnM3J2UGNwdU1VaDdRRTcwYUR3YVBRaXBuZ0YweGpPRFNzUXgzY0dzZEpkUXdpTzUvbWZQSVNGL0dQUWFhNXRZWkdRYzljV2VEUlo1Ri9pY2tkMWs4Mkh6QVpoYlQ1eU5GSUpDb3Y3U0RWb01aUUw4QmdTcHpWSDJhK1pWTmRuc1ZoVlcxODE2eWJTU3N5SU50UlZadHBYL3NxTWxzNXpKUTd3TEFqdmRHd0lPZkQxTXdHUjBmUk90SUplZE00YUU4c1N5cjdrWUxEVzJKS1JZSjdLOGVSUXBRZVNPbTlma0dJWVJjUklXbldZL1FaZ2JrUUU2K0JVVzJGeWd3ejJQTzcyeEhUMmV6T1ZMM1grYksyeDZHVFZJbTNuaDFHV3VIemJJY0RUVmlEZkV4VUZzT0k1N3d2WE9BdHNDMFpBOTRxRGJURXhVSVpHVGFLb0x6ZFNhNWR1L3dvVTM1bW9sZHkzSGdwejlPdUJ5eDhobXBsNk5rRVZRR2lRUzVHNFpJVjE0aXlqN1ZjQmhGdVRpb29LeGQwU0JVcnFvdHBOTXZiQ0t5Vzh6azlWdDVFOTdVTXp0aTRML0xFU3ZGNGlKbDF2NzVGdXl3ZGM0VWhBYVZ1TmRPS0dUSDFBUWFBbk9EbUI0OUhzbU5mUkJGQ2J2MTJaRDR6R2c0c0d3bE9QNzZSL2FpQXB5M2s4MGpyNWFYb3pnb2NwRU83c3QwTmZ3dktRK0RNK3pHMmFUT2puUG5CY01YR0hSUG5ITVAwT3JDODQ2YWN2R050cWgvOXhBcEVXMVF5NjQvcFJGd0MrVjUyckdmNDA0cy9td0pVS0NQc0VidGhlc1NJVE05VXkwNXFLYWdHMmszTWp1RTNvelFObHY2MEU1OWwrNnJYckVVR2dQVG9hSlZoZ1ZzRWxLine truncated
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from vnpy.trader.optimize import OptimizationSetting\n",
|
||||
"from vnpy_ctastrategy.backtesting import BacktestingEngine\n",
|
||||
"# from vnpy_ctastrategy.strategies.vip13 import vip13\n",
|
||||
"from vip13 import vip13"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"engine = BacktestingEngine()\n",
|
||||
"engine.set_parameters(\n",
|
||||
" vt_symbol=\"rbJQ00.SHFE\",\n",
|
||||
" interval=\"1h\",\n",
|
||||
" start=datetime(2020, 1, 1),\n",
|
||||
" end=datetime(2024, 3, 21),\n",
|
||||
" rate=1.5/10000,\n",
|
||||
" slippage=1,\n",
|
||||
" size=10,\n",
|
||||
" pricetick=1,\n",
|
||||
" capital=1_000_00,\n",
|
||||
")\n",
|
||||
"engine.add_strategy(vip13, {})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2025-01-30 22:14:21.167367\t开始加载历史数据\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:# [0%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:# [10%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:## [20%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:### [30%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:#### [40%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:##### [50%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:###### [60%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:####### [70%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:######## [80%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:######### [90%]\n",
|
||||
"2025-01-30 22:14:21.167367\t加载进度:########## [100%]\n",
|
||||
"2025-01-30 22:14:21.167367\t历史数据加载完成,数据量:0\n",
|
||||
"2025-01-30 22:14:21.167367\t策略初始化完成\n",
|
||||
"2025-01-30 22:14:21.167367\t开始回放历史数据\n",
|
||||
"2025-01-30 22:14:21.167367\t历史数据回放结束\n",
|
||||
"2025-01-30 22:14:21.167367\t开始计算逐日盯市盈亏\n",
|
||||
"2025-01-30 22:14:21.167367\t回测成交记录为空\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "KeyError",
|
||||
"evalue": "\"None of ['date'] are in the columns\"",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[10], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m engine\u001b[38;5;241m.\u001b[39mload_data()\n\u001b[0;32m 2\u001b[0m engine\u001b[38;5;241m.\u001b[39mrun_backtesting()\n\u001b[1;32m----> 3\u001b[0m df \u001b[38;5;241m=\u001b[39m \u001b[43mengine\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcalculate_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 4\u001b[0m engine\u001b[38;5;241m.\u001b[39mcalculate_statistics()\n\u001b[0;32m 5\u001b[0m engine\u001b[38;5;241m.\u001b[39mshow_chart()\n",
|
||||
"File \u001b[1;32mc:\\veighna_studio\\lib\\site-packages\\vnpy_ctastrategy\\backtesting.py:288\u001b[0m, in \u001b[0;36mBacktestingEngine.calculate_result\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 285\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m key, value \u001b[38;5;129;01min\u001b[39;00m daily_result\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__dict__\u001b[39m\u001b[38;5;241m.\u001b[39mitems():\n\u001b[0;32m 286\u001b[0m results[key]\u001b[38;5;241m.\u001b[39mappend(value)\n\u001b[1;32m--> 288\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdaily_df \u001b[38;5;241m=\u001b[39m \u001b[43mDataFrame\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfrom_dict\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresults\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset_index\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mdate\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 290\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moutput(_(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m逐日盯市盈亏计算完成\u001b[39m\u001b[38;5;124m\"\u001b[39m))\n\u001b[0;32m 291\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdaily_df\n",
|
||||
"File \u001b[1;32mc:\\veighna_studio\\lib\\site-packages\\pandas\\core\\frame.py:6109\u001b[0m, in \u001b[0;36mDataFrame.set_index\u001b[1;34m(self, keys, drop, append, inplace, verify_integrity)\u001b[0m\n\u001b[0;32m 6106\u001b[0m missing\u001b[38;5;241m.\u001b[39mappend(col)\n\u001b[0;32m 6108\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m missing:\n\u001b[1;32m-> 6109\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNone of \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmissing\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m are in the columns\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 6111\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m inplace:\n\u001b[0;32m 6112\u001b[0m frame \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\n",
|
||||
"\u001b[1;31mKeyError\u001b[0m: \"None of ['date'] are in the columns\""
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"engine.load_data()\n",
|
||||
"engine.run_backtesting()\n",
|
||||
"df = engine.calculate_result()\n",
|
||||
"engine.calculate_statistics()\n",
|
||||
"engine.show_chart()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# '''用于参数优化'''\n",
|
||||
"# setting = OptimizationSetting()\n",
|
||||
"# setting.set_target(\"sharpe_ratio\")\n",
|
||||
"# setting.add_parameter(\"boll_n\", 5, 60, 5)\n",
|
||||
"# setting.add_parameter(\"offset\", 0.5,3, 0.5)\n",
|
||||
"# setting.add_parameter(\"trailing_stop_rate\", 5, 100, 5)\n",
|
||||
"# setting.add_parameter(\"x\", 5, 80, 5)\n",
|
||||
"# from multiprocessing import cpu_count\n",
|
||||
"# # 获取 CPU 核心数量\n",
|
||||
"# num_cores = cpu_count()\n",
|
||||
"# print(f\"获取 CPU 核心数量:\",round(num_cores/2))\n",
|
||||
"# engine.run_ga_optimization(setting, max_workers=round(num_cores/2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# engine.run_bf_optimization(setting)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.9"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import numpy as np
|
||||
|
||||
class vip13(CtaTemplate):
|
||||
author = "松鼠Quant"
|
||||
|
||||
boll_n = 20
|
||||
offset = 1.5
|
||||
trailing_stop_rate =60
|
||||
x = 30
|
||||
rsi_n = 14
|
||||
over_sold = 30
|
||||
over_bought = 70
|
||||
|
||||
parameters = ["boll_n", "offset", "trailing_stop_rate", "x"]
|
||||
variables = ["lots", "up_line", "down_line", "mid_line", "band", "rsi_value", "kg", "highest_low_after_entry", "lowest_high_after_entry", "rsi_n", "over_sold", "over_bought"]
|
||||
|
||||
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
||||
""""""
|
||||
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
|
||||
|
||||
self.lots = 0
|
||||
self.up_line = 0
|
||||
self.down_line = 0
|
||||
self.mid_line = 0
|
||||
self.band = 0
|
||||
self.rsi_value = 0
|
||||
self.kaiduo = False
|
||||
self.kaikong = False
|
||||
self.kg = 0
|
||||
self.highest_low_after_entry = 0
|
||||
self.lowest_high_after_entry = 0
|
||||
self.bg = BarGenerator(self.on_bar)
|
||||
self.am = ArrayManager(300)
|
||||
self.pos=0
|
||||
|
||||
def on_init(self):
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(10)
|
||||
|
||||
def on_start(self):
|
||||
self.write_log("策略启动")
|
||||
|
||||
def on_stop(self):
|
||||
self.write_log("策略停止")
|
||||
|
||||
def on_tick(self, tick: TickData):
|
||||
pass
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
self.am.update_bar(bar)
|
||||
if not self.am.inited:
|
||||
return
|
||||
|
||||
self.lots = 1
|
||||
|
||||
self.calculate_boll()
|
||||
self.calculate_rsi()
|
||||
|
||||
self.kaikong = self.rsi_value > self.over_bought and self.am.close[-1] < self.up_line
|
||||
self.kaiduo = self.rsi_value < self.over_sold and self.am.close[-1] > self.down_line
|
||||
|
||||
if self.kaiduo:
|
||||
self.kg = 1
|
||||
self.hd = self.am.high[-self.x:-1].max()
|
||||
|
||||
if self.kaikong:
|
||||
self.kg = -1
|
||||
self.ld = self.am.low[-self.x:-1].min()
|
||||
|
||||
|
||||
if self.pos != 1 and self.kg == 1 and bar.close_price >= self.hd :
|
||||
self.buy(bar.close_price, self.lots)
|
||||
self.highest_low_after_entry = bar.close_price
|
||||
self.pos=1
|
||||
if self.pos != -1 and self.kg == -1 and bar.close_price <= self.ld:
|
||||
self.short(bar.close_price, self.lots)
|
||||
self.lowest_high_after_entry = bar.close_price
|
||||
self.pos=-1
|
||||
|
||||
self.trailing_stop(bar)
|
||||
|
||||
self.put_event()
|
||||
|
||||
def calculate_boll(self):
|
||||
self.mid_line = self.am.sma(self.boll_n)
|
||||
self.band = self.am.std(self.boll_n)
|
||||
self.up_line = self.mid_line + self.offset * self.band
|
||||
self.down_line = self.mid_line - self.offset * self.band
|
||||
|
||||
def calculate_rsi(self):
|
||||
self.rsi_value = self.am.rsi(self.rsi_n)
|
||||
|
||||
def trailing_stop(self, bar: BarData):
|
||||
if self.pos > 0:
|
||||
self.highest_low_after_entry = max(self.highest_low_after_entry, bar.low_price)
|
||||
dliq_point = self.highest_low_after_entry - bar.open_price * (self.trailing_stop_rate/1000)
|
||||
if bar.close_price < dliq_point:
|
||||
self.sell(bar.close_price, self.lots)
|
||||
self.pos=0
|
||||
if self.pos < 0:
|
||||
self.lowest_high_after_entry = min(self.lowest_high_after_entry, bar.high_price)
|
||||
kliq_point = self.lowest_high_after_entry + bar.open_price * (self.trailing_stop_rate/1000)
|
||||
if bar.close_price > kliq_point:
|
||||
self.cover(bar.close_price, self.lots)
|
||||
self.pos=0
|
||||
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJ2aXAxNeW3peS9nOWMuijlsI/ms6LlhL8pIiwiZGVzYyI6IiIsImRhdGEiOiJ2SSttc3BuNExrVUVuejFSU2hhK2FJK2xESlcrYkZLZSt0Szdock1uSUVyTndpZlFUclpMZ09QYjFmcFYwSGViWHhqbytLVGlncGlUdlUyN2o0OFJEQ1l5OE1Vb0xOdGt3cm0zb1d5T2Q4QnRXMloxZkM4TjFseTJ3MzFCUVIweSswaEhrK1YxeDZ0Z0tZdFdZd3R2b1MwSzJseEtwTWVONVN3MWlNSDY0ZTRUaG9KdmdaUGZrNm92amF1QzNEN1VkdllsaEJyekVxOUdJQjZrUmdDakw2NVljS3BvNHkrbFNNUDh2YmVUd0o4aUxjbDM3a3VUYnVTVVFGanJ0UTJkeDI4clZLSEJTMVpGemZyV25XUjluZ3Q4dFI2NzR5dDVrd2tQU0tJOEJWS3ZlTld1OFUwZUtDRVlPRURsMGZ5eUxFVXhEdWdZN2hGVEpsVUFyalQ0M1BNSkJjR291eHZIUDBDY1pMMVVIbytnR0d2clpkZFdtcmtZa1hZa3Q5dmJtTjhDTlRRanZHanJMcnAxd0dyalVhaThWUGFtek1IVG9LR1RIR25EQXh1b3ZGVDJwc3pCMDZGK0NobnJ5THVsYTVqVmY2U2QvMkxUY25GQkZCWm5mbnU0YmdNbnllV0hQMk90ckVOcVJpUjZHejdKakl6cUpoSlVXNHR5Q1pFYm9scDAzWTNBeVhFamJldUdpOWZuU3pEb24wSk42c2V0ZDhrWmZKTmVFN1FIU1lSUlZZSlZsaldOeVFibFlQTkdEbmRoalRWYWNJUFNrQ2ZlS2xEY01RaUpCQUpncUZpK0d4NFhGb2VaendRMUM2YlJ0VzU5RVo2REFpTVB1b2VrWVBPanNrM0MwSlNXWmF6cGZFSWwrR1hDOGMyOHRRSHVPRFBzSXB2Sk5DV1p1dE8yVVg2aUt2ZFo4dEFnR0dNeWpaY0x3SmlrTE9nS0ZETnQwbzF4K3lPUVBGYTFham5abDRJRWQvbTBHNVR0WjBJaHZudlpqb2FzSzQxckQvM3d2NVlPSDdsOTloUFRSR3d0K3VDaFRoeXBYbnB4Qmp4STg0YkpwMFFGR3N4WWpNV1pYK0ZOQmczZVB2bnVMRmtxMWVQMTlUbkdJVGpKTXFPZGFvSFUxUlFNVmZFSW5XVHJzNVBMbGhBaXF1M0xqNnFtV2xGOHU1bEtwS21XcTdhbHhyMUZJSDR0MU5jOWdlc1poeUl2ckVUR2tBZkZNbi9TQ2ZvVklWQ2VSVzUxTmNjQTdHdms5UXlWQ05rZGtZeTJURWdXKzhDS1RLT0paYzNJc0lnaWxGeUgzek1rT0ZGTFpxR3FyZDZHTzBjczMzdWxWelB0bStnb2NNTjZTSllkbDZwRzhTcTg5VHJEMDJNTzEyUHBpWml2WGtRMkVtdGdnc3pDY0EwdS9jT3JQbEx2M2FuSG1Vb2ZDVG5QZ3hzNkFCWmlURU1yMlJkb2lyMVdnckdCb3NsSWxOUlZQZklrR1NxTzk2QUo1eE5FOWNWaGtBTEJlT2hSMEVibkpIakxuVnk3K2xvZDQwUzBBbHpPUEFlakN3S0Q5b2Fwd0xSeTdzck1takMzaTV2Y0Q5U2hybzFqN1NTbVFNdzVDS0lyeTNBZXBpQ2tGMERCNmJOREJQcGdROHE2SGoxZG9jM0FEWEF4S1JaZUE1dWxLakZsTW1aaXYySFI3a0UwWU5CQ05FZmlmK1hPVTBHMzY0SUlwRnRkOXF4SU9vejhDcDhydTNwK05VbDFWVlNXeUI4em9vRE00SVFMOUZCbEtVUHF3aURhVURwTDc3bG5pTFdQcFplbkxVY0p6U3ZZcGlCNEVNVTJTV2d4eGVlTitHUHdiNmZWTDlyK0lENm1mM29jZVR5c1BGK3BVcnEwVFZJdENQWUdaRUpEUllHV05qZGt0a0xZUTR3MjhEM2krZ2pwbHIrc2Vlc1VQNFVpc0dRamh6RXdqV3B0bDBGREtGdkU0TXBEME1WRTBOTmhhVld2d1dmeUJRSEwwZUlBaVdBeS9ncXU2akxQSUNZemNSb0NDRGRKbm1qL3dTQmhKa2ROVkdReENWMWovQjBkSGxjT2N3YnVTdU5nQUlzb1BnSWp5YVNsaUJHaFNBYys5Ykl0OEU4K3FvWDFseWVRb3Vnekp0MGZlejg4cytpTHcwMTJLU1pRSVlSTXRMc0Y5R2xWY3dZb01HdnkyejJ1K2t1TGRWVG4vb1NyTXF2N0xwenVET3VyOWpLWG05Y0xZRnc1NFlBc1BkaTRRUGdodkQyeUhWa1pxS2QzVytTalpPVUdrVmRJNjF0eU82d25SMmdoL3A5Y2dMd29TeFY5QkZMRFFNcWNMbS9UaVg2OVV4c0kyVDBHRm5TOC9kczNLYllBWVFQVmNGa0tvalVxSy85eFNPU3Bid21sYlFFZGl6akVDajRHZVRMQzZJQm15K0FJUEY2c1Y2amI2alJuTXRyVFI4a1B0cWw3cVpCRnIveVFKNjRMaDlkMGo3aWFSL2JhbFhkUHlGNngxUEZoRVVSdjJnNWRHbitzOG9mY0xid1cxTEpOSHVEeGU2L2ZCRGRFM01rMVlzWmdQTjZ5QmNFMlpteXJTV3NzTVVHdC9NaVArQmN6UjF6SEE3d3FaV0VjZU1UWGpGSUhYVlZJdjBMK092bmVFNDJhSHFLMkRmTUsrY3RXRFYwb0R6SnpabTJ1bFhFRXphY3Z0UXB6OVJoMTNySjJKQ3RTVmRMem5sRS9Bc21rTWw1MjBBdkRwRDBrOW9WSFRSaDVaTzNUeVd4Z2VPSmovSXBMMzZrZ24rTGRBZis4SXE1bnR4RTZLUEIrS1ovRjlDY09jMUhRdjNmNUZBajAwaW95dktMZHl3SmJ1UldrUDY0YWQzUnVKYmFMRTZBcFovWlJiR3R4OE5tVkZsNExWSU9xbnplWnhEL2VpaDZDRlJZd0xHMXJKcFlhYWpRcTVUQkQ5WjZpMFNKcDhzOXFIY3UwQnNYb0VpRit0MzcrUXNhaXhKOFRhNnFFMHp4MGt1OW43dFBlMDhWSUZWNmUxdTcwNDNuWEkxOWhyb2hKVlduVER3YmhIaGNMSW15YmUrM1EybzdIVSs1bFhRM3BucCtLa1Y2WlBmaWRCbWh3QzNCbWpXK05xSVdGa0JZYUJ1ZnpzM2kweHpzTW1teUVuc0g4V1UxVzlHanNiSHhGMEczejFJQTRzVlczbjZsWWlORWJ2WEY0eDh3YWlhby9veWZZejRvRGFSclU1NGl0RUdxbmxaRlc4c0pINFBuMndNMDVyS3djM2RRazlFWlQzNndGeDQyUTVueFlxY3cySEZZbmlyRmI5MUF1Z0JVbC9PQkdNaVUzTTYyMTEvTzUxbVJ2YytBbVAyUkE1OUw4b0ZnaDRBZ21WYkRWRFd2UjFKajJSazJkQ28vZ0Y0ZmQ0dFhpVGhsOThoRE9jaFVPd3QzT3NCOTRRdXNIQWNlZjBWRlJKRXVISGxuKytDU0c3M3NSeHo4c2VZRFJ3OFZPRk5JNHZXMHdMZktTSCswNVVhbXRCeTJUUU14NzJoYlpRVnhLVUR0QmNXOHg0Sk5lTlBYVnRsbjRZUm1pTkFra0pPbDRTY0xwZFJLT3NaejBQd1pIL1NIdk53VldMZXp5c0h0YWd6SmdNbDF3YUpubWg2cTdidnVCRkF2UGgrSzhSVHd4MmlpK3ZaSnhYK0ZMbmJoTk4xV0Q1dkV3T2FnVTEwU3htcCtxWlVsb1E3VzVhSWlFcGdvOGtacnpZbXZ5NG1vVGhSR3RTekpHeWFza2piNGZlSHhsd0ZCS1J5dlBUZDYyWmlYUDE4b21yN2t3azBxcWdWUTUxOFZBeWNYUThVaS92c1N6bTB1dEZBRVNSU0JCZXpFUndpWStycEZJYVBIM01UcGNMY3FnNG9kODNJUkNYS2Y5NzF3U3Z2NlFsUWVuNm12V0RUc1RmUE14UkpjRVhuamwrRVpVYmE1SWtjMlVrdThBaWpwbUxFTEhJTncyQytMVHQwZndkVHArOTZZOEd2alhHcU1xUi9xOU91UzUxa2g2NXl0RnZqV0Zoekd1cHdRSkZPU2doNzBQNUpiaFNqc2h4aUl4RU81ci9VVUt2WG4wNjZSM1kwVVBORk5PVGtXcU5TdUZpWWNIM1lRdVR0VHdadVlOOUg5R1FrYTY2Tk5ockFGVm8yMG1tZ0owUVMzdk8yeDN6Vmh1bjREWlIwbVpqeG1ZVHFXVU44dWY5SGdXam02SEFMcDZTdVNYU2Nzam5GNUpUUlpTRHVnd1JCNXYyK2QxcUlIS3dndmZGejdaMitxNzl6R0NWNWhMdkUrL2xCY2pvd2tPTnpPY000M2ZYV0lhZ1Evditqd002NmI4aVVJV1RUNzBzemFlU2JsZW1ieDhacldvU3VUQUhWbmVtckNXNmhjVHhPcWZaUkhOM0JXRzhsbUhqSFd0ZXdlOUVxdy9GZEpQWG1RZWFGOGlwd3liSFpOajM5UFJwNzdLNi9QKzFDVlJjSDlrZkJKYWI5K3RsOFoyTnQ3U2dTT2VkdExtUWpWeVh3WnhjYWtHSm1NWWxiYkl5T3pQQnZFQ1FMVEVFRzZRUmdpeFMwaXlBbEJpcFlWOHRIMFRmNy91NW5JZHNKYXhhNFYwQ2p0REVRMmQ2S3Z3bTNVcWR1U3Rqb3VSZldBeUQrQjZxdVR3bkQ2REpQVFdocTJsYk9hOVh3TFFtcFNBRHdBY3ptOHpaeVZhaDVOQmRtVTRkYTBJdzB0UXlYVnYzcGtMTFdidFlTY2xYWmhoeW0rWC9JUVdaaWkwbG8vNmk0V3B4MEdIRlhtZzRPTEYwQU0yZmR2MDAzd0xneE5pazJyZVRLine truncated
|
||||
@@ -0,0 +1,155 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import numpy as np
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
|
||||
class vip15(CtaTemplate):
|
||||
author = "quant789.com"
|
||||
|
||||
period = 5
|
||||
pds = 24
|
||||
trailing_stop_rate = 65
|
||||
lots = 1
|
||||
kg = 0
|
||||
parameters = ["period", "pds", "trailing_stop_rate", "lots"]
|
||||
variables = ["sdo_value", "lots", "barcout", "kg", "dd", "ss"]
|
||||
|
||||
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(300) # Adjust size as needed
|
||||
|
||||
self.sdo_array = 0
|
||||
self.barcout = 0
|
||||
|
||||
self.dd = 0
|
||||
self.ss = 0
|
||||
|
||||
self.over_bought = 40
|
||||
self.over_sold = -40
|
||||
self.lb_period = 50
|
||||
|
||||
self.highest_low_after_entry = 0
|
||||
self.lowest_high_after_entry = 0
|
||||
self.liq_ka = 1
|
||||
self.dliq_point = 0
|
||||
self.kliq_point = 0
|
||||
self.ent_bar = 0
|
||||
self.ent_price = 0
|
||||
|
||||
def calculate_sdo(self):
|
||||
if len(self.am.close_array) < self.lb_period:
|
||||
return []
|
||||
|
||||
close = self.am.close_array
|
||||
dist = np.abs(close - np.roll(close, self.period))
|
||||
lowest_dist = np.minimum.accumulate(dist[::-1])[::-1]
|
||||
highest_dist = np.maximum.accumulate(dist[::-1])[::-1]
|
||||
|
||||
# 避免除以零
|
||||
denom = highest_dist - lowest_dist
|
||||
denom[denom == 0] = 1e-10 # 将零值替换为一个很小的数
|
||||
dval = np.where(denom != 0, (dist - lowest_dist) / denom, 0)
|
||||
|
||||
ddval = np.zeros_like(dval)
|
||||
ddval[close > np.roll(close, self.period)] = dval[close > np.roll(close, self.period)]
|
||||
ddval[close < np.roll(close, self.period)] = -dval[close < np.roll(close, self.period)]
|
||||
|
||||
sdo = np.convolve(ddval, np.ones(self.pds), 'valid') / self.pds * 100
|
||||
return sdo
|
||||
|
||||
def on_init(self):
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(10)
|
||||
|
||||
def on_start(self):
|
||||
self.write_log("策略启动")
|
||||
|
||||
def on_stop(self):
|
||||
self.write_log("策略停止")
|
||||
|
||||
def on_tick(self, tick: TickData):
|
||||
self.bg.update_tick(tick)
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
self.am.update_bar(bar)
|
||||
if not self.am.inited:
|
||||
return
|
||||
|
||||
self.sdo_array = self.calculate_sdo()
|
||||
|
||||
cond1 = self.sdo_array[-2] > self.over_bought and self.sdo_array[-1] <= self.over_bought
|
||||
cond2 = self.sdo_array[-2] < self.over_sold and self.sdo_array[-1] >= self.over_sold
|
||||
|
||||
hh = self.am.high_array[-self.period:].max()
|
||||
ll = self.am.low_array[-self.period:].min()
|
||||
|
||||
if cond1==True or cond2==True:
|
||||
self.dd = hh
|
||||
self.ss = ll
|
||||
self.kg = 1
|
||||
self.barcout = 0
|
||||
|
||||
if self.kg > 0:
|
||||
self.barcout += 1
|
||||
|
||||
if self.barcout > self.pds:
|
||||
self.barcout = 0
|
||||
self.dd = 0
|
||||
self.ss = 0
|
||||
self.kg = 0
|
||||
|
||||
if self.kg > 0 and bar.high_price >= self.dd and self.dd>0 : #
|
||||
self.buy(bar.close_price, self.lots)
|
||||
self.kg = 0
|
||||
self.barcout = 0
|
||||
self.highest_low_after_entry = bar.close_price
|
||||
self.initialize_vars_on_entry(bar)
|
||||
if self.kg > 0 and bar.low_price <= self.ss and self.ss>0 : #
|
||||
self.short(bar.close_price, self.lots)
|
||||
self.kg = 0
|
||||
self.barcout = 0
|
||||
self.lowest_high_after_entry = bar.close_price
|
||||
self.initialize_vars_on_entry(bar)
|
||||
else:
|
||||
self.trailing_stop(bar)
|
||||
|
||||
self.put_event()
|
||||
|
||||
|
||||
|
||||
def trailing_stop(self, bar: BarData):
|
||||
if self.pos > 0:
|
||||
self.highest_low_after_entry = max(self.highest_low_after_entry, bar.low_price)
|
||||
self.dliq_point = self.highest_low_after_entry - (bar.open_price * self.trailing_stop_rate / 10000) * self.liq_ka
|
||||
if bar.low_price <= self.dliq_point and bar.datetime.timestamp() > self.ent_bar:
|
||||
self.sell(bar.close_price , abs(self.pos))
|
||||
elif self.pos < 0:
|
||||
self.lowest_high_after_entry = min(self.lowest_high_after_entry, bar.high_price)
|
||||
self.kliq_point = self.lowest_high_after_entry + (bar.open_price * self.trailing_stop_rate / 10000) * self.liq_ka
|
||||
if bar.high_price >= self.kliq_point and bar.datetime.timestamp() > self.ent_bar:
|
||||
self.cover(bar.close_price , abs(self.pos))
|
||||
|
||||
def initialize_vars_on_entry(self, bar: BarData):
|
||||
self.ent_bar = bar.datetime.timestamp()
|
||||
self.ent_price = bar.close_price
|
||||
self.liq_ka = 1
|
||||
|
||||
|
||||
def on_order(self, order: OrderData):
|
||||
pass
|
||||
|
||||
def on_stop_order(self, stop_order: StopOrder):
|
||||
pass
|
||||
@@ -0,0 +1,7 @@
|
||||
通过网盘分享的文件:VIP15参数文件.rar
|
||||
链接: https://pan.baidu.com/s/1liwdmvmGJ7wjHWVewZUUWA?pwd=7777 提取码: 7777
|
||||
--来自百度网盘超级会员v7的分享
|
||||
|
||||
如果过期了不能用了,联系小松鼠微信:viquant01
|
||||
|
||||
quant789.com可以使用参数选取工具
|
||||
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJWSVAxNuWVhuWTgeW3peS9nOWMuiIsImRlc2MiOiIiLCJkYXRhIjoidkkrbXNwbjRMa1VFbnoxUlNoYSthSStsREpXK2JGS2V1Yk4vRmpzY3FhcVVJaE5kUVVCbEV0WXFHbnJEOUk1VlZSNDE1RmJLZmhtbW1wd1hOTE0rUDQ1c0pZTmV4WmJXUnRZckFrOTJWbk9pRWU3L0xJSjkzYVk2SG5FUkxOWEFwcGRqNUJMQzFCRklkcjNJVXVXdUV0SEhHZ0ppazZ3Wi83K29BOEhHcmRGMFVUVnF4Zkh1Y1BtenlvVE1VVTBDMVFPdXJBLzBBTHB6ZVh4d2ZPcHB4Q29aNHFoTUFKOUtWQjJKL0V5NjcxUURaVkR5ZjFMckpNZHZLMVNod1V0V1JjMzYxcDFrZlo0TGZMVWV1K01yZVpNSkQwaWlQQVZTcjNqVnJ2Rk5IaWdoR0RoQTVkSDhzaXhGTVE3b0dPNFJVeVpWQUs0MCtOenpDUVhCcUxzYnh6OUFuR1M5VkI2UG9CaHI2MlhYVnBxNUdKRjJKTGZiMjVqZkFqVTBJN3hvNnk2NmRjQnE0MUdvdkZUMnBzekIwNkNoa3h4cHd3TWJxTHhVOXFiTXdkT2hmZ29aNjhpN3BXdVkxWCtrbmY5aTAzSnhRUlFXWjM1N3VHNERKOG5saHo5anJheERha1lrZWhzK3lZeU02aVlTVkZ1TGNnbVJHNkphZE4yTndNbHhJMjNyaG92WDUwc3c2SjlDVGVySHJYZkpHWHlUWGhPMEIwbUVVVldDVlpZMWpja0c1V0R6Umc1M1lZMDFXbkNEMHBBbjNpcFEzREVJaVFRQ1lLaFl2aHNlRnhhSG1jOEUwNEM1cnNjbEF0cTlvSVhwaFZyMW1MeEZWQWMrYWRQZ2dCUGdkZXpySEJZSEQwTEV2c2NwQlovRkVmZDVXL1loT0RQc0lwdkpOQ1d0ZmZIb2hBME9qQ3dYZ08rVlkxNm5oVSt6akdBM1VRYzRCbWlseS9kZHNocWJGaTcva1ZDSVZmUGxhSkoyU3FGQ3BBcE1WOGdhYU5xbDJXRmpSZGx3SDI0cHR6M2ZlWEcvS1lQTlFVeXltWGp4ajBpZUg2RlA3WXIyZDZ1ZDJLMnA0WUNJVXdNTGlKN2JHMDNnNUJIRGFzYWhJa1ZTdFVzVEdpRHJ4Z3B3dGhmMkNIZkRFa1ZOWEpxSlJUNDBEa0RpV2pHLzhtVmpRb0Z0UVFaSmMvYkI2cHNjNS9PcFlBbDE3Z3ZUc1JvWndkQ1JxalpGdFp0ZEo3TWhKaGs5d3hEYmVUZGoyY3JuT1I0and1bW9rR2JHdmh6Ly9jK0R0bGtwRGROdjNHYnF3TEdzSmlZUTJkM3ZEMk5hdVE1VXFkMlpZZEpHaUJOWWJCVHZwR3ZmclUyTFY1clpUS2lQU0N4R2J1RWdtZlBUc1R1R0RTa3YzblQ5MndxUTRGNlk1cXhVbndISEZHdXU2TVZnVE9taTRXL0x3UVMyZ1Zrc0JsU1REc2c2bnpOSmNRSzU4Mnkzb0lLekd3SUU4L3dBdmxENzJPZWdZTnBWZ2Z0VjQ0dUF5VHZpbWZFWmE2YzYyR1Z6cXo1MzdoZ1N1cFpsOEc2NlpYSEFjUkFScjJtTHZ5V0FIMWIzUWtXNWZSaUxkRHM3SU51TUhReFRJUnVNQUlwTU9qQ09vZ1ZRR2lqQlhHNGlpczV4M21TNUh4czRKQmpUeWxtWTRPRFM0OXdsRHZvZGIxYWV0WkFMc0h6SmtNRWZtSkRLaitRNFZSS2twNDdEcHB4M3lEeEgxeWxHWlVKVERNZGhldDQ2elpvbEpnSkJ5VkNIVDMvQ002bFVISmN6TVVYcWdTS2h0bEIrNnE5cWJxMGl6ZnlCZVErTDdud1FVbm9UeVdiQjRnVzJNSk1BTXNkWVhSbGZBbTFYeTdkdnZTR3RMSWJNK2Z1UmtvMHMwY2o2OVVYb3gvQy9PMEltNEhUYlFYRFNmM3FPMkNqUkFsWGF4a1c1ckNsUlBQckpuSmcxOUVyS3RyaUM4SFVNOURjekxIQ2FrNExUaFJiRXJraFVRYXBxcGo4S0daSFR6aGhWbTFMU2NXMDA5alBDdDVVbWJndFduZlZmQVNOK0JaSVBzeXdEMEZMbkRQeGJONm1hOXcwck5pUEVORmxaMzNQQTRXZ2wwOGUyTkVTUU5MK0hDMTI4S3NCSGFGMU8rcTdadk0yaXoyU1MzenB4MVZUOHJ6V1RlaXRIanZ6dFgxZFhRWHBjVFlQb3hkeXdyclUxeitYc3JSczdFUWgzaS95WFZmMG5VMlc4K3FRRUF0RnRkVmhFdkZtL3lXTWRESWVzck5QbStMRlhGSFA4SURaOGJiWThuWTNRUEhSQUNtYkhja1Y5WWJqZ1hiczE4ZU1qOHhoMHVlaWZ4ck1odjRpdXdpdUJqaHA5OE50a3dGZ3kvaWQxMUsyMnJHS1gzZitMTkVNK3pqMjJkVlhuVmZuZm5XU2ZtS21WZEdKS2EzL1RNYmQ3cXdUNXVkWnRWajFuYjJFWDVLbVk5Vm1WamxFS0dkemlUc1R5MTV3bW5EeUpZVkFjNUF4LzFublpDazN5L0pDNklKYlVIekpzTTRUZUJ1VFMwbmFYdEROSHpva0FKUG04ZFU0UEJwUTJjM3kvd0svL1RVa0kyNHE4aHFlckE1NHRNbmlYYXFJUkZzQ1pnSW14VG1uY3RmOGRRaTNHQ3lveTdVN0FMWFJmcG40cStoUjJtT05UeFpMWkNTUGVpWEsxTElnREo3eFkrdnZoY3BPSk9WaVhpdi9aTGRmaDY2VVhTbGppamFaOXhOVDhNSTVkaUhmWEZ2U004V2RNS3NVZURWOEdLMTRUTFNMYmZNSjhTb2oyVlhVbG1tVjd4cm9waEVFSkVyYmtPQkhXZUM4Q1VlcU1WUkQ0Wlh2c2lydy9NOFZzd0lvMGZLQk51dlREUDdVM0QraUlEMHNFQXBZalFESE9hYjRQQmxzcERLOUFJd05hUFZZTXBRc3FOSzJxTENrdWRCS09PWU16cUNSVHdoOU91ZkcyMFhpcFpMb0JlWk9Rc1FRendLUG9iQ1FPOThjYloyc3FOMTRTZFNUc3BReU1HTW1jQUVwVlprZGNuYXU4b0dBVHpvL0V5VUdnU1ZOUE92N1lJd21lUndWWGpmSmlzUERFU29lWmRNbkJXdms1dTdWMldwS09PemovMXMrWmZFaUNPMDVPN0E2SllvVnVNNGZBVHVtR0RmakJydk5ZRm9vVHlxVHFDQWcrTUFXYjk0NnlMdzJ5ZStmN2lmQ1dseG92ZjZvU1lWbW8ySmNNcFlaQmNmSUlwdjY1TFlYVkRiUTM3ZHkzNG0wOGMzTWtEQXBnVEZZaFBHMDZqdzRvSzdQRzFDeXZvTUliRG04RmxESGM1eEM1NHlUY1djYzRHcDJna1JXaExMZDlkZzBpTFNkcWVYeDE3ajVaUVQ3cFRkVGVzcHZ2R1hxa25IUFBzWmkybUlEUEZSeTFGYUF2ZWRGVUtOem13eEswY0dIcnhUanlGZWRNWjk2V1ZxS1NnSUIzSXpWbi9QdVNqbEEwREw4RTZEeXNOUlBSVU5MMmtCR3lYeUU1cUJqMmhFSEl1L2Y1N0tYOUgxbnZoc0VxUGpocnBNM3V2YzZDdjJsTEdlemE1NW84QUNGREkwVEpRVU5VOUhCTU52N2NuNzBzTDBnNWlNdzM1T1Q0SUFRYXNCb0VrWXdqeVZjc2JKVnpWM2k4LzZxVm4ycXdKeHdGZWJaUlUxUkk1WjRydHNBZ2xhaW5iWlhBaWpPbXdjS2RKOXVuTHJLcTFqQ1Mwc1ZLMDViUHNpN3ZTSEVrSXM3QmVJeTRKT2Vobjh5U0tMV2xlQ3ovTi9CTGJHZlZXV29uVTc3ZG9qbCt6a3RGdkY0RElFSmNpVmorL3drWlY2OWE3L25kT0ZOTittQlRzcUZHVE8zVEh4SXM1RFljM0JRdWlmZEhnaUU0aDVxTGdGcEdSTWtCajR4c2JQRmh2STRtZDYrMEU2SllOVHQ0RXFhUEZBcm92cGlqelhab3lidjNMd2FSTHg0MXpGaFFwRVZlT3hXd2pqUnNMVTFlYUxybFhIMmh6R1VhS0FOamQ1eEJEUzNaZkZ3OE1QU1hoc1pMVDlOcWUvOFFGcExWeXVXelNmeTBEb3pTK0djUW1GaW1PeFJybEpEMVNzakJsM0dZUHdNUjh4MVVSeUh4SkZrVEc4WDg2eUJxUHc1RGdxWEJhOEt2LzU2ME9GUDdoS0VycjVIV2ZPVVlCVGhHanBMR1U3djgvblJvVDlHMllLYkZQbzVGWUc2TjBhNGxuNDU5TzZsZTc2RjVDM0w5MDA5eFgrMGdNdkhNRHhsb3VtcTAyVlVhWHJFQjdwZkZxS2RkNmlDYThqN05oRTh4MmNrZExLTVJ4d3c5UWxPZG53QkltZ3hHN2lpSnlPZm1nTXRHY1l5LzFQOE9QRW9nTlVLczhUMHcwTFQ3YmpQUzU2b3dFSm5JRWdqcGVON0YwSjFia2xpSnMxeFFLaXlVUTQ2NG9KWW1WdGNsc0VKK0VtY1FSSzVudUFxbENlby9uRWpXMWkyMnBZd29MNnRJdjNwWGhWcDV2RXZwRWFWd1ZSVVdFVnMrNHVOWC81MmdJM3JTbm40T3FiQk9XbEpQQk1tY3Y1ck5XOERSWkMxWG9zV1dlQ1hBL1RNbnR3ZUNvejdZU0gyUkMyYVAyRGpCZEtqcDZQeWdTNk1qcUQwV0M0ZG5vRGUrSmtvVzJwK0VXaVFvcnZGT1JMalZTMUtMTTV6b0ZqRjc0NnlaQkhDVnh2bGdCWDJQOHp6Nkh2MU11M1FYK0duZjh5R2NpdTBHeXJZTkNvc1ZPRDhRcFBCWXIrbXk1dTFXRXhLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJWSVAxNuiCoeaMh+W3peS9nOWMuiIsImRlc2MiOiIiLCJkYXRhIjoidkkrbXNwbjRMa1VFbnoxUlNoYSthSStsREpXK2JGS2V1Yk4vRmpzY3FhcGlwM1VlS3NxUW10WXFHbnJEOUk1VlZSNDE1RmJLZmhtbW1wd1hOTE0rUDQ1c0pZTmV4WmJXUnRZckFrOTJWbk9pRWU3L0xJSjkzYVk2SG5FUkxOWEFwcGRqNUJMQzFCRklkcjNJVXVXdUV0SEhHZ0ppazZ3Wi83K29BOEhHcmRGMFVUVnF4Zkh1Y1BtenlvVE1VVTBDMVFPdXJBLzBBTHB6ZVh4d2ZPcHB4Q29aNHFoTUFKOUtiWklBUHc5TFpHZkNDdGZ4UFRhZFk4ZHZLMVNod1V0V1JjMzYxcDFrZlo0TGZMVWV1K01yZVpNSkQwaWlQQVZTcjNqVnJ2Rk5IaWdoR0RoQTVkSDhzaXhGTVE3b0dPNFJVeVpWQUs0MCtOenpDUVhCcUxzYnh6OUFuR1M5VkI2UG9CaHI2MlhYVnBxNUdKRjJKTGZiMjVqZkFqVTBJN3hvNnk2NmRjQnE0MUdvdkZUMnBzekIwNkNoa3h4cHd3TWJxTHhVOXFiTXdkT2hmZ29aNjhpN3BXdVkxWCtrbmY5aTAzSnhRUlFXWjM1N3VHNERKOG5saHo5anJheERha1lrZWhzK3lZeU02aVlTVkZ1TGNnbVJHNkphZE4yTndNbHhJMjNyaG92WDUwc3c2SjlDVGVySHJYZkpHWHlUWGhPMEIwbUVVVldDVlpZMWpja0c1V0R6Umc1M1lZMDFXbkNEMHBBbjNpcFEzREVJaVFRQ1lLaFl2aHNlRnhhSG1jOEViVFlOL3FsVjNKYnV3KzZyanBUWm5OVjJBSDFWT2RscWdCUGdkZXpySEJiQm1ZandUQ3A5eHFRbk8zZWFVcGhnT0RQc0lwdkpOQ1d0ZmZIb2hBME9qQ3dYZ08rVlkxNm5oVSt6akdBM1VRY0JaNnpaaWZWckxOZ1RrdXJEQUdYbUxIdndKOERaMWtUWVorc3VkY0RjcU5ZekQvSkVnUE0zSlhDSXprTTlvVlkxaUNteUJiWWFNcWYrc0xXSS9FNHk0eVVkaStYQ2RDQTZ2WnAza0JtRWdDcFpFakZmbFJPbkllZ1FuNTh4MUYrQnVQb3U3UURrbGxUMkozNUMreWZYTnJSa2I2Nmd6QnU5aEJCMXpTb3QxWWhldGU5dUt0YnY0UUxuS1VjTDJjb3c1K0xoRy9rdm85anVxby8xR280NXJyeXpiakMyMDRlYUR1V3RQb0NNSzJLZjF1Yy9PcTdiK1RGeEhHaWZFakNiWGZ5K2l4Q2Q5eEpRb296eDJHYlo5aGlBTm1uRUh0bHZ4dU9aQjB0Q00yNmcrT2MxWUpQNWhSRzMxY3M2cWNZbXYvV3lJNG1IOUFqd2NZaHhyTWRBVEJTRFN6YVp0b2pQcEdvK1djZEdmNDhzVHhISWtlY0dwMTlJcXBsd2pQOVdFMHBNTWdrQVRqeWdFR1NEOHJGTzNtR09jNWw1L0JtaWJPQ0Q0WUM0V1Zvc3hKSUdzVFVBTGM4REhOajBjY2lhLzRRdFd4Tmh0cTRLOWh1R0FRbTlrTkszQkQ2NWVIa3BZMk1wRGFqK2VPNTlMS0NOZTl4eEFIZTVxdVhqNGVxQktKM29mT2l5cSs2UnFhMFJNZkZkMmZFVFVXelBiTE8zNTlkcCs3VjByRHpuS051eVBuUTRyd1Rid0I0eFArNkpJbWRnczNibmg3WEJJNTRtc1pVN2k4V01wSW93UjZaVERNZGhldDQ2elZYN2Q5VE9CR01EM1FsdjU4Ykd1ZEVZOTRuS2FPbVFZYVo2b1pqc21FcjdSY3R3U1ZzeWdvOUl4T3I4QzNCRHVRVzJNSk1BTXNkWVhSbGZBbTFYeTdlbXZZMTJpcXorVFFJeTVNWUE2VlJBQmxQZEx0S29TeWtyZHZ0dWxIM3VmZm1QOFh4NjkyWngrRnFjcGI5dnF0SThINVdydUlReENtbWpVdWdVdFlXdDJPZ3RsMThuWkF5NXp6cTc5WUVXYUlnNkZvVFJTRGpMQnU2S1pSaXRmaTJ0NlBnbWh4T2t6RTFhRjhSeHNHYjFqNjR1eEdRSkg5c2ZWNjNqRlZHZng4U1ZkZ2ZSUUV4ZGxVQzdLbWxXMEZ0Yk83ZHh6YVFNdDViSytxQ0ZuRFNQQkROOFhDbEszK2NNbUx1aDloUlZYam4xZU16cWorN0tqMFBFVG1xUnh0SEtZWlY1Zk1YUW8xbXdHd3hlZy9OU0VDS1V6d1VuaXhlRklrQTB0OEQ5TDNNM2s2NExCWTYzNHU5aURGbS9CLy90OVlsVUppcWpMNUZsQWpoYjJhaDhuYkllYjdqSS8ralh6RGRDQlA5OC85THdRR285SS81MkllUEN4aWZCOU9IdlBsY0VSUUlvY1N6dEpMM1AyQ3FybEt1VVFyVVh0WXl5SHU4QzZUdU9XS01EWTd1b1g2NHZKUzJBczYvMFNiaU51VHZjcUNTWWxOdTlvMVNBY004R2NKQ1QxRVpZVjYwS0NxS3RCeEVRSHRDTjk0NjBzYTB4UktYblYvL084TWVJazcvM2lRQ0F6R1BYa1YxUDYvRE9GNHhvc1drVmNhajV5MVlOWFNnUE1nQ1REbDJuVmxOam1YYkgzOXFDMWNCVjM0c0lJM0ZwN09wMHpFaFRKQ0RQcWRSaFZpREpkWVNqTXdWSGI5Y0VUcC9yK2tiMTRETnlQMnZWTTZQYytvRDBDckVuVENVaTg4MUZzUkxKRTJlR1hOdS9KVmVJeGVGOHBZZmxZcFpxdm5DeDJkYm41NjViNnVFL2JqVmplb2dFZjRYRi9SM3ZVOVIzWlRWbDF2MSs3S3M1WHNkTU1BVElUaGp2bk5WSXd5OExaWGNyOTF0dG5NRFRQTURGUStKWXNpSFJYeElGcGhQU0U5K3dZeGFzQ1g1czJnWnhkeStESnVNWk1QN0g1TWdBQ2hLcnJwZy96cGd4SWwrU2xBd2NhbnJsdGNrZmNEUDFrNnd4aGY4SkNDRGFlaWIrWkF1ZW9odzZZT1FJaS9XYmgzbzV3dmxHaGNIa1N5cTNWMlZweE0yaGxCbGdDQWpQV081cmE4U21FVGVITjc3Y1BDM0VOaXA4OU5QQTJhTzVLc3UvTG5lSTYvcXVkaXZOdC9HWVEzeW8zWXEva3ZsL3lFbmR1VzA1QjQvL2NyMGM1bXdoMm5aeFBEU3JWbWtWclFGRmxjYURHbnVHWEwrUU45NWVCbmFJenA5WjhNYVE2OXlFZEdWanc3UWk3eWd3RUJzRGhRenFMQ0dRZENhNTdRbHkwcUpqaWt0bVdRQjJjZE5ROUorQjk5Y1l2cmxaaUhHaEdDNTUwYjkxVEtyVEM1UGxuU2RnYVc5QXR3M0lIc1BQeUxZZnB4cTVQV2RWSkIreFRybko4ajJ4dWFmWi9BR20rejZ3ZlVWWDQ4UzJPYXlNU0ZHblJmcjVaUmE5a1pUSExzYVp0alN1ZlhtK1ZBM1JnaTU0M3dScE9FV2tJYnBaY3AvRDVRTlArMTQ2K0ludWhkNjVzS0lwSWtaejhVajlJelcxRkhsRWRPbzFHc2JFTmlPc3dOVExQK3NOUjdoVG5nSStOOXlJYmhtY3hSUTRzcjFBTlg1UGpSMHBTL1BrZWFrRm9hU0ZHQWlQcWt2b2R3bVBrVEs3ak9NdEhaNFFDd3JJcEFqeFBDcDZFckhLb0VwYS9Mcy9qdDRxcFhCNkNTU0pZelNzVWVlUWk2RURSQVdUZ0RXTzZBOFJja1ZqaVozc21wdmJiR1NYTDY3RGJFNXFUamZmL0ZubFhEWkVJVzV0MG94NVVHWWQ3T2tQQ29DaEdNQmRDeUNZVzgxWFFUdzRXMVcxRFVnVnpxZVIzMFBQSXo2cXVCVW93NWZITkVOS3BMYjkvSG9oRU1QWndzcURQemRHZVpLZk94STJjODNUN3Yvc0FzRjByMndLNVhOa1lmZXpaR2MzUEFGZ1ozdTZlbTI0RG1hN21GVzlDVUVySmNIMlhua1BXdkRoQUJRalUvaGJGZ2s2WndZeERPV2E2UFFmQnhXcVNrR2dqU2phbDZhQlpoWS80MzF2YkN1UEx2YUNZUWtEWXZJdkVRcldjbS9KakRWZmtxelhOOUxxblp4a09VVnVEZ2J1RUpybm0weDcwOUdwQXAxSnRqUDIvS2VzWmtzdEgwVWpDUEVhaGk4WGN3am44UC9GNlZxemFJeWZjeTA3bkhTTU5ZZitPdDJ2YUVVTDIzSGZ3VFpGUUVtdVdBYzF2Q2xZTERFN0lXN2NBMjNzYzdXUnJ4dk9CeVlXMks0SGMxRzQybE5UenJjc1lyeGtUSDgxVURqbE8xSVM2WFVVUW5jemlVbVYvbDZlUGlkU04xbUtBK1YrcVlTVjlDL2cvUUc0dXByVUhwMHRxQXdpY2JNQW9uSkd6YmVBODlwSXNhN2J2SWdPc0t4UjB5VktDNU41NmtMN2ZOazJ1S1VJazJyZmNjNjhhbWpmdS9KY0h2N3U2SWdsc1JFOEZBWHJabXBvQXNjamNiWTZZZ3I5V3B6Q2NHVWZIeDlnV0ZiczNsUjc4U0k4YmRTN21tRHVBOUEyWEkzU3BrdVd4emlrM0NFZ3lQT3BPNEh2dnRWb1kzWVE0M3lPazhHU1kwb25BazB4WlEyY1Y5S3pFV0xJMnVKQ3AwSjg5VVpibHh6a2FIUGZKaFZML1pPbVdneVFrdTJER1BFYndHRkozaGZIQVlxcWlad1RkMzlEYUJ5dWlXeEljRDJFYWtldloySEgrNDFFL1ltdkplRkdwNXVZZnBZeXk5OTRXTzFTNzNJanVld3BoL1k5empBK1gzN1l3RmREd0NLine truncated
|
||||
@@ -0,0 +1,226 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
class vip16(CtaTemplate):
|
||||
"""
|
||||
自适应高斯移动平均策略
|
||||
"""
|
||||
author = "Your Name"
|
||||
|
||||
# 策略参数
|
||||
length = 65 # 计算周期
|
||||
ends = 25 # 计算结束点
|
||||
x = 0.9 # 自适应系数
|
||||
trailing_stop_rate = 45 # 跟踪止损率
|
||||
lots = 1 # 交易手数
|
||||
|
||||
# 策略变量
|
||||
volatility_period = 20 # 自适应周期
|
||||
volatility_std = 1.0 # 波动率标准差
|
||||
adaptive = True # 是否自适应
|
||||
current_agma=[]
|
||||
|
||||
|
||||
parameters = ["length", "ends", "x", "trailing_stop_rate", "lots"]
|
||||
variables = ["volatility_period", "volatility_std", "adaptive"]
|
||||
|
||||
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(size=100) # 确保大小足够存储所需数据
|
||||
|
||||
# 初始化变量
|
||||
self.agma = 0
|
||||
self.score = 0
|
||||
self.re_score=0
|
||||
self.long_cout = 0
|
||||
self.short_cout = 0
|
||||
self.total_l = 0
|
||||
self.total_s = 0
|
||||
self.loss_long_x = 0.1
|
||||
self.loss_short_x = 0.1
|
||||
self.highest_low_after_entry = 0
|
||||
self.lowest_high_after_entry = 0
|
||||
self.ent_bar = 0
|
||||
self.current_bar = 0
|
||||
self.kg = 0
|
||||
self.Long_jiasu_kg=False
|
||||
self.Short_jiasu_kg=False
|
||||
|
||||
def calculate_agma(self):
|
||||
"""计算自适应高斯移动平均"""
|
||||
if not self.am.inited:
|
||||
return 0
|
||||
|
||||
# 计算标准差
|
||||
if self.adaptive:
|
||||
sigma = self.am.std(self.volatility_period)
|
||||
else:
|
||||
sigma = self.volatility_std
|
||||
|
||||
sum_weights = 0
|
||||
agma = 0
|
||||
|
||||
for i in range(self.length):
|
||||
# 计算高斯权重
|
||||
weight = np.exp(-((i - (self.length - 1)) / (2 * sigma)) ** 2 / 2)
|
||||
|
||||
# 计算最高价和最低价的和
|
||||
high = self.am.high_array[-(i+1)]
|
||||
low = self.am.low_array[-(i+1)]
|
||||
value = high + low
|
||||
|
||||
agma += value * weight
|
||||
sum_weights += weight
|
||||
|
||||
return (agma / sum_weights) / 2
|
||||
|
||||
def calculate_score(self):
|
||||
"""计算信号得分"""
|
||||
if not self.am.inited:
|
||||
return 0, 0,0,0
|
||||
|
||||
re_score=self.score
|
||||
score = 0
|
||||
long_cout = 0
|
||||
short_cout = 0
|
||||
|
||||
self.current_agma.append(self.calculate_agma())
|
||||
|
||||
if len(self.current_agma)<self.ends + 1:
|
||||
|
||||
return score, long_cout, short_cout,re_score
|
||||
|
||||
for j in range(1, self.ends + 1):
|
||||
# 计算AGMA比较得分
|
||||
if self.current_agma[-1] > self.current_agma[-j]:
|
||||
score += 1
|
||||
else:
|
||||
score -= 1
|
||||
|
||||
# 计算多空累计得分
|
||||
if self.am.close_array[-j] > self.am.open_array[-j]:
|
||||
long_cout += abs(self.am.close_array[-j] - self.am.open_array[-j])
|
||||
else:
|
||||
short_cout += abs(self.am.open_array[-j] - self.am.close_array[-j])
|
||||
|
||||
return score, long_cout, short_cout,re_score
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
Callback when strategy is inited.
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(10)
|
||||
|
||||
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)
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
"""
|
||||
Callback of new bar data update.
|
||||
"""
|
||||
self.am.update_bar(bar)
|
||||
if not self.am.inited:
|
||||
return
|
||||
|
||||
# 更新当前bar计数
|
||||
self.current_bar += 1
|
||||
|
||||
# 计算信号相关指标
|
||||
self.score, self.long_cout, self.short_cout,self.re_score = self.calculate_score()
|
||||
|
||||
if self.long_cout + self.short_cout > 0:
|
||||
self.total_l = self.long_cout / (self.long_cout + self.short_cout)
|
||||
self.total_s = self.short_cout / (self.long_cout + self.short_cout)
|
||||
|
||||
# 生成交易信号
|
||||
long_signal = self.score > 20 and self.re_score <= 20 # 上穿20
|
||||
short_signal = self.score < -20 and self.re_score >= -20 # 下穿-20
|
||||
|
||||
if long_signal:
|
||||
self.kg = 1
|
||||
elif short_signal:
|
||||
self.kg = -1
|
||||
|
||||
# 计算过去ends周期的最高最低价
|
||||
hhv = self.am.high_array[-self.ends:].max()
|
||||
llv = self.am.low_array[-self.ends:].min()
|
||||
|
||||
# 入场逻辑
|
||||
if self.kg == 1 and bar.high_price >= hhv and not self.pos:
|
||||
self.buy(max(bar.open_price, hhv), self.lots)
|
||||
self.highest_low_after_entry = max(bar.open_price, hhv)
|
||||
self.ent_bar = self.current_bar
|
||||
|
||||
elif self.kg == -1 and bar.low_price <= llv and not self.pos:
|
||||
self.short(min(bar.open_price, llv), self.lots)
|
||||
self.lowest_high_after_entry = min(bar.open_price, llv)
|
||||
self.ent_bar = self.current_bar
|
||||
|
||||
# 更新止损系数
|
||||
if not self.pos:
|
||||
self.loss_long_x = 1
|
||||
self.loss_short_x = 1
|
||||
self.Long_jiasu_kg=False
|
||||
self.Short_jiasu_kg=False
|
||||
else:
|
||||
if self.total_l >= self.x:
|
||||
if self.Long_jiasu_kg==False:
|
||||
self.loss_long_x=self.x
|
||||
self.Long_jiasu_kg=True
|
||||
else:
|
||||
self.loss_long_x = max(self.loss_long_x, self.total_l)
|
||||
if self.total_s >= self.x:
|
||||
if self.Short_jiasu_kg==False:
|
||||
self.loss_short_x=self.x
|
||||
self.Short_jiasu_kg=True
|
||||
else:
|
||||
self.loss_short_x = max(self.loss_short_x, self.total_s)
|
||||
|
||||
# 更新跟踪止损价位
|
||||
if self.pos > 0:
|
||||
if self.current_bar > self.ent_bar:
|
||||
self.highest_low_after_entry = max(self.highest_low_after_entry, bar.low_price)
|
||||
dliq_point = self.highest_low_after_entry - (bar.open_price * self.trailing_stop_rate/1000) * 1 if self.loss_long_x==1 else 1 - self.loss_long_x
|
||||
|
||||
if bar.low_price <= dliq_point:
|
||||
self.sell(min(bar.open_price, dliq_point), abs(self.pos))
|
||||
|
||||
elif self.pos < 0:
|
||||
if self.current_bar > self.ent_bar:
|
||||
self.lowest_high_after_entry = min(self.lowest_high_after_entry, bar.high_price)
|
||||
kliq_point = self.lowest_high_after_entry + (bar.open_price * self.trailing_stop_rate/1000) * 1 if self.loss_short_x==1 else 1 - self.loss_short_x
|
||||
|
||||
if bar.high_price >= kliq_point:
|
||||
self.cover(max(bar.open_price, kliq_point), abs(self.pos))
|
||||
|
||||
self.put_event()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
通过网盘分享的文件:VIP15参数文件.rar
|
||||
链接: https://pan.baidu.com/s/1liwdmvmGJ7wjHWVewZUUWA?pwd=7777 提取码: 7777
|
||||
--来自百度网盘超级会员v7的分享
|
||||
|
||||
如果过期了不能用了,联系小松鼠微信:viquant01
|
||||
|
||||
quant789.com可以使用参数选取工具
|
||||
|
After Width: | Height: | Size: 701 KiB |
@@ -0,0 +1,65 @@
|
||||
### 自适应高斯移动平均策略 (VIP16) 介绍
|
||||
|
||||
#### 策略概述
|
||||
|
||||
自适应高斯移动平均策略 (VIP16) 是一种基于市场波动性和价格行为的量化交易策略。该策略通过计算自适应高斯移动平均 (AGMA) 来识别市场的趋势,并结合信号得分 (Score) 来生成交易信号。策略的核心思想是通过动态调整标准差 (Sigma) 来适应市场的波动性,从而提高策略的适应性和稳定性。
|
||||
|
||||
#### 策略原理
|
||||
|
||||
1. **自适应高斯移动平均 (AGMA) 计算**:
|
||||
- 使用自适应的标准差 Sigma 来计算高斯移动平均。标准差可以根据市场波动性自适应调整,或者使用固定的标准差。
|
||||
- 通过循环计算每个周期的权重和加权平均值,得到自适应高斯移动平均 AGMA。
|
||||
|
||||
2. **信号得分 (Score) 计算**:
|
||||
- 通过比较当前 AGMA 与过去 Ends 个周期的 AGMA 值,计算信号得分 Score。得分越高表示多头信号越强,得分越低表示空头信号越强。
|
||||
- 同时计算多头和空头的累计得分 LongCout 和 ShortCout,用于后续的入场和出场逻辑。
|
||||
|
||||
3. **交易信号生成**:
|
||||
- 当 Score 上穿 20 时,生成多头信号 LongSignal。
|
||||
- 当 Score 下穿 -20 时,生成空头信号 ShortSignal。
|
||||
|
||||
4. **入场条件**:
|
||||
- 当多头信号 LongSignal 为真且当前价格高于过去 Ends 个周期的最高价 HHV,并且当前没有多头持仓时,买入。
|
||||
- 当空头信号 ShortSignal 为真且当前价格低于过去 Ends 个周期的最低价 LLV,并且当前没有空头持仓时,卖出。
|
||||
|
||||
5. **跟踪止损逻辑**:
|
||||
- 根据持仓状态和市场波动性,动态调整跟踪止损点 DliqPoint 和 KliqPoint。
|
||||
- 当多头持仓时,如果价格低于跟踪止损点 DliqPoint,则卖出平仓。
|
||||
- 当空头持仓时,如果价格高于跟踪止损点 KliqPoint,则买入平仓。
|
||||
|
||||
6. **图形绘制**:
|
||||
- 使用 plt0 和 plt1 绘制多头和空头的跟踪止损点。
|
||||
- 使用 plt2 绘制信号得分 Score。
|
||||
|
||||
#### 策略参数
|
||||
|
||||
- **Length**:计算周期,用于计算自适应高斯移动平均。
|
||||
- **Ends**:计算结束点,用于计算信号得分。
|
||||
- **X**:自适应系数,用于调整标准差。
|
||||
- **TrailingStopRate**:跟踪止损率,用于计算跟踪止损点。
|
||||
- **Fund**:投入保证金,用于计算交易手数。
|
||||
|
||||
#### 策略优势
|
||||
|
||||
- **自适应性**:通过动态调整标准差 Sigma,策略能够适应不同市场环境下的波动性。
|
||||
- **稳定性**:结合信号得分和跟踪止损逻辑,策略能够在不同市场条件下保持稳定的盈利能力。
|
||||
- **灵活性**:策略支持多头和空头交易,能够在不同市场趋势下进行交易。
|
||||
|
||||
#### 策略应用
|
||||
|
||||
自适应高斯移动平均策略 (VIP16) 适用于多种交易品种和时间周期,特别适合追求稳定盈利的量化交易者。通过合理的参数设置和风险管理,该策略能够在不同市场环境下实现稳定的收益。
|
||||
|
||||
#### 风险提示
|
||||
|
||||
量化交易策略存在一定的风险,包括但不限于市场风险、模型风险和执行风险。建议用户在使用该策略前进行充分的历史回测和模拟交易,并根据自身风险承受能力进行调整。
|
||||
|
||||
#### 变量介绍
|
||||
|
||||
- **losslongX**:多头止损系数,用于动态调整多头持仓的跟踪止损点。该系数根据多头累计得分 totalL 进行更新,确保在多头信号较强时,止损点能够更紧密地跟随价格。
|
||||
- **lossShortX**:空头止损系数,用于动态调整空头持仓的跟踪止损点。该系数根据空头累计得分 totalS 进行更新,确保在空头信号较强时,止损点能够更紧密地跟随价格。
|
||||
|
||||
这两个变量的引入使得策略能够根据市场信号的强弱动态调整止损点,从而更好地控制风险和保护利润。
|
||||
|
||||
---
|
||||
|
||||
由 Ai 生成的内容仅作为学习参考,不能保证正确性,不构成任何投资意见,风险自负。
|
||||
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJWSVAxN+W3peS9nOWMuijlpJrmjIfmoIflhbHmjK/miZPliIZf5aSa56m65YiG56a7KSIsImRlc2MiOiIiLCJkYXRhIjoidkkrbXNwbjRMa1VFbnoxUlNoYSthSStsREpXK2JGS2VZcWtzZTdJNmxjMG9kbGFqaHVjbFJOcjBtdWUySlRiZ0tDN2YwY0RSWCtZWEdqZXYweGdPVEo3SHFWR0JMRGNQNmNZUFQ1VitSd3RjWUpxSXllbFhEZEJJNmhYQkpwVDVrN0hEQ3VWNC9MYTVGQjRka2RuY0VBOVV0NXg1YU9IZGdhbll5KzlXc3dCSk52c0FqU1N0ZDlzNUtOeU5XN0sySU13aWM0QVZwbXo0OVpyREdpYU9rVFQ5bkpyNmZ4R3AzYjZpb0ZPbUtsTXNaOUdaT2UzZkd2bW9UK2poZDg2USt6NHkyVVA1ZVVMazYxM1NOWVJINmJZekQzL0wycGNMYi90T2d0QnhRTW14VzdIU3JjWXlrZ1FLTmoxU1gxNDVGYXJtRFp6SzhJRksyak1uaW9KbWRLaTZZM3RGdkQzYzlzV0JsK3BnQWlrNUtFZHUrYVkwbkczWld2OEFWV0Z2TUdtN2t0V0FQSHA5b21LcUxRMFUvM1M4V1FmUlJWMUNIOFB3Y1FvK3NDdUVNNDcveE5XWTZVRXRaY1BOVEY2SW1yeXB1cnM5cm9iekxvWG4zVmxlRXdtd1dKSlV3eWlKbGVvSXE2bWo3VVdtbDJQa0VzTFVFU2JNWmdPV2FyMS9kNTlaYXhUMEZnU1ZGVWJCZjA2Njg3ZWlPTUNHQmc5SE13MnAwcTNvL1JZZGpDTlNQTnZieksyU0VZRWdUWCtwTm1kalN3VVg0b2cxQnRLV2pMWmMydUJFcEpPVnI5TmNnTUM3OTlYRWwzK1VzR2pYN0FqZ0d4VWhDMG5sUlBRZEh2UVhjb2RLRUR3NWZKSTlwRWVBaS9MbVFWMVZzL3M4Zkk3ZjdoUW1OVk81bEVvY2M4Q2RBK21xek9FbGVyVFJ6dVlZT3RQdTEvUzZOSTdmRmJUc1AxUFo0aC85MUlPTzRsUE1WcC9RdHNRRFNDTWtHeHZzVWJpdUg3ZG0weEVCVTQwSnFDc1ZqUUMvOEVRemU5Nmh4OHl3bzBheitTUXpiQ1FNWjJ2MXV4UWtzbEZHSkJ4VG9mN0lpM1M2Ky81VFFzaGZxZDExaktNUkNyZExOc0t4a1U5Wk5QMmUwYmtpRXZqKzVhMkRNdTB0M25wT1kxcUJoUDVKSHJJWi9JTENqWFkrSSt3ektLVmdRbGRMYzBJbVQ2T1BEQW80ejc2RmR6OXNSTGYyQjM2TnZJbHVDQkVXUU1PaEJ5NDk0S0J4TTB1eVUzRGFYRnVGVnNmKzJzL3Z2anViNjk2YllYSkwwUHVXYW9QbWc0Rkh5V3JMMXp4NDRDM2FPVFhnWUpPNEJtYkh4bUJaRXJzRHBjQzh2dkpyZ2lPdGpaWlNVZ3h3TWVmaUVySWgrTm9Ba3Q1WTV6R0JOWlhoVGxLWW05bCtwWXhYRFA4MVVSSnV1eTQ5VjdrelFaM0ZvQy9hSG0rN1h3Q2F2ZUViMC9DWTJkOEdYTnlEVWZlMUdrQ2FETG44WnlNSWc3c2hFSjFtOHpUMFV3eDg5SWs4YVgwLzIxNTN1ZENzR2dIbGxqN2xEV244TnZQQzRvMVpmNlhTNUkrOVM5T1JCM25sd1QyUGdXMHQza0VFbTAxK1VmVko0bDN3UGM4RFdyeXEwMU81NjA0Q1BpeWg1NTJrL09BbXpMUEpDeUQzdUExaFI1czByL1N4ZW8xNVVUTUpjQlQ0QlpXcUJocjk1Rld1N045aEZqMzFVeXQyUDI1aitTQWlzdGFtd3RYelNBa3J5d1lOdVROYm5pLzU4N0YzcEJFdXNIYzZzbzhtajBzYStYZXBDcGxRWk1oMU5wYnRhSHZMZUlVazdtZk5IZmxxSm1NRGFsUUhsMzVmOTlza1V3eG5sZjdSYm1FakNGK2NOMWRaSlBDTXMrUmE5LzNnR09jY053UVJzV1Q0YXM5Y0djalYzYmNoNmtob2tmR3FDTldDUFBIYy9HVlZ6VGZRb01Fb0VHNzJia3owZGFRaVRjR2pSM2NScVZSUUtHZjM1RVgvelFEOHVCcm91MGV5cFNIcCsyT0duZmh6Q2xGSHk5dVR3em83OHJ0K0VoZHVpa3ZndjJKYnpURCtRM3NicmVBcS9aWTdzeVBsVlBjNVBUZU93bnE1VzRrRmRlWEFDcUpzWk9zVW9nYUtuemxFczBxc2hoRUdydkg2THNQYVB2c0dQNy8rU3hYS0IyTGVpbzU4d3dLcVIwK2p2Z2xSM2pqRldMRzN2bGZSNnhPSUw1bThVaUI3YWZDZ1Noam13Y0NOTTZaeWZ5R1F6YWRCMnhjaHc2OG9Gb0FjcDdVN0VFSlQzYjRrbjdic21nZkFrZWwvbXFGVFpSekdYbXNxRVhDUmI3WjF0eFlibWdyWmp3a25ON3V0YUo3Rm14SWxZYkw5SFdzTDlZRXp4WVo3UjZ6TWwyc2l1dmtFMUZFQy91bkV3QVZoMXVFWXJEd25DTER4VjV2YUZnaGg0TVpkbS96RHUxa3FBMkprUTlsNTlPenlYb0pRdVZVdnIrUHpqWW8vd0IxdDhmU3RuVWpsbTh4eXNyQzRGbWRERTZtekxQNmJyUW1Mazkrb1J0L1ZDQlJ4eXVEUFJHcXNUT2plbE1CNGJTRU9rVmdKUGJTRjUrOTVJWFBHSnNXR0FybjJ5dzhXMU8rWWltSHNTbEU1TXQ0RDBsbGtaVnBUYXBzNGRhdndwcERCaGRBMVZTMk01aldnQjk1algvOUI5MHNPMCtUWXcyRW4vZ1o2RmpsOGtqMmtSNENMOHVaQlhWV3orend5VVZjZ0w1QmdkUVNBK0thTmo3aGQ2YXJNNFNWNnROSE81aGc2MCs3WDlMbzBqdDhWdE93L1U5bmlILzNVZzQ3aVU4eFduOUMyeEFOSUl5UWJHK3hSdUs0ZnQyYlRFUUZUalFtb0t4V05BTC93UkRONzNxSEh5OFI1cnFNTS9nNnJVYU5nWm1jM0ZzMXIrRTJtTG96QVBkcFdNa0kwNGg1dVJhdXd3ektRdnZGL21QbWNrOHNpMksrdTFEcm9WWUJEKzJPQ05BTFMweWNNemg3Z0h1Y1ZvaCtGY2FQdjhVZ0c5SjdYcUUwVmVidFo3bStQaC9aN21iN0djNkVSR0IzTHJlTUt1KytSQ25MVkc0bTBNcTU5QzJsYmJleVBKVnlOK0Y2UGdyZGd0ZjhCMDhLc2RaeHViY0dCU2FWVFRnRXpUQ0lLcjJLSnQyWGdxRlU1NzdyNkxwNDN2SVFSRnpVWFpkM1ZBRldic3QvazQzSU1OTjI2SjlLZVhnRmdJQ2Rmek85V1pQbHI5czBqWmQ0RExjcWxBK0Vrb1QrVytKOHdPSVB3K2hPYjQvUHBYWXFIY3dINDJoUzB5STNnOW5TQlpldlVGeENFbGUxT2oyRzliNEpBUVF5aDVyTWI3dCtGVkF5eWZiaVRFNHdQeHVRZ3Jza2RvZXVhU2NWSGtBa1lBcWdqWllOSmtXV3AzMGZxa2N4RDdqT0UwcWtVVFJNNkdqczNxelRjNnVpTjNFZVhhSU9YaGJNRVhBNy83SkpIVTgrczJDRmtUY1VWV1JMZFdzZzhxTWg0elpCOHIxeVpQQ1RqdkdTSTNTdU0xaGpHRVZ0MGs0VVY4UDVWamtpeGd2bllQMVVGU01uSmlUZUY0QnZJTWxSUG9pLzh4VElyM2VmbDBtMVFnZ2d6VkFHYkJSQU1rSk85UTJPM29iT2owdm5Oc1pHL3A4RHhTTER0U2xSVXNEV0lwUElYdnBlTlVMK2JoOHBMdWJHYWdqRGFDNllrbUNsVHYwQnVXbkh1bTNNTzlnRXRpNG5kWUdHQm50cnRkektDM3dVc0JWc28rR29ZRE9WeFk5K2VFNnkzMFczUVkyM1ZHTFpmUmFoN3JmaHEwNW0yYXBHVzdjWmJncW5aWi93RXlHamhlcDZkT0tRSkU0VnNJeDFzZXRTWUV6ZjIyTzVVOXk0cExsblU4M1RhWVNNRWtGUWJHME40UEZHY0pXWk03aVNsYXFqcHN0M2JsMkhPcCtndmhMWkhsR09TcW95cWI3VFJIS3I1Y3NmUlY1cngzUlM2R0h0M2c5WnFOWm0zWitFSERmN0EvakFVcVo0WXl4ZEt3VkpnNyt6VXU2QkUxaTAxcUZLUUs4MlRMK0oxY1lLU2tPSTBtZU0yQlVKRUp2eU1WTkpudU41MERPN29WK3lEQkt2TE5NRkNpT2tkRW5PTHo4V3BXdUxyRVVPUVJ3a1JESEdBRlFEUnNZcTBsTzdCRWFMMWVmUjJRVXFCRGNpY25YUnBlWlRQUGUwVkEyUU1VVFN6eERTN0pyRkVFSGY1Sm5qWlVOalFtTW55Y2Z2cCs4OWkwN0dtODloeThYNDc2VTRwejJGUUFOcDhaRHkyOCthMytyVVNEVU53TkpJN2FOUW5ZSDMwclMzaDhYUnUvZXYrcHk2L3ljN3pqVVFtOU14L0F0RzdFcGRmQml1cWoya2lFV1AvMUFKajNYUlZsSkxJeXRCZEF1cGJhUXRubHZsT3l2ODNOWVR5b2hZMTFBQ003T0U2RitTUnpPUGQzK0JLUkhQQVVyQmhtK3BzNVVrQzgxL0NVaHpLenZMQmFOU2d0RjJBU2ZmTUE5blpFTmtxNys0VDlDTzlnZUNMVGxnNlFlTHpwUnNTRWp3ell6UTJjanQ4N0JHTXlYVldiVVIyYkJHTVlTZGF2d2VrZlkzSU1SZVBmYjc5ZnBuY3NDdDdtditBVXp4bDk0aC83eDNpUkJ6U0Z6ZWVDM2VaYWtHOU5uZ0JLine truncated
|
||||
@@ -0,0 +1,273 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 简称: VIP17_duo
|
||||
// 名称: VIP17_duo
|
||||
// 类别: 公式应用
|
||||
// 类型: 用户应用
|
||||
// 输出: Void
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
Params
|
||||
|
||||
Numeric Fund(20000); // 投入保证金
|
||||
Vars
|
||||
Numeric Lots(0); // 交易手数
|
||||
Numeric totalScore(5); // 开仓分数阈值,目前都是等权分配,总共6个指标及6分。
|
||||
Numeric rpmPeriod(14); // RPM Period
|
||||
Numeric bboPeriod(16); // BBO Period
|
||||
Numeric macdFastPeriod(12); // MACD Fast Period
|
||||
Numeric macdSlowPeriod(24); // MACD Slow Period
|
||||
Numeric macdSignalPeriod(9); // MACD Signal Period
|
||||
Numeric rsiPeriod(14); // RSI Period
|
||||
Numeric cciPeriod(14); // CCI Period
|
||||
Numeric stochasticKLength(14); // Stochastic %K Length
|
||||
Numeric stochasticKSmoothing(1); // Stochastic %K Smoothing
|
||||
Numeric stochasticDSmoothing(3); // Stochastic %D Smoothing
|
||||
Numeric supertrendPeriod(10); // SUPERTREND Period
|
||||
Numeric supertrendFactor(2); // SUPERTREND Factor
|
||||
Series<Numeric> rpmValue; // RPM Value
|
||||
Series<Numeric> bboValue; // BBO Value
|
||||
Series<Numeric> macdValue; // MACD Value
|
||||
Series<Numeric> macdSignal; // MACD Signal Line
|
||||
Series<Numeric> macdHist; // MACD Histogram
|
||||
Series<Numeric> rsiValue; // RSI Value
|
||||
Series<Numeric> stochasticValue; // Stochastic Value
|
||||
Series<Numeric> cciValue; // CCI Value
|
||||
Series<Numeric> supertrendValue; // SUPERTREND Value
|
||||
Series<Numeric> netChgAvg ;
|
||||
Series<Numeric> totChgAvg ;
|
||||
Series<Numeric> score;
|
||||
Numeric EntryStrength(90); // 趋势强度的进场值
|
||||
Numeric Length(5); // 强弱指标和通道计算的周期值
|
||||
Series<Numeric> CloseChange; // 收盘价变动值
|
||||
Numeric i; // 循环控制变量
|
||||
Numeric UpCloses; // 收盘价上涨累计值
|
||||
Numeric DnCloses; // 收盘价下跌累计值
|
||||
Numeric SumChange; // 收盘价变动累计值
|
||||
Series<Numeric> MarketStrength; // 市场强弱指标
|
||||
Plot plt1; // 用于画图的对象
|
||||
Events
|
||||
OnInit()
|
||||
{
|
||||
Range[0:DataCount-1]
|
||||
{
|
||||
//=========数据源相关设置==============
|
||||
AddDataFlag(Enum_Data_RolloverBackWard()); //设置后复权
|
||||
|
||||
AddDataFlag(Enum_Data_RolloverRealPrice()); //设置映射真实价格
|
||||
|
||||
AddDataFlag(Enum_Data_AutoSwapPosition()); //设置自动换仓
|
||||
|
||||
|
||||
//AddDataFlag(Enum_Data_IgnoreSwapSignalCalc()); //设置忽略换仓信号计算
|
||||
}
|
||||
// 创建第一个副图
|
||||
plt1.figure(0); // 独立副图区域
|
||||
plt1.setOption("Score", "width", Enum_7Pix); // 将指标“MA1”的输出线宽设置为3像素
|
||||
plt1.setOption("Score","color",Blue);
|
||||
}
|
||||
|
||||
OnBar(ArrayRef<Integer> indexs)
|
||||
{
|
||||
// 计算交易手数
|
||||
Lots = Max(1, Round(Fund/(O*ContractUnit*BigPointValue* MarginRatio/rollover), 0));
|
||||
// 计算市场强弱指标
|
||||
CloseChange = Close - Close[1];
|
||||
UpCloses = 0;
|
||||
DnCloses = 0;
|
||||
For i = 0 To Length-1
|
||||
{
|
||||
// 收盘价上涨计入涨幅累计
|
||||
If(CloseChange[i] > 0)
|
||||
UpCloses = UpCloses + CloseChange[i];
|
||||
// 否则计入跌幅累计
|
||||
Else
|
||||
DnCloses = DnCloses + CloseChange[i];
|
||||
}
|
||||
// 计算周期内涨跌
|
||||
SumChange = Summation(CloseChange, Length);
|
||||
// 周期内上涨,计算上涨强度,0-100之间
|
||||
If(SumChange >= 0)
|
||||
{
|
||||
MarketStrength = SumChange / UpCloses * 100;
|
||||
}
|
||||
// 周期内下跌,计算下跌强度,0-100之间
|
||||
Else
|
||||
{
|
||||
MarketStrength = SumChange / Abs(DnCloses) * 100;
|
||||
}
|
||||
|
||||
// 计算 MACD 值
|
||||
Numeric emaFast = XAverage(Close, macdFastPeriod);
|
||||
Numeric emaSlow = XAverage(Close, macdSlowPeriod);
|
||||
macdValue = emaFast - emaSlow;
|
||||
macdSignal = XAverage(macdValue, macdSignalPeriod);
|
||||
macdHist = macdValue - macdSignal;
|
||||
|
||||
// 计算 RSI 值
|
||||
Numeric SF = 1 / rsiPeriod;
|
||||
Numeric change = Close - Close[1];
|
||||
If(CurrentBar <= rsiPeriod - 1)
|
||||
{
|
||||
netChgAvg = (Close - Close[rsiPeriod]) / rsiPeriod;
|
||||
totChgAvg = Average(Abs(Close - Close[1]), rsiPeriod);
|
||||
}
|
||||
Else
|
||||
{
|
||||
netChgAvg = netChgAvg[1] + SF * (change - netChgAvg[1]);
|
||||
totChgAvg = totChgAvg[1] + SF * (Abs(change) - totChgAvg[1]);
|
||||
}
|
||||
If(totChgAvg <> 0)
|
||||
{
|
||||
rsiValue = 50 * (netChgAvg / totChgAvg + 1);
|
||||
}
|
||||
Else
|
||||
{
|
||||
rsiValue = 50;
|
||||
}
|
||||
|
||||
// 计算 Stochastic 值
|
||||
Numeric highestHigh = Highest(High, stochasticKLength);
|
||||
Numeric lowestLow = Lowest(Low, stochasticKLength);
|
||||
Numeric stochasticRaw = (Close - lowestLow) / (highestHigh - lowestLow) * 100;
|
||||
stochasticValue = Average(stochasticRaw, stochasticKSmoothing);
|
||||
|
||||
// 计算 CCI 值
|
||||
Numeric typicalPrice = (High + Low + Close) / 3;
|
||||
Numeric smaTypicalPrice = Average(typicalPrice, cciPeriod);
|
||||
Numeric meanDeviation = Summation(Abs(typicalPrice - smaTypicalPrice), cciPeriod) / cciPeriod;
|
||||
If(meanDeviation <> 0)
|
||||
{
|
||||
cciValue = (typicalPrice - smaTypicalPrice) / (0.015 * meanDeviation);
|
||||
}
|
||||
Else
|
||||
{
|
||||
cciValue = 0;
|
||||
}
|
||||
|
||||
// 计算 SUPERTREND 值
|
||||
Numeric atr = AvgTrueRange(supertrendPeriod);
|
||||
Numeric basicUpperBand = (High + Low) / 2 + supertrendFactor * atr;
|
||||
Numeric basicLowerBand = (High + Low) / 2 - supertrendFactor * atr;
|
||||
Numeric finalUpperBand = IIF(basicUpperBand < supertrendValue[1] || Close[1] > supertrendValue[1], basicUpperBand, supertrendValue[1]);
|
||||
Numeric finalLowerBand = IIF(basicLowerBand > supertrendValue[1] || Close[1] < supertrendValue[1], basicLowerBand, supertrendValue[1]);
|
||||
supertrendValue = IIF(Close > supertrendValue[1], finalLowerBand, finalUpperBand);
|
||||
|
||||
// 设置多头条件
|
||||
Bool macdCondition = macdValue > macdSignal;
|
||||
Bool rsiCondition = rsiValue >50;
|
||||
Bool stochasticCondition = stochasticValue > 50;
|
||||
Bool cciCondition = cciValue >0;
|
||||
Bool supertrendCondition = Close > supertrendValue;
|
||||
Bool Marketbool = MarketStrength >= EntryStrength;
|
||||
|
||||
// 计算得分
|
||||
score = 0;
|
||||
If(macdCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(rsiCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(stochasticCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(cciCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(supertrendCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
if(Marketbool)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
|
||||
// 在副图显示得分
|
||||
Commentary("Score: " + Text(score));
|
||||
|
||||
|
||||
plt1.barv("Score",score);
|
||||
// 判断是否达到条件数量
|
||||
If(score == totalScore)
|
||||
{
|
||||
Commentary("All conditions met!");
|
||||
}
|
||||
// 开多条件
|
||||
If(score[1] >= totalScore And MarketPosition == 0 ) //
|
||||
{
|
||||
Buy(Lots, Open);
|
||||
}
|
||||
|
||||
// 平仓条件
|
||||
If(score[1] == 0 And MarketPosition == 1)
|
||||
{
|
||||
Sell(0, Open);
|
||||
}
|
||||
|
||||
// 在副图显示条件成立和不成立
|
||||
|
||||
If(macdCondition)
|
||||
{
|
||||
PlotAuto("MACD_Condition",1,1,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("MACD_Condition",1,1,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(rsiCondition)
|
||||
{
|
||||
PlotAuto("RSI_Condition",2,2,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("RSI_Condition",2,2,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(stochasticCondition)
|
||||
{
|
||||
PlotAuto("Stochastic_Condition", 3,3,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("Stochastic_Condition", 3,3,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(cciCondition)
|
||||
{
|
||||
PlotAuto("CCI_Condition", 4,4,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("CCI_Condition", 4,4,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(supertrendCondition)
|
||||
{
|
||||
PlotAuto("Supertrend_Condition", 5,5,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("Supertrend_Condition", 5,5,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
If(Marketbool)
|
||||
{
|
||||
PlotAuto("Marketbool", 6,6,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("Marketbool", 6,6,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// 编译版本 2024/11/27 145803
|
||||
// 版权所有 songshu123
|
||||
// 更改声明 TradeBlazer Software保留对TradeBlazer平台
|
||||
// 每一版本的TradeBlazer公式修改和重写的权利
|
||||
//------------------------------------------------------------------------
|
||||
@@ -0,0 +1,274 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 简称: vip17_kong
|
||||
// 名称: vip17_kong
|
||||
// 类别: 公式应用
|
||||
// 类型: 用户应用
|
||||
// 输出: Void
|
||||
//------------------------------------------------------------------------
|
||||
Params
|
||||
Numeric Fund(20000); // 投入保证金
|
||||
|
||||
Vars
|
||||
Numeric Lots(0); // 交易手数
|
||||
Numeric totalScore(5); // 开仓分数阈值,目前都是等权分配,总共6个指标及6分。
|
||||
Numeric rpmPeriod(14); // RPM Period
|
||||
Numeric bboPeriod(16); // BBO Period
|
||||
Numeric macdFastPeriod(12); // MACD Fast Period
|
||||
Numeric macdSlowPeriod(24); // MACD Slow Period
|
||||
Numeric macdSignalPeriod(9); // MACD Signal Period
|
||||
Numeric rsiPeriod(14); // RSI Period
|
||||
Numeric cciPeriod(14); // CCI Period
|
||||
Numeric stochasticKLength(14); // Stochastic %K Length
|
||||
Numeric stochasticKSmoothing(1); // Stochastic %K Smoothing
|
||||
Numeric stochasticDSmoothing(3); // Stochastic %D Smoothing
|
||||
Numeric supertrendPeriod(10); // SUPERTREND Period
|
||||
Numeric supertrendFactor(2); // SUPERTREND Factor
|
||||
Series<Numeric> rpmValue; // RPM Value
|
||||
Series<Numeric> bboValue; // BBO Value
|
||||
Series<Numeric> macdValue; // MACD Value
|
||||
Series<Numeric> macdSignal; // MACD Signal Line
|
||||
Series<Numeric> macdHist; // MACD Histogram
|
||||
Series<Numeric> rsiValue; // RSI Value
|
||||
Series<Numeric> stochasticValue; // Stochastic Value
|
||||
Series<Numeric> cciValue; // CCI Value
|
||||
Series<Numeric> supertrendValue; // SUPERTREND Value
|
||||
Series<Numeric> netChgAvg ;
|
||||
Series<Numeric> totChgAvg ;
|
||||
Series<Numeric> score;
|
||||
Numeric EntryStrength(90); // 趋势强度的进场值
|
||||
Numeric Length(5); // 强弱指标和通道计算的周期值
|
||||
Series<Numeric> CloseChange; // 收盘价变动值
|
||||
Numeric i; // 循环控制变量
|
||||
Numeric UpCloses; // 收盘价上涨累计值
|
||||
Numeric DnCloses; // 收盘价下跌累计值
|
||||
Numeric SumChange; // 收盘价变动累计值
|
||||
Series<Numeric> MarketStrength; // 市场强弱指标
|
||||
Plot plt1; // 用于画图的对象
|
||||
Events
|
||||
OnInit()
|
||||
{
|
||||
Range[0:DataCount-1]
|
||||
{
|
||||
//=========数据源相关设置==============
|
||||
AddDataFlag(Enum_Data_RolloverBackWard()); //设置后复权
|
||||
|
||||
AddDataFlag(Enum_Data_RolloverRealPrice()); //设置映射真实价格
|
||||
|
||||
AddDataFlag(Enum_Data_AutoSwapPosition()); //设置自动换仓
|
||||
|
||||
|
||||
//AddDataFlag(Enum_Data_IgnoreSwapSignalCalc()); //设置忽略换仓信号计算
|
||||
}
|
||||
// 创建第一个副图
|
||||
plt1.figure(0); // 独立副图区域
|
||||
plt1.setOption("Score", "width", Enum_7Pix); // 将指标“MA1”的输出线宽设置为3像素
|
||||
plt1.setOption("Score","color",Blue);
|
||||
}
|
||||
|
||||
OnBar(ArrayRef<Integer> indexs)
|
||||
{
|
||||
// 计算交易手数
|
||||
Lots = Max(1, Round(Fund/(O*ContractUnit*BigPointValue* MarginRatio/rollover), 0));
|
||||
// 计算市场强弱指标
|
||||
CloseChange = Close - Close[1];
|
||||
UpCloses = 0;
|
||||
DnCloses = 0;
|
||||
For i = 0 To Length-1
|
||||
{
|
||||
// 收盘价上涨计入涨幅累计
|
||||
If(CloseChange[i] > 0)
|
||||
UpCloses = UpCloses + CloseChange[i];
|
||||
// 否则计入跌幅累计
|
||||
Else
|
||||
DnCloses = DnCloses + CloseChange[i];
|
||||
}
|
||||
// 计算周期内涨跌
|
||||
SumChange = Summation(CloseChange, Length);
|
||||
// 周期内上涨,计算上涨强度,0-100之间
|
||||
If(SumChange >= 0)
|
||||
{
|
||||
MarketStrength = SumChange / UpCloses * 100;
|
||||
}
|
||||
// 周期内下跌,计算下跌强度,0-100之间
|
||||
Else
|
||||
{
|
||||
MarketStrength = SumChange / Abs(DnCloses) * 100;
|
||||
}
|
||||
|
||||
// 计算 MACD 值
|
||||
Numeric emaFast = XAverage(Close, macdFastPeriod);
|
||||
Numeric emaSlow = XAverage(Close, macdSlowPeriod);
|
||||
macdValue = emaFast - emaSlow;
|
||||
macdSignal = XAverage(macdValue, macdSignalPeriod);
|
||||
macdHist = macdValue - macdSignal;
|
||||
|
||||
// 计算 RSI 值
|
||||
Numeric SF = 1 / rsiPeriod;
|
||||
Numeric change = Close - Close[1];
|
||||
If(CurrentBar <= rsiPeriod - 1)
|
||||
{
|
||||
netChgAvg = (Close - Close[rsiPeriod]) / rsiPeriod;
|
||||
totChgAvg = Average(Abs(Close - Close[1]), rsiPeriod);
|
||||
}
|
||||
Else
|
||||
{
|
||||
netChgAvg = netChgAvg[1] + SF * (change - netChgAvg[1]);
|
||||
totChgAvg = totChgAvg[1] + SF * (Abs(change) - totChgAvg[1]);
|
||||
}
|
||||
If(totChgAvg <> 0)
|
||||
{
|
||||
rsiValue = 50 * (netChgAvg / totChgAvg + 1);
|
||||
}
|
||||
Else
|
||||
{
|
||||
rsiValue = 50;
|
||||
}
|
||||
|
||||
// 计算 Stochastic 值
|
||||
Numeric highestHigh = Highest(High, stochasticKLength);
|
||||
Numeric lowestLow = Lowest(Low, stochasticKLength);
|
||||
Numeric stochasticRaw = (Close - lowestLow) / (highestHigh - lowestLow) * 100;
|
||||
stochasticValue = Average(stochasticRaw, stochasticKSmoothing);
|
||||
|
||||
// 计算 CCI 值
|
||||
Numeric typicalPrice = (High + Low + Close) / 3;
|
||||
Numeric smaTypicalPrice = Average(typicalPrice, cciPeriod);
|
||||
Numeric meanDeviation = Summation(Abs(typicalPrice - smaTypicalPrice), cciPeriod) / cciPeriod;
|
||||
If(meanDeviation <> 0)
|
||||
{
|
||||
cciValue = (typicalPrice - smaTypicalPrice) / (0.015 * meanDeviation);
|
||||
}
|
||||
Else
|
||||
{
|
||||
cciValue = 0;
|
||||
}
|
||||
|
||||
// 计算 SUPERTREND 值
|
||||
Numeric atr = AvgTrueRange(supertrendPeriod);
|
||||
Numeric basicUpperBand = (High + Low) / 2 + supertrendFactor * atr;
|
||||
Numeric basicLowerBand = (High + Low) / 2 - supertrendFactor * atr;
|
||||
Numeric finalUpperBand = IIF(basicUpperBand < supertrendValue[1] || Close[1] > supertrendValue[1], basicUpperBand, supertrendValue[1]);
|
||||
Numeric finalLowerBand = IIF(basicLowerBand > supertrendValue[1] || Close[1] < supertrendValue[1], basicLowerBand, supertrendValue[1]);
|
||||
supertrendValue = IIF(Close > supertrendValue[1], finalLowerBand, finalUpperBand);
|
||||
|
||||
// 设置空头条件
|
||||
Bool macdCondition = macdValue < macdSignal;
|
||||
Bool rsiCondition = rsiValue <50;
|
||||
Bool stochasticCondition = stochasticValue < 50;
|
||||
Bool cciCondition = cciValue <0;
|
||||
Bool supertrendCondition = Close < supertrendValue;
|
||||
Bool Marketbool = MarketStrength >= EntryStrength;
|
||||
|
||||
// 计算得分
|
||||
score = 0;
|
||||
If(macdCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(rsiCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(stochasticCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(cciCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
If(supertrendCondition)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
if(Marketbool)
|
||||
{
|
||||
score = score + 1;
|
||||
}
|
||||
|
||||
// 在副图显示得分
|
||||
Commentary("Score: " + Text(score));
|
||||
|
||||
|
||||
plt1.barv("Score",score);
|
||||
// 判断是否达到条件数量
|
||||
If(score == totalScore)
|
||||
{
|
||||
Commentary("All conditions met!");
|
||||
}
|
||||
// 开空条件
|
||||
If(score[1] >= totalScore And MarketPosition == 0 ) //
|
||||
{
|
||||
SellShort(Lots, Open);
|
||||
}
|
||||
|
||||
// 平仓条件
|
||||
If(score[1] == 0 And MarketPosition == -1)
|
||||
{
|
||||
BuyToCover(0, Open);
|
||||
}
|
||||
|
||||
// 在副图显示条件成立和不成立
|
||||
|
||||
If(macdCondition)
|
||||
{
|
||||
PlotAuto("MACD_Condition",1,1,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("MACD_Condition",1,1,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(rsiCondition)
|
||||
{
|
||||
PlotAuto("RSI_Condition",2,2,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("RSI_Condition",2,2,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(stochasticCondition)
|
||||
{
|
||||
PlotAuto("Stochastic_Condition", 3,3,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("Stochastic_Condition", 3,3,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(cciCondition)
|
||||
{
|
||||
PlotAuto("CCI_Condition", 4,4,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("CCI_Condition", 4,4,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
If(supertrendCondition)
|
||||
{
|
||||
PlotAuto("Supertrend_Condition", 5,5,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("Supertrend_Condition", 5,5,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
If(Marketbool)
|
||||
{
|
||||
PlotAuto("Marketbool", 6,6,Green,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
Else
|
||||
{
|
||||
PlotAuto("Marketbool", 6,6,Red,Enum_Line,Enum_Solid,Enum_7Pix);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// 编译版本 2024/11/27 150615
|
||||
// 版权所有 songshu123
|
||||
// 更改声明 TradeBlazer Software保留对TradeBlazer平台
|
||||
// 每一版本的TradeBlazer公式修改和重写的权利
|
||||
//------------------------------------------------------------------------
|
||||
|
After Width: | Height: | Size: 507 KiB |
@@ -0,0 +1,210 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
class vip17_duo(CtaTemplate):
|
||||
"""
|
||||
|
||||
VIP17多头策略
|
||||
基于多个技术指标的多头策略,当满足一定数量的多头条件时开仓
|
||||
|
||||
"""
|
||||
author = "松鼠Quant"
|
||||
|
||||
# 策略参数
|
||||
total_score = 5 # 开仓分数阈值
|
||||
entry_strength = 90 # 趋势强度的进场值
|
||||
length = 5 # 强弱指标和通道计算的周期值
|
||||
|
||||
# 指标参数
|
||||
rpm_period = 14 # RPM Period
|
||||
bbo_period = 16 # BBO Period
|
||||
macd_fast_period = 12 # MACD Fast Period
|
||||
macd_slow_period = 24 # MACD Slow Period
|
||||
macd_signal_period = 9 # MACD Signal Period
|
||||
rsi_period = 14 # RSI Period
|
||||
cci_period = 14 # CCI Period
|
||||
stoch_k_period = 14 # Stochastic %K Length
|
||||
stoch_k_smooth = 1 # Stochastic %K Smoothing
|
||||
stoch_d_smooth = 3 # Stochastic %D Smoothing
|
||||
supertrend_period = 10 # SUPERTREND Period
|
||||
supertrend_factor = 2 # SUPERTREND Factor
|
||||
|
||||
parameters = ["total_score", "entry_strength", "length",
|
||||
"macd_fast_period", "macd_slow_period", "macd_signal_period",
|
||||
"rsi_period", "cci_period", "stoch_k_period"]
|
||||
|
||||
variables = ["current_score"]
|
||||
|
||||
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(size=100)
|
||||
|
||||
# 指标变量
|
||||
self.current_score = 0
|
||||
self.market_strength = 0
|
||||
self.supertrend_value = 0
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
策略初始化
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(100)
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
策略启动
|
||||
"""
|
||||
self.write_log("策略启动")
|
||||
|
||||
def on_stop(self):
|
||||
"""
|
||||
策略停止
|
||||
"""
|
||||
self.write_log("策略停止")
|
||||
|
||||
def on_tick(self, tick: TickData):
|
||||
"""
|
||||
Tick数据更新
|
||||
"""
|
||||
self.bg.update_tick(tick)
|
||||
|
||||
def calculate_market_strength(self):
|
||||
"""计算市场强度指标"""
|
||||
close_change = self.am.close_array[1:] - self.am.close_array[:-1]
|
||||
up_closes = 0
|
||||
dn_closes = 0
|
||||
|
||||
for i in range(self.length):
|
||||
if close_change[-i-1] > 0:
|
||||
up_closes += close_change[-i-1]
|
||||
else:
|
||||
dn_closes += close_change[-i-1]
|
||||
|
||||
sum_change = np.sum(close_change[-self.length:])
|
||||
|
||||
if sum_change >= 0:
|
||||
self.market_strength = (sum_change / up_closes * 100) if up_closes != 0 else 0
|
||||
else:
|
||||
self.market_strength = (sum_change / abs(dn_closes) * 100) if dn_closes != 0 else 0
|
||||
|
||||
return self.market_strength >= self.entry_strength
|
||||
|
||||
def calculate_supertrend(self):
|
||||
"""计算SuperTrend指标"""
|
||||
atr = talib.ATR(self.am.high_array, self.am.low_array,
|
||||
self.am.close_array, self.supertrend_period)
|
||||
|
||||
basic_upper = (self.am.high_array + self.am.low_array) / 2 + self.supertrend_factor * atr
|
||||
basic_lower = (self.am.high_array + self.am.low_array) / 2 - self.supertrend_factor * atr
|
||||
|
||||
# 简化的SuperTrend计算
|
||||
self.supertrend_value = basic_lower[-1]
|
||||
return self.am.close_array[-1] > self.supertrend_value
|
||||
|
||||
def calculate_score(self):
|
||||
"""
|
||||
计算综合得分
|
||||
"""
|
||||
score = 0
|
||||
# MACD条件 - 多头信号
|
||||
# 使用talib直接计算MACD
|
||||
macd, signal, hist = talib.MACD(
|
||||
self.am.close_array,
|
||||
fastperiod=self.macd_fast_period,
|
||||
slowperiod=self.macd_slow_period,
|
||||
signalperiod=self.macd_signal_period
|
||||
)
|
||||
|
||||
if not np.isnan(macd[-1]) and not np.isnan(signal[-1]):
|
||||
if macd[-1] > signal[-1]: # 多头信号:MACD线在信号线上方
|
||||
score += 1
|
||||
|
||||
# RSI条件 - 多头信号
|
||||
rsi = talib.RSI(self.am.close_array, timeperiod=self.rsi_period)
|
||||
if not np.isnan(rsi[-1]) and rsi[-1] > 50: # 多头信号:RSI大于50
|
||||
score += 1
|
||||
|
||||
# Stochastic条件 - 多头信号
|
||||
k, d = talib.STOCH(
|
||||
self.am.high_array,
|
||||
self.am.low_array,
|
||||
self.am.close_array,
|
||||
fastk_period=self.stoch_k_period,
|
||||
slowk_period=self.stoch_k_smooth,
|
||||
slowd_period=self.stoch_d_smooth
|
||||
)
|
||||
if not np.isnan(k[-1]) and k[-1] > 50: # 多头信号:K值大于50
|
||||
score += 1
|
||||
|
||||
# CCI条件 - 多头信号
|
||||
cci = talib.CCI(
|
||||
self.am.high_array,
|
||||
self.am.low_array,
|
||||
self.am.close_array,
|
||||
timeperiod=self.cci_period
|
||||
)
|
||||
if not np.isnan(cci[-1]) and cci[-1] > 0: # 多头信号:CCI大于0
|
||||
score += 1
|
||||
|
||||
# Supertrend条件
|
||||
if self.calculate_supertrend():
|
||||
score += 1
|
||||
|
||||
# 市场强度条件
|
||||
if self.calculate_market_strength():
|
||||
score += 1
|
||||
|
||||
return score
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
"""
|
||||
K线更新回调
|
||||
"""
|
||||
self.am.update_bar(bar)
|
||||
if not self.am.inited:
|
||||
return
|
||||
|
||||
# 计算当前得分
|
||||
self.current_score = self.calculate_score()
|
||||
|
||||
# 交易信号
|
||||
if self.current_score >= self.total_score and not self.pos:
|
||||
self.buy(bar.close_price + 5, 1)
|
||||
elif self.current_score == 0 and self.pos > 0:
|
||||
self.sell(bar.close_price - 5, abs(self.pos))
|
||||
|
||||
# 输出当前得分
|
||||
self.put_event()
|
||||
|
||||
def on_trade(self, trade: TradeData):
|
||||
"""
|
||||
成交回调
|
||||
"""
|
||||
self.put_event()
|
||||
|
||||
def on_order(self, order: OrderData):
|
||||
"""
|
||||
委托回调
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_stop_order(self, stop_order: StopOrder):
|
||||
"""
|
||||
停止单回调
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,208 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
TargetPosTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
class vip17_kong(CtaTemplate):
|
||||
"""
|
||||
VIP17空头策略
|
||||
基于多个技术指标的空头策略,当满足一定数量的空头条件时开仓
|
||||
"""
|
||||
author = "松鼠Quant"
|
||||
|
||||
# 策略参数
|
||||
total_score = 5 # 开仓分数阈值
|
||||
entry_strength = 90 # 趋势强度的进场值
|
||||
length = 5 # 强弱指标和通道计算的周期值
|
||||
|
||||
# 指标参数
|
||||
rpm_period = 14 # RPM Period
|
||||
bbo_period = 16 # BBO Period
|
||||
macd_fast_period = 12 # MACD Fast Period
|
||||
macd_slow_period = 24 # MACD Slow Period
|
||||
macd_signal_period = 9 # MACD Signal Period
|
||||
rsi_period = 14 # RSI Period
|
||||
cci_period = 14 # CCI Period
|
||||
stoch_k_period = 14 # Stochastic %K Length
|
||||
stoch_k_smooth = 1 # Stochastic %K Smoothing
|
||||
stoch_d_smooth = 3 # Stochastic %D Smoothing
|
||||
supertrend_period = 10 # SUPERTREND Period
|
||||
supertrend_factor = 2 # SUPERTREND Factor
|
||||
|
||||
parameters = ["total_score", "entry_strength", "length",
|
||||
"macd_fast_period", "macd_slow_period", "macd_signal_period",
|
||||
"rsi_period", "cci_period", "stoch_k_period"]
|
||||
|
||||
variables = ["current_score"]
|
||||
|
||||
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(size=100)
|
||||
|
||||
# 指标变量
|
||||
self.current_score = 0
|
||||
self.market_strength = 0
|
||||
self.supertrend_value = 0
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
策略初始化
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
self.load_bar(100)
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
策略启动
|
||||
"""
|
||||
self.write_log("策略启动")
|
||||
|
||||
def on_stop(self):
|
||||
"""
|
||||
策略停止
|
||||
"""
|
||||
self.write_log("策略停止")
|
||||
|
||||
def on_tick(self, tick: TickData):
|
||||
"""
|
||||
Tick数据更新
|
||||
"""
|
||||
self.bg.update_tick(tick)
|
||||
|
||||
def calculate_market_strength(self):
|
||||
"""计算市场强度指标"""
|
||||
close_change = self.am.close_array[1:] - self.am.close_array[:-1]
|
||||
up_closes = 0
|
||||
dn_closes = 0
|
||||
|
||||
for i in range(self.length):
|
||||
if close_change[-i-1] > 0:
|
||||
up_closes += close_change[-i-1]
|
||||
else:
|
||||
dn_closes += close_change[-i-1]
|
||||
|
||||
sum_change = np.sum(close_change[-self.length:])
|
||||
|
||||
if sum_change >= 0:
|
||||
self.market_strength = (sum_change / up_closes * 100) if up_closes != 0 else 0
|
||||
else:
|
||||
self.market_strength = (sum_change / abs(dn_closes) * 100) if dn_closes != 0 else 0
|
||||
|
||||
return self.market_strength <= -self.entry_strength # 改为判断下跌强度
|
||||
|
||||
def calculate_supertrend(self):
|
||||
"""计算SuperTrend指标"""
|
||||
atr = talib.ATR(self.am.high_array, self.am.low_array,
|
||||
self.am.close_array, self.supertrend_period)
|
||||
|
||||
basic_upper = (self.am.high_array + self.am.low_array) / 2 + self.supertrend_factor * atr
|
||||
basic_lower = (self.am.high_array + self.am.low_array) / 2 - self.supertrend_factor * atr
|
||||
|
||||
# 简化的SuperTrend计算
|
||||
self.supertrend_value = basic_upper[-1] # 改为上轨
|
||||
return self.am.close_array[-1] < self.supertrend_value # 改为判断价格低于上轨
|
||||
|
||||
def calculate_score(self):
|
||||
"""
|
||||
计算综合得分
|
||||
"""
|
||||
score = 0
|
||||
|
||||
# MACD条件 - 空头信号
|
||||
# 使用talib直接计算MACD
|
||||
macd, signal, hist = talib.MACD(
|
||||
self.am.close_array,
|
||||
fastperiod=self.macd_fast_period,
|
||||
slowperiod=self.macd_slow_period,
|
||||
signalperiod=self.macd_signal_period
|
||||
)
|
||||
|
||||
if not np.isnan(macd[-1]) and not np.isnan(signal[-1]):
|
||||
if macd[-1] < signal[-1]: # 空头信号
|
||||
score += 1
|
||||
|
||||
# RSI条件 - 空头信号
|
||||
rsi = talib.RSI(self.am.close_array, timeperiod=self.rsi_period)
|
||||
if not np.isnan(rsi[-1]) and rsi[-1] < 50:
|
||||
score += 1
|
||||
|
||||
# Stochastic条件 - 空头信号
|
||||
k, d = talib.STOCH(
|
||||
self.am.high_array,
|
||||
self.am.low_array,
|
||||
self.am.close_array,
|
||||
fastk_period=self.stoch_k_period,
|
||||
slowk_period=self.stoch_k_smooth,
|
||||
slowd_period=self.stoch_d_smooth
|
||||
)
|
||||
if not np.isnan(k[-1]) and k[-1] < 50:
|
||||
score += 1
|
||||
|
||||
# CCI条件 - 空头信号
|
||||
cci = talib.CCI(
|
||||
self.am.high_array,
|
||||
self.am.low_array,
|
||||
self.am.close_array,
|
||||
timeperiod=self.cci_period
|
||||
)
|
||||
if not np.isnan(cci[-1]) and cci[-1] < 0:
|
||||
score += 1
|
||||
|
||||
# Supertrend条件 - 空头信号
|
||||
if self.calculate_supertrend():
|
||||
score += 1
|
||||
|
||||
# 市场强度条件 - 空头信号
|
||||
if self.calculate_market_strength():
|
||||
score += 1
|
||||
|
||||
return score
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
"""
|
||||
K线更新回调
|
||||
"""
|
||||
self.am.update_bar(bar)
|
||||
if not self.am.inited:
|
||||
return
|
||||
|
||||
# 计算当前得分
|
||||
self.current_score = self.calculate_score()
|
||||
|
||||
# 交易信号
|
||||
if self.current_score >= self.total_score and not self.pos:
|
||||
self.short(bar.close_price - 5, 1) # 改为做空
|
||||
elif self.current_score == 0 and self.pos < 0: # 改为判断空仓
|
||||
self.cover(bar.close_price + 5, abs(self.pos)) # 改为平空
|
||||
|
||||
self.put_event()
|
||||
|
||||
def on_trade(self, trade: TradeData):
|
||||
"""
|
||||
成交回调
|
||||
"""
|
||||
self.put_event()
|
||||
|
||||
def on_order(self, order: OrderData):
|
||||
"""
|
||||
委托回调
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_stop_order(self, stop_order: StopOrder):
|
||||
"""
|
||||
停止单回调
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,7 @@
|
||||
通过网盘分享的文件:VIP15参数文件.rar
|
||||
链接: https://pan.baidu.com/s/1liwdmvmGJ7wjHWVewZUUWA?pwd=7777 提取码: 7777
|
||||
--来自百度网盘超级会员v7的分享
|
||||
|
||||
如果过期了不能用了,联系小松鼠微信:viquant01
|
||||
|
||||
quant789.com可以使用参数选取工具
|
||||
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,144 @@
|
||||
from vnpy_ctastrategy import (
|
||||
CtaTemplate,
|
||||
StopOrder,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
OrderData,
|
||||
BarGenerator,
|
||||
ArrayManager,
|
||||
)
|
||||
|
||||
|
||||
class AtrRsiStrategy(CtaTemplate):
|
||||
""""""
|
||||
|
||||
author = "用Python的交易员"
|
||||
|
||||
atr_length = 22
|
||||
atr_ma_length = 10
|
||||
rsi_length = 5
|
||||
rsi_entry = 16
|
||||
trailing_percent = 0.8
|
||||
fixed_size = 1
|
||||
|
||||
atr_value = 0
|
||||
atr_ma = 0
|
||||
rsi_value = 0
|
||||
rsi_buy = 0
|
||||
rsi_sell = 0
|
||||
intra_trade_high = 0
|
||||
intra_trade_low = 0
|
||||
|
||||
parameters = [
|
||||
"atr_length",
|
||||
"atr_ma_length",
|
||||
"rsi_length",
|
||||
"rsi_entry",
|
||||
"trailing_percent",
|
||||
"fixed_size",
|
||||
]
|
||||
variables = [
|
||||
"atr_value",
|
||||
"atr_ma",
|
||||
"rsi_value",
|
||||
"rsi_buy",
|
||||
"rsi_sell",
|
||||
"intra_trade_high",
|
||||
"intra_trade_low",
|
||||
]
|
||||
|
||||
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()
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
Callback when strategy is inited.
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
|
||||
self.rsi_buy = 50 + self.rsi_entry
|
||||
self.rsi_sell = 50 - self.rsi_entry
|
||||
|
||||
self.load_bar(10)
|
||||
|
||||
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)
|
||||
|
||||
def on_bar(self, bar: BarData):
|
||||
"""
|
||||
Callback of new bar data update.
|
||||
"""
|
||||
self.cancel_all()
|
||||
|
||||
am = self.am
|
||||
am.update_bar(bar)
|
||||
if not am.inited:
|
||||
return
|
||||
|
||||
atr_array = am.atr(self.atr_length, array=True)
|
||||
self.atr_value = atr_array[-1]
|
||||
self.atr_ma = atr_array[-self.atr_ma_length :].mean()
|
||||
self.rsi_value = am.rsi(self.rsi_length)
|
||||
|
||||
if self.pos == 0:
|
||||
self.intra_trade_high = bar.high_price
|
||||
self.intra_trade_low = bar.low_price
|
||||
|
||||
if self.atr_value > self.atr_ma:
|
||||
if self.rsi_value > self.rsi_buy:
|
||||
self.buy(bar.close_price + 5, self.fixed_size)
|
||||
elif self.rsi_value < self.rsi_sell:
|
||||
self.short(bar.close_price - 5, self.fixed_size)
|
||||
|
||||
elif self.pos > 0:
|
||||
self.intra_trade_high = max(self.intra_trade_high, bar.high_price)
|
||||
self.intra_trade_low = bar.low_price
|
||||
|
||||
long_stop = self.intra_trade_high * (1 - self.trailing_percent / 100)
|
||||
self.sell(long_stop, abs(self.pos), stop=True)
|
||||
|
||||
elif self.pos < 0:
|
||||
self.intra_trade_low = min(self.intra_trade_low, bar.low_price)
|
||||
self.intra_trade_high = bar.high_price
|
||||
|
||||
short_stop = self.intra_trade_low * (1 + self.trailing_percent / 100)
|
||||
self.cover(short_stop, abs(self.pos), stop=True)
|
||||
|
||||
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.put_event()
|
||||
|
||||
def on_stop_order(self, stop_order: StopOrder):
|
||||
"""
|
||||
Callback of stop order update.
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,114 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pandas import Series,DataFrame
|
||||
|
||||
def initialize(context):
|
||||
# 设定沪深300作为基准
|
||||
set_benchmark('000300.XSHG')
|
||||
# 开启动态复权模式(真实价格)
|
||||
set_option('use_real_price', True)
|
||||
# 股票类交易手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
|
||||
set_order_cost(OrderCost(open_tax=0, close_tax=0.001, \
|
||||
open_commission=0.0003, close_commission=0.0003,\
|
||||
close_today_commission=0, min_commission=5), type='stock')
|
||||
def handle_data(context, data):
|
||||
#获取沪深300股票池
|
||||
stock_set=get_index_stocks('000300.XSHG')
|
||||
#此处可增加选股条件
|
||||
q = query(
|
||||
valuation.code, # 股票代码
|
||||
).filter(
|
||||
valuation.code.in_(stock_set),#只对设定股票池执行
|
||||
)
|
||||
#获取财务数据,指定日期为回测当天
|
||||
current_date=context.current_dt.strftime('%Y-%m-%d')
|
||||
fdf = get_fundamentals(q,current_date)
|
||||
#取前50只股
|
||||
fdf=fdf.head(100)
|
||||
#获取股票列表
|
||||
get_stocks=list(fdf['code'])
|
||||
# 去除ST,*ST
|
||||
st=get_extras('is_st',get_stocks,current_date,current_date, df=True)
|
||||
st=st.loc[current_date]
|
||||
get_stocks=list(st[st==False].index)
|
||||
#考虑5天的历史数据
|
||||
num=5
|
||||
#轨线用前面20天数据计算
|
||||
days=20
|
||||
#每只股票可用资金为当前资金除以50
|
||||
cash=context.portfolio.available_cash/100
|
||||
#获取所有股票前num+days天收盘价数据
|
||||
price=history(num+days,'1d','close',get_stocks,skip_paused=True)
|
||||
where_are_nan = np.isnan(price)
|
||||
where_are_inf = np.isinf(price)
|
||||
price[where_are_nan] = 0
|
||||
price[where_are_inf] = 0
|
||||
#循环每只股
|
||||
for security in get_stocks:
|
||||
#用数组保存均值,大小为num
|
||||
mid=np.arange(num)
|
||||
#标准差
|
||||
std=np.arange(num)
|
||||
#定义数组大小
|
||||
close_data=np.arange(num*days).reshape(num,days)
|
||||
for i in range(0,num):
|
||||
for j in range(0,days):
|
||||
close_data[i][j]=price[security][i+j]
|
||||
#中轨线即均值为days天收盘价数据平均
|
||||
mid[i]=np.mean(close_data[i])
|
||||
#计算标准差
|
||||
std[i]=np.std(close_data[i])
|
||||
#上轨线=中轨线+两倍的标准差
|
||||
up=mid[num-1]+2*std[num-1]
|
||||
#下轨线
|
||||
down=mid[num-1]-2*std[num-1]
|
||||
#保存num天数据,判断开口收口或平口
|
||||
boll=0
|
||||
for i in range(0,num-1):
|
||||
if std[i]>std[i+1]:
|
||||
boll=boll+1
|
||||
else:
|
||||
boll=boll-1
|
||||
|
||||
#判断目前股票是否停牌
|
||||
paused=data[security].paused
|
||||
|
||||
#取得当前股票价格
|
||||
current_price=data[security].price
|
||||
#如果连续num天开口
|
||||
if boll==-4:
|
||||
#如果当前价格超过昨日的上轨且价格高于均线
|
||||
if current_price>up and current_price>mid[num-1]:
|
||||
#计算可以买多少股票
|
||||
num_of_shares=int(cash/current_price)
|
||||
#如果可以买的数量超过0并且股票未停牌
|
||||
if num_of_shares>0 and paused==False:
|
||||
#购买股票
|
||||
order(security,+num_of_shares)
|
||||
#如果当前价格跌破了昨日的下轨且价格低于均线
|
||||
elif current_price<down and current_price<mid[num-1]:
|
||||
#如果股票未停牌
|
||||
if paused==False:
|
||||
#将股票卖空
|
||||
order_target(security,0)
|
||||
#如果连续num天收口
|
||||
if boll==4:
|
||||
#股价超过上轨且价格低于均线时卖
|
||||
if current_price>up and current_price<mid[num-1]:
|
||||
if paused==False:
|
||||
order_target(security,0)
|
||||
#跌破下轨且价格高于均线时买
|
||||
elif current_price<down and current_price>mid[num-1]:
|
||||
num_of_shares=int(cash/current_price)
|
||||
if num_of_shares>0 and paused==False:
|
||||
order(security,+num_of_shares)
|
||||
#连续平口
|
||||
if boll in range(-1,1):
|
||||
#价格在中轨线上且昨日均价高于三天前
|
||||
if current_price>mid[num-1] and mid[num-1]>mid[num-3]:
|
||||
num_of_shares=int(cash/current_price)
|
||||
if num_of_shares>0 and paused==False:
|
||||
order(security,+num_of_shares)
|
||||
if current_price<mid[num-1] and mid[num-1]<mid[num-3]:
|
||||
if paused==False:
|
||||
order_target(security,0)
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
# coding: utf-8
|
||||
|
||||
# In[ ]:
|
||||
|
||||
# 导入函数库
|
||||
import jqdata
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
# 初始化函数,设定基准等等
|
||||
def initialize(context):
|
||||
# 设定沪深300作为基准
|
||||
set_benchmark('000300.XSHG')
|
||||
# 开启动态复权模式(真实价格)
|
||||
set_option('use_real_price', True)
|
||||
# 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
|
||||
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
|
||||
|
||||
#获取沪深300股票池
|
||||
stock_set=get_index_stocks('000300.XSHG')
|
||||
#此处可增加选股条件
|
||||
q = query(
|
||||
valuation.code, # 股票代码
|
||||
).filter(
|
||||
valuation.code.in_(stock_set),#只对设定股票池执行
|
||||
)
|
||||
fdf = get_fundamentals(q)
|
||||
#取前50只股
|
||||
fdf=fdf.head(50)
|
||||
#获取股票列表
|
||||
stock_list=list(fdf['code'])
|
||||
trend={}
|
||||
for security in stock_list:
|
||||
trend[security]='None'
|
||||
|
||||
def handle_data(context, data):
|
||||
N = 20 # 计算TR时的N
|
||||
M = 49 # 计算MATRIX时的M
|
||||
num=3
|
||||
length_of_data = N+M+num # 取closeprice的天数,为了足够计算MATRIX、TRIX
|
||||
|
||||
cash=context.portfolio.available_cash/50
|
||||
for security in stock_list:
|
||||
close_price=attribute_history(security,length_of_data,'1d',('close'),skip_paused=True)
|
||||
where_are_nan = np.isnan(close_price)
|
||||
where_are_inf = np.isinf(close_price)
|
||||
close_price[where_are_nan] = 0
|
||||
close_price[where_are_inf] = 0
|
||||
|
||||
MA5=close_price['close'][-5:].mean()
|
||||
MA10=close_price['close'][-10:].mean()
|
||||
ma5=close_price['close'][-6:-1].mean()
|
||||
ma10=close_price['close'][-11:-1].mean()
|
||||
|
||||
price_array={}
|
||||
for i in range(0,M+num):
|
||||
price_array[i]=close_price['close'][i:i+N]
|
||||
#TR=收盘价的N日指数移动平均
|
||||
TR={}
|
||||
for i in range(0,M+num):
|
||||
TR[i]=np.mean(price_array[i])
|
||||
#TRIX=(TR-昨日TR)/昨日TR*100
|
||||
TRIX={}
|
||||
for i in range(1,M+num):
|
||||
if TR[i]==0:
|
||||
TRIX[i]=0
|
||||
continue
|
||||
TRIX[i]=(TR[i]-TR[i-1])/TR[i]*100
|
||||
#MATRIX=TRIX的M日简单移动平均
|
||||
MATRIX={}
|
||||
for i in range(0,num):
|
||||
TRIX_sum=0
|
||||
for j in range(1,M):
|
||||
TRIX_sum=TRIX_sum+TRIX[i+j]
|
||||
MATRIX[i]=TRIX_sum/M
|
||||
current_price=data[security].price
|
||||
|
||||
length=0
|
||||
for i in range(0,num-1):
|
||||
if TRIX[M+i]>MATRIX[i]:
|
||||
length=length+1
|
||||
else:
|
||||
length=length-1
|
||||
if length>0 and MATRIX[num-1]>MATRIX[0]:
|
||||
trend[security]='up'
|
||||
elif length<0 and MATRIX[num-1]<MATRIX[0]:
|
||||
trend[security]='down'
|
||||
|
||||
if trend[security]=='up':
|
||||
order_value(security,cash)
|
||||
elif trend[security]=='down' and security in context.portfolio.positions:
|
||||
if MA5<MA10:
|
||||
order_target(security,0)
|
||||
elif MA5>MA10 and ma5<ma10 :
|
||||
order_value(security,cash)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
eyJ2ZXJzaW9uIjoiMS4wLjAuMiIsImF1dGhvciI6IiIsImF1dGhlbm5hbWUiOiJWSVAxNOW3peS9nOWMuiIsImRlc2MiOiIiLCJkYXRhIjoidkkrbXNwbjRMa1VFbnoxUlNoYSthSStsREpXK2JGS2VKS1VNbjl5ZkY3Wjd2ZUtuU1pHTkNWOFk2UGlrNG9LWWs3MU51NCtQRVF3bU12REZLQ3piWk1LNXQ2RnNqbmZBYlZ0bWRYd3ZEZFpjdHNOOVFVRWRNdnRJUjVQbGRjZXJZQ21MVm1NTGI2RXRDdHBjU3FUSGplVXNOWWpCK3VIdUU0YUNiNEdUMzVPcUw0MnJndHcrMUhiMkpZUWE4eEt2UmlBZXBFWUFveSt1V0hDcWFPTXZwZVVoMExkSU43TFY2NnY4SmFDZ3V3L0hieXRVb2NGTFZrWE4rdGFkWkgyZUMzeTFIcnZqSzNtVENROUlvandGVXE5NDFhN3hUUjRvSVJnNFFPWFIvTElzUlRFTzZCanVFVk1tVlFDdU5QamM4d2tGd2FpN0c4Yy9RSnhrdlZRZWo2QVlhK3RsMTFhYXVSaVJkaVMzMjl1WTN3STFOQ084YU9zdXVuWEFhdU5ScUx4VTlxYk13ZE9nb1pNY2FjTURHNmk4VlBhbXpNSFRvWDRLR2V2SXU2VnJtTlYvcEozL1l0TnljVUVVRm1kK2U3aHVBeWZKNVljL1k2MnNRMnBHSkhvYlBzbU1qT29tRWxSYmkzSUprUnVpV25UZGpjREpjU050NjRhTDErZExNT2lmUWszcXg2MTN5Umw4azE0VHRBZEpoRkZWZ2xXV05ZM0pCdVZnODBZT2QyR05OVnB3ZzlLUUo5NHFVTnd4Q0lrRUFtQ29XTDRiSGhjV2g1blBCTHp4REwwUm5CS3dKOUlMUzN1d3BnMjhSVlFIUG1uVDRLekd6UllVTXdjaFZ3ckp4MkNHbHhzNE0rd2ltOGswSmExOThlaUVEUTZNUnlVSk9MSURzUTlBanRQYkdSY2g2TDRVYVRNVGRWRWFHQUR5cjJYZHZYYTEvOGFXRFd3SitYai9jQXdNOU1ySEV5ME1FZ1UzdTBHQ2tzbkUrTng4SUpua3FpY2dBU016QkEybzNPemJ2LzI3ZWJucmVzc3RYNlEzSUQrOVJhT2puK1IvNUQ5RzZTRTNnTGoxRC9Xa0lLemF4TmEyUVZTeVk5OXF3MUJwZ3NtT3ZOL3FvSTNDLzdxVWNTWWpRckdQbkFDVHgzemlTemxaM09IY2EyZitNd1ZSdURZWVk4NEhheDhwdVhlcGg1SUlkbHBVOVM0WVJ3bmdrb2t1aVZPcFRYUnUvbFEzY0lIZXlKNVNKdzRvUGd4cWVieDFFTlhFM0JYd1UvcFgrYkorcElKcmxjbEJVbDNZYkYzTEdMcENSWVBPZjQ2Q0FZS0JlMHQveUtvTC95NEs4dXhVM1dtUm5kQkhrQnZxa3hVWTFFeXo4aVlzZnN4MjZlaWZLRWJCZndyaGQrekRCT2dMWUVoN1p1ZzBuTEdNTUxLd3VVSU5XNjd6azdhMnZyVmRORlRUeDlpcXRRbW5DZmdJRW9iWmpqdldXdElaSGNTRDY3MWVPRXEyREplTVhlNjQ1ZlYrQW8waDZydVNPaGpQWWY1UzZ6cFFldmxEeGhLRmcya2NMUVhNL1BmdUYxc0tjdFpsUjhOdWJOYjVHUzY4dmg5OEE4WEZXdjcwcmM1NVMyVkp3dGpoVG9HYkQ0YTJKdVhhRDhjOG9oOENpK3A1UkVsQSsvWFBLdXpYWE9tRTFMdEJZRDR0OHhWT0JqN29xeUdaTnBEV1lFRmdQY1oxdFFxSGJkYjc1NjhSc0ZvZ2JaQnVjME5nNTdmZXRKK2V6THRQcHFEUG1CSmhHZWJpM3JjTEhiVzFQMXpKOWl4VFhmUlVrWCtPV3B2NFRUeUZCQ1g4VDhLK3A2UDM1MHRqMkNRSjB6SGN0MitDdHZFMVhEMHRoRmhVUWdLeHRFMVJrWHRyNW5tcS95SmlqR08xVFNNaEdySG9RcWhmRW8wWFJUTGpiZEI2VWsrTTQ2MVdvV2J5Um9wZzZOSjZVZ0hMMVRSMFY5cmw2cHRtUFZ0WERnWmJyRExqV3RvSFJqSmsyUUo3WWdYcjJVc1hBa0g0ZERaWDE5YTBRRVlVRXE3R1I4T1RZc1QyKzFwSHpCZzlJZGdlZlowcm1FdU0yeXNsdjc2bVFobjNQSXJuNWYvckVxVmFPTlA2ZnlHdTdMbXJVaHVGNkowYW5BRU1LVkE3SXdIMmNoSWxmdGRpRDdTZGdPb2ZpSFZqd21xUFBDQlA0NUhEZ2N1UWE4OVVETjJtSVpLemVxcjBWQUl3QWRpNENVVXdBT0lFVFd3ZDRpRW5Ld2RpYkcrbTQ2ci96UlpxbDdEVWdOanM5c09XMVEydmhBN1M4Skt5b1lrb204OXJJNkhnN1ZlRjhrdWEyUU9HSFNTOHYyaVJoWVZMTXJNdDcwRmxjc21FbzFZaXZ0UUJHK1EzMExpc2g2V2lHWFpWbHBlcWJTSXI4QitlbldGQVpMdEEwamU2SDRDeExuQTJaK3ZtK0RoTTl2WFd2VVJNOFZuVUs0V3E3QjBFdkFnQlR6NlV1dDRMRk1QeFhLMjdHZG1ZU2ZvODNLQUZ4TzI3dzlWS0g5RUYzaHNlRnhhSG1jOEU5VzRDelJIc2pXemdEOU9uaVp6WEV3SDZxWTdsSUZzUHJNYk5GaFF6QnlGWENzbkhZSWFYR3pnejdDS2J5VFFsYjdGSTN1WTZXTmVWa2pENFEySjlpTGZ0c0FCNk1LbGFNVnpEQmtGWldpbGVaekVBd1RHYUtmUCsydGpVOC9hLzhmbGZsbHVpSHR6Z0JCWmZmTzBscUdIb1RaODFqREpPWTZUc2h5Ui84aEhMbXRJL0ZnYnhObDJsY1MvcTUwYjBieURWZmFyWHpLWjd0ZVdqQ1krUWtWOWJwWDhlVlNmTDJGK3RRblpkNDRwWDNHaHB2N082UUN0QXlyYVh5aG1rUCtqa0prQ2EwV0g5dXpicENZS0tsKzMvTVJrZXpSMTlPSE95YlE4MFFab1J2R2drdjhXZno0WG1vOTZySDYrcDNJcTJiTDhCdTNmT2drRG1lTzNac0tHMkxQa1VGNXNzVDdyWG5SVlBaaEt3UzBFVGh1Z0xqUGhKM0JEcTdyOFpLMXBIVHFBVlhnSFFDQTNOS1FCMndSZFAyd2NaWDIxWFlGc2NSelptazBSd1F0NlFJM1BvYmhQWDl6dEdCTFd6L1FCbVNqWEZFempPc0s0KzdPNWZuTjFSUFdCeXc4Nkg4TEF5WE1uK2NWc0FPQml1SldROEpZMXliR2tjeEtyc1I3MEZqbzhBMDA4ZUdlWXJUWjlWaSt4QzY1dkRYMjByNVNZN0VWV01TSmJZOHI4RGVvWHVnTFhTSDduSDhrWWFETFdYblZITTBnaU0zVXZiSU11ODBKTXBUWWJqMmJSQlVUTE92YStINGFFTXdyc2YxbGZsdThCeEdnZVNwZ2M5Y25LTUxGZmRPZTBVUDZoT1pjd2ZQUERXWjkydTVqK3hoN05yOUpGWGFwcDNUcWtCOXdHU0ZhVFU0WXhkYjFnNERxT1pmcWxCdzFKVjBGWmUrSGdycmpUV3dicVpJZGdnUXpDSUUwaHNWMlc1bVBSaGY5NENzWlJjZ243ay83cUdjY0ZKL1NxT1RDazRrQ1EydkdmVTdOZW5VWHBaL2Q3TmhEbFdiYlZDZlNTV1V3ZHJ3ZkhkVE9Gc2pTNWp5a3VyYStpcTZMZENGT1JIQmplckZySXZrRXhOQ0dnR2pFWXNmZ0VueCsrWHcyTUxaRTNNUlR0eklSQ2FoQnN4dHdlV3MwQVpKSUFnOUpxYXpJQXdzNmM2TktoOEZRVmdyZnpJREdwWjZrL2FZTnZZN1k1RjltbHVtQi92M25WeVMrL0lNSlAyR3dWdTlHYzk0cytyQ2dXT1YrUDNNaWlnb1dpR082bk9iOHRJb1VkVFF0SkJiWkJhanNKQk9pRFFzUnJFblQvOFRHbWNDeUgyMzNNakw1TVNjUVN0dzIxcW43NUlqc1FCTDBZZU5xSk9pT1pUM25CcVRhaUF4MXNQWC9raDBsc1RobWNiOHB5MldINGU4UXlKOEdaNnhGQkJFVjJJU1lsb29DMXFrRTdSd0NJYmJYbFRLVlYxbzkreUJ5T0VtcGw2aUNybUtVUlNqQzlhb21xb3Y5cTRiYkFZY2xvQzdHTW1zSzR5bWJCZ1h6THA4STk5OTVYMXk2TnR6NTk1WVBFaU9tYktjeXUwb2Fua1V5YmhSbm5zeHd3dk5LM005QnloMU5NNDBQTmNBUFgwM21hTFpFZVpiOXB5dnFjaW1PMzJKQ2hydnMzNHJXa3lRNW5OdDVQZDRoeGxySVA2ZEFSdzhMRlRjWGhmbENUeFU3a3dhU2RHUXFtSGE2NmR5T0xlckwzSFlMWmJqN3IzdGFDeStrMTRRRTRZMnJVaW9FZTJIV1FaMTFOd082VXhXdUY2QkVHL3FSMTVMcXFIL2VmK29USG5HWjdCVklRbi9FMnFBMEVrSEFZUFkzZ3lqUjF5QkFTYmN1MDNkMjNJVU04dnFLZExybXFqaU1QSm1nbjRQYStTeVh5OTgrZVF3VVZPNEV6UEF6YW5aQi9XK2NwZ1ZpY214R2xiQndhUGVpYVZIdGN0dllyek9zTXJjU2ZReE9sL3JhMW4xVkI0TVhmNzB0WnlmU0hKbS9lQnlKNWxHMElhUzUweW5SazU1SGdOS0c5SWRXMVgrMVBMLzZsa21mZ2x3UVlvSmJValNNemFwaVVXK1NhRkF6YTZhN0UwMkNvaWV3UXZZNHJYcndhTnRmMTRvZ05Cem01QVZVYnJETHMwZDdtQTFVdm4rUE9kbjNjSzNMbE81b0UxbWhKOHVrR3VjNUpXM1pKNEFaNnRLine truncated
|
||||
@@ -0,0 +1,378 @@
|
||||
from typing import List, Dict
|
||||
from datetime import datetime
|
||||
|
||||
from vnpy_portfoliostrategy import (
|
||||
StrategyEngine,
|
||||
StrategyTemplate,
|
||||
TickData,
|
||||
BarData,
|
||||
TradeData,
|
||||
ArrayManager,
|
||||
)
|
||||
|
||||
from vnpy.trader.utility import Interval
|
||||
from vnpy_portfoliostrategy.utility import PortfolioBarGenerator
|
||||
import numpy as np
|
||||
import talib as ta
|
||||
|
||||
#使用portfoliostrategy模块编写的交易策略,无参数优化,请自行调试优化参数。
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
class VIP14(StrategyTemplate):
|
||||
""""""
|
||||
|
||||
author = "松鼠quant"
|
||||
|
||||
N = 2
|
||||
M = 5
|
||||
X = 5
|
||||
TRS=5
|
||||
|
||||
fixed_size=1
|
||||
|
||||
am_1=0
|
||||
am_2=0
|
||||
current_bar=0
|
||||
|
||||
parameters = [
|
||||
"N",
|
||||
"M",
|
||||
"X",
|
||||
"fixed_size",
|
||||
"TRS"
|
||||
]
|
||||
variables = []
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
strategy_engine: StrategyEngine,
|
||||
strategy_name: str,
|
||||
vt_symbols: List[str],
|
||||
setting: dict
|
||||
):
|
||||
""""""
|
||||
super().__init__(strategy_engine, strategy_name, vt_symbols, setting)
|
||||
|
||||
self.KG=0
|
||||
self.high_after_entry=0
|
||||
self.low_after_entry=0
|
||||
self.dliq_point=0
|
||||
self.kliq_point=0
|
||||
self.curbar_h = np.zeros(100) # 例如,初始化为长度为 100 的数组
|
||||
self.curbar_l = np.zeros(100)
|
||||
# 初始化 hd 属性
|
||||
self.hd = np.zeros(100) # 例如,初始化为长度为 100 的数组
|
||||
self.ld = np.zeros(100) # 例如,初始化为长度为 100 的数组
|
||||
self.targets: Dict[str, int] = {}
|
||||
self.last_tick_time: datetime = None
|
||||
|
||||
# Obtain contract info
|
||||
self.ams: Dict[str, ArrayManager] = {}
|
||||
for vt_symbol in self.vt_symbols:
|
||||
self.ams[vt_symbol] = ArrayManager(100)
|
||||
self.targets[vt_symbol] = 0
|
||||
|
||||
self.pbg = PortfolioBarGenerator(self.on_bars, 2, self.on_2hour_bars, Interval.HOUR)
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
Callback when strategy is inited.
|
||||
"""
|
||||
self.write_log("策略初始化")
|
||||
|
||||
self.load_bars(10)
|
||||
|
||||
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.pbg.update_tick(tick)
|
||||
|
||||
def on_bars(self, bars: Dict[str, BarData]):
|
||||
"""
|
||||
Callback of new bars data update.
|
||||
"""
|
||||
self.pbg.update_bars(bars)
|
||||
|
||||
|
||||
def on_2hour_bars(self, bars: Dict[str, BarData]):
|
||||
""""""
|
||||
self.cancel_all()
|
||||
# 初始化
|
||||
nx = 0
|
||||
|
||||
# 获取合约的数据
|
||||
for vt_symbol, bar in bars.items():
|
||||
nx += 1
|
||||
# 更新 ArrayManager
|
||||
am: ArrayManager = self.ams[vt_symbol]
|
||||
am.update_bar(bar)
|
||||
#print(vt_symbol)
|
||||
# 如果 ArrayManager 还未初始化完毕,则跳过
|
||||
if not am.inited:
|
||||
return
|
||||
# 将第一个和第二个合约的 ArrayManager 分别保存下来
|
||||
if nx == 1:
|
||||
self.am_1 = am
|
||||
elif nx == 2:
|
||||
self.am_2 = am
|
||||
|
||||
am_1=self.am_1
|
||||
am_2=self.am_2
|
||||
|
||||
self.current_bar=self.current_bar+1 #Bar线计数
|
||||
# 计算相关指标
|
||||
c_jb = am_1.close_array / am_2.close_array # 计算价比
|
||||
copen = am_1.open_array / am_2.open_array # 计算开比
|
||||
chigh = np.maximum(np.maximum(am_1.high_array / am_2.high_array, copen), c_jb)
|
||||
clow = np.minimum(np.minimum(am_1.low_array / am_2.low_array, copen), c_jb)
|
||||
|
||||
# 计算多个指标的序列
|
||||
var1 = self.highest(chigh, self.N) - self.lowest(clow, self.N)
|
||||
var2 = self.highest(chigh, self.N) - c_jb[-self.N:]
|
||||
var3 = c_jb[-self.N:] - self.lowest(clow, self.N)
|
||||
var4 = var2 / var1 * 100 - 70
|
||||
var5 = (c_jb[-self.N:] - self.lowest(clow, self.N)) / var1 * 100
|
||||
var6 = (2 * c_jb + chigh + clow) / 4
|
||||
var7 = self.sma(var3 / var1 * 100, 3, 1)
|
||||
var8 = self.lowest(clow, self.M)
|
||||
var9 = self.sma(var7, 3, 1) - self.sma(var4, 9, 1)
|
||||
vara = self.highest(chigh, self.M)
|
||||
bd1 = self.xaverage((var6[-self.M:] - var8) / (vara - var8) * 100, self.M)
|
||||
bd2 = self.xaverage(bd1, self.X)
|
||||
ma1 = self.xaverage(c_jb, self.X)
|
||||
# 检查金叉 (BD1 从小于 BD2 变为大于 BD2)
|
||||
dk_up = bd1[-1] > bd2[-1] and bd1[-2] <= bd2[-1]
|
||||
|
||||
# 检查死叉 (BD1 从大于 BD2 变为小于 BD2)
|
||||
kk_dn = bd1[-1] < bd2[-1] and bd1[-2] >= bd2[-1]
|
||||
|
||||
# 确保 dk_up 和 kk_dn 不为空
|
||||
if dk_up:
|
||||
self.curbar_h = self.current_bar # 在最后一次金叉发生时更新 curbar_h
|
||||
period = max(self.curbar_h - self.curbar_l, self.M)
|
||||
|
||||
# 确保 period 不超过 chigh 的长度
|
||||
if len(chigh) >= period:
|
||||
Highup = max(chigh[-period:-1])
|
||||
else:
|
||||
Highup = max(chigh[-self.M:-1]) # 如果 period 超出范围,则取整个序列的最大值
|
||||
|
||||
if kk_dn:
|
||||
self.curbar_l = self.current_bar # 在最后一次死叉发生时更新 curbar_l
|
||||
period = max(self.curbar_l - self.curbar_h, self.M)
|
||||
|
||||
# 确保 period 不超过 clow 的长度
|
||||
if len(clow) >= period:
|
||||
Lowdown = min(clow[-period:-1])
|
||||
else:
|
||||
Lowdown = min(clow[-self.M:-1]) # 如果 period 超出范围,则取整个序列的最小值
|
||||
|
||||
#print(f'bd1[-1]{bd1[-1] },bd2[-1] {bd2[-1]},chigh[-1]{chigh[-1]},Highup{Highup},c_jb[-1]{c_jb[-1]},ma1[-1]{ma1[-1]}')
|
||||
# 开仓条件
|
||||
cond_dk = (bd1[-1] > bd2[-1]) & (chigh[-1] >= Highup) & (c_jb[-1] > ma1[-1])
|
||||
cond_kk = (bd1[-1] < bd2[-1]) & (clow[-1] <= Lowdown) & (c_jb[-1] < ma1[-1])
|
||||
|
||||
self.KG = np.where(cond_dk, 1, np.where(cond_kk, -1, 0))
|
||||
|
||||
#print(self.KG)
|
||||
#开仓部分
|
||||
nx=0
|
||||
for vt_symbol, bar in bars.items():
|
||||
nx+=1
|
||||
am: ArrayManager = self.ams[vt_symbol]
|
||||
if not am.inited:
|
||||
return
|
||||
price = bar.close_price
|
||||
current_pos = self.get_pos(vt_symbol)
|
||||
#print(nx,current_pos,self.KG)
|
||||
#数据源1:
|
||||
if nx==1:
|
||||
#开多
|
||||
if self.KG>0 :
|
||||
|
||||
if current_pos<0:
|
||||
self.cover(vt_symbol, price, self.fixed_size)
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
if current_pos==0:
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
#开空
|
||||
if self.KG<0 :
|
||||
if current_pos > 0:
|
||||
self.sell(vt_symbol, price, self.fixed_size)
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
if current_pos == 0:
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
#数据源2
|
||||
elif nx==2:
|
||||
#开多
|
||||
if self.KG>0:
|
||||
if current_pos < 0:
|
||||
self.cover(vt_symbol, price, self.fixed_size)
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
if current_pos == 0:
|
||||
self.short(vt_symbol, price, self.fixed_size)
|
||||
self.high_after_entry = chigh[-1]
|
||||
#开空
|
||||
if self.KG<0 and current_pos >= 0 :
|
||||
if current_pos > 0 :
|
||||
self.sell(vt_symbol, price, self.fixed_size)
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
if current_pos == 0 :
|
||||
self.buy(vt_symbol, price, self.fixed_size)
|
||||
self.low_after_entry = clow[-1]
|
||||
#移动出场
|
||||
if self.KG == 0:
|
||||
self.high_after_entry = chigh[-1]
|
||||
self.low_after_entry = clow[-1]
|
||||
elif self.KG != 0:
|
||||
self.high_after_entry = min(self.high_after_entry, chigh[-1])
|
||||
self.low_after_entry = max(self.low_after_entry, clow[-1])
|
||||
|
||||
# Calculate liquidation points
|
||||
if self.KG > 0:
|
||||
self.dliq_point = self.low_after_entry - (copen[-1] * self.TRS / 1000)
|
||||
elif self.KG < 0:
|
||||
self.kliq_point = self.high_after_entry + (copen[-1] * self.TRS / 1000)
|
||||
|
||||
nx=0
|
||||
# print("多头",c_jb[-1],self.dliq_point)
|
||||
# print("空头",c_jb[-1],self.kliq_point)
|
||||
for vt_symbol, bar in bars.items():
|
||||
nx+=1
|
||||
am: ArrayManager = self.ams[vt_symbol]
|
||||
if not am.inited:
|
||||
return
|
||||
price = bar.close_price
|
||||
current_pos = self.get_pos(vt_symbol)
|
||||
|
||||
#数据源1
|
||||
if nx==1:
|
||||
|
||||
if self.KG > 0 and c_jb[-1]<= self.dliq_point and self.dliq_point > 0 and current_pos>0:
|
||||
self.sell(vt_symbol, price, current_pos)
|
||||
if self.KG < 0 and c_jb[-1] >= self.kliq_point and self.kliq_point > 0 and current_pos<0:
|
||||
self.cover(vt_symbol, price, current_pos)
|
||||
#数据源2
|
||||
if nx==2:
|
||||
if self.KG > 0 and c_jb[-1]<= self.dliq_point and self.dliq_point > 0 and current_pos<0:
|
||||
self.cover(vt_symbol, price, current_pos)
|
||||
if self.KG < 0 and c_jb[-1] >= self.kliq_point and self.kliq_point > 0 and current_pos>0:
|
||||
self.sell(vt_symbol, price, current_pos)
|
||||
|
||||
self.put_event()
|
||||
|
||||
|
||||
def highest(self, data, period):
|
||||
"""
|
||||
计算序列最后 period 个元素中的最高值,返回一个序列
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素中的最高值序列
|
||||
"""
|
||||
result = []
|
||||
start_index = max(0, len(data) - period)
|
||||
|
||||
for i in range(start_index, len(data)):
|
||||
result.append(max(data[max(0, i - period + 1):i + 1]))
|
||||
|
||||
return np.array(result)
|
||||
|
||||
|
||||
def lowest(self, data, period):
|
||||
"""
|
||||
计算序列最后 period 个元素中的最小值,返回一个序列
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素中的最小值序列
|
||||
"""
|
||||
result = []
|
||||
start_index = max(0, len(data) - period)
|
||||
|
||||
for i in range(start_index, len(data)):
|
||||
result.append(min(data[max(0, i - period + 1):i + 1]))
|
||||
|
||||
return np.array(result)
|
||||
|
||||
|
||||
def sma(self, data, period, weight=1):
|
||||
"""
|
||||
计算简单移动平均(SMA)的序列,仅计算最后 period 个元素
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:param weight: 权重
|
||||
:return: SMA序列
|
||||
"""
|
||||
if len(data) < period:
|
||||
return np.convolve(data, np.ones((len(data),))/len(data), mode='valid')
|
||||
else:
|
||||
return np.convolve(data[-period:], np.ones((period,))/period, mode='valid')
|
||||
|
||||
|
||||
|
||||
|
||||
def xaverage(self, data, period):
|
||||
"""
|
||||
计算加权平均(WMA)的序列
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: WMA序列
|
||||
"""
|
||||
if len(data) < period:
|
||||
weights = np.arange(1, len(data) + 1)
|
||||
return np.convolve(data, weights/weights.sum(), mode='full')[-len(data):]
|
||||
else:
|
||||
weights = np.arange(1, period + 1)
|
||||
return np.convolve(data[-period:], weights/weights.sum(), mode='full')[-period:]
|
||||
|
||||
|
||||
|
||||
def HHV(self, data, period):
|
||||
"""
|
||||
计算给定数据序列中最后 period 个元素的最大值
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素的最大值
|
||||
"""
|
||||
if len(data) == 0:
|
||||
return None # 如果数据为空,返回 None 或者抛出异常
|
||||
|
||||
if period > len(data):
|
||||
period = len(data) # 如果 period 超出数据长度,则取数据长度
|
||||
|
||||
return max(data[-period:])
|
||||
|
||||
|
||||
def LLV(self, data, period):
|
||||
"""
|
||||
计算给定数据序列中最后 period 个元素的最小值
|
||||
:param data: 数据序列
|
||||
:param period: 周期
|
||||
:return: 最后 period 个元素的最小值
|
||||
"""
|
||||
if len(data) == 0:
|
||||
return None # 如果数据为空,返回 None 或者抛出异常
|
||||
|
||||
if period > len(data):
|
||||
period = len(data) # 如果 period 超出数据长度,则取数据长度
|
||||
|
||||
return min(data[-period:])
|
||||
@@ -0,0 +1,171 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 加载功能模块\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from vnpy.trader.constant import Interval\n",
|
||||
"\n",
|
||||
"from elite_optionstrategy import BacktestingEngine\n",
|
||||
"\n",
|
||||
"from AdvancedSpreadStrategy_dualthrust2 import AdvancedSpreadStrategy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"设置回测的参数。参数及其含义如下 1. vt_symbol ==> 产品名称 2. interval ==> 周期 3. start ==> 开始时间 4. rate ==> 手续费 5. slippage ==> 滑点 6. size ==> 合约乘数 7. pricetick ==> 价格跳动 8. capital ==> 回测资本 9. end ==> 截止时间 10. mode ==> 回测的模式,一共有两种:BacktestingMode.BAR和BacktestingMode.TICK 11. inverse ==> 周期"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 创建回测引擎\n",
|
||||
"engine = BacktestingEngine()\n",
|
||||
"\n",
|
||||
"engine.set_parameters(\n",
|
||||
" interval=Interval.MINUTE,\n",
|
||||
" start=datetime(2024, 7, 1),\n",
|
||||
" end=datetime(2024, 8, 30),\n",
|
||||
" rate=0,\n",
|
||||
" slippage=0.6 + (16 / 100),\n",
|
||||
" capital= 1000000\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"setting = {\n",
|
||||
" # \"aroon_window\": 5,\n",
|
||||
" \"k1\":0.3,\n",
|
||||
" \"k2\": 0.9,\n",
|
||||
" \"percent_add\": 0,\n",
|
||||
" \"otm_level\": 0\n",
|
||||
"}\n",
|
||||
"engine.add_strategy(AdvancedSpreadStrategy, setting)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" 72%|███████▏ | 44/61 [02:21<00:55, 3.28s/it]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# 历史数据回放\n",
|
||||
"engine.run_backtesting()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 计算每日盈亏\n",
|
||||
"engine.calculate_result()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 统计绩效结果\n",
|
||||
"result = engine.calculate_statistics()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 显示资金图表\n",
|
||||
"engine.show_chart()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 打印逐笔成交\n",
|
||||
"for trade in engine.all_trades.values():\n",
|
||||
" print(trade.datetime, trade.vt_symbol, trade.direction.value, trade.offset.value, trade.volume, \"@\", trade.price)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.9"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1b43cb0bd93d5abbadd54afed8252f711d4681fe6223ad6b67ffaee289648f85"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
from datetime import time
|
||||
from vnpy.trader.constant import Interval
|
||||
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
|
||||
from vnpy.trader.object import TickData, BarData
|
||||
|
||||
from elite_optionstrategy import (
|
||||
StrategyTemplate,
|
||||
Variable,
|
||||
Parameter,
|
||||
PortfolioData,
|
||||
ChainData,
|
||||
OptionData,
|
||||
OptionBarGenerator,
|
||||
)
|
||||
|
||||
|
||||
class AdvancedSpreadStrategy(StrategyTemplate):
|
||||
"""基于Dual Thrust信号做空符合价差的策略"""
|
||||
|
||||
author: str = "用Python的交易员"
|
||||
|
||||
option_portfolio: str = Parameter("MO") # 期权产品代码
|
||||
underlying_symbol: str = Parameter("IMJQ00.CFFEX") # 标的合约代码
|
||||
k1: float = Parameter(0.3) # 多头系数
|
||||
k2: float = Parameter(0.9) # 空头系数
|
||||
exit_time: time = Parameter(time(14, 55)) # 平仓时间
|
||||
percent_add: float = Parameter(0.002) # 委托超价比例
|
||||
otm_level: int = Parameter(0) # 做空期权档位
|
||||
leg1_ratio: int = Parameter(4) # 顺势腿的比例
|
||||
leg2_ratio: int = Parameter(1) # 逆势腿的比例
|
||||
|
||||
dual_thrust_signal: int = Variable(0) # 当前信号多空(1多头,-1空头,0无信号)
|
||||
atm_strike: float = Variable(0) # 当前平值行权价
|
||||
|
||||
def on_init(self) -> None:
|
||||
"""策略初始化"""
|
||||
self.write_log("策略初始化")
|
||||
|
||||
# K线截面合成器
|
||||
self.obg: OptionBarGenerator = OptionBarGenerator(self.on_bars)
|
||||
|
||||
# 订阅行情
|
||||
self.subscribe_options(self.option_portfolio)
|
||||
self.subscribe_data(self.underlying_symbol)
|
||||
|
||||
# 标的信号对象
|
||||
self.factor: DualThrustFactor = DualThrustFactor(
|
||||
self.underlying_symbol, self.k1, self.k2, self.exit_time
|
||||
)
|
||||
|
||||
# 加载标的历史数据初始化
|
||||
bars: list[BarData] = self.load_bars(self.underlying_symbol, 100, Interval.MINUTE)
|
||||
for bar in bars:
|
||||
self.factor.update_bar(bar)
|
||||
|
||||
# 缓存文件名称
|
||||
self.data_filename: str = f"{self.name}_data.json"
|
||||
|
||||
def on_start(self) -> None:
|
||||
"""策略启动"""
|
||||
self.write_log("策略启动")
|
||||
data: dict = load_json(self.data_filename)
|
||||
self.dual_thrust_signal = data.get("dual_thrust_signal", 0)
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""策略停止"""
|
||||
self.write_log("策略停止")
|
||||
data: dict = {"dual_thrust_signal": self.dual_thrust_signal}
|
||||
save_json(self.data_filename, data)
|
||||
|
||||
def on_tick(self, tick: TickData) -> None:
|
||||
"""Tick推送"""
|
||||
self.obg.update_tick(tick)
|
||||
|
||||
def on_bars(self, bars: dict[str, BarData]) -> None:
|
||||
"""K线推送"""
|
||||
# 更新标的信号
|
||||
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
|
||||
if underlying_bar:
|
||||
self.factor.update_bar(underlying_bar)
|
||||
|
||||
# 获取期权组合对象
|
||||
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
|
||||
price_data: dict[str, float] = {}
|
||||
for bar in bars.values():
|
||||
price_data[bar.vt_symbol] = bar.close_price
|
||||
portfolio.update_price(price_data)
|
||||
|
||||
# 获取当月期权链
|
||||
front_chain: ChainData = portfolio.get_chain_by_level(0)
|
||||
if not front_chain:
|
||||
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
|
||||
return
|
||||
|
||||
# 计算平值期权
|
||||
front_chain.calculate_atm()
|
||||
self.atm_strike = front_chain.atm_strike
|
||||
|
||||
# 获取Dual Thrust信号
|
||||
signal: int = self.factor.get_signal()
|
||||
|
||||
# 固定交易手数为1
|
||||
trading_size: int = 1
|
||||
|
||||
# 根据信号开仓
|
||||
if signal != self.dual_thrust_signal:
|
||||
self.clear_targets()
|
||||
|
||||
call: OptionData = front_chain.get_option_by_level(cp=1, level=self.otm_level)
|
||||
put: OptionData = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
|
||||
|
||||
if call and put:
|
||||
if signal == 1: # 多头信号:做空Put(顺势腿)和Call(逆势腿)
|
||||
self.set_target(put.vt_symbol, -trading_size * self.leg1_ratio)
|
||||
self.set_target(call.vt_symbol, -trading_size * self.leg2_ratio)
|
||||
elif signal == -1: # 空头信号:做空Call(顺势腿)和Put(逆势腿)
|
||||
self.set_target(call.vt_symbol, -trading_size * self.leg1_ratio)
|
||||
self.set_target(put.vt_symbol, -trading_size * self.leg2_ratio)
|
||||
|
||||
# 平仓逻辑:在exit_time前平仓
|
||||
if underlying_bar.datetime.time() >= self.exit_time:
|
||||
self.clear_targets()
|
||||
|
||||
self.dual_thrust_signal = signal
|
||||
self.execute_trading(price_data, self.percent_add)
|
||||
self.put_event()
|
||||
|
||||
|
||||
class DualThrustFactor:
|
||||
"""标的物Dual Thrust因子(基于价格区间输出多空信号)"""
|
||||
|
||||
def __init__(self, vt_symbol: str, k1: float, k2: float, exit_time: time) -> None:
|
||||
self.vt_symbol: str = vt_symbol
|
||||
self.k1: float = k1
|
||||
self.k2: float = k2
|
||||
self.exit_time: time = exit_time
|
||||
self.bars = []
|
||||
|
||||
self.day_open: float = 0
|
||||
self.day_high: float = 0
|
||||
self.day_low: float = 0
|
||||
self.day_range: float = 0
|
||||
self.long_entry: float = 0
|
||||
self.short_entry: float = 0
|
||||
self.signal: int = 0 # 1多头,-1空头,0无信号
|
||||
|
||||
self.bg: BarGenerator = BarGenerator(self.update_bar, 1, self.update_window_bar)
|
||||
self.am: ArrayManager = ArrayManager(40) # 缓存足够的历史数据
|
||||
|
||||
def update_tick(self, tick: TickData) -> None:
|
||||
"""Tick更新"""
|
||||
self.bg.update_tick(tick)
|
||||
|
||||
def update_bar(self, bar: BarData) -> None:
|
||||
"""K线更新"""
|
||||
self.bg.update_bar(bar)
|
||||
|
||||
def update_window_bar(self, bar: BarData) -> None:
|
||||
"""日级别K线更新"""
|
||||
# 检查是否为新交易日
|
||||
self.bars.append(bar)
|
||||
if len(self.bars) <= 2:
|
||||
return
|
||||
else:
|
||||
self.bars.pop(0)
|
||||
last_bar = self.bars[-2]
|
||||
if len(self.bars) == 0 or last_bar.datetime.date() != bar.datetime.date():
|
||||
if len(self.bars) >= 1:
|
||||
# 计算前一交易日的波动范围
|
||||
prev_high = self.am.high[-1]
|
||||
prev_low = self.am.low[-1]
|
||||
prev_close = self.am.close[-1]
|
||||
self.day_range = max(prev_high - prev_low, prev_high - prev_close, prev_close - prev_low)
|
||||
|
||||
# 重置当日参数
|
||||
self.day_open = bar.open_price
|
||||
self.day_high = bar.high_price
|
||||
self.day_low = bar.low_price
|
||||
else:
|
||||
# 更新当日最高最低价
|
||||
self.day_high = max(self.day_high, bar.high_price)
|
||||
self.day_low = min(self.day_low, bar.low_price)
|
||||
|
||||
self.am.update_bar(bar)
|
||||
|
||||
# 计算入场价
|
||||
if self.day_range > 0:
|
||||
self.long_entry = self.day_open + self.k1 * self.day_range
|
||||
self.short_entry = self.day_open - self.k2 * self.day_range
|
||||
|
||||
# 生成信号
|
||||
if bar.close_price > self.long_entry:
|
||||
self.signal = 1
|
||||
elif bar.close_price < self.short_entry:
|
||||
self.signal = -1
|
||||
else:
|
||||
self.signal = 0
|
||||
|
||||
def get_signal(self) -> int:
|
||||
"""获取当前多空信号"""
|
||||
return self.signal
|
||||
@@ -0,0 +1,245 @@
|
||||
from vnpy.trader.constant import Interval
|
||||
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
|
||||
from vnpy.trader.object import TickData, BarData
|
||||
|
||||
from elite_optionstrategy import (
|
||||
StrategyTemplate,
|
||||
Variable,
|
||||
Parameter,
|
||||
PortfolioData,
|
||||
ChainData,
|
||||
OptionData,
|
||||
OptionBarGenerator,
|
||||
)
|
||||
|
||||
from datetime import time
|
||||
|
||||
class AdvancedSpreadStrategy(StrategyTemplate):
|
||||
"""基于DualThrust信号做空符合价差的策略"""
|
||||
|
||||
author: str = "用Python的交易员"
|
||||
|
||||
option_portfolio: str = Parameter("MO") # 期权产品代码
|
||||
underlying_symbol: str = Parameter("IMJQ00.CFFEX") # 标的合约代码
|
||||
k1: float = Parameter(0.3) # DualThrust参数k1
|
||||
k2: float = Parameter(0.9) # DualThrust参数k2
|
||||
risk_level: int = Parameter(40) # 开仓风险度
|
||||
percent_add: float = Parameter(0.02) # 委托超价比例
|
||||
otm_level: int = Parameter(0) # 做空期权档位
|
||||
leg1_ratio: int = Parameter(4) # 顺势腿的比例
|
||||
leg2_ratio: int = Parameter(1) # 逆势腿的比例
|
||||
|
||||
dt_signal: int = Variable(0) # 当前信号多空
|
||||
trading_size: int = Variable(1) # 当前交易数量
|
||||
atm_strike: float = Variable(0) # 当前平值行权价
|
||||
|
||||
def on_init(self) -> None:
|
||||
"""策略初始化"""
|
||||
self.write_log("策略初始化")
|
||||
|
||||
# K线截面合成器
|
||||
self.obg: OptionBarGenerator = OptionBarGenerator(self.on_bars)
|
||||
|
||||
# 订阅行情
|
||||
self.subscribe_options(self.option_portfolio)
|
||||
self.subscribe_data(self.underlying_symbol)
|
||||
|
||||
# 标的信号对象
|
||||
self.factor: DualThrustFactor = DualThrustFactor(self.underlying_symbol, self.k1, self.k2)
|
||||
|
||||
# 加载标的历史数据初始化
|
||||
bars: list[BarData] = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
|
||||
for bar in bars:
|
||||
self.factor.update_bar(bar)
|
||||
|
||||
# 缓存文件名称
|
||||
self.data_filename: str = f"{self.name}_data.json"
|
||||
|
||||
def on_start(self) -> None:
|
||||
"""策略启动"""
|
||||
self.write_log("策略启动")
|
||||
|
||||
data: dict = load_json(self.data_filename)
|
||||
self.dt_signal = data.get("dt_signal", 0)
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""策略停止"""
|
||||
self.write_log("策略停止")
|
||||
|
||||
data: dict = {"dt_signal": self.dt_signal}
|
||||
save_json(self.data_filename, data)
|
||||
|
||||
def on_tick(self, tick: TickData) -> None:
|
||||
"""Tick推送"""
|
||||
self.obg.update_tick(tick)
|
||||
|
||||
def on_bars(self, bars: dict[str, BarData]) -> None:
|
||||
"""K线推送"""
|
||||
# 回测首先计算标的信号
|
||||
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
|
||||
if underlying_bar:
|
||||
self.factor.update_bar(underlying_bar)
|
||||
|
||||
# 获取期权组合对象
|
||||
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
|
||||
|
||||
# 更新最新期权价格到组合
|
||||
price_data: dict[str, float] = {}
|
||||
for bar in bars.values():
|
||||
price_data[bar.vt_symbol] = bar.close_price
|
||||
|
||||
portfolio.update_price(price_data)
|
||||
|
||||
# 获取当月期权链
|
||||
front_chain: ChainData = portfolio.get_chain_by_level(0)
|
||||
if not front_chain:
|
||||
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
|
||||
return
|
||||
|
||||
# 计算平值期权
|
||||
front_chain.calculate_atm()
|
||||
self.atm_strike = front_chain.atm_strike
|
||||
|
||||
# 获取当前DualThrust多空信号
|
||||
dt_signal: int = self.factor.get_signal()
|
||||
|
||||
# 如果DualThrust多头信号,且尚未做多
|
||||
if dt_signal > 0 and self.dt_signal <= 0:
|
||||
# 做空Put
|
||||
call: OptionData = front_chain.get_option_by_level(cp=1, level=self.otm_level)
|
||||
put: OptionData = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
|
||||
|
||||
if call and put:
|
||||
# 清空之前的目标
|
||||
self.clear_targets()
|
||||
|
||||
atr_value: float = self.factor.get_atr()
|
||||
if atr_value:
|
||||
# self.trading_size = int(self.risk_level / atr_value)
|
||||
self.trading_size = 1 # max(self.trading_size, 1)
|
||||
else:
|
||||
self.trading_size = 1
|
||||
|
||||
self.set_target(put.vt_symbol, -self.trading_size * self.leg1_ratio)
|
||||
self.set_target(call.vt_symbol, -self.trading_size * self.leg2_ratio)
|
||||
# 如果DualThrust空头信号,且尚未做空
|
||||
elif dt_signal < 0 and self.dt_signal >= 0:
|
||||
# 做空Call
|
||||
call: OptionData = front_chain.get_option_by_level(cp=1, level=self.otm_level)
|
||||
put: OptionData = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
|
||||
|
||||
if call and put:
|
||||
# 清空之前的目标
|
||||
self.clear_targets()
|
||||
|
||||
atr_value: float = self.factor.get_atr()
|
||||
if atr_value:
|
||||
# self.trading_size = int(self.risk_level / atr_value)
|
||||
self.trading_size = 1 #max(self.trading_size, 1)
|
||||
else:
|
||||
self.trading_size = 1
|
||||
|
||||
self.set_target(call.vt_symbol, -self.trading_size * self.leg1_ratio)
|
||||
self.set_target(put.vt_symbol, -self.trading_size * self.leg2_ratio)
|
||||
|
||||
# 缓存DualThrust多空信号
|
||||
self.dt_signal = dt_signal
|
||||
|
||||
# 执行具体的委托交易
|
||||
self.execute_trading(price_data, self.percent_add)
|
||||
|
||||
# 推送UI事件更新
|
||||
self.put_event()
|
||||
|
||||
|
||||
class DualThrustFactor:
|
||||
"""标的物DualThrust因子(基于DualThrust输出多空信号)"""
|
||||
|
||||
def __init__(self, vt_symbol: str, k1: float, k2: float) -> None:
|
||||
"""构造函数"""
|
||||
self.vt_symbol: str = vt_symbol
|
||||
self.k1: float = k1
|
||||
self.k2: float = k2
|
||||
|
||||
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
|
||||
self.am: ArrayManager = ArrayManager(10)
|
||||
|
||||
self.day_open = 0
|
||||
self.day_high = 0
|
||||
self.day_low = 0
|
||||
self.day_range = 0
|
||||
self.long_entry = 0
|
||||
self.short_entry = 0
|
||||
self.exit_time = time(hour=14, minute=55)
|
||||
|
||||
self.long_entered = False
|
||||
self.short_entered = False
|
||||
|
||||
self.signal: int = 0
|
||||
self.bars: list[BarData] = [] # 手动维护最近两根K线
|
||||
|
||||
def update_tick(self, tick: TickData) -> None:
|
||||
"""Tick更新"""
|
||||
self.bg.update_tick(tick)
|
||||
|
||||
def update_bar(self, bar: BarData) -> None:
|
||||
"""K线更新"""
|
||||
self.bg.update_bar(bar)
|
||||
|
||||
def update_window_bar(self, bar: BarData) -> None:
|
||||
"""K线更新"""
|
||||
self.am.update_bar(bar)
|
||||
if not self.am.inited:
|
||||
return
|
||||
|
||||
# 维护最近两根K线
|
||||
self.bars.append(bar)
|
||||
if len(self.bars) > 2:
|
||||
self.bars.pop(0)
|
||||
|
||||
# 确保至少有两根K线用于日期比较
|
||||
if len(self.bars) < 2:
|
||||
return
|
||||
|
||||
last_bar = self.bars[-2]
|
||||
# bar = self.bars[-1]
|
||||
|
||||
# 检查是否新的一天
|
||||
if last_bar.datetime.date() != bar.datetime.date():
|
||||
if self.day_high:
|
||||
self.day_range = self.day_high - self.day_low
|
||||
self.long_entry = bar.open_price + self.k1 * self.day_range
|
||||
self.short_entry = bar.open_price - self.k2 * self.day_range
|
||||
|
||||
self.day_open = bar.open_price
|
||||
self.day_high = bar.high_price
|
||||
self.day_low = bar.low_price
|
||||
|
||||
self.long_entered = False
|
||||
self.short_entered = False
|
||||
else:
|
||||
self.day_high = max(self.day_high, bar.high_price)
|
||||
self.day_low = min(self.day_low, bar.low_price)
|
||||
|
||||
if not self.day_range:
|
||||
return
|
||||
|
||||
# 生成信号逻辑
|
||||
if bar.datetime.time() < self.exit_time:
|
||||
if bar.close_price > self.day_open:
|
||||
self.signal = 1
|
||||
else:
|
||||
self.signal = -1
|
||||
else:
|
||||
self.signal = 0
|
||||
|
||||
def get_signal(self) -> int:
|
||||
"""获取当前多空信号"""
|
||||
return self.signal
|
||||
|
||||
def get_atr(self) -> float:
|
||||
"""获取ATR风险度"""
|
||||
if self.am.inited:
|
||||
return self.am.atr(14)
|
||||
else:
|
||||
return 0
|
||||
@@ -0,0 +1,215 @@
|
||||
from vnpy.trader.constant import Interval
|
||||
from vnpy.trader.utility import ArrayManager, BarGenerator, load_json, save_json
|
||||
from vnpy.trader.object import TickData, BarData
|
||||
|
||||
from elite_optionstrategy import (
|
||||
StrategyTemplate,
|
||||
Variable,
|
||||
Parameter,
|
||||
PortfolioData,
|
||||
ChainData,
|
||||
OptionData,
|
||||
OptionBarGenerator,
|
||||
)
|
||||
|
||||
|
||||
class AdvancedSpreadStrategy(StrategyTemplate):
|
||||
"""基于均线信号做空符合价差的策略"""
|
||||
|
||||
author: str = "用Python的交易员"
|
||||
|
||||
option_portfolio: str = Parameter("IO") # 期权产品代码
|
||||
underlying_symbol: str = Parameter("IFJQ00.CFFEX") # 标的合约代码
|
||||
fast_window: int = Parameter(5) # 快速均线周期
|
||||
slow_window: int = Parameter(60) # 慢速均线周期
|
||||
risk_level: int = Parameter(40) # 开仓风险度
|
||||
percent_add: float = Parameter(0.02) # 委托超价比例
|
||||
otm_level: int = Parameter(0) # 做空期权档位
|
||||
leg1_ratio: int = Parameter(4) # 顺势腿的比例
|
||||
leg2_ratio: int = Parameter(1) # 逆势腿的比例
|
||||
|
||||
ma_signal: int = Variable(0) # 当前信号多空
|
||||
trading_size: int = Variable(1) # 当前交易数量
|
||||
atm_strike: float = Variable(0) # 当前平值行权价
|
||||
|
||||
def on_init(self) -> None:
|
||||
"""策略初始化"""
|
||||
self.write_log("策略初始化")
|
||||
|
||||
# K线截面合成器
|
||||
self.obg: OptionBarGenerator = OptionBarGenerator(self.on_bars)
|
||||
|
||||
# 订阅行情
|
||||
self.subscribe_options(self.option_portfolio)
|
||||
self.subscribe_data(self.underlying_symbol)
|
||||
|
||||
# 标的信号对象
|
||||
self.factor: MaFactor = MaFactor(self.underlying_symbol, self.fast_window, self.slow_window)
|
||||
|
||||
# 加载标的历史数据初始化
|
||||
bars: list[BarData] = self.load_bars(self.underlying_symbol, 40, Interval.MINUTE)
|
||||
for bar in bars:
|
||||
self.factor.update_bar(bar)
|
||||
|
||||
# 缓存文件名称
|
||||
self.data_filename: str = f"{self.name}_data.json"
|
||||
|
||||
def on_start(self) -> None:
|
||||
"""策略启动"""
|
||||
self.write_log("策略启动")
|
||||
|
||||
data: dict = load_json(self.data_filename)
|
||||
self.ma_signal = data.get("ma_signal", 0)
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""策略停止"""
|
||||
self.write_log("策略停止")
|
||||
|
||||
data: dict = {"ma_signal": self.ma_signal}
|
||||
save_json(self.data_filename, data)
|
||||
|
||||
def on_tick(self, tick: TickData) -> None:
|
||||
"""Tick推送"""
|
||||
self.obg.update_tick(tick)
|
||||
|
||||
def on_bars(self, bars: dict[str, BarData]) -> None:
|
||||
"""K线推送"""
|
||||
# 回测首先计算标的信号
|
||||
underlying_bar: BarData = bars.pop(self.underlying_symbol, None)
|
||||
if underlying_bar:
|
||||
self.factor.update_bar(underlying_bar)
|
||||
|
||||
# 获取期权组合对象
|
||||
portfolio: PortfolioData = self.get_portfolio(self.option_portfolio)
|
||||
|
||||
# 更新最新期权价格到组合
|
||||
price_data: dict[str, float] = {}
|
||||
for bar in bars.values():
|
||||
price_data[bar.vt_symbol] = bar.close_price
|
||||
|
||||
portfolio.update_price(price_data)
|
||||
|
||||
# 获取当月期权链
|
||||
front_chain: ChainData = portfolio.get_chain_by_level(0)
|
||||
if not front_chain:
|
||||
self.write_log("无法获取当月期权链,请检查是否正确添加了期权合约")
|
||||
return
|
||||
|
||||
# 计算平值期权
|
||||
front_chain.calculate_atm()
|
||||
self.atm_strike = front_chain.atm_strike
|
||||
|
||||
# 获取当前均线多空信号
|
||||
ma_signal: int = self.factor.get_signal()
|
||||
|
||||
# 如果均线多头排列,且尚未做多
|
||||
if ma_signal > 0 and self.ma_signal <= 0:
|
||||
# 做空Put
|
||||
call: OptionData = front_chain.get_option_by_level(cp=1, level=self.otm_level)
|
||||
put: OptionData = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
|
||||
|
||||
if call and put:
|
||||
# 清空之前的目标
|
||||
self.clear_targets()
|
||||
|
||||
atr_value: float = self.factor.get_atr()
|
||||
if atr_value:
|
||||
self.trading_size = int(self.risk_level / atr_value)
|
||||
self.trading_size = max(self.trading_size, 1)
|
||||
else:
|
||||
self.trading_size = 1
|
||||
|
||||
self.set_target(put.vt_symbol, -self.trading_size * self.leg1_ratio)
|
||||
self.set_target(call.vt_symbol, -self.trading_size * self.leg2_ratio)
|
||||
# 如果均线空头排列,且尚未做空
|
||||
elif ma_signal < 0 and self.ma_signal >= 0:
|
||||
# 做空Call
|
||||
call: OptionData = front_chain.get_option_by_level(cp=1, level=self.otm_level)
|
||||
put: OptionData = front_chain.get_option_by_level(cp=-1, level=self.otm_level)
|
||||
|
||||
if call and put:
|
||||
# 清空之前的目标
|
||||
self.clear_targets()
|
||||
|
||||
atr_value: float = self.factor.get_atr()
|
||||
if atr_value:
|
||||
self.trading_size = int(self.risk_level / atr_value)
|
||||
self.trading_size = max(self.trading_size, 1)
|
||||
else:
|
||||
self.trading_size = 1
|
||||
|
||||
self.set_target(call.vt_symbol, -self.trading_size * self.leg1_ratio)
|
||||
self.set_target(put.vt_symbol, -self.trading_size * self.leg2_ratio)
|
||||
|
||||
# 缓存均线多空信号
|
||||
self.ma_signal = ma_signal
|
||||
|
||||
# 执行具体的委托交易
|
||||
self.execute_trading(price_data, self.percent_add)
|
||||
|
||||
# 推送UI事件更新
|
||||
self.put_event()
|
||||
|
||||
|
||||
class MaFactor:
|
||||
"""标的物均线因子(基于均线输出多空信号)"""
|
||||
|
||||
def __init__(self, vt_symbol: str, fast_window: int, slow_window: int) -> None:
|
||||
"""构造函数"""
|
||||
self.vt_symbol: str = vt_symbol
|
||||
self.fast_window: int = fast_window
|
||||
self.slow_window: int = slow_window
|
||||
|
||||
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
|
||||
self.am: ArrayManager = ArrayManager(slow_window + 10)
|
||||
|
||||
self.signal: int = 0
|
||||
|
||||
def update_tick(self, tick: TickData) -> None:
|
||||
"""Tick更新"""
|
||||
self.bg.update_tick(tick)
|
||||
|
||||
def update_bar(self, bar: BarData) -> None:
|
||||
"""K线更新"""
|
||||
self.bg.update_bar(bar)
|
||||
|
||||
def update_window_bar(self, bar: BarData) -> None:
|
||||
"""K线更新"""
|
||||
self.am.update_bar(bar)
|
||||
if not self.am.inited:
|
||||
return
|
||||
|
||||
# 计算均线
|
||||
self.fast_ma = self.am.sma(self.fast_window)
|
||||
self.slow_ma = self.am.sma(self.slow_window)
|
||||
|
||||
# 判断信号
|
||||
if self.fast_ma > self.slow_ma:
|
||||
self.signal = 1
|
||||
elif self.fast_ma < self.slow_ma:
|
||||
self.signal = -1
|
||||
else:
|
||||
self.signal = 0
|
||||
|
||||
class TurtleSignalStrategy:
|
||||
def __init__(self, vt_symbol: str, entry_window: int, exit_window: int, atr_window: int) -> None:
|
||||
"""构造函数"""
|
||||
self.vt_symbol: str = vt_symbol
|
||||
self.entry_window: int = entry_window
|
||||
self.exit_window: int = exit_window
|
||||
|
||||
self.atr_window: int = atr_window
|
||||
|
||||
self.bg: BarGenerator = BarGenerator(self.update_bar, 30, self.update_window_bar)
|
||||
self.am: ArrayManager = ArrayManager(slow_window + 10)
|
||||
|
||||
def get_signal(self) -> int:
|
||||
"""获取当前多空信号"""
|
||||
return self.signal
|
||||
|
||||
def get_atr(self) -> float:
|
||||
"""获取ATR风险度"""
|
||||
if self.am.inited:
|
||||
return self.am.atr(self.slow_window)
|
||||
else:
|
||||
return 0
|
||||
@@ -0,0 +1,315 @@
|
||||
'''逐行解释代码:
|
||||
|
||||
1.导入所需的模块和库,包括 time、table(来自 matplotlib.pyplot)、pandas、numpy、numba 和 operator。
|
||||
|
||||
2.定义了一个名为 process 的函数,用于处理买卖盘的字典数据。
|
||||
|
||||
3.定义了一个名为 data 的函数,用于读取并处理 tick 数据,生成分钟级别的 bar 数据。
|
||||
|
||||
4.定义了一个名为 orderflow_df_new 的函数,用于处理 tick 数据和分钟级别的 bar 数据,生成订单流数据。
|
||||
|
||||
5.定义了一个名为 GetOrderFlow_dj 的函数,用于计算订单流的指标(堆积)。
|
||||
|
||||
6.定义了一个名为 back_data 的函数,用于保存回测数据。
|
||||
|
||||
7.在 if __name__ == "__main__": 下,首先调用 data() 函数获取 tick 数据和分钟级别的 bar 数据。
|
||||
|
||||
然后调用 orderflow_df_new() 函数,传入 tick 数据和 bar 数据,生成订单流数据 ofdata。
|
||||
|
||||
打印输出 ofdata。
|
||||
|
||||
8.调用 back_data() 函数,将订单流数据保存为回测数据。
|
||||
|
||||
打印输出 "done",表示程序执行完毕。
|
||||
|
||||
总体而言,该代码的功能是从 tick 数据中生成分钟级别的 bar 数据,然后根据 bar 数据计算订单流,并将订单流数据保存为回测数据。
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
|
||||
# 使用前注意事项:
|
||||
1、修改read_csv对应的文件地址
|
||||
2、修改resample对应的转化周期
|
||||
3、修改folder_path、to_csv对应的保存路径
|
||||
'''
|
||||
|
||||
import time
|
||||
from matplotlib.pyplot import table
|
||||
from datetime import timedelta,datetime
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from numba import *
|
||||
from numba import cuda
|
||||
import operator
|
||||
|
||||
import os
|
||||
|
||||
import chardet
|
||||
|
||||
def process(bidDict,askDict):
|
||||
bidDictResult,askDictResult = {},{}
|
||||
sList = sorted(set(list(bidDict.keys()) + list(askDict.keys())))
|
||||
#print('bidDict:',list(bidDict.keys()))
|
||||
#print('askDict:',list(askDict.keys()))
|
||||
#print('sList:',sList)
|
||||
#240884432
|
||||
for s in sList:
|
||||
if s in bidDict:
|
||||
bidDictResult[s] = bidDict[s]
|
||||
else:
|
||||
bidDictResult[s] = 0
|
||||
if s in askDict:
|
||||
askDictResult[s] = askDict[s]
|
||||
else:
|
||||
askDictResult[s] = 0
|
||||
|
||||
return bidDictResult,askDictResult
|
||||
|
||||
def dataload(data,cycle):
|
||||
#日期修正
|
||||
# data['业务日期'] = data['业务日期'].dt.strftime('%Y-%m-%d')
|
||||
# data['datetime'] = data['业务日期'] + ' '+data['最后修改时间'].dt.time.astype(str) + '.' + data['最后修改毫秒'].astype(str)
|
||||
# # 将 'datetime' 列的数据类型更改为 datetime 格式
|
||||
data['datetime'] = pd.to_datetime(data['datetime'], errors='coerce', format='%Y-%m-%d %H:%M:%S.%f')
|
||||
# 如果需要,可以将 datetime 列格式化为字符串
|
||||
#data['formatted_date'] = data['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S.%f')
|
||||
#计算瞬时成交量
|
||||
# data['volume'] = data['数量'] - data['数量'].shift(1)
|
||||
data['volume'] = data['volume'].fillna(0)
|
||||
#整理好要用的tick数据元素
|
||||
tickdata =pd.DataFrame({'datetime':data['datetime'],'symbol':data['symbol'],'lastprice':data['lastprice'],
|
||||
'volume':data['volume'],'bid_p':data['bid_p'],'bid_v':data['bid_v'],'ask_p':data['ask_p'],'ask_v':data['ask_v']})
|
||||
#tickdata['datetime'] = pd.to_datetime(tickdata['datetime'])
|
||||
tickdata['open'] = tickdata['lastprice']
|
||||
tickdata['high'] = tickdata['lastprice']
|
||||
tickdata['low'] = tickdata['lastprice']
|
||||
tickdata['close'] = tickdata['lastprice']
|
||||
tickdata['starttime'] = tickdata['datetime']
|
||||
|
||||
# # 找到满足条件的行的索引
|
||||
# condition = tickdata['datetime'].dt.time == pd.to_datetime('22:59:59').time()
|
||||
# indexes_to_update = tickdata.index[condition]
|
||||
|
||||
# # 遍历索引,将不一致的日期更新为上一行的日期
|
||||
# for idx in indexes_to_update:
|
||||
# if idx > 0:
|
||||
# tickdata.at[idx, 'datetime'] = tickdata.at[idx - 1, 'datetime'].replace(hour=22, minute=59, second=59)
|
||||
|
||||
# 确保日期列按升序排序
|
||||
tickdata.sort_values(by='datetime', inplace=True)
|
||||
# rule = '1T
|
||||
bardata = tickdata.resample(on = 'datetime',rule = cycle,label = 'right',closed = 'right').agg({'starttime':'first','symbol':'last','open':'first','high':'max','low':'min','close':'last','volume':'sum'}).reset_index(drop = False)
|
||||
#240884432
|
||||
bardata =bardata.dropna().reset_index(drop = True)
|
||||
return tickdata,bardata
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
|
||||
def orderflow_df_new(df_tick,df_min):
|
||||
df_of=pd.DataFrame({})
|
||||
t1 = time.time()
|
||||
startArray = pd.to_datetime(df_min['starttime']).values
|
||||
voluememin= df_min['volume'].values
|
||||
highs=df_min['high'].values
|
||||
lows=df_min['low'].values
|
||||
opens=df_min['open'].values
|
||||
closes=df_min['close'].values
|
||||
endArray = pd.to_datetime(df_min['datetime']).values
|
||||
tTickArray = pd.to_datetime(df_tick['datetime']).values
|
||||
bp1TickArray = df_tick['bid_p'].values
|
||||
ap1TickArray = df_tick['ask_p'].values
|
||||
lastTickArray = df_tick['lastprice'].values
|
||||
volumeTickArray = df_tick['volume'].values
|
||||
symbolarray = df_tick['symbol'].values
|
||||
indexFinal = 0
|
||||
for index,tEnd in enumerate(endArray):
|
||||
start = startArray[index]
|
||||
bidDict = {}
|
||||
askDict = {}
|
||||
bar_vol=voluememin[index]
|
||||
bar_close=closes[index]
|
||||
bar_open=opens[index]
|
||||
bar_low=lows[index]
|
||||
bar_high=highs[index]
|
||||
bar_symbol=symbolarray[index]
|
||||
dt=endArray[index]
|
||||
for indexTick in range(indexFinal,len(df_tick)):
|
||||
if tTickArray[indexTick] > tEnd:
|
||||
break
|
||||
elif (tTickArray[indexTick] >= start) & (tTickArray[indexTick] <= tEnd):
|
||||
if indexTick==0:
|
||||
Bp = round(bp1TickArray[indexTick],2)
|
||||
Ap = round(ap1TickArray[indexTick],2)
|
||||
else:
|
||||
Bp = round(bp1TickArray[indexTick - 1],2)
|
||||
Ap = round(ap1TickArray[indexTick - 1],2)
|
||||
LastPrice = round(lastTickArray[indexTick],2)
|
||||
Volume = volumeTickArray[indexTick]
|
||||
if LastPrice >= Ap:
|
||||
if LastPrice in askDict.keys():
|
||||
askDict[LastPrice] += Volume
|
||||
else:
|
||||
askDict[LastPrice] = Volume
|
||||
if LastPrice <= Bp:
|
||||
if LastPrice in bidDict.keys():
|
||||
bidDict[LastPrice] += Volume
|
||||
else:
|
||||
bidDict[LastPrice] = Volume
|
||||
indexFinal = indexTick
|
||||
|
||||
bidDictResult,askDictResult = process(bidDict,askDict)
|
||||
bidDictResult=dict(sorted(bidDictResult.items(),key=operator.itemgetter(0)))
|
||||
askDictResult=dict(sorted(askDictResult.items(),key=operator.itemgetter(0)))
|
||||
prinslist=list(bidDictResult.keys())
|
||||
asklist=list(askDictResult.values())
|
||||
bidlist=list(bidDictResult.values())
|
||||
delta=(sum(askDictResult.values()) - sum(bidDictResult.values()))
|
||||
df=pd.DataFrame({'price':pd.Series([prinslist]),'Ask':pd.Series([asklist]),'Bid':pd.Series([bidlist])})
|
||||
df['symbol']=bar_symbol
|
||||
df['datetime']=dt
|
||||
df['delta']=str(delta)
|
||||
df['close']=bar_close
|
||||
df['open']=bar_open
|
||||
df['high']=bar_high
|
||||
df['low']=bar_low
|
||||
df['volume']=bar_vol
|
||||
# 过滤'volume'列小于等于0的行
|
||||
df = df[df['volume'] > 0]
|
||||
# 重新排序DataFrame,按照'datetime'列进行升序排序
|
||||
df = df.sort_values(by='datetime', ascending=True)
|
||||
# 重新设置索引,以便索引能够正确对齐
|
||||
df = df.reset_index(drop=True)
|
||||
#df['ticktime']=tTickArray[indexTick]
|
||||
df['dj']=GetOrderFlow_dj(df)
|
||||
#print(df)
|
||||
df_of = pd.concat([df_of, df], ignore_index=True)
|
||||
print(time.time() - t1)
|
||||
return df_of
|
||||
|
||||
def GetOrderFlow_dj(kData):
|
||||
itemAskBG=['rgb(0,255,255)', 'rgb(255,0,255)', "rgb(255,182,193)"] # 买盘背景色
|
||||
itemBidBG=['rgb(173,255,47)', 'rgb(255,127,80)', "rgb(32,178,170)"] # 卖盘背景色
|
||||
Config={
|
||||
'Value1':3,
|
||||
'Value2':3,
|
||||
'Value3':3,
|
||||
'Value4':True,
|
||||
}
|
||||
aryData=kData
|
||||
djcout=0
|
||||
for index,row in aryData.iterrows():
|
||||
kItem=aryData.iloc[index]
|
||||
high=kItem['high']
|
||||
low=kItem['low']
|
||||
close=kItem['close']
|
||||
open=kItem['open']
|
||||
dtime=kItem['datetime']
|
||||
price_s=kItem['price']
|
||||
Ask_s=kItem['Ask']
|
||||
Bid_s=kItem['Bid']
|
||||
delta=kItem['delta']
|
||||
|
||||
price_s=price_s
|
||||
Ask_s=Ask_s
|
||||
Bid_s=Bid_s
|
||||
|
||||
gj=0
|
||||
xq=0
|
||||
gxx=0
|
||||
xxx=0
|
||||
for i in np.arange (0, len(price_s),1) :
|
||||
duiji={
|
||||
'price':0,
|
||||
'time':0,
|
||||
'longshort':0,
|
||||
'cout':0,
|
||||
'color':'blue'
|
||||
}
|
||||
if i==0 :
|
||||
delta=delta
|
||||
|
||||
order= {
|
||||
|
||||
"Price":price_s[i],
|
||||
"Bid":{ "Value":Bid_s[i]},
|
||||
"Ask":{ "Value":Ask_s[i]}
|
||||
}
|
||||
if i>=0 and i<len(price_s)-1:
|
||||
if (order["Bid"]["Value"]>Ask_s[i+1]*int(Config['Value1'])):
|
||||
order["Bid"]["Color"]=itemAskBG[1]
|
||||
gxx+=1
|
||||
gj+=1
|
||||
if gj>=int(Config['Value2']) and Config['Value4']==True:
|
||||
duiji['price']=price_s[i]
|
||||
duiji['time']=dtime
|
||||
duiji['longshort']=-1
|
||||
duiji['cout']=gj
|
||||
duiji['color']='rgba(0,139,0,0.45)'#绿色
|
||||
if float(duiji['price'])>0:
|
||||
djcout+=-1
|
||||
else :
|
||||
gj=0
|
||||
if i>=1 and i<=len(price_s)-1:
|
||||
if (order["Ask"]["Value"]>Bid_s[i-1]*int(Config['Value1'])):
|
||||
xq+=1
|
||||
xxx+=1
|
||||
order["Ask"]["Color"]=itemBidBG[1]
|
||||
if xq>=int(Config['Value2']) and Config['Value4']==True:
|
||||
duiji['price']=price_s[i]
|
||||
duiji['time']=dtime
|
||||
duiji['longshort']=1
|
||||
duiji['cout']=xq
|
||||
duiji['color']='rgba(255,0,0,0.45)' #红色
|
||||
if float(duiji['price'])>0:
|
||||
djcout+=1
|
||||
else :
|
||||
xq=0
|
||||
return djcout
|
||||
|
||||
def back_data(df,csv_path):
|
||||
# 创建新的DataFrame并填充需要的列
|
||||
new_df = pd.DataFrame()
|
||||
new_df['datetime'] = pd.to_datetime(df['datetime'], format='%Y/%m/%d %H:%M')
|
||||
new_df['close'] = df['close']
|
||||
new_df['open'] = df['open']
|
||||
new_df['high'] = df['high']
|
||||
new_df['low'] = df['low']
|
||||
new_df['volume'] = df['volume']
|
||||
new_df['sig'] = df['dj']
|
||||
new_df['symbol'] = df['symbol']
|
||||
new_df['delta'] = df['delta']
|
||||
new_df.to_csv(csv_path,index=False)
|
||||
#new_df.to_csv(f'{sym}back_ofdata_dj.csv',index=False)
|
||||
|
||||
|
||||
def ofdata_dj(file, cycle):
|
||||
print("file:", file)
|
||||
csv_df = pd.DataFrame()
|
||||
dir = os.getcwd()
|
||||
fileNum_errors = 0
|
||||
try:
|
||||
# 读取csv文件,并使用第一行为列标题,编译不通过可以改为gbk
|
||||
csv_df = pd.read_csv(file,encoding='GBK',parse_dates=['datetime'])
|
||||
except:
|
||||
file_path = os.path.join(dir, file)
|
||||
fileNum_errors += 1
|
||||
with open(file_path, 'rb') as file:
|
||||
data = file.read()
|
||||
|
||||
# 使用chardet检测编码
|
||||
detected_encoding = chardet.detect(data)['encoding']
|
||||
# print("%s当前文件不为gbk格式,其文件格式为%s,需要转换为gbk格式,错误总数为%s"%(file,detected_encoding,fileNum_errors))
|
||||
print("%s:%s当前文件不为gbk格式,其文件格式为%s,需要转换为gbk格式,错误总数为%s"%(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),file_path,detected_encoding,fileNum_errors))
|
||||
|
||||
with open('output_error.txt', 'a') as f:
|
||||
print("%s:%s当前文件不为gbk格式,其文件格式为%s,需要转换为gbk格式,错误总数为%s"%(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),file_path,detected_encoding,fileNum_errors), file = f)
|
||||
# print(csv_df)
|
||||
tick,bar=dataload(csv_df,cycle)
|
||||
ofdata = orderflow_df_new(tick,bar)
|
||||
print(ofdata)
|
||||
code_value = csv_df.loc[0, 'main_contract']# csv_df['main_contract'].keys
|
||||
|
||||
return code_value, ofdata
|
||||
@@ -0,0 +1,319 @@
|
||||
'''逐行解释代码:
|
||||
|
||||
1.导入所需的模块和库,包括 time、table(来自 matplotlib.pyplot)、pandas、numpy、numba 和 operator。
|
||||
|
||||
2.定义了一个名为 process 的函数,用于处理买卖盘的字典数据。
|
||||
|
||||
3.定义了一个名为 data 的函数,用于读取并处理 tick 数据,生成分钟级别的 bar 数据。
|
||||
|
||||
4.定义了一个名为 orderflow_df_new 的函数,用于处理 tick 数据和分钟级别的 bar 数据,生成订单流数据。
|
||||
|
||||
5.定义了一个名为 GetOrderFlow_dj 的函数,用于计算订单流的指标(堆积)。
|
||||
|
||||
6.定义了一个名为 back_data 的函数,用于保存回测数据。
|
||||
|
||||
7.在 if __name__ == "__main__": 下,首先调用 data() 函数获取 tick 数据和分钟级别的 bar 数据。
|
||||
|
||||
然后调用 orderflow_df_new() 函数,传入 tick 数据和 bar 数据,生成订单流数据 ofdata。
|
||||
|
||||
打印输出 ofdata。
|
||||
|
||||
8.调用 back_data() 函数,将订单流数据保存为回测数据。
|
||||
|
||||
打印输出 "done",表示程序执行完毕。
|
||||
|
||||
总体而言,该代码的功能是从 tick 数据中生成分钟级别的 bar 数据,然后根据 bar 数据计算订单流,并将订单流数据保存为回测数据。
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
|
||||
# 使用前注意事项:
|
||||
1、修改read_csv对应的文件地址
|
||||
2、修改resample对应的转化周期
|
||||
3、修改folder_path、to_csv对应的保存路径
|
||||
'''
|
||||
|
||||
import time
|
||||
from matplotlib.pyplot import table
|
||||
from datetime import timedelta
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from numba import *
|
||||
from numba import cuda
|
||||
import operator
|
||||
|
||||
import os
|
||||
|
||||
|
||||
|
||||
def process(bidDict,askDict):
|
||||
bidDictResult,askDictResult = {},{}
|
||||
sList = sorted(set(list(bidDict.keys()) + list(askDict.keys())))
|
||||
#print('bidDict:',list(bidDict.keys()))
|
||||
#print('askDict:',list(askDict.keys()))
|
||||
#print('sList:',sList)
|
||||
#240884432
|
||||
for s in sList:
|
||||
if s in bidDict:
|
||||
bidDictResult[s] = bidDict[s]
|
||||
else:
|
||||
bidDictResult[s] = 0
|
||||
if s in askDict:
|
||||
askDictResult[s] = askDict[s]
|
||||
else:
|
||||
askDictResult[s] = 0
|
||||
|
||||
return bidDictResult,askDictResult
|
||||
|
||||
|
||||
|
||||
|
||||
def dataload(data):
|
||||
#日期修正
|
||||
# data['业务日期'] = data['业务日期'].dt.strftime('%Y-%m-%d')
|
||||
# data['datetime'] = data['业务日期'] + ' '+data['最后修改时间'].dt.time.astype(str) + '.' + data['最后修改毫秒'].astype(str)
|
||||
# # 将 'datetime' 列的数据类型更改为 datetime 格式
|
||||
data['datetime'] = pd.to_datetime(data['datetime'], errors='coerce', format='%Y-%m-%d %H:%M:%S.%f')
|
||||
# 如果需要,可以将 datetime 列格式化为字符串
|
||||
#data['formatted_date'] = data['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S.%f')
|
||||
#计算瞬时成交量
|
||||
# data['volume'] = data['数量'] - data['数量'].shift(1)
|
||||
data['volume'] = data['volume'].fillna(0)
|
||||
#整理好要用的tick数据元素
|
||||
tickdata =pd.DataFrame({'datetime':data['datetime'],'symbol':data['symbol'],'lastprice':data['lastprice'],
|
||||
'volume':data['volume'],'bid_p':data['bid_p'],'bid_v':data['bid_v'],'ask_p':data['ask_p'],'ask_v':data['ask_v']})
|
||||
#tickdata['datetime'] = pd.to_datetime(tickdata['datetime'])
|
||||
tickdata['open'] = tickdata['lastprice']
|
||||
tickdata['high'] = tickdata['lastprice']
|
||||
tickdata['low'] = tickdata['lastprice']
|
||||
tickdata['close'] = tickdata['lastprice']
|
||||
tickdata['starttime'] = tickdata['datetime']
|
||||
|
||||
# # 找到满足条件的行的索引
|
||||
# condition = tickdata['datetime'].dt.time == pd.to_datetime('22:59:59').time()
|
||||
# indexes_to_update = tickdata.index[condition]
|
||||
|
||||
# # 遍历索引,将不一致的日期更新为上一行的日期
|
||||
# for idx in indexes_to_update:
|
||||
# if idx > 0:
|
||||
# tickdata.at[idx, 'datetime'] = tickdata.at[idx - 1, 'datetime'].replace(hour=22, minute=59, second=59)
|
||||
|
||||
# 确保日期列按升序排序
|
||||
tickdata.sort_values(by='datetime', inplace=True)
|
||||
|
||||
bardata = tickdata.resample(on = 'datetime',rule = '1T',label = 'right',closed = 'right').agg({'starttime':'first','symbol':'last','open':'first','high':'max','low':'min','close':'last','volume':'sum'}).reset_index(drop = False)
|
||||
#240884432
|
||||
bardata =bardata.dropna().reset_index(drop = True)
|
||||
return tickdata,bardata
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
|
||||
def orderflow_df_new(df_tick,df_min):
|
||||
df_of=pd.DataFrame({})
|
||||
t1 = time.time()
|
||||
startArray = pd.to_datetime(df_min['starttime']).values
|
||||
voluememin= df_min['volume'].values
|
||||
highs=df_min['high'].values
|
||||
lows=df_min['low'].values
|
||||
opens=df_min['open'].values
|
||||
closes=df_min['close'].values
|
||||
endArray = pd.to_datetime(df_min['datetime']).values
|
||||
tTickArray = pd.to_datetime(df_tick['datetime']).values
|
||||
bp1TickArray = df_tick['bid_p'].values
|
||||
ap1TickArray = df_tick['ask_p'].values
|
||||
lastTickArray = df_tick['lastprice'].values
|
||||
volumeTickArray = df_tick['volume'].values
|
||||
symbolarray = df_tick['symbol'].values
|
||||
indexFinal = 0
|
||||
for index,tEnd in enumerate(endArray):
|
||||
start = startArray[index]
|
||||
bidDict = {}
|
||||
askDict = {}
|
||||
bar_vol=voluememin[index]
|
||||
bar_close=closes[index]
|
||||
bar_open=opens[index]
|
||||
bar_low=lows[index]
|
||||
bar_high=highs[index]
|
||||
bar_symbol=symbolarray[index]
|
||||
dt=endArray[index]
|
||||
for indexTick in range(indexFinal,len(df_tick)):
|
||||
if tTickArray[indexTick] > tEnd:
|
||||
break
|
||||
elif (tTickArray[indexTick] >= start) & (tTickArray[indexTick] <= tEnd):
|
||||
if indexTick==0:
|
||||
Bp = round(bp1TickArray[indexTick],2)
|
||||
Ap = round(ap1TickArray[indexTick],2)
|
||||
else:
|
||||
Bp = round(bp1TickArray[indexTick - 1],2)
|
||||
Ap = round(ap1TickArray[indexTick - 1],2)
|
||||
LastPrice = round(lastTickArray[indexTick],2)
|
||||
Volume = volumeTickArray[indexTick]
|
||||
if LastPrice >= Ap:
|
||||
if LastPrice in askDict.keys():
|
||||
askDict[LastPrice] += Volume
|
||||
else:
|
||||
askDict[LastPrice] = Volume
|
||||
if LastPrice <= Bp:
|
||||
if LastPrice in bidDict.keys():
|
||||
bidDict[LastPrice] += Volume
|
||||
else:
|
||||
bidDict[LastPrice] = Volume
|
||||
indexFinal = indexTick
|
||||
|
||||
bidDictResult,askDictResult = process(bidDict,askDict)
|
||||
bidDictResult=dict(sorted(bidDictResult.items(),key=operator.itemgetter(0)))
|
||||
askDictResult=dict(sorted(askDictResult.items(),key=operator.itemgetter(0)))
|
||||
prinslist=list(bidDictResult.keys())
|
||||
asklist=list(askDictResult.values())
|
||||
bidlist=list(bidDictResult.values())
|
||||
delta=(sum(askDictResult.values()) - sum(bidDictResult.values()))
|
||||
df=pd.DataFrame({'price':pd.Series([prinslist]),'Ask':pd.Series([asklist]),'Bid':pd.Series([bidlist])})
|
||||
df['symbol']=bar_symbol
|
||||
df['datetime']=dt
|
||||
df['delta']=str(delta)
|
||||
df['close']=bar_close
|
||||
df['open']=bar_open
|
||||
df['high']=bar_high
|
||||
df['low']=bar_low
|
||||
df['volume']=bar_vol
|
||||
# 过滤'volume'列小于等于0的行
|
||||
df = df[df['volume'] > 0]
|
||||
# 重新排序DataFrame,按照'datetime'列进行升序排序
|
||||
df = df.sort_values(by='datetime', ascending=True)
|
||||
# 重新设置索引,以便索引能够正确对齐
|
||||
df = df.reset_index(drop=True)
|
||||
#df['ticktime']=tTickArray[indexTick]
|
||||
df['dj']=GetOrderFlow_dj(df)
|
||||
#print(df)
|
||||
df_of = pd.concat([df_of, df], ignore_index=True)
|
||||
print(time.time() - t1)
|
||||
return df_of
|
||||
|
||||
def GetOrderFlow_dj(kData):
|
||||
itemAskBG=['rgb(0,255,255)', 'rgb(255,0,255)', "rgb(255,182,193)"] # 买盘背景色
|
||||
itemBidBG=['rgb(173,255,47)', 'rgb(255,127,80)', "rgb(32,178,170)"] # 卖盘背景色
|
||||
Config={
|
||||
'Value1':3,
|
||||
'Value2':3,
|
||||
'Value3':3,
|
||||
'Value4':True,
|
||||
}
|
||||
aryData=kData
|
||||
djcout=0
|
||||
for index,row in aryData.iterrows():
|
||||
kItem=aryData.iloc[index]
|
||||
high=kItem['high']
|
||||
low=kItem['low']
|
||||
close=kItem['close']
|
||||
open=kItem['open']
|
||||
dtime=kItem['datetime']
|
||||
price_s=kItem['price']
|
||||
Ask_s=kItem['Ask']
|
||||
Bid_s=kItem['Bid']
|
||||
delta=kItem['delta']
|
||||
|
||||
price_s=price_s
|
||||
Ask_s=Ask_s
|
||||
Bid_s=Bid_s
|
||||
|
||||
gj=0
|
||||
xq=0
|
||||
gxx=0
|
||||
xxx=0
|
||||
for i in np.arange (0, len(price_s),1) :
|
||||
duiji={
|
||||
'price':0,
|
||||
'time':0,
|
||||
'longshort':0,
|
||||
'cout':0,
|
||||
'color':'blue'
|
||||
}
|
||||
if i==0 :
|
||||
delta=delta
|
||||
|
||||
order= {
|
||||
|
||||
"Price":price_s[i],
|
||||
"Bid":{ "Value":Bid_s[i]},
|
||||
"Ask":{ "Value":Ask_s[i]}
|
||||
}
|
||||
if i>=0 and i<len(price_s)-1:
|
||||
if (order["Bid"]["Value"]>Ask_s[i+1]*int(Config['Value1'])):
|
||||
order["Bid"]["Color"]=itemAskBG[1]
|
||||
gxx+=1
|
||||
gj+=1
|
||||
if gj>=int(Config['Value2']) and Config['Value4']==True:
|
||||
duiji['price']=price_s[i]
|
||||
duiji['time']=dtime
|
||||
duiji['longshort']=-1
|
||||
duiji['cout']=gj
|
||||
duiji['color']='rgba(0,139,0,0.45)'#绿色
|
||||
if float(duiji['price'])>0:
|
||||
djcout+=-1
|
||||
else :
|
||||
gj=0
|
||||
if i>=1 and i<=len(price_s)-1:
|
||||
if (order["Ask"]["Value"]>Bid_s[i-1]*int(Config['Value1'])):
|
||||
xq+=1
|
||||
xxx+=1
|
||||
order["Ask"]["Color"]=itemBidBG[1]
|
||||
if xq>=int(Config['Value2']) and Config['Value4']==True:
|
||||
duiji['price']=price_s[i]
|
||||
duiji['time']=dtime
|
||||
duiji['longshort']=1
|
||||
duiji['cout']=xq
|
||||
duiji['color']='rgba(255,0,0,0.45)' #红色
|
||||
if float(duiji['price'])>0:
|
||||
djcout+=1
|
||||
else :
|
||||
xq=0
|
||||
return djcout
|
||||
|
||||
def back_data(df):
|
||||
# 创建新的DataFrame并填充需要的列
|
||||
new_df = pd.DataFrame()
|
||||
new_df['datetime'] = pd.to_datetime(df['datetime'], format='%Y/%m/%d %H:%M')
|
||||
new_df['close'] = df['close']
|
||||
new_df['open'] = df['open']
|
||||
new_df['high'] = df['high']
|
||||
new_df['low'] = df['low']
|
||||
new_df['volume'] = df['volume']
|
||||
new_df['sig'] = df['dj']
|
||||
new_df['symbol'] = df['symbol']
|
||||
new_df['delta'] = df['delta']
|
||||
new_df.to_csv(f'./rb888_rs_2022_back_ofdata_dj.csv',index=False)
|
||||
#new_df.to_csv(f'{sym}back_ofdata_dj.csv',index=False)
|
||||
|
||||
if __name__ == "__main__":
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
data=pd.read_csv('D:/data_transfer/data_rs_merged/上期所/rb888/rb888_rs_2022.csv',encoding='GBK',parse_dates=['datetime']) # ['业务日期','最后修改时间']
|
||||
print(data)
|
||||
tick,bar=dataload(data)
|
||||
ofdata = orderflow_df_new(tick,bar)
|
||||
print(ofdata)
|
||||
#保存orderflow数据
|
||||
folder_path = 'D:/of_data/tick生成的OF数据/data_rs_merged/上期所/rb888/'
|
||||
if not os.path.exists(folder_path):
|
||||
# os.mkdir(folder_path)
|
||||
os.makedirs(folder_path)
|
||||
# 获取当前工作目录
|
||||
current_directory = os.getcwd()
|
||||
print("当前工作目录:", current_directory)
|
||||
# 设置新的工作目录
|
||||
os.chdir(folder_path)
|
||||
# 验证新的工作目录
|
||||
updated_directory = os.getcwd()
|
||||
print("已更改为新的工作目录:", updated_directory)
|
||||
|
||||
|
||||
ofdata.to_csv('./rb888_rs_2022_ofdata_dj.csv')
|
||||
#保存回测数据
|
||||
back_data(ofdata)
|
||||
print('done')
|
||||
|
||||