siat 3.10.130__py3-none-any.whl → 3.10.132__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. build/lib/build/lib/siat/__init__.py +75 -0
  2. build/lib/build/lib/siat/allin.py +137 -0
  3. build/lib/build/lib/siat/assets_liquidity.py +915 -0
  4. build/lib/build/lib/siat/beta_adjustment.py +1058 -0
  5. build/lib/build/lib/siat/beta_adjustment_china.py +548 -0
  6. build/lib/build/lib/siat/blockchain.py +143 -0
  7. build/lib/build/lib/siat/bond.py +2900 -0
  8. build/lib/build/lib/siat/bond_base.py +992 -0
  9. build/lib/build/lib/siat/bond_china.py +100 -0
  10. build/lib/build/lib/siat/bond_zh_sina.py +143 -0
  11. build/lib/build/lib/siat/capm_beta.py +783 -0
  12. build/lib/build/lib/siat/capm_beta2.py +887 -0
  13. build/lib/build/lib/siat/common.py +5360 -0
  14. build/lib/build/lib/siat/compare_cross.py +642 -0
  15. build/lib/build/lib/siat/copyrights.py +18 -0
  16. build/lib/build/lib/siat/cryptocurrency.py +667 -0
  17. build/lib/build/lib/siat/economy.py +1471 -0
  18. build/lib/build/lib/siat/economy2.py +1853 -0
  19. build/lib/build/lib/siat/esg.py +536 -0
  20. build/lib/build/lib/siat/event_study.py +815 -0
  21. build/lib/build/lib/siat/fama_french.py +1521 -0
  22. build/lib/build/lib/siat/fin_stmt2_yahoo.py +982 -0
  23. build/lib/build/lib/siat/financial_base.py +1160 -0
  24. build/lib/build/lib/siat/financial_statements.py +598 -0
  25. build/lib/build/lib/siat/financials.py +2339 -0
  26. build/lib/build/lib/siat/financials2.py +1278 -0
  27. build/lib/build/lib/siat/financials_china.py +4433 -0
  28. build/lib/build/lib/siat/financials_china2.py +2212 -0
  29. build/lib/build/lib/siat/fund.py +629 -0
  30. build/lib/build/lib/siat/fund_china.py +3307 -0
  31. build/lib/build/lib/siat/future_china.py +551 -0
  32. build/lib/build/lib/siat/google_authenticator.py +47 -0
  33. build/lib/build/lib/siat/grafix.py +3636 -0
  34. build/lib/build/lib/siat/holding_risk.py +867 -0
  35. build/lib/build/lib/siat/luchy_draw.py +638 -0
  36. build/lib/build/lib/siat/market_china.py +1168 -0
  37. build/lib/build/lib/siat/markowitz.py +2363 -0
  38. build/lib/build/lib/siat/markowitz2.py +3150 -0
  39. build/lib/build/lib/siat/markowitz2_20250704.py +2969 -0
  40. build/lib/build/lib/siat/markowitz2_20250705.py +3158 -0
  41. build/lib/build/lib/siat/markowitz_simple.py +373 -0
  42. build/lib/build/lib/siat/ml_cases.py +2291 -0
  43. build/lib/build/lib/siat/ml_cases_example.py +60 -0
  44. build/lib/build/lib/siat/option_china.py +3069 -0
  45. build/lib/build/lib/siat/option_pricing.py +1925 -0
  46. build/lib/build/lib/siat/other_indexes.py +409 -0
  47. build/lib/build/lib/siat/risk_adjusted_return.py +1576 -0
  48. build/lib/build/lib/siat/risk_adjusted_return2.py +1900 -0
  49. build/lib/build/lib/siat/risk_evaluation.py +2218 -0
  50. build/lib/build/lib/siat/risk_free_rate.py +351 -0
  51. build/lib/build/lib/siat/sector_china.py +4140 -0
  52. build/lib/build/lib/siat/security_price2.py +727 -0
  53. build/lib/build/lib/siat/security_prices.py +3408 -0
  54. build/lib/build/lib/siat/security_trend.py +402 -0
  55. build/lib/build/lib/siat/security_trend2.py +646 -0
  56. build/lib/build/lib/siat/stock.py +4284 -0
  57. build/lib/build/lib/siat/stock_advice_linear.py +934 -0
  58. build/lib/build/lib/siat/stock_base.py +26 -0
  59. build/lib/build/lib/siat/stock_china.py +2095 -0
  60. build/lib/build/lib/siat/stock_prices_kneighbors.py +910 -0
  61. build/lib/build/lib/siat/stock_prices_linear.py +386 -0
  62. build/lib/build/lib/siat/stock_profile.py +707 -0
  63. build/lib/build/lib/siat/stock_technical.py +3305 -0
  64. build/lib/build/lib/siat/stooq.py +74 -0
  65. build/lib/build/lib/siat/transaction.py +347 -0
  66. build/lib/build/lib/siat/translate.py +5183 -0
  67. build/lib/build/lib/siat/valuation.py +1378 -0
  68. build/lib/build/lib/siat/valuation_china.py +2076 -0
  69. build/lib/build/lib/siat/var_model_validation.py +444 -0
  70. build/lib/build/lib/siat/yf_name.py +811 -0
  71. build/lib/siat/__init__.py +75 -0
  72. build/lib/siat/allin.py +137 -0
  73. build/lib/siat/assets_liquidity.py +915 -0
  74. build/lib/siat/beta_adjustment.py +1058 -0
  75. build/lib/siat/beta_adjustment_china.py +548 -0
  76. build/lib/siat/blockchain.py +143 -0
  77. build/lib/siat/bond.py +2900 -0
  78. build/lib/siat/bond_base.py +992 -0
  79. build/lib/siat/bond_china.py +100 -0
  80. build/lib/siat/bond_zh_sina.py +143 -0
  81. build/lib/siat/capm_beta.py +783 -0
  82. build/lib/siat/capm_beta2.py +887 -0
  83. build/lib/siat/common.py +5360 -0
  84. build/lib/siat/compare_cross.py +642 -0
  85. build/lib/siat/copyrights.py +18 -0
  86. build/lib/siat/cryptocurrency.py +667 -0
  87. build/lib/siat/economy.py +1471 -0
  88. build/lib/siat/economy2.py +1853 -0
  89. build/lib/siat/esg.py +536 -0
  90. build/lib/siat/event_study.py +815 -0
  91. build/lib/siat/fama_french.py +1521 -0
  92. build/lib/siat/fin_stmt2_yahoo.py +982 -0
  93. build/lib/siat/financial_base.py +1160 -0
  94. build/lib/siat/financial_statements.py +598 -0
  95. build/lib/siat/financials.py +2339 -0
  96. build/lib/siat/financials2.py +1278 -0
  97. build/lib/siat/financials_china.py +4433 -0
  98. build/lib/siat/financials_china2.py +2212 -0
  99. build/lib/siat/fund.py +629 -0
  100. build/lib/siat/fund_china.py +3307 -0
  101. build/lib/siat/future_china.py +551 -0
  102. build/lib/siat/google_authenticator.py +47 -0
  103. build/lib/siat/grafix.py +3636 -0
  104. build/lib/siat/holding_risk.py +867 -0
  105. build/lib/siat/luchy_draw.py +638 -0
  106. build/lib/siat/market_china.py +1168 -0
  107. build/lib/siat/markowitz.py +2363 -0
  108. build/lib/siat/markowitz2.py +3150 -0
  109. build/lib/siat/markowitz2_20250704.py +2969 -0
  110. build/lib/siat/markowitz2_20250705.py +3158 -0
  111. build/lib/siat/markowitz_simple.py +373 -0
  112. build/lib/siat/ml_cases.py +2291 -0
  113. build/lib/siat/ml_cases_example.py +60 -0
  114. build/lib/siat/option_china.py +3069 -0
  115. build/lib/siat/option_pricing.py +1925 -0
  116. build/lib/siat/other_indexes.py +409 -0
  117. build/lib/siat/risk_adjusted_return.py +1576 -0
  118. build/lib/siat/risk_adjusted_return2.py +1900 -0
  119. build/lib/siat/risk_evaluation.py +2218 -0
  120. build/lib/siat/risk_free_rate.py +351 -0
  121. build/lib/siat/sector_china.py +4140 -0
  122. build/lib/siat/security_price2.py +727 -0
  123. build/lib/siat/security_prices.py +3408 -0
  124. build/lib/siat/security_trend.py +402 -0
  125. build/lib/siat/security_trend2.py +646 -0
  126. build/lib/siat/stock.py +4284 -0
  127. build/lib/siat/stock_advice_linear.py +934 -0
  128. build/lib/siat/stock_base.py +26 -0
  129. build/lib/siat/stock_china.py +2095 -0
  130. build/lib/siat/stock_prices_kneighbors.py +910 -0
  131. build/lib/siat/stock_prices_linear.py +386 -0
  132. build/lib/siat/stock_profile.py +707 -0
  133. build/lib/siat/stock_technical.py +3305 -0
  134. build/lib/siat/stooq.py +74 -0
  135. build/lib/siat/transaction.py +347 -0
  136. build/lib/siat/translate.py +5183 -0
  137. build/lib/siat/valuation.py +1378 -0
  138. build/lib/siat/valuation_china.py +2076 -0
  139. build/lib/siat/var_model_validation.py +444 -0
  140. build/lib/siat/yf_name.py +811 -0
  141. siat/__init__.py +0 -0
  142. siat/allin.py +0 -0
  143. siat/assets_liquidity.py +0 -0
  144. siat/beta_adjustment.py +0 -0
  145. siat/beta_adjustment_china.py +0 -0
  146. siat/blockchain.py +0 -0
  147. siat/bond.py +0 -0
  148. siat/bond_base.py +0 -0
  149. siat/bond_china.py +0 -0
  150. siat/bond_zh_sina.py +0 -0
  151. siat/capm_beta.py +0 -0
  152. siat/capm_beta2.py +0 -0
  153. siat/common.py +94 -30
  154. siat/compare_cross.py +0 -0
  155. siat/copyrights.py +0 -0
  156. siat/cryptocurrency.py +0 -0
  157. siat/economy.py +0 -0
  158. siat/economy2.py +0 -0
  159. siat/esg.py +0 -0
  160. siat/event_study.py +0 -0
  161. siat/fama_french.py +0 -0
  162. siat/fin_stmt2_yahoo.py +0 -0
  163. siat/financial_base.py +0 -0
  164. siat/financial_statements.py +0 -0
  165. siat/financials.py +0 -0
  166. siat/financials2.py +0 -0
  167. siat/financials_china.py +0 -0
  168. siat/financials_china2.py +0 -0
  169. siat/fund.py +0 -0
  170. siat/fund_china.py +0 -0
  171. siat/future_china.py +0 -0
  172. siat/google_authenticator.py +0 -0
  173. siat/grafix.py +1 -1
  174. siat/holding_risk.py +0 -0
  175. siat/luchy_draw.py +0 -0
  176. siat/market_china.py +7 -1
  177. siat/markowitz.py +0 -0
  178. siat/markowitz2.py +240 -39
  179. siat/markowitz2_20250704.py +2969 -0
  180. siat/markowitz2_20250705.py +3158 -0
  181. siat/markowitz_simple.py +0 -0
  182. siat/ml_cases.py +0 -0
  183. siat/ml_cases_example.py +0 -0
  184. siat/option_china.py +0 -0
  185. siat/option_pricing.py +0 -0
  186. siat/other_indexes.py +0 -0
  187. siat/risk_adjusted_return.py +0 -0
  188. siat/risk_adjusted_return2.py +0 -0
  189. siat/risk_evaluation.py +0 -0
  190. siat/risk_free_rate.py +0 -0
  191. siat/sector_china.py +0 -0
  192. siat/security_price2.py +0 -0
  193. siat/security_prices.py +3 -1
  194. siat/security_trend.py +0 -0
  195. siat/security_trend2.py +1 -1
  196. siat/stock.py +4 -2
  197. siat/stock_advice_linear.py +0 -0
  198. siat/stock_base.py +0 -0
  199. siat/stock_china.py +0 -0
  200. siat/stock_prices_kneighbors.py +0 -0
  201. siat/stock_prices_linear.py +0 -0
  202. siat/stock_profile.py +0 -0
  203. siat/stock_technical.py +0 -0
  204. siat/stooq.py +0 -0
  205. siat/transaction.py +0 -0
  206. siat/translate.py +11 -11
  207. siat/valuation.py +0 -0
  208. siat/valuation_china.py +0 -0
  209. siat/var_model_validation.py +0 -0
  210. siat/yf_name.py +0 -0
  211. {siat-3.10.130.dist-info → siat-3.10.132.dist-info}/METADATA +11 -11
  212. siat-3.10.132.dist-info/RECORD +218 -0
  213. siat-3.10.132.dist-info/top_level.txt +4 -0
  214. siat-3.10.130.dist-info/RECORD +0 -76
  215. siat-3.10.130.dist-info/top_level.txt +0 -1
  216. {siat-3.10.130.dist-info → siat-3.10.132.dist-info}/WHEEL +0 -0
  217. {siat-3.10.130.dist-info → siat-3.10.132.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,667 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 本模块功能:数字货币及其MSA交易策略
4
+ 所属工具包:证券投资分析工具SIAT
5
+ SIAT:Security Investment Analysis Tool
6
+ 创建日期:2018年9月19日
7
+ 最新修订日期:2020年2月3日
8
+ 作者:王德宏 (WANG Dehong, Peter)
9
+ 作者单位:北京外国语大学国际商学院
10
+ 版权所有:王德宏
11
+ 用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
12
+ 特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
13
+ """
14
+
15
+ #关闭所有警告
16
+ import warnings; warnings.filterwarnings('ignore')
17
+ #引用Python插件
18
+ from datetime import datetime
19
+ import numpy as np; import pandas as pd
20
+ #from scipy import stats
21
+ import json
22
+ from bs4 import BeautifulSoup; import requests
23
+ #==============================================================================
24
+ import matplotlib.pyplot as plt
25
+ #处理绘图汉字乱码问题
26
+ import sys; czxt=sys.platform
27
+ if czxt in ['win32','win64']:
28
+ #设置绘图时的汉字显示
29
+ #plt.rcParams['font.sans-serif'] = ['FangSong'] # 设置默认字体
30
+ plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
31
+ if czxt in ['darwin']:
32
+ plt.rcParams['font.sans-serif']=['Arial Unicode MS']
33
+ #plt.rcParams['font.sans-serif']= ['Heiti TC']
34
+ # 解决保存图像时'-'显示为方块的问题
35
+ plt.rcParams['axes.unicode_minus'] = False
36
+ #==============================================================================
37
+
38
+ #定义爬虫函数:抓取交易所信息
39
+ def fetchCrypto_Exchange(fsym,tsym,top=5):
40
+ urlprefix="https://min-api.cryptocompare.com/data/top/exchanges/full?fsym="
41
+ url = urlprefix + fsym + "&tsym=" + tsym
42
+ headers = {
43
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
44
+ }
45
+
46
+ response = requests.get(url,headers=headers)
47
+ soup = BeautifulSoup(response.content, "html.parser")
48
+ dic = json.loads(soup.prettify())
49
+
50
+ #筛选可交易所选产品的交易所,并显示列表
51
+ market = []
52
+ d = dic['Data']['Exchanges']
53
+ for i in range(len(d)):
54
+ market.append(d[i]['MARKET'])
55
+
56
+ #基于过去24小时内交易量进行交易所排名
57
+ vol = []
58
+ d = dic['Data']['Exchanges']
59
+ for i in range(len(d)):
60
+ volamt=round(float(d[i]['VOLUME24HOUR']),2)
61
+ if volamt > 0:
62
+ vol.append([d[i]['MARKET'], volamt])
63
+
64
+ #基于子列表中的第二项对子列表排序
65
+ vol = sorted(vol, key=lambda x: -x[1])
66
+
67
+ #基于过去24小时交易量显示所选产品活跃的5个交易所名单
68
+ print("\n全球最活跃的五家数字货币交易所:",fsym+"/"+tsym,"\n (24小时交易金额,"+tsym+"百万)")
69
+ for e in vol:
70
+ print("%10s%15.2f" % (e[0], e[1]))
71
+ import datetime as dt; nowtime=dt.datetime.now().strftime('%Y-%m-%d %H:%M')
72
+ print("*数据来源:CryptoCompare,",nowtime)
73
+ #活跃交易所Top 5,markets
74
+ markets = [e[0] for e in vol][0:top]
75
+ """
76
+ print("\n",markets,"\n")
77
+ """
78
+ return markets
79
+
80
+ if __name__=='__main__':
81
+ fsym="ETH"
82
+ tsym="USD"
83
+ top=10
84
+ markets=fetchCrypto_Exchange("ETH","USD")
85
+
86
+ #==============================================================================
87
+
88
+ #定义爬虫函数:从指定交易所抓取所选产品价格
89
+ #限制:考虑到网络传输量,一次抓取最多2000条交易(2000/365=5.5年最多)
90
+ def fetchCrypto_Price_byExchange(fsym, tsym, exchange):
91
+ #数据源: https://www.cryptocompare.com/api/
92
+ cols = ['date', 'timestamp', 'open', 'high', 'low', 'close']
93
+ lst = ['time', 'open', 'high', 'low', 'close']
94
+
95
+ timestamp_today = datetime.today().timestamp()
96
+ curr_timestamp = timestamp_today
97
+
98
+ headers = {
99
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
100
+ }
101
+
102
+ for j in range(2):
103
+ df = pd.DataFrame(columns=cols)
104
+ urlprefix="https://min-api.cryptocompare.com/data/histoday?fsym="
105
+ url = urlprefix + fsym + \
106
+ "&tsym=" + tsym + "&toTs=" + str(int(curr_timestamp)) + \
107
+ "&limit=2000" + "&e=" + exchange
108
+
109
+ response = requests.get(url,headers=headers)
110
+ soup = BeautifulSoup(response.content, "html.parser")
111
+ dic = json.loads(soup.prettify())
112
+ if len(dic['Data']) < 1:
113
+ return None #爬虫失败,返回空
114
+
115
+ for i in range(1, 2001):
116
+ tmp = []
117
+ for e in enumerate(lst):
118
+ x = e[0]; y = dic['Data'][i][e[1]]
119
+ if(x == 0):
120
+ #将timestamp转换为日期
121
+ td = datetime.fromtimestamp(int(y)).strftime('%Y-%m-%d')
122
+ tmp.append(td)
123
+ tmp.append(y)
124
+ if(np.sum(tmp[-4::]) > 0):
125
+ df.loc[len(df)] = np.array(tmp)
126
+
127
+ df.index = pd.to_datetime(df.date)
128
+ df.drop('date', axis=1, inplace=True)
129
+ curr_timestamp = int(df.iloc[0][0])
130
+
131
+ if(j == 0):
132
+ df0 = df.copy()
133
+ else:
134
+ data = pd.concat([df, df0], axis=0)
135
+
136
+ return data.astype(np.float64)
137
+
138
+ if __name__=='__main__':
139
+ data=fetchCrypto_Price_byExchange("ETH", "USD", "Coinsbit")
140
+
141
+
142
+ #==============================================================================
143
+ def Crypto_Price_Trend(fsym, tsym, exchange,fromdate,todate,power=4):
144
+ """
145
+ 功能:绘制价格趋势线
146
+ fsym: 数字货币产品
147
+ tsym: 交易币种
148
+ exchange: 交易所
149
+
150
+ """
151
+ print("... Searching for information, please wait ...")
152
+
153
+ #检查日期期间是否有效
154
+ import siat.common as cmn
155
+ result, start, end=cmn.check_period(fromdate, todate)
156
+ if not result:
157
+ print("#Error(Crypto_Price_Trend): invalid date period from",fromdate,'to',todate)
158
+ return None
159
+
160
+ #获得价格
161
+ p=fetchCrypto_Price_byExchange(fsym, tsym, exchange)
162
+ p1=p[p.index >= start]
163
+ p2=p1[p1.index <= end]
164
+
165
+ import siat.grafix as gfx
166
+ colname='close'
167
+ collabel='收盘价'
168
+ ylabeltxt='价格('+tsym+')'
169
+ titletxt="数字货币价格趋势:"+fsym+'/'+tsym+','+exchange
170
+ import datetime as dt; nowtime=dt.datetime.now().strftime('%Y-%m-%d %H:%M')
171
+ footnote="数据来源:CryptoCompare,"+str(nowtime)
172
+
173
+ import siat.grafix as g
174
+ g.plot_line(p2,colname,collabel,ylabeltxt,titletxt,footnote,power=power)
175
+
176
+ return p2
177
+
178
+ if __name__=='__main__':
179
+ fsym="ETH"
180
+ tsym="USD"
181
+ exchange='Coinbase'
182
+ fromdate='2020-7-1'
183
+ todate='2020-12-31'
184
+ power=3
185
+ price=Crypto_Price_Trend(fsym, tsym, exchange,fromdate,todate,power)
186
+ #==============================================================================
187
+ def compCrypto_Price(product1,product2,days=30):
188
+ """
189
+ 功能:比较两种数字货币的价格趋势,最近days天的情形
190
+ 输入:
191
+ product1/product2:产品列表,格式:[数字货币价格数据框,产品,币种,市场]
192
+ 输出:
193
+ 绘制折线图
194
+ 无返回数据
195
+ """
196
+
197
+ #绘制折线图
198
+ label1=product1[1]+'/'+product1[2]+' @'+product1[3]
199
+ p1=product1[0].tail(days)
200
+ plt.plot(p1['close'],label=label1,lw=3)
201
+
202
+ label2=product2[1]+'/'+product2[2]+' @'+product2[3]
203
+ p2=product2[0].tail(days)
204
+ plt.plot(p2['close'],label=label2,lw=3,ls=':')
205
+
206
+ #图示标题
207
+ titletxt="数字货币产品:跨市场的价格套利机会"
208
+ plt.title(titletxt,fontweight='bold')
209
+ plt.ylabel("收盘价")
210
+ plt.xticks(rotation=30)
211
+ plt.legend(loc='best')
212
+
213
+ plt.gca().set_facecolor('whitesmoke')
214
+ plt.show()
215
+
216
+ return
217
+
218
+ if __name__=='__main__':
219
+ prices1=fetchCrypto_Price_byExchange("ETH", "USD", "Coinsbit")
220
+ product1=[prices1,"ETH", "USD", "Coinsbit"]
221
+ prices2=fetchCrypto_Price_byExchange("ETH", "USD", "Coinbase")
222
+ product2=[prices2,"ETH", "USD", "Coinbase"]
223
+ compCrypto_Price(product1,product2)
224
+
225
+
226
+ #==============================================================================
227
+ def compCrypto_Return(product1,product2,days=30):
228
+ """
229
+ 功能:比较两种数字货币的收益率趋势,最近days天的情形
230
+ 输入:
231
+ product1/product2:产品列表,格式:[数字货币价格数据框,产品,币种,市场]
232
+ 输出:
233
+ 绘制折线图
234
+ 无返回数据
235
+ """
236
+ import pandas as pd
237
+
238
+ #绘制折线图
239
+ r1=product1[0]['close'].pct_change()
240
+ r1df=pd.DataFrame(r1)
241
+ r1df.columns=['ret']
242
+ r1df['ret%']=round(r1df['ret']*100.0,2)
243
+ r1t=r1df.tail(days)
244
+ label1=product1[1]+'/'+product1[2]+' @'+product1[3]
245
+ plt.plot(r1t['ret%'],label=label1,lw=3)
246
+
247
+ r2=product2[0]['close'].pct_change()
248
+ r2df=pd.DataFrame(r2)
249
+ r2df.columns=['ret']
250
+ r2df['ret%']=round(r2df['ret']*100.0,2)
251
+ r2t=r2df.tail(days)
252
+ label2=product2[1]+'/'+product2[2]+' @'+product2[3]
253
+ plt.plot(r2t['ret%'],label=label2,lw=3,ls=':')
254
+
255
+ plt.axhline(y=0.0,color='green',linestyle='--')
256
+
257
+ #图示标题
258
+ titletxt="数字货币产品:跨市场的收益率套利机会"
259
+ plt.title(titletxt,fontweight='bold')
260
+ plt.ylabel("资本利得%")
261
+ plt.xticks(rotation=30)
262
+ plt.legend(loc='best')
263
+
264
+ plt.gca().set_facecolor('whitesmoke')
265
+ plt.show()
266
+
267
+ return
268
+
269
+ if __name__=='__main__':
270
+ prices1=fetchCrypto_Price_byExchange("ETH", "USD", "Coinsbit")
271
+ product1=[prices1,"ETH", "USD", "Coinsbit"]
272
+ prices2=fetchCrypto_Price_byExchange("ETH", "USD", "Coinbase")
273
+ product2=[prices2,"ETH", "USD", "Coinbase"]
274
+ compCrypto_Return(product1,product2)
275
+
276
+
277
+ #==============================================================================
278
+ #定义爬虫函数:抓取列表中每个交易所的价格数据,合并数据到cp中
279
+ #注意:在指定的时间段,并非每个交易所都有ETH交易,若无则以nan表示
280
+ def fetchCrypto_Price_byExchList(fsym,tsym,markets,begdate,enddate):
281
+ cp=pd.DataFrame([]); print("\n%s/%s" % (fsym, tsym))
282
+ for market in markets:
283
+ print("%12s... " % market, end="")
284
+ df = fetchCrypto_Price_byExchange(fsym, tsym, market)
285
+ if df is None:
286
+ print("price retrieving failed")
287
+ continue #爬虫失败,跳出本次循环,继续下一轮循环
288
+
289
+ ts = df[(df.index >= begdate) & (df.index <= enddate)]["close"]
290
+ ts.name = market
291
+ if ('cp' in globals()) or ('cp' in locals()): #已经存在cp
292
+ tsdf=pd.DataFrame(ts)
293
+ cp = pd.merge(cp,tsdf,how='outer',left_index=True,right_index=True)
294
+ else: #第一次生成cp
295
+ cp = pd.DataFrame(ts)
296
+ print("price retrieved successfully")
297
+ return cp
298
+
299
+ #==============================================================================
300
+
301
+ #定义函数:计算2个市场组合之间所选产品的价差估计:均值,标准差
302
+ def calcSpread_in2Markets(cp):
303
+ dist = [] #存放每两个交易所组合之间价差的均值和标准差
304
+ for i in range(cp.shape[1]):
305
+ for j in range(i):
306
+ if(i != j):
307
+ x = np.array(cp.iloc[:,i], dtype=np.float32)
308
+ y = np.array(cp.iloc[:,j], dtype=np.float32)
309
+ diff = np.abs(x-y)
310
+ avg = np.mean(diff)
311
+ std = np.std(diff, ddof=1)
312
+ dist.append([cp.columns[i], cp.columns[j], avg, std])
313
+
314
+ import pandas as pd
315
+ dist=pd.DataFrame(dist)
316
+ dist.columns=['Market1','Market2','avg','std']
317
+ dist=dist.dropna(axis=0,how='any') #删除带有nan的行
318
+ dist1=dist.sort_values(by=['avg','std'],ascending=(False,False))
319
+ dist1=dist1.reset_index(drop = True) #重新索引
320
+ dist2=dist1.sort_values(by=['std','avg'],ascending=(True,False))
321
+
322
+ #打印
323
+ pd.set_option('display.max_columns', 1000)
324
+ pd.set_option('display.width', 1000)
325
+ pd.set_option('display.max_colwidth', 1000)
326
+ pd.set_option('display.unicode.ambiguous_as_wide', True)
327
+ pd.set_option('display.unicode.east_asian_width', True)
328
+
329
+ # Descending by spread
330
+ print("\n***Average inter-market spread:")
331
+ print(dist1.to_string(index=False))
332
+ # Descending by risk
333
+ print("\n***Inter-market spread volatility:")
334
+ print(dist2.to_string(index=False))
335
+ return dist1,dist2
336
+
337
+ #==============================================================================
338
+
339
+ #定义函数:显示2个市场组合之间所选产品的价差估计:均值,标准差
340
+ def printSpread_in2Markets(dist1,dist2):
341
+
342
+ dist1.rename(columns={'Market1':'市场1','Market2':'市场2','avg':'价差均值','std':'价差标准差'},inplace=True)
343
+ dist2.rename(columns={'Market1':'市场1','Market2':'市场2','avg':'价差均值','std':'价差标准差'},inplace=True)
344
+ dist2b=dist2[['市场1','市场2','价差标准差','价差均值']]
345
+
346
+ import pandas as pd
347
+ pd.set_option('display.unicode.ambiguous_as_wide', True)
348
+ pd.set_option('display.unicode.east_asian_width', True)
349
+ pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
350
+
351
+ print("\n ===== 市场间价差大小:按均值降序排列 =====")
352
+ print(dist1)
353
+ print("\n===== 市场间价差风险:按标准差降序排列 =====")
354
+ print(dist2b)
355
+
356
+ dist2b['收益-风险性价比']=round(dist2b['价差均值']/dist2b['价差标准差'],4)
357
+ dist3=dist2b[['市场1','市场2','收益-风险性价比','价差标准差','价差均值']]
358
+ dist3=dist3.sort_values(by=['收益-风险性价比'],ascending=(False))
359
+ print("\n ===== 市场间价差:按收益-风险性价比降序排列 =====")
360
+ print(dist3)
361
+
362
+ return
363
+ #==============================================================================
364
+ if __name__=='__main__':
365
+ fsym = "ETH"; tsym = "USD"
366
+ begdate="2020-1-1"; enddate="2020-12-31"
367
+ market1 = "primexbt"; market2 = "Coinbase"
368
+
369
+ #定义函数:绘图所选市场组合中的价差分布
370
+ def evalSpread_in2Markets(fsym,tsym,market1,market2,begdate,enddate):
371
+ #抓取两个市场的价格数据
372
+ df1 = fetchCrypto_Price_byExchange(fsym, tsym, market1)
373
+ df2 = fetchCrypto_Price_byExchange(fsym, tsym, market2)
374
+
375
+ #过滤时间段
376
+ df1 = df1[(df1.index > begdate) & (df1.index <= enddate)]
377
+ df2 = df2[(df2.index > begdate) & (df2.index <= enddate)]
378
+
379
+ #检查:两个市场在所选时间段每日必须都有交易,即收盘价数据表必须形状相同
380
+ #如果数据表形状不同,需要寻找双方共同具有交易数据的时间段,并重设时间段
381
+ #print(df1.close.shape[0], df2.close.shape[0])
382
+
383
+ #绘图:观察两个市场间的价差分布,再次检查是否存在套利机会:曲线不完全重叠
384
+ import pandas as pd
385
+ df12=pd.merge(df1,df2,how='inner',left_index=True,right_index=True)
386
+ df12['spread']=df12['close_x'] - df12['close_y']
387
+
388
+ #plt.figure(figsize=(12,6))
389
+ plt.figure(figsize=(12.8,6.4))
390
+ labeltxt=market1+"与"+market2+"的价差"
391
+ plt.plot(df12.spread, '.-', label=labeltxt)
392
+ #plt.plot(df2.close, '.-', label=market2)
393
+ plt.legend(loc='best')
394
+ plt.title(fsym+":在两个市场中的价格差异", fontsize=12)
395
+ plt.ylabel("价差("+tsym+")", fontsize=12)
396
+ plt.grid()
397
+
398
+ return df1,df2
399
+
400
+ #==============================================================================
401
+
402
+ #定义函数:回测MSA投资策略
403
+ def backtestMSA_Strategy(investment,account1,account2,position,df1,df2):
404
+ roi = [] #投资回报率记录
405
+ money = [] #资金头寸记录
406
+ ac1 = [account1]; ac2 = [account2]
407
+ #各个交易所的盈亏记录;pnl表示盈亏Profit and Loss
408
+ pnl_exch1 = []; pnl_exch2 = []
409
+ trade = False #标识当前是否处于交易状态
410
+ n = df1.close.shape[0] #所选产品的收盘价样本个数
411
+ trade_pnl=[] #整体投资的盈亏记录
412
+
413
+ #交易回测
414
+ for i in range(n): #对于每个日交易价格样本循环
415
+ p1 = float(df1.close.iloc[i]); p2 = float(df2.close.iloc[i])
416
+ if(p1 > p2): #[if-A]若交易所1中产品的价格高于交易所2,卖出1买入2
417
+ asset1 = "SHORT"; asset2 = "LONG"
418
+ if(trade == False):
419
+ #若当前未处于交易状态,则开始一个新的交易
420
+ open_p1 = p1; open_p2 = p2
421
+ open_asset1 = asset1; open_asset2 = asset2
422
+ trade = True #标识:当前处于交易状态
423
+ print("new traded opened:")
424
+ new_trade = False #标识:当前已处于交易状态,并非新交易
425
+ elif(asset1 == open_asset1):
426
+ new_trade = False
427
+ elif(asset1 == open_asset2):
428
+ new_trade = True
429
+
430
+ elif(p2 > p1): #[if-A]若交易所1中产品的价格低于交易所2,买入1卖出2
431
+ asset1 = "LONG"; asset2 = "SHORT"
432
+ if(trade == False):
433
+ #若当前未处于交易状态,则开始一个新的交易
434
+ open_p1 = p1; open_p2 = p2
435
+ open_asset1 = asset1; open_asset2 = asset2
436
+ trade = True
437
+ print("new traded opened:")
438
+ new_trade = False
439
+ elif(asset1 == open_asset1):
440
+ new_trade = False
441
+ elif(asset1 == open_asset2):
442
+ new_trade = True
443
+
444
+ if(i == 0): #[if-B]抓取交易样本的起点
445
+ print(df1.close.iloc[i], df2.close.iloc[i], \
446
+ asset1, asset2, trade, "----first trade info")
447
+ else: #[if-B]当前并非交易样本的起点,处于交易中间
448
+ if(new_trade): #[if-B:if-B1]当前处于交易空档期
449
+ #核算当前账户头寸:交易所1卖出
450
+ if(open_asset1 == "SHORT"): #[if-B:if-B1:if-B11]若交易所1卖出
451
+ #计算交易盈亏
452
+ pnl_asset1 = open_p1/p1 - 1
453
+ pnl_asset2 = p2/open_p2 -1
454
+ pnl_exch1.append(pnl_asset1)
455
+ pnl_exch2.append(pnl_asset2)
456
+ print(open_p1, p1, open_p2, p2, open_asset1, \
457
+ open_asset2, round(pnl_asset1,4), round(pnl_asset2,4))
458
+ #更新账户余额
459
+ account1 = account1 + position*pnl_asset1
460
+ account2 = account2 + position*pnl_asset2
461
+ print("accounts [USD] = ", round(account1,2), round(account2,2))
462
+ if((account1 <=0) or (account2 <=0)):
463
+ print("--trading halted") #账户余额不足,停止交易
464
+ break
465
+ #计算投资回报ROI
466
+ total = account1 + account2
467
+ roi.append(total/investment-1)
468
+ ac1.append(account1); ac2.append(account2)
469
+ money.append(total)
470
+ print("ROI = ", round(roi[-1],4)); print("trade closed\n")
471
+ trade = False
472
+
473
+ #开始一个新的交易
474
+ if(asset1 == "SHORT"): #交易所1卖出交易
475
+ open_p1 = p1; open_p2 = p2
476
+ open_asset1 = asset1; open_asset2 = asset2
477
+ else:
478
+ open_p1 = p1; open_p2 = p2
479
+ open_asset1 = asset1; open_asset2 = asset2
480
+ trade = True
481
+ print("new trade opened:", asset1, asset2, \
482
+ open_p1, open_p2)
483
+
484
+ #核算当前账户头寸:交易所1买入
485
+ if(open_asset1 == "LONG"): #[if-B:if-B1:if-B12]若交易所1买入
486
+ #计算交易盈亏
487
+ pnl_asset1 = p1/open_p1 -1
488
+ pnl_asset2 = open_p2/p2 - 1
489
+ pnl_exch1.append(pnl_asset1)
490
+ pnl_exch2.append(pnl_asset2)
491
+ print(open_p1, p1, open_p2, p2, open_asset1, \
492
+ open_asset2, round(pnl_asset1,4), round(pnl_asset2,4))
493
+ #更新账户余额
494
+ account1 = account1 + position*pnl_asset1
495
+ account2 = account2 + position*pnl_asset2
496
+ print("accounts [USD] = ", round(account1,2), round(account2,2))
497
+ if((account1 <=0) or (account2 <=0)):
498
+ print("--trading halted")
499
+ break
500
+ #计算投资回报ROI
501
+ total = account1 + account2
502
+ roi.append(total/investment-1)
503
+ ac1.append(account1); ac2.append(account2)
504
+ money.append(total)
505
+ trade_pnl.append(pnl_asset1+pnl_asset2)
506
+ print("ROI = ", round(roi[-1],4))
507
+ print("trade closed\n")
508
+ trade = False
509
+
510
+ #开始一个新的交易
511
+ if(open_asset1 == "SHORT"): #若交易所1卖出
512
+ open_p1 = p1; open_p2 = p2
513
+ open_asset1 = asset1; open_asset2 = asset2
514
+ else:
515
+ open_p1 = p1; open_p2 = p2
516
+ open_asset1 = asset1; open_asset2 = asset2
517
+ new_trade = False
518
+ trade = True
519
+ print("new trade opened:", asset1, asset2, \
520
+ open_p1, open_p2)
521
+
522
+ else: #[if-B:if-B1]
523
+ print(" ",df1.close.iloc[i], df2.close.iloc[i], \
524
+ asset1, asset2)
525
+
526
+ return ac1,ac2,money,roi
527
+
528
+ #==============================================================================
529
+
530
+ #定义函数:绘图每个账户的头寸
531
+ def eval_Position(market1,market2,investment,ac1,ac2,money):
532
+
533
+
534
+ ymax=(round(max(money)/1000)+1)*1000
535
+ #plt.figure(figsize=(18,9))
536
+ plt.figure(figsize=(12.8,6.4))
537
+ # market1的账户ac1变化图
538
+ plt.subplot(2,3,1)
539
+ plt.plot(ac1)
540
+ plt.title(market1 + "市场的资金账户余额")
541
+ plt.xlabel("交易序列"); plt.ylabel("账户余额")
542
+ plt.grid()
543
+ plt.xlim([0, len(money)]); plt.ylim([0, ymax])
544
+ # market2的账户ac2变化图
545
+ plt.subplot(2,3,2)
546
+ plt.plot(ac2)
547
+ plt.title(market2 + "市场的资金账户余额")
548
+ plt.xlabel("交易序列"); plt.ylabel("账户余额")
549
+ plt.grid()
550
+ plt.xlim([0, len(money)]); plt.ylim([0, ymax])
551
+ # 总的账户money变化图
552
+ plt.subplot(2,3,3)
553
+ plt.plot(np.array(money))
554
+ plt.title("资金总额")
555
+ plt.xlabel("交易序列"); plt.ylabel("金额")
556
+ plt.grid()
557
+ plt.xlim([0, len(money)]); plt.ylim([investment, ymax])
558
+
559
+ return
560
+
561
+ #定义函数:绘图投资总收益ROI
562
+ def eval_Roi(fsym,tsym,market1,market2,roi,begdate,enddate):
563
+
564
+
565
+ ttltrdnum=len(roi)
566
+ ymax=round(10*roi[-1])*10+10
567
+ product=fsym+"/"+tsym+":"
568
+ mktpair="市场配对("+market1+","+market2+")"
569
+ #plt.figure(figsize=(8,5))
570
+ plt.figure(figsize=(12.8,6.4))
571
+ plt.plot(np.array(roi)*100, 'r')
572
+
573
+ footnote1="交易序列(总计"+str(ttltrdnum)+"次)"
574
+ footnote2="\n交易期间:"+begdate+"至"+enddate
575
+ plt.xlabel(footnote1+footnote2)
576
+
577
+ plt.ylabel("收益率%")
578
+ plt.title(product+mktpair+", ROI = %s%%" % str(round(100*roi[-1],2)))
579
+ plt.xlim([0, len(roi)]); plt.ylim([0, ymax])
580
+ plt.grid()
581
+
582
+ return
583
+
584
+ #定义函数:显示任意2个市场间的平均价差和风险,供手动选择两个市场
585
+ def select2Markets(fsym,tsym,begdate,enddate):
586
+ #抓取所选产品最活跃的交易所Top5,放入市场列表markets中
587
+ markets=fetchCrypto_Exchange(fsym,tsym,5)
588
+
589
+ #抓取指定交易所列表markets中所选产品在指定期间内的收盘价,放入收盘价cp中
590
+ cp=fetchCrypto_Price_byExchList(fsym,tsym,markets,begdate,enddate)
591
+
592
+ #计算cp中任意2个市场组合之间所选产品的价差估计
593
+ dist1,dist2=calcSpread_in2Markets(cp)
594
+
595
+ return
596
+
597
+
598
+ #定义函数:选择价差均值avg最大的2个市场。风险偏好:进取型
599
+ def select2Markets_TopSpreadAvg(fsym,tsym,begdate,enddate):
600
+ #抓取所选产品最活跃的交易所Top5,放入市场列表markets中
601
+ markets=fetchCrypto_Exchange(fsym,tsym,5)
602
+
603
+ #基于投资风险偏好,选择2个价差最大的交易所
604
+ #抓取指定交易所列表markets中所选产品在指定期间内的收盘价,放入收盘价cp中
605
+ cp=fetchCrypto_Price_byExchList(fsym,tsym,markets,begdate,enddate)
606
+
607
+ #计算cp中任意2个市场组合之间所选产品的价差估计
608
+ dist1,dist2=calcSpread_in2Markets(cp)
609
+
610
+ #显示任意2个市场组合收盘价价差的均值和标准差
611
+ print("\n*****Sorted by descending average of market spread\n",dist1)
612
+
613
+ market1=dist1.loc[0,'Market1']; market2=dist1.loc[0,'Market2']
614
+ return market1,market2
615
+
616
+ #定义函数:选择价差标准差std最小的2个市场。风险偏好:保守型
617
+ def select2Markets_BottomSpreadStd(fsym,tsym,begdate,enddate):
618
+ #抓取所选产品最活跃的交易所Top5,放入市场列表markets中
619
+ markets=fetchCrypto_Exchange(fsym,tsym,5)
620
+
621
+ #基于投资风险偏好,选择2个价差最大的交易所
622
+ #抓取指定交易所列表markets中所选产品在指定期间内的收盘价,放入收盘价cp中
623
+ cp=fetchCrypto_Price_byExchList(fsym,tsym,markets,begdate,enddate)
624
+
625
+ #计算cp中任意2个市场组合之间所选产品的价差估计
626
+ dist1,dist2=calcSpread_in2Markets(cp)
627
+ dist2=dist2.reset_index(drop = True) #重新索引
628
+ #显示任意2个市场组合收盘价价差的均值和标准差
629
+ print("\n*****Sorted by descending std of market spread\n",dist2)
630
+
631
+ market1=dist2.loc[len(dist2)-1,'Market1']
632
+ market2=dist2.loc[len(dist2)-1,'Market2']
633
+ return market1,market2
634
+
635
+ #定义函数:选择市场后,一步实现所有MSA操作
636
+ def implementMSA_Strategy(fsym,tsym,investment,account1,account2,position, \
637
+ market1,market2,begdate,enddate,verbose=False):
638
+
639
+ print(" Starting MSA spread trading and backtesting ......")
640
+ print(" If expecting transaction details, please set verbose=True in command")
641
+
642
+ df1,df2=evalSpread_in2Markets(fsym,tsym,market1,market2,begdate,enddate)
643
+
644
+ #屏蔽函数内print信息输出的类
645
+ import os, sys
646
+ class HiddenPrints:
647
+ def __enter__(self):
648
+ self._original_stdout = sys.stdout
649
+ sys.stdout = open(os.devnull, 'w')
650
+
651
+ def __exit__(self, exc_type, exc_val, exc_tb):
652
+ sys.stdout.close()
653
+ sys.stdout = self._original_stdout
654
+
655
+ if not verbose:
656
+ with HiddenPrints():
657
+ ac1,ac2,money,roi=backtestMSA_Strategy( \
658
+ investment,account1,account2,position,df1,df2)
659
+ else:
660
+ ac1,ac2,money,roi=backtestMSA_Strategy( \
661
+ investment,account1,account2,position,df1,df2)
662
+
663
+ #省略此步骤?
664
+ eval_Position(market1,market2,investment,ac1,ac2,money)
665
+ eval_Roi(fsym,tsym,market1,market2,roi,begdate,enddate)
666
+ return
667
+ #==============================================================================