Giter Site home page Giter Site logo

skcom's People

Contributors

kiraxie avatar sean0921 avatar virus-warnning avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

skcom's Issues

設定檔加解密

  • 採用 cryptography 處理加解密作業
  • 採用 AES-CBC 加密
  • 設定檔加解密工具
    • skcom.tools.cfenc
    • skcom.tools.cfdec
  • 設定檔載入函數
    • 載入設定檔時, 發現設定檔沒加密, 提示加密用法
    • 載入設定檔時, 發現設定檔有加密, 詢問用戶密碼
    • 處理 logger 設定相依問題
    • 導入 skcom/__init__.py
    • 導入 skcom/receiver.py
    • 讀取多次設定檔時, 防止密碼被重複詢問

skcom.samples.bot 問題

skcom.samples.bot 會用到 telegram 設定, 目前的作法會讓 busm 套件再去載入一次 skcom.yaml, 如果這個檔案被加密, 產生 busm handler 的階段就會觸發例外, telegram 通知功能的配置可能要捨棄 dictConfig 的作法

Ticks 範例總量錯誤

暫存的 pStock 物件不會隨時間自動更新,需要重新載入一次才會取得新的總量

當本機沒安裝群益API時,自動安裝API會失敗

先前為了全自動化安裝,在每一個powershell command執行中強制加了"-NoNewWindow",但這會導致原本沒安裝API的狀況下執行"install_skcom" 會失敗!(但若先執行remove_skcom再執行install_skcom 會正確安裝)

if curr_dll_ver == version.parse('0.0.0.0'):
    helper.install_skcom(_DLL_VER_REQUIRED)  # fail
elif curr_dll_ver != req_dll_ver:
    helper.clean_mod()
    helper.remove_skcom() # success
    helper.install_skcom(_DLL_VER_REQUIRED) # success

環境檢查與安裝程式

實作細節

  • 檢查 Visual C++ 2010 可轉發套件是否安裝
  • 檢查 Visual C++ 2010 可轉發套件的版本,必須是 10.0.40219.325
  • 安裝 Visual C++ 2010 可轉發套件
  • 檢查群益 API 元件是否已註冊
  • 檢查群益 API 元件版本
  • 下載與安裝群益 API 元件
  • 元件版本太舊顯示恐嚇訊息

假如群益證券願意改用 VC++ 2015 or 2017,第二項就改為簡單版本判斷

Visual C++ 2010 可轉發套件版本問題

Visual C++ 2010 可轉發套件,Google 搜尋後可以找到三個版本

下載後檔名都是 vcredist_x64.exe,無法直觀看出差異,
其中 10.0.40219.325 含有安全性更新 MS11-025,需要協助使用者取得較可靠的版本
而使用 WMI 查版本時, 後兩項都顯示 10.0.40219, 無法分辨差異, 需要透過登錄資訊看出差異

10.0.40219.1 的登錄資訊

40219.1 的登錄資訊

10.0.40219.325 的登錄資訊

40219.325 的登錄資訊

正確的版本資訊

pkg-detail

比較之後可以發現, 正確的版本資訊只有安裝包的詳細內容可以查到,
安裝後兩個套件裝完後版本資訊完全相同, 僅 EstimatedSize 和 InstallSource 不同
看起來只能用 EstimatedSize 檢查版本,確認數值是 14169。

CMD 指令查套件 (使用這個作法)

(base) PS>wmic product get name,version
Name                                                            Version
Update for Windows 10 for x64-based Systems (KB4023057)         2.57.0.0
Microsoft Visual C++ 2017 X64 Additional Runtime - 14.16.27024  14.16.27024
Boot Camp 服務                                                  6.0.6136
Update for Windows 10 for x64-based Systems (KB4480730)         2.51.0.0
Google Update Helper                                            1.3.34.7
Microsoft Visual C++ 2017 X64 Minimum Runtime - 14.16.27024     14.16.27024
Apple Software Update                                           2.2.0.150
LibreOffice 6.1.5.2                                             6.1.5.2
Intel(R) Chipset Device Software                                10.0.27
Microsoft Visual C++ 2010  x64 Redistributable - 10.0.30319     10.0.30319

PowerShell 指令查套件

(base) PS>get-wmiobject -class Win32_Product | select-object -property Name

Name
----
Update for Windows 10 for x64-based Systems (KB4023057)
Microsoft Visual C++ 2017 X64 Additional Runtime - 14.16.27024
Boot Camp 服務
Update for Windows 10 for x64-based Systems (KB4480730)
Google Update Helper
Microsoft Visual C++ 2017 X64 Minimum Runtime - 14.16.27024
Apple Software Update
LibreOffice 6.1.5.2
Intel(R) Chipset Device Software
Microsoft Visual C++ 2010  x64 Redistributable - 10.0.30319

檢查元件是否註冊與取得元件 DLL 的絕對路徑

(base) PS>reg query HKLM\SOFTWARE\Classes\TypeLib /s /f SKCOM.dll

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{75AAD71C-8F4F-4F1F-9AEE-3D41A8C9BA5E}\1.0\0\win32
    (預設值)    REG_SZ    C:\Users\Unknown\Downloads\CapitalAPI_2.13.16_PythonExample\元件\x64\SKCOM.dll

搜尋的結果: 發現 1 個相符。

安裝元件

regsvr32 ...\SKCOM.dll

檢查 VC 可轉發套件詳細版本資訊的方法

(base) PS>reg query HKLM\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\10.0\VC\VCRedist\x64 /v Version

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\10.0\VC\VCRedist\x64
    Version    REG_SZ    v10.0.40219.325

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\10.0\VC\VCRedist\x64
    Version    REG_SZ    v10.0.40219.01

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\10.0\VC\VCRedist\x64
    Version    REG_SZ    v10.0.30319.01

改善外部指令執行的可靠性

針對這些程式提升外部指令執行的可靠性

  • ps_exec() 改名為 win_exec()
  • 一般使用者模式直接用 subprocess 執行
  • 系統管理者模式用 powershell.exe Start-Process 執行
  • 單元測試
程式 管理員身分 參數含空白字元
vcredist_x64.exe 自動 X
regsvr32.exe 手動 X
tasklist.exe 不需要 O

API 元件細部測試與註解補充

  • 引用 API 部分補充註解, 對應到官方文件位置
  • 紀錄與官方文件有出入的細節
  • 邊緣參數值測試
  • 修正 ticks 總量錯誤與紀錄發生原因
  • 修正 ticks 的買賣顯示相反問題
  • 增加 ticks 回補設計
  • 改良 ticks 總量取得方式
  • 日 K 取樣截止日設計
  • 確認日 K 出現當日資料的時間點 (14:00 ~ 15:00 之間)

連續期貨轉倉報價

願望內容

由於近月期直接拿來做回測的話 會有失真的問題 想解決遠近月接一起連續報價 也可以因需求的關西 調整固定轉倉的日期時間~

** 群益 API 對應功能 **

TODO

** 實作方法 **

TODO

日K整組回傳設計

  • 以股為單位蒐集整組數據,所有數據取得後再回傳 dict
  • 依倒數天數篩選日期範圍

架構改良

  • 可共用程式抽象化
  • 模組化
  • 解決 pythoncom.PumpMessages() 問題
  • 確認 comtypes.client.GetEvents() 必要原因補充註解
  • 設定即時行情接收器
  • 設定日K接收器
  • 未配置接收器不啟動連線

研究 ps_exec, cmd_exec 的差異

研究 PowerShell 與 cmd 的差異, 確認不可取代性, 如果實際狀況允許, 全數替代成 PowerShell 用法

  • 測試管理員身分執行
  • 測試等待工作完成
  • 測試取得 stdout
  • 測試含有空白字元的參數
  • 測試 subprocess 是否支援 Windows 系統管理員身分執行 (不行)
  • 測試 subprocesswin32 (文件不足)

參考資料

實測結果

  • cmd_exec() 可以完全被 ps_exec() 取代
  • ps_exec() 如果是使用者模式, 可以直接取得 stdout
  • ps_exec() 如果是系統管理員, 需要用間接方式取得 stdout

使用者模式

Start-Process -FilePath netstat.exe `
  -ArgumentList '-a','-n','-p','tcp' `
  -NoNewWindow `
  -Wait

系統管理員模式

$stdout_id = Get-Random -Max 65535 | % ToString 'x4'; `
Start-Process -FilePath powershell.exe `
  -ArgumentList "Start-Process", `
    "-FilePath","netstat.exe", `
    "-ArgumentList","'-a','-n','-p','tcp'", `
    "-RedirectStandardOutput","$env:USERPROFILE\stdout-$stdout_id.txt", `
    "-NoNewWindow" `
  -Verb RunAs `
  -Wait; `
Get-Content -Path "$env:USERPROFILE\stdout-$stdout_id.txt"; `
Remove-Item -Path "$env:USERPROFILE\stdout-$stdout_id.txt";

使用 -Verb RunAs (以系統管理員身分執行) 的時候

  • 不能搭配 -NoNewWindow 參數
  • 不能搭配 -RedirectStandardOutput 參數

因此需要用兩層 Start-Process 才能取得系統管理員身分執行的 stdout

盤中監控與通知

  • 計算各均線位置, 作為突破/跌破的參考價位
  • 計算20日均量, 60日均量, 20日最大量, 作為爆量偵測參考值
  • 盤中突破/跌破偵測
  • 盤中爆量偵測
  • 導入 BusmHandler
  • Telegram 通知機制設計
  • bot.py: function, property 名稱重新定義
  • 控制重複事件的出現頻率
  • 改善手機排版問題
  • 空白環境完整測一輪 setup, skcom.yaml, bot
  • 更新 README.md
    • 更新 Ticks 範例輸出 (逐筆交易後的)
    • 新增 Bot 範例輸出
    • 新增 Bot 範例的 screenshot
    • 修正破圖問題
  • 佈署到 testpypi
    • 佈署
    • 測試: 重裝 python > 執行 skcom.tools.setup > 改 skcom.yaml > 執行 skcom.samples.bot
    • 監測盤中實際執行效果

實測狀況

test.pypi.org 沒有 pywin32, comtypes 這兩個套件, 所以在測試模式需要自己先安裝好, 否則會失敗

ERROR: Could not find a version that satisfies the requirement comtypes>=1.1.7; platform_system == "Windows" (from skcom) (from versions: none)
ERROR: No matching distribution found for comtypes>=1.1.7; platform_system == "Windows" (from skcom)
ERROR: Could not find a version that satisfies the requirement pywin32>=1.0; platform_system == "Windows" (from skcom) (from versions: none)
ERROR: No matching distribution found for pywin32>=1.0; platform_system == "Windows" (from skcom)

logger 名稱變更後 init.py 的檢查程式沒跟進而發生首次執行失敗

Traceback (most recent call last):
  File "C:\Users\Unknown\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 184, in _run_module_as_main
    mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
  File "C:\Users\Unknown\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 110, in _get_module_details
    __import__(pkg_name)
  File "C:\Users\Unknown\AppData\Local\Programs\Python\Python38\lib\site-packages\skcom\__init__.py", line 37, in <module>
    cfg_dict['loggers']['busm']['handlers'].remove('telegram')

test.pypi.org 安裝後發現 tools 沒包進去

C:\Users\Unknown>python -m skcom.tools.clean
C:\Users\Unknown\AppData\Local\Programs\Python\Python38\python.exe: Error while finding module specification for 'skcom.tools.clean' (ModuleNotFoundError: No module named 'skcom.tools')

環境清除工具

移除理想環境, 用來驗證初始化動作是否正常

  • 移除 comtypes.gen.*
  • 解除註冊 SKCOM.dll
  • 移除 ~/.skcom/lib 目錄

API 元件版本選擇工具

skcom.samples.setup 增加版本清單,讓使用者可以決定元件版本

  • 爬蟲取得最近的三個版本字串
  • 提示與使用者輸入元件版本, 預設最新版
  • 驗證使用者輸入的版本字串

打包 pypi 套件

  • 範例程式搬進模組目錄
  • 設定檔範本搬進模組目錄
  • pylint for quicksk.helper
  • pylint for quicksk.receiver
  • pylint for quicksk.samples
  • README.rst
  • setup.py
  • bin/publish.py (rstcheck, testing, ...)
  • PyPi 測試站驗證

install_skcom() 調整後無法安裝 2.13.16

先前為了讓 2.13.18 能正確安裝,在 DLL 偵測 pattern 之前加上 .+

不過這導致 2.13.16 變成無法安裝,因此需要再調整一下 pattern 讓兩個版本都能正常安裝

改善佈署程式的可靠性

  • 確保 Windows 運作正常, 避免使用 Linux 指令
  • 確認 Windows 是否可用 pyenv, 如果可行就實作 wheel 單機測試流程
    • 發現可以用 pyenv-win
    • 已安裝 3.8.1-amd64, 3.7.6-amd64
    • 不支援 miniconda
  • 參數控制 wheel, testing, production

錯誤處理

SKCOM 元件回報錯誤的時候,回應有效的錯誤訊息

  • 回傳值轉換錯誤訊息
  • 顯示
  • logging
  • 元件本身的 log 移至 ~.skcom\logs\capital
  • 紀錄可以故意製造錯誤的方法,供日後測試用

logging.ini 載入失敗

@kiraxie 反應 init.py 的 file 變數,以 import 方式運作時會得到原始程式的路徑,而不是 init.py 的路徑,因此造成 logging.fileConfig 找不到設定檔

  • 先處理完 #36
  • 嘗試重現問題
  • 變更路徑取得方式
  • 使用原先重現步驟測試

登入後出現待回應事項,程式無法繼續

使用時遇到 SK_WARNING_REGISTER_REPLYLIB_ONREPLYMESSAGE_FIRST
就是使用 API 登入的時候會需要閱讀一個公告並且回傳已閱讀

任務細節

  • 對應 API 元件版本升級為 2.13.18
  • 互動式處理未讀訊息

即時報價程式與設定檔樣本

  • 即時報價程式從 Gist 搬過來
  • 設定檔樣本從 Gist 搬過來
  • 首次執行時,複製一份設定檔樣本到文件目錄,並且提示用戶去改成自己的帳號

自動重新連線機制

  • 發生重新連線時,確保已發出的 tick 不重複發送 - 延後處理 #92
  • EnterMonitor 失敗後 retry 功能
  • 改用 asyncio 重新設計 QuoteReceiver
  • 研究 signal 與 EnterMonitor() 衝突問題
  • 改用生命週期狀態取代多項狀態值
  • 除錯用 DELAY 參數
  • Login 失敗後 retry 功能
  • 觸發 3021 時,自動呼叫 self.start() 觀察副作用
  • 區別 self.start() 裡面可以重複執行與不可重複執行的部份
  • 確認 self.__init__() 裡面需要重置的部份

斷線時, 會出下下列錯誤訊息

[09:40:26] ERROR   | 執行動作 [狀態變更 3021] 時發生錯誤, 詳細原因: 3

在盤中時間會需要自動重新連線

重新連線實測紀錄

實際執行重新連線, 會發生無法初始化的狀況

[15:57:38] ERROR   | 執行動作 [狀態變更 3021] 時發生錯誤, 詳細原因: 3
[15:57:38] ERROR   | 異常斷線
[15:57:38] INFO    | 系統公告: SKReplyLib_OnReplyMessage:Announcement callback.
[15:57:38] ERROR   | 執行動作 [Login()] 時發生錯誤, 詳細原因: SK_ERROR_INITIALIZE_FAIL
[15:57:38] INFO    | 結束連線
[15:57:38] INFO    | 監聽結束

改良 3021 處理方式後, 仍然無法正常運作

實測發現群益 API 的 3021 事件並不能信任, 需要自己實作網路連線偵測的功能

python -m skcom.sandbox.reconnect
WARNING | 目前設定檔沒有加密, 建議您加密避免帳號外流
WARNING | 執行下列指令即可加密:
WARNING |   python -m skcom.tools.cfenc
nKind=3001, nCode=0
連線成功
nKind=3003, nCode=0
連線就緒
0001: Loop start() ...
[14:30:00] 596.00
0002: Loop start() ...
0003: Loop start() ...
0004: Loop start() ...
0005: Loop start() ... <------ 在連線後 5 秒時關閉 Wifi
0006: Loop start() ...
0007: Loop start() ...
0008: Loop start() ...
0009: Loop start() ...
0010: Loop start() ...
0011: Loop start() ...
0012: Loop start() ...
0013: Loop start() ...
0014: Loop start() ...
0015: Loop start() ...
0016: Loop start() ...
0017: Loop start() ...
0018: Loop start() ...
0019: Loop start() ...
0020: Loop start() ...
0021: Loop start() ...
0022: Loop start() ...
0023: Loop start() ...
0024: Loop start() ...
0025: Loop start() ...
0026: Loop start() ...
0027: Loop start() ... <------ 在連線後 27 秒才回應 3021 錯誤
執行動作 [狀態變更 3021] 時發生錯誤, 詳細原因: 3
nKind=3021, nCode=3
異常斷線
Loop reconnect ...
nKind=3001, nCode=0
連線成功
0028: Loop start() ... <------ WiFi 還在斷線狀態, 但是群益 API 回應連線成功

在運行ticks範本的時候返回錯誤SK_SUBJECT_TICK_PAGE_EXCEED

運行ticks範本時遇到上述的錯誤代碼 查詢後發現 我在設定skcom.json 裡面發現範例共監聽了5筆股票資料造成這個錯誤,想請問否有辦法及時監聽所有的股票的即時數據 這裡的測試結果是只要同時監聽超過一筆股票數據就會發生這個情況

清除 VC++ 可轉發套件

研究自動化移除 VC++ 可轉發套件的方法,節省模擬時間

  • 研究實作方法
  • skcom.helper.remove_vcredist()
  • 導入到 skcom.samples.clean

執行 wmic 時遇到問題

能動的:

wmic product where "name like '%Visual C++ 2010%'" get name
wmic product where "name='Microsoft Visual C++ 2010  x64 Redistributable - 10.0.40219'" get name
powershell.exe Start-Process -FilePath wmic -ArgumentList "product,where,name!=1,get,Name" -NoNewWindow -Wait
Get-Package -Name 'Microsoft Visual C++ 2010  x64 Redistributable - 10.0.40219'
Uninstall-Package -Name 'Microsoft Visual C++ 2010  x64 Redistributable - 10.0.40219'
start-process tasklist -argumentlist '"/fi" "imagename eq LINE.exe" "/fo" "csv"' -nonewwindow -wait
Start-Process -FilePath powershell.exe -ArgumentList Start-Process,-FilePath,tasklist,"-argumentlist '/fo csv'",-NoNewWindow -Verb RunAs -Wait

不能動的:

只要 -ArgumentList 的值有空白字元就一定不能動

另外 wmic 執行後的 stdout 會是個奇怪的檔案, 不確定是 unicode 編碼還是 binary 檔案

而且 WMI 似乎也被 CIM 取代

還沒試的:

Get-CimInstance -class win32_product -filter 'name="Microsoft Visual C++ 2010  x64 Redistributable - 10.0.40219"' | Invoke-CimMethod -MethodName Uninstall

參考資料:

Ctrl+C 會出現錯誤, 但不影響使用

(base) PS>python ticks.py
登入成功
連線成功
連線就緒
產品資訊載入完成
[0050 元大台灣50] 時間:13:30:00.000 買:82.55 賣:82.50 成:82.55 單量:476 總量:13554
偵測到 Ctrl+C, 結束監聽
斷線
監聽結束
Error in sys.excepthook:

Original exception was:

原因調查

斷點觀察後, 發現主因是這行

self.skH = comtypes.client.GetEvents(self.skQ, self)

做了一些交叉測試, 發現這樣的結果

GetEvents 位置 回傳值位置 sys.excepthook
global global 會發生
function local 不會發生
class function class member 會發生
class function global 會發生
class function local 不會發生
class function 不指定 不會發生, 但收不到事件

Ticks 撮合頻率異常

問題描述

我看你的 readme,台積電是每5秒
實際上應該不是5秒吧?不可能每筆交易都間隔5秒
所以這應該是5秒 data?

Ticks 轉換為分鐘線,供當沖客使用

  • 最後一分鐘內的資料丟進緩衝區
  • 一分鐘之內,緩衝區的資料結算但不清除
  • 一分鐘之後,緩衝區的資料結算且清除,準備放下一分鐘的資料

非交易日無法取得 K 線資料

因為目前 K 線資料有日期判斷, 但是只考慮到交易日情境
在非交易日會發生錯誤判斷, 導致無法回傳整批資料
暫時先用時間差的方式解決, 最後一筆資料取得後的 0.15 秒觸發事件

群益 API 並沒有提供交易日/休市日的功能,這部份需要自行判斷,
依照目前證交所的規定,採用這樣的方式進行判斷

  • 週六、日視為休市
  • 納入表列休市日期

自動回答登入前公告

設定檔增加參數 reply_read

  • 設定為 true,自動回報已讀
  • 設定為 false,維持互動式決定是否已讀

跟進 API 2.13.21

  • API 採用 2.13.21
  • 測試原先的 sample 是否能正常運作
    • skcom.tools.ticks - OK!
    • skcom.tools.kline
  • 盤中觀察逐筆交易狀況

ModuleNotFoundError: No module named 'comtypes.gen.SKCOMLib'

首次使用群益 API 需要自己生成 SKCOMLib.py, 第二次之後就不需要此動作

  • skcom\helper.py 檢查與生成函數
  • skcom\samples\setup.py 實作安裝步驟
  • skcom\samples\kline.py 無法載入模組的提示
  • skcom\samples\ticks.py 無法載入模組的提示

交易策略回測功能

透過進出邏輯測試套用在歷史行情的績效,僅參考價量與均線,暫不參考籌碼

  • 交易策略抽象 class
  • 交易策略實體 class (順勢交易)
    • 進場邏輯
    • 加碼邏輯
    • 減碼邏輯
    • 停利邏輯
    • 停損邏輯
  • 回測程式

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.