├── .gitignore ├── LICENSE ├── README.md ├── class_01 ├── READMD.md └── 课程前言.pdf ├── class_02 ├── btc.mp4 └── class_02.md ├── class_03 ├── class_03.md └── helloworld.py ├── class_04 ├── class_04.md ├── common_errors.py └── requirements.txt ├── class_05 ├── class_05.md └── class_05.py ├── class_06 ├── class_06.md └── main.py ├── class_07 ├── class_07.py └── class_7.md ├── class_08 ├── class_08.md ├── crawl_data.py └── main.py ├── class_09 ├── class_09.md ├── crawl_data.py └── main.py ├── class_10 ├── class_10.md ├── main_script.py ├── main_window.py └── strategies │ ├── class_10_simple_strategy.py │ └── class_10_simple_strategy1.py ├── class_11 ├── class_11.md ├── main_script.py ├── main_window.py └── strategies │ └── class_11_simple_strategy.py ├── class_12 ├── backtest_fixed_time.py ├── class_12.md ├── main_window.py └── strategies │ ├── fixed_trade_price_strategy.py │ └── fixed_trade_time_strategy.py ├── class_13 └── class_13.md ├── class_14 └── class_14.md ├── class_15 ├── class_15.md └── imgs │ └── order1.png ├── class_16 ├── class_16.md ├── grid.py ├── main_window.py └── strategies │ └── spot_grid_strategy.py ├── class_17 ├── class_17.md ├── grid.py ├── main_window.py └── strategies │ └── spot_profit_grid_strategy.py ├── class_18 ├── class_18.md ├── main_window.py └── strategies │ └── high_frequency_strategy.py ├── class_19 ├── class_19.md ├── main_window.py └── strategies │ ├── future_neutral_grid_strategy.py │ └── future_profit_grid_strategy.py ├── class_20 └── 20.资金费率套利.md ├── class_21 └── 21.资金费率实战操作.md ├── class_22 └── 22.套利软件策略参数说明和使用.md ├── class_23 └── 23.100万资金永续合约资金费率套利每天赚多少钱?.md ├── class_24 └── 24.fundtrade软件是用经常遇到的问题解答.md ├── class_25 ├── 25.马丁策略.md ├── main_window.py └── strategies │ ├── martingle_future_strategy.py │ ├── martingle_future_strategyV2.py │ ├── martingle_spot_strategy.py │ └── martingle_spot_strategyV2.py ├── class_26 ├── 26.强势币的马丁策略.md ├── main_window.py └── strategies │ ├── martingle_future_strategyV3.py │ └── martingle_spot_strategyV3.py ├── class_27 ├── 27.howtrader脚本运行操作和多策略多交易对交易.md ├── main_no_ui.py ├── main_window.py ├── run.sh └── strategies │ ├── martingle_future_strategyV3.py │ ├── martingle_spot_strategyV2.py │ └── martingle_spot_strategyV3.py └── class_33 ├── backtest.py ├── class_15.md ├── main_window.py └── strategies ├── grid_balance_strategy.py ├── martingle_spot_strategy.py ├── martingle_spot_strategyV2.py └── martingle_spot_strategyV3.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # pycharm 132 | .idea 133 | .DS_Store 134 | howtrader 135 | mystudies 136 | collect_data 137 | *.out 138 | */.idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 51bitquant 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #VNPY 数字货币量化交易从零到实盘交易的课程代码和资料 2 | 3 | ## requirements 4 | 需要安装howtrader, 具体安装请查看课程的相应章节。 5 | 6 | ## 课程的代码和资料 7 | class_01 就是第一课, class_02 就是第二课的代码和资料,以此类推。 8 | 9 | ## 课程视频 10 | 课程公开面免费,你也可以在B站,youtube,百度等搜索51bitquant即可找到相关视频。 11 | 12 | [网易云课堂视频连接](https://study.163.com/course/courseMain.htm?courseId=1210904816) 13 | 14 | ## 声明 15 | 本课程的视频和资料仅作为教学使用,任何与投资相关的损失与本人无关。 16 | 相关的代码和资料仅作为教学使用和参考。市场有风险投资需要谨慎。 17 | 18 | ## 联系方式: 19 | 微信: bitquant51 20 | 21 | 22 | ## 赞赏 23 | 24 | ETH地址: 0x0174C7b955114b3108Df18AD9b63eD5F3A02623b -------------------------------------------------------------------------------- /class_01/READMD.md: -------------------------------------------------------------------------------- 1 | ## howtrader 2 | 框架代码: https://github.com/51bitquant/howtrader 3 | 4 | 使用前要安装howtrader, 具体安装方式请参考github文档。 5 | 6 | # 联系方式 7 | 微信: **bitquant51** 8 | discord: 51bitquant#8078 9 | -------------------------------------------------------------------------------- /class_01/课程前言.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/51bitquant/course_codes/f8d41e5b82502d36d15e2381bc93eab27285af98/class_01/课程前言.pdf -------------------------------------------------------------------------------- /class_02/btc.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/51bitquant/course_codes/f8d41e5b82502d36d15e2381bc93eab27285af98/class_02/btc.mp4 -------------------------------------------------------------------------------- /class_02/class_02.md: -------------------------------------------------------------------------------- 1 | # 什么是比特币? 比特币是bitcoin的翻译,简称btc 2 | 3 | ## 声明 4 | 本课程的视频和资料仅作为教学使用,任何与投资相关的损失与本人无关。 相关的代码和资料仅作为教学使用和参考。市场有风险投资需要谨慎。 5 | 6 | 我们先来看下一个视频: 7 | 8 | ## btc历史价格变化 9 | 10 | 1. 我们来看下btc的价格变化。https://www.aicoin.cn/chart/bitstamp_btc, 11 | 可以看到当前btc的价格16000多美金,折合人民币105000元。价值决定价格,价格反映价值。 12 | 比特币被称作数字黄金。 13 | 14 | 2. 比特币可以分割,10多万人民币一个,但是你不一定要买一个,可以买0.001个, 15 | btc最小单位是聪(中本聪的聪) 16 | 17 | 1BTC = 1亿聪 = 1000mBTC(毫) = 1000,000uBTC(微) 18 | 但是大家还是喜欢用个btc来计算, 如0.32个btc。 19 | 20 | 甚至更小的数量。类似黄金,不一定要买一斤,可以买一克,或者买一盎司。 21 | 22 | 3. 比特币看的见,摸不着的,跟银行的数字一样,可以转账消费。银行的消费通过银行来记账。 23 | 比特币的消费转账通过链来记账。https://btc.com/block, https://eth.btc.com/ 24 | 25 | 4. 同种数字货币,转账需要在相同的链上流通,或者通过中心交易所去转换。 26 | 如:ERC20的USDT和TRC20的USDT, 27 | 不能直接转账,因为不同的链上的资产。就类似不通过银行直接不能转账。 28 | 29 | ## 数字货币、比特币和区块链的关系 30 | 1. 比特币是数字货币其中的一种,是数字货币的鼻祖。 31 | 32 | 2. 每个数字货币都有它的链(区块链, 33 | 由一个个数据块连接起来的,叫区块链,是数字货币背后的技术。 34 | 35 | 3. 数字货币类似小时候玩的游戏币一样, 36 | 每个公司的游戏币不一样。但是都是需要记账,不同的链之间不能进行转账,但是可以赚到支付宝 37 | 或者其他银行再转到别的银行 38 | 39 | ## 比特币和法币的价值兑换 40 | 1. 场外交易: 通过OTC场外交易购买或者卖出。 41 | 42 | 2. 场内交易: 通过其他资产购买别的资产。 43 | 44 | 3. 常见的法币锚定的资产: USDT(锚定美金的数字货币,由泰达公司发行), 45 | BUSDT(锚定美金的数字货币,由币安的美国站发行的) 46 | 47 | ## 交易所邀请链接: 48 | 币安邀请链接: **https://www.binancezh.pro/cn/futures/ref/51bitquant** 49 | 50 | 币安合约邀请码:**51bitquant** 51 | 52 | 53 | 火币网: **https://www.huobi.pr/zh-cn/topic/invited/?invite_code=asd43** 54 | 55 | 火币网邀请码: **asd43** 56 | 57 | ## 如何转账 58 | 59 | 60 | ## 劝告 61 | 不要买其他山寨币,不要听信其他什么技术牛逼的数字货币。 62 | 最有价值的还是btc,其次eth,bnb等。 63 | 要买山寨币,也是要买主流币。价值排名前10的。 64 | 65 | ## 其他问题可以百度或者微信咨询我 66 | 微信: **bitquant51** 67 | 68 | discord: 51bitquant#8078 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /class_03/class_03.md: -------------------------------------------------------------------------------- 1 | # 第三课: python环境的安装和创建虚拟环境 2 | 3 | ## 安装软件: 4 | 1. Anaconda, 下载地址: https://www.anaconda.com/products/individual 5 | 6 | 2. PyCharm, 下载地址: https://www.jetbrains.com/pycharm/download/ 7 | 8 | 9 | ## python解析器和虚拟环境 10 | 1. Python是一门解释性的语言,需要解析器去帮把写好的代码运行起来,这就是python的运行环境。 11 | 12 | 2. 一个电脑可以运行很多个环境,类似微信的多开的功能,每个解析器的内容完全隔离开。 13 | 14 | 15 | ## conda创建虚拟环境: 16 | 在终端termial, window在cmd命令行中输入 17 | > conda create -n your_interpreter_name python=3.9 # 18 | 19 | 例如创建名为study的解析器 20 | > conda create -n study python=3.9 21 | 22 | ## conda激活安装环境 23 | > conda activate study 24 | 25 | ## 查看环境列表 26 | > conda env list 27 | 28 | 29 | ## 删除环境 30 | > conda deactivate # 先激活自己当前的环境 31 | 32 | > conda remove -n <你的环境名称> 33 | 34 | > conda remove -n study --all 35 | 36 | 37 | # Pycharm 配置环境 38 | PyCharm需要能把代码跑起来,需要配置解析环境。 Window系统设置解析器: 39 | ``` 40 | file --> setting --> project --> interpreter --> 找你的环境解析器 41 | 42 | ``` 43 | 44 | MacOS系统设置解析器: 45 | ``` 46 | Pycharm --> Preferences ---> project ---> interpreter --> 找到你的环境环境解析器 47 | 48 | ``` 49 | -------------------------------------------------------------------------------- /class_03/helloworld.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | print("Hello world") -------------------------------------------------------------------------------- /class_04/class_04.md: -------------------------------------------------------------------------------- 1 | # 如何debug Python程序 2 | 3 | ## 常见的错误 4 | 1 中英文混乱 SyntaxError: invalid character in identifier 5 | ``` python 6 | print("Hello world") 7 | print('hello world') 8 | 9 | # File "", line 1 10 | # print('hello world') 11 | # ^ 12 | # SyntaxError: invalid character in identifier 13 | 14 | 15 | ``` 16 | 17 | 2. 缩进不正确 18 | 19 | ``` python 20 | 21 | def hello(): 22 | print('hello world') 23 | print('hello world') 24 | 25 | # IndentationError: unexpected indent 26 | 27 | ``` 28 | 29 | 3. 函数参数个不对,或者参数传递有误 30 | 31 | ``` python 32 | def func(a,b, c=10): 33 | print(a,b, c) 34 | 35 | 36 | func(1,c=5) 37 | 38 | ``` 39 | 40 | 4. 网络问题 类似 connection, 怎么看呢? 41 | 42 | 解决问题: 把报错的信息复制到百度,然后搜索,或者先翻译然后再搜索. 43 | 44 | 5. 如何定位问题的代码出现在哪里? 通过TraceStack进行查找,函数调用栈的问题. 45 | 46 | 47 | Traceback (most recent call last): 48 | File "", line 1, in 49 | ModuleNotFoundError: No module named 'requests' 50 | 51 | 52 | -------------------------------------------------------------------------------- /class_04/common_errors.py: -------------------------------------------------------------------------------- 1 | 2 | # error1: 找不模块, 解决方法: pip install <包的名称>, 如: pip install ccxt 3 | # import tensortrade ModuleNotFoundError: No module named 'tensortrade' 4 | # 或者通过requirements.txt 文件, 执行命令: pip install -r requirements.txt 5 | # 如果有的库安装库,或者错误,可以通过 pip install <库名称> 来装指定的库。 6 | # print("hello world") 7 | 8 | # import ccxt 9 | # print("hello world") 10 | 11 | 12 | # error 2: 语法错误 13 | 14 | # 中英文字符混合的 15 | # print('Helloworld') 16 | print('hello world') 17 | 18 | # SyntaxError: Missing parentheses in call to 'print'. Did you mean print(a)? 19 | # a = 10 20 | # print(a) 21 | 22 | # def hello: # 定义函数错误 23 | # print(100) 24 | 25 | # def hello1(): 26 | # print(100) 27 | 28 | # class Hello: 29 | # pass 30 | 31 | # class Person(object): 32 | # def hello(self): 33 | # pass 34 | 35 | # class Human: 36 | # def hell(self): 37 | # pass 38 | 39 | 40 | # error 3: NameError: name 'a' is not defined, 变量没有定义, 解决查看变量有没有定义 41 | # 局部不定义,看下全局有没有定义. 42 | # print(a) 43 | 44 | 45 | # a = 11 46 | # print("hello world1") 47 | # def print_data(): 48 | # print(a) 49 | # 50 | # if __name__ == '__main__': 51 | # print("hello world2") 52 | # a = 10 53 | # print_data() 54 | 55 | 56 | # error 4: 缩进错误 IndentationError: expected an indented block 57 | 58 | # for i in [1,3,4]: 59 | # print(i) 60 | 61 | 62 | # error 5: 属性错误 AttributeError: 'Student' object has no attribute 'num' 63 | # class Student(object): 64 | # def __init__(self, name, age): 65 | # self.age = age 66 | # self.name = name 67 | # # 68 | # stu1 = Student('Mary', 10) 69 | # print(stu1.age) 70 | # print(stu1.name) 71 | # print(stu1.num) 72 | # print(dir(stu1)) # 查看属性 73 | # print(stu1.__dict__) # 属性和值 74 | # print(stu1.__class__) 75 | 76 | # error 6: Type Error类型错误 TypeError: unsupported operand type(s) for /: 'str' and 'int' 77 | # a = 'I love China' 78 | # print(a/5) 79 | # print(a*5) 80 | 81 | ## error7 数组越界 IndexError: list index out of range 82 | 83 | # a = [1,3,4] 84 | # print(a[4]) 85 | 86 | ## error 8 函数参数传递错误 TypeError: fun1() missing 1 required positional argument: 'b' 87 | 88 | # def fun1(a, b, c=10): 89 | # print(a,b,c) 90 | 91 | # fun1(1,10,20) 92 | 93 | ## Key error KeyError: 'b', 常常出现在dict 和pandas 中 94 | 95 | # a = {"hello": "world"} 96 | # print(a['b']) 97 | 98 | import pandas as pd 99 | # a = {'a': [1,3,4], 'b': [567]} # 这也是一个错误 100 | # df = pd.DataFrame(a) 101 | 102 | 103 | # a = {'a': [1,3,4], 'b': [5,6,7]} 104 | # df = pd.DataFrame(a) 105 | # print(df['c']) 106 | 107 | 108 | ## json解析错误: raise JSONDecodeError("Expecting value", s, err.value) from None 109 | # json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) 110 | 111 | import requests 112 | 113 | # {"key": "value} 114 | # [1,b,c, {}] 115 | # a = requests.get("https://api.binance.com/exchangeinfo").json() 116 | # a = requests.get("https://api.binance.com/exchangeinfo") 117 | # print(a.text) 118 | 119 | # import json 120 | # 121 | # a = None 122 | # 123 | # json.loads(a) 124 | 125 | ## 网络错误 126 | """ 127 | raise ConnectTimeout(e, request=request) 128 | requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host='api.binance.com', port=443): 129 | Max retries exceeded with url: /api/v3/ping 130 | (Caused by ConnectTimeoutError(, 131 | 'Connection to api.binance.com timed out. (connect timeout=5)')) 132 | """ 133 | 134 | # 通过ping 测试下: ping api.binance.com 135 | # a = requests.get('https://api.binance.com/api/v3/ping', timeout=5).json() 136 | # print(a) 137 | # try: 138 | # a = requests.get('https://api.binance.com/api/v3/ping', timeout=5).json() 139 | # print(a) 140 | # 141 | # except Exception as error: 142 | # print(error) 143 | 144 | 145 | # ZeroDivisionError: division by zero, 查看函数的调用过程. Traceback 146 | def afunc(a): 147 | print(a) 148 | 149 | return 20/a 150 | 151 | def bfunc(a,b): 152 | d = a-b 153 | return afunc(d) 154 | 155 | 156 | # value = bfunc(20,10) 157 | value = bfunc(20,20) 158 | # value = bfunc(20,'hello') 159 | # print(value) 160 | 161 | ## 百度搜索 162 | # 关键字 inurl: 搜索某个网站的网址 163 | # 量化 inurl:www.jianshu.com 164 | # 51bitquant blog 165 | 166 | # 搜索字 blog 指明搜索博客类的 167 | # "indent error" blog 168 | 169 | # error , exception , Raise -------------------------------------------------------------------------------- /class_04/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | pandas 3 | -------------------------------------------------------------------------------- /class_05/class_05.md: -------------------------------------------------------------------------------- 1 | # 第五课课件内容 2 | 3 | ## 要求熟练掌握python的常见数据结构 4 | 1. list 5 | 2. dict 6 | 3. tuple 7 | 4. set 8 | 9 | 一般来说,常见的数据结构都用增删改查的功能,记得他们的常见的用法就可以。 10 | 主要是熟练他们的组合用法 11 | 12 | 13 | ## list, 两边是方括号[], 可以通过下标和遍历取值 14 | ``` python 15 | 16 | a = [1,3,4,5] 17 | 18 | 19 | ``` 20 | 21 | ## 字典, 两边是花括号{}, 他们成对出现,通过key获取值 22 | 23 | ## 元祖, 两边是小括号(), 不可变, 通过下标获取值 24 | 25 | 26 | ## 组合用法 27 | 28 | 29 | 30 | ## 类, 面向对象编程 31 | -------------------------------------------------------------------------------- /class_05/class_05.py: -------------------------------------------------------------------------------- 1 | ##1. list,列表, 两边是方括号[], 可以通过下标和遍历取值 2 | 3 | # a = [1,3,0, 6, 4,5] # 4 | # print(a) 5 | # print(type(a)) 6 | 7 | # 下标获取 8 | # print(a[0]) 9 | 10 | # 遍历 11 | # for i in a: 12 | # # print(i) 13 | # if i % 2 == 0: 14 | # print(i) 15 | 16 | # 排序 17 | # a = sorted(a, reverse=True) 18 | # a = sorted(a) 19 | # print(a) 20 | 21 | # 增 22 | # a.append(10) 23 | # print(a) 24 | # a.insert(0, 100) 25 | # print(a) 26 | 27 | # 删除 28 | 29 | # a = [1,3,3,5, 6] 30 | # a.remove(3) # 删除某个具体的数, 要求先判断在不在,然后再移除 31 | # print(a) 32 | # a.remove(100) 33 | # print(a) 34 | 35 | # value = 100 # 为了防止报错,需要对删除的元素进行检验 36 | # if value in a: 37 | # a.remove(value) 38 | # print(a) 39 | 40 | # try: 41 | # a.remove(100) 42 | # 43 | # except Exception as error: 44 | # print(error) 45 | # finally: 46 | # print(a) 47 | 48 | # del a[0] 49 | # print(a) 50 | 51 | # a.pop(1) # 通过下标 52 | # print(a) 53 | 54 | # 改 55 | 56 | # a[0] = 100 57 | # print(a) 58 | 59 | # 查, 通过下标访问 60 | # print(a[0]) 61 | # 遍历 62 | 63 | 64 | # dict 字典, 两边是花括号{}, 他们成对出现,通过key获取值 65 | # d = {'a': 10, 'b': 10} # 钥匙和锁 66 | # print(d) 67 | # print(type(d)) 68 | 69 | # 字典和字符串的区别 70 | # d1 = '{"a": "1"}' 71 | # print(type(d1)) 72 | # import json 73 | # d2 = json.loads(d1) 74 | # print(d2, type(d2)) 75 | 76 | 77 | # print(d.get('a')) 78 | # print(d.get('c')) 79 | # print(d['c']) 80 | 81 | # 增 82 | # d['c'] = 100 83 | # print(d) 84 | 85 | # 删 86 | # del d['a'] 87 | # print(d) 88 | 89 | # del d['c'] 90 | 91 | # if 'c' in d: 92 | # del d['c'] 93 | 94 | # 改 95 | # d['a'] = 1000 96 | # print(d) 97 | 98 | # 查 99 | 100 | # data = {'a': '1', 'b': 2} 101 | # for key, value in data.items(): 102 | # print(key, value) 103 | 104 | 105 | # for key in data.keys(): 106 | # print(key) 107 | # 108 | # for value in data.values(): 109 | # print(value) 110 | 111 | # for key in data.keys(): 112 | # print(data.get(key)) 113 | 114 | 115 | ## 元祖, 两边是小括号(), 不可变, 通过下标获取值 116 | 117 | # b = 1 118 | # t = 1, 119 | # 120 | # t0 = (1,) 121 | # print(type(b)) 122 | # print(type(t)) 123 | # print(type(t0)) 124 | 125 | # 主要用在函数返回值,可以返回多个值 126 | def func(a, b, c): 127 | return a + b, a + c, b + c 128 | 129 | 130 | # a,b,c = func(1,2,3) 131 | # print(a,b,c) 132 | 133 | # 查询操作 134 | # t1 = (1,3,4) 135 | # print(t1[0], t1[1]) 136 | # print(len(t1)) 137 | 138 | # t1 = (1,3,4) 139 | # for i in t1: 140 | # print(i) 141 | 142 | # 143 | # ## 不定参数 144 | # def hello(a, *args, **kwargs): 145 | # print(a,type(a)) # 10 146 | # print(args, type(args)) # (11,) 147 | # print(kwargs, type(kwargs)) # {'name': 'helloworld'} 148 | # 149 | # hello(10,11, 12, name='helloworld', age=10) 150 | 151 | 152 | ## 组合用法 153 | 154 | # a1 = [ 155 | # { 156 | # "counterParty":"master", 157 | # "email":"master@test.com", 158 | # "type":1, 159 | # "asset":"BTC", 160 | # "qty":"1", 161 | # "status":"SUCCESS", 162 | # "tranId":11798835829, 163 | # "time":1544433325000 164 | # }, 165 | # { 166 | # "counterParty": "subAccount", 167 | # "email": "sub2@test.com", 168 | # "type": 2, 169 | # "asset":"ETH", 170 | # "qty":"2", 171 | # "status":"SUCCESS", 172 | # "tranId":11798829519, 173 | # "time":1544433326000 174 | # } 175 | # ] 176 | 177 | # print(a1) 178 | 179 | # print(a1[0]) 180 | # print(a1[0]['asset']) 181 | # print(a1[0].get('asset')) 182 | 183 | 184 | # a2 = { 185 | # "timezone": "UTC", 186 | # "serverTime": 1565246363776, 187 | # "rateLimits": [ 188 | # { 189 | # 190 | # } 191 | # ], 192 | # "exchangeFilters": [ 193 | # 194 | # ], 195 | # "symbols": [ 196 | # { 197 | # "symbol": "ETHBTC", 198 | # "status": "TRADING", 199 | # "baseAsset": "ETH", 200 | # "baseAssetPrecision": 8, 201 | # "quoteAsset": "BTC", 202 | # "quotePrecision": 8, 203 | # "quoteAssetPrecision": 8, 204 | # "orderTypes": [ 205 | # "LIMIT", 206 | # "LIMIT_MAKER", 207 | # "MARKET", 208 | # "STOP_LOSS", 209 | # "STOP_LOSS_LIMIT", 210 | # "TAKE_PROFIT", 211 | # "TAKE_PROFIT_LIMIT" 212 | # ], 213 | # "icebergAllowed": True, 214 | # "ocoAllowed": True, 215 | # "isSpotTradingAllowed": True, 216 | # "isMarginTradingAllowed": True, 217 | # "filters": [ 218 | # ], 219 | # "permissions": [ 220 | # "SPOT", 221 | # "MARGIN" 222 | # ] 223 | # } 224 | # ] 225 | # } 226 | 227 | 228 | # print(a2) 229 | # print(a2.get('symbols')) 230 | # print(a2.get('symbols')[0]) 231 | # print(a2.get('symbols')[0]["orderTypes"]) 232 | # a21 = a2.get('symbols')[0]["orderTypes"] 233 | # print(a21[1]) 234 | 235 | 236 | # a3 = { 237 | # "lastUpdateId": 1027024, 238 | # "bids": [ 239 | # [ 240 | # "4.00000000", 241 | # "431.00000000" 242 | # 243 | # ], 244 | # [ 245 | # "4.10000000", 246 | # "432.00000000" 247 | # 248 | # ], 249 | # 250 | # ], 251 | # "asks": [ 252 | # [ 253 | # "2.00000200", 254 | # "12.50000000" 255 | # ], 256 | # [ 257 | # "4.50000200", 258 | # "13.00000000" 259 | # ] 260 | # ] 261 | # } 262 | # 263 | # print(a3['bids']) 264 | # print(a3['bids'][1]) 265 | # print(a3['bids'][1][0]) 266 | -------------------------------------------------------------------------------- /class_06/class_06.md: -------------------------------------------------------------------------------- 1 | # 第六课: VNPY量化交易软件的安装, 图形界面的启动和功能介绍 2 | 软件代码地址: **https://github.com/51bitquant/howtrader** 3 | 4 | ## step 1 创建一个新的虚拟环境并激活 5 | 更新anaconda, 如果你切换到其他解析器了,可以通过执行conda deactivate, 6 | 然后再执行下面的命令。 7 | 8 | > conda update conda 9 | 10 | > conda create -n mytrader python==3.9 11 | 12 | > conda activate mytrader 13 | 14 | ## step 2 安装howtrader 15 | 16 | > pip install git+https://github.com/51bitquant/howtrader.git 17 | 18 | ## step 3 创建howtrader文件夹 19 | 20 | 在项目下面创建一个文件夹howtrader,该文件夹主要是存放log日志和配置文件的 21 | 22 | ## step 4 创建一个启动文件 23 | 24 | ``` python 25 | 26 | from howtrader.event import EventEngine 27 | 28 | from howtrader.trader.engine import MainEngine 29 | from howtrader.trader.ui import MainWindow, create_qapp 30 | 31 | from howtrader.gateway.binance import BinanceUsdtGateway, BinanceSpotGateway 32 | 33 | 34 | from howtrader.app.cta_strategy import CtaStrategyApp 35 | from howtrader.app.data_manager import DataManagerApp 36 | from howtrader.app.data_recorder import DataRecorderApp 37 | from howtrader.app.algo_trading import AlgoTradingApp 38 | from howtrader.app.cta_backtester import CtaBacktesterApp 39 | from howtrader.app.risk_manager import RiskManagerApp 40 | from howtrader.app.spread_trading import SpreadTradingApp 41 | 42 | def main(): 43 | """""" 44 | 45 | qapp = create_qapp() 46 | 47 | event_engine = EventEngine() 48 | 49 | main_engine = MainEngine(event_engine) 50 | 51 | main_engine.add_gateway(BinanceUsdtGateway) 52 | main_engine.add_gateway(BinanceSpotGateway) 53 | main_engine.add_app(CtaStrategyApp) 54 | main_engine.add_app(CtaBacktesterApp) 55 | main_engine.add_app(DataManagerApp) 56 | main_engine.add_app(AlgoTradingApp) 57 | main_engine.add_app(DataRecorderApp) 58 | main_engine.add_app(RiskManagerApp) 59 | main_engine.add_app(SpreadTradingApp) 60 | 61 | main_window = MainWindow(main_engine, event_engine) 62 | main_window.showMaximized() 63 | 64 | qapp.exec() 65 | 66 | 67 | if __name__ == "__main__": 68 | """ 69 | howtrader main window demo 70 | howtrader 的图形化界面 71 | 72 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 73 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 74 | while the futures use the upper cases. 75 | 76 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 77 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 78 | """ 79 | 80 | main() 81 | 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /class_06/main.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway, BinanceUsdtGateway 7 | 8 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 9 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 10 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 11 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 12 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 13 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 14 | 15 | 16 | def main(): 17 | """""" 18 | 19 | qapp = create_qapp() 20 | 21 | event_engine = EventEngine() 22 | 23 | main_engine = MainEngine(event_engine) 24 | 25 | main_engine.add_gateway(BinanceSpotGateway) # spot gateway 26 | main_engine.add_gateway(BinanceUsdtGateway) # usdt_future gateway 27 | main_engine.add_app(CtaStrategyApp) 28 | main_engine.add_app(DataManagerApp) 29 | main_engine.add_app(AlgoTradingApp) 30 | main_engine.add_app(DataRecorderApp) 31 | main_engine.add_app(RiskManagerApp) 32 | main_engine.add_app(SpreadTradingApp) 33 | 34 | main_window = MainWindow(main_engine, event_engine) 35 | main_window.showMaximized() 36 | 37 | qapp.exec() 38 | 39 | 40 | if __name__ == "__main__": 41 | """ 42 | howtrader main window demo 43 | howtrader 的图形化界面 44 | 45 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 46 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 47 | while the futures use the upper cases. 48 | 49 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 50 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 51 | 52 | BTCUSDT, BTCUSDT 53 | """ 54 | 55 | main() -------------------------------------------------------------------------------- /class_07/class_07.py: -------------------------------------------------------------------------------- 1 | from howtrader.gateway.binance import BinanceUsdtGateway 2 | from howtrader.gateway.binance import BinanceSpotGateway 3 | from howtrader.event.engine import EventEngine, Event, EVENT_TIMER 4 | 5 | from howtrader.trader.constant import Status, OrderType 6 | from howtrader.trader.constant import Exchange, Interval 7 | # from howtrader.trader.object import Exchange, Interval 8 | from howtrader.trader.object import TickData 9 | 10 | 11 | 12 | # Exchange.BINANCE 13 | # Interval.MINUTE 14 | 15 | # tick = TickData() 16 | # tick.ask_volume_1 17 | # tick.bid_price_1 18 | 19 | # {'ask1': 100} # key value 20 | 21 | 22 | # def hello(a): 23 | # print(a) 24 | 25 | # int hello() { 26 | # 27 | # return 10; 28 | # } 29 | 30 | 31 | def hello(string: str) -> int: 32 | print(string) 33 | 34 | return len(string) 35 | 36 | 37 | # a = hello("helloworld") 38 | # 39 | # print(a) 40 | 41 | # b = hello(10) 42 | # print(b) 43 | 44 | class Person(object): 45 | def __init__(self, name, age): 46 | self.name = name 47 | self.age = age 48 | 49 | 50 | class Human(object): 51 | def __init__(self, name, age): 52 | self.name = name 53 | 54 | 55 | def func(person: Person) -> Person: 56 | print(person.age, person.name) 57 | 58 | return person 59 | 60 | a: Person = func(Person('lisi', 10)) 61 | 62 | b = func(Person('lisi',15)) 63 | 64 | print(a.name) 65 | print(a.age) 66 | 67 | print(b.age) 68 | 69 | from typing import Dict, Tuple, List 70 | 71 | def hi(a: List) -> List: 72 | 73 | return a -------------------------------------------------------------------------------- /class_07/class_7.md: -------------------------------------------------------------------------------- 1 | # 第七课:VNPY的常见类的介绍和python类型编程 2 | 3 | ## 导入类的方式 4 | 5 | ```python 6 | 7 | from howtrader.A模块.文件 import 具体的某个类 8 | from howtrader.trader.object import Status # 订单状态 9 | from howtrader.trader.object import OrderType 10 | from howtrader.trader.object import Exchange, Interval 11 | 12 | """ 13 | 实践的类型 14 | 15 | EVENT_TICK = "eTick." 16 | EVENT_TRADE = "eTrade." 17 | EVENT_ORDER = "eOrder." 18 | EVENT_POSITION = "ePosition." 19 | EVENT_ACCOUNT = "eAccount." 20 | EVENT_CONTRACT = "eContract." 21 | EVENT_LOG = "eLog" 22 | EVENT_TIMER = "eTimer" 23 | """ 24 | 25 | # 配置文件: "vt_setting.json" 26 | 27 | ``` 28 | 29 | 30 | 31 | ## python的类型编程 32 | 33 | ```python 34 | 35 | from typing import Dict, Tuple, List 36 | 37 | def hello(a: int, b: int) -> int: 38 | print(a, b) 39 | return a+b 40 | 41 | ``` 42 | 43 | 44 | -------------------------------------------------------------------------------- /class_08/class_08.md: -------------------------------------------------------------------------------- 1 | # 第八课: VNPY数据库配置和数据爬取 2 | 3 | 4 | ## 币安邀请码 5 | 没有开通币安或者交易所的可以通过下面链接去开通,有手续费减免: 6 | **https://www.binancezh.pro/cn/futures/ref/51bitquant** 7 | 合约邀请码:**51bitquant** 8 | 9 | 10 | ## 答疑: 安装vnpy的错误 11 | 1. MacOS系统,要记得安装Xcode开发工具,主要是Xcode会帮你把一些构建工具和库安装好, 12 | window系统不需要安装Xcode开发工具,因为没有这个软件。 13 | 2. 安装通过pip install git+https://github.com/51bitquant/howtrader.git 14 | 3. 如果没有安装git工具的,记得去安装,具体百度下。 15 | 16 | ## 支持的数据库类型 17 | 1. 支持sqlite3数据库(默认使用) 18 | 2. mongodb数据库 19 | 3. mysql 20 | 21 | ## 配置文件 22 | 23 | 1. mongodb 需要安装 先启动数据库 24 | > mongod --dbpath /Users/wanglin/mongodb/data/db(替换成你的路径, replace 25 | > with your own database path) 26 | 27 | 28 | 配置文件的详细信息请参考: howtrader/trader/setting.py文件 29 | ```python 30 | 31 | SETTINGS: Dict[str, Any] = { 32 | "font.family": "Arial", 33 | "font.size": 12, 34 | 35 | "log.active": True, 36 | "log.level": CRITICAL, 37 | "log.console": True, 38 | "log.file": True, 39 | 40 | "email.server": "smtp.qq.com", 41 | "email.port": 465, 42 | "email.username": "", 43 | "email.password": "", 44 | "email.sender": "", 45 | "email.receiver": "", 46 | 47 | "database.timezone": get_localzone().zone, 48 | "database.driver": "sqlite", # see database.Driver 49 | "database.database": "database.db", # for sqlite, use this as filepath 50 | "database.host": "localhost", 51 | "database.port": 3306, 52 | "database.user": "root", 53 | "database.password": "", 54 | "database.authentication_source": "admin", # for mongodb 55 | } 56 | 57 | ``` 58 | 59 | ``` json 60 | { 61 | "font.family": "Arial", 62 | "font.size": 12, 63 | 64 | "log.active": true, 65 | "log.level": "CRITICAL", 66 | "log.console": true, 67 | "log.file": true, 68 | 69 | "email.server": "smtp.qq.com", 70 | "email.port": 465, 71 | "email.username": "", 72 | "email.password": "", 73 | "email.sender": "", 74 | "email.receiver": "", 75 | 76 | "database.driver": "mongodb", 77 | "database.database": "howtrader", 78 | "database.host": "localhost", 79 | "database.port": 3306, 80 | "database.user": "root", 81 | "database.password": "", 82 | "database.authentication_source": "admin" 83 | } 84 | 85 | 86 | ``` 87 | 1. database.driver : 填写的值有: sqlite, mysql, mongodb 88 | 2. database.database: 数据库名称, 89 | howtrader,随便填写,如果默认用sqlite.db就不用修改。 90 | 3. host 主机, user用户, passwordm密码 91 | 92 | 默认的sqlite的配置 93 | ```json 94 | 95 | { 96 | "database.driver": "sqlite", 97 | "database.database": "database.db" 98 | } 99 | 100 | 101 | ``` 102 | 使用mongodb的配置 103 | ``` json 104 | { 105 | "database.driver": "mongodb", 106 | "database.database": "howtrader", 107 | "database.host": "localhost", 108 | "database.port": 27017, 109 | "database.authentication_source": "admin" 110 | } 111 | 112 | ``` 113 | 114 | 115 | 116 | 117 | 可能的报错: 118 | ``` 119 | pymongo.errors.ServerSelectionTimeoutError: Got response 120 | id 3158574 but expected 996497972, Timeout: 30s, Topology Description: 121 | ]> 124 | 125 | ``` 126 | 127 | 128 | ## 关于连接交易所的配置 129 | 130 | 1. 现货 131 | 132 | ```json 133 | { 134 | "key": "xxxxxx", 135 | "secret": "xxx", 136 | "session_number": 3, 137 | "proxy_host": "127.0.0.1", 138 | "proxy_port": 1087 139 | } 140 | 141 | ``` 142 | 143 | 2. 合约 144 | 145 | ```json 146 | { 147 | "key": "xxxx", 148 | "secret": "xxxx", 149 | "会话数": 3, 150 | "服务器": "REAL", 151 | "合约模式": "正向", 152 | "代理地址": "127.0.0.1", 153 | "代理端口": 1087 154 | } 155 | 156 | ``` 157 | 158 | 记得合约代理的proxy_host或者代理地址: 159 | 只能写ip地址,不能写http://xxx.xxx.xxx.xxx, 不包含http或者https协议头 160 | 161 | 162 | ## 数据批量爬取数据 163 | 具体参考crawl_data.py脚本,修改成自己需要的交易对,填写具体的交易对就可以了。记得修改对应的爬取时间 164 | 1. download_spot(), 这个方法是爬取现货的数据. 165 | 166 | 2. download_future(), 这是合约的数据 167 | 168 | 记得下载好BTCUSDT和ETHUSDT的数据,后面的课程会用到。 169 | -------------------------------------------------------------------------------- /class_08/crawl_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | use binance api to get the kline datas. 3 | author: 51bitquant 4 | 5 | discord: 51bitquant#8078 6 | """ 7 | 8 | import pandas as pd 9 | import time 10 | from datetime import datetime 11 | import requests 12 | import pytz 13 | from howtrader.trader.database import BaseDatabase, get_database 14 | 15 | pd.set_option('expand_frame_repr', False) # 16 | from howtrader.trader.object import BarData, Interval, Exchange 17 | 18 | BINANCE_SPOT_LIMIT = 1000 19 | BINANCE_FUTURE_LIMIT = 1500 20 | 21 | CHINA_TZ = pytz.timezone("Asia/Shanghai") 22 | from threading import Thread 23 | 24 | 25 | database: BaseDatabase = get_database() 26 | 27 | 28 | def generate_datetime(timestamp: float) -> datetime: 29 | """ 30 | :param timestamp: 31 | :return: 32 | """ 33 | dt = datetime.fromtimestamp(timestamp / 1000) 34 | dt = CHINA_TZ.localize(dt) 35 | return dt 36 | 37 | 38 | def get_binance_data(symbol: str, exchange: str, start_time: str, end_time: str): 39 | """ 40 | crawl binance exchange data 41 | :param symbol: BTCUSDT. 42 | :param exchange: spot、usdt_future, inverse_future. 43 | :param start_time: format :2020-1-1 or 2020-01-01 year-month-day 44 | :param end_time: format: 2020-1-1 or 2020-01-01 year-month-day 45 | :param gate_way the gateway name for binance is:BINANCE_SPOT, BINANCE_USDT, BINANCE_INVERSE 46 | :return: 47 | """ 48 | 49 | api_url = '' 50 | save_symbol = symbol 51 | 52 | 53 | if exchange == 'spot': 54 | print("spot") 55 | limit = BINANCE_SPOT_LIMIT 56 | save_symbol = symbol.lower() 57 | gateway = 'BINANCE_SPOT' 58 | api_url = f'https://api.binance.com/api/v3/klines?symbol={symbol}&interval=1m&limit={limit}' 59 | 60 | elif exchange == 'usdt_future': 61 | print('usdt_future') 62 | limit = BINANCE_FUTURE_LIMIT 63 | gateway = 'BINANCE_USDT' 64 | api_url = f'https://fapi.binance.com/fapi/v1/klines?symbol={symbol}&interval=1m&limit={limit}' 65 | 66 | elif exchange == 'inverse_future': 67 | print("inverse_future") 68 | limit = BINANCE_FUTURE_LIMIT 69 | gateway = 'BINANCE_INVERSE' 70 | f'https://dapi.binance.com/dapi/v1/klines?symbol={symbol}&interval=1m&limit={limit}' 71 | 72 | else: 73 | raise Exception('the exchange name should be one of :spot, usdt_future, inverse_future') 74 | 75 | start_time = int(datetime.strptime(start_time, '%Y-%m-%d').timestamp() * 1000) 76 | end_time = int(datetime.strptime(end_time, '%Y-%m-%d').timestamp() * 1000) 77 | 78 | while True: 79 | try: 80 | print(start_time) 81 | url = f'{api_url}&startTime={start_time}' 82 | print(url) 83 | datas = requests.get(url=url, timeout=10, proxies=proxies).json() 84 | 85 | """ 86 | [ 87 | [ 88 | 1591258320000, // 开盘时间 89 | "9640.7", // 开盘价 90 | "9642.4", // 最高价 91 | "9640.6", // 最低价 92 | "9642.0", // 收盘价(当前K线未结束的即为最新价) 93 | "206", // 成交量 94 | 1591258379999, // 收盘时间 95 | "2.13660389", // 成交额(标的数量) 96 | 48, // 成交笔数 97 | "119", // 主动买入成交量 98 | "1.23424865", // 主动买入成交额(标的数量) 99 | "0" // 请忽略该参数 100 | ] 101 | 102 | """ 103 | 104 | buf = [] 105 | 106 | for row in datas: 107 | bar: BarData = BarData( 108 | symbol=save_symbol, 109 | exchange=Exchange.BINANCE, 110 | datetime=generate_datetime(row[0]), 111 | interval=Interval.MINUTE, 112 | volume=float(row[5]), 113 | turnover=float(row[7]), 114 | open_price=float(row[1]), 115 | high_price=float(row[2]), 116 | low_price=float(row[3]), 117 | close_price=float(row[4]), 118 | gateway_name=gateway 119 | ) 120 | buf.append(bar) 121 | 122 | database.save_bar_data(buf) 123 | 124 | # 到结束时间就退出, 后者收盘价大于当前的时间. 125 | if (datas[-1][0] > end_time) or datas[-1][6] >= (int(time.time() * 1000) - 60 * 1000): 126 | break 127 | 128 | start_time = datas[-1][0] 129 | 130 | except Exception as error: 131 | print(error) 132 | time.sleep(10) 133 | 134 | 135 | def download_spot(symbol): 136 | """ 137 | 下载现货数据的方法. 138 | :return: 139 | """ 140 | t1 = Thread(target=get_binance_data, args=(symbol, 'spot', "2018-1-1", "2019-1-1")) 141 | 142 | t2 = Thread(target=get_binance_data, args=(symbol, 'spot', "2019-1-1", "2020-1-1")) 143 | 144 | t3 = Thread(target=get_binance_data, args=(symbol, 'spot', "2020-1-1", "2020-11-16")) 145 | 146 | t1.start() 147 | t2.start() 148 | t3.start() 149 | 150 | t1.join() 151 | t2.join() 152 | t3.join() 153 | 154 | 155 | def download_usdt_future(symbol): 156 | """ 157 | 下载合约数据的方法。 158 | :return: 159 | """ 160 | t1 = Thread(target=get_binance_data, args=(symbol, 'usdt_future', "2019-9-10", "2020-3-1")) 161 | t2 = Thread(target=get_binance_data, args=(symbol, 'usdt_future', "2019-3-1", "2020-11-16")) 162 | 163 | t1.start() 164 | t2.start() 165 | 166 | t1.join() 167 | t2.join() 168 | 169 | 170 | if __name__ == '__main__': 171 | 172 | # 如果你有代理你就设置,如果没有你就设置为 None 或者空的字符串 "", 173 | # 但是你要确保你的电脑网络能访问币安交易所,你可以通过 ping api.binance.com 看看过能否ping得通 174 | proxy_host = "127.0.0.1" # 如果没有就设置为"", 如果有就设置为你的代理主机如:127.0.0.1 175 | proxy_port = 1087 # 设置你的代理端口号如: 1087, 没有你修改为0,但是要保证你能访问api.binance.com这个主机。 176 | 177 | proxies = None 178 | if proxy_host and proxy_port: 179 | proxy = f'http://{proxy_host}:{proxy_port}' 180 | proxies = {'http': proxy, 'https': proxy} 181 | 182 | symbol = "BTCUSDT" 183 | 184 | # download_spot(symbol) # 下载现货的数据. 185 | download_usdt_future(symbol) # 下载合约的数据 186 | -------------------------------------------------------------------------------- /class_08/main.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_09/class_09.md: -------------------------------------------------------------------------------- 1 | # 第九课: 第二章总结--数据准备和学习要求 2 | 3 | 4 | ## 1. 软件要求 5 | 通过pip install的方式进行安装 6 | > pip install git+https://github.com/ramoslin02/howtrader.git 7 | 8 | 更新到最新, 后面加上-U表示更新到最新的 9 | > pip install git+https://github.com/ramoslin02/howtrader.git -U 10 | 11 | 12 | ## 1. 数据爬取 13 | 1. 数据库: 为了方便学习,降低学习成本,用sqlite数据库,不用任何配置 14 | 2. 用crawl_data爬取BTCUSDT, ETHUSDT, BNBUSDT等现货和合约的数据, 15 | 如果没有数据后面课程没法学习。 16 | 3. okex和火币没有提供历史数据,他们最多提供2000个K线的数据,学习和研究非常不方便,除非自己购买第三方数据。 17 | 18 | 19 | ## 2. 把UI界面和行情跑起来 20 | 21 | 1. 注册币安账号 22 | 没有开通币安或者交易所的可以通过下面链接去开通,有手续费减免: 23 | **https://www.binancezh.pro/cn/futures/ref/51bitquant** 24 | 合约邀请码:**51bitquant** 25 | 26 | 2. 配置API: 生成api地址: 27 | https://www.binancezh.pro/cn/usercenter/settings/api-management 28 | 29 | 3. 启动界面 30 | > python main.py 31 | 32 | ## 注意事项 33 | 如何确定自己电脑的网络是否能访问币安交易所呢? 34 | 35 | 在终端输入 36 | > ping api.binance.com 37 | 38 | 如果能访问就不用配置代理,如果不能访问就需要配置代理主机和代理端口 39 | 40 | -------------------------------------------------------------------------------- /class_09/crawl_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | 我们使用币安原生的api进行数据爬取. 3 | 1. 增加代理配置 4 | 5 | author: 51bitquant 6 | 7 | discord: 51bitquant#8078 8 | 9 | """ 10 | 11 | import pandas as pd 12 | import time 13 | from datetime import datetime 14 | import requests 15 | import pytz 16 | from howtrader.trader.database import get_database, BaseDatabase 17 | 18 | pd.set_option('expand_frame_repr', False) # 19 | 20 | BINANCE_SPOT_LIMIT = 1000 21 | BINANCE_FUTURE_LIMIT = 1500 22 | 23 | CHINA_TZ = pytz.timezone("Asia/Shanghai") 24 | from threading import Thread 25 | 26 | database: BaseDatabase = get_database() 27 | 28 | def generate_datetime(timestamp: float) -> datetime: 29 | """ 30 | :param timestamp: 31 | :return: 32 | """ 33 | dt = datetime.fromtimestamp(timestamp / 1000) 34 | dt = CHINA_TZ.localize(dt) 35 | return dt 36 | 37 | 38 | def get_binance_data(symbol: str, exchanges: str, start_time: str, end_time: str): 39 | """ 40 | 爬取币安交易所的数据 41 | :param symbol: BTCUSDT. 42 | :param exchanges: 现货、USDT合约, 或者币币合约. 43 | :param start_time: 格式如下:2020-1-1 或者2020-01-01 44 | :param end_time: 格式如下:2020-1-1 或者2020-01-01 45 | :param gate_way the gateway name for binance is:BINANCE_SPOT, BINANCE_USDT, BINANCE_INVERSE 46 | :return: 47 | """ 48 | 49 | api_url = '' 50 | save_symbol = symbol 51 | 52 | if exchanges == 'spot': 53 | print("spot") 54 | limit = BINANCE_SPOT_LIMIT 55 | save_symbol = symbol.lower() 56 | gateway = "BINANCE_SPOT" 57 | api_url = f'https://api.binance.com/api/v3/klines?symbol={symbol}&interval=1m&limit={limit}' 58 | 59 | elif exchanges == 'usdt_future': 60 | print('usdt_future') 61 | limit = BINANCE_FUTURE_LIMIT 62 | gateway = "BINANCE_USDT" 63 | api_url = f'https://fapi.binance.com/fapi/v1/klines?symbol={symbol}&interval=1m&limit={limit}' 64 | 65 | elif exchanges == 'inverse_future': 66 | print("inverse_future") 67 | limit = BINANCE_FUTURE_LIMIT 68 | gateway = "BINANCE_INVERSE" 69 | f'https://dapi.binance.com/dapi/v1/klines?symbol={symbol}&interval=1m&limit={limit}' 70 | 71 | else: 72 | raise Exception('交易所名称请输入以下其中一个:spot, future, coin_future') 73 | 74 | start_time = int(datetime.strptime(start_time, '%Y-%m-%d').timestamp() * 1000) 75 | end_time = int(datetime.strptime(end_time, '%Y-%m-%d').timestamp() * 1000) 76 | 77 | while True: 78 | try: 79 | print(start_time) 80 | url = f'{api_url}&startTime={start_time}' 81 | print(url) 82 | datas = requests.get(url=url, timeout=10, proxies=proxies).json() 83 | 84 | """ 85 | [ 86 | [ 87 | 1591258320000, // 开盘时间 88 | "9640.7", // 开盘价 89 | "9642.4", // 最高价 90 | "9640.6", // 最低价 91 | "9642.0", // 收盘价(当前K线未结束的即为最新价) 92 | "206", // 成交量 93 | 1591258379999, // 收盘时间 94 | "2.13660389", // 成交额(标的数量) 95 | 48, // 成交笔数 96 | "119", // 主动买入成交量 97 | "1.23424865", // 主动买入成交额(标的数量) 98 | "0" // 请忽略该参数 99 | ] 100 | 101 | """ 102 | 103 | buf = [] 104 | 105 | for row in datas: 106 | bar: BarData = BarData( 107 | symbol=save_symbol, 108 | exchange=Exchange.BINANCE, 109 | datetime=generate_datetime(row[0]), 110 | interval=Interval.MINUTE, 111 | volume=float(row[5]), 112 | turnover=float(row[7]), 113 | open_price=float(row[1]), 114 | high_price=float(row[2]), 115 | low_price=float(row[3]), 116 | close_price=float(row[4]), 117 | gateway_name=gateway 118 | ) 119 | buf.append(bar) 120 | buf.append(bar) 121 | 122 | database.save_bar_data(buf) 123 | 124 | # 到结束时间就退出, 后者收盘价大于当前的时间. 125 | if (datas[-1][0] > end_time) or datas[-1][6] >= (int(time.time() * 1000) - 60 * 1000): 126 | break 127 | 128 | start_time = datas[-1][0] 129 | 130 | except Exception as error: 131 | print(error) 132 | time.sleep(10) 133 | 134 | 135 | def download_spot(symbol): 136 | """ 137 | 下载现货数据的方法. 138 | :return: 139 | """ 140 | t1 = Thread(target=get_binance_data, args=(symbol, 'spot', "2018-1-1", "2019-1-1")) 141 | t2 = Thread(target=get_binance_data, args=(symbol, 'spot', "2019-1-1", "2020-1-1")) 142 | t3 = Thread(target=get_binance_data, args=(symbol, 'spot', "2020-1-1", "2020-12-1")) 143 | 144 | t1.start() 145 | t2.start() 146 | t3.start() 147 | 148 | t1.join() 149 | t2.join() 150 | t3.join() 151 | 152 | 153 | def download_future(symbol): 154 | """ 155 | 下载合约数据的方法。 156 | :return: 157 | """ 158 | 159 | # BTCUSDT的, 要注意看该币的上市时间。 160 | t1 = Thread(target=get_binance_data, args=(symbol, 'future', "2019-9-10", "2020-2-1")) 161 | t2 = Thread(target=get_binance_data, args=(symbol, 'future', "2020-2-1", "2020-7-1")) 162 | t3 = Thread(target=get_binance_data, args=(symbol, 'future', "2020-7-1", "2020-12-1")) 163 | 164 | # ETHUSDT 165 | # t1 = Thread(target=get_binance_data, args=(symbol, 'future', "2019-11-30", "2020-4-1")) 166 | # t2 = Thread(target=get_binance_data, args=(symbol, 'future', "2020-4-1", "2020-8-1")) 167 | # t3 = Thread(target=get_binance_data, args=(symbol, 'future', "2020-8-1", "2020-12-1")) 168 | 169 | # BNBUSDT 170 | # t1 = Thread(target=get_binance_data, args=(symbol, 'future', "2020-02-11", "2020-5-1")) 171 | # t2 = Thread(target=get_binance_data, args=(symbol, 'future', "2020-5-1", "2020-9-1")) 172 | # t3 = Thread(target=get_binance_data, args=(symbol, 'future', "2020-9-1", "2020-12-1")) 173 | 174 | t1.start() 175 | t2.start() 176 | t3.start() 177 | 178 | t1.join() 179 | t2.join() 180 | t3.join() 181 | 182 | 183 | if __name__ == '__main__': 184 | 185 | # 如果你有代理你就设置,如果没有你就设置为 None 或者空的字符串 "", 186 | # 但是你要确保你的电脑网络能访问币安交易所,你可以通过 ping api.binance.com 看看过能否ping得通 187 | proxy_host = "127.0.0.1" # 如果没有就设置为"", 如果有就设置为你的代理主机如:127.0.0.1 188 | proxy_port = 1087 # 设置你的代理端口号如: 1087, 没有你修改为0,但是要保证你能访问api.binance.com这个主机。 189 | 190 | proxies = None 191 | if proxy_host and proxy_port: 192 | proxy = f'http://{proxy_host}:{proxy_port}' 193 | proxies = {'http': proxy, 'https': proxy} 194 | 195 | symbol = "BTCUSDT" 196 | # symbol = "ETHUSDT" 197 | # symbol = "BNBUSDT" 198 | download_spot(symbol) # 下载现货的数据. 199 | 200 | 201 | # symbol = "BTCUSDT" 202 | # symbol = "ETHUSDT" 203 | # symbol = "BNBUSDT" 204 | 205 | # download_future(symbol) # 下载合约的数据 206 | -------------------------------------------------------------------------------- /class_09/main.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_10/class_10.md: -------------------------------------------------------------------------------- 1 | # 第十课: 认识VNPY CTA策略模板 2 | 3 | ## 要求 4 | 1. 更新软件到最新的版本, 截止目前最新版本为V2.1.7.6,不然可能会有问题 5 | 6 | 2. 更新方法: 在终端输入一下命令: 7 | 8 | > pip install git+https://github.com/51bitquant/howtrader.git -U 9 | 10 | 或者先卸载然后再安装 11 | > pip uninstall howtrader 12 | 13 | > pip install git+https://github.com/51bitquant/howtrader.git 14 | 15 | 3. 课程代码和课件下载地址: https://github.com/51bitquant/course_codes 16 | 17 | ## 系统的内置的策略 18 | 在框架howtrader.app.cta_strategy.strategies目录下 19 | 20 | ## 写一个简单的策略 21 | 1. 在项目的启动文件夹下面创建一个叫strategies文件夹 22 | 2. 拷贝系统内置的策略,一份然后修改里面的方法 23 | 24 | 25 | ## 策略启动的步骤 26 | 1. 连接交易所 27 | 2. 添加策略(添加策略可以通过 cta_engine.add_strategy()) 28 | 3. 启动策略 29 | -------------------------------------------------------------------------------- /class_10/main_script.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from time import sleep 3 | from datetime import datetime, time 4 | from logging import INFO 5 | 6 | from howtrader.event import EventEngine 7 | from howtrader.trader.setting import SETTINGS 8 | from howtrader.trader.engine import MainEngine 9 | from howtrader.app.cta_strategy.engine import CtaEngine 10 | 11 | from howtrader.gateway.binance import BinanceSpotGateway, BinanceUsdtGateway, BinanceInverseGateway 12 | from howtrader.app.cta_strategy import CtaStrategyApp 13 | from howtrader.app.cta_strategy.base import EVENT_CTA_LOG 14 | 15 | SETTINGS["log.active"] = True # 16 | SETTINGS["log.level"] = INFO 17 | SETTINGS["log.console"] = True # 打印信息到终端. 18 | 19 | gateway_settings = { 20 | "key": "xx", 21 | "secret": "xxx", 22 | "proxy_host": "127.0.0.1", 23 | "proxy_port": 1087 24 | } 25 | 26 | 27 | if __name__ == "__main__": 28 | 29 | SETTINGS["log.file"] = True 30 | 31 | event_engine = EventEngine() # 初始化事件引擎 32 | main_engine = MainEngine(event_engine) # 初始化主引擎 33 | main_engine.add_gateway(BinanceUsdtGateway) # 添加cta策略的app 34 | 35 | 36 | cta_engine: CtaEngine = main_engine.add_app(CtaStrategyApp) 37 | # 添加cta引擎, 实际上就是初始化引擎。 38 | 39 | main_engine.write_log("主引擎创建成功") 40 | 41 | log_engine = main_engine.get_engine("log") 42 | event_engine.register(EVENT_CTA_LOG, log_engine.process_log_event) 43 | main_engine.write_log("注册日志事件监听") 44 | 45 | main_engine.connect(gateway_settings, "BINANCE_USDT") 46 | main_engine.write_log("连接BINANCE接口") 47 | 48 | sleep(10) # 稍作等待策略启动完成。 49 | 50 | cta_engine.init_engine() 51 | # 启动引擎 --> 实际上是处理CTA策略要准备的事情,加载策略 52 | # 具体加载的策略来自于配置文件howtrader/cta_strategy_settings.json 53 | # 仓位信息来自于howtrader/cta_strategy_data.json 54 | main_engine.write_log("CTA策略初始化完成") 55 | 56 | # cta_engine.add_strategy() # 类似于我们在UI界面添加策略的操作类似 57 | 58 | cta_engine.init_all_strategies() # 初始化所有的策略, 具体启动的哪些策略是来自于配置文件的 59 | 60 | sleep(60) # 预留足够的时间让策略去初始化. 61 | 62 | main_engine.write_log("CTA策略全部初始化") 63 | 64 | cta_engine.start_all_strategies() # 开启所有的策略. 65 | 66 | main_engine.write_log("CTA策略全部启动") 67 | 68 | while True: 69 | sleep(10) 70 | -------------------------------------------------------------------------------- /class_10/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceSpotGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceSpotGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(CtaBacktesterApp) 30 | main_engine.add_app(DataManagerApp) 31 | main_engine.add_app(AlgoTradingApp) 32 | main_engine.add_app(DataRecorderApp) 33 | main_engine.add_app(RiskManagerApp) 34 | main_engine.add_app(SpreadTradingApp) 35 | 36 | main_window = MainWindow(main_engine, event_engine) 37 | main_window.showMaximized() 38 | 39 | qapp.exec() 40 | 41 | 42 | if __name__ == "__main__": 43 | """ 44 | howtrader main window demo 45 | howtrader 的图形化界面 46 | 47 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 48 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 49 | while the futures use the upper cases. 50 | 51 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 52 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 53 | 54 | BTCUSDT, BTCUSDT 55 | """ 56 | 57 | main() 58 | 59 | -------------------------------------------------------------------------------- /class_10/strategies/class_10_simple_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.trader.constant import Interval 8 | from howtrader.app.cta_strategy.engine import CtaEngine, EngineType 9 | from howtrader.trader.utility import BarGenerator 10 | from decimal import Decimal 11 | 12 | class Class10SimpleStrategy(CtaTemplate): 13 | author = "51bitquant" 14 | 15 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 16 | """""" 17 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 18 | self.bg2 = BarGenerator(self.on_bar, 2, self.on_2min_bar, Interval.MINUTE) 19 | self.bg5 = BarGenerator(self.on_bar, 5, self.on_5min_bar, Interval.MINUTE) 20 | self.bg_1hour = BarGenerator(self.on_bar, 1, self.on_1hour_bar, Interval.HOUR) 21 | 22 | self.place_order = False 23 | self.orders = [] 24 | # self.pos # 25 | 26 | def on_init(self): 27 | """ 28 | Callback when strategy is inited. 29 | """ 30 | self.write_log("策略初始化") 31 | 32 | 33 | def on_start(self): 34 | """ 35 | Callback when strategy is started. 36 | """ 37 | self.write_log("策略启动") 38 | self.put_event() 39 | 40 | 41 | def on_stop(self): 42 | """ 43 | Callback when strategy is stopped. 44 | """ 45 | self.write_log("策略停止") 46 | 47 | self.put_event() 48 | 49 | 50 | def on_tick(self, tick: TickData): 51 | 52 | print(f"tick, ask1:{tick.ask_price_1}, {tick.ask_volume_1}, bid:{tick.bid_price_1}, {tick.bid_volume_1}") 53 | print(f"my current pos is: {self.pos}, ask:{tick.ask_price_1}, bid: {tick.bid_price_1}") 54 | 55 | if self.place_order is False and self.trading: 56 | buy_order = self.buy(Decimal(tick.bid_price_1 * 0.9999), Decimal("0.5")) 57 | # sell_order = self.short(tick.ask_price_1 * 1.0001, 0.01) 58 | sell_order = self.sell(Decimal(tick.ask_price_1 * 1.0002), Decimal("0.5")) 59 | 60 | # self.short() 61 | # self.cover() # 62 | 63 | # self.buy() 64 | # self.short() 65 | self.place_order = True 66 | print(f"buy_order: {buy_order}, sell_order: {sell_order}") 67 | self.orders.extend(buy_order) 68 | self.orders.extend(sell_order) 69 | 70 | self.bg2.update_tick(tick) 71 | self.bg5.update_tick(tick) 72 | self.bg_1hour.update_tick(tick) 73 | 74 | def on_bar(self, bar: BarData): 75 | """ 76 | Callback of new bar data update. 77 | """ 78 | print("1分钟的K线数据", bar) 79 | self.bg2.update_bar(bar) 80 | self.bg5.update_bar(bar) # 合成2分钟的K线 81 | self.bg_1hour.update_bar(bar) # 合成一小时的数据。 82 | self.put_event() 83 | 84 | def on_2min_bar(self, bar: BarData): 85 | """ 86 | Callback of new bar data update. 87 | """ 88 | print("2分钟的K线数据", bar) 89 | self.put_event() 90 | 91 | def on_5min_bar(self, bar: BarData): 92 | """ 93 | Callback of new bar data update. 94 | """ 95 | print("5分钟的K线数据", bar) 96 | self.put_event() 97 | 98 | def on_1hour_bar(self, bar:BarData): 99 | 100 | print("1小时的K线数据", bar) 101 | self.put_event() 102 | 103 | def on_order(self, order: OrderData): 104 | """ 105 | 订单的回调方法: 订单状态更新的时候,会调用这个方法。 106 | """ 107 | 108 | print("策略推送过来的order: ", order) 109 | 110 | self.put_event() 111 | 112 | 113 | 114 | def on_trade(self, trade: TradeData): 115 | """ 116 | 订单成交的推送,比如你下10个BTC,那么可能不会一下子成交,会不断慢慢的成交, 117 | 这时有成交它就会推送给你,告诉你成交了多少,还有多少没有成交 118 | 系统通过里面处理这个方法,知道你当前的仓位数量 119 | 120 | """ 121 | print("最新的成交: ", trade) 122 | self.put_event() # 更新UI界面方法。 123 | 124 | 125 | def on_stop_order(self, stop_order: StopOrder): 126 | """ 127 | 这个是一个停止单的方法,用来监听你止损单的方法。 128 | """ 129 | pass 130 | 131 | -------------------------------------------------------------------------------- /class_10/strategies/class_10_simple_strategy1.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData, Interval 7 | 8 | from howtrader.trader.utility import BarGenerator, ArrayManager 9 | from datetime import datetime 10 | from howtrader.app.cta_strategy.engine import CtaEngine 11 | from decimal import Decimal 12 | 13 | 14 | class Class10SimpleStrategy1(CtaTemplate): 15 | author = "51bitquant" 16 | 17 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 18 | """""" 19 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 20 | 21 | self.bg2 = BarGenerator(self.on_bar, 2, self.on_2min_bar, Interval.MINUTE) 22 | self.bg3 = BarGenerator(self.on_bar, 3, self.on_3min_bar, Interval.MINUTE) 23 | self.bg5 = BarGenerator(self.on_bar, 5, self.on_5min_bar, Interval.MINUTE) 24 | 25 | # self.am3 = ArrayManager() # 3分钟的时间序列. 26 | # # 如果想获取其他周期的K线数据 27 | # self.bg_1hour = BarGenerator(self.on_bar, 1, self.on_1hour_bar, Interval.HOUR) # 1小时的K线. 28 | # self.am_1hour = ArrayManager() 29 | self.place_order = False 30 | self.orders = [] 31 | # cta_engine.register_event() 32 | # cta_engine.register(EVENT_POSITION, self.process_position_event) 33 | 34 | def on_init(self): 35 | """ 36 | Callback when strategy is inited. 37 | """ 38 | self.write_log("策略初始化") 39 | # self.load_bar(1) 40 | # self.load_bar() 方法向交易所请求获取K线数据,这个n表示获取多少天的一分钟的K线数据. 41 | # 通过1一分钟的K线来合成5/10/15,30分钟的数据,甚至一小时等更多的数据. 42 | # 这个方法不许要调用,不然会出错. 43 | 44 | 45 | def on_start(self): 46 | """ 47 | Callback when strategy is started. 48 | """ 49 | self.write_log("策略启动") 50 | self.put_event() # 如果你要让UI界面更新你就要调用这个方法,这个方法是用来通知系统更新UI图形界面的。 51 | 52 | 53 | def on_stop(self): 54 | """ 55 | Callback when strategy is stopped. 56 | """ 57 | self.write_log("策略停止") 58 | 59 | self.put_event() 60 | 61 | 62 | def on_tick(self, tick: TickData): 63 | """ 64 | 盘口的数据更新的方法. 65 | """ 66 | # self.bg.update_tick(tick) # 该方法是把tick数据合成分钟的K线数据 67 | # print("\n") 68 | # print(tick.ask_price_1, tick.ask_volume_1, tick.datetime, datetime.now()) 69 | # print("-------") 70 | # print(tick.bid_price_1, tick.bid_volume_1, tick.datetime, datetime.now()) 71 | # print("\n") 72 | print(f"my current pos is: {self.pos}, ask:{tick.ask_price_1}, bid: {tick.bid_price_1}") 73 | 74 | if self.place_order is False and self.trading: 75 | buy_order = self.buy(Decimal(tick.bid_price_1 * 0.99), Decimal("0.5")) 76 | # sell_order = self.short(tick.ask_price_1 * 1.0001, 0.01) 77 | sell_order = self.sell(Decimal(tick.ask_price_1 * 1.01), Decimal("0.5")) 78 | self.place_order = True 79 | print(f"buy_order: {buy_order}, sell_order: {sell_order}") 80 | self.orders += buy_order 81 | self.orders += sell_order 82 | 83 | self.bg2.update_tick(tick) 84 | self.bg3.update_tick(tick) 85 | self.bg5.update_tick(tick) 86 | 87 | def on_bar(self, bar: BarData): 88 | """ 89 | Callback of new bar data update. 90 | """ 91 | print("一分钟的K线数据", bar) 92 | self.bg2.update_bar(bar) 93 | self.bg3.update_bar(bar) 94 | self.bg5.update_bar(bar) 95 | self.put_event() 96 | 97 | 98 | def on_2min_bar(self, bar: BarData): 99 | print("2分钟的Bar", bar) # 2分钟的K线数据. 100 | 101 | 102 | def on_3min_bar(self, bar: BarData): 103 | print("3分钟的Bar", bar) # 3分钟的K线数据. 104 | 105 | 106 | def on_5min_bar(self, bar: BarData): 107 | print("5分钟的Bar", bar) # 5分钟的K线数据. 108 | 109 | 110 | def on_10min_bar(self, bar: BarData): 111 | print("10分钟的Bar", bar) # 10分钟的K线数据. 112 | 113 | 114 | def on_20min_bar(self, bar: BarData): 115 | print("20分钟的Bar", bar) # 20分钟的K线数据. 116 | 117 | 118 | def on_30min_bar(self, bar: BarData): 119 | print("30分钟的Bar", bar) # 30分钟的K线数据. 120 | 121 | 122 | def on_1hour_bar(self, bar: BarData): 123 | print("1小时的Bar", bar) 124 | 125 | 126 | def on_order(self, order: OrderData): 127 | """ 128 | 订单的回调方法: 订单状态更新的时候,会调用这个方法。 129 | """ 130 | 131 | print("策略推送过来的order: ", order) 132 | 133 | self.put_event() 134 | 135 | 136 | def on_trade(self, trade: TradeData): 137 | """ 138 | 订单成交的推送,比如你下10个BTC,那么可能不会一下子成交,会不断慢慢的成交, 139 | 这时有成交它就会推送给你,告诉你成交了多少,还有多少没有成交 140 | 系统通过里面处理这个方法,知道你当前的仓位数量 141 | 142 | """ 143 | print("最新的成交: ", trade) 144 | self.put_event() # 更新UI界面方法。 145 | 146 | 147 | def on_stop_order(self, stop_order: StopOrder): 148 | """ 149 | 这个是一个停止单的方法,用来监听你止损单的方法。 150 | """ 151 | pass 152 | 153 | -------------------------------------------------------------------------------- /class_11/class_11.md: -------------------------------------------------------------------------------- 1 | # 第十一课: VNPY CTA策略代码加载启动过程讲解 2 | 3 | 具体代码: main_script.py 4 | 5 | ## 策略启动的步骤 6 | 1. 启动主引擎 7 | 2. 添加网关 8 | 3. 添加CtaStrategyApp 9 | 4. 链接交易所 10 | 5. 初始化cta引擎(加载策略配置信息等,添加策略等等) 11 | 6. 启动策略 12 | -------------------------------------------------------------------------------- /class_11/main_script.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from time import sleep 3 | from datetime import datetime, time 4 | from logging import INFO 5 | 6 | from howtrader.event import EventEngine 7 | from howtrader.trader.setting import SETTINGS 8 | from howtrader.trader.engine import MainEngine 9 | from howtrader.app.cta_strategy.engine import CtaEngine 10 | 11 | from howtrader.gateway.binance import BinanceSpotGateway 12 | from howtrader.gateway.binance import BinanceUsdtGateway 13 | from howtrader.app.cta_strategy import CtaStrategyApp 14 | from howtrader.app.cta_strategy.base import EVENT_CTA_LOG 15 | 16 | SETTINGS["log.active"] = True # 17 | SETTINGS["log.level"] = INFO 18 | SETTINGS["log.console"] = True # 打印信息到终端. 19 | 20 | # 现货的 21 | gateway_settings = { 22 | "key": "xxx", 23 | "secret": "xxx", 24 | "proxy_host": "", 25 | "proxy_port": 0 26 | } 27 | 28 | 29 | if __name__ == "__main__": 30 | 31 | SETTINGS["log.file"] = True 32 | 33 | event_engine = EventEngine() # 初始化事件引擎 34 | main_engine = MainEngine(event_engine) # 初始化主引擎 35 | main_engine.add_gateway(BinanceSpotGateway) # 加载币安现货的网关 36 | main_engine.add_gateway(BinanceUsdtGateway) # 加载币安合约的网关 37 | 38 | cta_engine: CtaEngine = main_engine.add_app(CtaStrategyApp) #添加cta策略的app 39 | # 添加cta引擎, 实际上就是初始化引擎。 40 | 41 | 42 | main_engine.write_log("主引擎创建成功") 43 | 44 | log_engine = main_engine.get_engine("log") 45 | event_engine.register(EVENT_CTA_LOG, log_engine.process_log_event) 46 | main_engine.write_log("注册日志事件监听") 47 | 48 | # 连接到交易所 49 | main_engine.connect(gateway_settings, "BINANCE_SPOT") 50 | main_engine.write_log("connect binance spot gateway") 51 | 52 | main_engine.connect(gateway_settings, "BINANCE_USDT") 53 | main_engine.write_log("connect binance usdt future gateway") 54 | 55 | sleep(20) # 稍作等待策略启动完成。 56 | 57 | cta_engine.init_engine() 58 | # 启动引擎 --> 实际上是处理CTA策略要准备的事情,加载策略 59 | # 具体加载的策略来自于配置文件howtrader/cta_strategy_settings.json 60 | # 仓位信息来自于howtrader/cta_strategy_data.json 61 | main_engine.write_log("CTA策略初始化完成") 62 | 63 | # cta_engine.add_strategy() # 类似于我们在UI界面添加策略的操作类似 64 | # cta_engine.add_strategy('Class11SimpleStrategy', 'bnbusdt_spot', 'bnbusdt.BINANCE', {}) 65 | # 在配置文件有这个配置信息就不需要手动添加。 66 | 67 | cta_engine.init_all_strategies() # 初始化所有的策略, 具体启动的哪些策略是来自于配置文件的 68 | 69 | sleep(30) # 预留足够的时间让策略去初始化. 70 | 71 | main_engine.write_log("CTA策略全部初始化") 72 | 73 | cta_engine.start_all_strategies() # 开启所有的策略. 74 | 75 | main_engine.write_log("CTA策略全部启动") 76 | 77 | while True: 78 | sleep(10) 79 | 80 | # shell nohub -------------------------------------------------------------------------------- /class_11/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() 57 | 58 | -------------------------------------------------------------------------------- /class_11/strategies/class_11_simple_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.trader.utility import BarGenerator 8 | 9 | from howtrader.trader.constant import Interval 10 | from decimal import Decimal 11 | from howtrader.app.cta_strategy.engine import CtaEngine 12 | 13 | # 记得修改你的文件的类名 14 | class Class11SimpleStrategy(CtaTemplate): 15 | 16 | author = "51bitquant" 17 | 18 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 19 | """""" 20 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 21 | self.bg2 = BarGenerator(self.on_bar, 2, self.on_2min_bar, Interval.MINUTE) 22 | self.bg5 = BarGenerator(self.on_bar, 5, self.on_5min_bar, Interval.MINUTE) 23 | self.bg_1hour = BarGenerator(self.on_bar, 1, self.on_1hour_bar, Interval.HOUR) 24 | self.bg_4hour = BarGenerator(self.on_bar, 4, self.on_4hour_bar, Interval.HOUR) 25 | 26 | self.place_order = False 27 | self.orders = [] 28 | # self.pos # 29 | 30 | def on_init(self): 31 | """ 32 | Callback when strategy is inited. 33 | """ 34 | self.write_log("策略初始化") 35 | 36 | 37 | def on_start(self): 38 | """ 39 | Callback when strategy is started. 40 | """ 41 | self.write_log(f"我的策略启动, {self.trading}") 42 | self.put_event() 43 | 44 | 45 | def on_stop(self): 46 | """ 47 | Callback when strategy is stopped. 48 | """ 49 | self.write_log("策略停止") 50 | 51 | self.put_event() 52 | 53 | 54 | def on_tick(self, tick: TickData): 55 | print(f"tick, ask1:{tick.ask_price_1}, {tick.ask_volume_1}, bid:{tick.bid_price_1}, {tick.bid_volume_1}") 56 | print(f"my current pos is: {self.pos}, ask:{tick.ask_price_1}, bid: {tick.bid_price_1}") 57 | 58 | if self.place_order is False and self.trading: 59 | buy_order = self.buy(Decimal(tick.bid_price_1 * 0.9999), Decimal("0.5")) 60 | sell_order = self.sell(Decimal(tick.ask_price_1 * 1.0002), Decimal("0.5")) 61 | 62 | # self.short() 63 | # self.cover() # 64 | 65 | # self.buy() 66 | # self.short() 67 | self.place_order = True 68 | print(f"buy_order: {buy_order}, sell_order: {sell_order}") 69 | self.orders += buy_order 70 | self.orders += sell_order 71 | 72 | self.bg2.update_tick(tick) 73 | self.bg5.update_tick(tick) 74 | self.bg_1hour.update_tick(tick) 75 | self.bg_4hour.update_tick(tick) 76 | 77 | def on_bar(self, bar: BarData): 78 | """ 79 | Callback of new bar data update. 80 | """ 81 | print("1分钟的K线数据", bar) 82 | self.bg2.update_bar(bar) 83 | self.bg5.update_bar(bar) # 合成2分钟的K线 84 | self.bg_1hour.update_bar(bar) # 合成一小时的数据。 85 | self.put_event() 86 | 87 | def on_2min_bar(self, bar: BarData): 88 | """ 89 | Callback of new bar data update. 90 | """ 91 | print("2分钟的K线数据", bar) 92 | self.put_event() 93 | 94 | def on_5min_bar(self, bar: BarData): 95 | """ 96 | Callback of new bar data update. 97 | """ 98 | print("5分钟的K线数据", bar) 99 | self.put_event() 100 | 101 | def on_1hour_bar(self, bar:BarData): 102 | 103 | print("1小时的K线数据", bar) 104 | self.put_event() 105 | 106 | def on_4hour_bar(self, bar: BarData): 107 | print("4小时的K线数据", bar) 108 | 109 | self.put_event() 110 | 111 | def on_order(self, order: OrderData): 112 | """ 113 | 订单的回调方法: 订单状态更新的时候,会调用这个方法。 114 | """ 115 | 116 | print("策略推送过来的order: ", order) 117 | 118 | self.put_event() 119 | 120 | 121 | 122 | def on_trade(self, trade: TradeData): 123 | """ 124 | 订单成交的推送,比如你下10个BTC,那么可能不会一下子成交,会不断慢慢的成交, 125 | 这时有成交它就会推送给你,告诉你成交了多少,还有多少没有成交 126 | 系统通过里面处理这个方法,知道你当前的仓位数量 127 | 128 | """ 129 | print("最新的成交: ", trade) 130 | self.put_event() # 更新UI界面方法。 131 | 132 | 133 | def on_stop_order(self, stop_order: StopOrder): 134 | """ 135 | 这个是一个停止单的方法,用来监听你止损单的方法。 136 | """ 137 | pass 138 | 139 | -------------------------------------------------------------------------------- /class_12/backtest_fixed_time.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy.backtesting import BacktestingEngine 2 | from howtrader.trader.object import Interval 3 | from datetime import datetime 4 | from strategies.fixed_trade_time_strategy import FixedTradeTimeStrategy 5 | 6 | if __name__ == '__main__': 7 | engine = BacktestingEngine() 8 | 9 | engine.set_parameters( 10 | vt_symbol="btcusdt.BINANCE", # 现货的数据 11 | interval=Interval.MINUTE, 12 | start=datetime(2018,1,1), 13 | end=datetime(2018,6,1), 14 | rate=1/1000, # 币安手续费千分之1, BNB 万7.5 7.5/10000 15 | slippage=0, 16 | size=1, # 币本位合约 100 17 | pricetick=0.01, # 价格精度. 18 | capital=300000) 19 | 20 | engine.add_strategy(FixedTradeTimeStrategy, {}) 21 | 22 | engine.load_data() 23 | engine.run_backtesting() 24 | 25 | engine.calculate_result() # 计算回测的结果 26 | engine.calculate_statistics() # 计算一些统计指标 27 | 28 | engine.show_chart() # 绘制图表 29 | 30 | # 收益上看似不高,但是要知道你一开始你就没有投入100万,这个是相当于你慢慢挣钱,50%-60% 31 | # 慢慢投资,然后等你攒到100万的时候,实际上你已经有了140万 32 | # 所以定投有相当于懒人理财的功能。 33 | # 30 -- 805,090.27 34 | -------------------------------------------------------------------------------- /class_12/class_12.md: -------------------------------------------------------------------------------- 1 | # 第十二课: 开发两个不同原理资金定投策略并回测研究分析 2 | 3 | 目的我们先学习一个简单的开发流程,然后根据自己的思路和方式去交易。学习分析的方法、思路和编程的思路才是我们这个课程首要的学习目标。 4 | 5 | ## 定投的方式 6 | 7 | 1. 基于时间的定投: 循规蹈矩, 通过时间来平均投资成本 8 | 2. 基于价格来定投: 下跌多少就买入,越跌越买 9 | 10 | ## 回测 11 | 12 | 1. 基于代码和基于UI界面的回测. 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /class_12/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_12/strategies/fixed_trade_price_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.trader.constant import Interval 8 | from howtrader.trader.utility import BarGenerator, ArrayManager 9 | from howtrader.app.cta_strategy.engine import CtaEngine 10 | from decimal import Decimal 11 | 12 | class FixedTradPriceStrategy(CtaTemplate): 13 | """ 14 | 基于价格的定投 15 | """ 16 | author = "51bitquant" 17 | fixed_trade_money = 1000 # 每次定投的资金比例. 18 | price_change_pct = 0.05 # 价格变动多少的时候定投 19 | 20 | parameters = ['fixed_trade_money', 'price_change_pct'] 21 | 22 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 23 | """""" 24 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 25 | self.bg_4hour = BarGenerator(self.on_bar, 4, self.on_4hour_bar, Interval.HOUR) 26 | self.am = ArrayManager(size=100) # 时间序列,类似我们用的pandas, 值保留最近的N个K线的数据. 27 | 28 | def on_init(self): 29 | """ 30 | Callback when strategy is inited. 31 | """ 32 | self.write_log("策略初始化") 33 | self.load_bar(1) # 具体加载多少天的数据, 1表示1天的数据,如果是2表示过去2天的数据 34 | 35 | def on_start(self): 36 | """ 37 | Callback when strategy is started. 38 | """ 39 | self.write_log(f"我的策略启动") 40 | self.put_event() 41 | 42 | 43 | def on_stop(self): 44 | """ 45 | Callback when strategy is stopped. 46 | """ 47 | self.write_log("策略停止") 48 | self.put_event() 49 | 50 | 51 | def on_tick(self, tick: TickData): 52 | self.bg_4hour.update_tick(tick) 53 | 54 | def on_bar(self, bar: BarData): 55 | """ 56 | Callback of new bar data update. 57 | """ 58 | self.bg_4hour.update_bar(bar) # 合成四小时的数据. 59 | self.put_event() 60 | 61 | def on_4hour_bar(self, bar: BarData): 62 | """ 63 | 四小时的K线数据. 64 | """ 65 | self.cancel_all() # 撤销所有订单. 66 | self.am.update_bar(bar) # 把最新的K线放进时间序列里面. 67 | # 下面可以计算基数指标等等.... 68 | # 以及下单的事情. 69 | 70 | if not self.am.inited: 71 | return 72 | 73 | # [0,1,2,3,4,5,6] 74 | last_close_price = self.am.close_array[-2] # 上一根K线 75 | current_close_price = bar.close_price # self.am.close_array[-1] # 当前的收盘价 76 | 77 | # 如果四小时价格下跌5%就买入. 78 | if (last_close_price - current_close_price)/last_close_price >= self.price_change_pct: 79 | price = bar.close_price * 1.001 80 | self.buy(Decimal(price), Decimal(self.fixed_trade_money/price)) 81 | 82 | self.put_event() 83 | 84 | def on_order(self, order: OrderData): 85 | """ 86 | 订单的回调方法: 订单状态更新的时候,会调用这个方法。 87 | """ 88 | self.put_event() 89 | 90 | def on_trade(self, trade: TradeData): 91 | """ 92 | """ 93 | self.put_event() # 更新UI界面方法。 94 | 95 | 96 | def on_stop_order(self, stop_order: StopOrder): 97 | """ 98 | 这个是一个停止单的方法,用来监听你止损单的方法。 99 | """ 100 | pass 101 | 102 | -------------------------------------------------------------------------------- /class_12/strategies/fixed_trade_time_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.trader.utility import BarGenerator, ArrayManager 8 | from howtrader.trader.constant import Interval 9 | from howtrader.app.cta_strategy.engine import CtaEngine 10 | from decimal import Decimal 11 | 12 | 13 | class FixedTradeTimeStrategy(CtaTemplate): 14 | """ 15 | 基于价格的定投 16 | """ 17 | 18 | author = "51bitquant" 19 | 20 | fixed_trade_money = 1000 21 | 22 | parameters = ["fixed_trade_money"] 23 | 24 | 25 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 26 | """""" 27 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 28 | self.bg_1hour = BarGenerator(self.on_bar, 1, self.on_1hour_bar, Interval.HOUR) 29 | self.am = ArrayManager(size=100) # 时间序列,类似我们用的pandas, 值保留最近的N个K线的数据. 30 | 31 | def on_init(self): 32 | """ 33 | Callback when strategy is inited. 34 | """ 35 | self.write_log("策略初始化") 36 | self.load_bar(1) # 具体加载多少天的数据, 1表示1天的数据,如果是2表示过去2天的数据 37 | 38 | def on_start(self): 39 | """ 40 | Callback when strategy is started. 41 | """ 42 | self.write_log(f"我的策略启动") 43 | self.put_event() 44 | 45 | def on_stop(self): 46 | """ 47 | Callback when strategy is stopped. 48 | """ 49 | self.write_log("策略停止") 50 | self.put_event() 51 | 52 | def on_tick(self, tick: TickData): 53 | self.bg_1hour.update_tick(tick) 54 | 55 | def on_bar(self, bar: BarData): 56 | """ 57 | Callback of new bar data update. 58 | """ 59 | self.bg_1hour.update_bar(bar) # 合成1小时的数据. 60 | self.put_event() 61 | 62 | def on_1hour_bar(self, bar: BarData): 63 | """ 64 | 1小时的K线数据. 65 | """ 66 | self.cancel_all() # 取消订单. 67 | self.am.update_bar(bar) # 把最新的K线放进时间序列里面. 68 | if not self.am.inited: # True 69 | return 70 | 71 | """ 72 | 定投逻辑: 周四下午三点定投, 周五下午四点定投 73 | """ 74 | # 2000 * 54 # 10万美金, 75 | if bar.datetime.isoweekday() == 5 and bar.datetime.hour == 16: 76 | price = bar.close_price * 1.001 77 | self.buy(Decimal(price), Decimal(self.fixed_trade_money/price)) 78 | 79 | if bar.datetime.isoweekday() == 4 and bar.datetime.hour == 15: 80 | price = bar.close_price * 1.001 81 | self.buy(Decimal(price), Decimal(self.fixed_trade_money / price)) 82 | 83 | 84 | # 下面可以计算基数指标等等.... 85 | # 以及下单的事情. 86 | 87 | self.put_event() 88 | 89 | def on_order(self, order: OrderData): 90 | """ 91 | 订单的回调方法: 订单状态更新的时候,会调用这个方法。 92 | """ 93 | self.put_event() 94 | 95 | def on_trade(self, trade: TradeData): 96 | """ 97 | """ 98 | self.put_event() # 更新UI界面方法。 99 | 100 | def on_stop_order(self, stop_order: StopOrder): 101 | """ 102 | 这个是一个停止单的方法,用来监听你止损单的方法。 103 | """ 104 | pass 105 | 106 | -------------------------------------------------------------------------------- /class_13/class_13.md: -------------------------------------------------------------------------------- 1 | # 第十三课: Window服务器的部署 2 | 实盘策略可以部署可以在Linux和Window, 看你的需求。但是Linux服务器相对性能和稳定性来说会更好。 3 | 4 | ## Linux的服务器部署 5 | Linux的服务器部署可以看网易云课堂这个视频 6 | [Linux服务器视频链接](https://study.163.com/course/courseLearn.htm?courseId=1209509824&share=2&shareId=480000001919830#/learn/video?lessonId=1281123249&courseId=1209509824) 7 | 8 | 9 | ## 服务器选择 10 | 11 | [优惠券: 12 | https://www.ucloud.cn/site/global.html?invitation_code=C1x2EA81CD79B8C#dongjing](https://www.ucloud.cn/site/global.html?invitation_code=C1x2EA81CD79B8C#dongjing) 13 | 14 | 如果付款的时候,没有找到优惠券可以联系他们客服,找他们要优惠券。 15 | 16 | ## 连接服务器方式 17 | 1. Microsoft Remote Desktop 18 | 2. Mac需要是美国地区的,但是你可以修改自己的appstore的地区,或者注册一个美国地区的,具体自己百度。 19 | 20 | ## 设置系统语言 21 | 22 | 1. 搜索languages,然后安装Chinese 23 | 2. 然后重启 24 | 25 | ## 安装软件 26 | 1. IE浏览器需要设置下下载安全选项. 27 | 2. chrome 浏览器 28 | 3. 安装anaconda和pycharm 29 | 4. 安装git工具 30 | 31 | ## 安装howtrader 32 | > pip install git+https://github.com/51bitquant/howtrader.git 33 | 34 | ## 上传文件到服务器 35 | 1. windowd的可以自己百度学习下,如何把文件上传到window服务器 36 | 2. linux服务器的可以看下网易云课堂的实盘部署 37 | 3. 使用Microsoft Remote Desktop文件同步的功能 38 | 39 | 40 | -------------------------------------------------------------------------------- /class_14/class_14.md: -------------------------------------------------------------------------------- 1 | # 第十四课: Window服务器的安装Talib库 2 | 3 | 本课程对talib不做要求,如果你学习talib的技术指标库,你可以学习 4 | 网易云课堂的视频:[Python数字货币量化交易进阶课程](https://study.163.com/course/courseMain.htm?courseId=1209509824) 5 | 6 | 本视频只是看到有太多的人安装不成功talib,所以来教大家安装下。 7 | 8 | ## 要求 9 | 1. 学习13课的Window环境下搭建Python的开发环境,然后能来Python代码。 10 | 11 | 12 | ## 安装过程 13 | 14 | 1. github搜索ta-lib: https://github.com/mrjbq7/ta-lib 15 | 16 | 2. 下载地址: https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib 17 | 18 | 3. 搜索ta-lib找到ta-lib的包: 19 | TA_Lib‑0.4.24‑cp39‑cp39‑win_amd64.whl 20 | 21 | 记得下载自己对应的python版本,cp39就是python3.9版本, amd64就是64位的意思。 22 | 23 | 4. 通过pip命令安装 24 | > pip install TA_Lib‑0.4.24‑cp39‑cp39‑win_amd64.whl 25 | -------------------------------------------------------------------------------- /class_15/class_15.md: -------------------------------------------------------------------------------- 1 | # 第十五课: 为什么合约(期货)危险?现货和合约(期货)的交易你要了解的东西 2 | 3 | 4 | ## 现货和合约 5 | 1. 现货就是简单的买卖。但是首先你得有(买), 然后才能卖(无) 6 | 7 | 2. 现货有什么局限性?(1) 8 | 只能买,然后在卖,只能在上涨过程中赚钱,下跌过程是不能赚钱的(类似A股), 9 | 要想赚钱,只能等牛市, (2) 10 | 你有多少钱,就只能买多少钱的东西,没法扩大你的资金利用率。 11 | 12 | 3. 那么合约 Contract,或者叫期货Future, 解决了从无到有,和从有到无的过程。 13 | 开仓和平仓。 14 | 15 | 4. 合约自带杠杆, 最高125倍杠杆。那么就容易导致梭哈亏光, 16 | 也可能暴富。但是这一刻可能不会到来。 17 | 18 | 5. 有杠杆就是涉及到借贷,或者说信用问题。那么就是保证金问题。你最多亏的就是你的保证金(破产,爆仓价格也叫破产价), 19 | 如果你把全部本金作为保证金,那么你最多就是亏光你的所有本金。看看中行的原油宝事件,亏光本金还倒贴欠债,就是很不合理啊。想想下!!! 20 | 21 | 22 | ## 合约需要保证金保证金交易 23 | 24 | 1. 合约介绍参考文档: 25 | [https://www.binancezh.pro/cn/support/faq/c-4?navId=4](https://www.binancezh.pro/cn/support/faq/c-4?navId=4) 26 | 27 | 2. 强平文档: [https://www.binancezh.pro/cn/support/faq/360033525271](https://www.binancezh.pro/cn/support/faq/360033525271) 28 | 29 | 30 | 31 | 3. 标记价格和市场价格的区别。 32 | 1. 标记价格是多个市场的均价。 33 | 2. 市场价格是当前交易的市场的价格。 34 | 35 | 4. 维持保证金: [https://www.binancezh.pro/cn/support/faq/360033162192](https://www.binancezh.pro/cn/support/faq/360033162192) 36 | 37 | 5. 资金费率: 38 | 永续合约为了让它的价格回归现货,跟现货价格一致。所以设置一个收费制度。就是价格高于现货,多头给这个手续费。价格低于这个现货,空头给这个钱。每8小时计算一次。 39 | 想象为啥? 40 | 41 | 6. 爆仓清算 42 | **用户爆仓时,保险金基金会收取一定比例的清算金,交易历史记录中标记为“爆仓清算”。但建议用户严格控制风险,避免爆仓。 43 | 用户的强平价格并不会因此发生变化。** 44 | 45 | 1. 清算费率: BTC 0.5%, ETH等75倍的0.75%, 最高50倍的交易对1% 46 | 47 | 6. 如何计算爆仓价格: 48 | 49 | ![img](./imgs/order1.png) 50 | 51 | 1. 开仓价 22988.00, 爆仓价格: 23079.59, 仓位 52 | 2. 按理说你是要亏掉0.18的时候,你才爆仓,但是真实是你什么时候爆仓呢? 53 | 3. 什么时候亏掉0.18呢? (x-22988) * 0.001 = 0.18 => 0.18/0.001+22988 = 54 | 23168 55 | 4. 但是真实是23079.59 就爆仓了。为啥呢? => 它有个千分之5的清算手续费。 0.18 56 | \- 22988.00 * 0.001 * 0.005 = 0.06506, 所以你的爆仓价格是: 57 | 0.06506/0.001+22988 = 23053, 这个是市场价格 58 | 59 | 通过维持保证金来计算: 初始保证金 - 维持保证金 = 你的亏损的金额. 60 | 维持保证金和初始保证金。 125倍的初始保证金是1/125 = 0.8% 61 | 62 | 1. (x-22988) * 0.001 = 22988 * 0.001* 0.8% - 22988 * 0.001 * 0.004 63 | 2. x = (22988 * 0.8% * 0.001 - 22988 * 0.001 * 0.004)/0.001 + 64 | 22988 65 | 3. 维持保证金是0.4% 66 | ## 那么我们交易用多少倍杠杆呢? 67 | 68 | 1. 要在爆仓价格前设置止损。这样你的钱能少亏很多。少亏你开仓仓位价值的0.5%。 69 | 2. 如果你做到上面第一点。那么你开仓倍数没有太大的关系。 70 | 3. 计算看看 71 | 72 | 1. 假设你有10,000元。一倍杠杆,也就是不加杠杆,只用自己的本金。全仓梭哈 73 | 2. 我用10倍杠杆, 买入10000元的资产,这时保证金是1000元。 74 | 3. 我100倍杠杆, 买入10000元的资产,保证金是100元。 75 | 76 | 以上三种情况,它的爆仓价是多少呢? 77 | 1. 一倍杠杆的,你的10000块钱亏光就会爆仓。涨到价格翻倍就会爆仓。 78 | 2. 如果你全仓跟1倍杠杆是一样的,但是如果逐仓就不一样 79 | 80 | 3. 1% - 0.4% = 0.6% 81 | 4. 10% - 0.4% = 9.6% 82 | 83 | ## 注意 84 | 1. 合约交易一定要在爆仓前设置止损,能帮助节省很多钱。 100万, -> 5000 1W * 0.005 85 | 10000* 0.0005 =50 86 | 87 | 2. 如果你是一个严格设置止损的交易者,杠杆倍数多少没什么关系。但是杠杆越大,月容易爆仓。 88 | 3. 一般设置倍数在20倍以内就够了。 89 | 4. 合理利用逐仓能保证你的资金安全。 90 | 91 | 92 | ## 币安邀请链接 93 | 94 | 1. 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 95 | 2. 合约邀请码:51bitquant 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /class_15/imgs/order1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/51bitquant/course_codes/f8d41e5b82502d36d15e2381bc93eab27285af98/class_15/imgs/order1.png -------------------------------------------------------------------------------- /class_16/class_16.md: -------------------------------------------------------------------------------- 1 | # 第十六课: 现货网格交易策略讲解和实盘部署(1) 2 | 3 | 讲两种不同方式的实现方式和思路。一种是简单的,直接挂单买卖的,另一个重是有止盈和止损方式的。 4 | 5 | ## 课程要求 6 | 7 | 8 | ### 1. 学过前面的课时 9 | 10 | 1. 不要跳着看,很多知识点我讲了,你不看就不知道。跟着课程来学习,进步最快。 11 | 2. 最好结合课程《Python数字货币量化交易进阶课程》的网格部分来学习。 12 | 3. 课程代码下载地址: https://github.com/51bitquant/course_codes 13 | 4. 框架代码: https://github.com/51bitquant/howtrader 14 | 15 | ### 2. 更新软件 16 | 17 | > pip install git+https://github.com/51bitquant/howtrader.git -U 18 | 19 | 更新到当前的最新版本: 3.0 版本 20 | 21 | 22 | ### 3. 能访问交易所或者购买一个服务器 23 | 24 | 1. 如果需要在服务器上部署,可以通过一下链接购买服务器: 25 | **https://www.ucloud.cn/site/global.html?invitation_code=C1x2EA81CD79B8C#dongjing** 26 | 27 | 价格非常便宜,一年也就是500块钱左右。可以根据个人的需求来选择不同价位的服务器。 28 | 29 | 30 | -------------------------------------------------------------------------------- /class_16/grid.py: -------------------------------------------------------------------------------- 1 | # 网格交易的知识讲解 2 | 3 | """ 4 | 655 5 | 6 | 654 7 | 8 | 653 9 | 10 | 652 11 | 12 | 651 13 | -------------------- 650 14 | 650 15 | 16 | 649 17 | 18 | 648 19 | 20 | 647 21 | 22 | 646 23 | 24 | 645 25 | 26 | 27 | """ 28 | 29 | import numpy as np 30 | 31 | if __name__ == '__main__': 32 | 33 | # prices = np.arange(660, 500, -3) 34 | # print(prices) 35 | # 36 | # print(len(prices), prices.mean()) 37 | 38 | price1 = np.arange(660, 620, -3) 39 | 40 | price2 = np.arange(620, 580, -5) 41 | 42 | price3 = np.arange(580,500, -10) 43 | 44 | prices = list(price1) + list(price2) + list(price3) 45 | 46 | p = np.array(prices) 47 | print(len(p), p.mean()) -------------------------------------------------------------------------------- /class_16/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_16/strategies/spot_grid_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | 8 | from howtrader.app.cta_strategy.engine import CtaEngine 9 | from howtrader.trader.event import EVENT_TIMER, EVENT_ACCOUNT 10 | from howtrader.event import Event 11 | from howtrader.trader.object import Status 12 | from typing import Optional 13 | from decimal import Decimal 14 | 15 | 16 | TIMER_WAITING_INTERVAL = 30 17 | 18 | class SpotGridStrategy(CtaTemplate): 19 | """ 20 | 币安现货网格交易策略 21 | 免责声明: 本策略仅供测试参考,本人不负有任何责任。使用前请熟悉代码。测试其中的bugs, 请清楚里面的功能后使用。 22 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 23 | 合约邀请码:51bitquant 24 | """ 25 | author = "51bitquant" 26 | 27 | grid_step = 2.0 # 网格间隙. 价格*手续费*5 0.001 * 4 28 | trading_size = 0.5 # 每次下单的头寸. # 数量乘以价格>= 10USDT 29 | max_size = 100.0 # 最大单边的数量. 30 | 31 | parameters = ["grid_step", "trading_size", "max_size"] 32 | 33 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 34 | """""" 35 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 36 | 37 | self.buy_orders = [] # 所有的buy orders. 38 | self.sell_orders = [] # 所有的sell orders. 39 | 40 | self.timer_interval = 0 41 | 42 | self.last_filled_order: Optional[OrderData] = None # 联合类型, 或者叫可选类型,二选一那种. 43 | self.tick: Optional[TickData] = None # 44 | 45 | print("交易的交易对:", vt_symbol) 46 | 47 | # 订阅的资产信息. BINANCE_SPOT.资产名 48 | self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE_SPOT.USDT", self.process_account_event) 49 | self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE_SPOT.BNB", self.process_account_event) 50 | self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE_SPOT.ETH", self.process_account_event) 51 | 52 | def on_init(self): 53 | """ 54 | Callback when strategy is inited. 55 | """ 56 | self.write_log("策略初始化") 57 | 58 | def on_start(self): 59 | """ 60 | Callback when strategy is started. 61 | """ 62 | self.write_log("策略启动") 63 | 64 | # 定时器. 65 | self.cta_engine.event_engine.register(EVENT_TIMER, self.process_timer_event) 66 | 67 | def on_stop(self): 68 | """ 69 | Callback when strategy is stopped. 70 | """ 71 | self.write_log("策略停止") 72 | self.cta_engine.event_engine.unregister(EVENT_TIMER, self.process_timer_event) 73 | 74 | def process_timer_event(self, event: Event): 75 | 76 | if self.tick is None: 77 | return 78 | 79 | self.timer_interval += 1 80 | if self.timer_interval >= TIMER_WAITING_INTERVAL: 81 | 82 | self.timer_interval = 0 83 | # 如果你想比较高频可以把定时器给关了。 84 | 85 | if len(self.buy_orders) == 0 and len(self.sell_orders) == 0: 86 | 87 | if abs(self.pos) > self.max_size * self.trading_size: 88 | # 限制下单的数量. 89 | return 90 | 91 | buy_price = self.tick.bid_price_1 - self.grid_step / 2 92 | sell_price = self.tick.ask_price_1 + self.grid_step / 2 93 | 94 | buy_orders_ids = self.buy(Decimal(buy_price), Decimal(str(self.trading_size))) # 列表. 95 | sell_orders_ids = self.sell(Decimal(sell_price), Decimal(str(self.trading_size))) 96 | 97 | self.buy_orders.extend(buy_orders_ids) 98 | self.sell_orders.extend(sell_orders_ids) 99 | 100 | print(f"开启网格交易,双边下单:BUY: {buy_orders_ids}@{buy_price}, SELL: {sell_orders_ids}@{sell_price}") 101 | 102 | elif len(self.buy_orders) == 0 or len(self.sell_orders) == 0: 103 | # 网格两边的数量不对等. 104 | self.cancel_all() 105 | 106 | def process_account_event(self, event:Event): 107 | print("收到的账户资金的信息:", event.data) 108 | 109 | def on_tick(self, tick: TickData): 110 | """ 111 | Callback of new tick data update. 112 | """ 113 | self.tick = tick 114 | 115 | def on_bar(self, bar: BarData): 116 | """ 117 | Callback of new bar data update. 118 | """ 119 | pass 120 | 121 | def on_order(self, order: OrderData): 122 | """ 123 | Callback of new order data update. 124 | """ 125 | 126 | if order.status == Status.ALLTRADED: 127 | 128 | if order.vt_orderid in self.buy_orders: 129 | self.buy_orders.remove(order.vt_orderid) 130 | 131 | if order.vt_orderid in self.sell_orders: 132 | self.sell_orders.remove(order.vt_orderid) 133 | 134 | self.cancel_all() 135 | print(f"订单买卖单完全成交, 先撤销所有订单") 136 | 137 | self.last_filled_order = order 138 | 139 | # tick 存在且仓位数量还没有达到设置的最大值. 140 | if self.tick and abs(self.pos) < self.max_size * self.trading_size: 141 | step = self.get_step() 142 | 143 | buy_price = order.price - step * self.grid_step 144 | sell_price = order.price + step * self.grid_step 145 | 146 | buy_price = min(self.tick.bid_price_1 * (1 - 0.0001), buy_price) # marker 147 | sell_price = max(self.tick.ask_price_1 * (1 + 0.0001), sell_price) 148 | 149 | buy_ids = self.buy(Decimal(buy_price), Decimal(str(self.trading_size))) 150 | sell_ids = self.sell(Decimal(sell_price), Decimal(str(self.trading_size))) 151 | 152 | self.buy_orders.extend(buy_ids) 153 | self.sell_orders.extend(sell_ids) 154 | 155 | print( 156 | f"订单完全成交, 分别下双边网格: BUY: {buy_ids}@{buy_price}, SELL: {sell_ids}@{sell_price}") 157 | 158 | if not order.is_active(): 159 | if order.vt_orderid in self.buy_orders: 160 | self.buy_orders.remove(order.vt_orderid) 161 | 162 | elif order.vt_orderid in self.sell_orders: 163 | self.sell_orders.remove(order.vt_orderid) 164 | 165 | self.put_event() 166 | 167 | def on_trade(self, trade: TradeData): 168 | """ 169 | Callback of new trade data update. 170 | """ 171 | self.put_event() 172 | 173 | def on_stop_order(self, stop_order: StopOrder): 174 | """ 175 | Callback of stop order update. 176 | """ 177 | pass 178 | 179 | def get_step(self) -> int: 180 | """ 181 | 这个步长的乘积,随你你设置, 你可以都设置为1 182 | :return: 183 | """ 184 | 185 | return 1 186 | # pos = abs(self.pos) # 187 | # 188 | # if pos < 3 * self.trading_size: 189 | # return 1 190 | # 191 | # elif pos < 5 * self.trading_size: 192 | # return 2 193 | # 194 | # elif pos < 7 * self.trading_size: 195 | # return 3 196 | # 197 | # return 4 198 | 199 | # or you can set it to only one. 200 | # return 1 201 | 202 | -------------------------------------------------------------------------------- /class_17/class_17.md: -------------------------------------------------------------------------------- 1 | # 第十七课: 现货网格交易策略讲解和实盘部署(2) 2 | 3 | 讲两种不同方式的实现方式和思路。一种是简单的,直接挂单买卖的,另一个重是有止盈和止损方式的。 4 | 5 | ## 课程要求 6 | 7 | 8 | ### 1. 学过前面的课时 9 | 10 | 1. 不要跳着看,很多知识点我讲了,你不看就不知道。跟着课程来学习,进步最快。 11 | 2. 最好结合课程《Python数字货币量化交易进阶课程》的网格部分来学习。 12 | 3. 课程代码下载地址: https://github.com/51bitquant/course_codes 13 | 4. 框架代码: https://github.com/51bitquant/howtrader 14 | 15 | ### 2. 更新软件 16 | 17 | > pip install git+https://github.com/51bitquant/howtrader -U 18 | 19 | 更新到当前的最新版本: 2.1.8.0 版本 20 | 21 | 22 | ### 3. 能访问交易所或者购买一个服务器 23 | 24 | 1. 如果需要在服务器上部署,可以通过一下链接购买服务器: 25 | **https://www.ucloud.cn/site/global.html?invitation_code=C1x2EA81CD79B8C#dongjing** 26 | 27 | 价格非常便宜,一年也就是500块钱左右。可以根据个人的需求来选择不同价位的服务器。 28 | 29 | 30 | ### 止盈网格的思路 31 | 32 | 33 | 计算网格的平均价格 34 | 35 | 假设ETH当前的价格是730, 然后你在730, 728, 726,买了三个, 然后价格回到728, 36 | 你卖了一个,然后此时你的仓位数量数2个ETH, 那么你的均价是多少? # (730 + 728)/2 = 37 | 729 38 | 39 | 40 | ```python 41 | from decimal import Decimal 42 | from howtrader.trader.object import OrderData, Status, Direction 43 | 44 | class GridPositionCalculator(object): 45 | """ 46 | 用来计算网格头寸的平均价格 47 | Use for calculating the grid position's average price. 48 | :param grid_step: 网格间隙. 49 | """ 50 | 51 | def __init__(self, grid_step: float = 1.0): 52 | self.pos: Decimal = Decimal("0") 53 | self.avg_price: Decimal = Decimal("0") 54 | self.grid_step: Decimal = Decimal(str(grid_step)) 55 | 56 | def update_position(self, order: OrderData): 57 | if order.status != Status.ALLTRADED: 58 | return 59 | 60 | previous_pos = self.pos 61 | previous_avg = self.avg_price 62 | 63 | if order.direction == Direction.LONG: 64 | self.pos += order.volume 65 | 66 | if self.pos == Decimal("0"): 67 | self.avg_price = Decimal("0") 68 | else: 69 | 70 | if previous_pos == Decimal("0"): 71 | self.avg_price = order.price 72 | 73 | elif previous_pos > 0: 74 | self.avg_price = (previous_pos * previous_avg + order.volume * order.price) / abs(self.pos) 75 | 76 | elif previous_pos < 0 and self.pos < 0: 77 | self.avg_price = (previous_avg * abs(self.pos) - ( 78 | order.price - previous_avg) * order.volume - order.volume * self.grid_step) / abs( 79 | self.pos) 80 | 81 | elif previous_pos < 0 < self.pos: 82 | self.avg_price = order.price 83 | 84 | elif order.direction == Direction.SHORT: 85 | self.pos -= order.volume 86 | 87 | if self.pos == Decimal("0"): 88 | self.avg_price = Decimal("0") 89 | else: 90 | 91 | if previous_pos == Decimal("0"): 92 | self.avg_price = order.price 93 | 94 | elif previous_pos < 0: 95 | self.avg_price = (abs(previous_pos) * previous_avg + order.volume * order.price) / abs(self.pos) 96 | 97 | elif previous_pos > 0 and self.pos > 0: 98 | self.avg_price = (previous_avg * self.pos - ( 99 | order.price - previous_avg) * order.volume + order.volume * self.grid_step) / abs( 100 | self.pos) 101 | 102 | elif previous_pos > 0 > self.pos: 103 | self.avg_price = order.price 104 | 105 | ``` 106 | -------------------------------------------------------------------------------- /class_17/grid.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import numpy as np 4 | 5 | if __name__ == '__main__': 6 | pass 7 | 8 | # 732 9 | # 10 | # 730 11 | # 12 | # 728 13 | # 14 | # 726 15 | 16 | 17 | # prices = [730, 728, 726, 724, 722] # 5个ETH 727 729 -------------------------------------------------------------------------------- /class_17/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_18/class_18.md: -------------------------------------------------------------------------------- 1 | # 第十八课: 简单的做市高频网格策略 2 | 3 | ## 策略原理 4 | 5 | 在当前盘口上分别挂买卖单,等待成交。 如果多头成交,就把之前的空头单子撤销,然后在成交价上方挂一个网格的价差止盈, 同时挂一个网格价差买入。 6 | 如果价格继续向下,那么就会成交多头的单子,同样在均价上方挂止一个网格价差止盈位,在下方继续挂买单。 7 | 如果成功止盈,就重新开始。如果多头或单子单边成交达到5个,就会在最后一个成交价下方设置一个止损点位(或者在均价下下方设置止损)。 8 | 如果发生止损后,策略就会暂停15分钟。 9 | 10 | 11 | 该策略跟网格策略一样,在震荡行情下会比较有效,但是如果发生单边趋势,或者波动比较大的时候,会容易发生止损。 12 | 适合低波动的币种。盈亏比不高,但是胜率会很高。我们下节课会把它修改成类似现货的止盈止损的网格策略。 -------------------------------------------------------------------------------- /class_18/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_19/class_19.md: -------------------------------------------------------------------------------- 1 | # 第十九课: 合约网格交易策略 2 | 3 | 策略代码在: 4 | https://github.com/51bitquant/howtrader/blob/main/howtrader/app/cta_strategy/strategies/future_profit_grid_strategy.py 5 | 6 | 主要是根据高频网格进行改进, 7 | 在双边进行挂单,会有止盈止损的功能。合约主要是手续费低,可以放大杠杆, 8 | 提高资金利用率。但是要考虑止盈止损的情况。 9 | 如果设置的太小。容易发生止损,设置的过大,容易发生单次亏损过大。建议在震荡行情中使用。 10 | 或者在波动比较小的时候使用。可以通过计算AT的值,在ATR变化平稳或者计算历史波动率比较低时候使用。 11 | 或者可以类似现货那种跑,但是你不要加杠杆。 12 | 13 | 之前测试过,在震荡行情有单天10%的收益,杠杆3倍杠杆,但是如果发生趋势亏损会比较严重。建议使用前测试。如果是趋势行情,谨慎使用。同时记得止损。 14 | 15 | 免责声明: 本策略仅供研究学习,本人不负有任何责任。使用前请熟悉代码。 16 | 测试其中的代码, 请清楚里面的功能后再使用。 17 | 投资有风险入市需谨慎。 18 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 19 | 合约邀请码:51bitquant 20 | 21 | ## 课程要求 22 | 23 | 24 | ### 1. 学过前面的课时 25 | 26 | 1. 不要跳着看,很多知识点我讲了,你不看就不知道。跟着课程来学习,进步最快。 27 | 2. 最好结合课程《Python数字货币量化交易进阶课程》的网格部分来学习。 28 | 3. 课程代码下载地址: https://github.com/51bitquant/course_codes 29 | 4. 框架代码: https://github.com/51bitquant/howtrader 30 | 31 | ### 2. 更新软件 32 | 33 | > pip install git+https://github.com/51bitquant/howtrader.git -U 34 | 35 | 更新到当前的最新版本: 2.1.8.0 版本 36 | 37 | 38 | ### 3. 能访问交易所或者购买一个服务器 39 | 40 | 1. 如果需要在服务器上部署,可以通过一下链接购买服务器: 41 | **https://www.ucloud.cn/site/global.html?invitation_code=C1x2EA81CD79B8C#dongjing** 42 | 43 | 价格非常便宜,一年也就是500块钱左右。可以根据个人的需求来选择不同价位的服务器。 44 | 45 | 46 | -------------------------------------------------------------------------------- /class_19/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_19/strategies/future_neutral_grid_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | 8 | from howtrader.app.cta_strategy.engine import CtaEngine 9 | from howtrader.trader.object import Status 10 | from typing import Optional 11 | from howtrader.trader.utility import BarGenerator 12 | from decimal import Decimal 13 | 14 | 15 | class FutureNeutralGridStrategy(CtaTemplate): 16 | """ 17 | 币安合约中性网格 18 | 策略在震荡行情下表现很好,但是如果发生趋势行情,单次止损会比较大,导致亏损过多。 19 | 20 | 免责声明: 本策略仅供测试参考,本人不负有任何责任。使用前请熟悉代码。测试其中的bugs, 请清楚里面的功能后再使用。 21 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 22 | 合约邀请码:51bitquant 23 | 24 | """ 25 | author = "51bitquant" 26 | 27 | high_price = 0.0 # 执行策略的最高价. 28 | low_price = 0.0 # 执行策略的最低价. 29 | grid_count = 100 # 网格的数量. 30 | order_volume = 0.05 # 每次下单的数量. 31 | max_open_orders = 2 # 一边订单的数量. 32 | 33 | trade_count = 0 34 | 35 | parameters = ["high_price", "low_price", "grid_count", "order_volume", "max_open_orders"] 36 | 37 | variables = ["trade_count"] 38 | 39 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 40 | """""" 41 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 42 | 43 | self.long_orders = [] # 所有的long orders. 44 | self.short_orders = [] # 所有的short orders. 45 | self.tick: Optional[TickData] = None 46 | self.bg = BarGenerator(self.on_bar) 47 | self.step_price = 0 48 | 49 | def on_init(self): 50 | """ 51 | Callback when strategy is inited. 52 | """ 53 | self.write_log("策略初始化") 54 | 55 | def on_start(self): 56 | """ 57 | Callback when strategy is started. 58 | """ 59 | self.write_log("策略启动") 60 | 61 | def on_stop(self): 62 | """ 63 | Callback when strategy is stopped. 64 | """ 65 | self.write_log("策略停止") 66 | 67 | def on_tick(self, tick: TickData): 68 | """ 69 | Callback of new tick data update. 70 | """ 71 | if tick and tick.bid_price_1 > 0: 72 | self.tick = tick 73 | 74 | self.bg.update_tick(tick) 75 | 76 | def on_bar(self, bar: BarData): 77 | """ 78 | Callback of new bar data update. 79 | """ 80 | if not self.tick: 81 | return 82 | 83 | if len(self.long_orders) == 0 or len(self.short_orders) == 0: 84 | 85 | self.step_price = (self.high_price - self.low_price) / self.grid_count 86 | mid_count = round((self.tick.bid_price_1 - self.low_price) / self.step_price) 87 | if len(self.long_orders) == 0: 88 | 89 | for i in range(self.max_open_orders): 90 | price = self.low_price + (mid_count - i - 1) * self.step_price 91 | if price < self.low_price: 92 | break 93 | 94 | orders = self.buy(Decimal(price), Decimal(self.order_volume)) 95 | self.long_orders.extend(orders) 96 | 97 | if len(self.short_orders) == 0: 98 | for i in range(self.max_open_orders): 99 | price = self.low_price + (mid_count + i + 1) * self.step_price 100 | if price > self.high_price: 101 | break 102 | 103 | orders = self.short(Decimal(price), Decimal(self.order_volume)) 104 | self.short_orders.extend(orders) 105 | 106 | if len(self.short_orders + self.long_orders) > 100: 107 | self.cancel_all() 108 | 109 | self.put_event() 110 | 111 | def on_order(self, order: OrderData): 112 | """ 113 | Callback of new order data update. 114 | """ 115 | 116 | if order.vt_orderid not in (self.short_orders + self.long_orders): 117 | return 118 | 119 | if order.status == Status.ALLTRADED: 120 | 121 | if order.vt_orderid in self.long_orders: 122 | self.long_orders.remove(order.vt_orderid) 123 | self.trade_count += 1 124 | 125 | short_price = order.price + Decimal(self.step_price) 126 | 127 | if short_price <= self.high_price: 128 | orders = self.short(short_price, Decimal(self.order_volume)) 129 | self.short_orders.extend(orders) 130 | 131 | if len(self.long_orders) < self.max_open_orders: 132 | count = len(self.long_orders) + 1 133 | long_price = order.price - Decimal(self.step_price) * Decimal(str(count)) 134 | if long_price >= self.low_price: 135 | orders = self.buy(long_price, Decimal(self.order_volume)) 136 | self.long_orders.extend(orders) 137 | 138 | if order.vt_orderid in self.short_orders: 139 | self.short_orders.remove(order.vt_orderid) 140 | self.trade_count += 1 141 | long_price = order.price - Decimal(self.step_price) 142 | if long_price >= self.low_price: 143 | orders = self.buy(long_price, Decimal(self.order_volume)) 144 | self.long_orders.extend(orders) 145 | 146 | if len(self.short_orders) < self.max_open_orders: 147 | count = len(self.long_orders) + 1 148 | short_price = order.price + Decimal(self.step_price) * Decimal(str(count)) 149 | if short_price <= self.high_price: 150 | orders = self.short(short_price, Decimal(self.order_volume)) 151 | self.short_orders.extend(orders) 152 | 153 | if not order.is_active(): 154 | if order.vt_orderid in self.long_orders: 155 | self.long_orders.remove(order.vt_orderid) 156 | 157 | elif order.vt_orderid in self.short_orders: 158 | self.short_orders.remove(order.vt_orderid) 159 | 160 | self.put_event() 161 | 162 | def on_trade(self, trade: TradeData): 163 | """ 164 | Callback of new trade data update. 165 | """ 166 | self.put_event() 167 | 168 | def on_stop_order(self, stop_order: StopOrder): 169 | """ 170 | Callback of stop order update. 171 | """ 172 | pass 173 | -------------------------------------------------------------------------------- /class_20/20.资金费率套利.md: -------------------------------------------------------------------------------- 1 | # 套利介绍 2 | 3 | 1. 我们先来看B站up主的一个视频: 4 | [57万放余额宝,一天利息到底有多少?](https://www.bilibili.com/video/BV1B54y1h7PC) 5 | 6 | 余额宝的每天的利息: 36/570000 = 0.000064 = 0.0063% 年华收益 = 2.3% 的收益。 7 | 8 | 放余额宝的主要是它是货币基金,风险低(基本没有什么风险),收益也稳定,但是收益不高。 9 | 10 | 币圈有没有什么相对比较低的风险投资呢? 11 | 12 | 1. 借贷和套利 13 | 14 | 1.1抵押USDT给交易所,交易所帮你放贷,主要是给那些杠杆交易的用户使用。活期年华收益大概6%左右, 15 | 风险在于USDT贬值或者暴雷的风险。 16 | 17 | 1.2 交易所给你放贷,交易所要赚中间差价了,我们能不能不让交易所赚差价呢 18 | 19 | 20 | ## 资金费率的产生 21 | 22 | [介绍文档](https://www.binancezh.cc/zh-CN/support/faq/360033525031) 23 | 24 | 由于永续合约没有交割日期,为了缩小永续合约价格和现货市场价差(价格回归),引入一个资金费率。 25 | 26 | 资金费率又两部分组成: 利率和溢价, 利率是固定的,每天大概0.03%,每个周期为0.01%. 27 | 溢价就是盘口和现货价格的一个价差。如果资金费率为正,做多的要给你做空的人付费,如果为负,做空的人 28 | 给做多的人付费。交易所不收资金费率的钱,在币安合约交易平台上,资金费用每8小时计算一次,计算时间为UTC时间00:00点、08:00点和16:00点 29 | 30 | ## 套利的原理 31 | 在资金费率为正的时候,买入现货,合约做空(数量相等的币), 然后等待资金费率, 32 | 在金费率变小,价差变小的时候平仓。 33 | 34 | 在资金费率为负的时候,杠杆借币卖出,合约做多(数量相等的币), 然后等待资金费率, 35 | 在金费率变小,价差变小的时候平仓。 36 | 37 | 1. 利润: 价差+资金费率 38 | 39 | 2. 风险: 1. 资金费率没有了,亏了手续费 2. 40 | 平仓的时候价差扩大,赚的资金费率没有覆盖手续费和价差 3. 41 | 对冲不及时,造成仓位敞口风险。 4. 爆仓导致仓位清算 42 | 5. USDT的价格下跌或者暴雷等风险. 43 | 44 | 45 | 为了解决上述问题,我开发了一套资金费率的软件。下次给大家演示下。 46 | 47 | 48 | ##推荐链接 49 | 50 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant, 51 | 合约邀请码:51bitquant 52 | 53 | okex邀请链接: https://www.okexcn.com/join/1847111 54 | okex推荐码: 1847111 55 | 56 | gateio邀请链接: https://www.gateio.pro/ref/1100714 57 | gateio推荐码:1100714 58 | 59 | 火币邀请码: https://www.huobi.sh/topic/invited/?invite_code=asd43 60 | 火币推荐码:asd43 61 | 62 | 63 | -------------------------------------------------------------------------------- /class_21/21.资金费率实战操作.md: -------------------------------------------------------------------------------- 1 | # 资金费率实战 2 | 3 | ## 资金费率的套利大概多少 4 | 1. 资金费率又行情决定的, 看市场氛围, 基本上一个月6%- 15%左右的利润 5 | 6 | ## 软件的功能介绍 7 | 1. 获取资金费率 8 | 2. 盘口价差, 通过价差进行开仓 9 | 3. 防止爆仓的功能, 爆仓或者减仓平仓功能 10 | 4. 策略交易: 现货maker, 现货taker两个策略对冲 11 | 12 | 13 | ##推荐链接 14 | 15 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant, 16 | 合约邀请码:51bitquant 17 | 18 | okex邀请链接: https://www.okexcn.com/join/1847111 19 | okex推荐码: 1847111 20 | 21 | gateio邀请链接: https://www.gateio.pro/ref/1100714 22 | gateio推荐码:1100714 23 | 24 | 火币邀请码: https://www.huobi.sh/topic/invited/?invite_code=asd43 25 | 火币推荐码:asd43 26 | 27 | 28 | -------------------------------------------------------------------------------- /class_22/22.套利软件策略参数说明和使用.md: -------------------------------------------------------------------------------- 1 | # 套利软件策略参数说明和使用 2 | 3 | ## 套利介绍 4 | 1. 资金费率为正的时候,通过做空永续合约,买入现货,然后再0点,8点和16点的时候收资金费率。 5 | 因为做空和做多(买入现货)的数量是相等的,所以你不爆仓的情况下是不会亏钱的。当然你的手续费也要考虑进去。 6 | 7 | 8 | ## 软件的功能 9 | 1. 相比其他软件,手续费率低。现货maker是0手续费(BUSD交易对),只有taker(0.04%)的手续费。 10 | 当然也提供合约是maker(0.02%),现货taker(0.075%), 看你的选择。 11 | 12 | 2. 算法拆单,降低对冲的滑点,容纳大资金对冲 13 | 14 | 3. 防止合约爆仓功能,爆仓会卖出相应的现货,降低单边敞口风险。 15 | 16 | 4. 防止交易所减仓,也会卖出相应的现货 17 | 18 | 19 | ## 策略参数说明 20 | 21 | 1. strategy_name : 策略的名称,随便取一个名字,多个策略的时候,要取不同的名字 22 | 23 | 2. spot_vt_symbol: 现货的交易对,交易对小写.BINANCE, 如: btcusdt.BINANCE, 24 | ethbusd.BINANCE 25 | 3. future_vt_symbol: 合约的交易对, 交易对大写.BINANCE, 如:BTCUSDT.BINANCE, 26 | ETHUSDT.BINANCE, UNIUSDT.BINANCE 27 | 28 | 4. spot_trade_asset: 需要交易现货的币种名称, 如果交易的是BTCUSDT, 29 | 那么就填写BTC,如果是ETHUSDT, 那么就写ETH, 这里要全部大写。 30 | 31 | 5. spot_quote_asset: 计价的资产名称,如果你现货交易的是btcbusd, 32 | 那么这里写BUSD, 如果是btcusdt, 这里填写USDT 33 | 34 | 6. initial_target_pos: 要对冲的最大的目标数量, 35 | 如果你写10,就是要现货要买10个币,如果资金不够,就不会继续下单,满足条件的时候,会尽可能下单到这个数量。 36 | 37 | 7. trade_max_usd_every_time: 38 | 每次最多下单多少个USDT/BUSD的订单,会根据盘口数量来选择下单数量,但是也会参考这个。防止对冲滑点过大。 39 | 40 | 8. slippage_tolerance_pct: 滑点的承受百分比, 41 | 主要是maker成交后,会下taker单,默认0.03, 42 | 就是万三的滑点,就是下一个超价的限价单去尽可能保证对冲的时效性。 43 | 44 | 9. open_spread_ptc: 大于多少的时候下单 0.1,表示的0.1%, 45 | 就是盘口价差大于0.1%的时候才开启套利。 46 | 47 | 10. open_rate_pct: 48 | 资金费率大于多少的时候,才开始套利。要满足价差和资金费率的值才开仓,0.1,表示0.1% 49 | 50 | 11. close_spread_ptc: 要平仓的价差,如果价差缩小到一定程度的时候,会考虑平仓。 51 | 52 | 12. close_rate_pct: 53 | 资金费率小于某个百分比的时候,会平仓。但是要同时满足价差和资金费率。 54 | 55 | 13. close_before_liquidation_pct: 在爆仓价格/现在价格-1 56 | 小于一定百分比的时候回平仓合约, 如果爆仓价格是1000, 57 | 如果你写0.5,那就是在价格大于995的时候会平仓合约。 58 | 59 | 14. timer_interval: 挂单多久不成交会撤单. 60 | 61 | 62 | 如果需要平仓,可以把initial_target_pos设置为零 63 | 64 | ## 软件是收费的,如果有需要联系我 65 | 66 | 1. 微信: bitquant51 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /class_23/23.100万资金永续合约资金费率套利每天赚多少钱?.md: -------------------------------------------------------------------------------- 1 | # 100万资金永续合约资金费率套利每天赚多少钱? 2 | 3 | 4 | ## 我们来看下目前的资金费率行情 5 | 6 | 我们用Fundtrader软件来看看, 资金费率的大小情况.目前普遍很多币都在0.3%左右, 7 | 一天收三次,那就是将近0.9%的收益。当然这个资金费率是波动的。 8 | 9 | 假设你有100W RMB, 10 | 80万买入现货,20万,开四倍杠杆,卖出80万的合约。你合约80W的资金收取永续合约资金费率。 11 | 12 | 80W * 0.9% = 7200块钱。 13 | 14 | ## 我们来看下实盘 15 | 16 | 17 | ## 风险 18 | 如果你的币暴涨了25%那么你可能要爆仓,但是爆仓的时候会被清算,要收取清算费用。 19 | 所以尽可能避免爆仓, 涨得多的币调下保证金。避免爆仓。 20 | 21 | 如果极端情况,发生爆仓,也就是亏算的手续费和清算费用,亏损有限。 22 | 23 | ## Fundtrader 特点 24 | 25 | 1. 优化手续费,现货手续费为零(BUSD交易对) 26 | 2. 算法自动下单,降低滑点,能容纳更大的资金 27 | 3. 可以设置价差开仓,赚资金费率,也赚价差, 28 | 4. 防止爆仓功能,爆仓前平仓 29 | 5. 交易所发生减仓,会自动对冲,防止仓位敞口的出现 30 | 6. 高频实时对冲 31 | 32 | 33 | ### 需要购买软件的,请联系我VX: bitquant51 34 | 35 | 36 | -------------------------------------------------------------------------------- /class_24/24.fundtrade软件是用经常遇到的问题解答.md: -------------------------------------------------------------------------------- 1 | Fundtrader使用过程中遇到的问题有如下: 2 | 3 | ## 问题1: api报错 4 | ``` 5 | C:\Users\Administrator\anaconda3\envs\first\python.exe "C:/Users/Administrator/Desktop/fundtrader 2.2.0/fundtrader/main.py" 6 | request : GET /api/v3/account?timestamp=1617330189743&signature=656bb2d44a3f217ab7331e6d52a270956e1f070d08aea555e29f68174d8bedf4 failed because 400: 7 | headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'X-MBX-APIKEY': 'cndT4vuA6HKzEr8OMGzAabHuMzdoIsfeFIVdfkJ1Jwy2UvQ3157ZB98OqRhAofgF'} 8 | params: {} 9 | data: {} 10 | response:{"code":-1022,"msg":"Signature for this request is not valid."} 11 | request : GET /api/v3/openOrders?timestamp=1617330189758&signature=c60f4b68f49524eaf1da71eb33e81d7917d98bd25534b0924522f7ddc9ccb952 failed because 400: 12 | headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'X-MBX-APIKEY': 'cndT4vuA6HKzEr8OMGzAabHuMzdoIsfeFIVdfkJ1Jwy2UvQ3157ZB98OqRhAofgF'} 13 | params: {} 14 | data: {} 15 | response:{"code":-1022,"msg":"Signature for this request is not valid."} 16 | request : GET /api/v3/account?timestamp=1617330198899&signature=15b9ad7651fa59162b12394f34bb8c07beeb9170da693669b5ee927c5a501897 failed because 400: 17 | headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'X-MBX-APIKEY': 'cndT4vuA6HKzEr8OMGzAabHuMzdoIsfeFIVdfkJ1Jwy2UvQ3157ZB98OqRhAofgF'} 18 | params: {} 19 | data: {} 20 | response:{"code":-1022,"msg":"Signature for this request is not valid."} 21 | 22 | ``` 23 | ### 解决方法 24 | 检查自己的apikey和secret是否有这样的问题 25 | 26 | 1. 检查自己是否复制错误、 是否含有空格、是否复制了多次, 可以打开项目下的howtrader文件下的connect_binance.json 和 connect_binances.json, 查看两个文件中的apikey和secret是否跟交易所的一致。 27 | 28 | 2. 自己生成apikey的时候是否有勾选上合约和现货 29 | ![api要求.jpg](https://upload-images.jianshu.io/upload_images/814550-a21eee8efce8cdac.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 30 | 31 | 32 | ## 问题2: 下单失败 33 | 1. 仓位模式不匹配,需要在app或者网页那边设置仓位模式为单向 34 | 2. 检查你的账户资金是否够。如果报错insuficient margin就是保证金不足 35 | 36 | ## 问题3. 没有开仓 37 | 1. 检查你设置的open_spread_pct和open_rate_pct是否过高,他们要跟当前的价差和资金费率相比,只有当前的价差和资金费率都比你设置的open_spread_pct和open_rate_pct高他们才会开仓,否则不满足开仓条件。 38 | 39 | 2. 检查你设置的trade_max_usd_every_time这个参数,他们的值是过小。这个是的意思是每次你下单的单子的最大值,如果你设置的这个值为20,那么你每次单子的数量就是20/你交易的品种价格,假设你交易的是UNI这个交易对,他的当前价值是30USDT, 那么你下单的数量为20/30 = 0.66个,但是交易所要求合约的交易的数量为至少1个UNI, 那么你设置的这个20就不能下单。一般这个最小值要太小的话,要根据交易对来看,但是交易设置这个值至少为50以上。 40 | 41 | ## 问题4: 如何防止爆仓 42 | 43 | 我建议一般现货放75-80%的资金,合约放20-25%的资金。这样你的资金利用率为80%左右,这样最大限度保障你的收益。为了让合约现货的资金完全对冲,合约的资金乘以杠杆倍数要大于现货的资金。如果你的合约是20%资金,那么要设置为5倍杠杆。我建议设置全仓,然后五倍杠杆。为了降低爆仓的风险,我建议平均分配下资金跑2-3个币种,这样他们只要不是同时上涨,那么爆仓的风险就很小。每天定时看一下,通过app查看下他们是否暴涨,然后如果涨得多了,可以适当调仓下,把爆仓的价格提高。如果你同时跑三个交易对,可以把close_before_liquidation_pct设置为1.5-2.5之间,这样就是他们快到爆仓价前的1.5%-2.5%会平仓。 44 | 45 | # 问题4: 如何进行仓位管理 46 | 程序中有initial_target_pos, 这个值是你开仓的最大值。如果你设置为300, 那么它就是你最多买这个币为三百个,但是如果你已经完成了交易,你把策略停止,然后设置为200,那么它就会慢慢给你减仓。如果单个币上涨比较多,你可以适当的减仓,然后把多余的保证金划转到合约。 47 | 48 | 另外如果跑的某个交易对,他们的资金费率下降的比较多,你可以选择平仓,这时只需把initial_target_pos设置为零, 然后重新启动策略,然后策略就会给你慢慢平仓。同时可以跑其他交易对。 49 | 50 | 另外单个客户端,跑的交易对不要超过四个,如果超过四个,程序会效率不高,可能对冲不那么及时。如果你实在想跑多个比较多的交易对,建议你多开几个客户端。每个客户端跑不同的交易对。 51 | 52 | ## 问题5: 控制台报错,下不了单 53 | 错误提示 54 | ``` 55 | {'code': -1021, 'msg': "Timestamp for this request was 1000ms ahead of the server's time."} 400 56 | 57 | ``` 58 | 服务器的时钟不准确,导致时间戳问题。需要调整下自己的服务器的时间戳,这个百度下就搞定了。 59 | 60 | ## 问题6:程序中看不懂英文 61 | 可以看项目下的 ``策略参数说明.md`` 文件 62 | 63 | ## 问题7: 如何调整杠杆和查看收益 64 | 在app设置,最好设置全仓和5倍杠杆,然后收益在合约那边,点击过滤选择资金费率就可以了。 65 | 66 | ## 问题8:服务器选择 67 | 最好选择东京地区的服务器,最低配置要求2核4G的Window服务器。 68 | ![i服务器选择.png](https://upload-images.jianshu.io/upload_images/814550-846064460a2629b0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 69 | 70 | ## 问题9: 下单不成功 71 | 1. 保证金不足的问题,需要划转下。 72 | 73 | ![下单不成功保证金不足.png](https://upload-images.jianshu.io/upload_images/814550-5ede85571948ab96.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 74 | 75 | ## 问题10 :如何想在价差小的时候平仓 76 | 程序自动平仓是在当前市场行情价差和资金费率分别小于你设置的close_spread_pct和close_rate_pct的时候,才会自动平仓。如果想平仓可以设置为一个小的数。但是如果不设置的也可以的,使用默认值为0也可以的。因为资金费率一般都是大于零的。蚊子腿也是肉。 77 | 78 | ## 问题11: 如何兑换busd 79 | 可以通过 BUSD/USDT交易对兑换 80 | 81 | ## 问题12: 爆仓和交易所减仓会卖出现货吗? 82 | 会的。爆仓和交易所减仓是两个大的风险。爆仓的话,可能会亏损一些价差、滑点,已经清算的费用。减仓是交易所在合约没有对手盘了,需要拿获利较多的来进行平仓。如果你在app那边看点与四个杠的竖线亮起,那么发生大波动的时候,你的仓位可能在减仓序列,但是一般不会减仓的,除非波动比较大。 83 | 84 | 如果发生爆仓或者减仓,如果你服务器没有发生问题,交易所没有宕机,那么程序会给你卖出相应的现货的。 85 | 86 | ## 问题12:爆仓加如何算的 87 | 爆仓价是从交易所的获取的。 88 | 89 | ## 问题13: 找不到库 90 | 环境没有安装对,需要conda 激活你环境,然后进行处理。或者你运行的环境没有安装howtrader. 91 | 92 | ![找不到库.png](https://upload-images.jianshu.io/upload_images/814550-eed2cc08aa7d8900.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 93 | 94 | ## 问题14: 没有收到行情或者资产信息 95 | 需要检查下你的websocket是否连接成功。 96 | 97 | ## 如何查看你的订单成交信息 98 | 可以在软件中查看,或者在app中查看。 99 | 100 | ![查看订单.jpeg](https://upload-images.jianshu.io/upload_images/814550-4950f81a747c859f.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 101 | 102 | ## 问题15:现货和合约的数量不完全相等 103 | 这个问题很正常。币安要求现货下单的价值是10USDT以上,而且合约要求的每个币种最小的下单数量也是有要求的。只要他们相差的数量小于合约的最小下单要求或者他们相差的价值小于11USDT, 那么程序就是认为他们相当的。而且他们相差的数量不会对我们的盈亏没有太大的影响。 104 | 105 | ## 问题15: 找不到蓝牙的api 106 | 107 | ![pyqt蓝牙api不支持.png](https://upload-images.jianshu.io/upload_images/814550-bf9d91f522de9434.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 108 | 109 | 这个问题主要是pyqt5的蓝牙api支持window 10一下的系统造成的。 110 | 解决问题方法,要找到你项目项目下的mytrader的文件夹, 类似下面的截图。 111 | 112 | 首先找到你的项目下面的这个mytrader解析器环境的路径 113 | ![解析器环境.png](https://upload-images.jianshu.io/upload_images/814550-5a172d29247cd1a6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 114 | 115 | 然后开始在对应目录下找到mytrader中pyqt5的蓝牙的库,然后把它的名字修为:Qt5Bluetooth.dll1, 或者删除也可以的。 116 | ![修改蓝牙库的名字.png](https://upload-images.jianshu.io/upload_images/814550-eed1f9f256f9572e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 117 | 118 | 119 | ## 问题16: backports.zoneinfo.ZoneInfo object has no attribute 120 | 这是因为tzlocal这个库更新到3.0以后会出现问题,它3.0版本对代码进行了重构了,所以需要安装2.1版本就可以了。 121 | > pip install tzlocal==2.1 -------------------------------------------------------------------------------- /class_25/25.马丁策略.md: -------------------------------------------------------------------------------- 1 | # 25课马丁策略 2 | 3 | 1. 马丁策略就是通过不断加仓的方式,来降低入场价格。 4 | 2. 假设第一次开仓100块钱,那么第二次开仓200, 第三次400, 第四次800...... 5 | 3. 有盈亏后,会平仓,然后重新开始 6 | 4. 如果加仓多次后,需要加仓的资金越来越多 7 | 5. 如果资金不过,如果是合约的话,容易导致爆仓. 8 | 6. 逆势加仓,因此,如果市场出现单边市的话非常考验系统的抗风险能力。 9 | 10 | 所以尽可能找到强势币或者波动比较大的币种,进行入场 11 | -------------------------------------------------------------------------------- /class_25/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_25/strategies/martingle_future_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | 8 | from howtrader.app.cta_strategy.engine import CtaEngine 9 | from howtrader.event import Event 10 | from howtrader.trader.object import Status, Direction, Interval, ContractData, AccountData 11 | from howtrader.trader.utility import BarGenerator, ArrayManager 12 | from typing import Optional 13 | from decimal import Decimal 14 | 15 | 16 | class MartingleFutureStrategy(CtaTemplate): 17 | """ 18 | 1. 马丁策略. 19 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 20 | 币安合约邀请码:51bitquant 21 | https://github.com/51bitquant/course_codes 22 | """ 23 | author = "51bitquant" 24 | 25 | # 策略的核心参数. 26 | boll_window = 30 27 | boll_dev = 2.2 28 | 29 | increase_pos_when_dump_pct = 0.04 # 回撤多少加仓 30 | exit_profit_pct = 0.02 # 出场平仓百分比 2% 31 | initial_trading_value = 1000 # 首次开仓价值 1000USDT. 32 | trading_value_multiplier = 1.3 # 加仓的比例. 1000 1300 1300 * 1.3 33 | max_increase_pos_times = 10.0 # 最大的加仓次数 34 | trading_fee = 0.00075 35 | 36 | # 变量 37 | avg_price = 0.0 # 当前持仓的平均价格. 38 | last_entry_price = 0.0 # 上一次入场的价格. 39 | current_pos = 0.0 # 当前的持仓的数量. 40 | current_increase_pos_times = 0 # 当前的加仓的次数. 41 | 42 | # 统计总的利润. 43 | total_profit = 0 44 | 45 | parameters = ["boll_window", "boll_dev", "increase_pos_when_dump_pct", "exit_profit_pct", "initial_trading_value", 46 | "trading_value_multiplier", "max_increase_pos_times", "trading_fee"] 47 | 48 | variables = ["avg_price", "last_entry_price", "current_pos", "current_increase_pos_times", "total_profit"] 49 | 50 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 51 | """""" 52 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 53 | 54 | self.last_filled_order: Optional[OrderData, None] = None 55 | self.tick: Optional[TickData, None] = None 56 | self.contract: Optional[ContractData, None] = None 57 | self.account: Optional[AccountData, None] = None 58 | 59 | self.bg = BarGenerator(self.on_bar, 15, self.on_15min_bar, Interval.MINUTE) # 15分钟的数据. 60 | self.am = ArrayManager(60) # 默认是100,设置60 61 | 62 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 63 | # 现货的资产订阅 64 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 65 | # # 合约的资产订阅 66 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCES.USDT", self.process_account_event) 67 | 68 | self.buy_orders = [] # 买单id列表。 69 | self.sell_orders = [] # 卖单id列表。 70 | self.min_notional = 11 # 最小的交易金额. 71 | 72 | def on_init(self): 73 | """ 74 | Callback when strategy is inited. 75 | """ 76 | self.write_log("策略初始化") 77 | self.load_bar(2) # 加载两天的数据. 78 | 79 | def on_start(self): 80 | """ 81 | Callback when strategy is started. 82 | """ 83 | self.write_log("策略启动") 84 | 85 | def on_stop(self): 86 | """ 87 | Callback when strategy is stopped. 88 | """ 89 | self.write_log("策略停止") 90 | 91 | def process_account_event(self, event: Event): 92 | self.account: AccountData = event.data 93 | # if self.account: 94 | # print( 95 | # f"self.account available: {self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 96 | 97 | def on_tick(self, tick: TickData): 98 | """ 99 | Callback of new tick data update. 100 | """ 101 | if tick and tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 102 | self.tick = tick 103 | self.bg.update_tick(tick) 104 | 105 | def on_bar(self, bar: BarData): 106 | """ 107 | Callback of new bar data update. 108 | """ 109 | 110 | if self.current_pos * bar.close_price >= self.min_notional: 111 | 112 | if len(self.sell_orders) <= 0 < self.avg_price: 113 | # 有利润平仓的时候 114 | profit_percent = bar.close_price / self.avg_price - 1 115 | if profit_percent >= self.exit_profit_pct: 116 | self.cancel_all() 117 | orderids = self.short(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 118 | self.sell_orders.extend(orderids) 119 | 120 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 121 | dump_percent = self.last_entry_price / bar.close_price - 1 122 | if len( 123 | self.buy_orders) <= 0 and self.current_increase_pos_times <= self.max_increase_pos_times and dump_percent >= self.increase_pos_when_dump_pct: 124 | # ** 表示的是乘方. 125 | self.cancel_all() 126 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_times 127 | price = bar.close_price 128 | vol = increase_pos_value / price 129 | orderids = self.buy(Decimal(price), Decimal(vol)) 130 | self.buy_orders.extend(orderids) 131 | 132 | self.bg.update_bar(bar) 133 | 134 | def on_15min_bar(self, bar: BarData): 135 | 136 | am = self.am 137 | am.update_bar(bar) 138 | if not am.inited: 139 | return 140 | 141 | current_close = am.close_array[-1] 142 | last_close = am.close_array[-2] 143 | boll_up, boll_down = am.boll(self.boll_window, self.boll_dev, array=False) # 返回最新的布林带值. 144 | 145 | # 突破上轨 146 | if last_close <= boll_up < current_close: 147 | if len(self.buy_orders) == 0 and self.current_pos * bar.close_price < self.min_notional: # 每次下单要大于等于10USDT, 为了简单设置11USDT. 148 | # 这里没有仓位. 149 | self.cancel_all() 150 | # 重置当前的数据. 151 | self.current_increase_pos_times = 0 152 | self.avg_price = 0 153 | 154 | price = bar.close_price 155 | vol = self.initial_trading_value / price 156 | orderids = self.buy(Decimal(price), Decimal(vol)) 157 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 158 | 159 | self.put_event() 160 | 161 | def on_order(self, order: OrderData): 162 | """ 163 | Callback of new order data update. 164 | """ 165 | if order.status == Status.ALLTRADED: 166 | if order.direction == Direction.LONG: 167 | # 买单成交. 168 | self.current_increase_pos_times += 1 169 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 170 | 171 | if not order.is_active(): 172 | if order.vt_orderid in self.sell_orders: 173 | self.sell_orders.remove(order.vt_orderid) 174 | 175 | elif order.vt_orderid in self.buy_orders: 176 | self.buy_orders.remove(order.vt_orderid) 177 | 178 | self.put_event() # 更新UI使用. 179 | 180 | def on_trade(self, trade: TradeData): 181 | """ 182 | Callback of new trade data update. 183 | """ 184 | if trade.direction == Direction.LONG: 185 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 186 | self.current_pos += float(trade.volume) 187 | self.avg_price = total / self.current_pos 188 | elif trade.direction == Direction.SHORT: 189 | self.current_pos -= float(trade.volume) 190 | 191 | # 计算统计下总体的利润. 192 | self.total_profit += (float(trade.price) - self.avg_price) * float(trade.volume) - float(trade.volume) * float(trade.price) * 2 * self.trading_fee 193 | 194 | self.put_event() 195 | 196 | def on_stop_order(self, stop_order: StopOrder): 197 | """ 198 | Callback of stop order update. 199 | """ 200 | pass 201 | -------------------------------------------------------------------------------- /class_25/strategies/martingle_future_strategyV2.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.app.cta_strategy.engine import CtaEngine 8 | from howtrader.trader.object import Status, Direction, ContractData, AccountData 9 | from howtrader.trader.utility import ArrayManager, BarGenerator 10 | from typing import Optional 11 | from decimal import Decimal 12 | 13 | 14 | class MartingleFutureStrategyV2(CtaTemplate): 15 | """ 16 | 1. 马丁策略. 17 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 18 | 币安合约邀请码:51bitquant 19 | """ 20 | 21 | """ 22 | 1. 开仓条件是 最高价回撤一定比例 4% 23 | 2. 止盈2% 24 | 3. 加仓: 入场后, 价格最低下跌超过5%, 最低点反弹上去1%, 那么就可以加仓. 均价止盈2%. 25 | """ 26 | author = "51bitquant" 27 | 28 | # 策略的核心参数. 29 | donchian_window = 2880 # two days 30 | open_pos_when_drawdown_pct = 0.04 # 最高值回撤2%时开仓. 31 | 32 | dump_down_pct = 0.04 # 33 | bounce_back_pct = 0.01 # 34 | 35 | exit_profit_pct = 0.02 # 出场平仓百分比 2% 36 | initial_trading_value = 1000 # 首次开仓价值 1000USDT. 37 | trading_value_multiplier = 1.3 # 加仓的比例. 38 | max_increase_pos_times = 7 # 最大的加仓次数 39 | trading_fee = 0.00075 40 | 41 | # 变量 42 | avg_price = 0.0 # 当前持仓的平均价格. 43 | last_entry_price = 0.0 # 上一次入场的价格. 44 | current_pos = 0.0 # 当前的持仓的数量. 45 | current_increase_pos_times = 0 # 当前的加仓的次数. 46 | 47 | upband = 0.0 48 | downband = 0.0 49 | entry_lowest = 0.0 # 进场之后的最低价. 50 | 51 | # 统计总的利润. 52 | total_profit = 0 53 | 54 | parameters = ["donchian_window", "open_pos_when_drawdown_pct", "dump_down_pct", "bounce_back_pct", 55 | "exit_profit_pct", "initial_trading_value", 56 | "trading_value_multiplier", "max_increase_pos_times", "trading_fee"] 57 | 58 | variables = ["avg_price", "last_entry_price", "current_pos", "current_increase_pos_times", 59 | "upband", "downband", "entry_lowest", "total_profit"] 60 | 61 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 62 | """""" 63 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 64 | 65 | self.last_filled_order: Optional[OrderData, None] = None 66 | self.tick: Optional[TickData, None] = None 67 | self.contract: Optional[ContractData, None] = None 68 | self.account: Optional[AccountData, None] = None 69 | self.bg = BarGenerator(self.on_bar) # generate 1min bar. 70 | self.am = ArrayManager(3000) # default is 100, we need 3000 71 | 72 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 73 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 74 | 75 | self.buy_orders = [] # 买单id列表。 76 | self.sell_orders = [] # 卖单id列表。 77 | self.min_notional = 11 # 最小的交易金额. 78 | 79 | def on_init(self): 80 | """ 81 | Callback when strategy is inited. 82 | """ 83 | self.write_log("策略初始化") 84 | self.load_bar(3) # 加载3天的数据. 85 | 86 | def on_start(self): 87 | """ 88 | Callback when strategy is started. 89 | """ 90 | self.write_log("策略启动") 91 | 92 | def on_stop(self): 93 | """ 94 | Callback when strategy is stopped. 95 | """ 96 | self.write_log("策略停止") 97 | 98 | # def process_account_event(self, event: Event): 99 | # self.account: AccountData = event.data 100 | # if self.account: 101 | # print( 102 | # f"self.account: available{self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 103 | 104 | def on_tick(self, tick: TickData): 105 | """ 106 | Callback of new tick data update. 107 | """ 108 | if tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 109 | self.tick = tick 110 | self.bg.update_tick(tick) 111 | 112 | def on_bar(self, bar: BarData): 113 | """ 114 | Callback of new bar data update. 115 | """ 116 | am = self.am 117 | am.update_bar(bar) 118 | if not am.inited: 119 | return 120 | 121 | current_close = am.close_array[-1] 122 | current_low = am.low_array[-1] 123 | 124 | self.upband, self.downband = am.donchian(self.donchian_window, array=False) # 返回最新的布林带值. 125 | 126 | dump_pct = self.upband / current_low - 1 127 | 128 | if self.entry_lowest > 0: 129 | self.entry_lowest = min(self.entry_lowest, bar.low_price) 130 | 131 | # 回调一定比例的时候. 132 | if self.current_pos * current_close < self.min_notional: 133 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 134 | if dump_pct >= self.open_pos_when_drawdown_pct and len(self.buy_orders) == 0: 135 | # 这里没有仓位. 136 | # 重置当前的数据. 137 | self.cancel_all() 138 | self.current_increase_pos_times = 0 139 | self.avg_price = 0 140 | self.entry_lowest = 0 141 | 142 | price = current_close 143 | vol = self.initial_trading_value / price 144 | orderids = self.buy(Decimal(price), Decimal(vol)) 145 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 146 | else: 147 | if len(self.sell_orders) <= 0 < self.avg_price: 148 | # 有利润平仓的时候 149 | # 清理掉其他买单. 150 | 151 | profit_percent = bar.close_price / self.avg_price - 1 152 | if profit_percent >= self.exit_profit_pct: 153 | self.cancel_all() 154 | orderids = self.short(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 155 | self.sell_orders.extend(orderids) 156 | 157 | if self.entry_lowest > 0 >= len(self.buy_orders): 158 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 159 | 160 | dump_down_pct = self.last_entry_price / self.entry_lowest - 1 161 | bounce_back_pct = bar.close_price / self.entry_lowest - 1 162 | 163 | if self.current_increase_pos_times <= self.max_increase_pos_times and dump_down_pct >= self.dump_down_pct and bounce_back_pct >= self.bounce_back_pct: 164 | # ** 表示的是乘方. 165 | self.cancel_all() # 清理其他卖单. 166 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_times 167 | # if self.account and self.account.available >= increase_pos_value: 168 | price = bar.close_price 169 | vol = increase_pos_value / price 170 | orderids = self.buy(Decimal(price), Decimal(vol)) 171 | self.buy_orders.extend(orderids) 172 | 173 | self.put_event() 174 | 175 | def on_order(self, order: OrderData): 176 | """ 177 | Callback of new order data update. 178 | """ 179 | if order.status == Status.ALLTRADED: 180 | if order.direction == Direction.LONG: 181 | # 买单成交. 182 | 183 | self.current_increase_pos_times += 1 184 | self.last_entry_price =float(order.price) # 记录上一次成绩的价格. 185 | self.entry_lowest = float(order.price) 186 | 187 | if not order.is_active(): 188 | if order.vt_orderid in self.sell_orders: 189 | self.sell_orders.remove(order.vt_orderid) 190 | 191 | elif order.vt_orderid in self.buy_orders: 192 | self.buy_orders.remove(order.vt_orderid) 193 | 194 | self.put_event() # 更新UI使用. 195 | 196 | def on_trade(self, trade: TradeData): 197 | """ 198 | Callback of new trade data update. 199 | """ 200 | if trade.direction == Direction.LONG: 201 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 202 | self.current_pos += float(trade.volume) 203 | self.avg_price = total / self.current_pos 204 | elif trade.direction == Direction.SHORT: 205 | self.current_pos -= float(trade.volume) 206 | 207 | # 计算统计下总体的利润. 208 | self.total_profit += (float(trade.price) - self.avg_price) * float(trade.volume) - float(trade.volume) * float(trade.price) * 2 * self.trading_fee 209 | 210 | self.put_event() 211 | 212 | def on_stop_order(self, stop_order: StopOrder): 213 | """ 214 | Callback of stop order update. 215 | """ 216 | pass 217 | -------------------------------------------------------------------------------- /class_25/strategies/martingle_spot_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.app.cta_strategy.engine import CtaEngine 8 | from howtrader.trader.event import EVENT_TIMER 9 | from howtrader.event import Event 10 | from howtrader.trader.object import Status, Direction, Interval, ContractData, AccountData 11 | 12 | from typing import Optional 13 | from howtrader.trader.utility import BarGenerator, ArrayManager 14 | from decimal import Decimal 15 | 16 | class MartingleSpotStrategy(CtaTemplate): 17 | """ 18 | 1. 马丁策略. 19 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 20 | 币安合约邀请码:51bitquant 21 | https://github.com/51bitquant/course_codes 22 | """ 23 | author = "51bitquant" 24 | 25 | # 策略的核心参数. 26 | boll_window = 30 27 | boll_dev = 2.2 28 | 29 | increase_pos_when_dump_pct = 0.04 # 回撤多少加仓 30 | exit_profit_pct = 0.02 # 出场平仓百分比 2% 31 | initial_trading_value = 1000 # 首次开仓价值 1000USDT. 32 | trading_value_multiplier = 1.3 # 加仓的比例. 1000 1300 1300 * 1.3 33 | max_increase_pos_times = 10.0 # 最大的加仓次数 34 | trading_fee = 0.00075 35 | 36 | # 变量 37 | avg_price = 0.0 # 当前持仓的平均价格. 38 | last_entry_price = 0.0 # 上一次入场的价格. 39 | current_pos = 0.0 # 当前的持仓的数量. 40 | current_increase_pos_times = 0 # 当前的加仓的次数. 41 | 42 | # 统计总的利润. 43 | total_profit = 0 44 | 45 | parameters = ["boll_window", "boll_dev", "increase_pos_when_dump_pct", "exit_profit_pct", "initial_trading_value", 46 | "trading_value_multiplier", "max_increase_pos_times", "trading_fee"] 47 | 48 | variables = ["avg_price", "last_entry_price", "current_pos", "current_increase_pos_times", "total_profit"] 49 | 50 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 51 | """""" 52 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 53 | 54 | self.last_filled_order: Optional[OrderData] = None 55 | self.tick: Optional[TickData] = None 56 | self.contract: Optional[ContractData] = None 57 | self.account: Optional[AccountData] = None 58 | 59 | self.bg = BarGenerator(self.on_bar, 15, self.on_15min_bar, Interval.MINUTE) # 15分钟的数据. 60 | self.am = ArrayManager(60) # 默认是100,设置60 61 | # ArrayManager 62 | 63 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 64 | # 现货的资产订阅 65 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 66 | # # 合约的资产订阅 67 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCES.USDT", self.process_account_event) 68 | 69 | self.buy_orders = [] # 买单id列表。 70 | self.sell_orders = [] # 卖单id列表。 71 | self.min_notional = 11 # 最小的交易金额. 72 | 73 | def on_init(self): 74 | """ 75 | Callback when strategy is inited. 76 | """ 77 | self.write_log("策略初始化") 78 | self.load_bar(2) # 加载两天的数据. 79 | 80 | def on_start(self): 81 | """ 82 | Callback when strategy is started. 83 | """ 84 | self.write_log("策略启动") 85 | 86 | def on_stop(self): 87 | """ 88 | Callback when strategy is stopped. 89 | """ 90 | self.write_log("策略停止") 91 | 92 | def process_account_event(self, event: Event): 93 | self.account: AccountData = event.data 94 | # if self.account: 95 | # print( 96 | # f"self.account available: {self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 97 | 98 | def on_tick(self, tick: TickData): 99 | """ 100 | Callback of new tick data update. 101 | """ 102 | if tick and tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 103 | self.tick = tick 104 | self.bg.update_tick(tick) 105 | 106 | def on_bar(self, bar: BarData): 107 | """ 108 | Callback of new bar data update. 109 | """ 110 | 111 | if self.current_pos * bar.close_price >= self.min_notional: 112 | 113 | if len(self.sell_orders) <= 0 < self.avg_price: 114 | # 有利润平仓的时候 115 | profit_percent = bar.close_price / self.avg_price - 1 116 | if profit_percent >= self.exit_profit_pct: 117 | self.cancel_all() 118 | orderids = self.sell(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 119 | self.sell_orders.extend(orderids) 120 | 121 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 122 | dump_percent = self.last_entry_price / bar.close_price - 1 123 | if len( 124 | self.buy_orders) <= 0 and self.current_increase_pos_times <= self.max_increase_pos_times and dump_percent >= self.increase_pos_when_dump_pct: 125 | # ** 表示的是乘方. 126 | self.cancel_all() 127 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_times 128 | price = bar.close_price 129 | vol = increase_pos_value / price 130 | orderids = self.buy(Decimal(price), Decimal(vol)) 131 | self.buy_orders.extend(orderids) 132 | 133 | self.bg.update_bar(bar) 134 | 135 | def on_15min_bar(self, bar: BarData): 136 | 137 | am = self.am 138 | am.update_bar(bar) 139 | if not am.inited: 140 | return 141 | 142 | current_close = am.close_array[-1] 143 | last_close = am.close_array[-2] 144 | boll_up, boll_down = am.boll(self.boll_window, self.boll_dev, array=False) # 返回最新的布林带值. 145 | 146 | # 突破上轨 147 | if last_close <= boll_up < current_close: 148 | if len(self.buy_orders) == 0 and self.current_pos * bar.close_price < self.min_notional: # 每次下单要大于等于10USDT, 为了简单设置11USDT. 149 | # 这里没有仓位. 150 | self.cancel_all() 151 | # 重置当前的数据. 152 | self.current_increase_pos_times = 0 153 | self.avg_price = 0 154 | 155 | price = bar.close_price 156 | vol = self.initial_trading_value / price 157 | orderids = self.buy(Decimal(price), Decimal(vol)) 158 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 159 | 160 | self.put_event() 161 | 162 | def on_order(self, order: OrderData): 163 | """ 164 | Callback of new order data update. 165 | """ 166 | if order.status == Status.ALLTRADED: 167 | if order.direction == Direction.LONG: 168 | # 买单成交. 169 | self.current_increase_pos_times += 1 170 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 171 | 172 | if not order.is_active(): 173 | if order.vt_orderid in self.sell_orders: 174 | self.sell_orders.remove(order.vt_orderid) 175 | 176 | elif order.vt_orderid in self.buy_orders: 177 | self.buy_orders.remove(order.vt_orderid) 178 | 179 | self.put_event() # 更新UI使用. 180 | 181 | def on_trade(self, trade: TradeData): 182 | """ 183 | Callback of new trade data update. 184 | """ 185 | if trade.direction == Direction.LONG: 186 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 187 | self.current_pos += float(trade.volume) 188 | self.avg_price = total / self.current_pos 189 | elif trade.direction == Direction.SHORT: 190 | self.current_pos -= float(trade.volume) 191 | 192 | # 计算统计下总体的利润. 193 | self.total_profit += (float(trade.price) - self.avg_price) * float(trade.volume) - float(trade.volume) * float(trade.price) * 2 * self.trading_fee 194 | 195 | self.put_event() 196 | 197 | def on_stop_order(self, stop_order: StopOrder): 198 | """ 199 | Callback of stop order update. 200 | """ 201 | pass 202 | -------------------------------------------------------------------------------- /class_25/strategies/martingle_spot_strategyV2.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.app.cta_strategy.engine import CtaEngine 8 | from howtrader.trader.object import Status, Direction, ContractData, AccountData 9 | 10 | from typing import Optional 11 | from howtrader.trader.utility import ArrayManager, BarGenerator 12 | from decimal import Decimal 13 | 14 | class MartingleSpotStrategyV2(CtaTemplate): 15 | """ 16 | 1. 马丁策略. 17 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 18 | 币安合约邀请码:51bitquant 19 | """ 20 | 21 | """ 22 | 1. 开仓条件是 最高价回撤一定比例 4% 23 | 2. 止盈2% 24 | 3. 加仓: 入场后, 价格最低下跌超过5%, 最低点反弹上去1%, 那么就可以加仓. 均价止盈2%. 25 | """ 26 | author = "51bitquant" 27 | 28 | # 策略的核心参数. 29 | donchian_window = 2880 # two days 30 | open_pos_when_drawdown_pct = 0.04 # 最高值回撤2%时开仓. 31 | 32 | dump_down_pct = 0.04 # 33 | bounce_back_pct = 0.01 # 34 | 35 | exit_profit_pct = 0.02 # 出场平仓百分比 2% 36 | initial_trading_value = 1000 # 首次开仓价值 1000USDT. 37 | trading_value_multiplier = 1.3 # 加仓的比例. 38 | max_increase_pos_times = 7 # 最大的加仓次数 39 | trading_fee = 0.00075 40 | 41 | # 变量 42 | avg_price = 0.0 # 当前持仓的平均价格. 43 | last_entry_price = 0.0 # 上一次入场的价格. 44 | current_pos = 0.0 # 当前的持仓的数量. 45 | current_increase_pos_times = 0 # 当前的加仓的次数. 46 | 47 | upband = 0.0 48 | downband = 0.0 49 | entry_lowest = 0.0 # 进场之后的最低价. 50 | 51 | # 统计总的利润. 52 | total_profit = 0 53 | 54 | parameters = ["donchian_window", "open_pos_when_drawdown_pct", "dump_down_pct", "bounce_back_pct", 55 | "exit_profit_pct", "initial_trading_value", 56 | "trading_value_multiplier", "max_increase_pos_times", "trading_fee"] 57 | 58 | variables = ["avg_price", "last_entry_price", "current_pos", "current_increase_pos_times", 59 | "upband", "downband", "entry_lowest", "total_profit"] 60 | 61 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 62 | """""" 63 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 64 | 65 | self.last_filled_order: Optional[OrderData] = None 66 | self.tick: Optional[TickData] = None 67 | self.contract: Optional[ContractData] = None 68 | self.account: Optional[AccountData] = None 69 | self.bg = BarGenerator(self.on_bar) # generate 1min bar. 70 | self.am = ArrayManager(3000) # 默认是100,设置3000 71 | 72 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 73 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 74 | 75 | self.buy_orders = [] # 买单id列表。 76 | self.sell_orders = [] # 卖单id列表。 77 | self.min_notional = 11 # 最小的交易金额. 78 | 79 | def on_init(self): 80 | """ 81 | Callback when strategy is inited. 82 | """ 83 | self.write_log("策略初始化") 84 | self.load_bar(3) # 加载3天的数据. 85 | 86 | def on_start(self): 87 | """ 88 | Callback when strategy is started. 89 | """ 90 | self.write_log("策略启动") 91 | 92 | def on_stop(self): 93 | """ 94 | Callback when strategy is stopped. 95 | """ 96 | self.write_log("策略停止") 97 | 98 | # def process_account_event(self, event: Event): 99 | # self.account: AccountData = event.data 100 | # if self.account: 101 | # print( 102 | # f"self.account: available{self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 103 | 104 | def on_tick(self, tick: TickData): 105 | """ 106 | Callback of new tick data update. 107 | """ 108 | if tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 109 | self.tick = tick 110 | self.bg.update_tick(tick) 111 | 112 | def on_bar(self, bar: BarData): 113 | """ 114 | Callback of new bar data update. 115 | """ 116 | am = self.am 117 | am.update_bar(bar) 118 | if not am.inited: 119 | return 120 | 121 | current_close = am.close_array[-1] 122 | current_low = am.low_array[-1] 123 | 124 | self.upband, self.downband = am.donchian(self.donchian_window, array=False) # 返回最新的布林带值. 125 | 126 | dump_pct = self.upband / current_low - 1 127 | 128 | if self.entry_lowest > 0: 129 | self.entry_lowest = min(self.entry_lowest, bar.low_price) 130 | 131 | # 回调一定比例的时候. 132 | if self.current_pos * current_close < self.min_notional: 133 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 134 | if dump_pct >= self.open_pos_when_drawdown_pct and len(self.buy_orders) == 0: 135 | # 这里没有仓位. 136 | # 重置当前的数据. 137 | self.cancel_all() 138 | self.current_increase_pos_times = 0 139 | self.avg_price = 0 140 | self.entry_lowest = 0 141 | 142 | price = current_close 143 | vol = self.initial_trading_value / price 144 | orderids = self.buy(price, vol) 145 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 146 | else: 147 | 148 | if len(self.sell_orders) <= 0 and self.avg_price > 0: 149 | # 有利润平仓的时候 150 | # 清理掉其他买单. 151 | 152 | profit_percent = bar.close_price / self.avg_price - 1 153 | if profit_percent >= self.exit_profit_pct: 154 | self.cancel_all() 155 | orderids = self.sell(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 156 | self.sell_orders.extend(orderids) 157 | 158 | if self.entry_lowest > 0 and len(self.buy_orders) <= 0: 159 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 160 | 161 | dump_down_pct = self.last_entry_price / self.entry_lowest - 1 162 | bounce_back_pct = bar.close_price / self.entry_lowest - 1 163 | 164 | if self.current_increase_pos_times <= self.max_increase_pos_times and dump_down_pct >= self.dump_down_pct and bounce_back_pct >= self.bounce_back_pct: 165 | # ** 表示的是乘方. 166 | self.cancel_all() # 清理其他卖单. 167 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_times 168 | # if self.account and self.account.available >= increase_pos_value: 169 | price = bar.close_price 170 | vol = increase_pos_value / price 171 | orderids = self.buy(Decimal(price), Decimal(vol)) 172 | self.buy_orders.extend(orderids) 173 | 174 | self.put_event() 175 | 176 | def on_order(self, order: OrderData): 177 | """ 178 | Callback of new order data update. 179 | """ 180 | if order.status == Status.ALLTRADED: 181 | if order.direction == Direction.LONG: 182 | # 买单成交. 183 | 184 | self.current_increase_pos_times += 1 185 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 186 | self.entry_lowest = float(order.price) 187 | 188 | if not order.is_active(): 189 | if order.vt_orderid in self.sell_orders: 190 | self.sell_orders.remove(order.vt_orderid) 191 | 192 | elif order.vt_orderid in self.buy_orders: 193 | self.buy_orders.remove(order.vt_orderid) 194 | 195 | self.put_event() # 更新UI使用. 196 | 197 | def on_trade(self, trade: TradeData): 198 | """ 199 | Callback of new trade data update. 200 | """ 201 | if trade.direction == Direction.LONG: 202 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 203 | self.current_pos += float(trade.volume) 204 | self.avg_price = total / self.current_pos 205 | elif trade.direction == Direction.SHORT: 206 | self.current_pos -= float(trade.volume) 207 | 208 | # 计算统计下总体的利润. 209 | self.total_profit += (float(trade.price) - self.avg_price) * float(trade.volume) - float(trade.volume) * float(trade.price) * 2 * self.trading_fee 210 | 211 | self.put_event() 212 | 213 | def on_stop_order(self, stop_order: StopOrder): 214 | """ 215 | Callback of stop order update. 216 | """ 217 | pass 218 | -------------------------------------------------------------------------------- /class_26/26.强势币的马丁策略.md: -------------------------------------------------------------------------------- 1 | # 26课. 强势币的马丁策略 2 | 3 | ## 策略思路 4 | 5 | 1. 挑选1小时涨幅超过2.6%的币,或者4小涨幅超过4.6%的币, 6 | 且上引线不能过长(防止入场) 3%,然后入场 7 | 2. 利润超过1%,且最高价回调1%后平仓,当然你可以选择自己的参数 8 | 3. 如果入场后,没有利润,价格继续下跌。那么入场价格下跌5%后,采用马丁策略加仓。 -------------------------------------------------------------------------------- /class_26/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_26/strategies/martingle_future_strategyV3.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | 8 | from howtrader.app.cta_strategy.engine import CtaEngine 9 | from howtrader.trader.object import Status, Direction, Interval, ContractData, AccountData 10 | 11 | from typing import Optional 12 | from howtrader.trader.utility import BarGenerator 13 | from decimal import Decimal 14 | 15 | class MartingleFutureStrategyV3(CtaTemplate): 16 | """ 17 | 1. 马丁策略. 18 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 19 | 币安合约邀请码:51bitquant 20 | 21 | ## 策略思路 22 | 23 | 1. 挑选1小时涨幅超过2.6%的币,或者4小涨幅超过4.6%的币, 然后入场 24 | 2. 利润超过1%,且最高价回调1%后平仓,当然你可以选择自己的参数 25 | 3. 如果入场后,没有利润,价格继续下跌。那么入场价格下跌5%后,采用马丁策略加仓。 26 | 27 | """ 28 | author = "51bitquant" 29 | 30 | # 策略的核心参数. 31 | initial_trading_value = 200 # 首次开仓价值 1000USDT. 32 | trading_value_multiplier = 2 # 加仓的比例. 33 | max_increase_pos_count = 5 # 最大的加仓次数 34 | 35 | hour_pump_pct = 0.026 # 小时的上涨百分比 36 | four_hour_pump_pct = 0.046 # 四小时的上涨百分比. 37 | high_close_change_pct = 0.03 # 最高价/收盘价 -1, 防止上引线过长. 38 | increase_pos_when_dump_pct = 0.05 # 价格下跌 5%就继续加仓. 39 | exit_profit_pct = 0.01 # 出场平仓百分比 1% 40 | exit_pull_back_pct = 0.01 # 最高价回调超过1%,且利润超过1% 就出场. 41 | trading_fee = 0.00075 # 交易手续费 42 | 43 | # 变量 44 | avg_price = 0.0 # 当前持仓的平均价格. 45 | last_entry_price = 0.0 # 上一次入场的价格. 46 | entry_highest_price = 0.0 47 | current_pos = 0.0 # 当前的持仓的数量. 48 | current_increase_pos_count = 0 # 当前的加仓的次数. 49 | total_profit = 0 # 统计总的利润. 50 | 51 | parameters = ["initial_trading_value", "trading_value_multiplier", "max_increase_pos_count", 52 | "hour_pump_pct", "four_hour_pump_pct", "high_close_change_pct", "increase_pos_when_dump_pct", 53 | "exit_profit_pct", 54 | "exit_pull_back_pct", "trading_fee"] 55 | 56 | variables = ["avg_price", "last_entry_price", "entry_highest_price", "current_pos", "current_increase_pos_count", 57 | "total_profit"] 58 | 59 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 60 | """""" 61 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 62 | 63 | self.last_filled_order: Optional[OrderData] = None 64 | self.tick: Optional[TickData] = None 65 | self.contract: Optional[ContractData] = None 66 | self.account: Optional[AccountData] = None 67 | 68 | self.bg_1hour = BarGenerator(self.on_bar, 1, on_window_bar=self.on_1hour_bar, interval=Interval.HOUR) # 1hour 69 | self.bg_4hour = BarGenerator(self.on_bar, 4, on_window_bar=self.on_4hour_bar, interval=Interval.HOUR) # 4hour 70 | 71 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 72 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 73 | 74 | self.buy_orders = [] # 买单id列表。 75 | self.sell_orders = [] # 卖单id列表。 76 | self.min_notional = 11 # 最小的交易金额. 77 | 78 | def on_init(self): 79 | """ 80 | Callback when strategy is inited. 81 | """ 82 | self.write_log("策略初始化") 83 | self.load_bar(3) # 加载3天的数据. 84 | 85 | def on_start(self): 86 | """ 87 | Callback when strategy is started. 88 | """ 89 | self.write_log("策略启动") 90 | 91 | def on_stop(self): 92 | """ 93 | Callback when strategy is stopped. 94 | """ 95 | self.write_log("策略停止") 96 | 97 | # def process_account_event(self, event: Event): 98 | # self.account: AccountData = event.data 99 | # if self.account: 100 | # print( 101 | # f"self.account: available{self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 102 | 103 | def on_tick(self, tick: TickData): 104 | """ 105 | Callback of new tick data update. 106 | """ 107 | if tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 108 | self.bg_1hour.update_tick(tick) 109 | self.bg_4hour.update_tick(tick) 110 | 111 | def on_bar(self, bar: BarData): 112 | """ 113 | Callback of new bar data update. 114 | """ 115 | if self.entry_highest_price > 0: 116 | self.entry_highest_price = max(bar.high_price, self.entry_highest_price) 117 | 118 | if self.current_pos * bar.close_price >= self.min_notional: 119 | 120 | if len(self.sell_orders) <= 0 < self.avg_price: 121 | # 有利润平仓的时候 122 | # 清理掉其他买单. 123 | 124 | profit_percent = bar.close_price / self.avg_price - 1 125 | profit_pull_back_pct = self.entry_highest_price / bar.close_price - 1 126 | if profit_percent >= self.exit_profit_pct and profit_pull_back_pct >= self.exit_pull_back_pct: 127 | self.cancel_all() 128 | orderids = self.short(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 129 | self.sell_orders.extend(orderids) 130 | 131 | if len(self.buy_orders) <= 0: 132 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 133 | 134 | dump_down_pct = self.last_entry_price / bar.close_price - 1 135 | 136 | if self.current_increase_pos_count <= self.max_increase_pos_count and dump_down_pct >= self.increase_pos_when_dump_pct: 137 | # ** 表示的是乘方. 138 | self.cancel_all() # 清理其他卖单. 139 | 140 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_count 141 | price = bar.close_price 142 | vol = increase_pos_value / price 143 | orderids = self.buy(Decimal(price), Decimal(vol)) 144 | self.buy_orders.extend(orderids) 145 | 146 | self.bg_1hour.update_bar(bar) 147 | self.bg_4hour.update_bar(bar) 148 | 149 | self.put_event() 150 | 151 | def on_1hour_bar(self, bar: BarData): 152 | 153 | close_change_pct = bar.close_price / bar.open_price - 1 # 收盘价涨了多少. 154 | high_change_pct = bar.high_price / bar.close_price - 1 # 计算上引线 155 | 156 | # 回调一定比例的时候. 157 | if self.current_pos * bar.close_price < self.min_notional: 158 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 159 | if close_change_pct >= self.hour_pump_pct and high_change_pct < self.high_close_change_pct and len( 160 | self.buy_orders) == 0: 161 | # 这里没有仓位. 162 | # 重置当前的数据. 163 | self.cancel_all() 164 | self.current_increase_pos_count = 0 165 | self.avg_price = 0 166 | self.entry_highest_price = 0.0 167 | 168 | price = bar.close_price 169 | vol = self.initial_trading_value / price 170 | orderids = self.buy(Decimal(price), Decimal(vol)) 171 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 172 | 173 | def on_4hour_bar(self, bar: BarData): 174 | close_change_pct = bar.close_price / bar.open_price - 1 # 收盘价涨了多少. 175 | high_change_pct = bar.high_price / bar.close_price - 1 # 计算上引线 176 | 177 | # 回调一定比例的时候. 178 | if self.current_pos * bar.close_price < self.min_notional: 179 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 180 | if close_change_pct >= self.four_hour_pump_pct and high_change_pct < self.high_close_change_pct and len( 181 | self.buy_orders) == 0: 182 | # 这里没有仓位. 183 | # 重置当前的数据. 184 | self.cancel_all() 185 | self.current_increase_pos_count = 0 186 | self.avg_price = 0 187 | self.entry_highest_price = 0.0 188 | 189 | price = bar.close_price 190 | vol = self.initial_trading_value / price 191 | orderids = self.buy(Decimal(price), Decimal(vol)) 192 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 193 | 194 | def on_order(self, order: OrderData): 195 | """ 196 | Callback of new order data update. 197 | """ 198 | if order.status == Status.ALLTRADED: 199 | if order.direction == Direction.LONG: 200 | # 买单成交. 201 | self.current_increase_pos_count += 1 202 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 203 | self.entry_highest_price = float(order.price) 204 | 205 | if not order.is_active(): 206 | if order.vt_orderid in self.sell_orders: 207 | self.sell_orders.remove(order.vt_orderid) 208 | 209 | elif order.vt_orderid in self.buy_orders: 210 | self.buy_orders.remove(order.vt_orderid) 211 | 212 | self.put_event() # 更新UI使用. 213 | 214 | def on_trade(self, trade: TradeData): 215 | """ 216 | Callback of new trade data update. 217 | """ 218 | if trade.direction == Direction.LONG: 219 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 220 | self.current_pos += float(trade.volume) 221 | self.avg_price = total / self.current_pos 222 | 223 | elif trade.direction == Direction.SHORT: 224 | self.current_pos -= float(trade.volume) 225 | 226 | # 计算统计下总体的利润. 227 | profit = (float(trade.price) - self.avg_price) * float(trade.volume) 228 | total_fee = float(trade.volume) * float(trade.price) * 2 * self.trading_fee 229 | self.total_profit += profit - total_fee 230 | 231 | self.put_event() 232 | 233 | def on_stop_order(self, stop_order: StopOrder): 234 | """ 235 | Callback of stop order update. 236 | """ 237 | pass 238 | -------------------------------------------------------------------------------- /class_26/strategies/martingle_spot_strategyV3.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | 8 | from howtrader.app.cta_strategy.engine import CtaEngine 9 | from howtrader.trader.object import Status, Direction, Interval, ContractData, AccountData 10 | 11 | from typing import Optional 12 | from howtrader.trader.utility import BarGenerator 13 | from decimal import Decimal 14 | 15 | 16 | class MartingleSpotStrategyV3(CtaTemplate): 17 | """ 18 | 1. 马丁策略. 19 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 20 | 币安合约邀请码:51bitquant 21 | 22 | ## 策略思路 23 | 24 | 1. 挑选1小时涨幅超过2.6%的币,或者4小涨幅超过4.6%的币, 然后入场 25 | 2. 利润超过1%,且最高价回调1%后平仓,当然你可以选择自己的参数 26 | 3. 如果入场后,没有利润,价格继续下跌。那么入场价格下跌5%后,采用马丁策略加仓。 27 | 28 | """ 29 | author = "51bitquant" 30 | 31 | # 策略的核心参数. 32 | initial_trading_value = 200 # 首次开仓价值 1000USDT. 33 | trading_value_multiplier = 2 # 加仓的比例. 34 | max_increase_pos_count = 5 # 最大的加仓次数 35 | 36 | hour_pump_pct = 0.026 # 小时的上涨百分比 37 | four_hour_pump_pct = 0.046 # 四小时的上涨百分比. 38 | high_close_change_pct = 0.03 # 最高价/收盘价 -1, 防止上引线过长. 39 | increase_pos_when_dump_pct = 0.05 # 价格下跌 5%就继续加仓. 40 | exit_profit_pct = 0.01 # 出场平仓百分比 1% 41 | exit_pull_back_pct = 0.01 # 最高价回调超过1%,且利润超过1% 就出场. 42 | trading_fee = 0.00075 # 交易手续费 43 | 44 | # 变量 45 | avg_price = 0.0 # 当前持仓的平均价格. 46 | last_entry_price = 0.0 # 上一次入场的价格. 47 | entry_highest_price = 0.0 48 | current_pos = 0.0 # 当前的持仓的数量. 49 | current_increase_pos_count = 0 # 当前的加仓的次数. 50 | total_profit = 0 # 统计总的利润. 51 | 52 | parameters = ["initial_trading_value", "trading_value_multiplier", "max_increase_pos_count", 53 | "hour_pump_pct", "four_hour_pump_pct", "high_close_change_pct", "increase_pos_when_dump_pct", 54 | "exit_profit_pct", 55 | "exit_pull_back_pct", "trading_fee"] 56 | 57 | variables = ["avg_price", "last_entry_price", "entry_highest_price", "current_pos", "current_increase_pos_count", 58 | "total_profit"] 59 | 60 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 61 | """""" 62 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 63 | 64 | self.last_filled_order: Optional[OrderData] = None 65 | self.tick: Optional[TickData] = None 66 | self.contract: Optional[ContractData] = None 67 | self.account: Optional[AccountData] = None 68 | 69 | self.bg_1hour = BarGenerator(self.on_bar, 1, on_window_bar=self.on_1hour_bar, interval=Interval.HOUR) # 1hour 70 | self.bg_4hour = BarGenerator(self.on_bar, 4, on_window_bar=self.on_4hour_bar, interval=Interval.HOUR) # 4hour 71 | 72 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 73 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 74 | 75 | self.buy_orders = [] # 买单id列表。 76 | self.sell_orders = [] # 卖单id列表。 77 | self.min_notional = 11 # 最小的交易金额. 78 | 79 | def on_init(self): 80 | """ 81 | Callback when strategy is inited. 82 | """ 83 | self.write_log("策略初始化") 84 | self.load_bar(3) # 加载3天的数据. 85 | 86 | def on_start(self): 87 | """ 88 | Callback when strategy is started. 89 | """ 90 | self.write_log("策略启动") 91 | 92 | def on_stop(self): 93 | """ 94 | Callback when strategy is stopped. 95 | """ 96 | self.write_log("策略停止") 97 | 98 | # def process_account_event(self, event: Event): 99 | # self.account: AccountData = event.data 100 | # if self.account: 101 | # print( 102 | # f"self.account: available{self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 103 | 104 | def on_tick(self, tick: TickData): 105 | """ 106 | Callback of new tick data update. 107 | """ 108 | if tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 109 | self.bg_1hour.update_tick(tick) 110 | self.bg_4hour.update_tick(tick) 111 | 112 | def on_bar(self, bar: BarData): 113 | """ 114 | Callback of new bar data update. 115 | """ 116 | if self.entry_highest_price > 0: 117 | self.entry_highest_price = max(bar.high_price, self.entry_highest_price) 118 | 119 | if self.current_pos * bar.close_price >= self.min_notional: 120 | 121 | if len(self.sell_orders) <= 0 < self.avg_price: 122 | # 有利润平仓的时候 123 | # 清理掉其他买单. 124 | 125 | profit_percent = bar.close_price / self.avg_price - 1 126 | profit_pull_back_pct = self.entry_highest_price / bar.close_price - 1 127 | if profit_percent >= self.exit_profit_pct and profit_pull_back_pct >= self.exit_pull_back_pct: 128 | self.cancel_all() 129 | orderids = self.sell(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 130 | self.sell_orders.extend(orderids) 131 | 132 | if len(self.buy_orders) <= 0: 133 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 134 | 135 | dump_down_pct = self.last_entry_price / bar.close_price - 1 136 | 137 | if self.current_increase_pos_count <= self.max_increase_pos_count and dump_down_pct >= self.increase_pos_when_dump_pct: 138 | # ** 表示的是乘方. 139 | self.cancel_all() # 清理其他卖单. 140 | 141 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_count 142 | price = bar.close_price 143 | vol = increase_pos_value / price 144 | orderids = self.buy(Decimal(price), Decimal(vol)) 145 | self.buy_orders.extend(orderids) 146 | 147 | self.bg_1hour.update_bar(bar) 148 | self.bg_4hour.update_bar(bar) 149 | 150 | self.put_event() 151 | 152 | def on_1hour_bar(self, bar: BarData): 153 | 154 | close_change_pct = bar.close_price / bar.open_price - 1 # 收盘价涨了多少. 155 | high_change_pct = bar.high_price / bar.close_price - 1 # 计算上引线 156 | 157 | # 回调一定比例的时候. 158 | if self.current_pos * bar.close_price < self.min_notional: 159 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 160 | if close_change_pct >= self.hour_pump_pct and high_change_pct < self.high_close_change_pct and len( 161 | self.buy_orders) == 0: 162 | # 这里没有仓位. 163 | # 重置当前的数据. 164 | self.cancel_all() 165 | self.current_increase_pos_count = 0 166 | self.avg_price = 0 167 | self.entry_highest_price = 0.0 168 | 169 | price = bar.close_price 170 | vol = self.initial_trading_value / price 171 | orderids = self.buy(Decimal(price), Decimal(vol)) 172 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 173 | 174 | def on_4hour_bar(self, bar: BarData): 175 | close_change_pct = bar.close_price / bar.open_price - 1 # 收盘价涨了多少. 176 | high_change_pct = bar.high_price / bar.close_price - 1 # 计算上引线 177 | 178 | # 回调一定比例的时候. 179 | if self.current_pos * bar.close_price < self.min_notional: 180 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 181 | if close_change_pct >= self.four_hour_pump_pct and high_change_pct < self.high_close_change_pct and len( 182 | self.buy_orders) == 0: 183 | # 这里没有仓位. 184 | # 重置当前的数据. 185 | self.cancel_all() 186 | self.current_increase_pos_count = 0 187 | self.avg_price = 0 188 | self.entry_highest_price = 0.0 189 | 190 | price = bar.close_price 191 | vol = self.initial_trading_value / price 192 | orderids = self.buy(Decimal(price), Decimal(vol)) 193 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 194 | 195 | def on_order(self, order: OrderData): 196 | """ 197 | Callback of new order data update. 198 | """ 199 | if order.status == Status.ALLTRADED: 200 | if order.direction == Direction.LONG: 201 | # 买单成交. 202 | self.current_increase_pos_count += 1 203 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 204 | self.entry_highest_price = float(order.price) 205 | 206 | if not order.is_active(): 207 | if order.vt_orderid in self.sell_orders: 208 | self.sell_orders.remove(order.vt_orderid) 209 | 210 | elif order.vt_orderid in self.buy_orders: 211 | self.buy_orders.remove(order.vt_orderid) 212 | 213 | self.put_event() # 更新UI使用. 214 | 215 | def on_trade(self, trade: TradeData): 216 | """ 217 | Callback of new trade data update. 218 | """ 219 | if trade.direction == Direction.LONG: 220 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 221 | self.current_pos += float(trade.volume) 222 | 223 | self.avg_price = total / self.current_pos 224 | elif trade.direction == Direction.SHORT: 225 | self.current_pos -= float(trade.volume) 226 | 227 | # 计算统计下总体的利润. 228 | profit = (float(trade.price) - self.avg_price) * float(trade.volume) 229 | total_fee = float(trade.volume) * float(trade.price) * 2 * self.trading_fee 230 | self.total_profit += profit - total_fee 231 | 232 | self.put_event() 233 | 234 | def on_stop_order(self, stop_order: StopOrder): 235 | """ 236 | Callback of stop order update. 237 | """ 238 | pass 239 | -------------------------------------------------------------------------------- /class_27/27.howtrader脚本运行操作和多策略多交易对交易.md: -------------------------------------------------------------------------------- 1 | # 27. howtrader脚本运行操作和多策略多交易对交易 2 | 3 | 4 | ## No-UI(脚本交易策略) 5 | 6 | ``` 7 | import sys 8 | from time import sleep 9 | from datetime import datetime, time 10 | from logging import INFO 11 | 12 | from howtrader.event import EventEngine 13 | from howtrader.trader.setting import SETTINGS 14 | from howtrader.trader.engine import MainEngine, LogEngine 15 | 16 | from howtrader.gateway.binance import Binance # 合约接口 17 | from howtrader.gateway.binance import BinanceUsdtGateway 18 | 19 | from howtrader.gateway.binance import BinanceSpotGateway # 现货接口 20 | from howtrader.app.cta_strategy import CtaStrategyApp, CtaEngine 21 | from howtrader.app.cta_strategy.base import EVENT_CTA_LOG 22 | 23 | SETTINGS["log.active"] = True 24 | SETTINGS["log.level"] = INFO 25 | SETTINGS["log.console"] = True 26 | 27 | # 合约的api配置 28 | gateway_setting = { 29 | "key": "", 30 | "secret": "", 31 | "会话数": 3, 32 | "服务器": "REAL", 33 | "合约模式": "正向", 34 | "代理地址": "", 35 | "代理端口": 0, 36 | } 37 | 38 | 39 | def run(): 40 | """ 41 | Running in the child process. 42 | """ 43 | SETTINGS["log.file"] = True 44 | 45 | event_engine = EventEngine() 46 | main_engine = MainEngine(event_engine) 47 | main_engine.add_gateway(BinanceUsdtGateway) 48 | main_engine.add_gateway(BinanceSpotGateway) 49 | # cta_engine = main_engine.add_app(CtaStrategyApp) 50 | cta_engine: CtaEngine = main_engine.add_app(CtaStrategyApp) 51 | main_engine.write_log("主引擎创建成功") 52 | 53 | # log_engine = main_engine.get_engine("log") 54 | log_engine: LogEngine = main_engine.get_engine("log") 55 | event_engine.register(EVENT_CTA_LOG, log_engine.process_log_event) 56 | main_engine.write_log("注册日志事件监听") 57 | 58 | # main_engine.connect(gateway_setting, "BINANCE_USDT") # 连接合约的 59 | main_engine.connect(gateway_setting, "BINANCE_SPOT") # 连接现货的 60 | main_engine.write_log("连接接口成功") 61 | 62 | sleep(10) 63 | 64 | cta_engine.init_engine() 65 | main_engine.write_log("CTA策略初始化完成") 66 | 67 | cta_engine.init_all_strategies() 68 | sleep(60) # Leave enough time to complete strategy initialization 69 | main_engine.write_log("CTA策略全部初始化") 70 | 71 | cta_engine.start_all_strategies() 72 | main_engine.write_log("CTA策略全部启动") 73 | 74 | while True: 75 | sleep(10) 76 | 77 | 78 | if __name__ == "__main__": 79 | run() 80 | 81 | ``` 82 | 83 | ## 如果添加运行的策略 84 | 1. 修改配置文件 howtrader/cta_strategy_settings.json 85 | 86 | 里面是一个json文件,如果不知道如何配置可以通过UI的方式添加策略后,然后再拷贝进来修改 87 | 88 | 89 | ## howtrader多个交易对交易 90 | 添加不同交易对的就可以,可以添加不同策略或者不同的交易对都可以的。 91 | 92 | 93 | 94 | ## 脚本运行 95 | > sh run.sh 96 | 97 | ## 查看你的进程 98 | > ps -ef | grep main_no_ui.py 99 | 100 | 101 | ## 停止软件 102 | kill -9 进程id -------------------------------------------------------------------------------- /class_27/main_no_ui.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from time import sleep 3 | from datetime import datetime, time 4 | from logging import INFO 5 | 6 | from howtrader.event import EventEngine 7 | from howtrader.trader.setting import SETTINGS 8 | from howtrader.trader.engine import MainEngine, LogEngine 9 | 10 | from howtrader.gateway.binance import BinanceSpotGateway # 合约接口 11 | from howtrader.gateway.binance import BinanceUsdtGateway # 现货接口 12 | from howtrader.app.cta_strategy import CtaStrategyApp, CtaEngine 13 | from howtrader.app.cta_strategy.base import EVENT_CTA_LOG 14 | 15 | SETTINGS["log.active"] = True 16 | SETTINGS["log.level"] = INFO 17 | SETTINGS["log.console"] = True 18 | 19 | # 合约的api配置 20 | gateway_setting = { 21 | "key": "", 22 | "secret": "", 23 | "proxy_host": "", 24 | "proxy_port": 0, 25 | } 26 | 27 | def run(): 28 | """ 29 | Running in the child process. 30 | """ 31 | SETTINGS["log.file"] = True 32 | 33 | event_engine = EventEngine() 34 | main_engine = MainEngine(event_engine) 35 | main_engine.add_gateway(BinanceSpotGateway) 36 | main_engine.add_gateway(BinanceUsdtGateway) 37 | # cta_engine = main_engine.add_app(CtaStrategyApp) 38 | cta_engine: CtaEngine = main_engine.add_app(CtaStrategyApp) 39 | main_engine.write_log("主引擎创建成功") 40 | 41 | # log_engine = main_engine.get_engine("log") 42 | log_engine: LogEngine = main_engine.get_engine("log") 43 | event_engine.register(EVENT_CTA_LOG, log_engine.process_log_event) 44 | main_engine.write_log("注册日志事件监听") 45 | 46 | main_engine.connect(gateway_setting, "BINANCE_USDT") # 连接USDT合约 47 | main_engine.connect(gateway_setting, "BINANCE_SPOT") # 连接现货的 48 | main_engine.write_log("连接接口成功") 49 | 50 | sleep(10) 51 | 52 | cta_engine.init_engine() 53 | main_engine.write_log("CTA策略初始化完成") 54 | 55 | cta_engine.init_all_strategies() 56 | sleep(60) # Leave enough time to complete strategy initialization 57 | main_engine.write_log("CTA策略全部初始化") 58 | 59 | cta_engine.start_all_strategies() 60 | main_engine.write_log("CTA策略全部启动") 61 | 62 | while True: 63 | sleep(10) 64 | 65 | 66 | if __name__ == "__main__": 67 | run() 68 | -------------------------------------------------------------------------------- /class_27/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_27/run.sh: -------------------------------------------------------------------------------- 1 | nohup python -u main_no_ui.py > nohup_log.out 2>&1 & -------------------------------------------------------------------------------- /class_27/strategies/martingle_spot_strategyV2.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.app.cta_strategy.engine import CtaEngine 8 | from howtrader.trader.object import Status, Direction, ContractData, AccountData 9 | 10 | from typing import Optional 11 | from howtrader.trader.utility import ArrayManager, BarGenerator 12 | from decimal import Decimal 13 | 14 | class MartingleSpotStrategyV2(CtaTemplate): 15 | """ 16 | 1. 马丁策略. 17 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 18 | 币安合约邀请码:51bitquant 19 | """ 20 | 21 | """ 22 | 1. 开仓条件是 最高价回撤一定比例 4% 23 | 2. 止盈2% 24 | 3. 加仓: 入场后, 价格最低下跌超过5%, 最低点反弹上去1%, 那么就可以加仓. 均价止盈2%. 25 | """ 26 | author = "51bitquant" 27 | 28 | # 策略的核心参数. 29 | donchian_window = 2880 # two days 30 | open_pos_when_drawdown_pct = 0.04 # 最高值回撤2%时开仓. 31 | 32 | dump_down_pct = 0.04 # 33 | bounce_back_pct = 0.01 # 34 | 35 | exit_profit_pct = 0.02 # 出场平仓百分比 2% 36 | initial_trading_value = 1000 # 首次开仓价值 1000USDT. 37 | trading_value_multiplier = 1.3 # 加仓的比例. 38 | max_increase_pos_times = 7 # 最大的加仓次数 39 | trading_fee = 0.00075 40 | 41 | # 变量 42 | avg_price = 0.0 # 当前持仓的平均价格. 43 | last_entry_price = 0.0 # 上一次入场的价格. 44 | current_pos = 0.0 # 当前的持仓的数量. 45 | current_increase_pos_times = 0 # 当前的加仓的次数. 46 | 47 | upband = 0.0 48 | downband = 0.0 49 | entry_lowest = 0.0 # 进场之后的最低价. 50 | 51 | # 统计总的利润. 52 | total_profit = 0 53 | 54 | parameters = ["donchian_window", "open_pos_when_drawdown_pct", "dump_down_pct", "bounce_back_pct", 55 | "exit_profit_pct", "initial_trading_value", 56 | "trading_value_multiplier", "max_increase_pos_times", "trading_fee"] 57 | 58 | variables = ["avg_price", "last_entry_price", "current_pos", "current_increase_pos_times", 59 | "upband", "downband", "entry_lowest", "total_profit"] 60 | 61 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 62 | """""" 63 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 64 | 65 | self.last_filled_order: Optional[OrderData] = None 66 | self.tick: Optional[TickData] = None 67 | self.contract: Optional[ContractData] = None 68 | self.account: Optional[AccountData] = None 69 | self.bg = BarGenerator(self.on_bar) # generate 1min bar. 70 | self.am = ArrayManager(3000) # 默认是100,设置3000 71 | 72 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 73 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 74 | 75 | self.buy_orders = [] # 买单id列表。 76 | self.sell_orders = [] # 卖单id列表。 77 | self.min_notional = 11 # 最小的交易金额. 78 | 79 | def on_init(self): 80 | """ 81 | Callback when strategy is inited. 82 | """ 83 | self.write_log("策略初始化") 84 | self.load_bar(3) # 加载3天的数据. 85 | 86 | def on_start(self): 87 | """ 88 | Callback when strategy is started. 89 | """ 90 | self.write_log("策略启动") 91 | 92 | def on_stop(self): 93 | """ 94 | Callback when strategy is stopped. 95 | """ 96 | self.write_log("策略停止") 97 | 98 | # def process_account_event(self, event: Event): 99 | # self.account: AccountData = event.data 100 | # if self.account: 101 | # print( 102 | # f"self.account: available{self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 103 | 104 | def on_tick(self, tick: TickData): 105 | """ 106 | Callback of new tick data update. 107 | """ 108 | if tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 109 | self.tick = tick 110 | self.bg.update_tick(tick) 111 | 112 | def on_bar(self, bar: BarData): 113 | """ 114 | Callback of new bar data update. 115 | """ 116 | am = self.am 117 | am.update_bar(bar) 118 | if not am.inited: 119 | return 120 | 121 | current_close = am.close_array[-1] 122 | current_low = am.low_array[-1] 123 | 124 | self.upband, self.downband = am.donchian(self.donchian_window, array=False) # 返回最新的布林带值. 125 | 126 | dump_pct = self.upband / current_low - 1 127 | 128 | if self.entry_lowest > 0: 129 | self.entry_lowest = min(self.entry_lowest, bar.low_price) 130 | 131 | # 回调一定比例的时候. 132 | if self.current_pos * current_close < self.min_notional: 133 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 134 | if dump_pct >= self.open_pos_when_drawdown_pct and len(self.buy_orders) == 0: 135 | # 这里没有仓位. 136 | # 重置当前的数据. 137 | self.cancel_all() 138 | self.current_increase_pos_times = 0 139 | self.avg_price = 0 140 | self.entry_lowest = 0 141 | 142 | price = current_close 143 | vol = self.initial_trading_value / price 144 | orderids = self.buy(price, vol) 145 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 146 | else: 147 | 148 | if len(self.sell_orders) <= 0 and self.avg_price > 0: 149 | # 有利润平仓的时候 150 | # 清理掉其他买单. 151 | 152 | profit_percent = bar.close_price / self.avg_price - 1 153 | if profit_percent >= self.exit_profit_pct: 154 | self.cancel_all() 155 | orderids = self.sell(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 156 | self.sell_orders.extend(orderids) 157 | 158 | if self.entry_lowest > 0 and len(self.buy_orders) <= 0: 159 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 160 | 161 | dump_down_pct = self.last_entry_price / self.entry_lowest - 1 162 | bounce_back_pct = bar.close_price / self.entry_lowest - 1 163 | 164 | if self.current_increase_pos_times <= self.max_increase_pos_times and dump_down_pct >= self.dump_down_pct and bounce_back_pct >= self.bounce_back_pct: 165 | # ** 表示的是乘方. 166 | self.cancel_all() # 清理其他卖单. 167 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_times 168 | # if self.account and self.account.available >= increase_pos_value: 169 | price = bar.close_price 170 | vol = increase_pos_value / price 171 | orderids = self.buy(Decimal(price), Decimal(vol)) 172 | self.buy_orders.extend(orderids) 173 | 174 | self.put_event() 175 | 176 | def on_order(self, order: OrderData): 177 | """ 178 | Callback of new order data update. 179 | """ 180 | if order.status == Status.ALLTRADED: 181 | if order.direction == Direction.LONG: 182 | # 买单成交. 183 | 184 | self.current_increase_pos_times += 1 185 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 186 | self.entry_lowest = float(order.price) 187 | 188 | if not order.is_active(): 189 | if order.vt_orderid in self.sell_orders: 190 | self.sell_orders.remove(order.vt_orderid) 191 | 192 | elif order.vt_orderid in self.buy_orders: 193 | self.buy_orders.remove(order.vt_orderid) 194 | 195 | self.put_event() # 更新UI使用. 196 | 197 | def on_trade(self, trade: TradeData): 198 | """ 199 | Callback of new trade data update. 200 | """ 201 | if trade.direction == Direction.LONG: 202 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 203 | self.current_pos += float(trade.volume) 204 | self.avg_price = total / self.current_pos 205 | elif trade.direction == Direction.SHORT: 206 | self.current_pos -= float(trade.volume) 207 | 208 | # 计算统计下总体的利润. 209 | self.total_profit += (float(trade.price) - self.avg_price) * float(trade.volume) - float(trade.volume) * float(trade.price) * 2 * self.trading_fee 210 | 211 | self.put_event() 212 | 213 | def on_stop_order(self, stop_order: StopOrder): 214 | """ 215 | Callback of stop order update. 216 | """ 217 | pass 218 | -------------------------------------------------------------------------------- /class_33/backtest.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy.backtesting import BacktestingEngine 2 | from howtrader.trader.object import Interval 3 | from datetime import datetime 4 | 5 | from strategies.martingle_spot_strategyV3 import MartingleSpotStrategyV3 6 | 7 | if __name__ == '__main__': 8 | engine = BacktestingEngine() 9 | 10 | engine.set_parameters( 11 | vt_symbol="btcusdt.BINANCE", # 现货的数据 12 | interval=Interval.MINUTE, 13 | start=datetime(2018,1,11), 14 | end=datetime(2018, 5, 1), 15 | # end=datetime(2020,12,1), 16 | rate=7.5/10000, # 币安手续费千分之1, BNB 万7.5 7.5/10000 17 | slippage=0, 18 | size=1, # 币本位合约 100 19 | pricetick=0.01, # 价格精度. 20 | capital=300000) 21 | 22 | 23 | engine.load_data() 24 | 25 | engine.add_strategy(MartingleSpotStrategyV3, {}) 26 | engine.run_backtesting() 27 | 28 | engine.calculate_result() # 计算回测的结果 29 | engine.calculate_statistics() # 计算一些统计指标 30 | 31 | engine.show_chart() # 绘制图表 32 | 33 | # 一个参数没法进行优化. 34 | # setttings = OptimizationSetting() 35 | # setttings.add_parameter("balance_diff_pct", start=0.001, end=0.10, step=0.001) 36 | # setttings.set_target("total_return") 37 | # result = engine.run_ga_optimization(setttings) 38 | # print(result) 39 | 40 | 41 | -------------------------------------------------------------------------------- /class_33/class_15.md: -------------------------------------------------------------------------------- 1 | # 第十五课: 均仓策略并回测 2 | 3 | 均仓策略是网格策略的一种变种。它原理就是通过不断平衡现金和持有的资产使其动态平衡。 4 | 5 | 假设你现在有20,000美金,btc的价格是18,000美金。 你如果全部梭哈了,只能买到 6 | 7 | 20,000/18,000 = 1.11个btc(不计算手续费), 但是你可能担心买在高位,不买又怕继续涨 8 | 这时候,你可以半仓操作买10000美金的btc,留一办的现金 9 | 10 | 如果你的网格或者动态调整仓位的比例是5%, 11 | 那么如果下跌的5%的时候回加点仓位,涨5%的时候会减少仓位 一直循环操作。 12 | 这个就是动态网格。 13 | 14 | ## 策略实现过程分析 15 | 1. 首先得知道你有多少本金,有多少币 16 | 2. 然后比较他们所持仓的价值,如果他们的价值超过一定的误差,我们就调整仓位 17 | 18 | ## howtrader里面的实现过程 19 | 1. 需要订阅我们的资产 20 | 21 | 22 | 23 | 24 | ## 均仓策略特点 25 | 1. 相比其他网格来说逻辑简单,算法也很简单. 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /class_33/main_window.py: -------------------------------------------------------------------------------- 1 | from howtrader.event import EventEngine 2 | 3 | from howtrader.trader.engine import MainEngine 4 | from howtrader.trader.ui import MainWindow, create_qapp 5 | 6 | from howtrader.gateway.binance import BinanceSpotGateway #现货 7 | from howtrader.gateway.binance import BinanceUsdtGateway # 合约 8 | 9 | from howtrader.app.cta_strategy import CtaStrategyApp # CTA策略 10 | from howtrader.app.data_manager import DataManagerApp # 数据管理, csv_data 11 | from howtrader.app.data_recorder import DataRecorderApp # 录行情数据 12 | from howtrader.app.algo_trading import AlgoTradingApp # 算法交易 13 | from howtrader.app.risk_manager import RiskManagerApp # 风控管理 14 | from howtrader.app.spread_trading import SpreadTradingApp # 价差交易 15 | 16 | 17 | def main(): 18 | """""" 19 | 20 | qapp = create_qapp() 21 | 22 | event_engine = EventEngine() 23 | 24 | main_engine = MainEngine(event_engine) 25 | 26 | main_engine.add_gateway(BinanceSpotGateway) 27 | main_engine.add_gateway(BinanceUsdtGateway) 28 | main_engine.add_app(CtaStrategyApp) 29 | main_engine.add_app(DataManagerApp) 30 | main_engine.add_app(AlgoTradingApp) 31 | main_engine.add_app(DataRecorderApp) 32 | main_engine.add_app(RiskManagerApp) 33 | main_engine.add_app(SpreadTradingApp) 34 | 35 | main_window = MainWindow(main_engine, event_engine) 36 | main_window.showMaximized() 37 | 38 | qapp.exec() 39 | 40 | 41 | if __name__ == "__main__": 42 | """ 43 | howtrader main window demo 44 | howtrader 的图形化界面 45 | 46 | we have binance gate way, which is for spot, while the binances gateway is for contract or futures. 47 | the difference between the spot and future is their symbol is just different. Spot uses the lower case for symbol, 48 | while the futures use the upper cases. 49 | 50 | 币安的接口有现货和合约接口之分。 他们之间的区别是通过交易对来区分的。现货用小写,合约用大写。 btcusdt.BINANCE 是现货的symbol, 51 | BTCUSDT.BINANCE合约的交易对。 BTCUSD.BINANCE是合约的币本位保证金的交易对. 52 | 53 | BTCUSDT, BTCUSDT 54 | """ 55 | 56 | main() -------------------------------------------------------------------------------- /class_33/strategies/grid_balance_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData, Offset 7 | from howtrader.trader.event import ( 8 | EVENT_ACCOUNT 9 | ) 10 | 11 | from howtrader.event import Event 12 | 13 | from howtrader.app.cta_strategy.engine import CtaEngine, EngineType 14 | from decimal import Decimal 15 | 16 | class GridBalanceStrategy(CtaTemplate): 17 | 18 | author = "51bitquant" 19 | 20 | balance_diff_pct = 0.01 21 | 22 | parameters = ["balance_diff_pct"] 23 | 24 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 25 | """""" 26 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 27 | 28 | if cta_engine.engine_type == EngineType.LIVE: 29 | self.cta_engine.event_engine.register(EVENT_ACCOUNT, self.process_account) 30 | 31 | self.my_balance = 300000 32 | def on_init(self): 33 | """ 34 | Callback when strategy is inited. 35 | """ 36 | self.write_log("策略初始化") 37 | self.load_bar(1) 38 | 39 | 40 | def on_start(self): 41 | """ 42 | Callback when strategy is started. 43 | """ 44 | self.write_log(f"我的策略启动, {self.trading}") 45 | self.put_event() 46 | 47 | 48 | def on_stop(self): 49 | """ 50 | Callback when strategy is stopped. 51 | """ 52 | self.write_log("策略停止") 53 | 54 | self.put_event() 55 | 56 | def on_tick(self, tick: TickData): 57 | pass 58 | 59 | def on_bar(self, bar: BarData): 60 | """ 61 | Callback of new bar data update. 62 | """ 63 | # print("1分钟的K线数据", bar) 64 | 65 | price = bar.close_price 66 | self.cancel_all() # 撤单. 67 | 68 | if self.my_balance <= 0: 69 | return 70 | 71 | if (abs(self.my_balance - float(self.pos) * price) / self.my_balance) >= self.balance_diff_pct: 72 | 73 | balance_diff = abs(self.my_balance - float(self.pos) * price) / 2 74 | # print('需要进行资金平衡.', balance_diff, self.my_balance, self.pos, price) 75 | if self.my_balance > float(self.pos) * price: 76 | self.buy(Decimal(price*1.001), Decimal(balance_diff/price)) 77 | else: 78 | self.sell(Decimal(price * 0.999), Decimal(balance_diff / price)) 79 | 80 | self.put_event() 81 | 82 | def process_account(self, event: Event): 83 | print("event account", event.data) 84 | 85 | def on_order(self, order: OrderData): 86 | """ 87 | 订单的回调方法: 订单状态更新的时候,会调用这个方法。 88 | """ 89 | 90 | self.put_event() 91 | 92 | 93 | def on_trade(self, trade: TradeData): 94 | """ 95 | 订单成交的推送,比如你下10个BTC,那么可能不会一下子成交,会不断慢慢的成交, 96 | 这时有成交它就会推送给你,告诉你成交了多少,还有多少没有成交 97 | 系统通过里面处理这个方法,知道你当前的仓位数量 98 | 99 | """ 100 | 101 | if trade.offset == Offset.OPEN: 102 | self.my_balance -= float(trade.price) * float(trade.volume) 103 | elif trade.offset == Offset.CLOSE: 104 | self.my_balance += float(trade.price) * float(trade.volume) 105 | 106 | self.put_event() # 更新UI界面方法。 107 | 108 | 109 | def on_stop_order(self, stop_order: StopOrder): 110 | """ 111 | 这个是一个停止单的方法,用来监听你止损单的方法。 112 | """ 113 | pass 114 | 115 | -------------------------------------------------------------------------------- /class_33/strategies/martingle_spot_strategy.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.app.cta_strategy.engine import CtaEngine 8 | from howtrader.trader.event import EVENT_TIMER 9 | from howtrader.event import Event 10 | from howtrader.trader.object import Status, Direction, Interval, ContractData, AccountData 11 | 12 | from typing import Optional 13 | from howtrader.trader.utility import BarGenerator, ArrayManager 14 | from decimal import Decimal 15 | 16 | class MartingleSpotStrategy(CtaTemplate): 17 | """ 18 | 1. 马丁策略. 19 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 20 | 币安合约邀请码:51bitquant 21 | https://github.com/51bitquant/course_codes 22 | """ 23 | author = "51bitquant" 24 | 25 | # 策略的核心参数. 26 | boll_window = 30 27 | boll_dev = 2.2 28 | 29 | increase_pos_when_dump_pct = 0.04 # 回撤多少加仓 30 | exit_profit_pct = 0.02 # 出场平仓百分比 2% 31 | initial_trading_value = 1000 # 首次开仓价值 1000USDT. 32 | trading_value_multiplier = 1.3 # 加仓的比例. 1000 1300 1300 * 1.3 33 | max_increase_pos_times = 10.0 # 最大的加仓次数 34 | trading_fee = 0.00075 35 | 36 | # 变量 37 | avg_price = 0.0 # 当前持仓的平均价格. 38 | last_entry_price = 0.0 # 上一次入场的价格. 39 | current_pos = 0.0 # 当前的持仓的数量. 40 | current_increase_pos_times = 0 # 当前的加仓的次数. 41 | 42 | # 统计总的利润. 43 | total_profit = 0 44 | 45 | parameters = ["boll_window", "boll_dev", "increase_pos_when_dump_pct", "exit_profit_pct", "initial_trading_value", 46 | "trading_value_multiplier", "max_increase_pos_times", "trading_fee"] 47 | 48 | variables = ["avg_price", "last_entry_price", "current_pos", "current_increase_pos_times", "total_profit"] 49 | 50 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 51 | """""" 52 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 53 | 54 | self.last_filled_order: Optional[OrderData] = None 55 | self.tick: Optional[TickData] = None 56 | self.contract: Optional[ContractData] = None 57 | self.account: Optional[AccountData] = None 58 | 59 | self.bg = BarGenerator(self.on_bar, 15, self.on_15min_bar, Interval.MINUTE) # 15分钟的数据. 60 | self.am = ArrayManager(60) # 默认是100,设置60 61 | # ArrayManager 62 | 63 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 64 | # 现货的资产订阅 65 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 66 | # # 合约的资产订阅 67 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCES.USDT", self.process_account_event) 68 | 69 | self.buy_orders = [] # 买单id列表。 70 | self.sell_orders = [] # 卖单id列表。 71 | self.min_notional = 11 # 最小的交易金额. 72 | 73 | def on_init(self): 74 | """ 75 | Callback when strategy is inited. 76 | """ 77 | self.write_log("策略初始化") 78 | self.load_bar(2) # 加载两天的数据. 79 | 80 | def on_start(self): 81 | """ 82 | Callback when strategy is started. 83 | """ 84 | self.write_log("策略启动") 85 | 86 | def on_stop(self): 87 | """ 88 | Callback when strategy is stopped. 89 | """ 90 | self.write_log("策略停止") 91 | 92 | def process_account_event(self, event: Event): 93 | self.account: AccountData = event.data 94 | # if self.account: 95 | # print( 96 | # f"self.account available: {self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 97 | 98 | def on_tick(self, tick: TickData): 99 | """ 100 | Callback of new tick data update. 101 | """ 102 | if tick and tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 103 | self.tick = tick 104 | self.bg.update_tick(tick) 105 | 106 | def on_bar(self, bar: BarData): 107 | """ 108 | Callback of new bar data update. 109 | """ 110 | 111 | if self.current_pos * bar.close_price >= self.min_notional: 112 | 113 | if len(self.sell_orders) <= 0 < self.avg_price: 114 | # 有利润平仓的时候 115 | profit_percent = bar.close_price / self.avg_price - 1 116 | if profit_percent >= self.exit_profit_pct: 117 | self.cancel_all() 118 | orderids = self.sell(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 119 | self.sell_orders.extend(orderids) 120 | 121 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 122 | dump_percent = self.last_entry_price / bar.close_price - 1 123 | if len( 124 | self.buy_orders) <= 0 and self.current_increase_pos_times <= self.max_increase_pos_times and dump_percent >= self.increase_pos_when_dump_pct: 125 | # ** 表示的是乘方. 126 | self.cancel_all() 127 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_times 128 | price = bar.close_price 129 | vol = increase_pos_value / price 130 | orderids = self.buy(Decimal(price), Decimal(vol)) 131 | self.buy_orders.extend(orderids) 132 | 133 | self.bg.update_bar(bar) 134 | 135 | def on_15min_bar(self, bar: BarData): 136 | 137 | am = self.am 138 | am.update_bar(bar) 139 | if not am.inited: 140 | return 141 | 142 | current_close = am.close_array[-1] 143 | last_close = am.close_array[-2] 144 | boll_up, boll_down = am.boll(self.boll_window, self.boll_dev, array=False) # 返回最新的布林带值. 145 | 146 | # 突破上轨 147 | if last_close <= boll_up < current_close: 148 | if len(self.buy_orders) == 0 and self.current_pos * bar.close_price < self.min_notional: # 每次下单要大于等于10USDT, 为了简单设置11USDT. 149 | # 这里没有仓位. 150 | self.cancel_all() 151 | # 重置当前的数据. 152 | self.current_increase_pos_times = 0 153 | self.avg_price = 0 154 | 155 | price = bar.close_price 156 | vol = self.initial_trading_value / price 157 | orderids = self.buy(Decimal(price), Decimal(vol)) 158 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 159 | 160 | self.put_event() 161 | 162 | def on_order(self, order: OrderData): 163 | """ 164 | Callback of new order data update. 165 | """ 166 | if order.status == Status.ALLTRADED: 167 | if order.direction == Direction.LONG: 168 | # 买单成交. 169 | self.current_increase_pos_times += 1 170 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 171 | 172 | if not order.is_active(): 173 | if order.vt_orderid in self.sell_orders: 174 | self.sell_orders.remove(order.vt_orderid) 175 | 176 | elif order.vt_orderid in self.buy_orders: 177 | self.buy_orders.remove(order.vt_orderid) 178 | 179 | self.put_event() # 更新UI使用. 180 | 181 | def on_trade(self, trade: TradeData): 182 | """ 183 | Callback of new trade data update. 184 | """ 185 | if trade.direction == Direction.LONG: 186 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 187 | self.current_pos += float(trade.volume) 188 | self.avg_price = total / self.current_pos 189 | elif trade.direction == Direction.SHORT: 190 | self.current_pos -= float(trade.volume) 191 | 192 | # 计算统计下总体的利润. 193 | self.total_profit += (float(trade.price) - self.avg_price) * float(trade.volume) - float(trade.volume) * float(trade.price) * 2 * self.trading_fee 194 | 195 | self.put_event() 196 | 197 | def on_stop_order(self, stop_order: StopOrder): 198 | """ 199 | Callback of stop order update. 200 | """ 201 | pass 202 | -------------------------------------------------------------------------------- /class_33/strategies/martingle_spot_strategyV2.py: -------------------------------------------------------------------------------- 1 | from howtrader.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder 4 | ) 5 | 6 | from howtrader.trader.object import TickData, BarData, TradeData, OrderData 7 | from howtrader.app.cta_strategy.engine import CtaEngine 8 | from howtrader.trader.object import Status, Direction, ContractData, AccountData 9 | 10 | from typing import Optional 11 | from howtrader.trader.utility import ArrayManager, BarGenerator 12 | from decimal import Decimal 13 | 14 | class MartingleSpotStrategyV2(CtaTemplate): 15 | """ 16 | 1. 马丁策略. 17 | 币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant 18 | 币安合约邀请码:51bitquant 19 | """ 20 | 21 | """ 22 | 1. 开仓条件是 最高价回撤一定比例 4% 23 | 2. 止盈2% 24 | 3. 加仓: 入场后, 价格最低下跌超过5%, 最低点反弹上去1%, 那么就可以加仓. 均价止盈2%. 25 | """ 26 | author = "51bitquant" 27 | 28 | # 策略的核心参数. 29 | donchian_window = 2880 # two days 30 | open_pos_when_drawdown_pct = 0.04 # 最高值回撤2%时开仓. 31 | 32 | dump_down_pct = 0.04 # 33 | bounce_back_pct = 0.01 # 34 | 35 | exit_profit_pct = 0.02 # 出场平仓百分比 2% 36 | initial_trading_value = 1000 # 首次开仓价值 1000USDT. 37 | trading_value_multiplier = 1.3 # 加仓的比例. 38 | max_increase_pos_times = 7 # 最大的加仓次数 39 | trading_fee = 0.00075 40 | 41 | # 变量 42 | avg_price = 0.0 # 当前持仓的平均价格. 43 | last_entry_price = 0.0 # 上一次入场的价格. 44 | current_pos = 0.0 # 当前的持仓的数量. 45 | current_increase_pos_times = 0 # 当前的加仓的次数. 46 | 47 | upband = 0.0 48 | downband = 0.0 49 | entry_lowest = 0.0 # 进场之后的最低价. 50 | 51 | # 统计总的利润. 52 | total_profit = 0 53 | 54 | parameters = ["donchian_window", "open_pos_when_drawdown_pct", "dump_down_pct", "bounce_back_pct", 55 | "exit_profit_pct", "initial_trading_value", 56 | "trading_value_multiplier", "max_increase_pos_times", "trading_fee"] 57 | 58 | variables = ["avg_price", "last_entry_price", "current_pos", "current_increase_pos_times", 59 | "upband", "downband", "entry_lowest", "total_profit"] 60 | 61 | def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting): 62 | """""" 63 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 64 | 65 | self.last_filled_order: Optional[OrderData] = None 66 | self.tick: Optional[TickData] = None 67 | self.contract: Optional[ContractData] = None 68 | self.account: Optional[AccountData] = None 69 | self.bg = BarGenerator(self.on_bar) # generate 1min bar. 70 | self.am = ArrayManager(3000) # 默认是100,设置3000 71 | 72 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event) 73 | # self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event) 74 | 75 | self.buy_orders = [] # 买单id列表。 76 | self.sell_orders = [] # 卖单id列表。 77 | self.min_notional = 11 # 最小的交易金额. 78 | 79 | def on_init(self): 80 | """ 81 | Callback when strategy is inited. 82 | """ 83 | self.write_log("策略初始化") 84 | self.load_bar(3) # 加载3天的数据. 85 | 86 | def on_start(self): 87 | """ 88 | Callback when strategy is started. 89 | """ 90 | self.write_log("策略启动") 91 | 92 | def on_stop(self): 93 | """ 94 | Callback when strategy is stopped. 95 | """ 96 | self.write_log("策略停止") 97 | 98 | # def process_account_event(self, event: Event): 99 | # self.account: AccountData = event.data 100 | # if self.account: 101 | # print( 102 | # f"self.account: available{self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}") 103 | 104 | def on_tick(self, tick: TickData): 105 | """ 106 | Callback of new tick data update. 107 | """ 108 | if tick.bid_price_1 > 0 and tick.ask_price_1 > 0: 109 | self.tick = tick 110 | self.bg.update_tick(tick) 111 | 112 | def on_bar(self, bar: BarData): 113 | """ 114 | Callback of new bar data update. 115 | """ 116 | am = self.am 117 | am.update_bar(bar) 118 | if not am.inited: 119 | return 120 | 121 | current_close = am.close_array[-1] 122 | current_low = am.low_array[-1] 123 | 124 | self.upband, self.downband = am.donchian(self.donchian_window, array=False) # 返回最新的布林带值. 125 | 126 | dump_pct = self.upband / current_low - 1 127 | 128 | if self.entry_lowest > 0: 129 | self.entry_lowest = min(self.entry_lowest, bar.low_price) 130 | 131 | # 回调一定比例的时候. 132 | if self.current_pos * current_close < self.min_notional: 133 | # 每次下单要大于等于10USDT, 为了简单设置11USDT. 134 | if dump_pct >= self.open_pos_when_drawdown_pct and len(self.buy_orders) == 0: 135 | # 这里没有仓位. 136 | # 重置当前的数据. 137 | self.cancel_all() 138 | self.current_increase_pos_times = 0 139 | self.avg_price = 0 140 | self.entry_lowest = 0 141 | 142 | price = current_close 143 | vol = self.initial_trading_value / price 144 | orderids = self.buy(price, vol) 145 | self.buy_orders.extend(orderids) # 以及已经下单的orderids. 146 | else: 147 | 148 | if len(self.sell_orders) <= 0 and self.avg_price > 0: 149 | # 有利润平仓的时候 150 | # 清理掉其他买单. 151 | 152 | profit_percent = bar.close_price / self.avg_price - 1 153 | if profit_percent >= self.exit_profit_pct: 154 | self.cancel_all() 155 | orderids = self.sell(Decimal(bar.close_price), Decimal(abs(self.current_pos))) 156 | self.sell_orders.extend(orderids) 157 | 158 | if self.entry_lowest > 0 and len(self.buy_orders) <= 0: 159 | # 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。 160 | 161 | dump_down_pct = self.last_entry_price / self.entry_lowest - 1 162 | bounce_back_pct = bar.close_price / self.entry_lowest - 1 163 | 164 | if self.current_increase_pos_times <= self.max_increase_pos_times and dump_down_pct >= self.dump_down_pct and bounce_back_pct >= self.bounce_back_pct: 165 | # ** 表示的是乘方. 166 | self.cancel_all() # 清理其他卖单. 167 | increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_times 168 | # if self.account and self.account.available >= increase_pos_value: 169 | price = bar.close_price 170 | vol = increase_pos_value / price 171 | orderids = self.buy(Decimal(price), Decimal(vol)) 172 | self.buy_orders.extend(orderids) 173 | 174 | self.put_event() 175 | 176 | def on_order(self, order: OrderData): 177 | """ 178 | Callback of new order data update. 179 | """ 180 | if order.status == Status.ALLTRADED: 181 | if order.direction == Direction.LONG: 182 | # 买单成交. 183 | 184 | self.current_increase_pos_times += 1 185 | self.last_entry_price = float(order.price) # 记录上一次成绩的价格. 186 | self.entry_lowest = float(order.price) 187 | 188 | if not order.is_active(): 189 | if order.vt_orderid in self.sell_orders: 190 | self.sell_orders.remove(order.vt_orderid) 191 | 192 | elif order.vt_orderid in self.buy_orders: 193 | self.buy_orders.remove(order.vt_orderid) 194 | 195 | self.put_event() # 更新UI使用. 196 | 197 | def on_trade(self, trade: TradeData): 198 | """ 199 | Callback of new trade data update. 200 | """ 201 | if trade.direction == Direction.LONG: 202 | total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume) 203 | self.current_pos += float(trade.volume) 204 | self.avg_price = total / self.current_pos 205 | elif trade.direction == Direction.SHORT: 206 | self.current_pos -= float(trade.volume) 207 | 208 | # 计算统计下总体的利润. 209 | self.total_profit += (float(trade.price) - self.avg_price) * float(trade.volume) - float(trade.volume) * float(trade.price) * 2 * self.trading_fee 210 | 211 | self.put_event() 212 | 213 | def on_stop_order(self, stop_order: StopOrder): 214 | """ 215 | Callback of stop order update. 216 | """ 217 | pass 218 | --------------------------------------------------------------------------------