├── .gitignore
├── README.md
├── backtest.py
├── chart
├── chart.py
├── cycle_indicators.py
├── momentum_indicators.py
├── other_indicators.py
├── overlap_studies.py
├── pattern_recognition.py
├── price_transform.py
├── statistic_functions.py
├── volatility_indicators.py
└── volume_indicators.py
├── common
├── __init__.py
├── bill.py
├── instance.py
├── kline.py
├── log.py
├── signal.py
└── xquant.py
├── db
├── __init__.py
└── mongodb.py
├── engine
├── __init__.py
├── backtestengine.py
├── engine.py
├── order.py
├── realengine.py
└── signalengine.py
├── exchange
├── __init__.py
├── binance
│ ├── __init__.py
│ ├── client.py
│ ├── depthcache.py
│ ├── enums.py
│ ├── exceptions.py
│ ├── future.py
│ ├── margin.py
│ └── websockets.py
├── binanceExchange.py
├── binanceFuture.py
├── binanceMargin.py
├── exchange.py
├── kuaiqiBroker.py
├── okex
│ ├── Client.py
│ ├── HttpMD5Util.py
│ ├── OkcoinFutureAPI.py
│ ├── OkcoinSpotAPI.py
│ └── __init__.py
└── okexExchange.py
├── importer
├── binance.py
├── check.py
├── download.py
├── download.sh
├── download2.py
├── download_binance.sh
├── download_kuaiqi.sh
├── fix.py
├── fixKline.py
└── importer.py
├── md
├── dbmd.py
├── exmd.py
└── md.py
├── monitor
├── daily_report.py
├── daily_report.sh
├── insert_trade.py
├── monitor.py
├── monitor.sh
└── template
│ └── template.html
├── real.py
├── real2.py
├── scripts
└── auto_repay.sh
├── setup.py
├── strategy
├── __init__.py
├── kd
│ ├── kd.py
│ └── kd_btc_usdt.jsn
└── strategy.py
├── tests
├── binance
│ ├── bnFuture_test.py
│ ├── bnMargin_test.py
│ ├── future_test.py
│ ├── margin_test.py
│ └── spot_test.py
└── md_test.py
├── tools
├── account.py
├── auto_repay.py
├── balance.py
├── order.py
├── position.py
├── show.py
├── slippage.py
├── strategy.py
└── trade.py
├── utils
├── __init__.py
├── email_obj.py
├── indicator.py
├── indicator_test.py
├── tal.py
├── ti.py
├── ti_test.py
└── tools.py
└── xlib
└── step.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 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
106 | .vscode
107 |
108 | # xquant
109 | strategies
110 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xquant
2 | 量化交易
3 |
4 | ## 运行环境
5 | python3.6
6 | mongodb3.6
7 | ## 实盘
8 | python3 real.py -e binance -sc strategy/kd/kd_btc_usdt.jsn -sii testkdj -v 500 --log
9 | ## 回测
10 | python3 backtest.py -m binance -sc strategy/kd/kd_btc_usdt.jsn -r 2020-4-1~2020-4-12
11 | ## 目录说明
12 | ### utils
13 | 工具,不仅限于本项目
14 | ### common
15 | 本项目共用
16 | ### db
17 | 数据库
18 | 目前支持mongodb
19 | ### exchange
20 | 交易所
21 | 目前完全支持binance(现货、保证金杠杆、期货合约,都支持)
22 | 部分支持okex(调试未完成)
23 | ### md
24 | 市场行情数据
25 | 目前支持本地数据库行情数据、交易所实时行情数据
26 | ### engine
27 | 引擎
28 | 目前已经支持实盘、历史回测
29 | 实时仿真待续...
30 | ### strategy
31 | 策略库
32 |
33 |
--------------------------------------------------------------------------------
/chart/cycle_indicators.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import pandas as pd
4 | import talib
5 |
6 | import utils.tools as ts
7 | import utils.indicator as ic
8 |
9 |
10 | def add_argument_cycle_indicators(parser):
11 |
12 | # talib
13 | group = parser.add_argument_group('Cycle Indicators (TaLib)')
14 | group.add_argument('--HT_DCPERIOD' , action="store_true", help='Hilbert Transform - Dominant Cycle Period')
15 | group.add_argument('--HT_DCPHASE' , action="store_true", help='Hilbert Transform - Dominant Cycle Phase')
16 | group.add_argument('--HT_PHASOR' , action="store_true", help='Hilbert Transform - Phasor Components')
17 | group.add_argument('--HT_SINE' , action="store_true", help='Hilbert Transform - SineWave')
18 | group.add_argument('--HT_TRENDMODE', action="store_true", help='Hilbert Transform - Trend vs Cycle Mode')
19 |
20 |
21 | def get_cycle_indicators_count(args):
22 | count = 0
23 |
24 | if args.HT_DCPERIOD:
25 | count += 1
26 | if args.HT_DCPHASE:
27 | count += 1
28 | if args.HT_PHASOR:
29 | count += 1
30 | if args.HT_SINE:
31 | count += 1
32 | if args.HT_TRENDMODE:
33 | count += 1
34 |
35 | return count
36 |
37 |
38 | def handle_cycle_indicators(args, axes, i, klines_df, close_times, display_count):
39 | # talib
40 | if args.HT_DCPERIOD:
41 | name = 'HT_DCPERIOD'
42 | real = talib.HT_DCPERIOD(klines_df["close"])
43 | i += 1
44 | axes[i].set_ylabel(name)
45 | axes[i].grid(True)
46 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
47 |
48 | if args.HT_DCPHASE:
49 | name = 'HT_DCPHASE'
50 | real = talib.HT_DCPHASE(klines_df["close"])
51 | i += 1
52 | axes[i].set_ylabel(name)
53 | axes[i].grid(True)
54 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
55 |
56 | if args.HT_PHASOR:
57 | name = 'HT_PHASOR'
58 | real = talib.HT_PHASOR(klines_df["close"])
59 | i += 1
60 | axes[i].set_ylabel(name)
61 | axes[i].grid(True)
62 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
63 |
64 | if args.HT_SINE:
65 | name = 'HT_SINE'
66 | real = talib.HT_SINE(klines_df["close"])
67 | i += 1
68 | axes[i].set_ylabel(name)
69 | axes[i].grid(True)
70 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
71 |
72 | if args.HT_TRENDMODE:
73 | name = 'HT_TRENDMODE'
74 | real = talib.HT_TRENDMODE(klines_df["close"])
75 | i += 1
76 | axes[i].set_ylabel(name)
77 | axes[i].grid(True)
78 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
79 |
80 |
81 |
--------------------------------------------------------------------------------
/chart/other_indicators.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import pandas as pd
4 | import talib
5 |
6 | import utils.indicator as ic
7 | from .overlap_studies import plot_colors
8 |
9 |
10 | def add_argument_other_indicators(parser):
11 | # other Indicators
12 |
13 | group = parser.add_argument_group('Other Indicators')
14 | group.add_argument('--nBIAS', type=int, nargs='*', help='Bias Ratio: close/ma - 1')
15 | group.add_argument('--nmBIAS', type=int, nargs='*', help='Bias Ratio: nBIAS - mBIAS')
16 | group.add_argument('--BIAS', type=int, nargs='*', help='Bias Ratio: ma_short/ma_long - 1')
17 |
18 |
19 | def get_other_indicators_count(args):
20 | count = 0
21 |
22 | if args.nBIAS is not None or args.BIAS is not None:
23 | count += 1
24 | if args.nmBIAS is not None:
25 | count += 1
26 |
27 | return count
28 |
29 |
30 | def handle_other_indicators(args, axes, i, klines_df, close_times, display_count):
31 | if args.nBIAS is not None or args.BIAS is not None:
32 | name = 'BIAS'
33 | i += 1
34 | axes[i].grid(True)
35 |
36 | if args.nBIAS is not None:
37 | if len(args.nBIAS) == 0:
38 | tps = [55]
39 | else:
40 | tps = args.nBIAS
41 |
42 | name = "%s %s "%(name, tps)
43 | axes[i].set_ylabel(name)
44 | for idx, tp in enumerate(tps):
45 | ta_emas = talib.EMA(klines_df["close"], tp)
46 | real = ic.pd_biases(pd.to_numeric(klines_df["close"]), ta_emas)
47 | axes[i].plot(close_times, real[-display_count:], plot_colors[idx], label=tp)
48 |
49 | if args.BIAS is not None:
50 | if len(args.BIAS) == 0:
51 | tps = [13, 55]
52 | else:
53 | tps = args.BIAS
54 | name += " %s" % tps
55 | axes[i].set_ylabel(name)
56 | idx = 0
57 | while idx < len(tps)-1:
58 | emas_s = talib.EMA(klines_df["close"], tps[idx])
59 | emas_l = talib.EMA(klines_df["close"], tps[idx+1])
60 | real = ic.pd_biases(emas_s, emas_l)
61 | axes[i].plot(close_times, real[-display_count:], plot_colors[idx//2]+"--", label="%d-%d"%(tps[idx], tps[idx+1]))
62 | idx += 2
63 |
64 | if args.nmBIAS is not None:
65 | name = 'nmBIAS'
66 | i += 1
67 | axes[i].grid(True)
68 |
69 | if len(args.nmBIAS) == 0:
70 | tps = [13, 55]
71 | else:
72 | tps = args.nBIAS
73 | cs = ["r", "y", "b", "m"]
74 | axes[i].set_ylabel("%s%s"%(name, tps))
75 |
76 | closes = pd.to_numeric(klines_df["close"])
77 | emas_s = talib.EMA(closes, tps[0])
78 | emas_l = talib.EMA(closes, tps[1])
79 | real = ic.pd_biases(closes, emas_s) - ic.pd_biases(closes, emas_l)
80 | axes[i].plot(close_times, real[-display_count:], cs[0], label=tps)
81 |
82 |
--------------------------------------------------------------------------------
/chart/overlap_studies.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import talib
3 |
4 | plot_colors = ['y', 'r', 'b', 'm', 'k']
5 |
6 | def add_argument_overlap_studies(parser):
7 | # Overlap Studies
8 | group = parser.add_argument_group('Overlap Studies')
9 |
10 | group.add_argument('--ABANDS', nargs='*', help='ATR Bands')
11 | group.add_argument('--BANDS', type=float, nargs='?', const=0.1, help=' Bands')
12 |
13 | # talib
14 | group = parser.add_argument_group('Overlap Studies (TaLib)')
15 | group.add_argument('--BBANDS', action="store_true", help='Bollinger Bands')
16 | group.add_argument('--DEMA', type=int, nargs='?', const=30, help='Double Exponential Moving Average')
17 | group.add_argument('--EMA', nargs='*', help='Exponential Moving Average')
18 | group.add_argument('--HT_TRENDLINE', action="store_true", help='Hilbert Transform - Instantaneous Trendline')
19 | group.add_argument('--KAMA', nargs='*', help='Kaufman Adaptive Moving Average')
20 | group.add_argument('--MA', nargs='*', help='Moving average')
21 | group.add_argument('--MAMA', action="store_true", help='MESA Adaptive Moving Average')
22 | group.add_argument('--MIDPOINT', type=int, nargs='?', const=14, help='MidPoint over period')
23 | group.add_argument('--MIDPRICE', type=int, nargs='?', const=14, help='Midpoint Price over period')
24 | group.add_argument('--SAR', action="store_true", help='Parabolic SAR')
25 | group.add_argument('--SAREXT', action="store_true", help='Parabolic SAR - Extended')
26 | group.add_argument('--SMA', type=int, nargs='?', const=30, help='Simple Moving Average')
27 | group.add_argument('--T3', type=int, nargs='?', const=5, help='Triple Exponential Moving Average')
28 | group.add_argument('--TEMA', type=int, nargs='?', const=30, help='Triple Exponential Moving Average')
29 | group.add_argument('--TRIMA', type=int, nargs='?', const=30, help='Triangular Moving Average')
30 | group.add_argument('--WMA', type=int, nargs='?', const=30, help='Weighted Moving Average')
31 |
32 | group.add_argument('--TSF', action="store_true", help='Time Series Forecast')
33 |
34 | def handle_overlap_studies(args, kax, klines_df, close_times, display_count):
35 | all_name = ""
36 | if args.ABANDS: # ATR BANDS
37 | name = 'ABANDS'
38 | real = talib.ATR(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
39 | emas = talib.EMA(klines_df["close"], timeperiod=26)
40 | kax.plot(close_times, emas[-display_count:], "b--", label=name)
41 |
42 | #cs = ['y', 'c', 'm', 'k']
43 | for idx, n in enumerate(args.ABANDS):
44 | """
45 | if idx >= len(cs):
46 | break
47 | c = cs[idx]
48 | """
49 | c = 'y'
50 | cl = c + '--'
51 | n = int(n)
52 | kax.plot(close_times, (emas+n*real)[-display_count:], cl, label=name+' upperband')
53 | kax.plot(close_times, (emas-n*real)[-display_count:], cl, label=name+' lowerband')
54 |
55 | if args.BANDS: # BANDS
56 | name = 'BANDS'
57 | emas = talib.EMA(klines_df["close"], timeperiod=26)
58 | kax.plot(close_times, emas[-display_count:], "b--", label=name)
59 | r= args.BANDS
60 | kax.plot(close_times, (1+r)*emas[-display_count:], 'y--', label=name+' upperband')
61 | kax.plot(close_times, (1-r)*emas[-display_count:], 'y--', label=name+' lowerband')
62 |
63 | # talib
64 | os_key = 'BBANDS'
65 | if args.BBANDS:
66 | upperband, middleband, lowerband = talib.BBANDS(klines_df["close"], timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)
67 | kax.plot(close_times, upperband[-display_count:], "y", label=os_key+' upperband')
68 | kax.plot(close_times, middleband[-display_count:], "b", label=os_key+' middleband')
69 | kax.plot(close_times, lowerband[-display_count:], "y", label=os_key+' lowerband')
70 |
71 | os_key = 'DEMA'
72 | if args.DEMA:
73 | real = talib.DEMA(klines_df["close"], timeperiod=args.DEMA)
74 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
75 |
76 | if args.EMA:
77 | name = 'EMA'
78 | all_name += " %s%s" % (name, args.EMA)
79 | for idx, e_p in enumerate(args.EMA):
80 | if idx >= len(plot_colors):
81 | break
82 | e_p = int(e_p)
83 | emas = talib.EMA(klines_df["close"], timeperiod=e_p)
84 | kax.plot(close_times, emas[-display_count:], plot_colors[idx]+'--', label="%sEMA" % (e_p))
85 |
86 | if args.MA:
87 | name = 'MA'
88 | all_name += " %s%s" % (name, args.MA)
89 | for idx, e_p in enumerate(args.MA):
90 | if idx >= len(plot_colors):
91 | break
92 | e_p = int(e_p)
93 | emas = talib.MA(klines_df["close"], timeperiod=e_p)
94 | kax.plot(close_times, emas[-display_count:], plot_colors[idx], label="%sMA" % (e_p))
95 |
96 | os_key = 'KAMA'
97 | if args.KAMA:
98 | all_name += " %s%s" % (os_key, args.KAMA)
99 | for idx, e_p in enumerate(args.KAMA):
100 | if idx >= len(plot_colors):
101 | break
102 | e_p = int(e_p)
103 | real = talib.KAMA(klines_df["close"], timeperiod=e_p)
104 | kax.plot(close_times, real[-display_count:], plot_colors[idx]+'.', label="%s%s" % (e_p, os_key))
105 |
106 | os_key = 'MAMA'
107 | if args.MAMA:
108 | mama, fama = talib.MAMA(klines_df["close"], fastlimit=0, slowlimit=0)
109 | kax.plot(close_times, mama[-display_count:], "b", label=os_key)
110 | kax.plot(close_times, fama[-display_count:], "c", label=os_key)
111 |
112 | os_key = 'HT_TRENDLINE'
113 | if args.HT_TRENDLINE:
114 | real = talib.HT_TRENDLINE(klines_df["close"])
115 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
116 |
117 | os_key = 'MIDPOINT'
118 | if args.MIDPOINT:
119 | real = talib.MIDPOINT(klines_df["close"], timeperiod=args.MIDPOINT)
120 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
121 |
122 | os_key = 'MIDPRICE'
123 | if args.MIDPRICE:
124 | real = talib.MIDPRICE(klines_df["high"], klines_df["low"], timeperiod=args.MIDPRICE)
125 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
126 |
127 | os_key = 'SAR'
128 | if args.SAR:
129 | real = talib.SAR(klines_df["high"], klines_df["low"], acceleration=0, maximum=0)
130 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
131 |
132 | os_key = 'SAREXT'
133 | if args.SAREXT:
134 | real = talib.SAREXT(klines_df["high"], klines_df["low"],
135 | startvalue=0, offsetonreverse=0,
136 | accelerationinitlong=0, accelerationlong=0, accelerationmaxlong=0,
137 | accelerationinitshort=0, accelerationshort=0, accelerationmaxshort=0)
138 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
139 |
140 | os_key = 'SMA'
141 | if args.SMA:
142 | real = talib.SMA(klines_df["close"], timeperiod=args.SMA)
143 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
144 |
145 | os_key = 'T3'
146 | if args.T3:
147 | real = talib.T3(klines_df["close"], timeperiod=args.T3, vfactor=0)
148 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
149 |
150 | os_key = 'TEMA'
151 | if args.TEMA:
152 | real = talib.TEMA(klines_df["close"], timeperiod=args.TEMA)
153 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
154 |
155 | os_key = 'TRIMA'
156 | if args.TRIMA:
157 | real = talib.TRIMA(klines_df["close"], timeperiod=args.TRIMA)
158 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
159 |
160 | os_key = 'WMA'
161 | if args.WMA:
162 | real = talib.WMA(klines_df["close"], timeperiod=args.WMA)
163 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
164 |
165 | if args.TSF:
166 | name = 'TSF'
167 | real = talib.TSF(klines_df["close"], timeperiod=14)
168 | kax.plot(close_times, real[-display_count:], "y:", label=name)
169 |
170 | return all_name
171 |
172 |
--------------------------------------------------------------------------------
/chart/pattern_recognition.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import pandas as pd
4 | import talib
5 |
6 | import utils.tools as ts
7 | import utils.indicator as ic
8 |
9 |
10 | def add_argument_pattern_recognition(parser):
11 | # Pattern Recognition
12 |
13 | # talib
14 | group = parser.add_argument_group('Pattern Recognition (TaLib)')
15 | group.add_argument('--CDL2CROWS' , action="store_true", help='Two Crows')
16 | group.add_argument('--CDL3BLACKCROWS' , action="store_true", help='Three Black Crows')
17 | group.add_argument('--CDL3INSIDE' , action="store_true", help='Three Inside Up/Down')
18 | group.add_argument('--CDL3LINESTRIKE' , action="store_true", help='Three-Line Strike')
19 | group.add_argument('--CDL3OUTSIDE' , action="store_true", help='Three Outside Up/Down')
20 | group.add_argument('--CDL3STARSINSOUTH' , action="store_true", help='Three Stars In The South')
21 | group.add_argument('--CDL3WHITESOLDIERS' , action="store_true", help='Three Advancing White Soldiers')
22 |
23 |
24 | group.add_argument('--CDLABANDONEDBABY' , action="store_true", help='Abandoned Baby')
25 | group.add_argument('--CDLADVANCEBLOCK' , action="store_true", help='Advance Block')
26 | #group.add_argument('--' , action="store_true", help='')
27 |
28 | def get_pattern_recognition_count(args):
29 | count = 0
30 |
31 | if args.CDL2CROWS:
32 | count += 1
33 | if args.CDL3BLACKCROWS:
34 | count += 1
35 | if args.CDL3INSIDE:
36 | count += 1
37 | if args.CDL3LINESTRIKE:
38 | count += 1
39 | if args.CDL3OUTSIDE:
40 | count += 1
41 | if args.CDL3STARSINSOUTH:
42 | count += 1
43 | if args.CDL3WHITESOLDIERS:
44 | count += 1
45 |
46 | if args.CDLABANDONEDBABY:
47 | count += 1
48 | if args.CDLADVANCEBLOCK:
49 | count += 1
50 | #if args.:
51 | # count += 1
52 | return count
53 |
54 |
55 | def handle_pattern_recognition(args, axes, i, klines_df, close_times, display_count):
56 | # talib
57 | if args.CDL2CROWS:
58 | name = 'CDL2CROWS'
59 | integer = talib.CDL2CROWS(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
60 | i += 1
61 | axes[i].set_ylabel(name)
62 | axes[i].grid(True)
63 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
64 |
65 | if args.CDL3BLACKCROWS:
66 | name = 'CDL3BLACKCROWS'
67 | integer = talib.CDL3BLACKCROWS(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
68 | i += 1
69 | axes[i].set_ylabel(name)
70 | axes[i].grid(True)
71 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
72 |
73 | if args.CDL3INSIDE:
74 | name = 'CDL3INSIDE'
75 | integer = talib.CDL3INSIDE(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
76 | i += 1
77 | axes[i].set_ylabel(name)
78 | axes[i].grid(True)
79 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
80 |
81 | if args.CDL3LINESTRIKE:
82 | name = 'CDL3LINESTRIKE'
83 | integer = talib.CDL3LINESTRIKE(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
84 | i += 1
85 | axes[i].set_ylabel(name)
86 | axes[i].grid(True)
87 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
88 |
89 | if args.CDL3OUTSIDE:
90 | name = 'CDL3OUTSIDE'
91 | integer = talib.CDL3OUTSIDE(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
92 | i += 1
93 | axes[i].set_ylabel(name)
94 | axes[i].grid(True)
95 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
96 |
97 | if args.CDL3STARSINSOUTH:
98 | name = 'CDL3STARSINSOUTH'
99 | integer = talib.CDL3STARSINSOUTH(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
100 | i += 1
101 | axes[i].set_ylabel(name)
102 | axes[i].grid(True)
103 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
104 |
105 | if args.CDL3WHITESOLDIERS:
106 | name = 'CDL3WHITESOLDIERS'
107 | integer = talib.CDL3WHITESOLDIERS(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
108 | i += 1
109 | axes[i].set_ylabel(name)
110 | axes[i].grid(True)
111 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
112 |
113 | if args.CDLABANDONEDBABY:
114 | name = 'CDLABANDONEDBABY'
115 | integer = talib.CDLABANDONEDBABY(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
116 | i += 1
117 | axes[i].set_ylabel(name)
118 | axes[i].grid(True)
119 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
120 |
121 | if args.CDLADVANCEBLOCK:
122 | name = 'CDLADVANCEBLOCK'
123 | integer = talib.CDLADVANCEBLOCK(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
124 | i += 1
125 | axes[i].set_ylabel(name)
126 | axes[i].grid(True)
127 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
128 |
129 | '''
130 | if args.:
131 | name = ''
132 | integer = talib.(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
133 | i += 1
134 | axes[i].set_ylabel(name)
135 | axes[i].grid(True)
136 | axes[i].plot(close_times, integer[-display_count:], "y:", label=name)
137 | '''
138 |
139 |
--------------------------------------------------------------------------------
/chart/price_transform.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import talib
3 |
4 |
5 | def add_argument_price_transform(parser):
6 | # talib
7 | group = parser.add_argument_group('Price Transform (TaLib)')
8 | group.add_argument('--AVGPRICE', action="store_true", help='Average Price')
9 | group.add_argument('--MEDPRICE', action="store_true", help='Median Price')
10 | group.add_argument('--TYPPRICE', action="store_true", help='Typical Price')
11 | group.add_argument('--WCLPRICE', action="store_true", help='Weighted Close Price')
12 |
13 | def handle_price_transform(args, kax, klines_df, close_times, display_count):
14 |
15 | os_key = 'AVGPRICE'
16 | if args.AVGPRICE:
17 | real = talib.AVGPRICE(klines_df["open"], klines_df["high"], klines_df["low"], klines_df["close"])
18 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
19 |
20 | os_key = 'MEDPRICE'
21 | if args.MEDPRICE:
22 | real = talib.MEDPRICE(klines_df["high"], klines_df["low"])
23 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
24 |
25 | os_key = 'TYPPRICE'
26 | if args.TYPPRICE:
27 | real = talib.TYPPRICE(klines_df["high"], klines_df["low"], klines_df["close"])
28 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
29 |
30 | os_key = 'WCLPRICE'
31 | if args.WCLPRICE:
32 | real = talib.WCLPRICE(klines_df["high"], klines_df["low"], klines_df["close"])
33 | kax.plot(close_times, real[-display_count:], "y", label=os_key)
34 |
35 |
--------------------------------------------------------------------------------
/chart/statistic_functions.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import pandas as pd
4 | import talib
5 |
6 | import utils.tools as ts
7 | import utils.indicator as ic
8 |
9 |
10 | def add_argument_statistic_functions(parser):
11 | # Statistic Functions
12 |
13 | # talib
14 | group = parser.add_argument_group('Volatility Indicators (TaLib)')
15 | group.add_argument('--BETA', action="store_true", help='Beta')
16 | group.add_argument('--CORREL', action="store_true", help='Pearson’s Correlation Coefficient (r)')
17 | group.add_argument('--LINEARREG', action="store_true", help='Linear Regression')
18 | group.add_argument('--LINEARREG_ANGLE', action="store_true", help='Linear Regression Angle')
19 | group.add_argument('--LINEARREG_INTERCEPT', action="store_true", help='Linear Regression Intercept')
20 | group.add_argument('--LINEARREG_SLOPE', action="store_true", help='Linear Regression Slope')
21 | group.add_argument('--STDDEV', action="store_true", help='Standard Deviation')
22 | group.add_argument('--VAR', action="store_true", help='VAR')
23 |
24 |
25 | def get_statistic_functions_count(args):
26 | count = 0
27 |
28 | if args.BETA:
29 | count += 1
30 | if args.CORREL:
31 | count += 1
32 | if args.LINEARREG:
33 | count += 1
34 | if args.LINEARREG_ANGLE:
35 | count += 1
36 | if args.LINEARREG_INTERCEPT:
37 | count += 1
38 | if args.LINEARREG_SLOPE:
39 | count += 1
40 | if args.STDDEV:
41 | count += 1
42 | if args.VAR:
43 | count += 1
44 |
45 | return count
46 |
47 |
48 | def handle_statistic_functions(args, axes, i, klines_df, close_times, display_count):
49 | # talib
50 | if args.BETA:
51 | name = 'BETA'
52 | real = talib.BETA(klines_df["high"], klines_df["low"], timeperiod=5)
53 | i += 1
54 | axes[i].set_ylabel(name)
55 | axes[i].grid(True)
56 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
57 |
58 | if args.CORREL:
59 | name = 'CORREL'
60 | real = talib.CORREL(klines_df["high"], klines_df["low"], timeperiod=30)
61 | i += 1
62 | axes[i].set_ylabel(name)
63 | axes[i].grid(True)
64 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
65 |
66 | if args.LINEARREG:
67 | name = 'LINEARREG'
68 | real = talib.LINEARREG(klines_df["close"], timeperiod=14)
69 | i += 1
70 | axes[i].set_ylabel(name)
71 | axes[i].grid(True)
72 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
73 |
74 | if args.LINEARREG_ANGLE:
75 | name = 'LINEARREG_ANGLE'
76 | real = talib.LINEARREG_ANGLE(klines_df["close"], timeperiod=14)
77 | i += 1
78 | axes[i].set_ylabel(name)
79 | axes[i].grid(True)
80 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
81 |
82 | if args.LINEARREG_INTERCEPT:
83 | name = 'LINEARREG_INTERCEPT'
84 | real = talib.LINEARREG_INTERCEPT(klines_df["close"], timeperiod=14)
85 | i += 1
86 | axes[i].set_ylabel(name)
87 | axes[i].grid(True)
88 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
89 |
90 | if args.LINEARREG_SLOPE:
91 | name = 'LINEARREG_SLOPE'
92 | real = talib.LINEARREG_SLOPE(klines_df["close"], timeperiod=14)
93 | i += 1
94 | axes[i].set_ylabel(name)
95 | axes[i].grid(True)
96 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
97 |
98 | if args.STDDEV:
99 | name = 'STDDEV'
100 | real = talib.STDDEV(klines_df["close"], timeperiod=5, nbdev=1)
101 | i += 1
102 | axes[i].set_ylabel(name)
103 | axes[i].grid(True)
104 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
105 |
106 | if args.VAR:
107 | name = 'VAR'
108 | real = talib.VAR(klines_df["close"], timeperiod=5, nbdev=1)
109 | i += 1
110 | axes[i].set_ylabel(name)
111 | axes[i].grid(True)
112 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/chart/volatility_indicators.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import pandas as pd
4 | import talib
5 |
6 | import utils.tools as ts
7 | import utils.indicator as ic
8 |
9 |
10 | def add_argument_volatility_indicators(parser):
11 | # Volume Indicators
12 |
13 | # talib
14 | group = parser.add_argument_group('Volatility Indicators (TaLib)')
15 | group.add_argument('--ATR' , action="store_true", help='Average True Range')
16 | group.add_argument('--NATR' , action="store_true", help='Normalized Average True Range')
17 | group.add_argument('--TRANGE', action="store_true", help='On Balance Volume')
18 |
19 |
20 | def get_volatility_indicators_count(args):
21 | count = 0
22 |
23 | if args.ATR:
24 | count += 1
25 | if args.NATR:
26 | count += 1
27 | if args.TRANGE:
28 | count += 1
29 |
30 | return count
31 |
32 |
33 | def handle_volatility_indicators(args, axes, i, klines_df, close_times, display_count):
34 | # talib
35 | if args.ATR:
36 | name = 'ATR'
37 | real = talib.ATR(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
38 | i += 1
39 | axes[i].set_ylabel(name)
40 | axes[i].grid(True)
41 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
42 |
43 | if args.NATR:
44 | name = 'NATR'
45 | real = talib.NATR(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
46 | i += 1
47 | axes[i].set_ylabel(name)
48 | axes[i].grid(True)
49 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
50 |
51 | if args.TRANGE:
52 | name = 'TRANGE'
53 | real = talib.TRANGE(klines_df["high"], klines_df["low"], klines_df["close"])
54 | i += 1
55 | axes[i].set_ylabel(name)
56 | axes[i].grid(True)
57 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/chart/volume_indicators.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import pandas as pd
4 | import talib
5 |
6 | import utils.tools as ts
7 | import utils.indicator as ic
8 |
9 |
10 | def add_argument_volume_indicators(parser):
11 | # Volume Indicators
12 |
13 | # talib
14 | group = parser.add_argument_group('Volume Indicators (TaLib)')
15 | group.add_argument('--AD' , action="store_true", help='Chaikin A/D Line')
16 | group.add_argument('--ADOSC', action="store_true", help='Chaikin A/D Oscillator')
17 | group.add_argument('--OBV' , action="store_true", help='On Balance Volume')
18 |
19 |
20 | def get_volume_indicators_count(args):
21 | count = 0
22 |
23 | if args.AD: #
24 | count += 1
25 | if args.ADOSC: #
26 | count += 1
27 | if args.OBV: #
28 | count += 1
29 |
30 | return count
31 |
32 |
33 | def handle_volume_indicators(args, axes, i, klines_df, close_times, display_count):
34 | # talib
35 | if args.AD: # AD
36 | name = 'AD'
37 | real = talib.AD(klines_df["high"], klines_df["low"], klines_df["close"], klines_df["volume"])
38 | i += 1
39 | axes[i].set_ylabel(name)
40 | axes[i].grid(True)
41 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
42 |
43 | if args.ADOSC: # ADOSC
44 | name = 'ADOSC'
45 | real = talib.ADOSC(klines_df["high"], klines_df["low"], klines_df["close"], klines_df["volume"], fastperiod=3, slowperiod=10)
46 | i += 1
47 | axes[i].set_ylabel(name)
48 | axes[i].grid(True)
49 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
50 |
51 | if args.OBV: # OBV
52 | name = 'OBV'
53 | real = talib.OBV(klines_df["close"], klines_df["volume"])
54 | i += 1
55 | axes[i].set_ylabel(name)
56 | axes[i].grid(True)
57 | axes[i].plot(close_times, real[-display_count:], "y:", label=name)
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/common/__init__.py
--------------------------------------------------------------------------------
/common/bill.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """bill"""
3 |
4 | DIRECTION_LONG = "LONG" # 做多
5 | DIRECTION_SHORT = "SHORT" # 做空
6 |
7 | OPEN_POSITION = "OPEN" # 开仓
8 | CLOSE_POSITION = "CLOSE" # 平仓
9 | LOCK_POSITION = "LOCK" # 锁仓
10 | UNLOCK_POSITION = "UNLOCK" # 解锁仓位
11 |
12 | directions = [DIRECTION_LONG, DIRECTION_SHORT]
13 | actions = [OPEN_POSITION, CLOSE_POSITION, LOCK_POSITION, UNLOCK_POSITION]
14 |
15 | def create_bill(direction, action, pst_rate, describe, rmk, can_open_time=None, stop_loss_price=None):
16 | """创建单据"""
17 | return {"direction": direction, "action": action, "pst_rate": pst_rate, "describe": describe, "rmk": rmk, "can_open_time": can_open_time,
18 | "stop_loss_price": stop_loss_price
19 | }
20 |
21 | def open_long_bill(pst_rate, describe, rmk, can_open_time=None, stop_loss_price=None):
22 | """创建买单"""
23 | return create_bill(DIRECTION_LONG, OPEN_POSITION, pst_rate, describe, rmk, can_open_time, stop_loss_price)
24 |
25 | def close_long_bill(pst_rate, describe, rmk, can_open_time=None, stop_loss_price=None):
26 | """创建卖单"""
27 | return create_bill(DIRECTION_LONG, CLOSE_POSITION, pst_rate, describe, rmk, can_open_time, stop_loss_price)
28 |
29 | def open_short_bill(pst_rate, describe, rmk, can_open_time=None, stop_loss_price=None):
30 | """创建买信号"""
31 | return create_bill(DIRECTION_SHORT, OPEN_POSITION, pst_rate, describe, rmk, can_open_time, stop_loss_price)
32 |
33 | def close_short_bill(pst_rate, describe, rmk, can_open_time=None, stop_loss_price=None):
34 | """创建卖信号"""
35 | return create_bill(DIRECTION_SHORT, CLOSE_POSITION, pst_rate, describe, rmk, can_open_time, stop_loss_price)
36 |
37 | def is_open_bill(bill):
38 | return bill["action"] == OPEN_POSITION
39 |
40 | def is_close_bill(bill):
41 | return bill["action"] == CLOSE_POSITION
42 |
43 | def is_long_bill(bill):
44 | return bill["direction"] == DIRECTION_LONG
45 |
46 | def is_short_bill(bill):
47 | return bill["direction"] == DIRECTION_SHORT
48 |
49 | def lock_long_bill(pst_rate, describe, rmk):
50 | return create_bill(DIRECTION_LONG, LOCK_POSITION, pst_rate, describe, rmk, None, None)
51 |
52 | def unlock_long_bill(pst_rate, describe, rmk):
53 | return create_bill(DIRECTION_LONG, UNLOCK_POSITION, pst_rate, describe, rmk, None, None)
54 |
55 | def lock_short_bill(pst_rate, describe, rmk):
56 | return create_bill(DIRECTION_SHORT, LOCK_POSITION, pst_rate, describe, rmk, None, None)
57 |
58 | def unlock_short_bill(pst_rate, describe, rmk):
59 | return create_bill(DIRECTION_SHORT, UNLOCK_POSITION, pst_rate, describe, rmk, None, None)
60 |
61 | def decision_bills(bills):
62 | """决策交易信号"""
63 | if not bills:
64 | return None
65 |
66 | ds_bill = bills[0]
67 |
68 | for bill in bills[1:]:
69 | # 暂时不支持同时做多、做空
70 | if ds_bill["direction"] != bill["direction"]:
71 | return None
72 |
73 | if ds_bill["action"] == bill["action"]:
74 | # 持仓率低的信号优先
75 | if ds_bill["pst_rate"] > bill["pst_rate"]:
76 | ds_bill = bill
77 | elif ds_bill["pst_rate"] == bill["pst_rate"]:
78 | # 合并信号
79 | ds_bill["describe"] += ", " + bill["describe"]
80 | ds_bill["rmk"] += ", " + bill["rmk"]
81 | # 限制开仓时间长的优先
82 | if bill["can_open_time"]:
83 | if (not ds_bill["can_open_time"]) or (ds_bill["can_open_time"] < bill["can_open_time"]):
84 | ds_bill["can_open_time"] = bill["can_open_time"]
85 | else:
86 | # 平仓信号优先于开仓信号
87 | if ds_bill["action"] == OPEN_POSITION:
88 | ds_bill = bill
89 |
90 | return ds_bill
91 |
92 |
--------------------------------------------------------------------------------
/common/instance.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | from db.mongodb import get_mongodb
3 | import setup
4 |
5 | STRATEGY_INSTANCE_COLLECTION_NAME = 'strategies'
6 |
7 | STRATEGY_INSTANCE_STATUS_START = "start"
8 | STRATEGY_INSTANCE_STATUS_STOP = "stop"
9 | strategy_instance_statuses = [STRATEGY_INSTANCE_STATUS_START, STRATEGY_INSTANCE_STATUS_STOP]
10 |
11 | def get_strategy_instance(sii):
12 | db = get_mongodb(setup.trade_db_name)
13 | db.ensure_index(STRATEGY_INSTANCE_COLLECTION_NAME, [("instance_id",1)])
14 |
15 | instances = db.find(STRATEGY_INSTANCE_COLLECTION_NAME, {"instance_id": sii})
16 | #print(instances)
17 | if len(instances) is 0:
18 | print("strategy instance id (%s) not exist!" % (sii))
19 | exit(1)
20 | elif len(instances) > 1:
21 | exit(1)
22 | return instances[0]
23 |
24 | def add_strategy_instance(record):
25 | db = get_mongodb(setup.trade_db_name)
26 | db.insert_one(STRATEGY_INSTANCE_COLLECTION_NAME, record)
27 |
28 | def update_strategy_instance(query, record):
29 | db = get_mongodb(setup.trade_db_name)
30 | db.update(STRATEGY_INSTANCE_COLLECTION_NAME, query, record)
31 |
32 | def delete_strategy_instance(query):
33 | db = get_mongodb(setup.trade_db_name)
34 | db.delete_one(STRATEGY_INSTANCE_COLLECTION_NAME, query)
35 |
--------------------------------------------------------------------------------
/common/kline.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | from datetime import datetime, timedelta, time
4 |
5 |
6 | KLINE_DATA_TYPE_LIST = 0
7 | KLINE_DATA_TYPE_JSON = 1
8 |
9 | KLINE_KEY_OPEN_TIME = "open_time"
10 | KLINE_KEY_CLOSE_TIME = "close_time"
11 | KLINE_KEY_OPEN = "open"
12 | KLINE_KEY_CLOSE = "close"
13 | KLINE_KEY_HIGH = "high"
14 | KLINE_KEY_LOW = "low"
15 | KLINE_KEY_VOLUME = "volume"
16 |
17 | KLINE_INTERVAL_1MINUTE = '1m'
18 | KLINE_INTERVAL_3MINUTE = '3m'
19 | KLINE_INTERVAL_5MINUTE = '5m'
20 | KLINE_INTERVAL_15MINUTE = '15m'
21 | KLINE_INTERVAL_30MINUTE = '30m'
22 | KLINE_INTERVAL_1HOUR = '1h'
23 | KLINE_INTERVAL_2HOUR = '2h'
24 | KLINE_INTERVAL_4HOUR = '4h'
25 | KLINE_INTERVAL_6HOUR = '6h'
26 | KLINE_INTERVAL_8HOUR = '8h'
27 | KLINE_INTERVAL_12HOUR = '12h'
28 | KLINE_INTERVAL_1DAY = '1d'
29 | KLINE_INTERVAL_3DAY = '3d'
30 | KLINE_INTERVAL_1WEEK = '1w'
31 | KLINE_INTERVAL_1MONTH = '1M'
32 |
33 | SECONDS_MINUTE = 60
34 | SECONDS_HOUR = 60 * SECONDS_MINUTE
35 | SECONDS_DAY = 24 * SECONDS_HOUR
36 |
37 | def get_kline_collection(symbol, interval):
38 | return "kline_%s_%s" % (symbol, interval)
39 |
40 | def get_open_time(interval, dt):
41 | if interval == KLINE_INTERVAL_1MINUTE:
42 | return datetime.combine(dt.date(), time(dt.hour, dt.minute, 0))
43 | elif interval == KLINE_INTERVAL_3MINUTE:
44 | open_minute = (dt.minute // 3) * 3
45 | return datetime.combine(dt.date(), time(dt.hour, open_minute, 0))
46 | elif interval == KLINE_INTERVAL_5MINUTE:
47 | open_minute = (dt.minute // 5) * 5
48 | return datetime.combine(dt.date(), time(dt.hour, open_minute, 0))
49 | elif interval == KLINE_INTERVAL_15MINUTE:
50 | open_minute = (dt.minute // 15) * 15
51 | return datetime.combine(dt.date(), time(dt.hour, open_minute, 0))
52 | elif interval == KLINE_INTERVAL_30MINUTE:
53 | if dt.minute < 30:
54 | open_minute = 0
55 | else:
56 | open_minute = 30
57 | return datetime.combine(dt.date(), time(dt.hour, open_minute, 0))
58 |
59 | elif interval == KLINE_INTERVAL_1HOUR:
60 | open_hour = dt.hour
61 | return datetime.combine(dt.date(), time(open_hour, 0, 0))
62 | elif interval == KLINE_INTERVAL_2HOUR:
63 | open_hour = (dt.hour // 2) * 2
64 | return datetime.combine(dt.date(), time(open_hour, 0, 0))
65 | elif interval == KLINE_INTERVAL_4HOUR:
66 | open_hour = (dt.hour // 4) * 4
67 | return datetime.combine(dt.date(), time(open_hour, 0, 0))
68 |
69 | elif interval == KLINE_INTERVAL_6HOUR:
70 | if dt.hour < 2:
71 | return datetime.combine(dt.date() - timedelta(days=1), time(20, 0, 0))
72 | elif dt.hour < 8:
73 | return datetime.combine(dt.date(), time(2, 0, 0))
74 | elif dt.hour < 14:
75 | return datetime.combine(dt.date(), time(8, 0, 0))
76 | elif dt.hour < 20:
77 | return datetime.combine(dt.date(), time(14, 0, 0))
78 | else:
79 | return datetime.combine(dt.date(), time(20, 0, 0))
80 |
81 | elif interval == KLINE_INTERVAL_8HOUR:
82 | open_hour = (dt.hour // 8) * 8
83 | return datetime.combine(dt.date(), time(open_hour, 0, 0))
84 |
85 | elif interval == KLINE_INTERVAL_12HOUR:
86 | if dt.hour < 8:
87 | return datetime.combine(dt.date() - timedelta(days=1), time(20, 0, 0))
88 | elif dt.hour >= 20:
89 | return datetime.combine(dt.date(), time(20, 0, 0))
90 | else:
91 | return datetime.combine(dt.date(), time(8, 0, 0))
92 |
93 | elif interval == KLINE_INTERVAL_1DAY:
94 | if dt.hour < 8:
95 | return datetime.combine(dt.date() - timedelta(days=1), time(8, 0, 0))
96 | else:
97 | return datetime.combine(dt.date(), time(8, 0, 0))
98 | else:
99 | return None
100 |
101 | def get_timedelta(interval, size):
102 | if interval == KLINE_INTERVAL_1MINUTE:
103 | return timedelta(minutes=size-1)
104 | elif interval == KLINE_INTERVAL_3MINUTE:
105 | return timedelta(minutes=3*size-1)
106 | elif interval == KLINE_INTERVAL_5MINUTE:
107 | return timedelta(minutes=5*size-1)
108 | elif interval == KLINE_INTERVAL_15MINUTE:
109 | return timedelta(minutes=15*size-1)
110 | elif interval == KLINE_INTERVAL_30MINUTE:
111 | return timedelta(minutes=30*size-1)
112 |
113 | elif interval == KLINE_INTERVAL_1HOUR:
114 | return timedelta(hours=1*size-1)
115 | elif interval == KLINE_INTERVAL_2HOUR:
116 | return timedelta(hours=2*size-1)
117 | elif interval == KLINE_INTERVAL_4HOUR:
118 | return timedelta(hours=4*size-1)
119 | elif interval == KLINE_INTERVAL_6HOUR:
120 | return timedelta(hours=6*size-1)
121 | elif interval == KLINE_INTERVAL_8HOUR:
122 | return timedelta(hours=8*size-1)
123 | elif interval == KLINE_INTERVAL_12HOUR:
124 | return timedelta(hours=12*size-1)
125 |
126 | elif interval == KLINE_INTERVAL_1DAY:
127 | return timedelta(days=size-1)
128 |
129 | else:
130 | return None
131 |
132 | def get_interval_timedelta(interval):
133 | if interval == KLINE_INTERVAL_1MINUTE:
134 | return timedelta(minutes=1)
135 | elif interval == KLINE_INTERVAL_3MINUTE:
136 | return timedelta(minutes=3)
137 | elif interval == KLINE_INTERVAL_5MINUTE:
138 | return timedelta(minutes=5)
139 | elif interval == KLINE_INTERVAL_15MINUTE:
140 | return timedelta(minutes=15)
141 | elif interval == KLINE_INTERVAL_30MINUTE:
142 | return timedelta(minutes=30)
143 |
144 | elif interval == KLINE_INTERVAL_1HOUR:
145 | return timedelta(hours=1)
146 | elif interval == KLINE_INTERVAL_2HOUR:
147 | return timedelta(hours=2)
148 | elif interval == KLINE_INTERVAL_4HOUR:
149 | return timedelta(hours=4)
150 | elif interval == KLINE_INTERVAL_6HOUR:
151 | return timedelta(hours=6)
152 | elif interval == KLINE_INTERVAL_8HOUR:
153 | return timedelta(hours=8)
154 | elif interval == KLINE_INTERVAL_12HOUR:
155 | return timedelta(hours=12)
156 |
157 | elif interval == KLINE_INTERVAL_1DAY:
158 | return timedelta(days=1)
159 |
160 | else:
161 | return None
162 |
163 | def get_interval_seconds(interval):
164 | if interval == KLINE_INTERVAL_1MINUTE:
165 | return 1 * SECONDS_MINUTE
166 | elif interval == KLINE_INTERVAL_3MINUTE:
167 | return 3 * SECONDS_MINUTE
168 | elif interval == KLINE_INTERVAL_5MINUTE:
169 | return 5 * SECONDS_MINUTE
170 | elif interval == KLINE_INTERVAL_15MINUTE:
171 | return 15 * SECONDS_MINUTE
172 | elif interval == KLINE_INTERVAL_30MINUTE:
173 | return 30 * SECONDS_MINUTE
174 |
175 | elif interval == KLINE_INTERVAL_1HOUR:
176 | return 1 * SECONDS_HOUR
177 | elif interval == KLINE_INTERVAL_2HOUR:
178 | return 2 * SECONDS_HOUR
179 | elif interval == KLINE_INTERVAL_4HOUR:
180 | return 4 * SECONDS_HOUR
181 | elif interval == KLINE_INTERVAL_6HOUR:
182 | return 6 * SECONDS_HOUR
183 | elif interval == KLINE_INTERVAL_8HOUR:
184 | return 8 * SECONDS_HOUR
185 | elif interval == KLINE_INTERVAL_12HOUR:
186 | return 12 * SECONDS_HOUR
187 |
188 | elif interval == KLINE_INTERVAL_1DAY:
189 | return SECONDS_DAY
190 |
191 | else:
192 | return None
193 |
194 | def get_next_open_time(interval, dt):
195 | return get_open_time(interval, dt) + get_interval_timedelta(interval)
196 |
197 | def get_next_open_timedelta(interval, dt):
198 | return get_next_open_time(interval, dt) - dt
199 |
200 | def get_kline_index(key, kline_column_names):
201 | for index, value in enumerate(kline_column_names):
202 | if value == key:
203 | return index
204 |
205 | def trans_from_json_to_list(kls, kline_column_names):
206 | return [[(kline[column_name] if (column_name in kline) else "0") for column_name in kline_column_names] for kline in kls]
207 |
208 | def trans_from_list_to_json(kls_list, kline_column_names):
209 | kls_json = []
210 | for kl_list in kls_list:
211 | kl_json = {}
212 | for idx, v in enumerate(kl_list):
213 | kl_json[kline_column_names[idx]] = v
214 | kls_json.append(kl_json)
215 | return kls_json
216 |
--------------------------------------------------------------------------------
/common/log.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """xquant log"""
3 |
4 | import os
5 | import logging
6 | import logging.handlers
7 |
8 | logger = logging.getLogger('QuantLogger')
9 | logger.setLevel(logging.DEBUG)
10 |
11 | def init(path, logname, server_ip=None, server_port=None):
12 | global logger
13 | #logpath = os.path.join(os.getcwd(), "log")
14 | logpath = os.path.join("log")
15 | if not os.path.exists(logpath):
16 | os.mkdir(logpath)
17 |
18 | fullpath = os.path.join(logpath, path)
19 | if not os.path.exists(fullpath):
20 | os.mkdir(fullpath)
21 |
22 | filename = os.path.join(fullpath, logname)
23 | logging.basicConfig(level=logging.NOTSET, filename=filename)
24 |
25 | if server_ip != None:
26 | extra = {'tags':logname}
27 | handler = logging.handlers.SysLogHandler(address = (server_ip, int(server_port)))
28 | f = logging.Formatter('%(tags)s: %(message)s')
29 | handler.setFormatter(f)
30 | logger.addHandler(handler)
31 | logger = logging.LoggerAdapter(logger, extra)
32 |
33 | def info(info):
34 | logger.info(info)
35 |
36 | def warning(info):
37 | logger.warning(info)
38 |
39 | def error(info):
40 | logger.error(info)
41 |
42 | def critical(info):
43 | logger.critical(info)
44 |
45 | def debug(info):
46 | logger.debug(info)
47 |
--------------------------------------------------------------------------------
/common/signal.py:
--------------------------------------------------------------------------------
1 | """signal"""
2 |
3 | SIGNAL_COLOR_BLACK = "k"
4 | SIGNAL_COLOR_WHITE = "w"
5 | SIGNAL_COLOR_RED = "r"
6 | SIGNAL_COLOR_YELLOW = "y"
7 | SIGNAL_COLOR_BLUE = "b"
8 | SIGNAL_COLOR_GREEN = "g"
9 | SIGNAL_COLOR_CYAN = "c"
10 | SIGNAL_COLOR_MAGENTA = "m"
11 |
12 | SIGNAL_COLOR_GREY = "grey"
13 | SIGNAL_COLOR_SILVER = "silver"
14 |
15 | SIGNAL_COLOR_BROWN = "brown"
16 | SIGNAL_COLOR_TAN = "tan"
17 |
18 | SIGNAL_COLOR_GOLD = "gold"
19 |
20 | SIGNAL_COLOR_ORANGE = "orange"
21 | SIGNAL_COLOR_ORANGERED = "orangered"
22 |
23 | SIGNAL_COLOR_FUCHSIA = "fuchsia"
24 | SIGNAL_COLOR_ORCHID = "orchid"
25 | SIGNAL_COLOR_PURPLE = "purple"
26 |
27 | SIGNAL_COLOR_SALMON = "salmon"
28 |
29 | SIGNAL_COLOR_SIENNA = "sienna"
30 | SIGNAL_COLOR_MAROON = "maroon"
31 |
32 |
33 | def create_signal(name, describe="", color=None, result=None):
34 | """创建信号"""
35 | signal = {"name": name, "describe": describe, "result": result}
36 | if color:
37 | signal["color"] = color
38 | return signal
39 |
40 |
41 |
--------------------------------------------------------------------------------
/common/xquant.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """xquant公共定义及公用函数"""
3 |
4 | import utils.tools as ts
5 | from datetime import datetime, timedelta, time
6 | import json
7 |
8 |
9 | time_range_split = "~"
10 |
11 | ORDER_TYPE_LIMIT = "LIMIT"
12 | ORDER_TYPE_MARKET = "MARKET"
13 |
14 | ORDER_STATUS_WAIT = "wait"
15 | ORDER_STATUS_OPEN = "open"
16 | ORDER_STATUS_CLOSE = "close"
17 | ORDER_STATUS_CANCELLING = "cancelling"
18 | ORDER_STATUS_CANCELLED = "cancelled"
19 |
20 | ordertypes = [ORDER_TYPE_LIMIT, ORDER_TYPE_MARKET]
21 |
22 | def creat_symbol(target_coin, base_coin):
23 | """create symbol"""
24 | return "%s_%s" % (target_coin.lower(), base_coin.lower())
25 |
26 |
27 | def get_symbol_coins(symbol):
28 | """获取coins"""
29 | coins = symbol.split("_")
30 | return tuple(coins)
31 |
32 |
33 | def create_balance(coin, free, frozen):
34 | """ 创建余额 """
35 | return {"coin": coin, "free": free, "frozen": frozen}
36 |
37 |
38 | def get_balance_coin(balance):
39 | return balance["coin"]
40 |
41 |
42 | def get_balance_free(balance):
43 | """ 获取可用数 """
44 | return float(balance["free"])
45 |
46 |
47 | def get_balance_frozen(balance):
48 | """ 获取冻结数 """
49 | return float(balance["frozen"])
50 |
51 |
52 | def down_area(ss, ls):
53 | total_len = len(ss)
54 |
55 | ei = -1
56 | i = -2
57 | while i >= -total_len:
58 | v = ss[i]-ls[i]
59 | if v > 0:
60 | return -1, -1, -1
61 | if v < ss[ei]-ls[ei]:
62 | break
63 | ei = i
64 | i -= 1
65 |
66 | mi = i
67 | bi = i
68 | while i >= -total_len:
69 | v = ss[i]-ls[i]
70 | if v > 0:
71 | break
72 |
73 | if ss[mi] - ls[mi] > v:
74 | mi = i
75 | bi = i
76 | i -= 1
77 |
78 | return bi, mi, ei
79 |
80 | def get_strategy_config(config_path):
81 | fo = open(config_path, "r")
82 | config = json.loads(fo.read())
83 | fo.close()
84 | return config
85 |
--------------------------------------------------------------------------------
/db/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/db/__init__.py
--------------------------------------------------------------------------------
/db/mongodb.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """mongodb"""
3 | import common.log as log
4 | from pymongo import MongoClient
5 | from pymongo.errors import BulkWriteError
6 | from bson import ObjectId
7 | from setup import mongo_user, mongo_pwd, db_url
8 |
9 |
10 | def get_mongodb(db_name):
11 | return MongoDB(mongo_user, mongo_pwd, db_name, db_url)
12 |
13 | def get_datetime_by_id(_id):
14 | """get datetime from _id"""
15 | return ObjectId(_id).generation_time
16 |
17 |
18 | class MongoDB:
19 | """MongoDB"""
20 |
21 | def __init__(self, user, password, db_name, db_url):
22 | client = MongoClient(db_url)
23 | self.__client = eval("%s.%s" % (client, db_name))
24 | if user:
25 | self.__client.authenticate(user, password)
26 |
27 | def create_index(self, collection, index):
28 | self.__client[collection].create_index(index, unique=True)
29 |
30 | def ensure_index(self, collection, index, unique=False):
31 | return self.__client[collection].ensure_index(index, unique=unique)
32 |
33 | def insert_one(self, collection, record):
34 | """insert_one"""
35 | try:
36 | log.debug("mongodb %s insert : %s" % (collection, record))
37 | _id = self.__client[collection].insert_one(record).inserted_id
38 | return _id
39 | except Exception as exc:
40 | print(exc)
41 | return None
42 |
43 | def insert_many(self, collection, records):
44 | """insert_many"""
45 | try:
46 | ret = self.__client[collection].insert_many(records)
47 | return ret
48 | except BulkWriteError as exc:
49 | print(exc.details)
50 | return None
51 |
52 | def update_one(self, collection, _id, record):
53 | """update_one"""
54 | log.debug("mongodb %s(_id=%s) update: %s" % (collection, _id, record))
55 | self.__client[collection].update_one({"_id": ObjectId(_id)}, {"$set": record})
56 |
57 | def update(self, collection, query, record):
58 | """update"""
59 | log.debug("mongodb %s(qurey=%s) update: %s" % (collection, query, record))
60 | self.__client[collection].update_many(query, {"$set": record})
61 |
62 | def delete_one(self, collection, query):
63 | """delete_one"""
64 | log.debug("mongodb %s(query=%s) delete" % (collection, query))
65 | self.__client[collection].delete_one(query)
66 |
67 | def find(self, collection, query, projection=None):
68 | """find"""
69 | #print(query)
70 | #print(projection)
71 | if projection:
72 | ret = self.__client[collection].find(query, projection=projection)
73 | else:
74 | ret = self.__client[collection].find(query)
75 |
76 | records = []
77 | for i in ret:
78 | records.append(i)
79 | return records
80 |
81 | def find_sort(self, collection, query, sort_field, sort_dir, limit=None, projection=None):
82 | """find and sort
83 | sort_dir: 1. ASCENDING,
84 | -1. DESCENDING
85 | """
86 | #print(query)
87 | #print(projection)
88 | if limit:
89 | ret = self.__client[collection].find(query, projection=projection).sort(sort_field, sort_dir).limit(limit)
90 | else:
91 | ret = self.__client[collection].find(query, projection=projection).sort(sort_field, sort_dir)
92 | """
93 | if projection:
94 | ret = self.__client[collection].find(query, projection=projection).sort(sort_field, sort_dir)
95 | else:
96 | ret = self.__client[collection].find(query).sort(sort_field, sort_dir)
97 | """
98 | records = []
99 | for i in ret:
100 | records.append(i)
101 | return records
102 |
103 | def count(self, collection, query, projection=None):
104 | """count"""
105 | #print(query)
106 | #print(projection)
107 | if projection:
108 | ret = self.__client[collection].find(query, projection=projection).count()
109 | else:
110 | ret = self.__client[collection].find(query).count()
111 | return ret
112 |
--------------------------------------------------------------------------------
/engine/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/engine/__init__.py
--------------------------------------------------------------------------------
/engine/backtestengine.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """回测环境"""
3 | import sys
4 | from datetime import datetime, timedelta, time
5 | import uuid
6 | import utils.tools as ts
7 | import common.xquant as xq
8 | import common.kline as kl
9 | import common.bill as bl
10 | from .engine import Engine
11 | from .order import *
12 | from md.dbmd import DBMD
13 |
14 |
15 | class BackTest(Engine):
16 | """回测引擎"""
17 |
18 | def __init__(self, instance_id, exchange_name, config, log_switch=False, *symbols):
19 | super().__init__(instance_id, config, 10000, log_switch)
20 |
21 | self.md = DBMD(exchange_name, kl.KLINE_DATA_TYPE_JSON)
22 | self.orders = []
23 |
24 | def now(self):
25 | return self.md.tick_time
26 |
27 | def get_balances(self, *coins):
28 | """ 获取账户余额,回测默认1个亿,哈哈 """
29 | coin_balances = []
30 | for coin in coins:
31 | balance = xq.create_balance(coin, "100000000", "0")
32 | coin_balances.append(balance)
33 |
34 | if len(coin_balances) <= 0:
35 | return
36 | elif len(coin_balances) == 1:
37 | return coin_balances[0]
38 | else:
39 | return tuple(coin_balances)
40 |
41 | def get_position(self, symbol, cur_price):
42 | """ 获取持仓信息 """
43 | if len(self.orders) > 0 and self.orders[-1][ORDER_ACTION_KEY] in [bl.OPEN_POSITION, bl.UNLOCK_POSITION]:
44 | pst_first_order = get_pst_first_order(self.orders)
45 | if "high" not in pst_first_order or pst_first_order["high"] < cur_price:
46 | pst_first_order["high"] = cur_price
47 | pst_first_order["high_time"] = self.now().timestamp()
48 | if "low" not in pst_first_order or pst_first_order["low"] > cur_price:
49 | pst_first_order["low"] = cur_price
50 | pst_first_order["low_time"] = self.now().timestamp()
51 | return self._get_position(symbol, self.orders, cur_price)
52 |
53 | def send_order_limit(
54 | self, direction, action, symbol, pst_rate, cur_price, limit_price, amount, stop_loss_price, rmk
55 | ):
56 | """ 提交委托,回测默认以当前价全部成交 """
57 | # order_id = uuid.uuid1()
58 | order_id = ""
59 | self.orders.append({
60 | "create_time": self.now().timestamp(),
61 | "instance_id": self.instance_id,
62 | "symbol": symbol,
63 | "direction": direction,
64 | "action": action,
65 | "pst_rate": pst_rate,
66 | "type": xq.ORDER_TYPE_LIMIT,
67 | "market_price": cur_price,
68 | "price": limit_price,
69 | "amount": amount,
70 | "stop_loss_price": stop_loss_price,
71 | "status": xq.ORDER_STATUS_CLOSE,
72 | "order_id": order_id,
73 | "cancle_amount": 0,
74 | "deal_amount": amount,
75 | "deal_value": amount * cur_price,
76 | "rmk": rmk,
77 | })
78 |
79 | return order_id
80 |
81 | def cancle_orders(self, symbol):
82 | """ 撤掉本策略的所有挂单委托 """
83 | pass
84 |
85 | def view(self, symbol, orders):
86 | if len(orders) == 0:
87 | return
88 |
89 | pst_info = self.get_pst_by_orders(orders)
90 | self.view_history(symbol, orders, pst_info)
91 |
92 |
93 | def set_pst_lock_to_close(self, symbol, rmk):
94 | trans_lock_to_close(self.orders[-1], rmk, self.now())
95 |
96 |
--------------------------------------------------------------------------------
/engine/realengine.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """实盘"""
3 | import time
4 | import datetime
5 | import utils.tools as ts
6 | import common.xquant as xq
7 | import common.kline as kl
8 | import common.bill as bl
9 | from .engine import Engine
10 | from .order import *
11 | from exchange.exchange import create_exchange
12 | from md.exmd import ExchangeMD
13 | from db.mongodb import get_mongodb
14 | import setup
15 | from pprint import pprint
16 |
17 | DB_ORDERS_NAME = "orders"
18 |
19 |
20 | class RealEngine(Engine):
21 | """实盘引擎"""
22 |
23 | def __init__(self, instance_id, exchange_name, config, value, log_switch=False):
24 | super().__init__(instance_id, config, value, log_switch)
25 |
26 | self.db_orders_name = DB_ORDERS_NAME
27 | self.td_db = get_mongodb(setup.trade_db_name)
28 | self.td_db.ensure_index(self.db_orders_name, [("instance_id",1),("symbol",1)])
29 |
30 | self.__exchange = create_exchange(exchange_name)
31 | if not self.__exchange:
32 | print("Wrong exchange name: %s" % exchange_name)
33 | exit(1)
34 | self.__exchange.connect()
35 |
36 | self.md = ExchangeMD(self.__exchange, kl.KLINE_DATA_TYPE_JSON)
37 |
38 | def now(self):
39 | return datetime.datetime.now()
40 |
41 | def get_account(self):
42 | """ 获取账户信息 """
43 | return self.__exchange.get_account()
44 |
45 | def get_balances(self, *coins):
46 | """ 获取余额 """
47 | return self.__exchange.get_balances(*coins)
48 |
49 | def get_position(self, symbol, cur_price):
50 | """ 获取持仓信息 """
51 | self.sync_orders(symbol)
52 |
53 | orders = self.get_orders(symbol)
54 |
55 | if len(orders) > 0 and orders[-1][ORDER_ACTION_KEY] in [bl.OPEN_POSITION, bl.UNLOCK_POSITION]:
56 | pst_first_order = get_pst_first_order(orders)
57 | now_ts = self.now().timestamp()
58 |
59 | if "high" not in pst_first_order or pst_first_order["high"] < cur_price:
60 | pst_first_order["high"] = cur_price
61 | pst_first_order["high_time"] = now_ts
62 | self.td_db.update_one(
63 | self.db_orders_name,
64 | pst_first_order["_id"],
65 | {
66 | "high": cur_price,
67 | "high_time": now_ts,
68 | },
69 | )
70 | if "low" not in pst_first_order or pst_first_order["low"] > cur_price:
71 | pst_first_order["low"] = cur_price
72 | pst_first_order["low_time"] = now_ts
73 | self.td_db.update_one(
74 | self.db_orders_name,
75 | pst_first_order["_id"],
76 | {
77 | "low": cur_price,
78 | "low_time": now_ts,
79 | },
80 | )
81 |
82 | return self._get_position(symbol, orders, cur_price)
83 |
84 | def set_pst_lock_to_close(self, symbol, rmk):
85 | orders = self.get_orders(symbol)
86 | if len(orders) == 0:
87 | return
88 | lastly_order = orders[-1]
89 | trans_lock_to_close(lastly_order, rmk, self.now())
90 | self.td_db.update_one(self.db_orders_name, lastly_order["_id"], lastly_order)
91 | return
92 |
93 |
94 | def get_orders(self, symbol):
95 | return self.td_db.find(
96 | DB_ORDERS_NAME,
97 | {
98 | "instance_id": self.instance_id,
99 | "symbol": symbol,
100 | },
101 | )
102 |
103 |
104 | def get_open_orders(self, symbol):
105 | """ 是否有open状态的委托 """
106 | return self.td_db.find(
107 | DB_ORDERS_NAME,
108 | {
109 | "instance_id": self.instance_id,
110 | "symbol": symbol,
111 | "status": xq.ORDER_STATUS_OPEN,
112 | },
113 | )
114 |
115 | def sync_orders(self, symbol):
116 | orders = self.get_open_orders(symbol)
117 | if not orders:
118 | return
119 |
120 | df_amount, df_value = self.__exchange.get_deals(symbol)
121 | for order in orders:
122 | self.log_debug("order: %r" % order)
123 | order_id = order["order_id"]
124 | order_amount = order["amount"]
125 |
126 | if order_id not in df_amount.index:
127 | """ 没有成交信息 """
128 | continue
129 |
130 | deal_amount = df_amount[order_id]
131 |
132 | target_coin, base_coin = xq.get_symbol_coins(symbol)
133 | deal_amount = ts.reserve_float(deal_amount, self.config["digits"][target_coin])
134 | deal_value = df_value[order_id]
135 |
136 | status = xq.ORDER_STATUS_OPEN
137 | if deal_amount > order_amount:
138 | self.log_error("最新成交数量(%f)大于委托数量(%f) %g" % (deal_amount, order_amount, (deal_amount-order_amount)))
139 | continue
140 | elif deal_amount == order_amount:
141 | status = xq.ORDER_STATUS_CLOSE
142 | else:
143 | if deal_amount < order["deal_amount"]:
144 | self.log_warning("最新成交数量小于委托里记载的旧成交数量")
145 | continue
146 | elif deal_amount == order["deal_amount"]:
147 | self.log_info("成交数量没有更新")
148 | else:
149 | pass
150 | if self.__exchange.order_status_is_close(symbol, order_id):
151 | status = xq.ORDER_STATUS_CLOSE
152 | self.log_debug("deal_amount: %f, deal_value: %g, deal_price: %g" % (deal_amount, deal_value, deal_value/deal_amount))
153 | self.td_db.update_one(
154 | DB_ORDERS_NAME,
155 | order["_id"],
156 | {
157 | "deal_amount": deal_amount,
158 | "deal_value": deal_value,
159 | "status": status,
160 | },
161 | )
162 | return
163 |
164 | def send_order_limit(
165 | self, direction, action, symbol, pst_rate, cur_price, limit_price, amount, stop_loss_price, rmk
166 | ):
167 | """ 提交委托 """
168 | """
169 | _id = self._db.insert_one(
170 | DB_ORDERS_NAME,
171 | {
172 | "create_time": self.now().timestamp(),
173 | "instance_id": self.instance_id,
174 | "symbol": symbol,
175 | "side": side,
176 | "pst_rate": pst_rate,
177 | "type": xq.ORDER_TYPE_LIMIT,
178 | "price": limit_price,
179 | "amount": amount,
180 | "status": xq.ORDER_STATUS_WAIT,
181 | "order_id": "",
182 | "cancle_amount": 0,
183 | "deal_amount": 0,
184 | "deal_value": 0,
185 | },
186 | )
187 |
188 | order_id = self.__exchange.send_order(
189 | side, xq.ORDER_TYPE_LIMIT, symbol, limit_price, amount, _id
190 | )
191 |
192 | self._db.update_one(
193 | DB_ORDERS_NAME, _id, {"order_id": order_id, "status": xq.ORDER_STATUS_OPEN}
194 | )
195 | """
196 | # 暂时简单处理
197 | order_id = self.__exchange.send_order(
198 | direction, action, xq.ORDER_TYPE_LIMIT, symbol, limit_price, amount
199 | )
200 |
201 | _id = self.td_db.insert_one(
202 | DB_ORDERS_NAME,
203 | {
204 | "create_time": self.now().timestamp(),
205 | "instance_id": self.instance_id,
206 | "symbol": symbol,
207 | "direction": direction,
208 | "action": action,
209 | "pst_rate": pst_rate,
210 | "type": xq.ORDER_TYPE_LIMIT,
211 | "market_price": cur_price,
212 | "price": limit_price,
213 | "amount": amount,
214 | "stop_loss_price": stop_loss_price,
215 | "status": xq.ORDER_STATUS_OPEN,
216 | "order_id": order_id,
217 | "cancle_amount": 0,
218 | "deal_amount": 0,
219 | "deal_value": 0,
220 | "rmk": rmk,
221 | },
222 | )
223 |
224 | return order_id
225 |
226 |
227 | def cancle_orders(self, symbol):
228 | """ 撤掉本策略的所有挂单委托 """
229 | orders = self.get_open_orders(symbol)
230 | if not orders:
231 | return
232 |
233 | e_order_ids = self.__exchange.get_open_order_ids(symbol)
234 |
235 | for order in orders:
236 | order_id = order["order_id"]
237 | if order_id not in e_order_ids:
238 | continue
239 | self.__exchange.cancel_order(symbol, order_id)
240 | self.td_db.update_one(
241 | DB_ORDERS_NAME, order["_id"], {"status": xq.ORDER_STATUS_CANCELLING}
242 | )
243 |
244 |
245 | def run(self, strategy, debug):
246 | """ run """
247 | while True:
248 | tick_start = datetime.datetime.now()
249 | self.log_info(
250 | "%s tick start......................................" % tick_start
251 | )
252 |
253 | if debug:
254 | strategy.on_tick()
255 | else:
256 | try:
257 | strategy.on_tick()
258 | except Exception as ept:
259 | self.log_critical(ept)
260 |
261 | tick_end = datetime.datetime.now()
262 | self.log_info(
263 | "%s tick end...; tick cost: %s -----------------------\n\n" % (
264 | tick_end,
265 | tick_end - tick_start
266 | )
267 | )
268 | time.sleep(strategy.config["sec"])
269 |
270 | def get_floating(self, symbol, pst_info):
271 | if pst_info[POSITON_AMOUNT_KEY] == 0:
272 | return 0, 0, 0, None
273 | kls = self.md.get_klines_1day(symbol, 1)
274 | cur_price = float(kls[-1][self.md.get_kline_seat_close()])
275 | floating_profit, total_profit, floating_profit_rate, total_profit_rate = get_floating_profit(pst_info, self.value, self.config["mode"], cur_price)
276 | floating_commission = pst_info[POSITON_COMMISSION_KEY]
277 | return floating_profit, floating_profit_rate, floating_commission, cur_price
278 |
279 |
280 | def view(self, symbol, orders):
281 | if len(orders) == 0:
282 | return
283 |
284 | pst_info = self.get_pst_by_orders(orders)
285 | self.view_history(symbol, orders, pst_info)
286 |
287 | floating_profit, floating_profit_rate, floating_commission, cur_price = self.get_floating(symbol, pst_info)
288 | print("floating: profit = %.2f(%.2f%%) commission = %.2f cur_price = %s" % (floating_profit, floating_profit_rate*100, floating_commission, cur_price))
289 | print("\nposition infomation:")
290 | pprint(pst_info)
291 |
--------------------------------------------------------------------------------
/engine/signalengine.py:
--------------------------------------------------------------------------------
1 | import common.log as log
2 |
3 |
4 | class SignalEngine:
5 | """引擎"""
6 |
7 | def __init__(self, instance_id, config, log_switch):
8 | self.instance_id = instance_id
9 | self.log_switch = log_switch
10 |
11 |
12 | def log_info(self, info):
13 | if self.log_switch:
14 | log.info(info)
15 |
16 | def log_warning(self, info):
17 | if self.log_switch:
18 | log.warngin(info)
19 |
20 | def log_error(self, info):
21 | if self.log_switch:
22 | log.error(info)
23 |
24 | def log_critical(self, info):
25 | if self.log_switch:
26 | log.critical(info)
27 |
28 | def log_debug(self, info):
29 | if self.log_switch:
30 | log.debug(info)
31 |
32 |
33 | class TestSignal(SignalEngine):
34 | def __init__(self, md, instance_id, config, log_switch=False):
35 | super().__init__(instance_id, config, log_switch)
36 | self.md = md
37 | self.signals = []
38 |
39 | def handle_signal(self, symbol, signals, price, create_time):
40 | for s in signals:
41 | s["price"] = price
42 | s["create_time"] = create_time
43 | self.signals += signals
44 |
45 | def get_signalsets(self):
46 | signalsets = {}
47 | for s in self.signals:
48 | s_name = s["name"]
49 | if s_name not in signalsets:
50 | signalsets[s_name] = [s]
51 | else:
52 | signalsets[s_name].append(s)
53 | return signalsets
54 |
55 | def handle(self, symbol, strategy, price, create_time, info):
56 | signals, infos = strategy.check_signal_single()
57 | self.log_info(strategy.merge_infos(infos, strategy.aligning_log))
58 | self.handle_signal(symbol, signals, price, create_time)
59 | return
60 |
61 |
--------------------------------------------------------------------------------
/exchange/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/exchange/__init__.py
--------------------------------------------------------------------------------
/exchange/binance/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/exchange/binance/__init__.py
--------------------------------------------------------------------------------
/exchange/binance/depthcache.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 |
4 | from operator import itemgetter
5 |
6 | from .websockets import BinanceSocketManager
7 |
8 |
9 | class DepthCache(object):
10 |
11 | def __init__(self, symbol):
12 | """Intialise the DepthCache
13 |
14 | :param symbol: Symbol to create depth cache for
15 | :type symbol: string
16 |
17 | """
18 | self.symbol = symbol
19 | self._bids = {}
20 | self._asks = {}
21 |
22 | def add_bid(self, bid):
23 | """Add a bid to the cache
24 |
25 | :param bid:
26 | :return:
27 |
28 | """
29 | self._bids[bid[0]] = float(bid[1])
30 | if bid[1] == "0.00000000":
31 | del self._bids[bid[0]]
32 |
33 | def add_ask(self, ask):
34 | """Add an ask to the cache
35 |
36 | :param ask:
37 | :return:
38 |
39 | """
40 | self._asks[ask[0]] = float(ask[1])
41 | if ask[1] == "0.00000000":
42 | del self._asks[ask[0]]
43 |
44 | def get_bids(self):
45 | """Get the current bids
46 |
47 | :return: list of bids with price and quantity as floats
48 |
49 | .. code-block:: python
50 |
51 | [
52 | [
53 | 0.0001946, # Price
54 | 45.0 # Quantity
55 | ],
56 | [
57 | 0.00019459,
58 | 2384.0
59 | ],
60 | [
61 | 0.00019158,
62 | 5219.0
63 | ],
64 | [
65 | 0.00019157,
66 | 1180.0
67 | ],
68 | [
69 | 0.00019082,
70 | 287.0
71 | ]
72 | ]
73 |
74 | """
75 | return DepthCache.sort_depth(self._bids, reverse=True)
76 |
77 | def get_asks(self):
78 | """Get the current asks
79 |
80 | :return: list of asks with price and quantity as floats
81 |
82 | .. code-block:: python
83 |
84 | [
85 | [
86 | 0.0001955, # Price
87 | 57.0' # Quantity
88 | ],
89 | [
90 | 0.00019699,
91 | 778.0
92 | ],
93 | [
94 | 0.000197,
95 | 64.0
96 | ],
97 | [
98 | 0.00019709,
99 | 1130.0
100 | ],
101 | [
102 | 0.0001971,
103 | 385.0
104 | ]
105 | ]
106 |
107 | """
108 | return DepthCache.sort_depth(self._asks, reverse=False)
109 |
110 | @staticmethod
111 | def sort_depth(vals, reverse=False):
112 | """Sort bids or asks by price
113 | """
114 | lst = [[float(price), quantity] for price, quantity in vals.items()]
115 | lst = sorted(lst, key=itemgetter(0), reverse=reverse)
116 | return lst
117 |
118 |
119 | class DepthCacheManager(object):
120 |
121 | def __init__(self, client, symbol, callback):
122 | """Intialise the DepthCacheManager
123 |
124 | :param client: Binance API client
125 | :type client: binance.Client
126 | :param symbol: Symbol to create depth cache for
127 | :type symbol: string
128 | :param callback: Function to receive depth cache updates
129 | :type callback: function
130 |
131 | """
132 | self._client = client
133 | self._symbol = symbol
134 | self._callback = callback
135 | self._first_update_id = 0
136 | self._bm = None
137 | self._depth_cache = DepthCache(self._symbol)
138 |
139 | self._init_cache()
140 | self._start_socket()
141 |
142 | def _init_cache(self):
143 | res = self._client.get_order_book(symbol=self._symbol, limit=500)
144 |
145 | self._first_update_id = res['lastUpdateId']
146 |
147 | for bid in res['bids']:
148 | self._depth_cache.add_bid(bid)
149 | for ask in res['asks']:
150 | self._depth_cache.add_ask(ask)
151 |
152 | def _start_socket(self):
153 | self._bm = BinanceSocketManager(self._client)
154 |
155 | self._bm.start_depth_socket(self._symbol, self._depth_event)
156 |
157 | self._bm.start()
158 |
159 | def _depth_event(self, msg):
160 | """
161 |
162 | :param msg:
163 | :return:
164 |
165 | """
166 | # ignore any updates before the initial update id
167 | if msg['u'] <= self._first_update_id:
168 | return
169 |
170 | # add any bid or ask values
171 | for bid in msg['b']:
172 | self._depth_cache.add_bid(bid)
173 | for ask in msg['a']:
174 | self._depth_cache.add_ask(ask)
175 |
176 | # call the callback with the updated depth cache
177 | self._callback(self._depth_cache)
178 |
179 | def get_depth_cache(self):
180 | """Get the current depth cache
181 |
182 | :return: DepthCache object
183 |
184 | """
185 | return self._depth_cache
186 |
187 | def close(self):
188 | """Close the open socket for this manager
189 |
190 | :return:
191 | """
192 | self._bm.close()
193 |
--------------------------------------------------------------------------------
/exchange/binance/enums.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 |
4 | SYMBOL_TYPE_SPOT = 'SPOT'
5 |
6 | ORDER_STATUS_NEW = 'NEW'
7 | ORDER_STATUS_PARTIALLY_FILLED = 'PARTIALLY_FILLED'
8 | ORDER_STATUS_FILLED = 'FILLED'
9 | ORDER_STATUS_CANCELED = 'CANCELED'
10 | ORDER_STATUS_PENDING_CANCEL = 'PENDING_CANCEL'
11 | ORDER_STATUS_REJECTED = 'REJECTED'
12 | ORDER_STATUS_EXPIRED = 'EXPIRED'
13 |
14 | KLINE_INTERVAL_1MINUTE = '1m'
15 | KLINE_INTERVAL_3MINUTE = '3m'
16 | KLINE_INTERVAL_5MINUTE = '5m'
17 | KLINE_INTERVAL_15MINUTE = '15m'
18 | KLINE_INTERVAL_30MINUTE = '30m'
19 | KLINE_INTERVAL_1HOUR = '1h'
20 | KLINE_INTERVAL_2HOUR = '2h'
21 | KLINE_INTERVAL_4HOUR = '4h'
22 | KLINE_INTERVAL_6HOUR = '6h'
23 | KLINE_INTERVAL_8HOUR = '8h'
24 | KLINE_INTERVAL_12HOUR = '12h'
25 | KLINE_INTERVAL_1DAY = '1d'
26 | KLINE_INTERVAL_3DAY = '3d'
27 | KLINE_INTERVAL_1WEEK = '1w'
28 | KLINE_INTERVAL_1MONTH = '1M'
29 |
30 | SIDE_BUY = 'BUY'
31 | SIDE_SELL = 'SELL'
32 |
33 | ORDER_TYPE_LIMIT = 'LIMIT'
34 | ORDER_TYPE_MARKET = 'MARKET'
35 | ORDER_TYPE_STOP_LOSS = 'STOP_LOSS'
36 | ORDER_TYPE_STOP_LOSS_LIMIT = 'STOP_LOSS_LIMIT'
37 | ORDER_TYPE_TAKE_PROFIT = 'TAKE_PROFIT'
38 | ORDER_TYPE_TAKE_PROFIT_LIMIT = 'TAKE_PROFIT_LIMIT'
39 | ORDER_TYPE_LIMIT_MAKER = 'LIMIT_MAKER'
40 |
41 | TIME_IN_FORCE_GTC = 'GTC' # Good till cancelled
42 | TIME_IN_FORCE_IOC = 'IOC' # Immediate or cancel
43 | TIME_IN_FORCE_FOK = 'FOK' # Fill or kill
44 |
45 | ORDER_RESP_TYPE_ACK = 'ACK'
46 | ORDER_RESP_TYPE_RESULT = 'RESULT'
47 | ORDER_RESP_TYPE_FULL = 'FULL'
48 |
49 | WEBSOCKET_DEPTH_1 = '1'
50 | WEBSOCKET_DEPTH_5 = '5'
51 | WEBSOCKET_DEPTH_10 = '10'
52 | WEBSOCKET_DEPTH_20 = '20'
53 |
--------------------------------------------------------------------------------
/exchange/binance/exceptions.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 |
4 |
5 | class BinanceAPIException(Exception):
6 |
7 | LISTENKEY_NOT_EXIST = '-1125'
8 |
9 | def __init__(self, response):
10 | json_res = response.json()
11 | self.status_code = response.status_code
12 | self.response = response
13 | self.code = json_res['code']
14 | self.message = json_res['msg']
15 | self.request = getattr(response, 'request', None)
16 |
17 | def __str__(self): # pragma: no cover
18 | return 'APIError(code=%s): %s' % (self.code, self.message)
19 |
20 |
21 | class BinanceRequestException(Exception):
22 | def __init__(self, message):
23 | self.message = message
24 |
25 | def __str__(self):
26 | return 'BinanceRequestException: %s' % self.message
27 |
28 |
29 | class BinanceOrderException(Exception):
30 |
31 | def __init__(self, code, message):
32 | self.code = code
33 | self.message = message
34 |
35 | def __str__(self):
36 | return 'BinanceOrderException(code=%s): %s' % (self.code, self.message)
37 |
38 |
39 | class BinanceOrderMinAmountException(BinanceOrderException):
40 |
41 | def __init__(self, value):
42 | message = "Amount must be a multiple of %s" % value
43 | super(BinanceOrderMinAmountException, self).__init__(-1013, message)
44 |
45 |
46 | class BinanceOrderMinPriceException(BinanceOrderException):
47 |
48 | def __init__(self, value):
49 | message = "Price must be at least %s" % value
50 | super(BinanceOrderMinPriceException, self).__init__(-1013, message)
51 |
52 |
53 | class BinanceOrderMinTotalException(BinanceOrderException):
54 |
55 | def __init__(self, value):
56 | message = "Total must be at least %s" % value
57 | super(BinanceOrderMinTotalException, self).__init__(-1013, message)
58 |
59 |
60 | class BinanceOrderUnknownSymbolException(BinanceOrderException):
61 |
62 | def __init__(self, value):
63 | message = "Unknown symbol %s" % value
64 | super(BinanceOrderUnknownSymbolException, self).__init__(-1013, message)
65 |
66 |
67 | class BinanceOrderInactiveSymbolException(BinanceOrderException):
68 |
69 | def __init__(self, value):
70 | message = "Attempting to trade an inactive symbol %s" % value
71 | super(BinanceOrderInactiveSymbolException, self).__init__(-1013, message)
72 |
73 |
74 | class BinanceWithdrawException(Exception):
75 | def __init__(self, message):
76 | if message == u'参数异常':
77 | message = 'Withdraw to this address through the website first'
78 | self.message = message
79 |
80 | def __str__(self):
81 | return 'BinanceWithdrawException: %s' % self.message
82 |
--------------------------------------------------------------------------------
/exchange/exchange.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """交易所"""
3 |
4 | from exchange.binanceExchange import BinanceExchange
5 | from exchange.binanceMargin import BinanceMargin
6 | from exchange.binanceFuture import BinanceFuture
7 | from exchange.okexExchange import OkexExchange
8 | from exchange.kuaiqiBroker import KuaiqiBroker
9 |
10 |
11 | exchangeClasses = [BinanceExchange, BinanceMargin, BinanceFuture, OkexExchange, KuaiqiBroker]
12 |
13 |
14 | def get_exchange_names():
15 | return [ ec.name for ec in exchangeClasses]
16 |
17 |
18 | def create_exchange(exchange_name):
19 | for ec in exchangeClasses:
20 | if ec.name == exchange_name:
21 | return ec(debug=True)
22 | return None
23 |
24 |
--------------------------------------------------------------------------------
/exchange/okex/Client.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # encoding: utf-8
4 | #客户端调用,用于查看API返回结果
5 |
6 | from OkcoinSpotAPI import OKCoinSpot
7 | from OkcoinFutureAPI import OKCoinFuture
8 | import os
9 |
10 | #初始化apikey,secretkey,url
11 | apikey = os.environ.get('OKEX_API_KEY')
12 | secretkey = os.environ.get('OKEX_SECRET_KEY')
13 | okcoinRESTURL = 'www.okex.com' #请求注意:国内账号需要 修改为 www.okcoin.cn
14 |
15 | #现货API
16 | okcoinSpot = OKCoinSpot(okcoinRESTURL,apikey,secretkey)
17 |
18 | #期货API
19 | okcoinFuture = OKCoinFuture(okcoinRESTURL,apikey,secretkey)
20 |
21 | coin = 'dpy_eth'
22 |
23 | # print(len(okcoinSpot.get_kline(coin, '1day', 7)))
24 | # print (u' Spot ticker ')
25 | # print (okcoinSpot.ticker(coin))
26 | #
27 | # print (u' Spot depth ')
28 | # print (okcoinSpot.depth(coin))
29 | #
30 | # print (u' Spot trades history')
31 | # print (okcoinSpot.trades(coin))
32 |
33 | #print (u' Spot user info ')
34 | #print (okcoinSpot.userinfo())
35 |
36 | #print (u' Spot trade ')
37 | #print (okcoinSpot.trade(coin,'buy','0.1','0.2'))
38 |
39 | #print (u' 现货批量下单 ')
40 | #print (okcoinSpot.batchTrade(coin,'buy','[{price:0.1,amount:0.2},{price:0.1,amount:0.2}]'))
41 |
42 | #print (u' 现货取消订单 ')
43 | #print (okcoinSpot.cancelOrder(coin,'18243073'))
44 |
45 | #print (u' 现货订单信息查询 ')
46 | print (okcoinSpot.orderinfo(coin,'-1'))
47 |
48 | # print (u' 现货批量订单信息查询 ')
49 | # print (okcoinSpot.ordersinfo(coin,'18243800,18243801,18243644','0'))
50 |
51 | print (u' 现货历史订单信息查询 ')
52 | print (okcoinSpot.orderHistory(coin,'1','1','20'))
53 |
54 | #print (u' 期货行情信息')
55 | #print (okcoinFuture.future_ticker(coin,'this_week'))
56 |
57 | #print (u' 期货市场深度信息')
58 | #print (okcoinFuture.future_depth('btc_usd','this_week','6'))
59 |
60 | #print (u'期货交易记录信息')
61 | #print (okcoinFuture.future_trades(coin,'this_week'))
62 |
63 | #print (u'期货指数信息')
64 | #print (okcoinFuture.future_index(coin))
65 |
66 | #print (u'美元人民币汇率')
67 | #print (okcoinFuture.exchange_rate())
68 |
69 | #print (u'获取预估交割价')
70 | #print (okcoinFuture.future_estimated_price(coin))
71 |
72 | #print (u'获取全仓账户信息')
73 | #print (okcoinFuture.future_userinfo())
74 |
75 | #print (u'获取全仓持仓信息')
76 | #print (okcoinFuture.future_position(coin,'this_week'))
77 |
78 | #print (u'期货下单')
79 | #print (okcoinFuture.future_trade(coin,'this_week','0.1','1','1','0','20'))
80 |
81 | #print (u'期货批量下单')
82 | #print (okcoinFuture.future_batchTrade(coin,'this_week','[{price:0.1,amount:1,type:1,match_price:0},{price:0.1,amount:3,type:1,match_price:0}]','20'))
83 |
84 | #print (u'期货取消订单')
85 | #print (okcoinFuture.future_cancel(coin,'this_week','47231499'))
86 |
87 | #print (u'期货获取订单信息')
88 | #print (okcoinFuture.future_orderinfo(coin,'this_week','47231812','0','1','2'))
89 |
90 | #print (u'期货逐仓账户信息')
91 | #print (okcoinFuture.future_userinfo_4fix())
92 |
93 | #print (u'期货逐仓持仓信息')
94 | #print (okcoinFuture.future_position_4fix(coin,'this_week',1))
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/exchange/okex/HttpMD5Util.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | #用于进行http请求,以及MD5加密,生成签名的工具类
4 |
5 | import http.client
6 | import urllib
7 | import json
8 | import hashlib
9 | import time
10 |
11 | def buildMySign(params,secretKey):
12 | sign = ''
13 | for key in sorted(params.keys()):
14 | sign += key + '=' + str(params[key]) +'&'
15 | data = sign+'secret_key='+secretKey
16 | return hashlib.md5(data.encode("utf8")).hexdigest().upper()
17 |
18 | def httpGet(url,resource,params=''):
19 | conn = http.client.HTTPSConnection(url, timeout=10)
20 | conn.request("GET",resource + '?' + params)
21 | response = conn.getresponse()
22 | data = response.read().decode('utf-8')
23 | return json.loads(data)
24 |
25 | def httpPost(url,resource,params):
26 | headers = {
27 | "Content-type" : "application/x-www-form-urlencoded",
28 | }
29 | conn = http.client.HTTPSConnection(url, timeout=10)
30 | temp_params = urllib.parse.urlencode(params)
31 | conn.request("POST", resource, temp_params, headers)
32 | response = conn.getresponse()
33 | data = response.read().decode('utf-8')
34 | params.clear()
35 | conn.close()
36 | return data
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/exchange/okex/OkcoinFutureAPI.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | #用于访问OKCOIN 期货REST API
4 | from HttpMD5Util import buildMySign,httpGet,httpPost
5 |
6 | class OKCoinFuture:
7 |
8 | def __init__(self,url,apikey,secretkey):
9 | self.__url = url
10 | self.__apikey = apikey
11 | self.__secretkey = secretkey
12 |
13 | #OKCOIN期货行情信息
14 | def future_ticker(self,symbol,contractType):
15 | FUTURE_TICKER_RESOURCE = "/api/v1/future_ticker.do"
16 | params = ''
17 | if symbol:
18 | params += '&symbol=' + symbol if params else 'symbol=' +symbol
19 | if contractType:
20 | params += '&contract_type=' + contractType if params else 'contract_type=' +symbol
21 | return httpGet(self.__url,FUTURE_TICKER_RESOURCE,params)
22 |
23 | #OKCoin期货市场深度信息
24 | def future_depth(self,symbol,contractType,size):
25 | FUTURE_DEPTH_RESOURCE = "/api/v1/future_depth.do"
26 | params = ''
27 | if symbol:
28 | params += '&symbol=' + symbol if params else 'symbol=' +symbol
29 | if contractType:
30 | params += '&contract_type=' + contractType if params else 'contract_type=' +symbol
31 | if size:
32 | params += '&size=' + size if params else 'size=' + size
33 | return httpGet(self.__url,FUTURE_DEPTH_RESOURCE,params)
34 |
35 | #OKCoin期货交易记录信息
36 | def future_trades(self,symbol,contractType):
37 | FUTURE_TRADES_RESOURCE = "/api/v1/future_trades.do"
38 | params = ''
39 | if symbol:
40 | params += '&symbol=' + symbol if params else 'symbol=' +symbol
41 | if contractType:
42 | params += '&contract_type=' + contractType if params else 'contract_type=' +symbol
43 | return httpGet(self.__url,FUTURE_TRADES_RESOURCE,params)
44 |
45 | #OKCoin期货指数
46 | def future_index(self,symbol):
47 | FUTURE_INDEX = "/api/v1/future_index.do"
48 | params=''
49 | if symbol:
50 | params = 'symbol=' +symbol
51 | return httpGet(self.__url,FUTURE_INDEX,params)
52 |
53 | #获取美元人民币汇率
54 | def exchange_rate(self):
55 | EXCHANGE_RATE = "/api/v1/exchange_rate.do"
56 | return httpGet(self.__url,EXCHANGE_RATE,'')
57 |
58 | #获取预估交割价
59 | def future_estimated_price(self,symbol):
60 | FUTURE_ESTIMATED_PRICE = "/api/v1/future_estimated_price.do"
61 | params=''
62 | if symbol:
63 | params = 'symbol=' +symbol
64 | return httpGet(self.__url,FUTURE_ESTIMATED_PRICE,params)
65 |
66 | #期货全仓账户信息
67 | def future_userinfo(self):
68 | FUTURE_USERINFO = "/api/v1/future_userinfo.do?"
69 | params ={}
70 | params['api_key'] = self.__apikey
71 | params['sign'] = buildMySign(params,self.__secretkey)
72 | return httpPost(self.__url,FUTURE_USERINFO,params)
73 |
74 | #期货全仓持仓信息
75 | def future_position(self,symbol,contractType):
76 | FUTURE_POSITION = "/api/v1/future_position.do?"
77 | params = {
78 | 'api_key':self.__apikey,
79 | 'symbol':symbol,
80 | 'contract_type':contractType
81 | }
82 | params['sign'] = buildMySign(params,self.__secretkey)
83 | return httpPost(self.__url,FUTURE_POSITION,params)
84 |
85 | #期货下单
86 | def future_trade(self,symbol,contractType,price='',amount='',tradeType='',matchPrice='',leverRate=''):
87 | FUTURE_TRADE = "/api/v1/future_trade.do?"
88 | params = {
89 | 'api_key':self.__apikey,
90 | 'symbol':symbol,
91 | 'contract_type':contractType,
92 | 'amount':amount,
93 | 'type':tradeType,
94 | 'match_price':matchPrice,
95 | 'lever_rate':leverRate
96 | }
97 | if price:
98 | params['price'] = price
99 | params['sign'] = buildMySign(params,self.__secretkey)
100 | return httpPost(self.__url,FUTURE_TRADE,params)
101 |
102 | #期货批量下单
103 | def future_batchTrade(self,symbol,contractType,orders_data,leverRate):
104 | FUTURE_BATCH_TRADE = "/api/v1/future_batch_trade.do?"
105 | params = {
106 | 'api_key':self.__apikey,
107 | 'symbol':symbol,
108 | 'contract_type':contractType,
109 | 'orders_data':orders_data,
110 | 'lever_rate':leverRate
111 | }
112 | params['sign'] = buildMySign(params,self.__secretkey)
113 | return httpPost(self.__url,FUTURE_BATCH_TRADE,params)
114 |
115 | #期货取消订单
116 | def future_cancel(self,symbol,contractType,orderId):
117 | FUTURE_CANCEL = "/api/v1/future_cancel.do?"
118 | params = {
119 | 'api_key':self.__apikey,
120 | 'symbol':symbol,
121 | 'contract_type':contractType,
122 | 'order_id':orderId
123 | }
124 | params['sign'] = buildMySign(params,self.__secretkey)
125 | return httpPost(self.__url,FUTURE_CANCEL,params)
126 |
127 | #期货获取订单信息
128 | def future_orderinfo(self,symbol,contractType,orderId,status,currentPage,pageLength):
129 | FUTURE_ORDERINFO = "/api/v1/future_order_info.do?"
130 | params = {
131 | 'api_key':self.__apikey,
132 | 'symbol':symbol,
133 | 'contract_type':contractType,
134 | 'order_id':orderId,
135 | 'status':status,
136 | 'current_page':currentPage,
137 | 'page_length':pageLength
138 | }
139 | params['sign'] = buildMySign(params,self.__secretkey)
140 | return httpPost(self.__url,FUTURE_ORDERINFO,params)
141 |
142 | #期货逐仓账户信息
143 | def future_userinfo_4fix(self):
144 | FUTURE_INFO_4FIX = "/api/v1/future_userinfo_4fix.do?"
145 | params = {'api_key':self.__apikey}
146 | params['sign'] = buildMySign(params,self.__secretkey)
147 | return httpPost(self.__url,FUTURE_INFO_4FIX,params)
148 |
149 | #期货逐仓持仓信息
150 | def future_position_4fix(self,symbol,contractType,type1):
151 | FUTURE_POSITION_4FIX = "/api/v1/future_position_4fix.do?"
152 | params = {
153 | 'api_key':self.__apikey,
154 | 'symbol':symbol,
155 | 'contract_type':contractType,
156 | 'type':type1
157 | }
158 | params['sign'] = buildMySign(params,self.__secretkey)
159 | return httpPost(self.__url,FUTURE_POSITION_4FIX,params)
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/exchange/okex/OkcoinSpotAPI.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # 用于访问OKCOIN 现货REST API
4 | from .HttpMD5Util import buildMySign, httpGet, httpPost
5 |
6 |
7 | class OKCoinSpot:
8 |
9 | def __init__(self, url, apikey, secretkey):
10 | self.__url = url
11 | self.__apikey = apikey
12 | self.__secretkey = secretkey
13 |
14 | # 获取OKCOIN现货行情信息
15 | def ticker(self, symbol=''):
16 | TICKER_RESOURCE = "/api/v1/ticker.do"
17 | params = ''
18 | if symbol:
19 | params = 'symbol=%(symbol)s' % {'symbol': symbol}
20 | return httpGet(self.__url, TICKER_RESOURCE, params)
21 |
22 | # 获取OKCOIN现货市场深度信息
23 | def depth(self, symbol=''):
24 | DEPTH_RESOURCE = "/api/v1/depth.do"
25 | params = ''
26 | if symbol:
27 | params = 'symbol=%(symbol)s' % {'symbol': symbol}
28 | return httpGet(self.__url, DEPTH_RESOURCE, params)
29 |
30 | # 获取OKCOIN现货历史交易信息
31 |
32 | def trades(self, symbol=''):
33 | TRADES_RESOURCE = "/api/v1/trades.do"
34 | params = ''
35 | if symbol:
36 | params = 'symbol=%(symbol)s' % {'symbol': symbol}
37 | return httpGet(self.__url, TRADES_RESOURCE, params)
38 |
39 | # 获取用户现货账户信息
40 | def userinfo(self):
41 | USERINFO_RESOURCE = "/api/v1/userinfo.do"
42 | params = {}
43 | params['api_key'] = self.__apikey
44 | params['sign'] = buildMySign(params, self.__secretkey)
45 | return httpPost(self.__url, USERINFO_RESOURCE, params)
46 |
47 | # 现货交易
48 | def trade(self, symbol, tradeType, price='', amount=''):
49 | TRADE_RESOURCE = "/api/v1/trade.do"
50 | params = {
51 | 'api_key': self.__apikey,
52 | 'symbol': symbol,
53 | 'type': tradeType
54 | }
55 | if price:
56 | params['price'] = price
57 | if amount:
58 | params['amount'] = amount
59 |
60 | params['sign'] = buildMySign(params, self.__secretkey)
61 | return httpPost(self.__url, TRADE_RESOURCE, params)
62 |
63 | # 现货批量下单
64 | def batchTrade(self, symbol, tradeType, orders_data):
65 | BATCH_TRADE_RESOURCE = "/api/v1/batch_trade.do"
66 | params = {
67 | 'api_key': self.__apikey,
68 | 'symbol': symbol,
69 | 'type': tradeType,
70 | 'orders_data': orders_data
71 | }
72 | params['sign'] = buildMySign(params, self.__secretkey)
73 | return httpPost(self.__url, BATCH_TRADE_RESOURCE, params)
74 |
75 | # 现货取消订单
76 | def cancelOrder(self, symbol, orderId):
77 | CANCEL_ORDER_RESOURCE = "/api/v1/cancel_order.do"
78 | params = {
79 | 'api_key': self.__apikey,
80 | 'symbol': symbol,
81 | 'order_id': orderId
82 | }
83 | params['sign'] = buildMySign(params, self.__secretkey)
84 | return httpPost(self.__url, CANCEL_ORDER_RESOURCE, params)
85 |
86 | # 现货订单信息查询
87 | def orderinfo(self, symbol, orderId):
88 | ORDER_INFO_RESOURCE = "/api/v1/order_info.do"
89 | params = {
90 | 'api_key': self.__apikey,
91 | 'symbol': symbol,
92 | 'order_id': orderId
93 | }
94 | params['sign'] = buildMySign(params, self.__secretkey)
95 | return httpPost(self.__url, ORDER_INFO_RESOURCE, params)
96 |
97 | # 现货批量订单信息查询
98 | def ordersinfo(self, symbol, orderId, tradeType):
99 | ORDERS_INFO_RESOURCE = "/api/v1/orders_info.do"
100 | params = {
101 | 'api_key': self.__apikey,
102 | 'symbol': symbol,
103 | 'order_id': orderId,
104 | 'type': tradeType
105 | }
106 | params['sign'] = buildMySign(params, self.__secretkey)
107 | return httpPost(self.__url, ORDERS_INFO_RESOURCE, params)
108 |
109 | # 现货获得历史订单信息
110 | def orderHistory(self, symbol, status, currentPage, pageLength):
111 | ORDER_HISTORY_RESOURCE = "/api/v1/order_history.do"
112 | params = {
113 | 'api_key': self.__apikey,
114 | 'symbol': symbol,
115 | 'status': status,
116 | 'current_page': currentPage,
117 | 'page_length': pageLength
118 | }
119 | params['sign'] = buildMySign(params, self.__secretkey)
120 | return httpPost(self.__url, ORDER_HISTORY_RESOURCE, params)
121 |
122 | def get_kline(self, symbol, line_type, size=0, since=''):
123 | """
124 | :param symbol:
125 | :param line_type:
126 | :param size:
127 | :param since:
128 | :return:
129 | list[0]: UTC
130 | list[1]: start price
131 | list[2]: top price
132 | list[3]: bottom price
133 | list[4]: close price
134 | list[5]: volume
135 | """
136 | resource = "/api/v1/kline.do"
137 | if not symbol or not line_type:
138 | print('Error: symbol and line_type should be input')
139 | return []
140 | params = 'symbol=%s' % symbol
141 | params += '&type=%s' % line_type
142 | if size:
143 | params += '&size=%s' % size
144 | if since:
145 | params += '&since=%s' % since
146 | print(params)
147 | return httpGet(self.__url, resource, params)
148 |
--------------------------------------------------------------------------------
/exchange/okex/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/exchange/okex/__init__.py
--------------------------------------------------------------------------------
/exchange/okexExchange.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import os
3 | import common.xquant as xq
4 | from .okex.OkcoinSpotAPI import OKCoinSpot
5 |
6 | api_key = os.environ.get('OKEX_API_KEY')
7 | secret_key = os.environ.get('OKEX_SECRET_KEY')
8 | rest_url = 'www.okex.com'
9 |
10 |
11 | class OkexExchange:
12 | """docstring for OkexExchange"""
13 | name = "okex"
14 |
15 | def __init__(self, debug=False):
16 | self.client = OKCoinSpot(rest_url, api_key, secret_key)
17 |
18 | def __get_coinkey(self, coin):
19 | return coin.lower()
20 |
21 | def __trans_symbol(self, symbol):
22 | target_coin, base_coin = xq.get_symbol_coins(symbol)
23 | return '%s_%s' % (self.__get_coinkey(target_coin), self.__get_coinkey(base_coin))
24 |
25 | def _trans_side(self, side):
26 | if side == xq.SIDE_BUY:
27 | return 'buy'
28 | elif side == xq.SIDE_SELL:
29 | return 'sell'
30 | else:
31 | return None
32 |
33 | @staticmethod
34 | def get_kline_column_names():
35 | return ['open_time', 'open','high','low','close','volume','close_time']
36 |
37 | def __get_klines(self, symbol, interval, size, since):
38 | exchange_symbol = __trans_symbol(symbol)
39 | klines = self.client.get_kline(symbol=exchange_symbol, interval=interval, size=size)
40 | return klines
41 |
42 | def get_klines_1day(self, symbol, size=300, since=''):
43 | return self.__get_klines(symbol, '1day', size, since)
44 |
45 |
46 | def get_balances(self, *coins):
47 | coin_balances = []
48 | account = self.client.get_account()
49 | free = account['free']
50 | frozen = account['frozen']
51 |
52 | for coin in coins:
53 | coinKey = self.__get_coinkey(coin)
54 | balance = xq.create_balance(coin, free[coinKey], frozen[coinKey])
55 | coin_balances.append(balance)
56 |
57 | if len(coin_balances) <= 0:
58 | return
59 | elif len(coin_balances) == 1:
60 | return coin_balances[0]
61 | else:
62 | return tuple(coin_balances)
63 |
64 | def send_order(self, side, type, symbol, price, amount, client_order_id=''):
65 | exchange_symbol = __trans_symbol(symbol)
66 | self.debug('send order: pair(%s), side(%s), price(%s), amount(%s)' % (exchange_symbol, side, price, amount))
67 |
68 | okex_side = _trans_side(side)
69 | if okex_side is None:
70 | return
71 |
72 | if type != xq.ORDER_TYPE_LIMIT:
73 | return
74 |
75 | ret = json.loads(self.client.trade(exchange_symbol, okex_side, price=str(price), amount=str(amount)))
76 | # self.debug(ret)
77 | try:
78 | if ret['result']:
79 | # self.debug('Return buy order ID: %s' % ret['order_id'])
80 | return ret['order_id']
81 | else:
82 | self.debug('Place order failed')
83 | return None
84 | except Exception:
85 | self.debug('Error result: %s' % ret)
86 | return None
87 |
88 | def cancel_order(self, symbol, order_id):
89 | exchange_symbol = self.__trans_symbol(symbol)
90 | self.client.cancel_order(symbol=exchange_symbol, orderId=order_id)
91 |
--------------------------------------------------------------------------------
/importer/binance.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import time
5 | from datetime import datetime, timedelta
6 | import db.mongodb as md
7 | import common.xquant as xq
8 | import common.kline as kl
9 | import exchange as ex
10 | from setup import *
11 | import pandas as pd
12 | from importer import add_common_arguments, split_time_range
13 |
14 | if __name__ == "__main__":
15 | parser = add_common_arguments('Binance Importer')
16 | args = parser.parse_args()
17 | # print(args)
18 | if not (args.s and args.k and args.m):
19 | parser.print_help()
20 | exit(1)
21 |
22 | symbol = args.s
23 | interval = timedelta(seconds=kl.get_interval_seconds(args.k))
24 |
25 | collection = kl.get_kline_collection(symbol, args.k)
26 | #print("collection: ", collection)
27 |
28 | db = md.MongoDB(mongo_user, mongo_pwd, args.m, db_url)
29 | db.ensure_index(collection, [("open_time",1)], unique=True)
30 |
31 | exchange = ex.create_exchange(args.m)
32 | if not exchange:
33 | print("market data source error!")
34 | exit(1)
35 | exchange.connect()
36 |
37 | # 注意,下面代码有隐患,在上午8点前取1d、12h时,最后的一个是不完整的,后续再整改
38 | if args.r:
39 | start_time, end_time = split_time_range(args.r)
40 | else:
41 | # 续接db中最后一条记录,至今天之前
42 | klines = db.find_sort(collection, {}, 'open_time', -1, 1)
43 | if len(klines) > 0:
44 | start_time = (exchange.get_time_from_data_ts(klines[0]["open_time"]) + interval).replace(hour=0,minute=0,second=0,microsecond=0)
45 | else:
46 | start_time = None
47 | end_time = datetime.now().replace(hour=0,minute=0,second=0,microsecond=0)
48 |
49 | if not start_time:
50 | start_time = exchange.start_time
51 |
52 | size = 1000
53 | tmp_time = start_time
54 |
55 | while tmp_time < end_time:
56 | print(tmp_time, end=" ")
57 | if (tmp_time + size * interval) > end_time:
58 | batch = int((end_time - tmp_time)/interval)
59 | else:
60 | batch = size
61 | # print(batch)
62 |
63 | klines = exchange.get_klines(symbol, args.k, size=batch, since=exchange.get_data_ts_from_time(tmp_time))
64 |
65 | klines_df = pd.DataFrame(klines, columns=exchange.get_kline_column_names())
66 |
67 | klen = len(klines)
68 | print("klines len: ", klen)
69 | #print(klines)
70 | #print("klines[0]: ", klines.ix[0])
71 | #print("klines[-1]: ", klines.ix[klen-1])
72 | #print("records: ", klines.to_dict('records'))
73 | if not db.insert_many(collection, klines_df.to_dict('records')):
74 | for item in klines_df.to_dict('records'):
75 | db.insert_one(collection, item)
76 | tmp_time += batch * interval
77 |
78 | # df = db.get_kline(no_id=False)
79 | # pd.set_option('display.max_columns', None)
80 | # pd.set_option('display.max_rows', None)
81 | # pd.set_option('display.max_colwidth', -1)
82 | # print(df)
83 |
--------------------------------------------------------------------------------
/importer/check.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | from datetime import datetime, timedelta
5 | import db.mongodb as md
6 | from setup import *
7 | import common.xquant as xq
8 | import common.kline as kl
9 | from importer import add_common_arguments, split_time_range
10 |
11 | if __name__ == "__main__":
12 | parser = add_common_arguments('check kline')
13 | parser.add_argument('-d', '--display', action = 'store_true', help='display info')
14 | args = parser.parse_args()
15 | # print(args)
16 |
17 | if not (args.s and args.r and args.k and args.m):
18 | parser.print_help()
19 | exit(1)
20 |
21 | start_time, end_time = split_time_range(args.r)
22 | print("range: [%s, %s)"%(start_time, end_time))
23 |
24 | interval = args.k
25 | collection = kl.get_kline_collection(args.s, interval)
26 | td = kl.get_interval_timedelta(interval)
27 | period = kl.get_interval_seconds(interval)
28 | tick_time = kl.get_open_time(interval, start_time)
29 | if tick_time < start_time:
30 | tick_time = kl.get_open_time(interval, start_time+td)
31 |
32 |
33 | db = md.MongoDB(mongo_user, mongo_pwd, args.m, db_url)
34 | target_len = int((int(end_time.timestamp()) - int(start_time.timestamp())) / period)
35 | print("Target length:", target_len)
36 |
37 | klines = db.find_sort(collection, {"open_time": {
38 | "$gte": int(start_time.timestamp())*1000,
39 | "$lt": int(end_time.timestamp())*1000}}, 'open_time', 1)
40 | klines_len = len(klines)
41 | print("klines len: %d"% klines_len)
42 |
43 | i = 0
44 | repeat_count = 0
45 | wrong_count = 0
46 | miss_count = 0
47 | while tick_time < end_time:
48 | if i >= klines_len:
49 | miss_count += (end_time - tick_time).total_seconds()/td.total_seconds()
50 | print("miss tail %s~%s" % (tick_time, end_time) )
51 | break
52 |
53 | next_tick_time = tick_time + td
54 | open_time = klines[i]["open_time"]/1000
55 | #print("tick_time %s, next_tick_time %s" % (tick_time, next_tick_time) )
56 | #print("tick_time %s, open_time %s" % (tick_time.timestamp(), open_time) )
57 |
58 | if open_time < tick_time.timestamp():
59 | i += 1
60 | repeat_count += 1
61 | print("repeat %s" % (datetime.fromtimestamp(open_time)))
62 | elif open_time == tick_time.timestamp():
63 | kline = klines[i]
64 | if args.display:
65 | open_time = datetime.fromtimestamp(int(kline["open_time"])/1000)
66 | close_time = datetime.fromtimestamp(int(kline["close_time"])/1000)
67 | print("(%s ~ %s) high: %s low: %s close: %s volume: %s" %
68 | (open_time, close_time, kline["high"], kline["low"], kline["close"], kline["volume"]))
69 | i += 1
70 | tick_time = next_tick_time
71 | elif open_time < next_tick_time.timestamp():
72 | i += 1
73 | tick_time = next_tick_time
74 | wrong_count += 1
75 | print("tick_time %s, wrong 2 %s" % (tick_time, datetime.fromtimestamp(open_time)))
76 | else:
77 | miss_count += 1
78 | print("miss %s" % (tick_time) )
79 | tick_time = next_tick_time
80 |
81 | print("repeat count: ", repeat_count)
82 | print("wrong count: ", wrong_count)
83 | print("miss count: ", miss_count)
84 |
--------------------------------------------------------------------------------
/importer/download.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import time
5 | from datetime import datetime, timedelta
6 | import common.xquant as xq
7 | import common.kline as kl
8 | from exchange.exchange import create_exchange
9 | from db.mongodb import get_mongodb
10 | from setup import *
11 | import pandas as pd
12 | from importer import add_common_arguments, split_time_range
13 |
14 | if __name__ == "__main__":
15 | parser = add_common_arguments('Binance Importer')
16 | args = parser.parse_args()
17 | # print(args)
18 | if not (args.s and args.k and args.m):
19 | parser.print_help()
20 | exit(1)
21 |
22 | symbol = args.s
23 | interval = timedelta(seconds=kl.get_interval_seconds(args.k))
24 |
25 | collection = kl.get_kline_collection(symbol, args.k)
26 | #print("collection: ", collection)
27 |
28 | db = get_mongodb(args.m)
29 | db.ensure_index(collection, [("open_time",1)], unique=True)
30 |
31 | exchange = create_exchange(args.m)
32 | if not exchange:
33 | print("market data source error!")
34 | exit(1)
35 |
36 | if args.r:
37 | start_time, end_time = split_time_range(args.r)
38 | else:
39 | # 续接db中最后一条记录,至今天之前
40 | klines = db.find_sort(collection, {}, 'open_time', -1, 1)
41 | if len(klines) > 0:
42 | start_time = (exchange.get_time_from_data_ts(klines[0]["open_time"]) + interval)
43 | else:
44 | start_time = None
45 | end_time = datetime.now()
46 |
47 | print("%s connecting..." % (args.m), end='')
48 | exchange.connect()
49 | print('ok!')
50 |
51 | open_hour = exchange.start_time.hour
52 |
53 | if start_time.hour != open_hour:
54 | print("open time(%s) hour error! %s open time hour: %s" % (start_time, args.m, open_hour))
55 | exit(1)
56 |
57 | if end_time.hour < open_hour:
58 | end_time -= timedelta(days=1)
59 | end_time = end_time.replace(hour=open_hour, minute=0, second=0, microsecond=0)
60 | print("time range: %s ~ %s " % (start_time, end_time))
61 |
62 | size = exchange.max_count_of_single_download_kl
63 | tmp_time = start_time
64 | while tmp_time < end_time:
65 | print(tmp_time, end=" ")
66 |
67 | size_interval = size * interval
68 | if (tmp_time + size_interval) > end_time:
69 | batch = int((end_time - tmp_time)/interval)
70 | else:
71 | batch = size
72 | # print(batch)
73 |
74 | klines = exchange.get_klines(symbol, args.k, size=batch, since=exchange.get_data_ts_from_time(tmp_time))
75 | klines_df = pd.DataFrame(klines, columns=exchange.kline_column_names)
76 | klen = len(klines)
77 | print("klines len: ", klen)
78 | for i in range(klen-1, -1, -1):
79 | last_open_time = exchange.get_time_from_data_ts(klines_df["open_time"].values[i])
80 | if last_open_time + interval <= end_time:
81 | break
82 | klines_df = klines_df.drop([i])
83 |
84 | if not db.insert_many(collection, klines_df.to_dict('records')):
85 | for item in klines_df.to_dict('records'):
86 | db.insert_one(collection, item)
87 |
88 | last_time = exchange.get_time_from_data_ts(klines_df["open_time"].values[-1]) + interval
89 | if last_time > tmp_time + batch * interval:
90 | batch = int((last_time - tmp_time)/interval)
91 | tmp_time += batch * interval
92 |
--------------------------------------------------------------------------------
/importer/download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #source ~/.profile
4 | range=$1
5 | echo $range
6 |
7 | mds=(binance)
8 | echo ${mds[*]}
9 |
10 | symbles=(btc_usdt bnb_usdt eth_usdt btc_pax btc_usdc)
11 | echo ${symbles[*]}
12 |
13 | kline_types=(1m 5m 15m 30m 1h 2h 4h 6h 8h 12h 1d)
14 | echo ${kline_types[*]}
15 |
16 | for md in ${mds[*]}
17 | do
18 | for symble in ${symbles[*]}
19 | do
20 | #echo $symble
21 |
22 | for kline_type in ${kline_types[*]}
23 | do
24 | #echo $kline_type
25 | echo "downloading ${range} ${md} ${symble} ${kline_type}"
26 | if [ $range ]
27 | then
28 | python3 download.py -m $md -s $symble -k $kline_type -r $range
29 | else
30 | python3 download.py -m $md -s $symble -k $kline_type
31 | fi
32 | done
33 | done
34 | done
--------------------------------------------------------------------------------
/importer/download2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | import time
6 | from datetime import datetime, timedelta
7 | import db.mongodb as md
8 | import common.xquant as xq
9 | import common.kline as kl
10 | from exchange.exchange import create_exchange
11 | from db.mongodb import get_mongodb
12 | from setup import *
13 | import pandas as pd
14 | from importer import add_common_arguments, split_time_range
15 |
16 |
17 | def download_from_exchange(exchange, db, symbol, kline_type, time_range):
18 | print('%12s %6s ' % (' ', kline_type), end = '' )
19 | collection = kl.get_kline_collection(symbol, kline_type)
20 | open_time_key = exchange.kline_key_open_time
21 | db.ensure_index(collection, [(open_time_key,1)], unique=True)
22 |
23 | interval = kl.get_interval_timedelta(kline_type)
24 | if time_range:
25 | start_time, end_time = split_time_range(time_range)
26 | else:
27 | # 续接db中最后一条记录,至今天之前
28 | klines = db.find_sort(collection, {}, open_time_key, -1, 1)
29 | if len(klines) > 0:
30 | start_time = (exchange.get_time_from_data_ts(klines[0][open_time_key]) + interval)
31 | else:
32 | start_time = exchange.start_time
33 | end_time = datetime.now()
34 |
35 | #print(kl.get_open_time(kline_type, end_time))
36 | """
37 | if start_time.hour != exchange.start_time.hour:
38 | print("open time(%s) hour error! %s open time hour: %s" % (start_time, exchange.name, exchange.start_time.hour))
39 | exit(1)
40 |
41 | if end_time.hour < exchange.start_time.hour:
42 | end_time -= timedelta(days=1)
43 | end_time = end_time.replace(hour=exchange.start_time.hour, minute=0, second=0, microsecond=0)
44 | """
45 |
46 | end_time = end_time.replace(minute=0, second=0, microsecond=0)
47 | end_time = kl.get_open_time(kline_type, end_time)
48 | print("time range: %s ~ %s " % (start_time, end_time))
49 |
50 | size = exchange.max_count_of_single_download_kl
51 | tmp_time = start_time
52 | while tmp_time < end_time:
53 | size_interval = size * interval
54 | if (tmp_time + size_interval) > end_time:
55 | batch = int((end_time - tmp_time)/interval)
56 | else:
57 | batch = size
58 | # print(batch)
59 |
60 | if batch == 0:
61 | break
62 |
63 | klines = exchange.get_klines(symbol, kline_type, size=batch, since=exchange.get_data_ts_from_time(tmp_time))
64 | klines_df = pd.DataFrame(klines, columns=exchange.kline_column_names)
65 | klen = len(klines)
66 | print(" %20s start time: %s %s" % (' ', tmp_time, klen))
67 | for i in range(klen-1, -1, -1):
68 | last_open_time = exchange.get_time_from_data_ts(klines_df[open_time_key].values[i])
69 | if last_open_time + interval <= end_time:
70 | break
71 | klines_df = klines_df.drop([i])
72 |
73 | db_datalines = klines_df.to_dict('records')
74 | if len(db_datalines) == 0:
75 | break
76 | if not db.insert_many(collection, db_datalines):
77 | for item in db_datalines:
78 | db.insert_one(collection, item)
79 |
80 | last_time = exchange.get_time_from_data_ts(klines_df[open_time_key].values[-1]) + interval
81 | if last_time > tmp_time + batch * interval:
82 | batch = int((last_time - tmp_time)/interval)
83 | tmp_time += batch * interval
84 |
85 |
86 | if __name__ == "__main__":
87 | parser = argparse.ArgumentParser(description='klines print or check')
88 | parser.add_argument('-m', help='market data source')
89 | parser.add_argument('-r', help='time range (2018-7-1T8' + xq.time_range_split + '2018-8-1T8)')
90 | parser.add_argument('-ss', help='symbols: btc_usdt,eth_usdt')
91 | parser.add_argument('-kts', help='kline types: 1m,4h,1d')
92 | args = parser.parse_args()
93 | print(args)
94 | if not (args.ss and args.kts and args.m):
95 | parser.print_help()
96 | exit(1)
97 |
98 | exchange = create_exchange(args.m)
99 | if not exchange:
100 | print("market data source error!")
101 | exit(1)
102 |
103 | print("%s connecting..." % (args.m), end='')
104 | exchange.connect()
105 | print('ok!')
106 |
107 | db = get_mongodb(args.m)
108 |
109 | symbols = args.ss.split(',')
110 | kline_types = args.kts.split(',')
111 | for symbol in symbols:
112 | print('%12s ' % (symbol))
113 | for kline_type in kline_types:
114 | download_from_exchange(exchange, db, symbol, kline_type, args.r)
115 |
116 |
--------------------------------------------------------------------------------
/importer/download_binance.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #source ~/.profile
4 | range=$1
5 | echo $range
6 |
7 | mds=(binance)
8 | echo ${mds[*]}
9 |
10 | symbles=btc_usdt,eth_usdt,btc_busd,btc_usdc
11 | echo ${symbles}
12 |
13 | kline_types=1m,1h,2h,4h,1d
14 | echo ${kline_types}
15 |
16 | for md in ${mds[*]}
17 | do
18 |
19 | #echo $kline_type
20 | echo "downloading ${range} ${md}"
21 | if [ $range ]
22 | then
23 | python3 download2.py -m $md -ss $symbles -kts $kline_types -r $range
24 | else
25 | python3 download2.py -m $md -ss $symbles -kts $kline_types
26 | fi
27 |
28 | done
--------------------------------------------------------------------------------
/importer/download_kuaiqi.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #source ~/.profile
4 | range=$1
5 | echo $range
6 |
7 | mds=(kuaiqi)
8 | echo ${mds[*]}
9 |
10 | symbles=SHFE.cu_rmb
11 | echo ${symbles}
12 |
13 | kline_types=1m,5m,15m,1h,1d
14 | echo ${kline_types}
15 |
16 | for md in ${mds[*]}
17 | do
18 |
19 | #echo $kline_type
20 | echo "downloading ${range} ${md}"
21 | if [ $range ]
22 | then
23 | python3 download2.py -m $md -ss $symbles -kts $kline_types -r $range
24 | else
25 | python3 download2.py -m $md -ss $symbles -kts $kline_types
26 | fi
27 |
28 | done
--------------------------------------------------------------------------------
/importer/fix.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | from datetime import datetime,timedelta
5 | import common.kline as kl
6 | import db.mongodb as md
7 | from setup import *
8 | from importer import add_common_arguments, split_time_range
9 |
10 |
11 | def fix_middle_kline(database, collection, start_kline, end_kline):
12 | print(start_kline)
13 | print(end_kline)
14 | mid_kline = {
15 | 'open_time': int((start_kline['open_time'] + end_kline['open_time']) / 2),
16 | 'number_of_trades': int((start_kline['number_of_trades'] + end_kline['number_of_trades']) / 2),
17 | 'close_time': int((start_kline['close_time'] + end_kline['close_time']) / 2),
18 | 'open': str((float(start_kline['open']) + float(end_kline['open'])) / 2),
19 | 'high': str((float(start_kline['high']) + float(end_kline['high'])) / 2),
20 | 'low': str((float(start_kline['low']) + float(end_kline['low'])) / 2),
21 | 'close': str((float(start_kline['close']) + float(end_kline['close'])) / 2),
22 | 'volume': str((float(start_kline['volume']) + float(end_kline['volume'])) / 2),
23 | 'quote_asset_volume': str((float(start_kline['quote_asset_volume']) + float(end_kline['quote_asset_volume'])) / 2),
24 | 'taker_buy_base_asset_volume': str((float(start_kline['taker_buy_base_asset_volume']) + float(end_kline['taker_buy_base_asset_volume'])) / 2),
25 | 'taker_buy_quote_asset_volume': str((float(start_kline['taker_buy_quote_asset_volume']) + float(end_kline['taker_buy_quote_asset_volume'])) / 2),
26 | 'ignore': str(int((float(start_kline['ignore']) + float(end_kline['ignore'])) / 2)),
27 | }
28 | print(mid_kline)
29 | database.insert_one(collection, mid_kline)
30 | return mid_kline
31 |
32 |
33 | if __name__ == "__main__":
34 | parser = add_common_arguments('fix')
35 | args = parser.parse_args()
36 | # print(args)
37 |
38 | if not (args.s and args.r and args.k and args.m):
39 | parser.print_help()
40 | exit(1)
41 |
42 | start_time, end_time = split_time_range(args.r)
43 |
44 | interval = args.k
45 | collection = kl.get_kline_collection(args.s, interval)
46 | td = kl.get_interval_timedelta(interval)
47 | period = kl.get_interval_seconds(interval)
48 | tick_time = kl.get_open_time(interval, start_time)
49 | if tick_time < start_time:
50 | tick_time = kl.get_open_time(interval, start_time+td)
51 |
52 | db = md.MongoDB(mongo_user, mongo_pwd, args.m, db_url)
53 |
54 | klines = db.find_sort(collection, {"open_time": {
55 | "$gte": int(start_time.timestamp())*1000,
56 | "$lt": int(end_time.timestamp())*1000}}, 'open_time', 1)
57 |
58 | i = 0
59 | miss_count = 0
60 | print(len(klines))
61 |
62 | period_ms = period * 1000
63 | for kline in klines:
64 | rest = kline['open_time'] % period_ms
65 | if rest:
66 | print(kline)
67 | kline['open_time'] -= rest
68 | kline['close_time'] = kline['open_time'] + period_ms - 1
69 | print(kline)
70 | db.update_one(collection, kline['_id'], kline)
71 |
72 | rest = (kline['close_time'] + 1) % period_ms
73 | if rest:
74 | print(kline)
75 | kline['close_time'] = kline['open_time'] + period_ms - 1
76 | print(kline)
77 | db.update_one(collection, kline['_id'], kline)
78 |
79 |
--------------------------------------------------------------------------------
/importer/fixKline.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | from datetime import datetime,timedelta
5 | import common.kline as kl
6 | import db.mongodb as md
7 | from setup import *
8 | from importer import add_common_arguments, split_time_range
9 |
10 |
11 | def fix_middle_kline(database, collection, start_kline, end_kline):
12 | print(start_kline)
13 | print(end_kline)
14 | mid_kline = {
15 | 'open_time': int((start_kline['open_time'] + end_kline['open_time']) / 2),
16 | 'number_of_trades': int((start_kline['number_of_trades'] + end_kline['number_of_trades']) / 2),
17 | 'close_time': int((start_kline['close_time'] + end_kline['close_time']) / 2),
18 | 'open': str((float(start_kline['open']) + float(end_kline['open'])) / 2),
19 | 'high': str((float(start_kline['high']) + float(end_kline['high'])) / 2),
20 | 'low': str((float(start_kline['low']) + float(end_kline['low'])) / 2),
21 | 'close': str((float(start_kline['close']) + float(end_kline['close'])) / 2),
22 | 'volume': str((float(start_kline['volume']) + float(end_kline['volume'])) / 2),
23 | 'quote_asset_volume': str((float(start_kline['quote_asset_volume']) + float(end_kline['quote_asset_volume'])) / 2),
24 | 'taker_buy_base_asset_volume': str((float(start_kline['taker_buy_base_asset_volume']) + float(end_kline['taker_buy_base_asset_volume'])) / 2),
25 | 'taker_buy_quote_asset_volume': str((float(start_kline['taker_buy_quote_asset_volume']) + float(end_kline['taker_buy_quote_asset_volume'])) / 2),
26 | 'ignore': str(int((float(start_kline['ignore']) + float(end_kline['ignore'])) / 2)),
27 | }
28 | print(mid_kline)
29 | database.insert_one(collection, mid_kline)
30 | return mid_kline
31 |
32 |
33 | if __name__ == "__main__":
34 | parser = add_common_arguments('fix kline')
35 | args = parser.parse_args()
36 | # print(args)
37 |
38 | if not (args.s and args.r and args.k and args.m):
39 | parser.print_help()
40 | exit(1)
41 |
42 | start_time, end_time = split_time_range(args.r)
43 |
44 | interval = args.k
45 | collection = kl.get_kline_collection(args.s, interval)
46 | td = kl.get_interval_timedelta(interval)
47 | period = kl.get_interval_seconds(interval)
48 | tick_time = kl.get_open_time(interval, start_time)
49 | if tick_time < start_time:
50 | tick_time = kl.get_open_time(interval, start_time+td)
51 |
52 | db = md.MongoDB(mongo_user, mongo_pwd, args.m, db_url)
53 |
54 | target_len = int((int(end_time.timestamp()) - int(start_time.timestamp())) / period)
55 | print("Target length:", target_len)
56 |
57 | length = db.count(collection, {"open_time": {
58 | "$gte": int(start_time.timestamp())*1000,
59 | "$lt": int(end_time.timestamp())*1000}})
60 |
61 | print("Real length:", length)
62 | if length == target_len:
63 | print('No data lost. Everything is okay.')
64 | exit(0)
65 |
66 | klines = db.find_sort(collection, {"open_time": {
67 | "$gte": int(start_time.timestamp())*1000,
68 | "$lt": int(end_time.timestamp())*1000}}, 'open_time', 1)
69 |
70 | i = 0
71 | miss_count = 0
72 | print(len(klines))
73 |
74 | while i < len(klines):
75 | ts = int(tick_time.timestamp()*1000)
76 | if ts != klines[i]["open_time"]:
77 | if i == 0:
78 | print("The first kline is lost. Cannot be fixed. Try a longer period")
79 | exit(1)
80 | print(ts, klines[i]["open_time"])
81 | start = klines[i - 1]
82 | end = klines[i]
83 | # j = 1
84 | # while (end['open_time'] - start['open_time']) % (2 * period * 1000) != 0:
85 | # end = klines[i + j]
86 | # j += 1
87 | if (end['open_time'] - start['open_time']) % (2 * period * 1000) != 0:
88 | if i < 2:
89 | print("The second kline is lost. Cannot be fixed. Try a longer period")
90 | exit(1)
91 | else:
92 | start = klines[i - 2]
93 | if (end['open_time'] - start['open_time']) % (2 * period * 1000) != 0:
94 | print("Error: Something error. Please check")
95 | print("start kline:", start)
96 | print("end kline:", end)
97 | exit(1)
98 | mid_kline = fix_middle_kline(db, collection, start, end)
99 | klines.insert(i, mid_kline)
100 | continue
101 |
102 | tick_time += td
103 | i += 1
104 |
105 |
--------------------------------------------------------------------------------
/importer/importer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | from datetime import datetime
6 | import common.xquant as xq
7 |
8 | def split_time_range(range):
9 | time_range = range.split(xq.time_range_split)
10 | start_time = datetime.strptime(time_range[0], "%Y-%m-%dT%H")
11 | end_time = datetime.strptime(time_range[1], "%Y-%m-%dT%H")
12 | return start_time, end_time
13 |
14 | def add_common_arguments(description):
15 | parser = argparse.ArgumentParser(description='klines print or check')
16 | parser.add_argument('-m', help='market data source')
17 | parser.add_argument('-s', help='symbol (btc_usdt)')
18 | parser.add_argument('-r', help='time range (2018-7-1T8' + xq.time_range_split + '2018-8-1T8)')
19 | parser.add_argument('-k', help='kline type (1m、4h、1d...)')
20 | return parser
21 |
22 |
--------------------------------------------------------------------------------
/md/exmd.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """实盘"""
3 | from datetime import datetime
4 | import utils.tools as ts
5 | import common.xquant as xq
6 | import common.kline as kl
7 | from .md import MarketingData
8 |
9 |
10 | class ExchangeMD(MarketingData):
11 | """交易所实盘数据"""
12 |
13 | def __init__(self, exchange, kline_data_type=kl.KLINE_DATA_TYPE_JSON):
14 | super().__init__(exchange, kline_data_type)
15 |
16 |
17 | def get_latest_pirce(self, symbol):
18 | kls = self.get_klines(symbol, kl.KLINE_INTERVAL_1MINUTE, 1)
19 | if len(kls) <= 0:
20 | return None, None
21 | latest_kl = kls[0]
22 | latest_price = float(latest_kl[self.kline_key_close])
23 | latest_time = self.get_kline_close_time(latest_kl)
24 | return latest_price, latest_time
25 |
26 |
27 | def get_klines(self, symbol, interval, size):
28 | """ 获取日k线 """
29 | kls = self._exchange.get_klines(symbol, interval, size)
30 |
31 | if self._exchange.kline_data_type == self.kline_data_type:
32 | return kls
33 | elif self._exchange.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
34 | return kl.trans_from_list_to_json(kls, self._exchange.kline_column_names)
35 | else:
36 | return kl.trans_from_json_to_list(kls, self._exchange.kline_column_names)
37 |
38 | def get_klines_1day(self, symbol, size):
39 | """ 获取日k线 """
40 | return self.get_klines(symbol, kl.KLINE_INTERVAL_1DAY, size)
41 |
42 |
43 |
--------------------------------------------------------------------------------
/md/md.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """marketing data"""
3 | from datetime import datetime, timedelta
4 | import common.kline as kl
5 | import exchange.exchange as ex
6 |
7 |
8 | class MarketingData:
9 | """市场数据"""
10 |
11 | def __init__(self, exchange, kline_data_type):
12 | self.kline_column_names = exchange.kline_column_names
13 | self.kline_key_open_time = exchange.kline_key_open_time
14 | self.kline_key_close_time = exchange.kline_key_close_time
15 | self.kline_key_open = exchange.kline_key_open
16 | self.kline_key_close = exchange.kline_key_close
17 | self.kline_key_high = exchange.kline_key_high
18 | self.kline_key_low = exchange.kline_key_low
19 | self.kline_key_volume = exchange.kline_key_volume
20 |
21 | self._exchange = exchange
22 | self.kline_data_type = kline_data_type
23 |
24 | self.kline_seat_open_time = self.get_kline_seat_open_time()
25 | self.kline_seat_close_time = self.get_kline_seat_close_time()
26 | self.kline_seat_open = self.get_kline_seat_open()
27 | self.kline_seat_close = self.get_kline_seat_close()
28 | self.kline_seat_high = self.get_kline_seat_high()
29 | self.kline_seat_low = self.get_kline_seat_low()
30 | self.kline_seat_volume = self.get_kline_seat_volume()
31 |
32 |
33 | def get_kline_column_names(self):
34 | return self._exchange.kline_column_names
35 |
36 | def get_kline_seat_open_time(self):
37 | if self.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
38 | return self._exchange.kline_idx_open_time
39 | else:
40 | return self._exchange.kline_key_open_time
41 |
42 | def get_kline_seat_close_time(self):
43 | if self.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
44 | return self._exchange.kline_idx_close_time
45 | else:
46 | return self._exchange.kline_key_close_time
47 |
48 | def get_kline_seat_open(self):
49 | if self.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
50 | return self._exchange.kline_idx_open
51 | else:
52 | return self._exchange.kline_key_open
53 |
54 | def get_kline_seat_close(self):
55 | if self.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
56 | return self._exchange.kline_idx_close
57 | else:
58 | return self._exchange.kline_key_close
59 |
60 | def get_kline_seat_high(self):
61 | if self.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
62 | return self._exchange.kline_idx_high
63 | else:
64 | return self._exchange.kline_key_high
65 |
66 | def get_kline_seat_low(self):
67 | if self.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
68 | return self._exchange.kline_idx_low
69 | else:
70 | return self._exchange.kline_key_low
71 |
72 | def get_kline_seat_volume(self):
73 | if self.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
74 | return self._exchange.kline_idx_volume
75 | else:
76 | return self._exchange.kline_key_volume
77 |
78 | def get_time_from_data_ts(self, ts):
79 | return self._exchange.get_time_from_data_ts(ts)
80 |
81 | def get_data_ts_from_time(self, t):
82 | return self._exchange.get_data_ts_from_time(t)
83 |
84 | def get_kline_open_time(self, kl):
85 | return self.get_time_from_data_ts(kl[self.kline_seat_open_time])
86 |
87 | def get_kline_close_time(self, kl):
88 | return self.get_time_from_data_ts(kl[self.kline_seat_close_time])
89 |
90 | def get_kline_open(self, kl):
91 | return float(kl[self.kline_seat_open])
92 |
93 | def get_kline_close(self, kl):
94 | return float(kl[self.kline_seat_close])
95 |
96 | def get_kline_high(self, kl):
97 | return float(kl[self.kline_seat_high])
98 |
99 | def get_kline_low(self, kl):
100 | return float(kl[self.kline_seat_low])
101 |
102 | def get_kline_volume(self, kl):
103 | return float(kl[self.kline_seat_volume])
104 |
--------------------------------------------------------------------------------
/monitor/daily_report.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import argparse
3 | from jinja2 import Environment, FileSystemLoader
4 | import datetime
5 | import db.mongodb as md
6 | import re
7 | from utils.email_obj import EmailObj
8 | from setup import *
9 |
10 | db_name = trade_db_name
11 |
12 | if __name__ == "__main__":
13 | parser = argparse.ArgumentParser(description='Daily Report')
14 | parser.add_argument('-i', help='Instance')
15 | parser.add_argument('-d', help='Included Days')
16 | parser.add_argument('-f', help='Log file name')
17 | parser.add_argument('-r', help='Receiver Email Address')
18 |
19 | args = parser.parse_args()
20 | # print(args)
21 | if not (args.i and args.d and args.r and args.f):
22 | parser.print_help()
23 | exit(1)
24 |
25 | instance_id = args.i
26 | days = args.d
27 | email_addr = args.r
28 | file_name = args.f
29 |
30 | template_dir = location('monitor/template')
31 | print(template_dir)
32 | subject = 'Quant Daily Report - ' + instance_id
33 |
34 | '''
35 | process = os.popen('ps aux | grep %s | grep instance_id | grep -v grep' % instance_id).read()
36 | ret = re.search('python', process)
37 | if ret is None:
38 | process_info = 'Error: Instance (%s) is dead. Please check it ASAP' % instance_id
39 | else:
40 | process_info = process[ret.span()[0]:]
41 | print(process_info)
42 | '''
43 | process_info = ''
44 | with open(file_name, 'r') as f:
45 | off = 1800
46 | f.seek(0, os.SEEK_END)
47 | f.seek(f.tell() - off, os.SEEK_SET)
48 | lines = f.readlines()
49 | for line in lines:
50 | process_info += (line + "
")
51 |
52 | print(process_info)
53 |
54 | db = md.MongoDB(mongo_user, mongo_pwd, db_name, db_url)
55 | collection = 'orders'
56 |
57 | begin_time = datetime.datetime.now() - datetime.timedelta(days=int(days))
58 |
59 | orders = db.find_sort(collection, {"instance_id": instance_id,
60 | "create_time": {"$gte": int(begin_time.timestamp())}}, 'create_time', -1)
61 |
62 | print('orders:', orders)
63 | for order in orders:
64 | del order['_id']
65 | del order['instance_id']
66 | order['create_time'] = datetime.datetime.fromtimestamp(order['create_time']).strftime("%Y-%m-%d %H:%M:%S")
67 |
68 | # construct html
69 | env = Environment(
70 | loader=FileSystemLoader(template_dir),
71 | )
72 | template = env.get_template('template.html')
73 | html = template.render(orders=orders, process_info=process_info)
74 | # print(html)
75 | email_obj = EmailObj(email_srv, email_user, email_pwd, email_port)
76 | email_obj.send_mail(subject, html, email_user, to_addr=email_addr)
77 |
--------------------------------------------------------------------------------
/monitor/daily_report.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | source ~/.profile
5 | base_dir=$(cd `dirname $0`; pwd)
6 | echo $1
7 | python3.6 $base_dir/daily_report.py -i gbtc -d 5 -r pkguowu@yahoo.com -f /home/gw/xq/gbtc.log
8 |
--------------------------------------------------------------------------------
/monitor/insert_trade.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3.6
2 | import argparse
3 | import time
4 | import db.mongodb as md
5 | import re
6 | from utils.email_obj import EmailObj
7 | from setup import *
8 |
9 | # Example:
10 | # python3.6 insert_trade.py -i gbtc -d buy -s btc_usdt -p 11000.23 -a 1.11
11 |
12 | db_name = trade_db_name
13 | collection = 'orders'
14 | record = {
15 | "create_time": time.time(),
16 | "instance_id": "",
17 | "symbol": "",
18 | "direction": "LONG",
19 | "action":"",
20 | "pst_rate": 0,
21 | "type": "LIMIT",
22 | "market_price": 0,
23 | "price": 0,
24 | "amount": 0,
25 | "status": "close",
26 | "order_id": 264988599,
27 | "cancle_amount": 0,
28 | "deal_amount": 0,
29 | "deal_value": 0,
30 | "rmk": " "
31 | }
32 |
33 | if __name__ == "__main__":
34 | parser = argparse.ArgumentParser(description='Insert trade')
35 | parser.add_argument('-i', help='Instance')
36 | parser.add_argument('-d', help='Buy or sell')
37 | parser.add_argument('-s', help='Symbol like btc_usdt')
38 | parser.add_argument('-p', help='Price', type=float)
39 | parser.add_argument('-a', help='Amount', type=float)
40 |
41 | args = parser.parse_args()
42 | print(args)
43 | if not (args.i and args.d and args.s and args.p and args.a):
44 | parser.print_help()
45 | exit(1)
46 |
47 | record['instance_id'] = args.i
48 | record['symbol'] = args.s.lower()
49 | side = args.d.upper()
50 | if side == 'BUY':
51 | record['action'] = 'OPEN'
52 | else:
53 | record['action'] = 'CLOSE'
54 | record['price'] = record['market_price'] = args.p
55 | record['amount'] = record['deal_amount'] = args.a
56 | record['deal_value'] = round(record['price'] * record['deal_amount'], 2)
57 |
58 | print('record:', record)
59 | db = md.MongoDB(mongo_user, mongo_pwd, db_name, db_url)
60 | db.insert_one(collection, record)
61 |
--------------------------------------------------------------------------------
/monitor/monitor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import argparse
3 | from jinja2 import Environment, FileSystemLoader
4 | import datetime
5 | import time
6 | import db.mongodb as md
7 | import re
8 | from utils.email_obj import EmailObj
9 | from setup import *
10 |
11 |
12 | if __name__ == "__main__":
13 | parser = argparse.ArgumentParser(description='Daily Report')
14 | # parser.add_argument('-i', help='Instance')
15 | # parser.add_argument('-d', help='Included Days')
16 | parser.add_argument('-f', help='Log file name')
17 | parser.add_argument('-r', help='Receiver Email Address')
18 |
19 | args = parser.parse_args()
20 | # print(args)
21 | if not (args.r and args.f):
22 | parser.print_help()
23 | exit(1)
24 |
25 | # instance_id = args.i
26 | # days = args.d
27 | email_addr = args.r
28 | file_name = args.f
29 |
30 | # template_dir = location('monitor/template')
31 | # print(template_dir)
32 | subject = 'Quant Alert'
33 |
34 | modified_time = datetime.datetime.fromtimestamp(os.stat(file_name).st_mtime)
35 |
36 | if datetime.datetime.now() < modified_time + datetime.timedelta(minutes=10):
37 | print("Everything is okey")
38 | exit(0)
39 | # construct html
40 | # env = Environment(
41 | # loader=FileSystemLoader(template_dir),
42 | # )
43 | # template = env.get_template('alert.html')
44 | # html = template.render(orders=orders, process_info=process_info)
45 | html = "Quant is dead. Please check"
46 | print(html)
47 | email_obj = EmailObj(email_srv, email_user, email_pwd, email_port)
48 | email_obj.send_mail(subject, html, email_user, to_addr=email_addr)
49 |
50 |
--------------------------------------------------------------------------------
/monitor/monitor.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | source ~/.profile
5 | base_dir=$(cd `dirname $0`; pwd)
6 | echo $1
7 | python3.6 $base_dir/monitor.py -r pkguowu@yahoo.com -f ~/xq/gbtc.log
8 |
--------------------------------------------------------------------------------
/monitor/template/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
20 |
21 |
22 |
23 |
24 | Process Status
25 | {{ process_info }} |
26 |
27 |
28 |
29 |
30 | Order Status
31 |
32 | {% for item in orders[0] %}
33 | {{ item }} |
34 | {% endfor %}
35 |
36 |
37 | {% for order in orders %}
38 |
39 | {% for item in order %}
40 | {{ order[item] }} |
41 | {% endfor %}
42 |
43 | {% endfor %}
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/real.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | import os
6 | import utils.tools as ts
7 | import common.xquant as xq
8 | import common.log as log
9 | from engine.realengine import RealEngine
10 |
11 |
12 | def real_run(config, instance_id, exchange_name, value, args):
13 | info = 'instance_id: %s, exchange_name: %s, value: %s ' % (instance_id, exchange_name, value)
14 | print(info)
15 | module_name = config["module_name"].replace("/", ".")
16 | class_name = config["class_name"]
17 |
18 | if args.log:
19 | logfilename = instance_id + ".log"
20 | print(logfilename)
21 |
22 | server_ip = os.environ.get('LOG_SERVER_IP')
23 | server_port = os.environ.get('LOG_SERVER_PORT')
24 | print('Log server IP: %s, Log server port: %s' % (server_ip, server_port))
25 |
26 | log.init('real', logfilename, server_ip, server_port)
27 | log.info("%s" % (info))
28 | log.info("strategy name: %s; config: %s" % (class_name, config))
29 |
30 | engine = RealEngine(instance_id, exchange_name, config, value, args.log)
31 | strategy = ts.createInstance(module_name, class_name, config, engine)
32 |
33 | engine.run(strategy, args.debug)
34 |
35 |
36 | def real_view(config, instance_id, exchange_name, value):
37 | symbol = config['symbol']
38 |
39 | realEngine = RealEngine(instance_id, exchange_name, config, value)
40 | orders = realEngine.get_orders(symbol)
41 | realEngine.view(symbol, orders)
42 |
43 | def real_analyze(config, instance_id, exchange_name, value, print_switch_hl, display_rmk, print_switch_deal, print_switch_lock):
44 | symbol = config['symbol']
45 |
46 | realEngine = RealEngine(instance_id, exchange_name, config, value)
47 | orders = realEngine.get_orders(symbol)
48 | realEngine.analyze(symbol, orders, print_switch_hl, display_rmk, print_switch_deal, print_switch_lock)
49 |
50 |
51 | if __name__ == "__main__":
52 | parser = argparse.ArgumentParser(description='real')
53 | parser.add_argument('-e', help='exchange')
54 | parser.add_argument('-sc', help='strategy config')
55 | parser.add_argument('-sii', help='strategy instance id')
56 | parser.add_argument('-v', type=int, help='value')
57 | parser.add_argument('--debug', help='debug', action="store_true")
58 | parser.add_argument('--log', help='log', action="store_true")
59 | args = parser.parse_args()
60 | print(args)
61 | if not (args.e and args.sc and args.sii and args.v):
62 | parser.print_help()
63 | exit(1)
64 |
65 | config = xq.get_strategy_config(args.sc)
66 | real_run(config, args.sii, args.e, args.v, args)
67 |
--------------------------------------------------------------------------------
/real2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | import common.xquant as xq
6 | import common.instance as si
7 | from real import real_run
8 | from real import real_view
9 | from real import real_analyze
10 | from engine.realengine import RealEngine
11 | from db.mongodb import get_mongodb
12 | import setup
13 | from pprint import pprint
14 |
15 |
16 | def real2_run(args):
17 | instance = si.get_strategy_instance(args.sii)
18 | config = xq.get_strategy_config(instance['config_path'])
19 |
20 | real_run(config, args.sii, instance['exchange'], instance['value'], args)
21 |
22 | def real2_view(args):
23 | instance = si.get_strategy_instance(args.sii)
24 | config = xq.get_strategy_config(instance['config_path'])
25 |
26 | real_view(config, args.sii, instance['exchange'], instance['value'])
27 |
28 | def real2_analyze(args):
29 | instance = si.get_strategy_instance(args.sii)
30 | config = xq.get_strategy_config(instance['config_path'])
31 |
32 | real_analyze(config, args.sii, instance['exchange'], instance['value'], args.hl, args.rmk, args.deal, args.lock)
33 |
34 |
35 | def real2_list(args):
36 | td_db = get_mongodb(setup.trade_db_name)
37 | ss = td_db.find(si.STRATEGY_INSTANCE_COLLECTION_NAME, {"user": args.user})
38 | #pprint(ss)
39 | all_value = 0
40 | all_his_profit = 0
41 | all_flo_profit = 0
42 | all_commission = 0
43 |
44 | title_head_fmt = "%-30s %10s%12s"
45 | head_fmt = "%-30s %10s(%10.0f)"
46 |
47 | title_profit_fmt = "%21s %21s %12s"
48 | profit_fmt = "%12.2f(%6.2f%%) %12.2f(%6.2f%%) %12.2f"
49 |
50 | title_tail_fmt = " %-60s %-20s %10s"
51 |
52 |
53 | print(title_head_fmt % ("instance_id", "value", "") + title_profit_fmt % ("history_profit", "floating_profit", "commission") + title_tail_fmt % ("config_path", "exchange", "status"))
54 | for s in ss:
55 | instance_id = s["instance_id"]
56 | exchange_name = s["exchange"]
57 | value = s["value"]
58 | config_path = s["config_path"]
59 | if "status" in s:
60 | status = s["status"]
61 | else:
62 | status = ""
63 | if status != args.status and status != "":
64 | continue
65 |
66 | all_value += value
67 | profit_info = ""
68 | try:
69 | config = xq.get_strategy_config(config_path)
70 | symbol = config['symbol']
71 | realEngine = RealEngine(instance_id, exchange_name, config, value)
72 | orders = realEngine.get_orders(symbol)
73 | pst_info = realEngine.get_pst_by_orders(orders)
74 | history_profit, history_profit_rate, history_commission = realEngine.get_history(pst_info)
75 | all_his_profit += history_profit
76 | floating_profit, floating_profit_rate, floating_commission, cur_price = realEngine.get_floating(symbol, pst_info)
77 | all_flo_profit += floating_profit
78 | commission = history_commission + floating_commission
79 | all_commission += commission
80 | profit_info = profit_fmt % (history_profit, history_profit_rate*100, floating_profit, floating_profit_rate*100, commission)
81 |
82 | except Exception as ept:
83 | profit_info = "error: %s" % (ept)
84 |
85 | print(head_fmt % (instance_id, value, (value+history_profit+floating_profit)) + profit_info + title_tail_fmt % (config_path, exchange_name, status))
86 |
87 | if args.stat:
88 | print(head_fmt % ("all", all_value, all_value+all_his_profit+all_flo_profit) + profit_fmt % (all_his_profit, all_his_profit/all_value*100, all_flo_profit, all_flo_profit/all_value*100, all_commission))
89 |
90 |
91 | def real2_update(args):
92 | record = {}
93 | if args.user:
94 | record["user"] = args.user
95 | if args.instance_id:
96 | record["instance_id"] = args.instance_id
97 | if args.config_path:
98 | record["config_path"] = args.config_path
99 | if args.exchange:
100 | record["exchange"] = args.exchange
101 | if args.value:
102 | instance = si.get_strategy_instance(args.sii)
103 | instance_id = instance["instance_id"]
104 | exchange_name = instance["exchange"]
105 | if not exchange_name:
106 | exchange_name = record["exchange_name"]
107 | config_path = instance["config_path"]
108 | if not config_path:
109 | config_path = record["config_path"]
110 | value = instance["value"]
111 | config = xq.get_strategy_config(config_path)
112 | symbol = config['symbol']
113 | realEngine = RealEngine(instance_id, exchange_name, config, value)
114 | orders = realEngine.get_orders(symbol)
115 | if orders:
116 | return
117 |
118 | record["value"] = args.value
119 | if args.status:
120 | record["status"] = args.status
121 |
122 | if record:
123 | si.update_strategy_instance({"instance_id": args.sii}, record)
124 |
125 |
126 | def real2_add(args):
127 | record = {
128 | "user": args.user,
129 | "instance_id": args.sii,
130 | "config_path": args.config_path,
131 | "exchange": args.exchange,
132 | "value": args.value,
133 | "status": args.status,
134 | }
135 | si.add_strategy_instance(record)
136 |
137 |
138 | def real2_delete(args):
139 | si.delete_strategy_instance({"instance_id": args.sii})
140 |
141 |
142 | if __name__ == "__main__":
143 | parser = argparse.ArgumentParser(description='real')
144 | parser.add_argument('-sii', help='strategy instance id')
145 | parser.add_argument('--debug', help='debug', action="store_true")
146 | parser.add_argument('--log', help='log', action="store_true")
147 |
148 | subparsers = parser.add_subparsers(help='sub-command help')
149 |
150 | parser_view = subparsers.add_parser('view', help='view strategy instance')
151 | parser_view.add_argument('-sii', required=True, help='strategy instance id')
152 | parser_view.set_defaults(func=real2_view)
153 |
154 | parser_analyze = subparsers.add_parser('analyze', help='analyze strategy instance')
155 | parser_analyze.add_argument('-sii', required=True, help='strategy instance id')
156 | parser_analyze.add_argument('--hl', help='high low', action="store_true")
157 | parser_analyze.add_argument('--lock', help='lock info', action="store_true")
158 | parser_analyze.add_argument('--rmk', help='remark', action="store_true")
159 | parser_analyze.add_argument('--deal', help='deal amount', action="store_true")
160 | parser_analyze.set_defaults(func=real2_analyze)
161 |
162 | parser_list = subparsers.add_parser('list', help='list of strategy instance')
163 | parser_list.add_argument('-user', help='user name')
164 | parser_list.add_argument('--status', default=si.STRATEGY_INSTANCE_STATUS_START, choices=si.strategy_instance_statuses, help='strategy instance status')
165 | parser_list.add_argument('--stat', help='stat all', action="store_true")
166 | parser_list.set_defaults(func=real2_list)
167 |
168 | parser_update = subparsers.add_parser('update', help='update strategy instance')
169 | parser_update.add_argument('-sii', required=True, help='strategy instance id')
170 | parser_update.add_argument('--user', help='user name')
171 | parser_update.add_argument('--instance_id', help='new strategy instance id')
172 | parser_update.add_argument('--config_path', help='strategy config path')
173 | parser_update.add_argument('--exchange', help='strategy instance exchange')
174 | parser_update.add_argument('--value', type=int, help='strategy instance value')
175 | parser_update.add_argument('--status', choices=si.strategy_instance_statuses, help='strategy instance status')
176 | parser_update.set_defaults(func=real2_update)
177 |
178 | parser_add = subparsers.add_parser('add', help='add new strategy instance')
179 | parser_add.add_argument('-user', required=True, help='user name')
180 | parser_add.add_argument('-sii', required=True, help='strategy instance id')
181 | parser_add.add_argument('-config_path', required=True, help='strategy config path')
182 | parser_add.add_argument('-exchange', required=True, help='strategy instance exchange')
183 | parser_add.add_argument('-value', required=True, type=int, help='strategy instance value')
184 | parser_add.add_argument('-status', choices=si.strategy_instance_statuses, default=si.STRATEGY_INSTANCE_STATUS_START, help='strategy instance status')
185 | parser_add.set_defaults(func=real2_add)
186 |
187 | parser_delete = subparsers.add_parser('delete', help='delete strategy instance')
188 | parser_delete.add_argument('-sii', required=True, help='strategy instance id')
189 | parser_delete.set_defaults(func=real2_delete)
190 |
191 | args = parser.parse_args()
192 | #print(args)
193 |
194 | if hasattr(args, 'func'):
195 | args.func(args)
196 | else:
197 | real2_run(args)
198 |
--------------------------------------------------------------------------------
/scripts/auto_repay.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | OS=`uname -s`
4 | if [ ${OS} == "Darwin" ];then
5 | source ~/.bash_profile
6 | elif [ ${OS} == "Linux" ]; then
7 | source ~/.profile
8 | fi
9 |
10 | base_dir=$(cd `dirname $0`; pwd)
11 | echo $1
12 | PYTHONPATH=$base_dir/.. python3 $base_dir/../tools/auto_repay.py -e binance >> $base_dir/auto_repay.log 2>&1
13 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import os
3 |
4 | location = lambda x: os.path.join(
5 | os.path.dirname(os.path.realpath(__file__)), x)
6 |
7 | #db_name = location('database/main.db')
8 | trade_db_name = "xquant"
9 | mongo_user = os.environ.get('MONGO_USER')
10 | mongo_pwd = os.environ.get('MONGO_PWD')
11 | mongo_port = os.environ.get('MONGO_PORT')
12 | mongo_port = '%s' % (mongo_port) if mongo_port else '27017'
13 | db_url = "mongodb://localhost:%s/" % (mongo_port)
14 |
15 | email_srv = os.environ.get('EMAIL_SMTP')
16 | email_user = os.environ.get('EMAIL_FROM')
17 | email_pwd = os.environ.get('EMAIL_PWD')
18 | email_port = os.environ.get('EMAIL_PORT')
19 |
--------------------------------------------------------------------------------
/strategy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/strategy/__init__.py
--------------------------------------------------------------------------------
/strategy/kd/kd.py:
--------------------------------------------------------------------------------
1 | import random
2 | import common.bill as bl
3 | import utils.indicator as ic
4 | import utils.ti as ti
5 | from strategy.strategy import Strategy
6 |
7 |
8 | class KDStrategy(Strategy):
9 | """ simple KD stragegy"""
10 |
11 | def __init__(self, strategy_config, engine):
12 | super().__init__(strategy_config, engine)
13 | self.kline = strategy_config["kline"]
14 | self.kd_offset = strategy_config["kd_offset"]
15 |
16 | def search_init(self):
17 | kd_offset_cfg = self.config["search"]["kd_offset"]
18 |
19 | kd_offset = random.uniform(kd_offset_cfg["min"], kd_offset_cfg["max"])
20 | self.kd_offset = round(kd_offset, kd_offset_cfg["digits"])
21 |
22 | return self.kd_offset
23 |
24 | def check(self, symbol, klines):
25 | """ kdj指标,金叉全买入,下降趋势部分卖出,死叉全卖出 """
26 | '''
27 | kdj_arr = ic.py_kdj(klines, self.highseat, self.lowseat, self.closeseat)
28 | cur_k = kdj_arr[-1][1]
29 | cur_d = kdj_arr[-1][2]
30 | '''
31 | kkey, dkey = ti.KD(klines, self.closekey, self.highkey, self.lowkey)
32 | cur_k = klines[-1][kkey]
33 | cur_d = klines[-1][dkey]
34 |
35 | signal_info = (
36 | "(%6.2f) k(%6.2f) d(%6.2f)"
37 | % (
38 | cur_k - cur_d,
39 | cur_k,
40 | cur_d,
41 | )
42 | )
43 |
44 | if cur_k > cur_d + self.kd_offset:
45 | # 金叉
46 | return bl.open_long_bill(1, "买:", signal_info)
47 |
48 | elif cur_k < cur_d - self.kd_offset:
49 | # 死叉
50 | return bl.close_long_bill(0, "卖:", signal_info)
51 |
52 |
53 | return None
54 |
55 |
56 | def on_tick(self, klines=None):
57 | """ tick处理接口 """
58 | symbol = self.config["symbol"]
59 | # 之前的挂单全撤掉
60 | self.engine.cancle_orders(symbol)
61 |
62 | if not klines:
63 | klines = self.engine.md.get_klines(symbol, self.kline["interval"], self.kline["size"])
64 | self.cur_price = float(klines[-1][self.closeseat])
65 |
66 | check_signals = []
67 | signal = self.check(symbol, klines)
68 | if signal:
69 | check_signals.append(signal)
70 | position_info = self.engine.get_position(symbol, self.cur_price)
71 | self.engine.handle_order(symbol, position_info, self.cur_price, check_signals)
72 |
--------------------------------------------------------------------------------
/strategy/kd/kd_btc_usdt.jsn:
--------------------------------------------------------------------------------
1 | {
2 | "module_name": "strategy/kd/kd",
3 | "class_name": "KDStrategy",
4 | "commission_rate": 0.001,
5 | "symbol": "btc_usdt",
6 | "digits": {"btc": 6, "usdt": 2},
7 | "risk_control": {
8 | "stop_loss": {
9 | "condition": [],
10 | "defalut": {"r": -0.08}
11 | },
12 | "take_profit": {
13 | "base_open": []
14 | }
15 | },
16 | "sec": 60,
17 | "kline": {"interval": "1d", "size": 180},
18 | "kd_offset": 1.5,
19 | "mode": 1,
20 | "limit_price_rate": {"open": 0.01, "close": 0.1},
21 | "search": {
22 | "kd_offset": {"max": 5, "min": 0, "digits": 2}
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/strategy/strategy.py:
--------------------------------------------------------------------------------
1 | """strategy"""
2 | from datetime import timedelta, datetime
3 | import common.bill as bl
4 | from engine.order import pst_is_lock
5 |
6 | class Strategy:
7 | """Strategy"""
8 |
9 | def __init__(self, strategy_config, engine):
10 | self.name = ""
11 | self.config = strategy_config
12 | self.engine = engine
13 |
14 | self.instance_id = engine.instance_id
15 |
16 | md = engine.md
17 | self.md = md
18 |
19 | self.highkey = md.kline_key_high
20 | self.lowkey = md.kline_key_low
21 | self.openkey = md.kline_key_open
22 | self.closekey = md.kline_key_close
23 | self.volumekey = md.kline_key_volume
24 | self.opentimekey = md.kline_key_open_time
25 | self.closetimekey = md.kline_key_close_time
26 |
27 | self.highseat = md.get_kline_seat_high()
28 | self.lowseat = md.get_kline_seat_low()
29 | self.openseat = md.get_kline_seat_open()
30 | self.closeseat = md.get_kline_seat_close()
31 | self.volumeseat = md.get_kline_seat_volume()
32 | self.opentimeseat = md.get_kline_seat_open_time()
33 | self.closetimeseat = md.get_kline_seat_close_time()
34 |
35 | self.aligning_log = "\n%4s" % " "
36 | self.aligning_info = "\n%68s" % " "
37 |
38 | def log_info(self, info):
39 | self.engine.log_info(info)
40 |
41 | def log_warning(self, info):
42 | self.engine.log_warning(info)
43 |
44 | def log_error(self, info):
45 | self.engine.log_error(info)
46 |
47 | def log_critical(self, info):
48 | self.engine.log_critical(info)
49 |
50 | def log_debug(self, info):
51 | self.engine.log_debug(info)
52 |
53 |
54 | class SignalStrategy(Strategy):
55 | def __init__(self, strategy_config, engine):
56 | super().__init__(strategy_config, engine)
57 | self.open_time = None
58 | self.micro_open_time = None
59 |
60 | def is_open(self):
61 | open_time = self.md.get_kline_open_time(self.kls[-1])
62 | if not self.open_time or self.open_time < open_time:
63 | self.open_time = open_time
64 | return True
65 | return False
66 |
67 | def is_micro_open(self):
68 | micro_open_time = self.md.get_kline_open_time(self.micro_kls[-1])
69 | if not self.micro_open_time or self.micro_open_time < micro_open_time:
70 | self.micro_open_time = micro_open_time
71 | return True
72 | return False
73 |
74 | def merge_infos(self, infos, aligning):
75 | info = ""
76 | for info_tmp in infos:
77 | info += aligning + info_tmp
78 | return info
79 |
80 | def on_tick(self, master_kls=None, micro_kls=None):
81 | """ tick处理接口 """
82 | symbol = self.config["symbol"]
83 | kl_cfg = self.config["kline"]
84 | if not master_kls:
85 | master_kls = self.md.get_klines(symbol, kl_cfg["interval"], kl_cfg["size"])
86 | if len(master_kls) <= 0:
87 | return
88 | self.kls = master_kls
89 |
90 | if "micro_interval" in kl_cfg:
91 | if not micro_kls:
92 | micro_kls = self.md.get_klines(symbol, kl_cfg["micro_interval"], kl_cfg["size"])
93 | if len(micro_kls) <= 0:
94 | return
95 | self.micro_kls = micro_kls
96 |
97 | if self.is_open():
98 | self.on_open()
99 |
100 | #if self.is_micro_open():
101 | # self.on_micro_open()
102 |
103 | self.cur_price = self.md.get_kline_close(master_kls[-1])
104 | cur_close_time = self.md.get_kline_close_time(master_kls[-1])
105 | self.engine.handle(symbol, self, self.cur_price, cur_close_time, "")
106 |
107 | def check_bill(self, symbol, position_info):
108 | bill, info = self.check(symbol, position_info)
109 | self.log_info(info)
110 | if not bill:
111 | return []
112 | return [bill]
113 |
114 | def calc_ti(self):
115 | pass
116 |
117 | def check_signal(self):
118 | symbol = self.config["symbol"]
119 | signals = []
120 | infos = []
121 |
122 | if not self.calc_ti():
123 | return signals, infos
124 |
125 | s, tmp_infos = self.signal_long_close()
126 | infos += tmp_infos
127 | if s:
128 | signals.append(s)
129 |
130 | if "long_lock" in self.config:
131 | s, tmp_infos = self.signal_long_lock(symbol)
132 | infos += tmp_infos
133 | if s:
134 | signals.append(s)
135 |
136 | s, tmp_infos = self.signal_long_open()
137 | infos += tmp_infos
138 | if s:
139 | signals.append(s)
140 |
141 | s, tmp_infos = self.signal_short_close()
142 | infos += tmp_infos
143 | if s:
144 | signals.append(s)
145 |
146 | if "short_lock" in self.config:
147 | s, tmp_infos = self.signal_short_lock(symbol)
148 | infos += tmp_infos
149 | if s:
150 | signals.append(s)
151 |
152 | s, tmp_infos = self.signal_short_open()
153 | infos += tmp_infos
154 | if s:
155 | signals.append(s)
156 |
157 | return signals, infos
158 |
159 |
160 | def check_signal_single(self):
161 | symbol = self.config["symbol"]
162 | if not self.calc_ti():
163 | return [], []
164 |
165 | infos = []
166 | s, tmp_infos = self.signal_long_close()
167 | infos += tmp_infos
168 | if s:
169 | return [s], infos
170 | if "long_lock" in self.config:
171 | s, tmp_infos = self.signal_long_lock(symbol)
172 | infos += tmp_infos
173 | if s:
174 | return [s], infos
175 | s, tmp_infos = self.signal_long_open()
176 | infos += tmp_infos
177 | if s:
178 | return [s], infos
179 |
180 | s, tmp_infos = self.signal_short_close()
181 | infos += tmp_infos
182 | if s:
183 | return [s], infos
184 | if "short_lock" in self.config:
185 | s, tmp_infos = self.signal_short_lock(symbol)
186 | infos += tmp_infos
187 | if s:
188 | return [s], infos
189 | s, tmp_infos = self.signal_short_open()
190 | infos += tmp_infos
191 | if s:
192 | return [s], infos
193 |
194 | return [], infos
195 |
196 |
197 | def check(self, symbol, position_info):
198 | info = ""
199 | if not self.calc_ti():
200 | return None, info
201 | bill, infos = self.create_bill(symbol, position_info)
202 | return bill, self.merge_infos(infos, self.aligning_log)
203 |
204 |
205 | def create_bill(self, symbol, position_info):
206 | log_infos = []
207 | pst_amount = position_info["amount"]
208 |
209 | if pst_amount == 0 or (pst_amount > 0 and position_info["direction"] == bl.DIRECTION_LONG):
210 | signal, tmp_infos = self.signal_long_close()
211 | log_infos += tmp_infos
212 | if signal:
213 | log_infos.append("long close signal: %s" % (signal["name"]))
214 | if pst_amount > 0:
215 | rmk = self.merge_infos(tmp_infos, self.aligning_info)
216 | return bl.close_long_bill(0, signal["name"], rmk), log_infos
217 | else:
218 | if "long_lock" in self.config:
219 | signal, tmp_infos = self.signal_long_lock(symbol)
220 | log_infos += tmp_infos
221 | rmk = self.merge_infos(tmp_infos, self.aligning_info)
222 |
223 | if signal:
224 | log_infos.append("long lock signal: %s" % (signal["name"]))
225 | if pst_amount > 0 and not pst_is_lock(position_info):
226 | return bl.lock_long_bill(position_info["pst_rate"], signal["name"], rmk), log_infos
227 | else:
228 | if pst_is_lock(position_info):
229 | return bl.unlock_long_bill(position_info["pst_rate"], "unlock", rmk), log_infos
230 |
231 | if not signal:
232 | signal, tmp_infos = self.signal_long_open()
233 | log_infos += tmp_infos
234 | if signal:
235 | log_infos.append("long open signal: %s" % (signal["name"]))
236 | rmk = self.merge_infos(tmp_infos, self.aligning_info)
237 | return bl.open_long_bill(1, signal["name"], rmk), log_infos
238 |
239 | if pst_amount == 0 or (pst_amount > 0 and position_info["direction"] == bl.DIRECTION_SHORT):
240 | signal, tmp_infos = self.signal_short_close()
241 | log_infos += tmp_infos
242 | if signal:
243 | log_infos.append("short close signal: %s" % (signal["name"]))
244 | if pst_amount > 0:
245 | rmk = self.merge_infos(tmp_infos, self.aligning_info)
246 | return bl.close_short_bill(0, signal["name"], rmk), log_infos
247 | else:
248 | if "short_lock" in self.config:
249 | signal, tmp_infos = self.signal_short_lock(symbol)
250 | log_infos += tmp_infos
251 | rmk = self.merge_infos(tmp_infos, self.aligning_info)
252 | if signal:
253 | log_infos.append("short lock signal: %s" % (signal["name"]))
254 | if pst_amount > 0 and not pst_is_lock(position_info):
255 | return bl.lock_short_bill(position_info["pst_rate"], signal["name"], rmk), log_infos
256 | else:
257 | if pst_is_lock(position_info):
258 | return bl.unlock_short_bill(position_info["pst_rate"], "unlock", rmk), log_infos
259 |
260 | if not signal:
261 | signal, tmp_infos = self.signal_short_open()
262 | log_infos += tmp_infos
263 | if signal:
264 | log_infos.append("short open signal: %s" % (signal["name"]))
265 | rmk = self.merge_infos(tmp_infos, self.aligning_info)
266 | return bl.open_short_bill(1, signal["name"], rmk), log_infos
267 |
268 | return None, log_infos
269 |
270 |
--------------------------------------------------------------------------------
/tests/binance/bnFuture_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | from exchange.binanceFuture import BinanceFuture
4 | # from exchange.binance.enums import *
5 |
6 | api_key = os.environ.get('BINANCE_API_KEY')
7 | secret_key = os.environ.get('BINANCE_SECRET_KEY')
8 |
9 | if __name__ == "__main__":
10 | client = BinanceFuture(debug=True)
11 | # ret = client.get_account()
12 | # ret = client.get_balances("USDT")
13 | # ret = client.get_all_balances()
14 | # ret = client.get_trades("BTC_USDT")
15 | # ret = client.get_deals("BTC_USDT")
16 | # ret = client.send_order(direction='SHORT', action='OPEN', type='LIMIT', symbol='ETH_USDT', amount=0.1, price=450)
17 | # ret = client.send_order(direction='LONG', action='OPEN', type='LIMIT', symbol='ETH_USDT', amount=0.1, price=350)
18 | # ret = client.get_open_orders("ETH_USDT")
19 | # ret = client.get_open_order_ids(symbol='ETH_USDT')
20 | # ret = client.cancel_order(symbol='ETH_USDT', order_id='2892556980')
21 | # ret = client.cancel_orders(symbol='ETH_USDT', order_ids=[2892581474, 2892581810])
22 | # ret = client.get_order_book(symbol='ETH_USDT', limit=20)
23 | # ret = client.order_status_is_close(symbol='ETH_USDT', order_id=2892581810)
24 | # ret = client.transfer_to_future(asset='USDT', amount=5)
25 | # ret = client.transfer_from_future(asset='USDT', amount=5)
26 | # ret = client.get_klines_1hour(symbol="BTC_USDT", size=10)
27 | ret = client.get_klines_1day(symbol="BTC_USDT", size=10)
28 |
29 | print(ret)
30 |
--------------------------------------------------------------------------------
/tests/binance/bnMargin_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | from exchange.binanceMargin import BinanceMargin
4 | # from exchange.binance.enums import *
5 |
6 | api_key = os.environ.get('BINANCE_API_KEY')
7 | secret_key = os.environ.get('BINANCE_SECRET_KEY')
8 |
9 | if __name__ == "__main__":
10 | client = BinanceMargin(debug=True)
11 | # ret = client.get_account()
12 | # ret = client.transfer_to_margin(asset='BTC', amount=0.01)
13 | # ret = client.transfer_from_margin(asset='BTC', amount=0.01)
14 | # ret = client.loan(asset='USDT', amount='0.01')
15 | # ret = client.repay(asset='USDT', amount='0.01')
16 | # ret = client.get_loan(asset='USDT', startTime=1570700000000)
17 | # ret = client.get_repay(asset='USDT', startTime=1570700000000)
18 | # ret = client.get_klines_1hour(symbol="BTC_USDT", size=10)
19 | # ret = client.get_klines_1day(symbol="BTC_USDT", size=10)
20 | # ret = client.get_balances("USDT")
21 | # ret = client.get_trades("BTC_USDT")
22 | # ret = client.get_deals("BTC_USDT")
23 | # ret = client.get_open_orders("BTC_USDT")
24 | # ret = client.get_open_order_ids(symbol='ETH_USDT')
25 | # ret = client.get_order_book(symbol='ETH_USDT', limit=20)
26 | # ret = client.order_status_is_close(symbol='BTC_USDT', order_id=726519783)
27 | # ret = client.send_order(direction='SHORT', action='OPEN', type='LIMIT', symbol='ETH_USDT', amount=0.234123, price=200)
28 | ret = client.send_order(direction='LONG', action='OPEN', type='LIMIT', symbol='ETH_USDT', amount=10000.12234, price=80.213)
29 | # ret = client.cancel_order(symbol='ETH_USDT', order_id='498421194')
30 | # ret = client.cancel_orders(symbol='ETH_USDT', order_ids=[498425779, 498425781, 498426026, 498426027, 498424836])
31 |
32 | print(ret)
33 |
--------------------------------------------------------------------------------
/tests/binance/future_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | from exchange.binance.future import Client
4 | from exchange.binance.enums import *
5 |
6 | api_key = os.environ.get('BINANCE_API_KEY')
7 | secret_key = os.environ.get('BINANCE_SECRET_KEY')
8 |
9 | if __name__ == "__main__":
10 | client = Client(api_key, secret_key)
11 | # ret = client.get_symbol_info(symbol='BTCUSDT')
12 | # ret = client.get_order_book(symbol='BTCUSDT', limit=10)
13 | # ret = client.get_recent_trades(symbol='BTCUSDT', limit=10)
14 | # ret = client.get_historical_trades(symbol='BTCUSDT', limit=10)
15 | # ret = client.get_klines(symbol='BTCUSDT', interval=KLINE_INTERVAL_1DAY ,limit=10)
16 | # ret = client.get_mark_price(symbol='BTCUSDT')
17 | # ret = client.get_ticker(symbol='BTCUSDT')
18 | # ret = client.get_symbol_ticker(symbol='BTCUSDT')
19 | # ret = client.get_orderbook_ticker(symbol='BTCUSDT')
20 | # ret = client.transfer(asset='USDT', amount='20', type=1)
21 | # ret = client.transfer(asset='USDT', amount='10', type=2)
22 | # ret = client.get_transfer_history(asset='USDT', startTime=1598696422)
23 | # ret = client.order_limit_buy(symbol='ETHUSDT', quantity=0.1, price=380)
24 | # ret = client.order_limit_sell(symbol='ETHUSDT', quantity=0.1, price=450)
25 | # ret = client.get_order(symbol='ETHUSDT', orderId='2891778466')
26 | ret = client.cancel_order(symbol='ETHUSDT', orderId='2891900482')
27 | # ret = client.get_open_orders()
28 | # ret = client.get_all_orders(symbol='ETHUSDT')
29 | # ret = client.get_balance()
30 | # ret = client.get_account()
31 | # ret = client.get_position_risk()
32 | # ret = client.get_my_trades(symbol='ETHUSDT')
33 | # ret = client.stream_get_listen_key()
34 | # ret = client.stream_keepalive(listenKey='xkSWWmFJRj0yl1uCdZhfdulBPlRqeCbc5KKmfUJix8aPhy8MCnVOjbwIhJoydxbb')
35 | # ret = client.stream_close(listenKey='xkSWWmFJRj0yl1uCdZhfdulBPlRqeCbc5KKmfUJix8aPhy8MCnVOjbwIhJoydxbb')
36 |
37 | print(ret)
38 |
--------------------------------------------------------------------------------
/tests/binance/margin_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | from exchange.binance.margin import Client
4 | from exchange.binance.enums import *
5 |
6 | api_key = os.environ.get('BINANCE_API_KEY')
7 | secret_key = os.environ.get('BINANCE_SECRET_KEY')
8 |
9 | if __name__ == "__main__":
10 | client = Client(api_key, secret_key)
11 | # ret = client.transfer(asset='USDT', amount='10', type=1)
12 | # ret = client.transfer(asset='USDT', amount='0.1', type=2)
13 | # ret = client.loan(asset='USDT', amount='0.01')
14 | # ret = client.repay(asset='USDT', amount='0.01')
15 | # ret = client.order_limit_buy(symbol='ETHUSDT', quantity=0.1, price=180)
16 | # ret = client.transfer(asset='ETH', amount='0.1', type=1)
17 | # ret = client.order_limit_sell(symbol='ETHUSDT', quantity=0.1, price=200)
18 | # print(ret)
19 | # ret = client.cancel_order(symbol='ETHUSDT', orderId='445856389')
20 | # ret = client.get_order(symbol='ETHUSDT', orderId='445862537')
21 | # ret = client.get_open_orders()
22 | # ret = client.get_all_orders(symbol='ETHUSDT')
23 | # ret = client.get_loan(asset='USDT', startTime=1570700000000)
24 | # ret = client.get_repay(asset='USDT', startTime=1570700000000)
25 | # ret = client.get_account()
26 | # ret = client.get_asset(asset='ETH')
27 | # ret = client.get_pair(symbol='ETHUSDT')
28 | # ret = client.get_all_assets()
29 | ret = client.get_all_pairs()
30 | # ret = client.get_price_index(symbol='ETHUSDT')
31 | # ret = client.get_transfer_history(asset='ETH', type='ROLL_IN')
32 | # ret = client.get_transfer_history(type='ROLL_OUT')
33 | # ret = client.get_interest_history()
34 | # ret = client.get_force_liquidation_record()
35 | # ret = client.get_my_trades(symbol='ETHUSDT')
36 | # ret = client.get_max_borrowable(asset='ETH')
37 | # ret = client.get_max_transferable(asset='USDT')
38 | # ret = client.stream_get_listen_key()
39 | # ret = client.stream_keepalive(listenKey='BpJlFdbgdqQm9n7WQHG2URKSeu8qF1NZxFY02YgcyWzXNSMvHS065qNdJZqq')
40 | # ret = client.stream_close(listenKey='BpJlFdbgdqQm9n7WQHG2URKSeu8qF1NZxFY02YgcyWzXNSMvHS065qNdJZqq')
41 |
42 | print(ret)
43 |
--------------------------------------------------------------------------------
/tests/binance/spot_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | from exchange.binance.client import Client
4 | from exchange.binance.enums import *
5 |
6 | api_key = os.environ.get('BINANCE_API_KEY')
7 | secret_key = os.environ.get('BINANCE_SECRET_KEY')
8 |
9 | if __name__ == "__main__":
10 | client = Client(api_key, secret_key)
11 |
12 | # ret = client.create_order(symbol='ETHUSDT', side=SIDE_BUY, type=ORDER_TYPE_LIMIT,
13 | # timeInForce=TIME_IN_FORCE_GTC, price=180, quantity=0.1)
14 | # ret = client.create_order(symbol='ETHUSDT', side=SIDE_SELL, type=ORDER_TYPE_LIMIT,
15 | # timeInForce=TIME_IN_FORCE_GTC, price=210, quantity=0.1)
16 | # ret = client.get_all_orders(symbol='ETHUSDT')
17 | # ret = client.get_order(symbol='ETHUSDT', orderId='391540562')
18 | # ret = client.get_open_orders(symbol='ETHUSDT')
19 | # ret = client.get_my_trades(symbol='ETHUSDT')
20 | # ret = client.get_account()
21 | ret = client.cancel_order(symbol='ETHUSDT', orderId='391571598')
22 | print(ret)
23 |
24 |
--------------------------------------------------------------------------------
/tests/md_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import sys
3 | sys.path.append('../')
4 |
5 | from datetime import datetime
6 | import unittest
7 | from pprint import pprint
8 |
9 | import numpy as np
10 | import pandas as pd
11 | import talib
12 | import utils.ti as ti
13 |
14 | import common.xquant as xq
15 | import common.kline as kl
16 | from md.dbmd import DBMD
17 |
18 | class TestDBMD(unittest.TestCase):
19 | symbol = "eth_usdt"
20 | digits = 4
21 |
22 | interval = kl.KLINE_INTERVAL_1DAY
23 | interval_td = kl.get_interval_timedelta(interval)
24 | pre_count = 150
25 | display_count = 10
26 |
27 | tick_interval = kl.KLINE_INTERVAL_1MINUTE
28 | tick_collection = kl.get_kline_collection(symbol, tick_interval)
29 | tick_td = kl.get_interval_timedelta(tick_interval)
30 |
31 | start_time = datetime(2017, 9, 1)
32 | end_time = datetime(2020, 8, 1)
33 | md = DBMD("binance")
34 |
35 | def setUp(self):
36 | self.md.tick_time = self.start_time
37 | tick = (self.end_time - self.start_time) / self.tick_td
38 | print("tick td=%s, tick=%d, time rang: %s ~ %s" % (self.tick_td, tick, self.start_time, self.end_time))
39 |
40 | def tearDown(self):
41 | pass
42 |
43 | def perf_get_json_klines(self):
44 | while self.md.tick_time < self.end_time:
45 | klines = self.md.get_json_klines(self.symbol, self.interval, self.pre_count + self.display_count)
46 | self.md.tick_time += self.tick_td
47 |
48 | def perf_get_klines(self):
49 | while self.md.tick_time < self.end_time:
50 | klines = self.md.get_klines(self.symbol, self.interval, self.pre_count + self.display_count)
51 | self.md.tick_time += self.tick_td
52 |
53 |
54 | def perf_get_klines_adv(self):
55 | total_interval_count = int((self.end_time - self.start_time) / self.interval_td) + self.pre_count
56 | print("total_interval_count: %s" % (total_interval_count))
57 | interval_collection = kl.get_kline_collection(self.symbol, self.interval)
58 | interval_klines = self.md.get_original_klines(interval_collection, self.start_time - self.interval_td * self.pre_count, self.end_time)
59 | kl = interval_klines[0]
60 | print("open_time: %s" % (self.md.get_kline_open_time(kl)))
61 | #print("json: %s" % (kl))
62 |
63 | ti.EMA(interval_klines, "close", 13)
64 | ti.EMA(interval_klines, "close", 21)
65 | ti.EMA(interval_klines, "close", 55)
66 | ti.BIAS_EMA(interval_klines, 13, 21)
67 | ti.BIAS_EMA(interval_klines, 21, 55)
68 | ti.RSI(interval_klines, "close")
69 | #print(interval_klines[self.pre_count:self.pre_count+2])
70 | #print(interval_klines[-2:])
71 | #pprint(interval_klines[self.pre_count])
72 | #pprint(interval_klines[-1])
73 |
74 | for i in range(self.pre_count+1):
75 | if self.md.get_kline_open_time(interval_klines[i]) >= self.start_time:
76 | break
77 | interval_idx = i
78 | kl = interval_klines[interval_idx]
79 | print("interval_idx: %s" % (interval_idx))
80 | print("open time: %s" % (self.md.get_kline_open_time(kl)))
81 | #print("json: %s" % (kl))
82 |
83 | for i in range(interval_idx, len(interval_klines)):
84 | start_i = i - self.pre_count
85 | if start_i < 0:
86 | start_i = 0
87 | history_kls = interval_klines[start_i:i]
88 | #print(len(history_kls))
89 |
90 | interval_open_time = self.md.get_kline_open_time(interval_klines[i])
91 | #print(interval_open_time)
92 |
93 | tick_klines = self.md.get_original_klines(self.tick_collection, interval_open_time, interval_open_time+self.interval_td)
94 | new_interval_kl = tick_klines[0]
95 | for tick_kl in tick_klines[1:]:
96 | tick_open_time = self.md.get_kline_open_time(tick_kl)
97 | #print(tick_open_time)
98 | new_interval_kl["close"] = tick_kl["close"]
99 | new_interval_kl["close_time"] = tick_kl["close_time"]
100 |
101 | if new_interval_kl["high"] < tick_kl["high"]:
102 | new_interval_kl["high"] = tick_kl["high"]
103 | if new_interval_kl["low"] > tick_kl["low"]:
104 | new_interval_kl["low"] = tick_kl["low"]
105 |
106 | cur_kls = history_kls + [new_interval_kl]
107 | ti.EMA(cur_kls, "close", 13)
108 | ti.EMA(cur_kls, "close", 21)
109 | ti.EMA(cur_kls, "close", 55)
110 | ti.BIAS_EMA(cur_kls, 13, 21)
111 | ti.BIAS_EMA(cur_kls, 21, 55)
112 | ti.RSI(cur_kls, "close")
113 | #pprint(cur_kls[-2])
114 | #pprint(cur_kls[-1])
115 | #return
116 |
117 |
118 |
119 | if __name__ == '__main__':
120 | unittest.main()
121 |
--------------------------------------------------------------------------------
/tools/account.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | from exchange.exchange import get_exchange_names, create_exchange
6 | from exchange.binanceExchange import BinanceExchange
7 | from tabulate import tabulate as tb
8 | import pprint
9 |
10 |
11 | if __name__ == "__main__":
12 | parser = argparse.ArgumentParser(description='account infomation')
13 | parser.add_argument('-exchange', default=BinanceExchange.name, choices=get_exchange_names(), help='exchange name')
14 | args = parser.parse_args()
15 | # print(args)
16 | if not (args.exchange):
17 | parser.print_help()
18 | exit(1)
19 |
20 | exchange = create_exchange(args.exchange)
21 | if not exchange:
22 | print("exchange name error!")
23 | exit(1)
24 | exchange.connect()
25 |
26 | account = exchange.get_account()
27 | print("account info:" )
28 | pprint.pprint(account)
29 |
--------------------------------------------------------------------------------
/tools/auto_repay.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | from exchange.binanceMargin import BinanceMargin
6 | from decimal import Decimal
7 | import time
8 |
9 | if __name__ == "__main__":
10 | parser = argparse.ArgumentParser(description='account')
11 | parser.add_argument('-e', help='exchange')
12 | args = parser.parse_args()
13 | # print(args)
14 | if not (args.e):
15 | parser.print_help()
16 | exit(1)
17 |
18 | if args.e == "binance":
19 | exchange = BinanceMargin(debug=True)
20 | else:
21 | print("exchange error!")
22 | exit(1)
23 |
24 | account = exchange.get_account()
25 | # print("account info: %s" % account)
26 |
27 | for i in account['balances']:
28 | debt = Decimal(i['interest']) + Decimal(i['borrowed'])
29 | asset = Decimal(i['free'])
30 | if debt > 0 and asset > 0:
31 | repay = min(debt, asset)
32 | now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
33 | print("%s Repay %s: %s" % (now, i['asset'], repay))
34 | exchange.repay(i['asset'], repay)
35 |
--------------------------------------------------------------------------------
/tools/balance.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | import common.kline as kl
6 | from exchange.exchange import get_exchange_names, create_exchange
7 | from exchange.binanceExchange import BinanceExchange
8 | from tabulate import tabulate as tb
9 | from datetime import datetime
10 | import pprint
11 |
12 | from common.xquant import creat_symbol, get_balance_coin, get_balance_free, get_balance_frozen
13 |
14 |
15 | if __name__ == "__main__":
16 | parser = argparse.ArgumentParser(description='account infomation')
17 | parser.add_argument('-exchange', default=BinanceExchange.name, choices=get_exchange_names(), help='exchange name')
18 | parser.add_argument('-basecoin', default='usdt', help='assert sum by base coin')
19 | args = parser.parse_args()
20 | # print(args)
21 | if not (args.exchange):
22 | parser.print_help()
23 | exit(1)
24 |
25 | exchange = create_exchange(args.exchange)
26 | if not exchange:
27 | print("exchange name error!")
28 | exit(1)
29 | exchange.connect()
30 |
31 | balances = exchange.get_all_balances()
32 | print(" %s %s balances info:" % (datetime.now(), args.exchange) )
33 | #print(tb(balances))
34 |
35 | if exchange.kline_data_type == kl.KLINE_DATA_TYPE_LIST:
36 | closeseat = exchange.kline_idx_close
37 | else:
38 | closeseat = exchange.kline_key_close
39 |
40 | total_value = 0
41 | for item in balances:
42 | amount = max(get_balance_free(item), get_balance_frozen(item))
43 | if amount < 0:
44 | continue
45 |
46 | coin = get_balance_coin(item)
47 | if coin.upper() == args.basecoin.upper():
48 | value = amount
49 | else:
50 | #print(coin)
51 | symbol = creat_symbol(coin, args.basecoin)
52 | klines = exchange.get_klines_1min(symbol, size=1)
53 | price = float(klines[-1][closeseat])
54 | value = price * amount
55 |
56 | total_value += value
57 | item['value'] = value
58 |
59 | print(tb(balances))
60 | print("total value: %s %s" % (total_value, args.basecoin))
61 |
--------------------------------------------------------------------------------
/tools/order.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | import common.bill as bl
6 | import common.xquant as xq
7 | from exchange.exchange import get_exchange_names, create_exchange
8 | from exchange.binanceExchange import BinanceExchange
9 |
10 |
11 | def send_order(args):
12 | exchange = create_exchange(args.exchange)
13 | if not exchange:
14 | print("exchange name error!")
15 | exit(1)
16 | exchange.connect()
17 | exchange.send_order(args.direction, args.action, args.type, args.symbol, args.price, args.amount)
18 |
19 |
20 | def cancel_order(args):
21 | exchange = create_exchange(args.exchange)
22 | if not exchange:
23 | print("exchange name error!")
24 | exit(1)
25 | exchange.connect()
26 | exchange.cancel_order(args.symbol, args.orderid)
27 |
28 |
29 | def query_orders(args):
30 | exchange = create_exchange(args.exchange)
31 | if not exchange:
32 | print("exchange name error!")
33 | exit(1)
34 | exchange.connect()
35 | orders = exchange.get_open_orders(args.symbol)
36 | print(orders)
37 |
38 |
39 | if __name__ == "__main__":
40 | parser = argparse.ArgumentParser(description='account')
41 |
42 | subparsers = parser.add_subparsers(help='sub-command help')
43 |
44 | parser_send = subparsers.add_parser('send', help='send order')
45 | parser_send.add_argument('-exchange', default=BinanceExchange.name, choices=get_exchange_names(), help='exchange')
46 | parser_send.add_argument('-symbol', required=True, help='symbol (btc_usdt)')
47 | parser_send.add_argument('-price', required=True, type=float, help='price')
48 | parser_send.add_argument('-amount', required=True, type=float, help='amount')
49 | parser_send.add_argument('-direction', required=True, choices=bl.directions, help='direction')
50 | parser_send.add_argument('-action', choices=bl.actions, help='action')
51 | parser_send.add_argument('-type', choices=xq.ordertypes, default=xq.ORDER_TYPE_LIMIT, help='order type')
52 | parser_send.set_defaults(func=send_order)
53 |
54 | parser_cancel = subparsers.add_parser('cancel', help='cancel order')
55 | parser_cancel.add_argument('-exchange', default=BinanceExchange.name, choices=get_exchange_names(), help='exchange')
56 | parser_cancel.add_argument('-symbol', required=True, help='symbol (btc_usdt)')
57 | parser_cancel.add_argument('-orderid', required=True, help='order id')
58 | parser_cancel.set_defaults(func=cancel_order)
59 |
60 | parser_query = subparsers.add_parser('query', help='query open orders')
61 | parser_query.add_argument('-exchange', default=BinanceExchange.name, choices=get_exchange_names(), help='exchange')
62 | parser_query.add_argument('-symbol', required=True, help='symbol (btc_usdt)')
63 | parser_query.set_defaults(func=query_orders)
64 |
65 | args = parser.parse_args()
66 | # print(args)
67 | if hasattr(args, 'func'):
68 | args.func(args)
69 |
--------------------------------------------------------------------------------
/tools/position.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | import common.xquant as xq
6 | import common.bill as bl
7 | from common.instance import get_strategy_instance
8 | from engine.realengine import RealEngine
9 |
10 | if __name__ == "__main__":
11 | parser = argparse.ArgumentParser(description='close postion')
12 | parser.add_argument('-sii', help='strategy instance id')
13 | parser.add_argument('-direction', choices=bl.directions, help='direction')
14 | parser.add_argument('-action', choices=bl.actions, help='action')
15 | #parser.add_argument('-pr', type=float, default=0, help='postion rate')
16 | parser.add_argument('-rmk', help='remark')
17 | args = parser.parse_args()
18 | print(args)
19 | if not (args.sii and args.action):
20 | parser.print_help()
21 | exit(1)
22 |
23 | instance = get_strategy_instance(args.sii)
24 | config = xq.get_strategy_config(instance['config_path'])
25 | re = RealEngine(args.sii, instance['exchange'], config, instance['value'])
26 |
27 | symbol = config['symbol']
28 | klines = re.md.get_klines(symbol, config["kline"]["interval"], 1)
29 | if len(klines) <= 0:
30 | exit(1)
31 |
32 | cur_price = float(klines[-1][re.md.get_kline_seat_close()])
33 | pst_info = re.get_position(symbol, cur_price)
34 | if args.action in [bl.OPEN_POSITION, bl.UNLOCK_POSITION]:
35 | pst_rate = 1
36 | else:
37 | pst_rate = 0
38 |
39 | print('before position info: %s' % (pst_info))
40 | print('args.action: %s, pst_rate: %g' % (args.action, pst_rate))
41 | if args.rmk:
42 | rmk = args.rmk
43 | else:
44 | rmk = args.action
45 | rmk += ": "
46 | re.send_order(symbol, pst_info, cur_price, args.direction, args.action, pst_rate, None, rmk)
47 |
48 | after_pst_info = re.get_position(symbol, cur_price)
49 | print('after position info: %s' % (after_pst_info))
50 |
--------------------------------------------------------------------------------
/tools/slippage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | from exchange.exchange import get_exchange_names, create_exchange
6 | from exchange.binanceExchange import BinanceExchange
7 |
8 | directions = ["buy", "sell"]
9 |
10 |
11 | if __name__ == "__main__":
12 | parser = argparse.ArgumentParser(description='Slippage Calculation')
13 | parser.add_argument('-exchange', default=BinanceExchange.name, choices=get_exchange_names(), help='exchange name')
14 | parser.add_argument('-symbol', required=True, help='symbol, eg: btc_usdt')
15 | parser.add_argument('-direction', required=True, choices=directions, help='direction')
16 | parser.add_argument('-amount', type=float, required=True, help='amount')
17 |
18 | args = parser.parse_args()
19 | # print(args)
20 |
21 | symbol = args.symbol
22 | direction = args.direction
23 | amount = args.amount
24 |
25 | exchange = create_exchange(args.exchange)
26 | if not exchange:
27 | print("exchange name error!")
28 | exit(1)
29 | print("%s" % (args.exchange) )
30 | exchange.connect()
31 |
32 | klines = exchange.get_klines_1day(symbol, 1)
33 | cur_price = float(klines[-1][4])
34 |
35 | book = exchange.get_order_book(symbol, 1000)
36 | # print(book)
37 |
38 | if direction == 'buy':
39 | orders = book['asks']
40 | else:
41 | orders = book['bids']
42 |
43 | deal = 0
44 | cost = 0
45 | for order in orders:
46 | order_amount = float(order[1])
47 | order_price = float(order[0])
48 | if amount - deal > order_amount:
49 | deal += order_amount
50 | cost += order_amount * order_price
51 | else:
52 | cost += (amount - deal) * order_price
53 | deal = amount
54 | break
55 |
56 | if deal < amount:
57 | print("The order book depth is not enough. Please get more orders")
58 | exit(1)
59 | average_price = cost / amount
60 | if direction == 'buy':
61 | slippage = (average_price - cur_price) * 100 / cur_price
62 | else:
63 | slippage = (cur_price - average_price) * 100 / cur_price
64 | print("Current price: ", cur_price)
65 | print("Average deal price:", average_price)
66 | print("Deal amount: ", amount)
67 | print("Total cost: ", cost)
68 | print("Slippage: ", slippage, "%")
69 |
--------------------------------------------------------------------------------
/tools/strategy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import json
3 | import argparse
4 | import sys
5 | sys.path.append('../')
6 | from common.instance import add_strategy_instance, update_strategy_instance, delete_strategy_instance, get_strategy_instance
7 |
8 | """
9 | python3 strategy.py -m add -sii gwEma2BtcUsdt -p '{"user":"gw", "exchange" : "binance_margin","config_path" : "/home/gw/margin/xq/config/ema/ema2_btc_usdt_1d.jsn","value" : 10000}'
10 | python3 strategy.py -m update -sii gwEma2BtcUsdt -p '{"value" : 18000}'
11 | python3 strategy.py -m delete -sii gwEma2BtcUsdt
12 | python3 strategy.py -m show -sii gwEma2BtcUsdt
13 | """
14 | if __name__ == "__main__":
15 | parser = argparse.ArgumentParser(description='Strategy Operation')
16 | parser.add_argument('-m', help='Mode: add, delete, update, show')
17 | parser.add_argument('-sii', help='Instance ID')
18 | parser.add_argument('-p', help='Parameters')
19 | args = parser.parse_args()
20 |
21 | print(args)
22 |
23 | if not args.m and not args.sii:
24 | parser.print_help()
25 | exit(1)
26 |
27 | if args.m == 'add' and not args.p:
28 | parser.print_help()
29 | exit(1)
30 |
31 | if args.m == 'udpate' and not args.p:
32 | parser.print_help()
33 | exit(1)
34 |
35 | if args.m == 'add':
36 | add_strategy_instance({**{"instance_id": args.sii}, **json.loads(args.p)})
37 | elif args.m == 'update':
38 | update_strategy_instance({"instance_id": args.sii}, json.loads(args.p))
39 | elif args.m == 'delete':
40 | delete_strategy_instance({"instance_id": args.sii})
41 | elif args.m == 'show':
42 | print(get_strategy_instance(args.sii))
43 | else:
44 | parser.print_help()
45 | exit(1)
46 |
47 | exit(1)
48 |
--------------------------------------------------------------------------------
/tools/trade.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append('../')
4 | import argparse
5 | from exchange.exchange import create_exchange
6 |
7 | if __name__ == "__main__":
8 | parser = argparse.ArgumentParser(description='trade')
9 | parser.add_argument('-e', help='exchange')
10 | parser.add_argument('-s', help='symbol (btc_usdt)')
11 | args = parser.parse_args()
12 | # print(args)
13 | if not (args.e and args.s):
14 | parser.print_help()
15 | exit(1)
16 |
17 | exchange = create_exchange(args.e)
18 | if not exchange:
19 | print("exchange error!")
20 | exit(1)
21 | exchange.connect()
22 |
23 | trades = exchange.get_trades(args.s)
24 | print("trades: %s" % trades)
25 |
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiebing77/xquant/fa0b7afeca292326259ee7e64693e4501de2a735/utils/__init__.py
--------------------------------------------------------------------------------
/utils/email_obj.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import mimetypes
3 | from email import encoders
4 |
5 | import smtplib
6 | from email.mime.multipart import MIMEMultipart
7 | from email.mime.text import MIMEText
8 | from email.mime.base import MIMEBase
9 |
10 |
11 | def attachment(filename):
12 | """
13 | build attachment object
14 | """
15 | fd = open(filename, 'rb')
16 | mime_type, mime_encoding = mimetypes.guess_type(filename)
17 | if mime_encoding or (mime_type is None):
18 | mime_type = 'application/octet-stream'
19 |
20 | maintype, subtype = mime_type.split('/')
21 |
22 | if maintype == 'text':
23 | ret_val = MIMEText(fd.read(), _subtype=subtype)
24 | else:
25 | ret_val = MIMEBase(maintype, subtype)
26 | ret_val.set_payload(fd.read())
27 | encoders.encode_base64(ret_val)
28 |
29 | ret_val.add_header('Content-Disposition', 'attachment',
30 | filename=filename.split('/')[-1])
31 | fd.close()
32 | return ret_val
33 |
34 |
35 | class EmailObj(object):
36 | def __init__(self, server, user, pwd, port):
37 | self.server = server
38 | self.user = user
39 | self.pwd = pwd
40 | self.port = port
41 |
42 | def send_mail(self, subject, msg_body, from_addr, to_addr, cc_addr='', attachments=None):
43 | msg = MIMEMultipart()
44 | msg['To'] = to_addr
45 | msg['From'] = from_addr
46 | msg['Cc'] = cc_addr
47 | msg['Subject'] = subject
48 |
49 | body = MIMEText(msg_body, _subtype='html', _charset='utf-8')
50 | msg.attach(body)
51 |
52 | if attachments is not None:
53 | for filename in attachments:
54 | msg.attach(attachment(filename))
55 |
56 | s = smtplib.SMTP(self.server + ":" + self.port)
57 | #s = smtplib.SMTP_SSL(self.server + ":587")
58 | # s.set_debuglevel(True)
59 | s.ehlo()
60 | s.starttls()
61 | s.login(self.user, self.pwd)
62 | return s.sendmail(from_addr, cc_addr.split(",") + [to_addr], msg.as_string())
63 |
--------------------------------------------------------------------------------
/utils/indicator_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import sys
3 | sys.path.append('../')
4 |
5 | from datetime import datetime
6 | import unittest
7 | import pandas as pd
8 | import talib
9 |
10 | from md.dbmd import DBMD
11 | import utils.indicator as ic
12 | import xlib.ti as xti
13 | import common.xquant as xq
14 | import common.kline as kl
15 | import exchange.exchange as ex
16 |
17 | class TestIndicator(unittest.TestCase):
18 |
19 | def setUp(self):
20 | self.symbol = "eth_usdt"
21 | self.digits = 4
22 | self.interval = kl.KLINE_INTERVAL_1DAY
23 | self.display_count = 10
24 |
25 | exchange_name = ex.BINANCE_SPOT_EXCHANGE_NAME
26 | self.md = DBMD(exchange_name)
27 | self.md.tick_time = datetime(2019, 1, 1, 8)
28 |
29 | self.klines = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
30 |
31 | self.klines_df = pd.DataFrame(self.klines, columns=self.md.get_kline_column_names())
32 |
33 | self.closeseat = self.md.get_kline_seat_close()
34 | self.closekey = ex.get_kline_key_close(exchange_name)
35 |
36 | self.count = 5000
37 | self.steps = 1
38 | self.td = kl.get_interval_timedelta(kl.KLINE_INTERVAL_1MINUTE) * self.steps
39 |
40 |
41 | def tearDown(self):
42 | pass
43 |
44 |
45 | def _test_rsi(self):
46 | print("")
47 | py_rsis = ic.py_rsis(self.klines, self.closeseat)
48 | ta_rsis = talib.RSI(self.klines_df[self.closekey])
49 | #print(" X-Lib rsis: ", [round(a, 6) for a in py_rsis][-self.display_count:])
50 | #print("TA-Lib rsis: ", [round(a, 6) for a in ta_rsis][-self.display_count:])
51 |
52 | for i in range(-self.display_count, 0):
53 | #self.assertEqual(py_rsis[i], ta_rsis[i])
54 | self.assertTrue(abs(py_rsis[i] - ta_rsis.values[i]) < 0.01)
55 |
56 |
57 | def test_ema(self):
58 | print("test ema")
59 | period = 55
60 |
61 | pys = ic.py_emas(self.klines, self.closeseat, period)
62 | tas = talib.EMA(self.klines_df[self.closekey], period)
63 |
64 | closes = [c for c in pd.to_numeric(self.klines_df[self.closekey])]
65 | xtis = xti.EMA(closes, period)
66 |
67 | #kl_xtis = xti.EMA_KL(self.klines, self.closeseat, period)
68 |
69 | print(" ic emas: ", [round(a, 6) for a in pys][-self.display_count:])
70 | print("TA-Lib emas: ", [round(a, 6) for a in tas][-self.display_count:])
71 | print(" X-Lib emas: ", [round(a, 6) for a in xtis][-self.display_count:])
72 | #print(" X-Lib kl emas: ", [round(a, 6) for a in kl_xtis][-self.display_count:])
73 |
74 | for i in range(-self.display_count, 0):
75 | #self.assertEqual(pys[i], tas[i])
76 | self.assertTrue(abs(pys[i] - tas.values[i]) < 0.01)
77 | self.assertTrue(abs(xtis[i] - tas.values[i]) < 0.01)
78 | #self.assertTrue(abs(kl_xtis[i] - tas.values[i]) < 0.01)
79 |
80 |
81 | def _test_bias(self):
82 | print("")
83 | period_s = 21
84 | period_l = 55
85 |
86 | semas = ic.py_emas(self.klines, self.closeseat, period_s)
87 | ta_semas = talib.EMA(self.klines_df[self.closekey], period_s)
88 | print(" X-Lib semas(%d): %s" % (period_s, [round(a, self.digits) for a in semas][-self.display_count:]))
89 | print("TA-Lib semas(%d): %s" % (period_s, [round(a, self.digits) for a in ta_semas][-self.display_count:]))
90 | for i in range(-self.display_count, 0):
91 | self.assertTrue(abs(semas[i] - ta_semas.values[i]) < 0.01)
92 |
93 | lemas = ic.py_emas(self.klines, self.closeseat, period_l)
94 | ta_lemas = talib.EMA(self.klines_df[self.closekey], period_l)
95 | print(" X-Lib lemas(%d): %s" % (period_l, [round(a, self.digits) for a in lemas][-self.display_count:]))
96 | print("TA-Lib lemas(%d): %s" % (period_l, [round(a, self.digits) for a in ta_lemas][-self.display_count:]))
97 | for i in range(-self.display_count, 0):
98 | self.assertTrue(abs(lemas[i] - ta_lemas.values[i]) < 0.01)
99 |
100 | biases = ic.py_biases(semas, lemas)
101 | ta_biases = ic.pd_biases(ta_semas, ta_lemas)
102 | print(" X-Lib biases(%d, %d): %s" % (period_s, period_l, [round(a, 4) for a in biases][-self.display_count:]))
103 | print("TA-Lib biases(%d, %d): %s" % (period_s, period_l, [round(a, 4) for a in ta_biases][-self.display_count:]))
104 | for i in range(-self.display_count, 0):
105 | self.assertTrue(abs(biases[i] - ta_biases.values[i]) < 0.01)
106 |
107 |
108 |
109 | def _test_nbias(self):
110 | print("")
111 | period = 55
112 |
113 | closes = [float(k[self.closeseat]) for k in self.klines]
114 | print(" closes: ", [round(a, self.digits) for a in closes][-self.display_count:])
115 |
116 | emas = ic.py_emas(self.klines, self.closeseat, period)
117 | ta_emas = talib.EMA(self.klines_df[self.closekey], period)
118 | print(" X-Lib emas(%d): %s" % (period, [round(a, self.digits) for a in emas][-self.display_count:]))
119 | print("TA-Lib emas(%d): %s" % (period, [round(a, self.digits) for a in ta_emas][-self.display_count:]))
120 | for i in range(-self.display_count, 0):
121 | self.assertTrue(abs(emas[i] - ta_emas.values[i]) < 0.01)
122 |
123 | nbiases = ic.py_biases(closes, emas)
124 | ta_nbiases = ic.pd_biases(pd.to_numeric(self.klines_df[self.closekey]), ta_emas)
125 | print(" X-Lib nbiases(%d): %s" % (period, [round(a, 4) for a in nbiases][-self.display_count:]))
126 | print("TA-Lib nbiases(%d): %s" % (period, [round(a, 4) for a in ta_nbiases][-self.display_count:]))
127 | for i in range(-self.display_count, 0):
128 | self.assertTrue(abs(nbiases[i] - ta_nbiases.values[i]) < 0.01)
129 |
130 |
131 | def _test_perf_xlib_rsi(self):
132 | for i in range(self.count):
133 | klines = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
134 |
135 | py_rsis = ic.py_rsis(klines, self.closeseat)
136 | py_rsis = [round(a, 3) for a in py_rsis][-self.display_count:]
137 |
138 | self.md.tick_time += self.td
139 |
140 | def _test_perf_talib_rsi(self):
141 | for i in range(self.count):
142 | klines = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
143 | klines_df = pd.DataFrame(klines, columns=self.md.get_kline_column_names())
144 |
145 | ta_rsis = talib.RSI(klines_df[self.closekey])
146 | ta_rsis = [round(a, 3) for a in ta_rsis][-self.display_count:]
147 |
148 | self.md.tick_time += self.td
149 |
150 | def perf_py_ema(self):
151 | period = 55
152 | for i in range(self.count):
153 | klines = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
154 |
155 | emas = ic.py_emas(self.klines, self.closeseat, period)
156 |
157 | self.md.tick_time += self.td
158 |
159 | def perf_xlib_ema(self):
160 | period = 55
161 | for i in range(self.count):
162 | klines = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
163 |
164 | closes = [float(k[self.closeseat]) for k in klines]
165 | emas = xti.EMA(closes, period)
166 |
167 | self.md.tick_time += self.td
168 |
169 | def perf_xlib_ema_kl(self):
170 | period = 55
171 | for i in range(self.count):
172 | klines = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
173 |
174 | #emas = xti.EMA_KL(self.klines, self.closeseat, period)
175 |
176 | self.md.tick_time += self.td
177 |
178 | if __name__ == '__main__':
179 | unittest.main()
180 |
--------------------------------------------------------------------------------
/utils/tal.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """各种指标"""
3 | import talib
4 | import utils.tools as ts
5 |
6 | def STOCHRSI(klines_df, timeperiod=14):
7 | fastk, fastd = talib.STOCHRSI(klines_df["close"], timeperiod=14, fastk_period=5, fastd_period=3, fastd_matype=0)
8 | return [ts.reserve_float(a, 6) for a in fastk], [ts.reserve_float(a, 6) for a in fastd]
9 |
10 | def APO(klines_df, timeperiod=14):
11 | real = talib.APO(klines_df["close"], fastperiod=12, slowperiod=26, matype=0)
12 | return [ts.reserve_float(a, 6) for a in real]
13 |
14 | def DX(klines_df, timeperiod=14):
15 | real = talib.DX(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
16 | return [ts.reserve_float(a, 6) for a in real]
17 |
18 | def ADX(klines_df, timeperiod=14):
19 | real = talib.ADX(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
20 | return [ts.reserve_float(a, 6) for a in real]
21 |
22 | def ADXR(klines_df, timeperiod=14):
23 | real = talib.ADXR(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
24 | return [ts.reserve_float(a, 6) for a in real]
25 |
26 | def CCI(klines_df, timeperiod=14):
27 | real = talib.CCI(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
28 | return [ts.reserve_float(a, 6) for a in real]
29 |
30 | def MFI(klines_df, timeperiod=14):
31 | real = talib.MFI(klines_df["high"], klines_df["low"], klines_df["close"], klines_df["volume"], timeperiod=14)
32 | return [ts.reserve_float(a, 6) for a in real]
33 |
34 | def PLUS_DM(klines_df, timeperiod=14):
35 | real = talib.PLUS_DM(klines_df["high"], klines_df["low"], timeperiod=14)
36 | return [ts.reserve_float(a, 6) for a in real]
37 |
38 | def MINUS_DM(klines_df, timeperiod=14):
39 | real = talib.MINUS_DM(klines_df["high"], klines_df["low"], timeperiod=14)
40 | return [ts.reserve_float(a, 6) for a in real]
41 |
42 | def TRIX(klines_df, timeperiod=30):
43 | real = talib.TRIX(klines_df["close"], timeperiod=timeperiod)
44 | return [ts.reserve_float(a, 6) for a in real]
45 |
46 | def WILLR(klines_df, timeperiod=14):
47 | real = talib.WILLR(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14)
48 | return [ts.reserve_float(a, 6) for a in real]
49 |
--------------------------------------------------------------------------------
/utils/ti_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import sys
3 | sys.path.append('../')
4 |
5 | from datetime import datetime
6 | import unittest
7 | import pandas as pd
8 | import talib
9 |
10 | from md.dbmd import DBMD
11 | import utils.indicator as ic
12 | import utils.ti as ti
13 | import common.xquant as xq
14 | import common.kline as kl
15 | import exchange.exchange as ex
16 |
17 | def print_test_title(name):
18 | print("\n", "-"*8, "test", name, "-"*100)
19 |
20 |
21 | class TestIndicator(unittest.TestCase):
22 |
23 | def setUp(self):
24 | self.symbol = "eth_usdt"
25 | self.digits = 6
26 | self.interval = kl.KLINE_INTERVAL_1DAY
27 | self.display_count = 10
28 |
29 | exchange_name = ex.BINANCE_SPOT_EXCHANGE_NAME
30 | self.md = DBMD(exchange_name)
31 | self.md.tick_time = datetime(2019, 1, 1, 8)
32 |
33 | self.klines = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
34 |
35 | self.klines_df = pd.DataFrame(self.klines, columns=self.md.get_kline_column_names())
36 |
37 | self.closeseat = self.md.get_kline_seat_close()
38 | self.highseat = self.md.get_kline_seat_high()
39 | self.lowseat = self.md.get_kline_seat_low()
40 |
41 | self.closekey = ex.get_kline_key_close(exchange_name)
42 | self.highkey = ex.get_kline_key_high(exchange_name)
43 | self.lowkey = ex.get_kline_key_low(exchange_name)
44 |
45 | self.count = 5000
46 | self.steps = 1
47 | self.td = kl.get_interval_timedelta(kl.KLINE_INTERVAL_1MINUTE) * self.steps
48 |
49 |
50 | def tearDown(self):
51 | pass
52 |
53 | def test_ma(self):
54 | print_test_title("ma")
55 | period = 55
56 |
57 | kls =self.klines
58 | ma_key = ti.MA(kls, self.closekey, period)
59 | print(" ti mas: ", [round(kl[ma_key], self.digits) for kl in kls[-self.display_count:]])
60 |
61 | tas = talib.MA(self.klines_df[self.closekey], period)
62 | print("TA-Lib mas: ", [round(a, self.digits) for a in tas][-self.display_count:])
63 |
64 | for i in range(-self.display_count, 0):
65 | self.assertTrue(abs(kls[i][ma_key] - tas.values[i]) < 0.01)
66 |
67 |
68 | def test_ema(self):
69 | print_test_title("ema")
70 | period = 55
71 |
72 | kls =self.klines
73 | ema_key = ti.EMA(kls, self.closekey, period)
74 | print(" ti emas: ", [round(kl[ema_key], self.digits) for kl in kls[-self.display_count:]])
75 |
76 | tas = talib.EMA(self.klines_df[self.closekey], period)
77 | print("TA-Lib emas: ", [round(a, self.digits) for a in tas][-self.display_count:])
78 |
79 | for i in range(-self.display_count, 0):
80 | self.assertTrue(abs(kls[i][ema_key] - tas.values[i]) < 0.01)
81 |
82 |
83 | def test_bias(self):
84 | print_test_title("bias")
85 | period_s = 21
86 | period_l = 55
87 |
88 | kls =self.klines
89 | ema_s_key = ti.EMA(kls, self.closekey, period_s)
90 | ta_semas = talib.EMA(self.klines_df[self.closekey], period_s)
91 | print(" ti semas(%d): %s" % (period_s, [round(kl[ema_s_key], self.digits) for kl in kls[-self.display_count:]]))
92 | print("TA-Lib semas(%d): %s" % (period_s, [round(a, self.digits) for a in ta_semas][-self.display_count:]))
93 | for i in range(-self.display_count, 0):
94 | self.assertTrue(abs(kls[i][ema_s_key] - ta_semas.values[i]) < 0.01)
95 |
96 | ema_l_key = ti.EMA(kls, self.closekey, period_l)
97 | ta_lemas = talib.EMA(self.klines_df[self.closekey], period_l)
98 | print(" ti lemas(%d): %s" % (period_l, [round(kl[ema_l_key], self.digits) for kl in kls[-self.display_count:]]))
99 | print("TA-Lib lemas(%d): %s" % (period_l, [round(a, self.digits) for a in ta_lemas][-self.display_count:]))
100 | for i in range(-self.display_count, 0):
101 | self.assertTrue(abs(kls[i][ema_l_key] - ta_lemas.values[i]) < 0.01)
102 |
103 | bias_sl_key = ti.BIAS_EMA(kls, self.closekey, period_s, period_l)
104 | ta_biases = ic.pd_biases(ta_semas, ta_lemas)
105 | print(" ti biases(%d, %d): %s" % (period_s, period_l, [round(kl[bias_sl_key], self.digits) for kl in kls[-self.display_count:]]))
106 | print("TA-Lib biases(%d, %d): %s" % (period_s, period_l, [round(a, self.digits) for a in ta_biases][-self.display_count:]))
107 | for i in range(-self.display_count, 0):
108 | self.assertTrue(abs(kls[i][bias_sl_key] - ta_biases.values[i]) < 0.01)
109 |
110 |
111 | def test_rsi(self):
112 | print_test_title("rsi")
113 |
114 | kls =self.klines
115 | rsi_key = ti.RSI(kls, self.closekey)
116 | ta_rsis = talib.RSI(self.klines_df[self.closekey])
117 | print(" ti rsis: ", [round(kl[rsi_key], self.digits) for kl in kls[-self.display_count:]])
118 | print("TA-Lib rsis: ", [round(a, self.digits) for a in ta_rsis][-self.display_count:])
119 |
120 | for i in range(-self.display_count, 0):
121 | self.assertTrue(abs(kls[i][rsi_key] - ta_rsis.values[i]) < 0.01)
122 |
123 | def test_macd(self):
124 | print_test_title("macd")
125 |
126 | kls =self.klines
127 | dif_key, signal_key, hist_key = ti.MACD(kls, self.closekey)
128 | difs, signals, hists = talib.MACD(self.klines_df[self.closekey])
129 | print(" ti macd difs: ", [round(kl[dif_key], self.digits) for kl in kls[-self.display_count:]])
130 | print("TA-Lib macd difs: ", [round(a, self.digits) for a in difs][-self.display_count:])
131 |
132 | print(" ti macd signals: ", [round(kl[signal_key], self.digits) for kl in kls[-self.display_count:]])
133 | print("TA-Lib macd signals: ", [round(a, self.digits) for a in signals][-self.display_count:])
134 |
135 | print(" ti macd hists: ", [round(kl[hist_key], self.digits) for kl in kls[-self.display_count:]])
136 | print("TA-Lib macd hists: ", [round(a, self.digits) for a in hists][-self.display_count:])
137 |
138 | for i in range(-self.display_count, 0):
139 | self.assertTrue(abs(kls[i][dif_key] - difs.values[i]) < 0.01)
140 | self.assertTrue(abs(kls[i][signal_key] - signals.values[i]) < 0.01)
141 | self.assertTrue(abs(kls[i][hist_key] - hists.values[i]) < 0.01)
142 |
143 | def perf_macd(self):
144 | for i in range(self.count):
145 | kls = self.md.get_klines(self.symbol, self.interval, 150 + self.display_count)
146 | dif_key, signal_key, hist_key = ti.MACD(kls, self.closekey)
147 |
148 | kls_df = pd.DataFrame(kls, columns=self.md.get_kline_column_names())
149 | difs, signals, hists = talib.MACD(kls_df[self.closekey])
150 |
151 | i = -1
152 | self.assertTrue(abs(kls[i][dif_key] - difs.values[i]) < 0.01)
153 | self.assertTrue(abs(kls[i][signal_key] - signals.values[i]) < 0.01)
154 | self.assertTrue(abs(kls[i][hist_key] - hists.values[i]) < 0.01)
155 |
156 | self.md.tick_time += self.td
157 |
158 | def test_kd(self):
159 | print_test_title("kd")
160 | period = 9
161 | kls =self.klines
162 | kkey, dkey = ti.KD(kls, self.closekey, self.highkey, self.lowkey, period)
163 | '''
164 | kls_df = self.klines_df
165 | slowk, slowd = talib.STOCH(high=kls_df[self.highkey], low=kls_df[self.lowkey], close=kls_df[self.closekey],
166 | fastk_period=period, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0)
167 | '''
168 | kds = ic.py_kdj(kls, self.highseat, self.lowseat, self.closeseat)
169 |
170 |
171 | print(" ti kd ks: ", [round(kl[kkey], self.digits) for kl in kls[-self.display_count:]])
172 | #print("TA-Lib kd ks: ", [round(k, self.digits) for k in slowk][-self.display_count:])
173 | print("indicator kd ks: ", [round(kd[1], self.digits) for kd in kds[-self.display_count:]])
174 |
175 | print(" ti kd ds: ", [round(kl[dkey], self.digits) for kl in kls[-self.display_count:]])
176 | #print("TA-Lib kd ds: ", [round(d, self.digits) for d in slowd][-self.display_count:])
177 | print("indicator kd ds: ", [round(kd[2], self.digits) for kd in kds[-self.display_count:]])
178 |
179 | for i in range(-self.display_count, 0):
180 | #self.assertTrue(abs(kls[i][kkey] - slowk.values[i]) < 0.01)
181 | #self.assertTrue(abs(kls[i][dkey] - slowd.values[i]) < 0.01)
182 | self.assertTrue(abs(kls[i][kkey] - kds[i][1]) < 0.01)
183 | self.assertTrue(abs(kls[i][dkey] - kds[i][2]) < 0.01)
184 |
185 | def _test_atr(self):
186 | name = "atr"
187 | period = 14
188 | print_test_title(name)
189 |
190 | kls =self.klines
191 | ti_key = ti._ATR(kls, self.highkey, self.lowkey, self.closekey, period)
192 | tas = talib.ATR(self.klines_df[self.highkey], self.klines_df[self.lowkey], self.klines_df[self.closekey], timeperiod=period)
193 | print(" ti %ss: %s" % (name, [round(kl[ti_key], self.digits) for kl in kls[-self.display_count:]]))
194 | print("TA-Lib %ss: %s" % (name, [round(a, self.digits) for a in tas][-self.display_count:]))
195 |
196 | for i in range(-self.display_count, 0):
197 | self.assertTrue(abs(kls[i][ti_key] - tas.values[i]) < 0.01)
198 |
199 | def test_trix(self):
200 | name = "trix"
201 | print_test_title(name)
202 | period = 30
203 |
204 | kls =self.klines
205 | ti_key = ti.TRIX(kls, self.closekey, period)
206 | tas = talib.TRIX(self.klines_df[self.closekey], timeperiod=period)
207 | print(" ti %ss: %s" % (name, [round(kl[ti_key], self.digits) for kl in kls[-self.display_count:]]))
208 | print("TA-Lib %ss: %s" % (name, [round(a, self.digits) for a in tas][-self.display_count:]))
209 |
210 | for i in range(-self.display_count, 0):
211 | self.assertTrue(abs(kls[i][ti_key] - tas.values[i]) < 0.01)
212 |
213 |
214 | if __name__ == '__main__':
215 | unittest.main()
216 |
--------------------------------------------------------------------------------
/utils/tools.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """小工具函数"""
3 | import math
4 | from datetime import datetime, timedelta, time
5 | import logging
6 | import pandas as pd
7 | from decimal import Decimal
8 | import numpy as np
9 |
10 | MATH_FLOOR = 0 # 向下,舍去多余
11 | MATH_CEIL = 1 # 向上,
12 | MATH_ROUND = 2 # 四舍五入
13 |
14 | def parse_date_range(date_range):
15 | #print("time range: [ %s )" % date_range)
16 | dates = date_range.split("~")
17 |
18 | start_time = datetime.strptime(dates[0], "%Y-%m-%d")
19 | end_time = datetime.strptime(dates[1], "%Y-%m-%d")
20 | return start_time, end_time
21 |
22 | def parse_ic_keys(ss):
23 | print("keys: [ %s )" % ss)
24 | keys = {}
25 |
26 | if not ss:
27 | return keys
28 | for key in ss.split(","):
29 | keys[key] = True
30 | return keys
31 |
32 | def ax(ax, ic_key, x, y, c, drawstyle="default"):
33 | ax.set_ylabel(ic_key)
34 | ax.grid(True)
35 | ax.plot(x, y, c, label=ic_key, drawstyle=drawstyle)
36 |
37 | def createInstance(module_name, class_name, *args, **kwargs):
38 | # print("args :", args)
39 | # print("kwargs:", kwargs)
40 | module_meta = __import__(module_name, globals(), locals(), [class_name])
41 | class_meta = getattr(module_meta, class_name)
42 | obj = class_meta(*args, **kwargs)
43 | return obj
44 |
45 | def get_decimal(number):
46 | return len(str(Decimal(str(number))-Decimal(number).to_integral()).split('.')[1])
47 |
48 | def reserve_float_ceil(flo, float_digits=0):
49 | return reserve_float(flo, float_digits, MATH_CEIL)
50 |
51 | def multiply_ceil(var, const):
52 | decimal = get_decimal(var)
53 | return reserve_float_ceil(var * const, decimal)
54 |
55 | def multiply_floor(var, const):
56 | decimal = get_decimal(var)
57 | return reserve_float(var * const, decimal)
58 |
59 | def reserve_float(flo, float_digits=0, flag=MATH_FLOOR):
60 | """调整精度"""
61 | value_str = "%.11f" % flo
62 | return str_to_float(value_str, float_digits, flag)
63 |
64 |
65 | def str_to_float(string, float_digits=0, flag=MATH_FLOOR):
66 | """字符转浮点,并调整精度"""
67 | value_list = string.split(".")
68 | if len(value_list) == 1:
69 | return float(value_list[0])
70 |
71 | elif len(value_list) == 2:
72 | new_value_str = ".".join([value_list[0], value_list[1][0:float_digits]])
73 | new_value = float(new_value_str)
74 | if flag == MATH_FLOOR:
75 | pass
76 | elif flag == MATH_CEIL:
77 | if float(value_list[1][float_digits:]) > 0:
78 | new_value += math.pow(10, -float_digits)
79 | else:
80 | return None
81 |
82 | return new_value
83 | else:
84 | return None
85 |
86 |
87 | def cacl_today_fall_rate(klines, cur_price):
88 | """ 计算当天最高价的回落比例 """
89 | today_high_price = pd.to_numeric(klines["high"].values[-1])
90 | today_fall_rate = 1 - cur_price / today_high_price
91 | logging.info(
92 | "today high price(%f); fall rate(%f)", today_high_price, today_fall_rate
93 | )
94 | return today_fall_rate
95 |
96 |
97 | def cacl_period_fall_rate(md, klines, start_time, cur_price):
98 | """ 计算开仓日期到现在最高价的回落比例 """
99 | if start_time is None:
100 | return
101 |
102 | period_df = klines[
103 | klines["open_time"].map(lambda x: int(x)) > md.get_data_ts_from_time(start_time)
104 | ]
105 | period_high_price = period_df["high"].apply(pd.to_numeric).max()
106 |
107 | period_fall_rate = 1 - cur_price / period_high_price
108 | logging.info(
109 | "period high price(%f), fall rate(%f), start time(%s)"
110 | % (period_high_price, period_fall_rate, start_time)
111 | )
112 | return period_fall_rate
113 |
114 |
115 | def get_min_seat(arr):
116 | i_min = -len(arr)
117 | v_min = arr[i_min]
118 | for i in range(i_min+1, -1):
119 | v = arr[i]
120 | if v_min > v:
121 | v_min = v
122 | i_min = i
123 | return i_min
124 |
125 | def get_tops(arr, c):
126 | tops = []
127 | #print(arr)
128 | for i in range(-len(arr), 0):
129 | bi = i-c
130 | if bi < -len(arr):
131 | bi = -len(arr)
132 |
133 | ei = i + 1 + c
134 | if ei >= 0:
135 | sub_arr = arr[bi:]
136 | else:
137 | sub_arr = arr[bi:ei]
138 | if arr[i] == max(sub_arr):
139 | tops.append(i)
140 | return tops
141 |
142 | def get_bottoms(arr, c):
143 | bottoms = []
144 | #print(arr)
145 | for i in range(-len(arr), 0):
146 | bi = i-c
147 | if bi < -len(arr):
148 | bi = -len(arr)
149 |
150 | ei = i + 1 + c
151 | if ei >= 0:
152 | sub_arr = arr[bi:]
153 | else:
154 | sub_arr = arr[bi:ei]
155 |
156 | if arr[i] == min(sub_arr):
157 | bottoms.append(i)
158 | return bottoms
159 |
160 | def get_macd_tops(arr):
161 | tops = []
162 | bi = -len(arr)
163 | for i in range(bi+1, 0):
164 | v = arr[i]
165 | if v > 0:
166 | if arr[bi] < v:
167 | bi = i
168 | else:
169 | if arr[bi] > 0:
170 | tops.append(bi)
171 | bi = i
172 | if arr[bi] > 0:
173 | tops.append(bi)
174 |
175 | return tops
176 |
177 | def get_macd_bottoms(arr):
178 | bottoms = []
179 | bi = -len(arr)
180 | for i in range(bi+1, 0):
181 | v = arr[i]
182 | if v < 0:
183 | if arr[bi] > v:
184 | bi = i
185 | else:
186 | if arr[bi] < 0:
187 | bottoms.append(bi)
188 | bi = i
189 | if arr[bi] < 0:
190 | bottoms.append(bi)
191 |
192 | return bottoms
193 |
194 | def get_trend(ema1, ema2, std_diff_threshold=0.01, start=-5, end=0):
195 | if end == 0:
196 | diff_data = ema1[start:] + ema2[start:]
197 | else:
198 | diff_data = ema1[start:end] + ema2[start:end]
199 |
200 | std_diff = np.std(diff_data)/sum(diff_data) * len(diff_data)
201 | if std_diff < std_diff_threshold:
202 | ret = 0
203 | elif ema1[-1] < ema2[-1]:
204 | ret = -1
205 | else:
206 | ret = 1
207 |
208 | return ret, std_diff
209 |
--------------------------------------------------------------------------------
/xlib/step.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """ """
3 |
4 |
5 | def is_increment(arr):
6 | for i in range(1, len(arr)):
7 | if arr[i-1] >= arr[i]:
8 | return False
9 | return True
10 |
11 | def is_decrement(arr):
12 | for i in range(1, len(arr)):
13 | if arr[i-1] <= arr[i]:
14 | return False
15 | return True
16 |
17 | def get_inc_step(arr):
18 | i = -1
19 | while i >= -len(arr)+1:
20 | if arr[i-1] > arr[i]:
21 | break
22 | i -= 1
23 | return -i-1
24 |
25 | def get_dec_step(arr):
26 | i = -1
27 | while i >= -len(arr)+1:
28 | if arr[i-1] < arr[i]:
29 | break
30 | i -= 1
31 | return -i-1
32 |
33 | def get_over_step(vs, v):
34 | i = -1
35 | while i >= -len(vs)+1:
36 | if vs[i] > v:
37 | break
38 | i -= 1
39 | return -i-1
40 |
41 | def get_below_step(vs, v):
42 | i = -1
43 | while i >= -len(vs)+1:
44 | if vs[i] < v:
45 | break
46 | i -= 1
47 | return -i-1
48 |
49 | def is_more(arr, c=2):
50 | for i in range(c, len(arr)):
51 | if min(arr[i-c:i]) > arr[i]:
52 | return False
53 | return True
54 |
55 | def is_less(arr, c=2):
56 | for i in range(c, len(arr)):
57 | if max(arr[i-c:i]) < arr[i]:
58 | return False
59 | return True
60 |
61 | def get_more_step(arr, c=2):
62 | i = -1
63 | while i >= -len(arr)+c:
64 | if min(arr[i-c:i]) > arr[i]:
65 | break
66 | i -= 1
67 |
68 | if i == -1:
69 | return 0
70 |
71 | return -1-i
72 |
73 | def get_less_step(arr, c=2):
74 | i = -1
75 | while i >= -len(arr)+c:
76 | if max(arr[i-c:i]) < arr[i]:
77 | break
78 | i -= 1
79 |
80 | if i == -1:
81 | return 0
82 |
83 | return -1-i
84 |
85 |
--------------------------------------------------------------------------------