Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Howtrader: A crypto quant framework for developing, backtesting, and executing your own trading strategies. Seamlessly integrates with TradingView and other third-party signals. Simply send a post request to automate trading and order placement. Supports Binance and Okex exchanges.

License

NotificationsYou must be signed in to change notification settings

51bitquant/howtrader

Repository files navigation

中文文档

What does Howtrader means? It means how to be a trader, especially aquant trader.

HowTrader crypto quantitative trading framework, and was forked fromVNPY, so the core codes、functions and useages are pretty similar toVNPY, but would be more easy to install and use. By the way, Howtraderfixes some bugs, add more functions and strategies to framework. Weextend the TradingView signals and grid strategies to Howtrader and morecodes.

Howtrader VS VNPY

  1. some classes' definition are pretty different: for OrderData、TradeData、ContractData, we replace float with Decimal to meet theprecision, theses classes are defined in howtrader.trader.objectmodule.

  2. on_order and on_trade update sequence are different: in VNPY, ,theon_order is always updated before the on_trade if the order wastraded. And in the cta strategy, the self.pos(the position data) wascalculated in the on_trade callback. So if we want to get the latestself.pos value, we may need to define a variable to calculate thelatest position data. To solve this issue, we push the on_tradebefore the on_order callback. check out the code to find out thedetails:

    defon_order(self,order:OrderData)->None:"""on order update"""order.update_time=generate_datetime(time.time()*1000)last_order:OrderData=self.get_order(order.orderid)ifnotlast_order:self.orders[order.orderid]=copy(order)super().on_order(copy(order))else:traded:Decimal=order.traded-last_order.tradediftraded<0:# filter the order is not in sequencereturnNoneiftraded>0:trade:TradeData=TradeData(symbol=order.symbol,exchange=order.exchange,orderid=order.orderid,direction=order.direction,price=order.price,volume=traded,datetime=order.update_time,gateway_name=self.gateway_name,             )super().on_trade(trade)iftraded==0andorder.status==last_order.status:returnNoneself.orders[order.orderid]=copy(order)super().on_order(copy(order))defget_order(self,orderid:str)->OrderData:"""get order by order id"""returnself.orders.get(orderid,None)
  3. gateways are different: solve issues like disconnecting fromexchange, and reconnecting.

  4. TradingView app to receive other 3rd party signals and algo tradingstrategies, checkout the module: howtrader.app.tradingview

Installation

the framework depends on pandas, Numpy libraries, so we higly recommendyou to installAnaconda.

  1. Install Anaconda, the Anaconda download website ishere, remember toclick "Add Conda to System Path" in the process of AnacondaInstallation. If you forget to do so, you might need to unistall andreinstall the Anaconda, or just search how to add conda to you systempath, if you encounter the problem of "not found conda command".

    If you already install the anaconda before, you can simply updateyour conda by runing the following command:

    conda update conda

    conda update anaconda

  2. install git

Install git is pretty simple, just download git from[https://git-scm.com/downloads], then install it, remember to add git toyour system path. If you're using MacOX, git was integrated into thesystem, so you may not need to install git.3. create virtual env by conda

conda create -n mytrader python==3.9

mytrader is the name of your virtual env, if you have the mytradervirtual env before and the version is not 3.9, you can unistall it byfollowing command:

conda remove -n mytrader --all

if you encounter an error, you may need to deactivate the mytrader bythe command:

conda deactivate

then remove it:

conda remove -n mytrader --all

  1. activate your virtual env:

conda activate mytrader

  1. install howtrader

run the command:

pip install git+https://github.com/51bitquant/howtrader.git

if you want to update to the latest version, use the command:

pip install git+https://github.com/51bitquant/howtrader.git -U

if encounter the failure of installation TA-Lib, checkout the next step.

Install TA-Lib

If you can't install the howtrader in Window system, the main reason maybe the TA-Lib, here we go in to the detail of installing TA-Lib:

  1. open this url in your browser:https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib

  2. search ta-lib in the website and download the TA-Lib.whl file: herewe found the TA_Lib‑0.4.24‑cp39‑cp39‑win_amd64.whl in the website andwe click download, for the TA_Lib-0.4.24, it means TA-Lib version is0.4.24, cp39 is the python version is 3.9, amd64 your systemversion is 64 bit, choose the right one and download it.

  3. change your directory to your TA-Lib folder, and remember to activateyour virtual env to mytrader(if you use mytrader as your vitrualenv), then execute the following command:

pip install TA_Lib‑0.4.24‑cp39‑cp39‑win_amd64.whl

how-to-use

create a python proejct, and create a main.py file,and set yourproject's interpreter to the mytrader(the virtual env you just createand config).

then copy and paste the following code to main.py and run it, and youwill see a howtrader folder beside the main.py, the howtrader foldercontains the configs data(like exchange api key)、 your strategysettings and datas etc.

fromhowtrader.eventimportEventEngine,Eventfromhowtrader.trader.eventimportEVENT_TV_SIGNALfromhowtrader.trader.engineimportMainEnginefromhowtrader.trader.uiimportMainWindow,create_qappfromhowtrader.trader.settingimportSETTINGSfromhowtrader.gateway.binanceimportBinanceUsdtGateway,BinanceSpotGateway,BinanceInverseGatewayfromhowtrader.app.cta_strategyimportCtaStrategyApp# from howtrader.app.data_manager import DataManagerApp# from howtrader.app.data_recorder import DataRecorderApp# from howtrader.app.algo_trading import AlgoTradingApp# from howtrader.app.risk_manager import RiskManagerApp# from howtrader.app.spread_trading import SpreadTradingAppfromhowtrader.app.tradingviewimportTradingViewAppfromthreadingimportThreadimportjsonfromflaskimportFlask,request# create global event_engineevent_engine:EventEngine=EventEngine()passphrase=SETTINGS.get("passphrase","")port=SETTINGS.get("port",9999)app=Flask(__name__)@app.route('/',methods=['GET'])defwelcome():return"Hi, this is tv server!"@app.route('/webhook',methods=['POST'])defwebhook():try:data=json.loads(request.data)ifdata.get('passphrase',None)!=passphrase:return {"status":"failure","msg":"passphrase is incorrect"}deldata['passphrase']# del it for safety.event:Event=Event(type=EVENT_TV_SIGNAL,data=data)event_engine.put(event)return {"status":"success","msg":""}exceptExceptionaserror:return {"status":"error","msg":str(error)}defstart_tv_server():app.run(host="127.0.0.1",port=port)defmain():""""""qapp=create_qapp()main_engine=MainEngine(event_engine)main_engine.add_gateway(BinanceSpotGateway)main_engine.add_gateway(BinanceUsdtGateway)main_engine.add_gateway(BinanceInverseGateway)main_engine.add_app(CtaStrategyApp)main_engine.add_app(TradingViewApp)# if you don't use# main_engine.add_app(DataManagerApp)# main_engine.add_app(AlgoTradingApp)# main_engine.add_app(DataRecorderApp)# main_engine.add_app(RiskManagerApp)# main_engine.add_app(SpreadTradingApp)main_window=MainWindow(main_engine,event_engine)main_window.showMaximized()t1=Thread(target=start_tv_server)t1.daemon=Truet1.start()qapp.exec()if__name__=="__main__":main()

how-to-crawl binance kline data

howdtrader use sliqte database as default, and also support mongodb andmysql if you want to use it. If you want to use other database, you canchange the configuration in howtrader/vt_setting.json, here is the fullconfiguration key, you can just config the corresponding values.

{    "font.family": "", # set font family if display error    "font.size": 12,    "log.active": True,    "log.level": CRITICAL,    "log.console": True,    "log.file": True,    "email.server": "smtp.qq.com",    "email.port": 465,    "email.username": "",    "email.password": "",    "email.sender": "",    "email.receiver": "",    "order_update_interval": 600, # securing correct orders' status by synchronizing/updating orders through rest api    "update_server_time_interval": 300,  # sync with server time    "passphrase": "howtrader",  # tv passphrase    "port": 9999, # tv server port    "datafeed.name": "",    "datafeed.username": "",    "datafeed.password": "",    "database.timezone": get_localzone_name(),    "database.name": "sqlite",    "database.database": "database.db",    "database.host": "",    "database.port": 0,    "database.user": "",    "database.password": ""}

to crawl the Binance exchange kline data for backtesting, you just needto create a crawl_data.py file, just in the main.py directory. copy andpaste the following codes:

"""use the binance api to crawl data then save into the sqlite database."""import pandas as pdimport timefrom datetime import datetimeimport requestsimport pytzfrom howtrader.trader.database import get_database, BaseDatabasepd.set_option('expand_frame_repr', False)  #from howtrader.trader.object import BarData, Interval, ExchangeBINANCE_SPOT_LIMIT = 1000BINANCE_FUTURE_LIMIT = 1500from howtrader.trader.constant import LOCAL_TZfrom threading import Threaddatabase: BaseDatabase = get_database()def generate_datetime(timestamp: float) -> datetime:    """    :param timestamp:    :return:    """    dt = datetime.fromtimestamp(timestamp / 1000)    dt = LOCAL_TZ.localize(dt)    return dtdef get_binance_data(symbol: str, exchange: str, start_time: str, end_time: str):    """    crawl binance exchange data    :param symbol: BTCUSDT.    :param exchange: spot、usdt_future, inverse_future.    :param start_time: format :2020-1-1 or 2020-01-01 year-month-day    :param end_time: format: 2020-1-1 or 2020-01-01 year-month-day    :param gate_way the gateway name for binance is:BINANCE_SPOT, BINANCE_USDT, BINANCE_INVERSE    :return:    """    api_url = ''    save_symbol = symbol    gateway = "BINANCE_USDT"    if exchange == 'spot':        print("spot")        limit = BINANCE_SPOT_LIMIT        save_symbol = symbol.lower()        gateway = 'BINANCE_SPOT'        api_url = f'https://api.binance.com/api/v3/klines?symbol={symbol}&interval=1m&limit={limit}'    elif exchange == 'usdt_future':        print('usdt_future')        limit = BINANCE_FUTURE_LIMIT        gateway = "BINANCE_USDT"        api_url = f'https://fapi.binance.com/fapi/v1/klines?symbol={symbol}&interval=1m&limit={limit}'    elif exchange == 'inverse_future':        print("inverse_future")        limit = BINANCE_FUTURE_LIMIT        gateway = "BINANCE_INVERSE"        f'https://dapi.binance.com/dapi/v1/klines?symbol={symbol}&interval=1m&limit={limit}'    else:        raise Exception('the exchange name should be one of :spot, usdt_future, inverse_future')    start_time = int(datetime.strptime(start_time, '%Y-%m-%d').timestamp() * 1000)    end_time = int(datetime.strptime(end_time, '%Y-%m-%d').timestamp() * 1000)    while True:        try:            print(start_time)            url = f'{api_url}&startTime={start_time}'            print(url)            datas = requests.get(url=url, timeout=10, proxies=proxies).json()            """            [                [                    1591258320000,      // open time                    "9640.7",           // open price                    "9642.4",           // highest price                    "9640.6",           // lowest price                    "9642.0",           // close price(latest price if the kline is not close)                    "206",              // volume                    1591258379999,      // close time                    "2.13660389",       // turnover                    48,                 // trade count                     "119",              // buy volume                    "1.23424865",       //  buy turnover                    "0"                 // ignore                ]            """            buf = []            for row in datas:                bar: BarData = BarData(                    symbol=save_symbol,                    exchange=Exchange.BINANCE,                    datetime=generate_datetime(row[0]),                    interval=Interval.MINUTE,                    volume=float(row[5]),                    turnover=float(row[7]),                    open_price=float(row[1]),                    high_price=float(row[2]),                    low_price=float(row[3]),                    close_price=float(row[4]),                    gateway_name=gateway                )                buf.append(bar)            database.save_bar_data(buf)            # exit the loop, if close time is greater than the current time            if (datas[-1][0] > end_time) or datas[-1][6] >= (int(time.time() * 1000) - 60 * 1000):                break            start_time = datas[-1][0]        except Exception as error:            print(error)            time.sleep(10)def download_spot(symbol):    """    download binance spot data, config your start date and end date(format: year-month-day)    :return:    """    t1 = Thread(target=get_binance_data, args=(symbol, 'spot', "2018-1-1", "2018-6-1"))    t2 = Thread(target=get_binance_data, args=(symbol, 'spot', "2018-6-1", "2018-12-1"))    t3 = Thread(target=get_binance_data, args=(symbol, 'spot', "2018-12-1", "2019-6-1"))    t4 = Thread(target=get_binance_data, args=(symbol, 'spot', "2019-6-1", "2019-12-1"))    t5 = Thread(target=get_binance_data, args=(symbol, 'spot', "2019-12-1", "2020-6-1"))    t6 = Thread(target=get_binance_data, args=(symbol, 'spot', "2020-6-1", "2020-12-1"))    t7 = Thread(target=get_binance_data, args=(symbol, 'spot', "2020-12-1", "2021-6-1"))    t8 = Thread(target=get_binance_data, args=(symbol, 'spot', "2021-6-1", "2021-12-1"))    t9 = Thread(target=get_binance_data, args=(symbol, 'spot', "2021-12-1", "2022-6-28"))    t1.start()    t2.start()    t3.start()    t4.start()    t5.start()    t6.start()    t7.start()    t8.start()    t9.start()    t1.join()    t2.join()    t3.join()    t4.join()    t5.join()    t6.join()    t7.join()    t8.join()    t9.join()def download_future(symbol):    """    download binance future data, config your start date and end date(format: year-month-day)    :return:    """    t1 = Thread(target=get_binance_data, args=(symbol, 'usdt_future', "2020-1-1", "2020-6-1"))    t2 = Thread(target=get_binance_data, args=(symbol, 'usdt_future', "2020-6-1", "2020-12-1"))    t3 = Thread(target=get_binance_data, args=(symbol, 'usdt_future', "2020-12-1", "2021-6-1"))    t4 = Thread(target=get_binance_data, args=(symbol, 'usdt_future', "2021-6-1", "2021-12-1"))    t5 = Thread(target=get_binance_data, args=(symbol, 'usdt_future', "2021-12-1", "2022-6-28"))    t1.start()    t2.start()    t3.start()    t4.start()    t5.start()    t1.join()    t2.join()    t3.join()    t4.join()    t5.join()if __name__ == '__main__':    """    read the code before run it. the software crawl the binance data then save into the sqlite database.    you may need to change the start date and end date.    """    # proxy_host , if you can directly connect to the binance exchange, then set it to None or empty string "",如果没有你就设置为 None 或者空的字符串 "",    # you can use the command  ping api.binance.com to check whether your network work well: 你可以在终端运行 ping api.binance.com 查看你的网络是否正常。    proxy_host = "127.0.0.1"  # set it to your proxy_host 如果没有就设置为"", 如果有就设置为你的代理主机如:127.0.0.1    proxy_port = 1087  # set it to your proxy_port  设置你的代理端口号如: 1087, 没有你修改为0,但是要保证你能访问api.binance.com这个主机。    proxies = None    if proxy_host and proxy_port:        proxy = f'http://{proxy_host}:{proxy_port}'        proxies = {'http': proxy, 'https': proxy}    download_future(symbol="BTCUSDT")  # crawl usdt_future data. 下载合约的数据    download_spot(symbol="BTCUSDT") # crawl binance spot data.

if you want to change start date or end date, you check out the codes.And the data will be stored in howtrader/database.db file.

backtesting

for backtesting, here is the example. vt_symbol format likeBTCUSDT.BINANCE, the first part is symbol, and other part is exchangename. Howtrader uses lower case for spot market, and upper case forfuture market.

from howtrader.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSettingfrom howtrader.trader.object import Intervalfrom datetime import datetimefrom strategies.atr_rsi_strategy import AtrRsiStrategy  # import your strategy.engine = BacktestingEngine()engine.set_parameters(    vt_symbol="BTCUSDT.BINANCE",    interval=Interval.MINUTE,    start=datetime(2020, 1, 1),    end=datetime(2020, 5, 1),    rate=4/10000,    slippage=0,    size=1,    pricetick=0.01,    capital=1000000,)engine.add_strategy(AtrRsiStrategy, {})engine.load_data()engine.run_backtesting()df = engine.calculate_result()engine.calculate_statistics()engine.show_chart()setting = OptimizationSetting()setting.set_target("sharpe_ratio")setting.add_parameter("atr_length", 3, 39, 1)setting.add_parameter("atr_ma_length", 10, 30, 1)result = engine.run_ga_optimization(setting)  # optimization resultprint(result) # you can print the result.

for more example codes, checkout the examples:https://github.com/51bitquant/howtrader/tree/main/examples

material codes:

you can check out my github profile:https:github.com/51bitquant

howtrader course_codes:https://github.com/51bitquant/course_codes

Contact

Twitter: 51bitquant.eth

discord-community:https://discord.gg/fgySfwG9eJ

Binance Invite Link:https://www.binancezh.pro/cn/futures/ref/51bitquant

About

Howtrader: A crypto quant framework for developing, backtesting, and executing your own trading strategies. Seamlessly integrates with TradingView and other third-party signals. Simply send a post request to automate trading and order placement. Supports Binance and Okex exchanges.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp