diff --git a/config.yml b/config.yml
index 1b20c3cb1edcc78b121d6b0427a64bd6473cff51..a490ea58a4f097306db4066d8ef416a6d2e54f48 100644
--- a/config.yml
+++ b/config.yml
@@ -45,6 +45,11 @@ logger:
   root:
     level: INFO
     handlers: [console]
+datas:
+  navs:
+    exrate:
+      - from: EUR
+        ticker: EURUSD BGN Curncy
 fund_pool:
   fund_optimize:
     sortino_weight:
diff --git a/datas/datum/api.py b/datas/datum/api.py
index 609969164731ff8ea938eb0609321120cd4a20d1..9c5ff4b0aee7187da91582dab3e9e9b688e0379c 100644
--- a/datas/datum/api.py
+++ b/datas/datum/api.py
@@ -2,9 +2,13 @@ import datas.datum.robo_base_datum as _rbd
 from datas.datum.enums import DatumType
 
 
-def get_fund_datums(crncy=None, risk=None):
+def get_fund_datums(crncy=None, risk=None, fund_ids=None):
     return _rbd.get_base_datums(type=DatumType.FUND, crncy=crncy, risk=risk)
 
 
+def get_without_crncy(crncy):
+    pass
+
+
 if __name__ == '__main__':
     print(get_fund_datums(risk=1))
diff --git a/datas/mysql.sql b/datas/mysql.sql
index 7eccdcf560a14108a1c16e091cc32296b95f9e7f..88b3e543be589162534de1886c9b54effe2e284f 100644
--- a/datas/mysql.sql
+++ b/datas/mysql.sql
@@ -38,3 +38,33 @@ CREATE TABLE IF NOT EXISTS robo_fund_navs
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4 COMMENT '基金数据表';
 
+
+CREATE TABLE IF NOT EXISTS `robo_exrate`
+(
+    `re_id`          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+    `re_ticker`      VARCHAR(255)    NOT NULL COMMENT '汇率ticker',
+    `re_date`        DATETIME        NOT NULL COMMENT '日期',
+    `re_close`       DOUBLE          NOT NULL COMMENT '收盘价',
+    `re_create_time` DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    `re_update_time` DATETIME                 DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+    PRIMARY KEY (`re_id`),
+    UNIQUE (`re_ticker`, `re_date`),
+    INDEX (`re_date`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT '基金数据表';
+
+
+CREATE TABLE IF NOT EXISTS robo_optimize_pool
+(
+    rop_id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+    rop_date        DATETIME        NOT NULL COMMENT '数据日期',
+    rop_fund_ids    JSON                     DEFAULT NULL COMMENT '基金ID',
+    rop_create_time DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    rop_update_time DATETIME                 DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+    PRIMARY KEY (rop_id),
+    INDEX (rop_date)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT '优选基金池';
+
diff --git a/datas/navs/__init__.py b/datas/navs/__init__.py
index e4429d059eca1a2203970bb3a20d690242ba62fa..68f23671ff65b789e90707cdafbd9f21fcdfc8e9 100644
--- a/datas/navs/__init__.py
+++ b/datas/navs/__init__.py
@@ -1,3 +1,5 @@
-from .robo_fund_navs import *
+from .api import (
+    get_navs
+)
 
-del robo_fund_navs
+del api
diff --git a/datas/navs/api.py b/datas/navs/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..921910e543849542e13e916e615be114486486e7
--- /dev/null
+++ b/datas/navs/api.py
@@ -0,0 +1,32 @@
+import pandas as _pd
+from datas.navs import robo_exrate as _re, robo_fund_navs as _navs
+from datas import datum as _datum, DatumType
+from utils import config, to_bool
+from datetime import timedelta
+
+navs_config = config['datas']['navs'] if 'datas' in config and 'navs' in config['datas'] else {}
+
+
+def get_navs(fund_id=None, min_date=None, max_date=None):
+    navs = _navs.get_navs(fund_id=fund_id, min_date=min_date, max_date=max_date)
+    if 'exrate' in navs_config:
+        navs = _pd.DataFrame(navs)
+        navs = navs.pivot_table(index='nav_date', columns='fund_id', values='nav_cal')
+        for exrate_config in navs_config['exrate']:
+            exrate = _pd.DataFrame(_re.get_exrates(ticker=exrate_config['ticker'], min_date=navs.index.min(), max_date=navs.index.max()))
+            exrate = exrate[['date', 'close']]
+            exrate.set_index('date', inplace=True)
+            for fund in _datum.get_fund_datums(crncy=exrate_config['from']):
+                navs[fund['id']] = round(navs[fund['id']] * exrate['close'], 4)
+        navs = navs.reset_index().melt(id_vars='nav_date', value_name='nav_cal')
+        navs.dropna(inplace=True)
+        navs = navs[['fund_id', 'nav_date', 'nav_cal']]
+        navs.sort_values('fund_id', inplace=True)
+        navs = navs.to_dict('records')
+    return navs
+
+
+if __name__ == '__main__':
+    from utils import parse_date
+
+    print(get_navs(min_date=parse_date('2022-11-01')))
diff --git a/datas/navs/robo_exrate.py b/datas/navs/robo_exrate.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d21a5970f4daa5288a13bf3a3242325e28e036b
--- /dev/null
+++ b/datas/navs/robo_exrate.py
@@ -0,0 +1,26 @@
+from utils import read, where, format_date
+
+
+@read
+def _select(*args, **kwargs):
+    return f'''select re_id as id, re_ticker as ticker, re_date as date, re_close as close from robo_exrate {where(*args, **kwargs)}'''
+
+
+def get_exrates(ticker=None, min_date=None, max_date=None):
+    sqls = []
+    if min_date:
+        sqls.append(f"re_date >= '{format_date(min_date)}'")
+    if max_date:
+        sqls.append(f"re_date <= '{format_date(max_date)}'")
+    return _select(*sqls, re_ticker=ticker)
+
+
+@read(one=True)
+def get_exrate(ticker, date):
+    return f'''select re_id as id, re_ticker as ticker, re_date as date, re_close as close from robo_exrate {where(re_ticker=ticker, re_date=date)}'''
+
+
+if __name__ == '__main__':
+    from utils import parse_date
+
+    print(get_exrate(date=parse_date('2022-11-01'), ticker='EURUSD BGN Curncy'))
diff --git a/fund_pool/fund_optimize.py b/fund_pool/fund_optimize.py
index 9bf4025f2c26aac76ef5ca4daa1aa356fd579367..72828aae479482e0645fc439bcefdde17408cb8a 100644
--- a/fund_pool/fund_optimize.py
+++ b/fund_pool/fund_optimize.py
@@ -4,11 +4,14 @@ from datas import navs
 from dateutil.relativedelta import relativedelta
 from empyrical import sortino_ratio
 
-optimize_config = config['fund_pool']['fund_optimize']
-sortino_config = [{**x, 'name': [f"sortino_{y[1]}_{y[0]}" for y in x.items() if y[0] != 'weight'][0]} for x in optimize_config['sortino_weight']]
+optimize_config = config['fund_pool']['fund_optimize'] if 'fund_pool' in config and 'fund_optimize' in config['fund_pool'] else {}
+sortino_config = [{**x, 'name': [f"sortino_{y[1]}_{y[0]}" for y in x.items() if y[0] != 'weight'][0]} for x in optimize_config['sortino_weight']] \
+    if 'sortino_weight' in optimize_config else []
 
 
 def find_optimize(fund_ids, day):
+    if not sortino_config:
+        raise NameError(f"find optimize, but not found sortino config.")
     start = filter_weekend(sorted([day - relativedelta(days=1, **dict_remove(x, ('weight', 'name'))) for x in sortino_config])[0])
     fund_navs = pd.DataFrame(navs.get_navs(fund_id=tuple(fund_ids), min_date=start, max_date=day))
     fund_navs.sort_values('nav_date', inplace=True)
@@ -17,31 +20,21 @@ def find_optimize(fund_ids, day):
     day_income = round(fund_navs.pct_change(), 4)
     sortino = pd.DataFrame()
     for item in sortino_config:
-        delta_kwargs = {**item}
+        delta_kwargs = item.copy()
         del delta_kwargs['weight'], delta_kwargs['name']
         ratio = dict(sortino_ratio(day_income.truncate(before=(day - relativedelta(**delta_kwargs)))))
         sortino = pd.concat([sortino, pd.DataFrame([ratio], index=[item['name']])])
     sortino = sortino.T
-    sortino['score'] = sortino.apply(lambda r:sum([x['weight'] * r[x['name']] for x in sortino_config]), axis=1)
+    sortino['score'] = sortino.apply(lambda r: sum([x['weight'] * r[x['name']] for x in sortino_config]), axis=1)
     sortino.sort_values('score', ascending=False, inplace=True)
     return day_income.columns[sortino.index[0]]
 
 
-def get_base_fund_pool():
+def get_optimize_fund_pool(day):
     pass
 
 
 if __name__ == '__main__':
-    from datas import datum
     from datetime import date
 
-    funds = datum.get_fund_datums()
-    funds = pd.DataFrame(funds)
-    for (category, assetType), fund_group in funds.groupby(by=['category', 'assetType']):
-        if len(fund_group) > 1:
-            fund_ids = tuple(fund_group['id'])
-            fund_id = find_optimize(fund_ids, date.today())
-            print(fund_ids, "->", fund_id)
-        else:
-            fund_ids = tuple(fund_group['id'])
-            print(fund_ids, "->", fund_ids[0])
+    get_optimize_fund_pool(date.today())
diff --git a/utils/base.py b/utils/base.py
index 8e5af9736a511c5054b7a6b1c4fdd0dbaf08c094..8a8b9abacd7867743c1380a3776a9157f3f0f534 100644
--- a/utils/base.py
+++ b/utils/base.py
@@ -1,6 +1,13 @@
 import os
+from functools import reduce
 
-__all__ = ['get_project_path', 'deep_dict_update', 'dict_remove']
+__all__ = [
+    'get_project_path',
+    'deep_dict_update',
+    'dict_remove',
+    'equals_ignore_case',
+    'to_bool',
+]
 
 
 def get_project_path():
@@ -36,3 +43,20 @@ def dict_remove(d: dict, k) -> dict:
     for key in tuple(k):
         del result[key]
     return result
+
+
+def equals_ignore_case(a, b) -> bool:
+    return str(a).upper() == str(b).upper() if a and b else False
+
+
+_TRUE_STR = ['true', 't', 'yes', 'y', 'on']
+_FALSE_STR = ['false', 'f', 'no', 'n', 'off']
+
+
+def to_bool(v) -> bool:
+    if isinstance(v, str):
+        if reduce(lambda a, b: a or b, [equals_ignore_case(v, x) for x in _TRUE_STR]):
+            return True
+        if reduce(lambda a, b: a or b, [equals_ignore_case(v, x) for x in _FALSE_STR]):
+            return False
+    return bool(v)