siat 3.2.20__py3-none-any.whl → 3.2.30__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.
siat/allin.py CHANGED
@@ -120,3 +120,6 @@ from siat.stock_technical import *
120
120
  # 2FA: Google Authenticator
121
121
  from siat.google_authenticator import *
122
122
 
123
+ # 提问记录
124
+ from siat.luchy_draw import *
125
+
siat/common.py CHANGED
@@ -1620,7 +1620,7 @@ def print_progress_percent2(current,total_list,steps=5,leading_blanks=4):
1620
1620
  pct=pct_list[pos]
1621
1621
 
1622
1622
  if pct=="100%":
1623
- print("100% completing last job ...")
1623
+ print("100% wrapping up ...")
1624
1624
  else:
1625
1625
  print(pct,end=' ')
1626
1626
 
@@ -2182,10 +2182,27 @@ def descriptive_statistics2(df,titletxt,footnote,decimals=4,sortby='tpw_mean', \
2182
2182
  print(" #Error(descriptive_statistics2): zero data found")
2183
2183
  return
2184
2184
 
2185
+ #为避免nan的影响,对nan进行填充
2186
+ df.fillna(method='bfill',inplace=True)
2187
+ df.fillna(method='ffill',inplace=True)
2188
+
2189
+ #转换字符为数值
2190
+ import pandas as pd
2191
+ for c in list(df):
2192
+ try:
2193
+ df[c] = pd.to_numeric(df[c], errors='coerce')
2194
+ except:
2195
+ continue
2196
+
2197
+ dfn=df.select_dtypes(include='number')
2198
+ if dfn.empty:
2199
+ print(" #Error(descriptive_statistics2): no numeric columns found to describe")
2200
+ return
2201
+
2185
2202
  # 计算短期趋势
2186
2203
  df20=df.tail(trailing)
2187
2204
 
2188
- ds=df.describe(include='all',percentiles=[.5])
2205
+ ds=df.describe(include='number',percentiles=[.5])
2189
2206
  dst=ds.T
2190
2207
  cols=['min','max','50%','mean','std']
2191
2208
 
@@ -2952,6 +2969,10 @@ def df_preprocess(dfs,measure,axhline_label,x_label,y_label, \
2952
2969
  """
2953
2970
  功能:对于dfs中的数据进行预处理变换,以便克服数量级压制现象更好地展现多条曲线的趋势
2954
2971
  """
2972
+ #填充空值,防止后续处理出错
2973
+ dfs.fillna(method='ffill') #使用前一个非NaN值填充(向下填充)
2974
+ dfs.fillna(method='bfill') #使用下一个非NaN值填充(向上填充)
2975
+
2955
2976
  plus_sign=False
2956
2977
 
2957
2978
  preprocess1=preprocess.lower()
@@ -2999,8 +3020,10 @@ def df_preprocess(dfs,measure,axhline_label,x_label,y_label, \
2999
3020
  # 从头寻找第一个非空数值
3000
3021
  import numpy as np
3001
3022
  for n in range(0,len(dfs2)):
3002
- if np.isnan(dfs2[c][n]):
3003
- continue
3023
+ try:
3024
+ checknan=np.isnan(dfs2[c][n])
3025
+ except: continue
3026
+ if checknan: continue
3004
3027
  else:
3005
3028
  cmean=dfs2[c][n] #使用第一个非空值
3006
3029
  break
@@ -3011,8 +3034,9 @@ def df_preprocess(dfs,measure,axhline_label,x_label,y_label, \
3011
3034
  scalingOptionText=text_lang('百分比','percentage')
3012
3035
  else:
3013
3036
  scalingOptionText=text_lang('变化率%','change%')
3014
-
3015
- meanlist=meanlist+[cmean]
3037
+ try:
3038
+ meanlist=meanlist+[cmean]
3039
+ except: continue
3016
3040
 
3017
3041
  #print(cmean,cstd,dfs2[c])
3018
3042
 
@@ -3765,7 +3789,8 @@ if __name__=='__main__':
3765
3789
  upgrade_siat()
3766
3790
 
3767
3791
  def upgrade_siat(module_list=['siat','akshare','pandas','pandas_datareader', \
3768
- 'yfinance','yahooquery','urllib3','tabulate','twine','mplfinance','openpyxl'], \
3792
+ 'yfinance','yahooquery','urllib3','tabulate','twine', \
3793
+ 'mplfinance','openpyxl','pip'], \
3769
3794
  pipcmd="pip install --upgrade",alternative=""):
3770
3795
  """
3771
3796
  功能:一次性升级siat及其相关插件
siat/grafix.py CHANGED
@@ -1390,20 +1390,35 @@ def draw_lines2(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
1390
1390
  collist=df.columns.values.tolist()
1391
1391
  collist3=collist[:3] #专用于绘制布林带,取前3个字段
1392
1392
 
1393
+ if lslist==[]:
1394
+ lslist=['-','--','-.',':','-','--','-.',':','-','--','-.',':','-','--','-.',':',]
1395
+ if colorlist==[]:
1396
+ colorlist=['blue','tomato','green','chocolate','darksage','cyan','blueviolet','violet','darkcyan','gold','wheat','silver','darkred','brown','coral','pink',]
1397
+
1393
1398
  #绘制折线图
1394
1399
  for c in collist:
1395
1400
  pos=collist.index(c)
1396
1401
  try:
1397
1402
  lcolor=colorlist[pos]
1403
+ except:
1404
+ lcolor=''
1405
+ try:
1398
1406
  lls=lslist[pos]
1407
+ except:
1408
+ lls=''
1409
+ try:
1399
1410
  llw=lwlist[pos]
1400
1411
  except:
1401
- lwadjust=linewidth_adjust(df)
1402
- plt.plot(df[c],label=c,linewidth=lwadjust)
1403
- lines = plt.gca().lines; last_line_color = lines[-1].get_color()
1404
- else:
1412
+ llw=linewidth_adjust(df)
1413
+
1414
+ if (lcolor !='') and (lls !=''):
1405
1415
  plt.plot(df[c],label=c,linewidth=llw,ls=lls,color=lcolor)
1406
- lines = plt.gca().lines; last_line_color = lines[-1].get_color()
1416
+ elif (lcolor !=''):
1417
+ plt.plot(df[c],label=c,linewidth=llw,color=lcolor)
1418
+ else:
1419
+ plt.plot(df[c],label=c,linewidth=llw)
1420
+
1421
+ lines = plt.gca().lines; last_line_color = lines[-1].get_color()
1407
1422
 
1408
1423
  #为折线加数据标签
1409
1424
  if data_label==True:
@@ -1474,8 +1489,27 @@ def draw_lines2(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
1474
1489
 
1475
1490
  #绘制带状区域
1476
1491
  if band_area != '' and isinstance(band_area,list) and len(band_area)>=2:
1477
- plt.fill_between(df.index,df[band_area[0]],df[band_area[1]],alpha=0.5,label='')
1478
-
1492
+ upper_line=band_area[0]; lower_line=band_area[1]
1493
+ if upper_line not in collist:
1494
+ upper_line=ectranslate(upper_line)
1495
+ lower_line=ectranslate(lower_line)
1496
+
1497
+ if upper_line not in collist:
1498
+ upper_line=ticker_name(upper_line)
1499
+ lower_line=ticker_name(lower_line)
1500
+
1501
+ if upper_line not in collist:
1502
+ upper_line=None
1503
+ lower_line=None
1504
+
1505
+ if not (upper_line is None) and not (lower_line is None):
1506
+ """
1507
+ plt.fill_between(df.index,df[upper_line],df[lower_line],color='moccasin',alpha=0.5,label='',where=df[upper_line]>=df[lower_line])
1508
+ plt.fill_between(df.index,df[upper_line],df[lower_line],color='aquamarine',alpha=0.5,label='',where=df[upper_line]<=df[lower_line])
1509
+ """
1510
+ plt.fill_between(df.index,df[upper_line],df[lower_line],color='aquamarine',alpha=0.5,label='')
1511
+
1512
+
1479
1513
  #绘制水平辅助线
1480
1514
  if axhline_label !="":
1481
1515
  if "零线" in axhline_label:
siat/luchy_draw.py ADDED
@@ -0,0 +1,252 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ 本模块功能:幸运抽奖,仅限课堂案例演示用
5
+ 创建日期:2024年6月29日
6
+ 最新修订日期:
7
+ 作者:王德宏 (WANG Dehong, Peter)
8
+ 作者单位:北京外国语大学国际商学院
9
+ 用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
10
+ 特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
11
+ """
12
+
13
+ #==============================================================================
14
+ #关闭所有警告
15
+ import warnings; warnings.filterwarnings('ignore')
16
+ import warnings; warnings.filterwarnings('ignore')
17
+
18
+ import pandas as pd
19
+ import pickle
20
+ import random
21
+ import datetime
22
+ import time
23
+
24
+ #==============================================================================
25
+ if __name__=='__main__':
26
+ text="A B C"
27
+ text_color='red'
28
+ text_size=12
29
+ delay=1
30
+
31
+ typewriter(text,text_color='red',text_size=12,delay=1)
32
+
33
+ def typewriter(text,text_color='blue',text_size=12,delay=1):
34
+ from IPython.display import display_html
35
+
36
+ text_html="<center><font size="+str(text_size)+" color="+text_color+">"+text
37
+ display_html(text_html, raw=True)
38
+
39
+ return
40
+
41
+ #==============================================================================
42
+
43
+ def pickle_write(df,pickle_path):
44
+ with open(pickle_path, 'wb') as pickle_file:
45
+ # 使用pickle模块的dump函数写入对象
46
+ pickle.dump(df,pickle_file)
47
+
48
+ if __name__=='__main__':
49
+ file_path="S:\北外工作-24春\小学期-人大\学生名单\student_list.xlsx"
50
+ pickle_path="student_list.pkl"
51
+ skiprows=1
52
+ column='Name'
53
+
54
+ namelist_get(file_path,skiprows=1,column='Name',pickle_path="student_list.pkl")
55
+
56
+ def lucky_draw_initialize(file_path,skiprows=1,column='Name',pickle_path="student_list.pkl"):
57
+ """
58
+ 功能:读入带有指定路径的Excel文件file_path,跳过前skiprows行
59
+ Excel文件结构:抽奖名单字段为'Name',字段位于第2行
60
+ 输出:存入pickle文件student_list.pkl
61
+ """
62
+
63
+ df = pd.read_excel(file_path,skiprows=skiprows)
64
+
65
+ df1=df[[column]].copy()
66
+
67
+ todaydt = str(datetime.date.today())
68
+ df1['Date']=todaydt
69
+ df1['Lucky']=0
70
+ df1['Absent']=0
71
+ df1['Answer']=0
72
+
73
+ #排序
74
+ df1.sort_values(by=[column,'Date','Lucky','Absent','Answer'],inplace=True)
75
+ df1.reset_index(drop=True,inplace=True)
76
+
77
+ pickle_write(df1,pickle_path)
78
+
79
+ return
80
+
81
+ #==============================================================================
82
+ if __name__=='__main__':
83
+ pickle_path="student_list.pkl"
84
+
85
+ df=pickle_read(pickle_path)
86
+
87
+ def pickle_read(pickle_path="student_list.pkl"):
88
+ with open(pickle_path,'rb') as pickle_file:
89
+ df = pickle.load(pickle_file)
90
+ return df
91
+
92
+ #==============================================================================
93
+
94
+
95
+
96
+ def pickle_append(df,column='Name',pickle_path="student_list.pkl"):
97
+ df_tmp=pickle_read(pickle_path)
98
+ df_new=pd.concat([df_tmp,df])
99
+
100
+ df_new.sort_values(by=[column,'Date'],inplace=True)
101
+
102
+ pickle_write(df_new,pickle_path)
103
+
104
+ return
105
+
106
+ #==============================================================================
107
+ if __name__=='__main__':
108
+ alist=["A","B","C","D"]
109
+
110
+ for i in range(4):
111
+ print(random_select(alist))
112
+
113
+
114
+ def random_select(alist):
115
+ return random.choice(alist)
116
+
117
+ #==============================================================================
118
+
119
+ if __name__=='__main__':
120
+ prompt="Is the lucky person here in class?"
121
+
122
+ read_yes_no(prompt)
123
+
124
+
125
+ def read_yes_no(prompt, default=None):
126
+ if default is None:
127
+ prompt += " [yes/no] "
128
+ else:
129
+ prompt += " [yes/no] (default: %s) " % ('yes' if default else 'no')
130
+ while True:
131
+ user_input = input(prompt).lower()
132
+ if user_input in ['', 'yes', 'y', 'true']:
133
+ return True
134
+ elif user_input in ['no', 'n', 'false']:
135
+ return False
136
+ elif user_input == '' and default is not None:
137
+ return default
138
+ else:
139
+ print("Please enter 'yes' or 'no' (or 'y'/'n').")
140
+
141
+ return
142
+
143
+ #==============================================================================
144
+ if __name__=='__main__':
145
+ draw_limit=2
146
+ absent_limit=2
147
+ column='Name'
148
+ pickle_path="student_list.pkl"
149
+
150
+ lucky_draw()
151
+ df=pickle_read(pickle_path)
152
+
153
+
154
+ def lucky_draw(draw_limit=2,absent_limit=2,column='Name',pickle_path="student_list.pkl"):
155
+ """
156
+ draw_limit=2:整个课程每人最多2次抽签机会
157
+ absent_limit=2:整个课程每人最多缺席2次,超过就丧失抽签资格
158
+ """
159
+ df=pickle_read(pickle_path)
160
+
161
+ alist=list(set(list(df[column])))
162
+
163
+ found=False
164
+ todaydt = str(datetime.date.today())
165
+ prompt="*** Is the lucky person here on site?"
166
+ prompt2="*** Do you expect to pass?"
167
+
168
+ while True:
169
+ while True:
170
+ aname=random_select(alist)
171
+
172
+ adf=df[df[column]==aname]
173
+ atimes=adf['Lucky'].sum()
174
+ aonsite=adf['Absent'].sum()
175
+
176
+ if atimes < draw_limit and aonsite <= absent_limit:
177
+ #检查今日是否被抽中过
178
+ drew_today=False
179
+ try:
180
+ adf_today=adf[adf['Date']==todaydt]
181
+ if len(adf_today) > 0:
182
+ if adf_today['Lucky'].sum() > 0 or adf_today['Absent'].sum() > 0:
183
+ drew_today=True
184
+ except: pass
185
+
186
+ if not drew_today:
187
+ found=True
188
+ break
189
+ else: continue
190
+ else:
191
+ continue
192
+
193
+ if not found:
194
+ print("Congratulations! all person has been lucky for",limit,"times")
195
+ else:
196
+ """
197
+ print("\nThe lucky person is ",end='')
198
+ typewriter(aname,delay=1)
199
+ """
200
+ typewriter(text=aname,text_color='blue',text_size=12,delay=1)
201
+
202
+ #print('')
203
+ onsite=read_yes_no(prompt)
204
+ #是否到场
205
+ if onsite: absent=0
206
+ else: absent=1
207
+
208
+ onpass=False; answer=0
209
+ if onsite:
210
+ onpass=read_yes_no(prompt2)
211
+ #是否pass
212
+ if onpass: answer=0
213
+ else: answer=1
214
+
215
+ #只要抽中,不论是否到场都记录
216
+ row=pd.Series({column:aname,'Date':todaydt,'Lucky':1,'Absent':absent,'Answer':answer})
217
+ try:
218
+ df=df.append(row,ignore_index=True)
219
+ except:
220
+ df=df._append(row,ignore_index=True)
221
+
222
+ if onsite and not onpass:
223
+ #到场且不pass,结束本轮抽签
224
+ break
225
+ else:
226
+ #未到场或pass,继续抽签
227
+ continue
228
+
229
+ df.sort_values(by=[column,'Date'],inplace=True)
230
+ pickle_write(df,pickle_path)
231
+
232
+ return
233
+
234
+ #==============================================================================
235
+ #==============================================================================
236
+ #==============================================================================
237
+ #==============================================================================
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+ #==============================================================================#==============================================================================
249
+ #==============================================================================#==============================================================================
250
+ #==============================================================================#==============================================================================
251
+
252
+ #==============================================================================
siat/market_china.py CHANGED
@@ -904,6 +904,177 @@ def market_detail_china2table(df,titletxt,firstColSpecial=True,leftColAlign='l',
904
904
  return
905
905
 
906
906
  #==============================================================================
907
+ #==============================================================================
908
+ if __name__=='__main__':
909
+ category='price'
910
+ category='valuation'
911
+
912
+ facecolor='papayawhip'
913
+ decimals=2
914
+ font_size='16px'
915
+
916
+ df1=market_detail_china(category='price')
917
+ df1=market_detail_china(category='price')
918
+
919
+ def market_detail_china2(category='price',
920
+ facecolor='papayawhip',
921
+ decimals=2,
922
+ font_size='22px'):
923
+ """
924
+ 功能:给出中国当前最新的三大股票交易所的更多细节,合成
925
+ 输出:构造表格型数据框df,利用CSS格式输出
926
+ 数据来源:em东方财富
927
+ """
928
+ #确定表格字体大小
929
+ titile_font_size=font_size
930
+ heading_font_size=data_font_size=str(int(font_size.replace('px',''))-2)+'px'
931
+
932
+ # 检查信息类别
933
+ category1=category.upper()
934
+ catelist=["PRICE","VOLUME","RETURN","VALUATION"]
935
+ catenamelist=["当前股价","当前成交量","近期投资回报","市值与估值"]
936
+ if not (category1 in catelist):
937
+ print(" #Error(market_detail_china2): invalid category",category)
938
+ print(" Valid category",catelist)
939
+ return None
940
+
941
+ # 合并三大交易所
942
+ import pandas as pd
943
+ df=pd.DataFrame()
944
+ exchlist=["SSE","SZSE","BJSE"]
945
+ for e in exchlist:
946
+ dft=market_detail_exchange_china(exchange=e,category=category)
947
+ if dft is None:
948
+ print(" #Warning(market_detail_china): info inaccessible for",e,"\b, try later")
949
+ #return None
950
+ continue
951
+ if len(dft)==0:
952
+ print(" #Warning(market_detail_china): zero info found for",e,"\b, try later")
953
+ continue
954
+
955
+ if len(df)==0:
956
+ df=dft
957
+ else:
958
+ df=pd.merge(df,dft,left_index=True,right_index=True)
959
+
960
+ if len(df)==0:
961
+ print(" #Warning(market_detail_china2): zero info found for",exchlist,"\b, try later")
962
+ return None
963
+
964
+ # 处理索引字段
965
+ newcollist=['项目']+list(df)
966
+ df['项目']=df.index
967
+ df=df[newcollist]
968
+
969
+ # 将空缺值替换为空格
970
+ df.fillna('',inplace=True)
971
+
972
+ import datetime as dt
973
+ nowstr0=str(dt.datetime.now())
974
+ nowstr=nowstr0[:19]
975
+
976
+ # 前置空格个数
977
+ heading=' '*1
978
+
979
+ if category1=='PRICE':
980
+ titletxt="中国三大股票交易所横向对比:股价与涨跌"
981
+
982
+ ft0=heading+"信息来源:东方财富,统计时间:"+nowstr+"\n"
983
+ ft1=heading+"注释:\n"
984
+ ft2=heading+"☆可交易股票数量:将随着股票停复牌情况变化\n"
985
+ ft3=heading+"☆昨日指的是上一个交易日\n"
986
+ ft4=heading+"☆涨速:平均每分钟股价变化率,表示股价变化速度\n"
987
+ ft5=heading+"☆5分钟涨跌:最新5分钟内股价的涨跌幅度\n"
988
+ ft6=heading+"☆振幅:最高最低价差绝对值/昨收,表示股价变化活跃程度\n"
989
+ ft7=heading+"☆涨跌幅:(最新价-昨收)/昨收,表示相对昨日的变化程度\n"
990
+ ft8=heading+"☆涨跌额:最新价-昨收,表示相对昨日的变化金额\n"
991
+
992
+ ft9=heading+"☆使用实时数据,不同日期/每天不同时刻统计的结果可能不同\n"
993
+ ft10=heading+"☆若在非交易日或开市前后短期内统计,数据可能出现空缺\n"
994
+
995
+ footnote=ft0+ft1+ft2+ft3+ft4+ft5+ft6+ft7+ft8+ft9+ft10
996
+
997
+ df_display_CSS(df,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
998
+ first_col_align='left',second_col_align='right', \
999
+ last_col_align='right',other_col_align='right', \
1000
+ titile_font_size=titile_font_size, \
1001
+ heading_font_size=heading_font_size, \
1002
+ data_font_size=data_font_size)
1003
+
1004
+
1005
+ if category1=='VOLUME':
1006
+ titletxt="中国三大股票交易所横向对比:成交状况"
1007
+
1008
+ ft0=heading+"信息来源:东方财富,统计时间:"+nowstr+"\n"
1009
+ ft1=heading+"注:\n"
1010
+ ft2=heading+"☆可交易股票数量:将随着股票停复牌情况变化\n"
1011
+ ft3=heading+"☆成交量:当前成交股数,表示交易活跃度\n"
1012
+ ft4=heading+"☆成交额:当前开市后的累计成交金额\n"
1013
+ ft5=heading+"☆换手率:成交量/流通股数,表示成交量占比\n"
1014
+ ft6=heading+"☆量比:当前每分钟成交量/过去5个交易日均值,表示成交量变化\n"
1015
+
1016
+ ft9=heading+"☆使用实时数据,不同日期/每天不同时刻统计的结果可能不同\n"
1017
+ ft10=heading+"☆若在非开市时间或开市前后短期内统计,数据可能出现空缺\n"
1018
+
1019
+ footnote=ft0+ft1+ft2+ft3+ft4+ft5+ft6 + ft9+ft10
1020
+
1021
+ df_display_CSS(df,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
1022
+ first_col_align='left',second_col_align='right', \
1023
+ last_col_align='right',other_col_align='right', \
1024
+ titile_font_size=titile_font_size, \
1025
+ heading_font_size=heading_font_size, \
1026
+ data_font_size=data_font_size)
1027
+
1028
+
1029
+ if category1=='RETURN':
1030
+ titletxt="中国三大股票交易所横向对比:投资回报"
1031
+
1032
+ ft0=heading+"信息来源:东方财富,统计时间:"+nowstr+"\n"
1033
+ ft1=heading+"注:\n"
1034
+ ft2=heading+"☆可交易股票数量:将随着股票停复牌情况变化\n"
1035
+ ft3=heading+"☆MRQ:最近一个季度的滚动数据\n"
1036
+ ft4=heading+"☆YTD:今年以来的累计情况\n"
1037
+
1038
+ ft9=heading+"☆使用实时数据,不同日期/每天不同时刻统计的结果可能不同\n"
1039
+ ft10=heading+"☆若在非开市时间或开市前后短期内统计,数据可能出现空缺\n"
1040
+
1041
+ footnote=ft0+ft1+ft2+ft3+ft4 + ft9+ft10
1042
+
1043
+ df_display_CSS(df,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
1044
+ first_col_align='left',second_col_align='right', \
1045
+ last_col_align='right',other_col_align='right', \
1046
+ titile_font_size=titile_font_size, \
1047
+ heading_font_size=heading_font_size, \
1048
+ data_font_size=data_font_size)
1049
+
1050
+
1051
+ if category1=='VALUATION':
1052
+ titletxt="中国三大股票交易所横向对比:市值与估值"
1053
+
1054
+ ft0=heading+"信息来源:东方财富,统计时间:"+nowstr+"\n"
1055
+ ft1=heading+"注:\n"
1056
+ ft2=heading+"☆可交易股票数量:将随着股票停复牌情况变化\n"
1057
+ ft3=heading+"☆市盈率:这里为动态市盈率,即市盈率TTM,过去12个月的连续变化\n"
1058
+ ft4=heading+"☆市净率:这里为静态市净率\n"
1059
+ ft5=heading+"☆标准差/均值=标准差(数值)/均值,提升可比性\n"
1060
+
1061
+ ft9=heading+"☆使用实时数据,不同日期/每天不同时刻统计的结果可能不同\n"
1062
+ ft10=heading+"☆若在非开市时间或开市前后短期内统计,数据可能出现空缺\n"
1063
+
1064
+ footnote=ft0+ft1+ft2+ft3+ft4+ft5 + ft9+ft10
1065
+
1066
+ df_display_CSS(df,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
1067
+ first_col_align='left',second_col_align='right', \
1068
+ last_col_align='right',other_col_align='right', \
1069
+ titile_font_size=titile_font_size, \
1070
+ heading_font_size=heading_font_size, \
1071
+ data_font_size=data_font_size)
1072
+
1073
+
1074
+ return df
1075
+
1076
+ #==============================================================================
1077
+
907
1078
  #==============================================================================
908
1079
  #==============================================================================
909
1080
  #==============================================================================
@@ -699,17 +699,22 @@ if __name__=='__main__':
699
699
  ticker=["600519.SS","000858.SZ"]
700
700
  ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
701
701
 
702
- start="2024-1-1"
703
- end="2024-3-15"
704
- rar='sharpe'
705
- ret_type="Monthly Ret%"
706
- RF=0.01759
707
- regression_period=365
702
+ ticker=['601628.SS','601319.SS','601318.SS','00966.HK']
708
703
 
709
- graph=True; axhline_value=0; axhline_label=''
710
- printout=False; sortby='tpw_mean'; trailing=5; trend_threshhold=0.01
711
- annotate=False
704
+ start="2023-6-27"
705
+ end="2024-6-27"
706
+ rar='sharpe'
707
+ RF=0.01692
708
+ printout=True
709
+
710
+ ret_type="Annual Ret%"; regression_period=365
711
+ graph=True; loc1='best'
712
+ axhline_value=0; axhline_label=''
713
+ sortby='tpw_mean'; trailing=7; trend_threshhold=0.01
714
+ annotate=False; annotate_value=False
715
+ mark_top=False; mark_bottom=False; mark_end=False
712
716
  mktidx='auto'; source='auto'
717
+ style_print=True; ticker_type='auto';facecolor='whitesmoke'
713
718
 
714
719
  rars=compare_mticker_1rar(ticker=["600519.SS","000858.SZ"],start="2024-1-1",end="2024-6-16",rar='sharpe',printout=True)
715
720
 
@@ -719,7 +724,7 @@ def compare_mticker_1rar(ticker,start,end,rar='sharpe', \
719
724
  axhline_value=0,axhline_label='', \
720
725
  printout=False,sortby='tpw_mean',trailing=7,trend_threshhold=0.01, \
721
726
  annotate=False,annotate_value=False, \
722
- mark_top=False,mark_bottom=False,mark_end=False, \
727
+ mark_top=False,mark_bottom=False,mark_end=False, \
723
728
  mktidx='auto',source='auto', \
724
729
  style_print=True,ticker_type='auto',facecolor='whitesmoke'):
725
730
  """
@@ -781,6 +786,10 @@ def compare_mticker_1rar(ticker,start,end,rar='sharpe', \
781
786
 
782
787
  #仅用于绘图和制表
783
788
  df1=df.copy()
789
+ #进行空缺值填充,以便绘图连续
790
+ df1.fillna(method='bfill',inplace=True)
791
+ df1.fillna(method='ffill',inplace=True)
792
+
784
793
  for c in list(df1):
785
794
  if df1[c].max() > axhline_value and df1[c].min() < axhline_value:
786
795
  axhline_label='零线' #显示零线,但不标注图例
@@ -817,7 +826,7 @@ def compare_mticker_1rar(ticker,start,end,rar='sharpe', \
817
826
  facecolor=facecolor,loc=loc1)
818
827
 
819
828
  #制表
820
- recommenddf=pd.DataFrame()
829
+ #recommenddf=pd.DataFrame()
821
830
  if printout:
822
831
  if sortby=='tpw_mean':
823
832
  sortby_txt='按推荐标记+近期优先加权平均值降序排列'
@@ -837,8 +846,8 @@ def compare_mticker_1rar(ticker,start,end,rar='sharpe', \
837
846
  footnote7="近期优先趋势和星号为风险调整收益指标加趋势等多项因素综合研判,最多五颗星"
838
847
  footnotey=footnote6+'\n'+footnote7+'\n'+footnotex
839
848
 
840
- #删除含有Nan的行,否则可能引起近期优先加权平均计算结果市场出现Nan
841
- df1.dropna(inplace=True,axis=1)
849
+ #不能简单删除含有Nan的行,否则导致清空df1,应该进行填充
850
+ #df1.dropna(inplace=True,axis=1)
842
851
  recommenddf=descriptive_statistics2(df1,title_txt,footnotey,decimals=4, \
843
852
  sortby=sortby,recommend_only=True,trailing=trailing, \
844
853
  trend_threshhold=trend_threshhold, \