gitee.com/quant1x/engine@v1.8.4/trader/safes.go (about)

     1  package trader
     2  
     3  import (
     4  	"fmt"
     5  	"gitee.com/quant1x/engine/cache"
     6  	"gitee.com/quant1x/exchange"
     7  	"gitee.com/quant1x/gox/api"
     8  	"gitee.com/quant1x/gox/concurrent"
     9  	"gitee.com/quant1x/gox/coroutine"
    10  	"gitee.com/quant1x/gox/logger"
    11  	"os"
    12  	"path"
    13  	"slices"
    14  	"strings"
    15  	"sync"
    16  	"time"
    17  )
    18  
    19  const (
    20  	blacklistFilename = "safes.csv"
    21  )
    22  
    23  // SecureType 安全类型
    24  //
    25  //	白名单: Pure(纯净), Safe(安全), Trustworthy(值得信赖), Approved(已批准), Acceptable(可接受), Permitted(允许)
    26  //	黑名单: Forbidden(禁止), Unsafe(不安全), Untrustworthy(不值得信赖), Rejected(被拒绝), Unacceptable(不可接受), Prohibited(禁止)
    27  //	备选: SecureVariety, SecureType, SecureKind
    28  type SecureType uint8
    29  
    30  const (
    31  	FreeTrading        SecureType = iota // 自由交易
    32  	ProhibitForBuying                    // 禁止买入
    33  	ProhibitForSelling                   // 禁止卖出
    34  	ProhibitTrading    SecureType = 0xff // 禁止交易, 买入和卖出
    35  
    36  	//NotForSale         SecureType = 16   // 非卖品
    37  	//NotForBuy          SecureType = 86   // 非买品
    38  )
    39  
    40  var (
    41  	mapSecureTypes = map[SecureType]string{
    42  		FreeTrading:        "自由交易",
    43  		ProhibitForBuying:  "禁止买入",
    44  		ProhibitForSelling: "禁止卖出",
    45  		ProhibitTrading:    "禁止交易",
    46  	}
    47  )
    48  
    49  func UsageOfSecureType() string {
    50  	keys := api.Keys(mapSecureTypes)
    51  	slices.Sort(keys)
    52  	var builder strings.Builder
    53  	for _, typ := range keys {
    54  		desc, _ := mapSecureTypes[typ]
    55  		builder.WriteString(fmt.Sprintf("%d: %s\n", typ, desc))
    56  	}
    57  	return builder.String()
    58  }
    59  
    60  var (
    61  	onceSafes  coroutine.PeriodicOnce
    62  	mapSafes   = concurrent.NewTreeMap[string, SecureType]()
    63  	mutexSafes sync.RWMutex
    64  	//ctxSafes, _ = coroutine.GetContextWithCancel()
    65  )
    66  
    67  // BlackAndWhite 黑白名单
    68  type BlackAndWhite struct {
    69  	Code string     `name:"证券代码" dataframe:"code"`
    70  	Type SecureType `name:"类型" dataframe:"type"`
    71  }
    72  
    73  //func notify() {
    74  //	watcher, err := fsnotify.NewWatcher()
    75  //	if err != nil {
    76  //		logger.Error(err)
    77  //		return
    78  //	}
    79  //	defer watcher.Close()
    80  //}
    81  
    82  func getFileModTime(filename string) time.Time {
    83  	fileStat, err := os.Lstat(filename)
    84  	if err != nil {
    85  		return time.Now()
    86  	}
    87  	return fileStat.ModTime()
    88  }
    89  
    90  // 监控黑白名单文件的变化
    91  func notifyBlackAndWhiteList() {
    92  	filename := path.Join(cache.GetRootPath(), blacklistFilename)
    93  	if !api.FileExist(filename) {
    94  		_ = api.CheckFilepath(filename, true)
    95  		_ = os.WriteFile(filename, nil, 0644)
    96  	}
    97  
    98  	lastModTime := getFileModTime(filename)
    99  	ticker := time.NewTicker(1 * time.Second)
   100  	defer ticker.Stop()
   101  	for {
   102  		select {
   103  		case <-ticker.C:
   104  			modTime := getFileModTime(filename)
   105  			if modTime.After(lastModTime) {
   106  				logger.Warnf("黑白名单文件有变化, 重新加载...")
   107  				lazyLoadListOfBlackAndWhite()
   108  				logger.Warnf("黑白名单文件有变化, 重新加载...OK")
   109  				lastModTime = modTime
   110  			}
   111  		}
   112  	}
   113  }
   114  
   115  func init() {
   116  	go notifyBlackAndWhiteList()
   117  }
   118  
   119  // 加载黑白名单
   120  func lazyLoadListOfBlackAndWhite() {
   121  	mutexSafes.Lock()
   122  	defer mutexSafes.Unlock()
   123  	filename := path.Join(cache.GetRootPath(), blacklistFilename)
   124  	var list []BlackAndWhite
   125  	err := api.CsvToSlices(filename, &list)
   126  	if err != nil || len(list) == 0 {
   127  		return
   128  	}
   129  	mapSafes.Clear()
   130  	for _, v := range list {
   131  		securityCode := exchange.CorrectSecurityCode(v.Code)
   132  		mapSafes.Put(securityCode, v.Type)
   133  	}
   134  }
   135  
   136  // SyncLoadListOfBlackAndWhite 同步黑白名单
   137  func SyncLoadListOfBlackAndWhite() {
   138  	mutexSafes.Lock()
   139  	defer mutexSafes.Unlock()
   140  	filename := path.Join(cache.GetRootPath(), blacklistFilename)
   141  	var list []BlackAndWhite
   142  	mapSafes.Each(func(key string, value SecureType) {
   143  		if value == FreeTrading {
   144  			return
   145  		}
   146  		list = append(list, BlackAndWhite{Code: key, Type: value})
   147  	})
   148  	_ = api.SlicesToCsv(filename, list)
   149  }
   150  
   151  // 校验和修正证券代码
   152  func verify_and_correct_for_security_code(code string) (securityCode string) {
   153  	onceSafes.Do(lazyLoadListOfBlackAndWhite)
   154  	return exchange.CorrectSecurityCode(code)
   155  }
   156  
   157  // 检查是否禁止的类型
   158  func checkNotVarietyType(code string, varietyType SecureType) bool {
   159  	securityCode := verify_and_correct_for_security_code(code)
   160  	v, ok := mapSafes.Get(securityCode)
   161  	if !ok {
   162  		return true
   163  	}
   164  	if v != varietyType && v != ProhibitTrading {
   165  		return true
   166  	}
   167  	return false
   168  }
   169  
   170  // AddCodeToBlackList 新增黑白名单成分股
   171  func AddCodeToBlackList(code string, secureType SecureType) {
   172  	_, ok := mapSecureTypes[secureType]
   173  	if !ok {
   174  		fmt.Println()
   175  	}
   176  	securityCode := verify_and_correct_for_security_code(code)
   177  	mapSafes.Put(securityCode, secureType)
   178  	SyncLoadListOfBlackAndWhite()
   179  }
   180  
   181  // ProhibitTradingToBlackList 禁止交易 - 双向
   182  func ProhibitTradingToBlackList(code string) {
   183  	AddCodeToBlackList(code, ProhibitTrading)
   184  }
   185  
   186  func ProhibitBuyingToBlackList(code string) {
   187  	AddCodeToBlackList(code, ProhibitForBuying)
   188  }
   189  
   190  func ProhibitSellingToBlackList(code string) {
   191  	AddCodeToBlackList(code, ProhibitForSelling)
   192  }
   193  
   194  // CheckForBuy 检查是否可以买
   195  func CheckForBuy(code string) bool {
   196  	return checkNotVarietyType(code, ProhibitForBuying)
   197  }
   198  
   199  // CheckForSell 检查是否可卖
   200  func CheckForSell(code string) bool {
   201  	return checkNotVarietyType(code, ProhibitForSelling)
   202  }