siat 2.13.43__py3-none-any.whl → 2.14.1__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/common.py CHANGED
@@ -284,6 +284,12 @@ def calculate_days(start_date, end_date):
284
284
 
285
285
 
286
286
  #==============================================================================
287
+ if __name__ =="__main__":
288
+ basedate='2020-3-17'
289
+ adjust=-365
290
+ newdate = date_adjust(basedate, adjust)
291
+ print(newdate)
292
+
287
293
  def date_adjust(basedate, adjust=0):
288
294
  """
289
295
  功能:将给定日期向前或向后调整特定的天数
@@ -309,11 +315,105 @@ def date_adjust(basedate, adjust=0):
309
315
  return str(newdate)
310
316
 
311
317
  if __name__ =="__main__":
312
- basedate='2020-3-17'
313
- adjust=-365
314
- newdate = date_adjust(basedate, adjust)
315
- print(newdate)
318
+ basedate='2024-3-17'
319
+
320
+ adjust_year=-1; adjust_month=-1; adjust_day=-1
321
+ adjust_year=-1; adjust_month=-1; adjust_day=-17
322
+ adjust_year=-1; adjust_month=0; adjust_day=-17
323
+ adjust_year=0; adjust_month=-3; adjust_day=0
324
+ adjust_year=0; adjust_month=-13; adjust_day=0
325
+ adjust_year=0; adjust_month=-15; adjust_day=0
326
+ adjust_year=0; adjust_month=-27; adjust_day=0
327
+ adjust_year=0; adjust_month=-27; adjust_day=0
328
+
329
+ adjust_year=0; adjust_month=0; adjust_day=15
330
+ adjust_year=0; adjust_month=10; adjust_day=0
331
+ adjust_year=0; adjust_month=22; adjust_day=0
332
+
333
+ adjust_year=0; adjust_month=-1; adjust_day=-1
334
+ adjust_year=0; adjust_month=-2; adjust_day=-1
335
+ adjust_year=0; adjust_month=-3; adjust_day=-1
336
+ adjust_year=0; adjust_month=-6; adjust_day=-1
337
+ adjust_year=-1; adjust_month=0; adjust_day=0
338
+ adjust_year=-2; adjust_month=0; adjust_day=0
339
+ adjust_year=-3; adjust_month=0; adjust_day=0
340
+ adjust_year=-5; adjust_month=0; adjust_day=0
341
+
342
+ to_prev_month_end=False
343
+ to_prev_month_end=True
344
+
345
+ to_prev_year_end=False
346
+ to_prev_year_end=True
347
+
348
+ date_adjust2(basedate,adjust_year,adjust_month,adjust_day,to_prev_month_end,to_prev_year_end)
349
+
350
+ def date_adjust2(basedate,adjust_year=0,adjust_month=0,adjust_day=0, \
351
+ to_prev_month_end=False,to_prev_year_end=False):
352
+ """
353
+ 功能:将给定日期向前或向后调整特定的天数,按照年月日精确调整
354
+ 输入:基础日期,需要调整的天数。
355
+ basedate: 基础日期。
356
+ adjust_year, adjust_month, adjust_day:分别调整的年月日数量,负数向前调整,正数向后调整。
357
+ 输出:调整后的日期字符串。
358
+ """
359
+ import pandas as pd
360
+ from datetime import datetime, timedelta
361
+
362
+ #检查基础日期的合理性
363
+ try:
364
+ bd=pd.to_datetime(basedate)
365
+ except:
366
+ print(" #Error(date_adjust2): invalid date",basedate)
367
+ return None
316
368
 
369
+ #将基础日期分解为年月日
370
+ bd_year=bd.year
371
+ bd_month=bd.month
372
+ bd_day=bd.day
373
+
374
+ #预处理调整的月数
375
+ if adjust_month <= -12:
376
+ adjust_year=adjust_year + int(adjust_month / 12)
377
+ adjust_month=adjust_month % -12
378
+ if adjust_month >= 12:
379
+ adjust_year=adjust_year + int(adjust_month / 12)
380
+ adjust_month=adjust_month % 12
381
+
382
+ #调整年
383
+ new_year=bd_year + adjust_year
384
+
385
+ #调整月份
386
+ new_month=bd_month + adjust_month
387
+ if new_month <= 0:
388
+ new_month=new_month + 12
389
+ new_year=new_year - 1
390
+ if new_month > 12:
391
+ new_month=new_month % 12
392
+ new_year=new_year + 1
393
+
394
+ #合成中间日期:新年份,新月份,原日期
395
+ ndym=datetime(new_year,new_month,bd_day)
396
+
397
+ #调整日期
398
+ nd=ndym + timedelta(days=adjust_day)
399
+
400
+ #如果还需要调整到上年年末,但不可同时使用调整到上月月末
401
+ if to_prev_year_end:
402
+ to_prev_month_end=False
403
+
404
+ nd_year=nd.year - 1
405
+ nd=datetime(nd_year,12,31)
406
+
407
+ #如果还需要调整到上月月末
408
+ if to_prev_month_end:
409
+ nd_day=nd.day
410
+ nd=nd + timedelta(days=-nd_day)
411
+
412
+ #提取日期字符串
413
+ newdate=nd.date()
414
+
415
+ return str(newdate)
416
+
317
417
  #==============================================================================
318
418
  if __name__ =="__main__":
319
419
  portfolio={'Market':('US','^GSPC'),'EDU':0.4,'TAL':0.3,'TEDU':0.2}
@@ -357,13 +457,35 @@ def portfolio_name(portfolio):
357
457
  try:
358
458
  name=portfolio[keylist[0]][2]
359
459
  except:
360
- name="投资组合"
460
+ name="PF1"
361
461
 
362
462
  return name
363
463
 
364
464
  if __name__=='__main__':
365
465
  portfolio={'Market':('US','^GSPC','我的组合001'),'EDU':0.4,'TAL':0.3,'TEDU':0.2}
366
466
  portfolio_name(portfolio)
467
+
468
+ if __name__=='__main__':
469
+ portfolio={'Market':('US','^GSPC','China Edtraining'),'EDU':0.4,'TAL':0.3,'TEDU':0.2}
470
+ portfolio='600519.SS'
471
+
472
+ isinstance_portfolio(portfolio)
473
+
474
+ def isinstance_portfolio(portfolio):
475
+ """
476
+ 功能:判断是否投资组合
477
+ 输入:投资组合
478
+ 输出:True, False
479
+ 注意:为了维持兼容性,特此定义此函数
480
+ """
481
+ result=True
482
+
483
+ try:
484
+ scope,mktidx,tickerlist,sharelist=decompose_portfolio(portfolio)
485
+ except:
486
+ result=False
487
+
488
+ return result
367
489
 
368
490
  #==============================================================================
369
491
  def calc_monthly_date_range(start,end):
@@ -1744,7 +1866,7 @@ if __name__=='__main__':
1744
1866
  footnote="This is the footnote"
1745
1867
 
1746
1868
  def descriptive_statistics(df,titletxt,footnote,decimals=4,sortby='tpw_mean', \
1747
- recommend_only=False,trailing=20,trend_threshhold=0.01):
1869
+ recommend_only=False,trailing=7,trend_threshhold=0.01):
1748
1870
  """
1749
1871
  功能:进行描述性统计,并打印结果
1750
1872
  df的要求:
@@ -1764,7 +1886,8 @@ def descriptive_statistics(df,titletxt,footnote,decimals=4,sortby='tpw_mean', \
1764
1886
  return
1765
1887
 
1766
1888
  # 计算短期趋势
1767
- df20=df[-trailing:]
1889
+ #df20=df[-trailing:]
1890
+ df20=df.tail(trailing)
1768
1891
  """
1769
1892
  df20ds=df20.describe()
1770
1893
  df20mean=df20ds[df20ds.index=='mean'].T
@@ -1993,6 +2116,259 @@ def descriptive_statistics(df,titletxt,footnote,decimals=4,sortby='tpw_mean', \
1993
2116
 
1994
2117
  return dst5
1995
2118
 
2119
+ #==============================================================================
2120
+
2121
+ if __name__=='__main__':
2122
+ tickers=['NIO','LI','XPEV','TSLA']
2123
+ df=compare_mrar(tickers,
2124
+ rar_name='sharpe',
2125
+ start='2022-1-1',end='2023-1-31',
2126
+ market='US',market_index='^GSPC',
2127
+ window=240,axhline_label='零线')
2128
+
2129
+ titletxt="This is the title text"
2130
+ footnote="This is the footnote"
2131
+ decimals=4
2132
+ sortby='tpw_mean'
2133
+ recommend_only=False
2134
+ trailing=7
2135
+ trend_threshhold=0.01
2136
+
2137
+ def descriptive_statistics2(df,titletxt,footnote,decimals=4,sortby='tpw_mean', \
2138
+ recommend_only=False,trailing=7,trend_threshhold=0.01, \
2139
+ printout=True,style_print=False):
2140
+ """
2141
+ 功能:进行描述性统计,并打印结果
2142
+ df的要求:
2143
+ 索引列为datetime格式,不带时区
2144
+ 各个列为比较对象,均为数值型,降序排列
2145
+
2146
+ sortby='tpw_mean':按照近期时间优先加权(time priority weighted)平均数排序
2147
+ recommend_only=False:是否仅打印推荐的证券
2148
+ """
2149
+
2150
+ # 检查df
2151
+ if df is None:
2152
+ print(" #Error(descriptive_statistics2): none info found")
2153
+ return
2154
+ if len(df) == 0:
2155
+ print(" #Error(descriptive_statistics2): zero data found")
2156
+ return
2157
+
2158
+ # 计算短期趋势
2159
+ df20=df.tail(trailing)
2160
+
2161
+ ds=df.describe(include='all',percentiles=[.5])
2162
+ dst=ds.T
2163
+ cols=['min','max','50%','mean','std']
2164
+
2165
+ dst['item']=dst.index
2166
+ cols2=['item','min','max','50%','mean','std']
2167
+
2168
+ dst2=dst[cols2]
2169
+ for c in cols:
2170
+ dst2[c]=dst2[c].apply(lambda x: round(x,decimals))
2171
+
2172
+ if sortby != 'tpw_mean':
2173
+ if sortby=='median':
2174
+ sortby='50%'
2175
+ dst2.sort_values(by=sortby,ascending=False,inplace=True)
2176
+
2177
+ cols2cn=['比较对象','最小值','最大值','中位数','平均值','标准差']
2178
+ dst2.columns=cols2cn
2179
+
2180
+ # 近期优先加权平均
2181
+ dst2['近期优先加权平均']=dst2['比较对象'].apply(lambda x:time_priority_weighted_average(df,x,4))
2182
+ if sortby == "tpw_mean":
2183
+ dst2.sort_values(by='近期优先加权平均',ascending=False,inplace=True)
2184
+
2185
+ dst3=dst2
2186
+ dst3=dst3[(dst3['比较对象'] != 'time_weight') & (dst3['比较对象'] != 'relative_weight')]
2187
+
2188
+ dst3.reset_index(drop=True,inplace=True)
2189
+ dst3.index=dst3.index+1
2190
+
2191
+ # 趋势标记
2192
+ dst3['期间趋势']=dst3['比较对象'].apply(lambda x:curve_trend_direct(df,x,trend_threshhold))
2193
+ dst3['近期趋势']=dst3['比较对象'].apply(lambda x:curve_trend_direct(df20,x,trend_threshhold))
2194
+
2195
+ # 推荐标记:最大五颗星
2196
+ dst3['推荐标记']=''
2197
+ if sortby in ['tpw_mean','trailing']: #重视近期趋势
2198
+ #注意:务必先加后减!
2199
+ #若近期优先加权平均>0,给3星
2200
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+++') if (x['近期优先加权平均']>0) else x['推荐标记'],axis=1)
2201
+ #若近期趋势➹,加2星
2202
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'++') if (x['近期趋势']=='➹') else x['推荐标记'],axis=1)
2203
+ #若期间趋势➹,加1星
2204
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['期间趋势']=='➹') else x['推荐标记'],axis=1)
2205
+ #若平均值或中位数>0,加1星
2206
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['平均值']>0) | (x['中位数']>0) else x['推荐标记'],axis=1)
2207
+ #若最小值>0,加1星
2208
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['最小值']>0) else x['推荐标记'],axis=1)
2209
+
2210
+ #若近期趋势➷,减2星
2211
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['近期趋势']=='➷') else x['推荐标记'],axis=1)
2212
+ #若期间趋势➷,减1星
2213
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['期间趋势']=='➷') else x['推荐标记'],axis=1)
2214
+ #若平均值且中位数<0,减1星
2215
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['平均值']<0) & (x['中位数']<0) else x['推荐标记'],axis=1)
2216
+ #若最小值<0,减1星
2217
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['最小值']<0) else x['推荐标记'],axis=1)
2218
+
2219
+ #若近期优先加权平均<0,星星清零
2220
+ dst3['推荐标记']=dst3.apply(lambda x: '' if (x['近期优先加权平均']<0) else x['推荐标记'],axis=1)
2221
+
2222
+ elif sortby == 'min': #保守推荐
2223
+ #若最小值>0,加5星
2224
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+++++') if (x['最小值']>0) else x['推荐标记'],axis=1)
2225
+ #若近期优先加权平均>0,加1星
2226
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['近期优先加权平均']>0) else x['推荐标记'],axis=1)
2227
+ #若近期趋势➹,加1星
2228
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['近期趋势']=='➹') else x['推荐标记'],axis=1)
2229
+ #若期间趋势➹,加1星
2230
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['期间趋势']=='➹') else x['推荐标记'],axis=1)
2231
+ #若平均值或中位数>0,加1星
2232
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['平均值']>0) | (x['中位数']>0) else x['推荐标记'],axis=1)
2233
+
2234
+ #若近期优先加权平均<0,减1星
2235
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['近期优先加权平均']<0) else x['推荐标记'],axis=1)
2236
+ #若平均值且中位数<0,减1星
2237
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['平均值']<0) & (x['中位数']<0) else x['推荐标记'],axis=1)
2238
+ #若近期趋势➷,减1星
2239
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['近期趋势']=='➷') else x['推荐标记'],axis=1)
2240
+ #若期间趋势➷,减1星
2241
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['期间趋势']=='➷') else x['推荐标记'],axis=1)
2242
+
2243
+ elif sortby == 'mean': #进取推荐,均值,重视整体趋势,不在乎近期趋势
2244
+ #若平均值>0,给3星
2245
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+++') if (x['平均值']>0) else x['推荐标记'],axis=1)
2246
+ #若期间趋势➹,加2星
2247
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'++') if (x['期间趋势']=='➹') else x['推荐标记'],axis=1)
2248
+ #若中位数>0,加1星
2249
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['中位数']>0) else x['推荐标记'],axis=1)
2250
+ #若近期趋势➹,加1星
2251
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['近期趋势']=='➹') else x['推荐标记'],axis=1)
2252
+ #若近期优先加权平均>0,加1星
2253
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['近期优先加权平均']>0) else x['推荐标记'],axis=1)
2254
+ #若最小值>0,加1星
2255
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['最小值']>0) else x['推荐标记'],axis=1)
2256
+
2257
+ #若期间趋势➷,减2星
2258
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['期间趋势']=='➷') else x['推荐标记'],axis=1)
2259
+ #若近期趋势➷,减1星
2260
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['近期趋势']=='➷') else x['推荐标记'],axis=1)
2261
+ #若近期优先加权平均<0,减1星
2262
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['近期优先加权平均']<0) else x['推荐标记'],axis=1)
2263
+ #若中位数<0,减1星
2264
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['中位数']<0) else x['推荐标记'],axis=1)
2265
+ #若最小值<0,减1星
2266
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['最小值']<0) else x['推荐标记'],axis=1)
2267
+
2268
+ #若平均值<0,星星清零
2269
+ dst3['推荐标记']=dst3.apply(lambda x: '' if (x['平均值']<0) else x['推荐标记'],axis=1)
2270
+
2271
+ elif sortby == 'median': #进取推荐,中位数,看重整体趋势,不在乎近期短期变化
2272
+ #若中位数>0,给3星
2273
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+++') if (x['中位数']>0) else x['推荐标记'],axis=1)
2274
+ #若期间趋势➹,加2星
2275
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'++') if (x['期间趋势']=='➹') else x['推荐标记'],axis=1)
2276
+ #若平均值>0,加1星
2277
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['平均值']>0) else x['推荐标记'],axis=1)
2278
+
2279
+ #若近期趋势➹,加1星
2280
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['近期趋势']=='➹') else x['推荐标记'],axis=1)
2281
+ #若近期优先加权平均>0,加1星
2282
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['近期优先加权平均']>0) else x['推荐标记'],axis=1)
2283
+ #若最小值>0,加1星
2284
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'+') if (x['最小值']>0) else x['推荐标记'],axis=1)
2285
+
2286
+ #若期间趋势➷,减2星
2287
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['期间趋势']=='➷') else x['推荐标记'],axis=1)
2288
+ #若近期趋势➷,减1星
2289
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['近期趋势']=='➷') else x['推荐标记'],axis=1)
2290
+ #若近期优先加权平均<0,减1星
2291
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['近期优先加权平均']<0) else x['推荐标记'],axis=1)
2292
+ #若平均值<0,减1星
2293
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['平均值']<0) else x['推荐标记'],axis=1)
2294
+ #若最小值<0,减1星
2295
+ dst3['推荐标记']=dst3.apply(lambda x: change_stars2(x['推荐标记'],'-') if (x['最小值']<0) else x['推荐标记'],axis=1)
2296
+
2297
+ #若中位数<0,星星清零
2298
+ dst3['推荐标记']=dst3.apply(lambda x: '' if (x['中位数']<0) else x['推荐标记'],axis=1)
2299
+ else:
2300
+ pass
2301
+
2302
+ #最多5颗星星
2303
+ dst3['推荐标记']=dst3.apply(lambda x: x['推荐标记'][:5] if (hzlen(x['推荐标记'])>5) else x['推荐标记'],axis=1)
2304
+ #为了打印对齐,强制向左移动,不管用!
2305
+ #dst3['推荐标记']=dst3.apply(lambda x: x['推荐标记']+' ' if (hzlen(x['推荐标记'])>4) else x['推荐标记'],axis=1)
2306
+
2307
+ dst4=dst3
2308
+
2309
+ # 重排序:按照星星个数+数值,降序
2310
+ dst5=dst4
2311
+ if sortby == "tpw_mean":
2312
+ dst5.sort_values(by=['推荐标记','近期优先加权平均'],ascending=[False,False],inplace=True)
2313
+ #dst5.sort_values(by=['推荐标记','近期优先加权平均'],ascending=False,inplace=True)
2314
+ elif sortby == "min":
2315
+ dst5.sort_values(by=['推荐标记','最小值'],ascending=[False,False],inplace=True)
2316
+ elif sortby == "mean":
2317
+ dst5.sort_values(by=['推荐标记','平均值'],ascending=[False,False],inplace=True)
2318
+ elif sortby == "median":
2319
+ dst5.sort_values(by=['推荐标记','中位数'],ascending=[False,False],inplace=True)
2320
+ elif sortby == "trailing":
2321
+ dst5.sort_values(by=['推荐标记','最新均值差'],ascending=[False,False],inplace=True)
2322
+ else:
2323
+ pass
2324
+
2325
+ #是否过滤无推荐标志的证券,防止过多无推荐标志的记录使得打印列表过长
2326
+ if recommend_only:
2327
+ dst6=dst5[dst5['推荐标记'] != '']
2328
+ dst_num=len(dst6)
2329
+ #若无推荐标志也要显示头十个
2330
+ if dst_num < 10:
2331
+ dst6=dst5.head(10)
2332
+ else:
2333
+ dst6=dst5.head(dst_num+3)
2334
+ else:
2335
+ dst6=dst5
2336
+
2337
+ dst6.reset_index(drop=True,inplace=True)
2338
+ dst6.index=dst6.index+1
2339
+
2340
+ if printout:
2341
+ #控制显示的小数点位数
2342
+ for c in dst6.columns:
2343
+ try:
2344
+ dst6[c]=dst6[c].apply(lambda x: round(x,4))
2345
+ except:
2346
+ pass
2347
+ #确保display显示时不再自动在数值尾部添加零至6位小数
2348
+ dst6[c]=dst6[c].apply(lambda x: str(x))
2349
+
2350
+ if not style_print: #markdown打印
2351
+ print("\n"+titletxt+"\n")
2352
+ #如果index=True则显示index,这样alignlist的长度就需要dst6列数+1
2353
+ alignlist=['right','left']+['center']*(len(list(dst6))-3)+['center','left']
2354
+ try:
2355
+ print(dst6.to_markdown(index=True,tablefmt='plain',colalign=alignlist))
2356
+ except:
2357
+ #解决汉字编码gbk出错问题
2358
+ dst7=dst6.to_markdown(index=True,tablefmt='plain',colalign=alignlist)
2359
+ dst8=dst7.encode("utf-8",errors="strict")
2360
+ print(dst8)
2361
+ print("\n"+footnote)
2362
+
2363
+ else: #style打印
2364
+ print("\n"+titletxt)
2365
+ dst6sd= dst6.style.set_properties(**{'text-align': 'center'})
2366
+ from IPython.display import display
2367
+ display(dst6sd)
2368
+ print(footnote+"\n")
2369
+
2370
+ return dst5
2371
+
1996
2372
 
1997
2373
  #==============================================================================
1998
2374
  if __name__=='__main__':
@@ -2010,6 +2386,31 @@ def print_list(alist,leading_blanks=1):
2010
2386
  print('\n')
2011
2387
 
2012
2388
  return
2389
+
2390
+ if __name__=='__main__':
2391
+ alist=['NIO','LI','XPEV','TSLA']
2392
+ list2str(alist)
2393
+
2394
+ def list2str(alist):
2395
+ """
2396
+ 功能:将列表转换为字符串,不带引号,节省空间
2397
+ """
2398
+ if len(alist) > 1:
2399
+ result='['
2400
+ for i in alist:
2401
+ result=result+str(i)
2402
+ if i != alist[-1]:
2403
+ result=result+', '
2404
+ result=result+']'
2405
+
2406
+ elif len(alist) == 1:
2407
+ result=str(alist[0])
2408
+
2409
+ else:
2410
+ result=''
2411
+
2412
+ return result
2413
+
2013
2414
  #==============================================================================
2014
2415
  # FUNCTION TO REMOVE TIMEZONE
2015
2416
  def remove_timezone(dt):
@@ -2144,18 +2545,18 @@ def curve_trend_regress(df,col,threshhold=0.0001):
2144
2545
 
2145
2546
  return result
2146
2547
 
2147
- def curve_trend_direct(df,col,threshhold=0.0001):
2548
+ def curve_trend_direct(df,col,threshhold=0.01):
2148
2549
  """
2149
2550
  功能:直接对比首尾值大小。目的为判断曲线走势
2150
2551
 
2151
2552
  输入项:
2152
2553
  df: 数据框,假设其索引为日期项,且已升序排列
2153
2554
  col: 考察变量,检查该变量的走势,向上,向下,or 不明显(无显著性星星)
2154
-
2155
- 返回值:尾值-首值
2156
- '➠':差接近零(其绝对值小于threshhold)
2157
- '➷':差为负数且其绝对值不小于threshhold
2158
- '➹':差为正数且其绝对值不小于threshhold
2555
+ threshhold:相对值
2556
+ 返回值:(尾值-首值)/首值
2557
+ '➠':变化率接近零(其绝对值小于threshhold)
2558
+ '➷':变化率为负数且其绝对值不小于threshhold
2559
+ '➹':变化率为正数且其绝对值不小于threshhold
2159
2560
  """
2160
2561
  # 检查df是否为空
2161
2562
  if df is None:
@@ -2168,15 +2569,32 @@ def curve_trend_direct(df,col,threshhold=0.0001):
2168
2569
  df1.sort_index(ascending=True,inplace=True)
2169
2570
  first_value=df1.head(1)[col].values[0]
2170
2571
  last_value=df1.tail(1)[col].values[0]
2171
- diff=last_value - first_value
2572
+
2573
+ #采用相对值,避免数量级差异,同时避免负负得正
2574
+ if first_value != 0.0:
2575
+ diff=(last_value - first_value)/abs(first_value)
2576
+ elif last_value != 0.0:
2577
+ #不得已
2578
+ diff=(last_value - first_value)/abs(last_value)
2579
+ else:
2580
+ #实在不得已
2581
+ diff=last_value - first_value
2582
+
2172
2583
  diff_abs=abs(diff)
2173
2584
 
2174
2585
  # 判断斜率方向
2175
2586
  result='➠'
2587
+ """
2176
2588
  if diff_abs >= threshhold and diff > 0:
2177
2589
  result='➹'
2178
2590
  if diff_abs >= threshhold and diff < 0:
2179
2591
  result='➷'
2592
+ """
2593
+ #留出区间-threshhold至threshhold视为平行趋势
2594
+ if diff > threshhold:
2595
+ result='➹'
2596
+ if diff < -threshhold:
2597
+ result='➷'
2180
2598
 
2181
2599
  return result
2182
2600
  #==============================================================================
@@ -2244,8 +2662,40 @@ def change_recommend_stars(stars_current,change='+'):
2244
2662
  stars_new=stars2
2245
2663
 
2246
2664
  return stars_new
2665
+
2666
+ #==============================================================================
2667
+ if __name__=='__main__':
2668
+ stars_current=''
2669
+ stars_current='✮✮'
2247
2670
 
2671
+ change='+'
2672
+ change='++'
2673
+ change='-'
2674
+
2675
+ change_stars2(stars_current,change)
2676
+
2677
+ def change_stars2(stars_current,change='+'):
2678
+ """
2679
+ 功能:增减推荐的星星个数
2680
+ 注意:change中不能同时出现+-符号,只能出现一种,但可以多个
2681
+ """
2682
+ stars1='✮'
2683
+
2684
+ num_plus=change.count('+')
2685
+ if num_plus > 0:
2686
+ stars_new=stars_current+stars1 * num_plus
2248
2687
 
2688
+ num_minus=change.count('-')
2689
+ if num_minus >= 1:
2690
+ stars_new=stars_current
2691
+ for n in range(1,num_minus+1):
2692
+ stars_new=stars_new[:-1]
2693
+ if hzlen(stars_new)==0: break
2694
+ """
2695
+ if hzlen(stars_new)>5:
2696
+ stars_new=stars_new[:5]
2697
+ """
2698
+ return stars_new
2249
2699
 
2250
2700
  #==============================================================================
2251
2701
  if __name__=='__main__':
@@ -2320,6 +2770,12 @@ def fix_package(file='stooq.py',package='pandas_datareader'):
2320
2770
  """
2321
2771
  功能:修复stooq.py,使用siat包中的stooq.py覆盖pandas_datareader中的同名文件
2322
2772
  注意:执行本程序需要系统管理员权限,可以系统管理员权限启动Jupyter或Spyder
2773
+
2774
+ 改进:建立一个Excel文件,记录需要修复的文件和包,例如:
2775
+ file package
2776
+ stooq.py pandas_datareader
2777
+ bond_zh_sina.py akshare
2778
+
2323
2779
  """
2324
2780
  #判断操作系统
2325
2781
  import sys; czxt=sys.platform
@@ -2420,7 +2876,7 @@ def df_preprocess(dfs,measure,axhline_label,x_label,y_label,lang='Chinese', \
2420
2876
  #相对起点值变化的百分数(起点值为0)
2421
2877
  scalinglist=['mean','min','start','percentage','change%']
2422
2878
  if not (scaling_option in scalinglist):
2423
- print(" #Error(compare_msecurity): invalid scaling option",scaling_option)
2879
+ print(" #Error(df_preprocess): invalid scaling option",scaling_option)
2424
2880
  print(" Valid scaling option:",scalinglist)
2425
2881
  return None
2426
2882
  if scaling_option == 'mean':
@@ -2536,7 +2992,8 @@ def df_preprocess(dfs,measure,axhline_label,x_label,y_label,lang='Chinese', \
2536
2992
  measure_suffix='(相对百分数%)'
2537
2993
  elif scaling_option == 'change%':
2538
2994
  std_notes="注释:为突出变化趋势,图中数值为相对期间起点的增减百分比"
2539
- measure_suffix='(增/减%)'
2995
+ #measure_suffix='(增/减%)'
2996
+ measure_suffix='(涨/跌%)'
2540
2997
  axhline_label='零线' #可以在security_trend中使用critical_value选项指定水平线位置,默认0
2541
2998
  #axhline_value=0
2542
2999
 
@@ -2550,4 +3007,266 @@ def df_preprocess(dfs,measure,axhline_label,x_label,y_label,lang='Chinese', \
2550
3007
  #返回内容
2551
3008
  return dfs2,axhline_label,x_label,y_label,plus_sign
2552
3009
  #==============================================================================
3010
+ if __name__=='__main__':
3011
+ is_running_in_jupyter()
3012
+
3013
+
3014
+ def is_running_in_jupyter():
3015
+ """
3016
+ 功能:检测当前环境是否在Jupyter中,误判Spyder为Jupyter,不行!
3017
+ """
3018
+ try:
3019
+ # 尝试导入IPython的一些模块
3020
+ from IPython import get_ipython
3021
+
3022
+ ipython = get_ipython()
3023
+ if 'IPKernelApp' not in ipython.config:
3024
+ return False
3025
+ return True
3026
+
3027
+ except ImportError:
3028
+ return False
3029
+
3030
+ #==============================================================================
3031
+ if __name__=='__main__':
3032
+ date1=pd.to_datetime('2024-1-2')
3033
+ date2=pd.to_datetime('2024-1-9')
3034
+ days_between_dates(date1, date2)
3035
+
3036
+ def days_between_dates(date1, date2):
3037
+ """
3038
+ 注意:date1和date2为datetime类型
3039
+ """
3040
+ from datetime import datetime
3041
+ delta = date2 - date1
3042
+ return delta.days
3043
+
3044
+ #==============================================================================
3045
+ if __name__=='__main__':
3046
+ stars_num=0
3047
+ stars_num=3
3048
+ stars_num=4.2
3049
+ stars_num=4.6
3050
+
3051
+ generate_stars(stars_num)
3052
+
3053
+ def generate_stars(stars_num):
3054
+ """
3055
+ 功能:基于星星个数stars_num生成推荐星星符号,支持半颗星
3056
+ """
3057
+ stars1='✮'
3058
+ starsh='☆'
3059
+
3060
+ stars_int=int(stars_num)
3061
+ result=stars_int * stars1
3062
+
3063
+ if (stars_num - stars_int) >= 0.5:
3064
+ result=result + starsh
3065
+
3066
+ return result
3067
+
3068
+
3069
+ #==============================================================================
3070
+ if __name__=='__main__':
3071
+ df=get_price('000001.SS','2000-1-1','2024-3-22')
3072
+ column='Close'
3073
+ minimum=30
3074
+ method='max'
3075
+
3076
+ df1=df[column].resample('4H').max()
3077
+ df2=df1.interpolate(method='cubic')
3078
+
3079
+ period='24H'
3080
+ df2=df_resampling(df,column,period,method='mean',minimum=minimum)
3081
+ df2[column].plot(title=period)
3082
+
3083
+
3084
+ period='auto'; method='mean'; minimum=50
3085
+ df2=df_resampling(df,column,period,method='mean',minimum=minimum)
3086
+ df2[column].plot()
3087
+
3088
+
3089
+ def df_resampling(df,column,period='auto',method='max',minimum=30):
3090
+ """
3091
+ 注意:未完成状态。遇到的问题:平滑后数据的后面时间段丢失严重!
3092
+ 目的:将大量时间序列数据绘制重新采样,减少极端值,使得数据趋势简洁明了,可能丢失部分细节
3093
+ 功能:将df按照period对column字段数值重新采样,采样方法为method,采样前个数不少于minimum
3094
+ 注意:df需为时间序列;column为单个字段,需为数值型
3095
+ period:采样间隔,支持小时'H'或'nH'(n=2,3,...)、周'W'、月'M'、季'Q'或年'Y',默认由系统决定
3096
+ method:暂仅支持平均值方法mean(典型)和求和方法sum
3097
+ minimum:采样后需保留的最少数据个数,默认30个
3098
+ """
3099
+ DEBUG=True
3100
+
3101
+ import pandas as pd
3102
+ import numpy as np
3103
+
3104
+ #检查df长度:是否需要重新采样
3105
+ if len(df) <= minimum:
3106
+ if DEBUG: print(" #Notice(df_resampling): no need to resample",len(df))
3107
+ return df #无需重新采样,返回原值
3108
+
3109
+ #仅对采样字段进行处理
3110
+ if column not in df.columns:
3111
+ if DEBUG: print(" #Warning(df_resampling): non-exist column",column)
3112
+ return df #返回原值
3113
+
3114
+ #取出采样字段
3115
+ df1=df[[column]].copy() #避免影响到原值
3116
+
3117
+ #寻求最佳采样间隔
3118
+ period_list=['2H','4H','6H','8H','12H','24H']
3119
+ std_list=[]
3120
+ dft_list=[]
3121
+ for p in period_list:
3122
+ dft1=df1[column].resample(p).mean()
3123
+ dft2=dft1.interpolate(method='linear')
3124
+ stdt=dft2.std()
3125
+ std_list=std_list+[stdt]
3126
+ dft_list=dft_list+[dft2]
3127
+
3128
+ std_min=min(std_list)
3129
+ pos=std_list.index(std_min)
3130
+ period_min=period_list[pos]
3131
+ df2=dft_list[pos]
3132
+
3133
+
3134
+ #再次检查采样后的长度
3135
+ if len(df2) < minimum:
3136
+ if DEBUG: print(" #Warning(df_resampling): resampled number",len(df2),"< minimum",minimum)
3137
+ return df #返回原值
3138
+
3139
+ return df2
3140
+
3141
+ #==============================================================================
3142
+ if __name__=='__main__':
3143
+ df=get_price('000001.SS','2000-1-1','2024-3-22')
3144
+
3145
+
3146
+ def linewidth_adjust(df):
3147
+ """
3148
+ 功能:根据df中元素个数多少调节绘制曲线时线段的宽度linewidth
3149
+ """
3150
+
3151
+ dflen=len(df)
3152
+ if dflen > 100: lwadjust=1.2
3153
+ elif dflen > 200: lwadjust=1.0
3154
+ elif dflen > 300: lwadjust=0.8
3155
+ elif dflen > 500: lwadjust=0.4
3156
+ elif dflen > 1000: lwadjust=0.2
3157
+ elif dflen > 2000: lwadjust=0.1
3158
+ elif dflen > 3000: lwadjust=0.05
3159
+ elif dflen > 5000: lwadjust=0.01
3160
+ else: lwadjust=1.5
3161
+
3162
+ return lwadjust
3163
+
3164
+ #==============================================================================
3165
+ if __name__=='__main__':
3166
+ start='MRM'
3167
+ start='L3M'
3168
+ start='MRY'
3169
+ start='L30Y'
3170
+
3171
+ start='default'; end='default'
3172
+
3173
+ start='2024-1-1'; end='2023-1-1'
3174
+
3175
+ start_end_preprocess(start,end='today')
3176
+
3177
+ def start_end_preprocess(start,end='today'):
3178
+ """
3179
+ 功能:处理简约日期为具体日期,并检查日期的合理性
3180
+ """
3181
+
3182
+ # 检查日期:截至日期
3183
+ import datetime as dt;
3184
+ todaydt=dt.date.today();todaystr=todaydt.strftime('%Y-%m-%d')
3185
+
3186
+ end=end.lower()
3187
+ if end in ['default','today']:
3188
+ todate=todaystr
3189
+ else:
3190
+ validdate,todate=check_date2(end)
3191
+ if not validdate:
3192
+ print(" #Warning(start_end_preprocess): invalid date for",end)
3193
+ todate=todaystr
3194
+
3195
+ # 检查日期:开始日期
3196
+ start=start.lower()
3197
+
3198
+ """
3199
+ if start in ['default','mrm','l1m']: # 默认近一个月
3200
+ fromdate=date_adjust(todate,adjust=-31-7) #多几天有利于绘图坐标标示
3201
+ elif start in ['l2m']: # 近2个月
3202
+ fromdate=date_adjust(todate,adjust=-31*2-14)
3203
+ elif start in ['mrq','l3m']: # 近三个月
3204
+ fromdate=date_adjust(todate,adjust=-31*3-16)
3205
+ elif start in ['l6m','mrh']: # 近6个月
3206
+ fromdate=date_adjust(todate,adjust=-31*6-16)
3207
+ elif start in ['mry','l12m']: # 近一年
3208
+ fromdate=date_adjust(todate,adjust=-366-16)
3209
+ elif start in ['l2y']: # 近两年以来
3210
+ fromdate=date_adjust(todate,adjust=-366*2-15)
3211
+ elif start in ['l3y']: # 近三年以来
3212
+ fromdate=date_adjust(todate,adjust=-366*3-14)
3213
+ elif start in ['l5y']: # 近五年以来
3214
+ fromdate=date_adjust(todate,adjust=-366*5-13)
3215
+ elif start in ['l8y']: # 近八年以来
3216
+ fromdate=date_adjust(todate,adjust=-366*8-11)
3217
+ elif start in ['l10y']: # 近十年以来
3218
+ fromdate=date_adjust(todate,adjust=-366*10-9)
3219
+ elif start in ['l20y']: # 近20年以来
3220
+ fromdate=date_adjust(todate,adjust=-366*20-1)
3221
+ elif start in ['l30y']: # 近30年以来
3222
+ fromdate=date_adjust(todate,adjust=-366*30)
3223
+ elif start in ['ytd']: # 今年以来
3224
+ fromdate=str(todaydt.year-1)+'-12-1'
3225
+ else:
3226
+ validdate,fromdate=check_date2(start)
3227
+ if not validdate:
3228
+ print(" #Warning(security_trend): invalid date for",start,"/b, reset to MRM")
3229
+ fromdate=date_adjust(todate,adjust=-31-16)
3230
+ """
3231
+ if start in ['default','mrm','l1m']: # 默认近一个月
3232
+ fromdate=date_adjust2(todate,adjust_month=-1,adjust_day=-1) #有利于绘图横坐标日期标示
3233
+ elif start in ['l2m']: # 近2个月
3234
+ fromdate=date_adjust2(todate,adjust_month=-2,adjust_day=-1)
3235
+ elif start in ['mrq','l3m']: # 近三个月
3236
+ fromdate=date_adjust2(todate,adjust_month=-3,adjust_day=-1)
3237
+ elif start in ['l6m','mrh']: # 近6个月
3238
+ fromdate=date_adjust2(todate,adjust_month=-6,adjust_day=-1)
3239
+ elif start in ['mry','l12m']: # 近一年
3240
+ fromdate=date_adjust2(todate,adjust_year=-1,to_prev_month_end=True)
3241
+ elif start in ['l2y']: # 近两年以来
3242
+ fromdate=date_adjust2(todate,adjust_year=-2,to_prev_month_end=True)
3243
+ elif start in ['l3y']: # 近三年以来
3244
+ fromdate=date_adjust2(todate,adjust_year=-3,to_prev_month_end=True)
3245
+ elif start in ['l5y']: # 近五年以来
3246
+ fromdate=date_adjust2(todate,adjust_year=-5,to_prev_year_end=True)
3247
+ elif start in ['l8y']: # 近八年以来
3248
+ fromdate=date_adjust2(todate,adjust_year=-8,to_prev_year_end=True)
3249
+ elif start in ['l10y']: # 近十年以来
3250
+ fromdate=date_adjust2(todate,adjust_year=-10,to_prev_year_end=True)
3251
+ elif start in ['l20y']: # 近20年以来
3252
+ fromdate=date_adjust2(todate,adjust_year=-20,to_prev_year_end=True)
3253
+ elif start in ['l30y']: # 近30年以来
3254
+ fromdate=date_adjust2(todate,adjust_year=-30,to_prev_year_end=True)
3255
+ elif start in ['ytd']: # 今年以来
3256
+ fromdate=str(todaydt.year-1)+'-12-31'
3257
+ else:
3258
+ validdate,fromdate=check_date2(start)
3259
+ if not validdate:
3260
+ print(" #Warning(security_trend): invalid date",start)
3261
+ fromdate=date_adjust2(todate,adjust_month=-1,adjust_day=-1)
3262
+
3263
+ result,_,_=check_period(fromdate,todate)
3264
+ if not result:
3265
+ todate=todaystr
3266
+ print(" #Warning(start_end_preprocess): invalid date period between",fromdate,todate)
3267
+
3268
+ return fromdate,todate
3269
+
3270
+ #==============================================================================
3271
+ #==============================================================================
2553
3272