OneNote有时候会同步出错,未有任何错误信息,OneNote直接奔溃退出。此时方法之一是删除缓存。WIN+R后输入onenote /safeboot后会给出删除缓存的选项。

删除缓存之后,再重新打开网络的笔记本,一切都OK了。不过之前未同步的笔记会丢失,需谨慎使用。

基金按照种类的不同,可分为普通基金和ETF基金。其中普通基金用现金申购,赎回也得到现金。ETF基金用成分股股票申购(某些ETF支持部分股票用现金替代),赎回同样获得股票。一般ETF基金单笔申购下限较高,普通散户无法参与。以下只讨论普通基金的申购和赎回。

还有一些比较特殊的货币类基金,操作方法、费率、日程跟下面所述也差异较大。以下只针对普通的开放式基金(股票、债券、QDII基金等)。

申购注意事项

  • 申购赎回的时间点一般以工作日的下午三点为截止时点,并按照当日的净值进行结算。比如周五下午三点前的申购和赎回,将算入周五的操作,结算时按照周五的基金净值操作。周五下午三点后、周末以及下周一三点前,均算入下周一的操作(若下周一为工作日),结算时按照下周一的基金净值操作。如果下周一非工作日(比如遇到春节假期),结算时点还需要向后延迟。
  • 无论是场内和场外,申购时,都按照金额进行申购。实际获得份额数量需等当日净值出来之后才能确定。届时,实际份额=申购金额/结算单位净值/(1+申购费率)。
  • 赎回按照份额进行赎回,实际获得的资金回款=赎回份额 结算单位净值 (1-赎回费率)。
  • 申购赎回时需关注申购赎回费用,不同渠道不同方式费用不一样,尽量选用最便宜最合适的渠道操作。
  • 申购和赎回一般都有单笔下限,场内和场外还可能不一样。需参考基金合同。
  • 申购和赎回不是实时到帐的。申购后一般需T+2或T+3才能赎回或卖出;基金赎回根据基金的不同,需T+1到T+7才能回款,具体也许要查看基金合同。通常而言,货币基金T+1回款,普通股票债券类基金场内T+2回款,场外T+3回款,一些QDII类的基金则需要T+7才能回款。
  • 在基金募集期内(即正式上市前),还有种申购方法叫做”认购”。这种情况比较特殊,但其操作和”申购”一样,只不过在进入的菜单位置略有不同(一般和”申购”并列)。

国内的基金申购和赎回分为场内和场外两种,获得的份额分别场内份额和场外份额。这两种份额之间又可以通过转托管进行转换。

场内申购和赎回

场内申购和赎回是指用股票账户,使用股票账户的资金进行申购和赎回。它和买卖股票类似,一般在股票软件里的”股票”栏目下的”场内基金”里进行操作(注意别进到”场外基金”)。场内申购的一个好处是,可以用可用余额进行申购(比如卖出股票的回款当天不可取,但可以用来申购基金),在回款上也会提前一天。

某些券商的软件里把上海的基金放到另外一个位置,比如有专门的”上海LOF基金”栏目进行申购和赎回。但其操作是类似的。

95%的基金的场内赎回费率是0.5%,少数基金也有低至0.2%,高至0.7%的。申购费率则差异较大,从免费到1.6%的都有。通常而言,单笔申购金额越高,申购费率越低。而高到一定金额以上(比如500万),申购费将按照固定金额(比如1000元)收取,此时申购费可忽略不计(不到万分之二)。

除了基金公司公布的费率外,一些券商可在公布的费率基础上打折。部分券商可以做到申赎都是五折。申购费是直接打折(比如从1.2%降到0.6%)。赎回费的折扣则只基于券商所分部分(比如总赎回费0.5%,其中券商获得0.1%,基金公司和基金留存0.4%,那么五折的赎回费的实际费率为0.4%+0.1%/2=0.45%)。

场内申购一般T+2可卖出和赎回。QDII基金则需要T+3才能卖出和赎回。 场内赎回一般T+2资金可用,T+3资金可取。QDII基金则需要T+6可用,T+7可取,部分QDII基金所需时间更长。

场外申购和赎回

除股票账户以外的申购和赎回都被成为场外申购和赎回。这包括但不限于以下场所:

  • 基金公司官网
  • 银行(营业部或网银)
  • 证券公司的场外申购和赎回。这里和场内操作略有区别,场内操作是在股票账户进行操作,场外操作在基金账户进行操作。一般在股票软件上有一个专门的”场外基金”的操作栏目。
  • 基金销售网站(比如陆金所或我买基金网)
  • 淘宝基金公司旗舰店

基金公司官网通常有更优惠的费率,缺点是需要到每个基金公司的官网上去注册账户,不能统一管理所持有不同基金公司的基金。

场外申购最大的好处是费率低(因为场外申购的场所很多,经常有打折的地方)。一般来说,场外申购的费率至少能打四折。如果没有四折,可以再找找看别的渠道。淘宝网旗舰店可以二折。

基金公司官网直销有时候有特别优惠的费率(比如免费或一折),但通常需要一种”汇款”的付款方式。通常购买基金,都是基金公司或销售网站直接划款,就像我们用支付宝一样,填一下密码和手机验证码即可。但汇款的方式则是,我们需主动进入我们银行的网银,在网银里向基金公司提供的银行账户进行转账。

转托管

申购后,我们有了对应的场内份额和场外份额。因为费率的原因,我们更愿意在场外申购。但对于上市交易的基金,场内份额的变现能力更高,可以直接在市场上卖出,有时候还可以进行一二级市场上的套利。转托管便是将场外份额转为场内份额。

转托管的准备:

  • 股票账户里需挂上基金账户(对于这几年开户的账户,一般默认都挂上了,但最好向客户经理咨询一下)。如果没有挂上,可在基金公司或销售网站上查询自己的基金账户(深交所),然后在券商这边挂上(现在不需要去营业部了,可在网络上直接操作)
  • 向股票账户的客户经理要账户的席位号(一般为五位数的数字)

在基金公司网站上申购的T+2或T+3日后,在网站上找到转托管的链接,把席位号填上即可。但需要等到T+2才能在场内账户上看到场外转过来的份额。也就是说场外申购,转托管到场内卖出,至少需要5个工作日的时间。

如果我们要缓存过去固定天数的记录时,一种方法是使用std::deque,但这样当新记录出来后,还需要判断队列长度,必要时还需要手工pop老记录。boost的circular_buffer提供自动的方法来实现该操作,利用连续内存,以环形的方式保存记录,因此效率极高。

boost::circular_buffer支持:

  • std::deque操作,包括push_back, push_front, pop_back, pop_front, front, back等操作。所有操作复杂性都是$O(1)$。
  • 随机存取!和vector一样,支持下标[i]以及at(i),且存取时间为$O(1)$。
  • 支持iterator,因此begin, end, for in用于各种循环。
  • 所有stl算法可自动适用于circular_buffer,用法和其它stl容器一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <boost/circular_buffer.hpp>

int main() {
// set a curcular_buffer with max capacity of 3
boost::circular_buffer<int> v(3);

// push_back will push the data to the tail
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

// output 2, 3, 4
// circular_buffer supports interator, so can support STL for loop grammer and all STL algorithm
for (auto i : v)
std::cout << i << std::endl;

// we can also use push_front to the front
v.push_font(5);
// output 5, 2, 3
for (auto i = v.begin(); i != v.end(); ++i)
std::cout << *i << std::endl;

// we can change the capacity, the overflow records will be removed
v.set_capacaity(2);
// output 52
std::cout << v.front() << v.end();
// output 5, 2
for (auto i : v)
std::cout << i << std::endl;
// we can treat circula_buffer as a typical deque
v.pop_back();
v.pop_front();
}

boost提供了单元测试的框架,简单易用。在项目已经用了boost时,无需多余配置,使用非常方便。

简单用法

单元测试框架里有三层,第一层是TEST_MODULE,第二层是TEST_SUITE,第三层是TEST_CASE。简单的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// test.cpp
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE test_module_1

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(test_suit1)

BOOST_AUTO_TEST_CASE(test_case1)
{
BOOST_CHECK(2 + 2 == 4);
}

BOOST_AUTO_TEST_CASE(test_case2)
{
BOOST_CHECK(2 + 2 == 5);
}

BOOST_AUTO_TEST_SUITE_END();


BOOST_AUTO_TEST_SUITE(test_suite2)

BOOST_AUTO_TEST_CASE(test_case3) {
BOOST_CHECK_EQUAL(1, 2);
}

BOOST_AUTO_TEST_SUITE_END();

编译时,需将boost的头文件和lib文件目录加入搜寻路径,并将boost_unit_test_framework加入链接库。GCC编译的配置选项大概长这个样子

g++ -o test test.cpp -Iboost_include_path -Lboost_lib_path -lboost_unit_test_framework

可以将多个test文件加入到单元测试,编译成同一个可执行文件:

1
2
3
4
5
6
// test2.cpp
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_case4) {
BOOST_CHECK_EQUAL(2, 2);
}

编译选项:

g++ -o test test.cpp test2.cpp -Iboost_include_path -Lboost_lib_path -lboost_unit_test_framework

但需注意几点:

  1. #define BOOST_TEST_DYN_LINK用来自动生成main函数(所以不用手写),因此至多一个cpp文件里包含该宏,否则会出现错误。
  2. 同一个目标可执行文件,只能用一个TEST_MODULE,只在一个cpp文件定义#define BOOST_TEST_MODULE即可。

测试断言

测试断言即用来指定测试的行为,其基本格式为 BOOST_LEVEL_ITEM。

LEVEL有三类:

  1. WARN:在测试错误时,发出警告,不增加错误数量,不影响程序运行。实际用得不多。
  2. CHECK: 在测试错误时,增加错误数量,打印错误提示信息,但不影响程序运行。实际中用得最多。
  3. REQUIRE: 在测试错误时,增加错误数量,并终止测试。一般用于该错误将影响后续测试时。

ITEM有多个:

1
2
3
4
5
6
7
BOOST_CHECK(expr);              // error when expr == false
BOOST_CHECK_EQUAL(a, b); // error when a != b
BOOST_CHECK_CLOSE(a, b, e); // error when |a-b|/min(|a|,|b|) > e
BOOST_CHECK_CLOSE_FRACTION(a, b, e); // error when |a-b|>e
BOOST_CHECK_GE/GT/LT/LE/NE(a,b) // error when a not >=/>/</<=/!= b (compare)
BOOST_CHECK_NO_THROW(a); // error when a throw error
BOOST_CHECK_THROW(a, e) // error when a not throw e,where e is an error type

单元测试的初始化

boost的单元测试框架提供一个叫做FIXTURE的框架,可让在单元测试开始前进行初始化,在结束后自动析构。它可以应用在全局、SUITE和CASE级别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>
struct GlobalFixture {
GlobalFixture():global_i(0) { std::cout << "global setup" << std::endl; }
~GlobalFixture() { std::cout << "global teardown" << std::endl; }

int global_i;
};

struct SuiteFixture {
SuiteFixture():suite_i(1) { std::cout << "suite setup" << std::endl; }
~SuiteFixture() { std::cout << "suite teardown" << std::endl; }

int suite_i;
};

struct CaseFixture {
CaseFixture():case_i(2) { std::cout << "case setup" << std::endl; }
~CaseFixture() { std::cout << "case teardown" << std::endl; }

int case_i;
};

BOOST_GLOBAL_FIXTURE(GlobalFixture);

BOOST_FIXTURE_TEST_SUITE(test_suite10, SuiteFixture);

BOOST_AUTO_TEST_CASE(test_case10) {
BOOST_CHECK_EQUAL(suite_i, 1);
suite_i = 2;
}

BOOST_AUTO_TEST_CASE(test_case11) {
BOOST_CHECK_EQUAL(suite_i, 1);
}

BOOST_AUTO_TEST_SUITE_END();


BOOST_AUTO_TEST_SUITE(test_suite11);

BOOST_FIXTURE_TEST_CASE(test_case12, CaseFixture) {
BOOST_CHECK_EQUAL(case_i, 2);
case_i = 3;
}

BOOST_FIXTURE_TEST_CASE(test_case13, CaseFixture) {
BOOST_CHECK_EQUAL(case_i, 2);
case_i = 3;
}
BOOST_AUTO_TEST_SUITE_END();

注意几点:

  1. Case内部可直接使用SUITE和CASE的Fixutre类内部的成员变量和函数。
  2. SUITE级别的Fixture是在每个CASE级别进出时初始化和析构,而不是在SUITE进出时。也即,一个定义了Fixture的 SUITE,如果该SUITE包含多个CASE,那么这个Fixture会被初始化和析构多次,而Fixture内部的变量也无法在不同case之间共用。
  3. 注意没有BOOST_FIXTURE_TEST_SUITE_END,全部用BOOST_AUTO_TEST_SUITE_END标志一个SUITE的结束。

有时候不小心把一些大文件commit到了本地版本库,但往网络版本库同步时,因为文件过大被拒绝。如果在上传前,本地有多次提交,此时再在当前版本里删除这些大文件还不够,还需要把历史记录全删掉。效果要跟从来没提交过这些文件一样。

# 删除匹配文件
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *.zip' 

# 回收空间
rm -rf .git/refs/original/ 
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
du .git -lsh 

# 同步到网络
git push origin master --force

argparse是Python的optparse的替代库。目前optparse已经不再维护,python建议都更新为argparse。它们都是用来处理命令行参数的标准库。

载入标准库

import argparse
parser = argparse.ArgumentParser(description='help info for the program')

下面代码便能获得程序运行所输入的参数:

args = parser.parse_arg()

也可以忽略未定义的参数:

args, unknown_args_list = parser.parse_known_args()

添加命令行参数

parser.add_argument(name or flags...
    [, action][, nargs][, const][, default][, type]
    [, choices][, required][, help][, metavar][, dest])
  • name or flags - 参数名字。比如-f, –file,可以有多个
  • action - 遇到命令时的方法,有store,append,store_true, store_end, store_const等。
  • nargs - 要解释多少个参数
  • const - store_const时的值
  • default - 参数缺失时的默认值
  • type - 参数类型,直接写类型,不是字符串(和optparse不一样)
  • choices - 参数的可选值,若超出范围将出错
  • required - 参数是否必须有
  • help - 帮助信息
  • metavar - 命令行的提示信息
  • dest - 保存到的变量

一些例子:

parser.add_argument('-f', '--file', dest='file')
parser.add_argument('-f', '--file')  # 当有'--file'时,dest默认为file
# ./program.py -f abc.xml --> arg.file = 'abc.xml'

可以设定默认值:

parser.add_argument('-f', '--file', default='x.xml')
# ./program.py  --> arg.file = 'x.xml'

还可以指定参数的元素个数

parser.add_argument('--file', nargs=2)
# ./program.py --file a b -> arg.file = ['a', 'b']

nargs参数除了确定的数值以外,还可以是 * (任意多个),+(至少一个)。

为了识别 ./program.py --file a --file b这种写法,可以用下面这种:

parser.add_argument('-f', '--file', action='append')
# ./program.py --file a -f b -> arg.file = ['a', 'b']

可以指定类型

parser.add_argument('-n', dest='number', type=int)
# ./program.py -n 1 --> arg.number = 1

还可以定义常量,尤其是布尔参数:

parser.add_argument('-n', action='store_const', const=42)
parser.add_argument('-r', action='store_true', default=False)

非以’-‘或’–’开头的参数,可以通过位置变量保存:

parser.add_argument('pos1', nargs=1)
parser.add_argument('pos2')
# ./program.py a b c d --> arg.pos1 = ['a'], arg.pos2 = ['b', 'c', 'd']

BOOST_TTI里面有很多很用用的type traits,实现编译阶段的类型判断。其中BOOST_TTI_HAS_MEMBER_FUNCTION用来判断类是否包括指定成员函数,使用非常方便。

BOOST_TTI_HAS_MEMBER_FUNCTION是一个宏函数,用来生成一个type traits

具体例子如下,BOOST_TTI_HAS_MEMBER_FUNCTION(to_string)将生成一个has_member_function_to_string,用于判断类是否有to_string成员函数:

  • has_member_function_to_string<T, std::string>可用于判断类是否有std::string to_string()成员函数。
  • has_member_function_to_string<const T, std::string>判断类T是否有std::string to_string() const成员函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <boost/tti/has_member_function.hpp>
#include <string>
#include <iostream>

struct ClassWithToString {
std::string to_string() { return "with to_string"; }
};

struct ClassWithToStringConst {
std::string to_string() const { return "with to_string"; }
};

struct ClassWithoutToString {

};

BOOST_TTI_HAS_MEMBER_FUNCTION(to_string);

int main() {
std::cout << std::boolalpha
<< has_member_function_to_string<ClassWithToString, std::string>::value << std::endl
// true
<< has_member_function_to_string<ClassWithToStringConst, std::string>::value << std::endl
// false
<< has_member_function_to_string<const ClassWithToStringConst, std::string>::value << std::endl
// true
<< has_member_function_to_string<ClassWithoutToString, std::string>::value << std::endl;
// false
}

用这个可以实现很优雅的功能。比如对所有有std::string to_string() const成员函数的类,自动重载<<直接输出to_string()的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <boost/tti/has_member_function.hpp>
#include <string>
#include <iostream>

BOOST_TTI_HAS_MEMBER_FUNCTION(to_string)

template<class T>
std::enable_if<has_member_function_to_string<const T, std::string>::value, std::ostream&>::type
operator<<(std::ostream& out, const T& obj) {
out << obj.to_string();
}

struct ClassWithToString {
std::string to_string() const {
return "Class<ClassWithToString>";
}
}

int main() {
ClassWithToString obj;
std::cout << obj << std::endl;

return 0;
}

1
2
3
4
5
6
int dayofweek(int y, int m, int d)	/* 0 = Sunday */
{

static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
y -= m < 3;
return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}

假设定义基类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <string>
#include <iostream>

class Base {
public:
static int a;
std::string name = "base";
void print() {
std::cout << name << ": " << a << std::endl;
}

};

int Base::a = 1;

dll1中可以返回一个继承类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "../main/base.h"

class DLL1 : public Base {
public:
DLL1() {
name = "dll1";
a = 2;
print();
};
virtual void reset_a(const int& new_a) {
a = new_a;
print();
}
};

extern "C" _declspec(dllexport) Base* create();

#ifdef __cplusplus
extern "C" {
#endif

Base* create()
{

return new DLL1;
}

#ifdef __cplusplus
}
#endif

dll2中可以返回一个另一个继承类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "../main/base.h"

class DLL1 : public Base {
public:
DLL1() {
name = "dll2";
a = 3;
print();
};
};

extern "C" _declspec(dllexport) Base* create(); //

#ifdef __cplusplus
extern "C" {
#endif

Base* create()
{

return new DLL1;
}

#ifdef __cplusplus
}
#endif

在主程序中载入dll1dll2,相应输出的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include "base.h"
#include <windows.h>

typedef Base* (*Create)();

int main() {
auto dll1_module = LoadLibrary(L"dll1.dll");
Create dll1_create = (Create)GetProcAddress(static_cast<HMODULE>(dll1_module), "create");
Base* dll1_obj = dll1_create(); // dll1: 2

auto dll2_module = LoadLibrary(L"dll2.dll");
auto dll2_create = (Create)GetProcAddress(static_cast<HMODULE>(dll2_module), "create");
Base* dll2_obj = dll2_create(); // dll2: 3

dll1_obj->print(); // d111: 1
dll2_obj->print(); // dll2: 1

dll1_obj->a = 4;

dll1_obj->print(); // dll1: 4
dll2_obj->print(); // dll2: 4

dll1_obj->reset_a(5); // dll1: 5
dll1_obj->print(); // dll1: 4
dll2_obj->print(); // dll2: 4

dll2_obj->reset_a(6); // dll2: 6
dll1_obj->print(); // dll1: 4
dll2_obj->print(); // dll2: 4

getchar();

return 0;
}

这说明Base::a在程序中出现三个版本,主程序和两个dll文件中各一个。并且dll中调用的代码使用dll文件的版本,主文件中的代码则调用主文件中的版本。

这样让程序具有非常强的不确定性。因此,在dll中需特别留意静态变量。

资金理财和现金管理是指在正常的股票、基金、房产等中高风险投资以外,提高闲置资金的收益的理财方式。该类投资方式的主要原则有两条:

  • 无风险或承担极低的风险
  • 极高的流动性或确定的到期期限

现金管理的关注点

资金理财和其它投资关注的点一样,收益、风险和流动性。

收益

资金理财的对象几乎都是固定收益类型,收益也是固定收益,一般都用年化收益率来表示,然后按天计算收益。这里面要注意两点,一点是计息速度,另外一点是到期到账速度,这两点都会影响实际收益,是资金理财必须关注的地方。

起息速度

起息速度是指投入资金后,从哪一天开始可以获取收益。这在短期资金理财中最为重要。

对于一般的货币基金,下午三点前的起息日是T+1,三点后的起息日是T+2,而且这个T+1和T+2都是工作日概念。这意味着,如果在国庆节两天,比如9月29日下午三点半申购的货币基金,在国庆节期间将没有任何收益,白白浪费了将近8天的收益。如果碰到这种情况怎么办呢?这时候可以选择银行T+0理财,它的申购结束时点会晚一些,最晚能到晚上九点,即只要晚上九点前申购,当天就能有收益。

赎回到帐日

赎回到帐日是指资金到账的时间。由于从计息结束日到资金到帐日没有利息收入,因此赎回到账越慢,收益越低。

比如一般银行理财在募集期都没有利息,募集期比如三天。赎回T+3到账,又被扣掉三天。若是28天的理财,预期年化收益率为4%。那么该理财产品的实际年化收益率只有4%*28/(28+3+3)=3.2%,收益率下降了20%。

风险

资金理财最重要的是保本,风险承受能力极低。因此投入的方向必须是大型金融机构比如银行、基金公司或交易所场内的产品。其余产品可少量尝试但需严格控制投入比例和集中度。

对于金融机构发行的产品,大原则而言,保本的产品比不保本的产品风险低,大机构的产品比小机构的风险低,场内的比场外的风险低。

流动性

流动性是资金理财的核心。因为大部分资金理财品种的收益率都跟期限相关,相同期限的产品的收益率不会差异太大。所以资金理财最重要的是选择投资期限。

投资期限

投资期限是指产品的到期期限。资金理财产品有不定到期日和固定到期日两种。货币基金之类的产品都是不定到期日的,一般T+1就可以赎回(但需要注意起息速度和赎回速度)。而其它产品一般都有固定期限(计息期限),比如28天、91天等等,产品将根据该期限和年化收益率计算实际利息收益。

另外部分产品还需要注意赎回申请可能被延后,比如货币基金会出现巨赎 (指当天赎回的金额超过基金合同约定的比例),此时基金公司将有权拒绝赎回。一般在月末、季末、大型节假日比如春节、IPO打新前后最有可能出现巨赎风险,在这些时点需关注该类风险,如果资金需求,最好提前申请赎回。

注意起息日和到期到账速度

投资期限并不是全部。起息速度和赎回速度也会影响实际资金到账的速度。所以实际期限=产品计息期限+起息速度+赎回时间。

场外

对于实际的产品,可分为场外和场内两种。场外产品是指交易所以外,使用银行账户进行资金往来的产品。场内是指交易所内,使用证券账户进行投资的产品。

银行存款

银行存款是最简单的资金理财方式。

它的风险极低,正规银行的存款可认为是零风险。目前的存款保险已对五十万以内的存款全额担保,即使超过五十万的存款的风险也基本不需要担心,目前短期内银行的存款风险几乎为0。

收益也非常透明,国家对存款利率直接管制,定义了各期限的利率水平,虽然银行也有一些浮动的空间,但空间有限而且大家方向较为一致。银行存款的利率水平和期限严格正相关。

  • 流动性最好的活期存款没有任何流动性风险,但年化收益率不超过0.5%。
  • 再往上有七天通知存款,年化收益有1.5%左右。目前7天存款的流动性和活期存款是一样的,只不过在支取时,若不到7天的存续期,当期收益按照活期存款计算。因此7天通知存款,有活期存款的流动性,但利率搞了将近1%。
  • 其它的定期存款就不用多说,收益太低,不推荐。

总地说来,银行存款不是推荐的资金理财方式,现阶段有更好的方式。一般推荐只保留一周的生活必需费用放在银行账户里。而且银行账户建议启用7天自动理财,有更高的收益同时保留活期存款的流动性。这个在网上银行即可办理,办理一次终生有效(至少中信银行是这样的)。

货币基金

自从支付宝推出余额宝以来,货币基金已经是一个大家很熟悉的产品。但有些细节,在申购货币基金时需要注意。

货币基金的起息日和计息规则

货币基金最重要的规则是下午三点前申购T+1确认份额和计息(T+2可以看到利息),下午三点后申购则T+2才确认份额和计息。这里T+1和T+2都是指交易日的概念。这意味着周四下午三点后申购货币基金,下周一才计息,周五和周末两天没有收益。

货币基金的赎回

货币基金的普通赎回是T+1到账。货币基金的普通赎回的赎回风险极低,因为投资经理有一天时间用来调配资金。

普通赎回的赎回当天(包括后面连着的非工作日)将有收益。比如周五申请赎回,还可以享受周五、周六、周日的收益。但周一资金回账后就没有收益了。

但现在越来越多的货币基金支持T+0赎回。该赎回通常由基金管理公司垫资(一般和银行合作,银行提供流动性授信安排),所以一般都有额度限制,比如单个投资人不超过五万或者二十万。

T+0赎回就没有当天的利息收入了(否则可进行套利)。

银行T+0理财

普通的货币基金是T+1甚至T+2起息,一旦过了时点,可能浪费很多利息。比如国庆节前9月29日下午三点半有一笔闲置资金,申购货币基金将浪费一周多的收益。此时可以考虑银行T+0理财。

银行T+0理财最重要的一点是T+0起息,这意味着只要在当天规定时点之前申购,便能获得当天收益。这个时点一般都晚于普通货币基金的下午三点的时点。

但天下没有白吃的午餐,申购银行T+0理财有一些注意事项:

  • 银行T+0理财一般都有单笔申购下限,一般都是五万元。货币基金一块钱也可以申购。
  • 银行T+0理财当天申购也有截止时间,在截止时间之后申购也是T+1起息。所以申购前要注意截止时间。
  • 银行T+0理财一般都有额度限制,如果额度满了就不能申购。经常会出现还没到截止时间,就已经停止申购的情况。
  • 银行T+0理财的赎回风险比普通货币基金更高,因为它的资金来源都是短期的(它的收益相对较低,长期资金会选择收益更高的品种),导致其资金流动较大,在关键时点更容易出现流动性问题。

通常建议极短期的资金(比如一周以内)以及关键时点才考虑T+0理财。

银行理财产品

其它理财产品

场内

可用日和可取日

交易所逆回购

回购期限和利息计算方式
费用

交易所的货币基金

申购型货币基金
交易型货币基金

券商理财

现金宝
报价回购

场内和场外的转换

文本文件编码虽然有很多种,但从大类上可分为两大类,ANSI编码和UNICODE编码。这两大类编码方式的主要区别是支持的字符集不一样。前者一般只支持特定语言的特定字符集,后者则是大一统的字符集(理论上覆盖地球上所有用到的字符,以后扩展到外星语也未可知)。

ANSI编码: ASCII, GBK

ANSI编码只支持特定语言的特定字符集。最开始的编码只针对一个字节,共有0到255一共256个不同的字节状态:

  • 所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的 文字了。大家看到这样,都感觉很好,于是大家都把这个方案叫做 ANSI 的”Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。
  • 世界各地的都开始使用计算机,但是很多国家用的不是英文,他们用到的许多字母在ASCII中根本没有,为了也可以在计算机中保存他们的文字,他们决定采用127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称”扩展字符集”。

后来越来越多的语言用到计算机,尤其是中文,远远不止256个汉字,因此中国发展了一条独立的ASCI编码线:

  • 把那些127号之后的奇异符号们直接取消掉,并且规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符。这便是GB2312编码。
  • 后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
  • 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。

我们看到ASCII-GB2312-GBK-GB18030是向下兼容的编码线,其字符集为 “DBCS”(Double Byte Charecter Set 双字节字符集)。这个编码中,英文为一个字符,编码总是小于128,中文为两个字符,第一个字符的编码总是大于127,因此可以自动识别。

不同语言都定义了自己的ANSI编码,比如繁体中文是BIG5。

UNICODE编码和UTF-8

上面的ANSI编码可以处理单语言的问题。但遇到多种语言时,问题就来了。不同的ANSI编码,一个字节状态可能对应不同的字符(除了公共的ASCII部分)。从而无法显示不同语言的字符。此时的解决方案是定义一个大一统的字符集,包括地球上所有语言的字符,再对这个字符集定义编码方式。这个字符集就叫做UNICODE,这个编码方式是UTF-8(其实还有很多别的UNICODE编码方式,比如UTF-16,UTF-32等,但目前最常用的还是UTF-8)。

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。码位就是可以分配给字符的数字。目前UNICODE已经定了超过10万个字符,并且还在继续增加中。其中7万多个字符是汉字。

UTF-8则是最常用的UNICODE编码方式。它是不定长编码,并且兼容ASCII编码,因此英文还是一个字节,但中文要用三个字节来表示。所以,用UTF-8编码的中文文档要比GBK编码的文件大一半左右。

UTF-8的编码和UNICODE编码转换关系如下:

Unicode编码(十六进制)  UTF-8 字节流(二进制)
00000000 - 0000007F 0xxxxxxx
00000080 - 000007FF 110xxxxx 10xxxxxx
00000800 - 0000FFFF 1110xxxx 10xxxxxx 10xxxxxx
00010000 - 001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
00200000 - 03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
04000000 - 7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

该转换关系没啥用处。因为从UNICODE编码到其它的GBK编码没有简单的转换关系,只能查表实现。不过,这个转换关系可以用来快速计算一个UTF-8编码的字符串中含有几个字符。

编码问题为什么麻烦

编码本身并不复杂,说清楚了就那么几种。麻烦之处不在编码本身的复杂,而在于文件所用的编码方式不是明文规定,而是靠使用方去“猜”,猜错是常有之事。

这个猜也没有特别好的方法,只能一个一个编码方式的尝试。因为不同编码方式用到的码位不一样,如果发现无法识别的码位,便能否认这个编码。但也可能有意外,比如下面这个例子:

当你在 windows 的记事本里新建一个文件,输入”联通”两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵,有人说这就是联通之所以拼不过移动的原因。

其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。

当你新建一个文本文件时,记事本的编码默认是ANSI(代表系统默认编码,在中文系统中一般是GB系列编码), 如果你在ANSI的编码输入汉字,那么他实际就是GB系列的编码方式,在这种编码下,”联通”的内码是:

c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000

注意到了吗?第一二个字节、第三四个字节的起始部分的都是”110”和”10”,正好与UTF8规则里的两字节模板是一致的,

于是当我们再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了”00001 101010”,再把各位对齐,补上前导的0,就得到了”0000 0000 0110 1010”,不好意思,这是UNICODE的006A,也就是小写的字母”j”,而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有”联通”两个字的文件没有办法在记事本里正常显示的原因。

而如果你在”联通”之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。

另外某些软件不支持特定的编码,比如微软的VS2013便不支持不带签名的UTF-8编码,这让编码问题的兼容性问题尤其突出。

最近老遇到一个奇怪的问题。在VS 2013编译时,爆出很多警告:

warning C4819: The file contains a character that cannot be represented in the current code page (936). Save the file in Unicode format to prevent data loss

网上能找到一些方法禁止出现C4819的警告。但我这里还有一个事情是,随着警告还出现大量莫名其妙的错误,貌似VS 2013根本没有正确读取文本。

经过多次测试,我确定原因出现在我使用了utf-8 withou signature这种编码方式,如果改成gbkunicodeutf-8 with signature都不会出问题。那为什么会出现这个问题呢?vs 2013有一条设置auto detect encoding utf8 withou signature是自动勾选的,按道理应该没问题。可我这里就是不行。

现在还不知道怎么解决这个问题。要想保留utf8格式,只能加上文件签名,但这样会引发跨系统的移植问题。

后记:

把系统程序的语言改成英语(不是指界面),可以解决这个问题。但此时,程序将是UNICODE编码,需留意一下。更关键的是,系统里有些软件会变成乱码。所以一个方法是在编译时将系统程序语言变成英语,编译后再改回来。

由于UNICODE的优势,目前推荐都用UTF-8进行编码。

bash相关

查询文件所用编码

file命令

file可以查看文件的类型,包括编码:

$ file ~/.vimrc
/home/xxx/.vimrc: UTF-8 Unicode (with BOM) text

但根据测试,这个东西不是特别准,会出现误判情况。另外对于ANSI编码,无法区分到具体编码类型。

批量找出文件编码

下面这条语句可以查询当前目录下所有非UTF-8编码的文件:

find . -type f | xargs -I {} bash -c "iconv -f utf-8 -t utf-16 {} &>/dev/null || echo {}"

也可以指定文件类型,比如下面找出当前目录下所有非UTF-8编码的cpp文件:

find . -name *.cpp -type f | xargs -I {} bash -c "iconv -f utf-8 -t utf-16 {} &>/dev/null || echo {}"

iconv更换编码

iconv是Linux内置的编码转换的命令;

iconv -f from-encoding -t to-encoding inputfile -o outputfile

vim相关

~/.vimrc的编码置

在 Vim 中,有四个与编码有关的选项,它们是:fileencodings、fileencoding、encoding 和 termencoding。在实际使用中,任何一个选项出现错误,都会导致出现乱码。因此,每一个 Vim 用户都应该明确这四个选项的含义。

引用自:http://edyfox.codecarver.org/html/vim_fileencodings_detection.html

encoding

encodingVim内部使用的字符编码方式。当我们设置了encoding之后,Vim内部所有的buffer、寄存器、脚本中的字符串等,全都使用这个编码。Vim在工作的时候,如果编码方式与它的内部编码不一致,它会先把编码转换成内部编码。如果工作用的编码中含有无法转换为内部编码的字符,在这些字符就会丢失。因此,在选择Vim的内部编码的时候,一定要使用一种表现能力足够强的编码,以免影响正常工作。

由于encoding选项涉及到Vim中所有字符的内部表示,因此只能在Vim启动的时候设置一次。在Vim工作过程中修改encoding会造成非常多的问题。如果没有特别的理由,请始终将encoding设置为utf-8。为了避免在非UTF-8的系统如Windows下,菜单和系统提示出现乱码,可同时做这几项设置:

set encoding=utf-8
set langmenu=zh_CN.UTF-8
language message zh_CN.UTF-8
termencoding

termencoding 是Vim用于屏幕显示的编码,在显示的时候,Vim会把内部编码转换为屏幕编码,再用于输出。内部编码中含有无法转换为屏幕编码的字符时,该字符会变成问号,但不会影响对它的编辑操作。如果termencoding没有设置,则直接使用encoding不进行转换。

举个例子,当你在 Windows 下通过 telnet 登录 Linux 工作站时,由于 Windows 的 telnet 是 GBK 编码的,而 Linux 下使用 UTF-8 编码,你在 telnet 下的 Vim 中就会乱码。此时有两种消除乱码的方式:一是把 Vim 的 encoding 改为 gbk,另一种方法是保持 encoding 为 utf-8,把 termencoding 改为 gbk,让 Vim 在显示的时候转码。显然,使用前一种方法时,如果遇到编辑的文件中含有 GBK 无法表示的字符时,这些字符就会丢失。但如果使用后一种方法,虽然由于终端所限,这些字符无法显示,但在编辑过程中这些字符是不会丢失的。

对于图形界面下的 GVim,它的显示不依赖 TERM,因此 termencoding 对于它没有意义。在 GTK2 下的 GVim 中,termencoding 永远是 utf-8,并且不能修改。而 Windows 下的 GVim 则忽略 termencoding 的存在。

fileencoding

当 Vim 从磁盘上读取文件的时候,会对文件的编码进行探测。如果文件的编码方式和 Vim 的内部编码方式不同,Vim 就会对编码进行转换。转换完毕后,Vim 会将 fileencoding 选项设置为文件的编码。当 Vim 存盘的时候,如果 encoding 和 fileencoding 不一样,Vim 就会进行编码转换。因此,通过打开文件后设置 fileencoding,我们可以将文件由一种编码转换为另一种编码。但是,由前面的介绍可以看出,fileencoding 是在打开文件的时候,由 Vim 进行探测后自动设置的。因此,如果出现乱码,我们无法通过在打开文件后重新设置 fileencoding 来纠正乱码。

fileencodings

编码的自动识别是通过设置 fileencodings 实现的,注意是复数形式。fileencodings 是一个用逗号分隔的列表,列表中的每一项是一种编码的名称。当我们打开文件的时候,VIM 按顺序使用 fileencodings 中的编码进行尝试解码,如果成功的话,就使用该编码方式进行解码,并将 fileencoding 设置为这个值,如果失败的话,就继续试验下一个编码。

因此,我们在设置 fileencodings 的时候,一定要把要求严格的、当文件不是这个编码的时候更容易出现解码失败的编码方式放在前面,把宽松的编码方式放在后面。

例如,latin1 是一种非常宽松的编码方式,任何一种编码方式得到的文本,用 latin1 进行解码,都不会发生解码失败——当然,解码得到的结果自然也就是理所当然的“乱码”。因此,如果你把 latin1 放到了 fileencodings 的第一位的话,打开任何中文文件都是乱码也就是理所当然的了。

推荐设置汇总
set fileencodings=ucs-bom,utf-8,gb2312,gb18030,gbk,cp936,latin1
set termencoding=utf-8
set encoding=utf-8
set enc=utf-8
set fenc=utf-8

指定编码打开文件

上面vim设置中,可以设定vim尝试编码的顺序,大部分情况下不会出问题。但偶然情况是不可避免的,此时需直接指定vim打开文件所用的编码。

理论上而言,可以在打开文件时指定编码:

vim file ++enc=utf-8

但这个不一定有效。如果无效,可以在打开文件后再指定编码:

:e ++enc=utf-8

更换单个文件的编码

要更换单个文件的编码,只用先设置再保存即可:

:set fileencoding=utf-8
:w

vim批量更换编码

要批量更改文件编码,可使用vimargdo功能:

:args *.h *.cpp
:argdo set fenc=utf-8 | update

这个命令将当前目录下所有.h.cpp文件都保存为utf-8文件。这个有个缺点是不会修改子目录下的文件。要想将当前目录及所有子目录下的文件也修改掉,只能把前一句改成

:args *.h *.cpp */*.h */*.cpp

如果要修改孙目录的文件,就需要加更多的*/*/*.h

股指期货被认为今年股灾的元凶之一,从7月份以来就不断受到非议,也招致了历史上最严厉的监管。具体的监管措施有:大幅度提高保证金率、直接限制交易规模、大幅提高交易费率、大幅缩窄套保适用范围。具体总结如下:

  1. 套利投机的保证金为40%,套保保证金为20%,分别为股灾前的4倍和2倍。
  2. 套利投机在单个品种上每日开仓限额10手。
  3. 交易费万1.15,平今万21,分别约为原来的4倍和80倍。
  4. 只能对指数成份股使用套保编码,非成份股对冲只能使用套利或投机,受到更严格的限制。

话说在这种严格的限制下,股指期货几乎接近于死亡。这么低的手数限制和对套保如此严格的限制,机构几乎无法进入,股指期货愈发只能成为散户赌博的工具。

大幅度提高保证金率

经过多番调整,套利和投机的保证金比例从原来的10%上涨到40%,上涨3倍;套保的保证金比例从10%上涨到20%,上涨1倍。

  1. 自2015年7月8日(星期三)结算时起,中证500股指期货各合约的卖出持仓交易保证金,由目前合约价值的10%提高到20%(套期保值持仓除外);自2015年7月9日(星期四)结算时起,中证500股指期货各合约的卖出持仓交易保证金进一步提高到合约价值的30%(套期保值持仓除外)。

  2. 自2015年8月26日(星期三)结算时起,沪深300和上证50股指期货各合约的非套期保值持仓的交易保证金,由目前合约价值的10%提高到12%,中证500股指期货各合约的非套期保值持仓的买入持仓交易保证金,由目前合约价值的10%提高到12%;

  3. 自2015年8月27日(星期四)结算时起,沪深300和上证50股指期货各合约的非套期保值持仓的交易保证金进一步提高到合约价值的15%,中证500股指期货各合约的非套期保值持仓的买入持仓交易保证金进一步提高到15%;

  4. 自2015年8月28日(星期五)结算时起,沪深300和上证50股指期货各合约的非套期保值持仓的交易保证金再进一步提高到合约价值的20%,中证500股指期货各合约的非套期保值持仓的买入持仓交易保证金再进一步提高到20%。

  5. 自2015年8月31日(星期一)结算时起,沪深300和上证50股指期货各合约的非套期保值持仓的交易保证金,由目前合约价值的20%提高到30%,中证500股指期货各合约的非套期保值持仓的买入持仓交易保证金,由目前合约价值的20%提高到30%。

  6. 自2015年9月7日(星期一)结算时起,沪深300、上证50、中证500股指期货各合约的非套期保值持仓的交易保证金标准,由目前合约价值的30%提高到40%;沪深300、上证50、中证500股指期货各合约的套期保值持仓的交易保证金标准,由目前合约价值的10%提高到20%。

直接限制交易规模

经过多次监管升级,目前股指期货单个品种上每天只能开仓10手(套保除外)。

  1. 自2015年8月26日起,将客户在单个股指期货产品、单日开仓交易量超过600手的认定为“日内开仓交易量较大”的异常交易行为,进一步加强异常交易行为管理。日内开仓交易量是指客户单日在单个产品所有合约上的买开仓数量与卖开仓数量之和。套期保值交易的开仓数量不受此限。

  2. 2015年8月31日起,对沪深300、上证50、中证500股指期货客户在单个产品、单日开仓交易量超过100手的认定为“日内开仓交易量较大”的异常交易行为。日内开仓交易量是指客户单日在单个产品所有合约上的买开仓数量与卖开仓数量之和。套期保值交易的开仓数量不受此限。

  3. 9月2日,调整股指期货日内开仓限制标准。中金所决定,自2015年9月7日起,沪深300、上证50、中证500股指期货客户在单个产品、单日开仓交易量超过10手的构成“日内开仓交易量较大”的异常交易行为。日内开仓交易量是指客户单日在单个产品所有合约上的买开仓数量与卖开仓数量之和。套期保值交易的开仓数量不受此限。

大幅提高交易费率

经过多次调整后,最新的交易费率为万1.15,平今手续费万23。这使得平今手续费非常昂贵,非要平今还不如反方向新开仓,等到第二天把对锁仓都平掉。

  1. 经研究决定,自2015年8月3日起,我所调整股指期货手续费标准。具体调整如下:手续费包括交易手续费和申报费两部分,交易手续费标准调整为成交金额的万分之零点二三;申报费根据客户沪深300、上证50和中证500股指期货各合约的申报数量收取,每笔申报费为一元。会员应当根据客户申报量向客户收取。申报是指买入、卖出及撤销委托。

  2. 经研究决定,自2015年8月26日(星期三)起,沪深300、上证50、中证500股指期货各合约平今仓交易手续费标准调整为成交金额的万分之一点一五。

  3. 经研究决定,自2015年9月7日(星期一)起,沪深300、上证50、中证500股指期货各合约平今仓交易手续费标准调整为成交金额的万分之二十三。

缩窄套保适用范围

上面的监管规定中,套保的保证金比例低一半,并且没有交易手数限制。很多人估计动了心思,比如能不能多申请套保额度来用呢?中金所早就防了你们这种“刁民”了。

套期保值客户在套保编码下持有的某一品种的期货空头合约价值,不得超过该客户持有的 该期货品种对应的标的指数成分股及对应的 ETF 基金的市值。任何期货多头、该期货品种 对应标的成份股以外的股票、以及其他权益类产品,都不被认定为套期保值的现货部分。

最近在量化私募圈一个比较大的新闻是东海恒信被罚款7.3亿元。大家对它的手法特别好奇。最开始的新闻里提到

东海恒信控制使用“千石资本-东海恒信1期”等12个账户于2015年6月18日至7月30日,在自己实际控制的账户之间进行180ETF交易,影响180ETF交易量,变相进行180ETF与相应成分股日内回转交易套利,非法获利184,291,170.59元。

后来新闻里里提到一些信息:

东海恒信自述主要投资品种为ETF,主攻期现套利、量化阿尔法、程序化交易等,在过去的几年中变化惊人,从驻扎4平方米大户室到了此后2000多平方米的自有办公室。

从零起步,东海恒信目前管理着15只基金产品,管理规模逾30亿元。据东海恒信介绍,它在2015年1月至5月的成交额近6000亿元。

6000亿的成交量,平均每天约60亿,管理规模30亿,也就是平均每天都要一买一卖。这么大交易量说明其中至少有部分交易是T+0的。结合到上面提到的回转交易套利,很有可能是在现货和ETF之间套利,比如买入现货,合成为ETF卖出,或者买入ETF,拆分为股票卖出。不过单纯的ETF套利没这么多机会。

今天,证监会公布了更多信息,提供了一些有趣的细节:

今年6月18日至7月30日,东海恒信控制使用“千石资本—东海恒信1期”等12个账户,利用公司具有批量、快速下单功能的交易系统,在实际控制的账户内进行180ETF交易,影响180ETF交易量,变相进行180ETF与相应成份股日内回转交易套利,交易金额数百亿元,非法获利超过1.8亿元。在上述ETF日内回转套利交易流程中,东海恒信通过“买入一篮子成份股票→申购成ETF→ETF对倒→赎回ETF→卖出一揽子股票”操作方式,变相实现ETF与成份股的日内回转,从而最终锁定套利利润。

这说明东海恒信做的并不是套利,而是利用ETF变相实现了指数的T+0交易,其盈利来源自对指数短期走势的精准判断。ETF在其中扮演了非常关键的角色,实现了指数的T+0。而且优势明显:

  • 相比于citadel之前通过融券来做T+0,成本更低:融券成本每天约万3,而ETF实现的成本可以低于万2;
  • 规模更大,ETF的容量几乎是无限的,而融券则受制于券源的数量;i
  • 操作更灵活,ETF有机会就做,没机会不用多付成本,而融券交易必须锁券,不用也得掏钱(否则要用的时候不一定有券可用)。

当然ETF只能做多先买再卖,不像融券T+0可以做空先卖再买。另外一个更大的问题是这里面需要对倒交易(单靠ETF自然的交易量无法支撑这么大规模的交易),这里面有很大的合规瑕疵,这次东海恒信被罚,这个应该是最直接的原因。

不过话又说回来,如果只是为了交易换手换取T+0交易的资格,而不是为了操纵价格,比如每次都在当前盘口进行买卖,能说是操作市场吗?如果这种策略被罚款,很多量化交易都有违法的嫌疑,量化私募该何去何从呢?

基本结论:很多互联网金融创业公司的商业模式有重大缺陷,但对股东不一定是件坏事。

今天在微博上看到有网友说:

互联网金融对居民财富的影响巨大,它干掉了传统资金融行业的理财门槛,允许所有人配置同样的金融产品进行财富增值。在此之前,百万财富的人可以配房产、信托,普通中产只能配存款,两者收益率天上地下。互联网金融让不同财富起点的人拥有同样可能的财富增值速度。

该网友是R方面的技术大牛,目前也在从事互联网金融方面的创业工作。他的看法也代表了蛮多互联网金融从事者的看法。

可是我对这个观点深表怀疑:

互联网金融的好处是让普通大众投资更方便,而不是突破投资准入条件。后者非常危险,现在很多互联网金融的公司都把金融想得太简单了。

现在很多互联网金融的从事者,理论上都在做产品销售。这一块的确是互联网的强项,只要掌握巨大的用户量,再加上快捷的支付方式,销售的边际成本几乎为0。支付宝卖货币基金,轻松做出一个中国规模最大的基金公司。

但现实情况并不完全如此。如果单单只是渠道的创新,成功者只有阿里腾讯等寥寥数家,解释不了现在如此多的互联网金融的创业公司。

如果仔细分析这些小型的创业公司的商业逻辑,就会发现追踪到底,都归类到一个灰色(其实不是灰色,而是明确的非法)领域——资金池。

资金池的意思是,公司通过各种渠道收集资金,放到资金池内,再投资到单个或者各个项目。它的好处是,首先从资金来源上来说,可以用短期资金支持长期项目,而短期资金更容易获取,成本更低;其次从资金投向上来说,可以投向多个项目,有个好听点的说法是,可以分散风险。

通过资产池,投资者的资金就可以打通原来受准入限制的领域。比如,原来一百万才能投资的信托项目,现在可以大家一起凑,1元钱也可以投信托项目。

这个绝对不是业务创新。连监管创新都谈不上。监管层分分钟可以取消这个限制,为什么没取消?因为固定收益类资产虽然普遍被认为风险低于股票,但它并不是没有风险。固定收益类投资的收益和风险不对称。你也许每年有10%的收益,远高于存款利率,但是,只要10年里有一年违约,你就血本无归。

所以现在很多互联网金融公司都是在做风险套利。以8%甚至10%以上的成本从投资者手中筹措资金,再以15%甚至30%以上的利率贷出去。互联网公司自己也知道这有很大的风险。但是,但这里有个关键的地方,赚的钱是自己的(还不用本金,无限倍杠杆),亏的大钱是客户的。只要存活一年,就能赚一年的钱。万一哪天资金链断裂或者借出去的钱无法收回,大不了公司倒闭,之前赚的是不会吐出来的。这便是绝大多数互联网金融公司的商业逻辑。从这个角度上说,这些互联网金融的公司倒不是一个坏主意,对于公司的股东而言。

这里是拿到一台Linux主机后,需要什么的笔记。我的样本是一台阿里云主机,单CPU,2G内存,系统是CentOS 7。

管理Linux的用户和用户权限

Linux目前更多作为服务器使用,也就是天生就更需要更完善的用户和用户权限管理。这里在阿里云的机器上的一些操作备忘录。这台机器安装的是CentOS 7。

root用户

root用户是机器的超级管理员,它有一切权限。root用户是在安装机器时设立的。

root权限虽然很高,但平时如果总是以root身份登陆,失误操作将有巨大的破坏力,并且不会有明确的提示。所以登陆新服务器后的第一件工作是先生成一个普通帐户,平时都用普通帐户登录干活。

添加新用户和设置密码

useradd用来添加一个新用户,passwd修改密码。

useradd zzhang
passwd zzhang

这个都以root权限运行。

赋予普通用户特别权限(sudo)

通过sudo,可以赋予普通用户特别权限,并且要求在使用特别权限时输入sodu并再次输入密码进行确认。这么做可以分配一部分管理权限到普通用户,避免滥用root权限,又通过输入上的不同避免误操作。

配置方法是修改/etc/sudoers文件。比如我若想将zzhang用户分配sudo权限,可以添加一行:

zzhang ALL=(ALL) ALL

压缩和解压缩

Linux最常见的压缩文件的格式为.tar.gz。这里gz表示为gzip压缩过的,tar表示打包文件,即压缩文件里包括多个文件。

所以压缩(多个)文件可以分为两步,第一步是将多个文件打包为一个tar文件,第二步为将tar文件压缩为gz文件。tar命令可以在一条命令里同时做这两件事情。

解压缩也可分为相反的两步。

tar打包和压缩的命令为:

tar -cvf test.tar test/* # 只打包不压缩
tar -zcvf test.tar.gz test/* # 打包且压缩

解压缩的命令为:

tar -zxvf test.tar.gz # 解压缩并且解包

这几个参数虽然多,但也不难记。其中c表示打包,z表示压缩和解压缩(看跟c还是跟x配合),x表示解包。v用来显示过程(即输出运行状态,这个可以不要,没什么用)。

f表示后面指定了目标压缩包的文件名。注意这个参数比较特殊,一定要放在最后,前面几个参数可以随意。因此,-cvzf是可以的,但-fzxv不行。

vim配置(.vimrc和插件)

vim是linux最常用的编辑器之一,和emacs并列。可是我看到CentOS发行版默认安装vim,但没有装emacs,我估计现在vim的流行度已经超过了emacs。

vim的配置最重要的是两个,一个是安装插件,另一个是配置文件~/.vimrc。但自从出了一个牛逼的管理插件的插件Vundle后,这两个合二为一。我们只需要把配置文件写好即可,安装哪些插件可以放到配置文件中,这大大方便了vim环境的备份和部署。

第一步是安装vundle:

git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vundle

然后添加配置文件:

set nocompatible               " be iMproved
filetype off                   " required!

set rtp+=~/.vim/bundle/vundle/
call vundle#rc()

" let Vundle manage Vundle
" required!
Bundle 'gmarik/vundle'

" My Bundles here:
"
" original repos on github
Bundle 'tpope/vim-fugitive'
Bundle 'Lokaltog/vim-easymotion'
Bundle 'rstacruz/sparkup', {'rtp': 'vim/'}
Bundle 'tpope/vim-rails.git'
" vim-scripts repos
Bundle 'L9'
Bundle 'FuzzyFinder'
" non github repos
Bundle 'git://git.wincent.com/command-t.git'
" ...

filetype plugin indent on     " required!
"
" Brief help  -- 此处后面都是vundle的使用命令
" :BundleList          - list configured bundles
" :BundleInstall(!)    - install(update) bundles
" :BundleSearch(!) foo - search(or refresh cache first) for foo
" :BundleClean(!)      - confirm(or auto-approve) removal of unused bundles
"
" see :h vundle for more details or wiki for FAQ
" NOTE: comments after Bundle command are not allowed..

然后输入vim,再执行:BundleInstall便会安装所有的插件。

配置npm的淘宝镜像

境内直接用npm特别慢,但有几个npm的镜像可以用,它们与原版几乎是同步的(延迟时间在分钟级别)。有好几种方法使用这些镜像服务。不过下面这种方法对系统改变最小,删除也容易,并且同时保留使用境外原版的方式。

alias cnpm="npm --registry=http://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://dist.cnpmjs.org \
--userconfig=$HOME/.cnpmrc"

# 把上面的alias写到.bashrc脚本,以后每次都可以用
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=registry.npm.taobao.org \
  --cache=$HOME/.npm/.cache/cnpm \
  --disturl=http://dist.cnpmjs.org \
  --userconfig=$HOME/.cnpmrc"' >> ~/.bashrc && source ~/.bashrc

配置之后,用cnpm获取镜像,nmp仍是境外原版。

GIT版本控制

git已经是大多数Linux的标配版本控制软件,无需单独安装。

GIT免输入密码

下面的命令会让git缓存密码,10个小时以内不用重复输入,这样大约一天输入一次就够了。后面的timeout可以改成其它时长(单位秒)。这个只对Linux系统有效。

git config credential.helper 'cache --timeout=36000'

如果不在乎保存了明文密码,还有一个更简单的方式,在个人目录下建立.git-credentials文件,添加对应的账户和密码:

vim ~/.git-credentials
https://{username}:{password}@github.com

这个在win系统下同样有效,这时候个人目录是系统盘下的个人文件夹。

LD在链接生成目标文件时,会从左到有扫描输入的依赖库,当依赖库之间也有依赖关系时,必须将”依赖别人的库”放在“被别人依赖的库”的前面。否则会链接失败!失败的症状有:

  • 如果编译目标是可执行文件,将直接报链接错误,会出来一大堆函数定义找不到的错误。
  • 如果编译目标是动态链接库,则会在运行时,才爆出无法找到函数定义的错误。这个错误更要命。

举例: 有三个库a.ab.ac.a。其中b.a依赖了a.ac.aa.ac.a相互不依赖,而target依赖了a.ab.a。则

g++ -o target target.cpp a.a b.a c.a // 成功,
g++ -o target target.cpp a.a c.a b.a // 失败, 因为c.a应该在b.a后面

为什么会这样,这里的解释比较清楚:

在符号解析(symbol resolution)阶段,链接器按照所有目标文件和库文件出现在命令行中的顺序从左至右依次扫描它们,在此期间它要维护若干个集合:

  1. 集合E是将被合并到一起组成可执行文件的所有目标文件集合;
  2. 集合D是所有之前已被加入E的目标文件定义的符号集合;
  3. 集合U是未解析符号(unresolved symbols,即那些被E中目标文件引用过但在D中还不存在的符号)的集合。

一开始,E、D、U都是空的。

  • 对命令行中的每一个输入文件f,链接器确定它是目标文件还是库文件,如果它是目标文件,就把f加入到E,并把f中未解析的符号和已定义的符号分别加入到U、D集合中,然后处理下一个输入文件。
  • 如果f是一个库文件,链接器会尝试把U中的所有未解析符号与f中各目标模块定义的符号进行匹配。如果某个目标模块m定义了一个U中的未解析符号,那么就把m加入到E中,并把m中未解析的符号和已定义的符号分别加入到U、D集合中。不断地对f中的所有目标模块重复这个过程直至到达一个不动点(fixed point),此时U和D不再变化。而那些未加入到E中的f里的目标模块就被简单地丢弃,链接器继续处理下一输入文件。
  • 当扫描完所有输入文件时如果U非空或者有同名的符号被多次加入D,链接器报告错误信息并退出。否则,它把E中的所有目标文件合并在一起生成可执行文件。

上述规则针对的是Unix平台链接器,而VC(至少VC6.0)linker则有相当的不同: 它首先依次处理命令行中出现的所有目标文件,然后依照顺序不停地扫描所有的库文件,直至U为空或者某遍(从头到尾依次把所有的库文件扫描完称为一遍)扫描过程中U、D无任何变化时结束扫描,此刻再根据U是否为空以及是否有同名符号重复加入D来决定是出错退出还是生成可执行文件。很明显Unix链接器对输入文件在命令行中出现的顺序十分敏感,而VC的算法则可最大限度地减少文件顺序对链接的影响。

git现在是最流行的版本控制软件,大多数linux发行版已经默认安装,github等托管网站也越来越流行。每个程序员都需要学习git。

git最权威的教程是git pro。这里简单记下笔记,目的只是入门,介绍一些最简单和常用的操作命令。

git的基本工作模式

在git之前,最流行的版本控制软件是svn。svn非常简单易用,基本命令就三条:checkoutupdatecommit。svn只有两个区域,一个是服务器版本库,一个是本地工作区。checkout用来在最开始下载服务器的代码库,update用来获取服务器端的更新(来源于协助者),commit用来提交自己本地工作区的更新。

git则要复杂一些,这个复杂性大大增强了git的功能,这个暂且不谈。简而言之,对git用户而言存在四个区域,服务器版本库、本地版本库、暂存区和本地工作区。这比svn要多两个。这也让git的灵活性大大增加。最大的好处是,svn每次提交都必须联网,但git可以提交到本地版本库,等待联网时再同步到服务器版本库。

获取代码库

使用git的第一步是下载服务器版本库到本地,类似于svn的checkout。要下载,需要先获得服务器的地址,现在很多项目都采取网络托管,流行的有githubgitcafeoschina。如果想自己成立一个新项目,可以去这几个地方,其中oschina可以免费建私有项目,githubgitcafe建私有项目是收费的,但公开项目仍然免费。

假设我们有一个服务器版本库的地址http://xxx.com/yyy/zzz,下面命令便能建立一个本地版本库开始工作:

git clone http://xxx.com/yyy/zzz

它会在当前目录下新建zzz目录,目录下是工作文件。除了正常代码文件外,还有一个.git的文件夹,里面便保存了版本信息。我们一般都不用理会它。

我们也可以指定下载的文件夹

git clone http://xxx.com/yyy/zzz abc

此处abc可以是相对位置,也可以是绝对位置。如果abc不存在,git将新建该文件夹。abc的末尾可以带斜杠也可以不带。

如果想直接下载到当前文件夹,可以用.表示:

git clone http://xxx.com/yyy/zzz .

clone的过程中,目标文件夹一定要是空的。如果已经有一些本地文件需要放入版本库,可以先clone到另外一个文件夹,再复制本地文件到版本库,再把版本库挪到当前文件夹。

比如当前目录已经有一些文件,我们可以这么做:

git clone http://xxx.com/yyy/zzz ../tmp # 假设../tmp文件夹不存在或是空的
cp ./. ../tmp/ -ra
cp ../tmp/. ./ -ra
rm ../tmp -rf

它的一个基本思路是git的工作目录其实是可以移来移去。

提交自己的代码

现在程序员需在下载到本地的代码上开始自己的编程工作,然后修改了一些代码,新增了几个文件等等。接下来的操作和svn就有很大区别了。

在本地,git有三个区域,一个是工作区域(就是你不断修改代码的地方),一个是暂存区域,一个是本地版本库区域。对于一个已经纳入版本库的文件,根据处于位置的不同,就有三种状态,一种是未暂存,一种是已暂存未提交,第三种是已提交。

如果新增一个文件,其第一步是将其纳入版本库。把文件放入暂存区,就是纳入到版本库。

当文件提交到本地版本库后,最终还需要同步到网络共享服务器上。所以一个文件其实有四个状态(svn则只有两个状态)。一个修改的文件,需要经过三步,才能被其它共享者看到。这三步分别是:addcommitpush

status查看状态

使用git status命令可以看到当前文档的状态。包括

  • 目前本地版本库还有几个提交未同步到网络版本库,即ahead of 'origin/master' by 1 comment即目前还有一个本地提交未同步到网络共享版本库。
  • 未提交的修改:Changes to be committed
  • 未暂存的修改:Changes not stagged for commit
  • 未纳入新版本的修改:Untracked files

git status显示的信息示例如下:

# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#   (use "git push" to publish your local commits)
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:  xxx/abc
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:  yyy/edf 
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       zzz/ijk

有一些中间文件,比如C++编译产生的.o文件,不用纳入到版本控制中,但它总是会显示在此处的Untraced files列表。通过编辑.gitignore,可以在此列表自动排除掉指定文件:

# ./.gitignore
*.o

.gitignore文件也支持通配符,可以排除特定文件夹和特定文件类型。

.gitignore文件是递归调用的。当查看一个文件是否该忽略时,直接查找当前目录的.gitignore,若没有则往上查询,直到根目录为止。因此可以在不同文件夹下放置不同的.gitignore文件。

add添加文件到暂存区

无论是新建的文件,还是修改过的文件,都可以通过add命令将其添加到暂存区:

git add path/to/file

add已支持通配符:

git add path/to/*

注意上面这个会把所有path/to/目录下所有文件都加入暂存区。如果该目录下有.gitignore要求忽略的文件,该提交会出错。此时有两种方法,一种方法是增加-f参数:

git add path/to/* -f

另一种方法是不要用通配符,只用目录,此时.gitignore要求忽略的文件会被自动忽略:

git add path/to/ 

参数A可以把删除行为也提交到暂存区:

rm test.c
git add -A . # test.c的删除也被更新到暂存区

add会自动忽略.gitignore里指定忽略的文件。要想添加已要求指定忽略的文件,需添加-f参数:

git add test.o # not works because *.o is ignored in .gitignore
git add -f test.o # this works

另外一个需注意的地方是,git只针对文件,不针对目录。所以无法添加空目录到版本库。如果实在要的话怎么办呢?可以在该目录下添加一个空的.gitignore文件,再把这个文件添加到版本库。这样这个空目录就能添加到版本库了。当需删除该目录时,在版本库里删除该.gitignore文件即可。

如果有多个空目录,可以用下面bash命令同时添加.gitignore文件:

$ find . \( -type d -empty \) -and \( -not -regex ./\.git.* \) -exec touch {}/.gitignore \;

commit提交到本地存档

commit将暂存档里的文件保存到本地版本库。在commit前,可以运行多次add命令。参数-m用来指定提交的参考信息。

git commit -m 'bla bla...'

如果提交时无需做特别的选择,addcommit可合二为一,在commit时指定参数-a,可以自动将所有的修改添加到暂存档,同时提交:

git commit -a -m 'bla bla...'
commit --amend修改最后一次提交

有时候提交之后发现有几个文件没有贴上,commit --amend给了一次后悔的机会,它将把当前暂存区的文件和上次提交合并再次提交:

git commit -m 'init commit'
git add forgotten_files
git commit --amend

提交之后没有改动直接运行commit --amend则有机会重新编辑提交说明。

push同步到网络版本库

程序员可以无限制地通过commit向本地版本库提交更新,但此时其它人还看不到这些更新。这时候可以通过push将本地版本库更新到网络共享版本库。

git push

pull更新本地版本库

当其它程序员通过push更新了网络共享版本,这时候我们应该把他们的更新下载下来,并在其基础上继续接下来的工作。pull命令便是完成此事:

git pull

根据情况不同,会出现几种不同的情况。

  • 如果本地还没有任何提交,并且网络上的更新的文件在本地没有被更新,这时候网络版本直接下载到本地版本。
  • 如果本地还有未提交的修改,并且同一文件被其它人修改并更新到了网络共享版本库,这时候会出错。这时候需先把当前工作区或暂存区的修改提交,然后再次pull
  • 如果本地已经有提交,同时和网络上的更新修改了同样的文件。如果这两方面的修改没有冲突(比如修改不同文件,或者修改同一文件的不同位置),git将自动进行合并,并且生成一个新的提交(到本地版本库)。
  • 如果这两方面的修改有冲突(一般只修改了同一文件的相同位置),git将合并文件。

合并的文件一般长下面这样:

<<<<<<< HEAD
ver 1
=======
ver 2
>>>>>>> HASHIDFORLASTCOMMIT

此时用户需自行删除一些行,然后重新addcommit

回退到历史版本

版本控制的一个最大的功能(尤其是对于单个开发者的项目)便是保存历史修改记录,以便随时可以撤销修改回退到历史版本。由于git的文件有多个状态,所以回退也有几种情况:

回退到上次暂存状态

这个是指在把文件放到暂存区之后,又修改了文件,这时候想把文件回退到暂存区的状态,此时可以用checkout的命令:

git checkout -- path/to/file.post

回退到上次提交的状态

reset可以将文件恢复到上次提交的状态:

git reset HEAD 

取消本地所有修改和提交

git reset可以取消本地所有修改和提交:

git reset --hard origin/master

禁止函数指针和对象指针(包括void)的转换

出现的一个场景是将函数指针用void

warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object [-Wpedantic]

这个警告只有在gcc编译器并且开启pedantic编译选项才会出来。vs不会有提示。为避免此警告,可以在代码前添加__extension__。为保证可移植性,最佳方式是:

#ifdef __GNUC__
__extension__
#endif
the_statement_that_includes_the_cast;

这个是理论上是符合C++标准。解释如下:

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.

_WIN32_WINNT未被定义

vs编译项目有时候会有下面这一条警告:

_WIN32_WINNT not defined. Defaulting to _WIN32_WINNT_MAXVER (see WinSDKVer.h)
Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:
- add -D_WIN32_WINNT=0x0501 to the compiler command line; or
- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.
Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).

这个警告的原因是程序中用到了windows的API,程序在编译时需确定API的版本。如果有确定的版本,就用确定的版本。有时候实际上没有用到特别的API,此时可以简单在头文件或stdafx.h中指定_WIN32_WINNT=0x0501,也可以在编译选项中添加-D_WIN32_WINNT=0x0501

C4996(_CRT_SECURE_NO_WARNINGS)错误

很多人推荐大学的非专业的C++学习者直接用vc6而不是vs,其中一个很大的原因是vs的配置项太多,非专业学习者会比较迷惑。这个便是其中的一个。_CRT_SECURE_NO_WARNINGS是一个VS新手面临的最常见同时又难以理解的错误。这个错误主要原因是,VS以安全为由,禁止了某些不安全的C++函数。常见的错误信息如下:

error C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

理论上而言,在代码文件的最前端(或者stdafx.h文件中,若有)直接添加宏可以抑制该警告提示:

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif

或者添加下面这一行:

#pragma  warning(disable:4996)

但这种方法并不一定总是能行,原因不明。所以可以采取下面两种方法:

一、右击工程 - 属性 - 配置属性 - C/C++ - 命令行,命令行增加/D _CRT_SECURE_NO_WARNINGS

二、在Project properties->Configuration Properties->C/C++->Preprocessor->Preprocessor里添加_CRT_SECURE_NO_WARNING

要注意,VS在debugrelease两个编译模式下分别使用两套配置,这两套配置下都需要修改。注意第二种方法里,两种编译模式的预定义量(Preprocessor)的默认值不一样,不能简单复制粘贴给弄成一样的。否则编译时会出现非预期的结果。

最近在写项目时,发现一个非预期的结果。最后归结到程序用到的__FILE__上。一句话总结便是,__FILE__的结果是不可预期的。

项目时,我用__FILE__获取当前文件的位置,再通过相对路径获取一个配置文件的地址来读取。最后发现,在debug模式下可以正确读取到配置文件,但在release程序中找不到该配置文件。输出文件地址可以看到,在debug模式下,__FILE__返回的是该文件的全路径,即D:\path\file.cpp这样的。但在release编译时,__FILE__返回的只有文件名,这样后续读取配置文件便找不到文件。

如果看官方的定义,__FILE__的确只确保返回文件名。还有一种说法是,__FILE__返回的是编译时该文件的输入的地址。比如当前文件是D:\path1\path2\file.cpp

  • 如果编译命令为 gcc file.cpp,这时候只返回file.cpp
  • 如果编译命令为gcc path2\file.cpp,返回path2\file.cpp
  • 如果编译命令为gcc D:\path1\path2\file.cpp,返回D:\path1\path2\file.cpp

这样的结果也是合理的。因为__FILE__是编译器解释的一个宏,它自己也不会自动获取文件的全路径,只能获得输入时的部分路径信息。现在我不确定为何VS在不同编译模式下会给出不同的结果。无论如何,鉴于__FILE__的不稳定性,它不能用来去指定文件的位置。

编译源代码安装

通过这种方式安装的总是最新版本:

#!/bin/bash
wget http://nodejs.org/dist/node-latest.tar.gz
tar -zxvf node-latest.tar.gz
cd node-v* #如果有冲突,需改成明确的文件夹
./configure
make && sudo make install
node -v

CentOS包文件安装

CentOs下可以直接用yum包安装工具安装nodejs和npm。优点在于不用输入那么多命令。缺点是版本太低。当最新版是4.1时,yum只能安装0.10的版本,落后一年左右。

sudo yum install nodejs npm --enablerepo=epel
node -v

安装后常见问题

sudo npm提示command not found
sudo ln -s /usr/local/bin/node /usr/bin/node
sudo ln -s /usr/local/lib/node /usr/lib/node
sudo ln -s /usr/local/bin/npm /usr/bin/npm
sudo ln -s /usr/local/bin/node-waf /usr/bin/node-waf
npm安装包速度很慢。

可以改用境内的镜像源,具体方案见这里

安装

boost是除std外最常用的C++库,覆盖很多常用操作。目前最新的版本是1.59.0

1
2
3
wget http://sourceforge.net/projects/boost/files/boost/1.59.0/boost_1_59_0.tar.gz
tar -zxvf boost_1_59_0.tar.gz
cd boost_1_59_0

安装文档见官方文件。必要的几步是:

1
2
./bootstrap.sh --prefix=path/to/install
./b2 install

这会把boost安装到path/to/install,若不指定,默认将安装到/usr/local目录,但需要权限。安装后,安装目录下的文件结构为:

1
2
include # boost头文件
lib # boost连接文件

配置和使用

包含头文件一定要包括boost:

// test.cpp
#include <boost/algorithm/string.hpp>

如果安装到默认的/usr/local目录,编译时一般不需要做特别设置。如果安装在自定义的目录,则需要把头文件目录放入编译器搜寻路径:

g++ -o test test.cpp -Ipath/to/install

一部分boost是只有头文件实现,这时候上述设置已足够。有些库则还需链接二进制文件,如果没安装在默认位置,就需要指定二进制文件的位置:

g++ -o test test.cpp -Ipath/to/install -L path/to/install -lboost_system