贷款 – 步骤简谈

首先恭喜你找到一位放心、省心、有责任心和有能力的loan officer (简称LO)给你做贷款,在美国有时得自我宣传,请别见怪。贷款过程取决于情况30-60天,特别复杂的情况超过60天的也有。整个过程有不少步骤,这里简单介绍一下,帮助了解全过程。如果不想了解也没关系,你的LO会一步一步引导你完成贷款全过程。

  • 1. 准备材料:按照LO提供的材料准备清单来,这一步最快有人半小时搞定,因为所有资料都有电子版,并且平时就整理好了。正常的话,拿出1-2小时focus time应该就可以搞定。慢的话可能要一二天或更长。
  • 2. 你的LO会用你提供的资料,花时间做你的loan file,这个过程中要pull credit report。在我这里不用自己填贷款申请表,自己填经常不知道怎么填或很容易填错。做完loan file LO会提交给lender,之后如果利率在你预期内即可锁定,如果不在可以耐心等。一旦利率锁定,不能更改,利率涨跌都没关系了
  • 3. 三天内Lender会发一些文件给你电子签字。比较重要的文件有 Loan Estimate (LE),里面有很多数字。如果需要的话,你的LO会给你解释得很清楚(这是很多其它LO没有做到的地方)。注意即使是no refi cost,APR也会比rate高一丁点,但只要确保所有的cost被cover了就行。1003 application也可以快速看一下有没有错误。另外,过一些天lender会寄这些文档的hard copy到你家,因为你已经电子签字了所以不用管。
  • 4. Lender会初步review你的loan: 这一步可能会让你提供新的文件,
    1. 如果你的appraisal被waived的话,签PIW文件。我这边10套房子中在我的争取下有8-9套能够拿到waiver,所以拿到waiver是大概率事件。
    2. 如果appraisal不能被waive掉,就要做appraisal,可能要$700左右费用需要先付,投资房费用更高,因为需要估租金
    3. 签SSA-89, 用来核实你的SSN是否有问题
    4. 新的paystub或新的bank statements 等
    5. Condo需要HOA questionnaire,新Condo会需要很多其它文档。需要尽快联系你的HOA company,可能有$200左右的费用。有的HOA给力,有的不给力。邮件2天不回就打电话。
  • 5. 与时同时,Title and Escrow公司会联系你,与我合作很多的是Chicago Title
    • 填payoff authorization,escrow公司会找你现在lender拿payoff statement
    • 给你发wire transfer instructions,里面有不少红色字体说明提醒wire transfer fraud
    • 填questionnaire: Lender Name and Loan Account Number填你现在的loan信息;Pay Off Loan As Part of Closing 在Refinance时要填YES,因为新贷款会取代旧贷款;Have your entered into a forbearance…一般填NO,因为找我的客户财务状况都不错,不存在这种少见的情况
  • 6. 审核批准: 审核贷款的人的叫underwriter,中间可能找你要更多文档(e.g.解释credit report里的credit inquiries,解释大额转账),需要保证银行帐户有足够余额等。underwriter在所有conditions都被解决后会批准你的贷款,进入CTC (Cleared to Close)。之后会准备你的Closing Disclosure(CD),这个文档跟之前看到的Loan Estimate很像,但是是最后版本,建议仔细看。
  • 7. 签字和wire transfer:现在由于疫情,都是mobile notary and curbside signing。Escrow会派人上门,在车库门口签字,都戴口罩,保持距离,还是很安全的。当然你可以去他们办公室可以省$100。签完字后按wire instructions准备做Wire Transfer(一般银行要收$25手续费)。这一步如果从来没做过,建议签字前就把escrow银行账户加进去。

上面说的是简化的流程,有些cases会更加复杂,另外在后台有很多事要做,有时会非常繁琐,但你的LO与其它专业人士会搞定。

这整个过程中,给你打电话的人只有你的LO和最后约当面签字时间的人,给你发邮件的人只有Lender和Title/Escrow company,任何其它联系你贷款的人都跟本次贷款无关。Whenever in doubt,请随时联系你的LO。

贷款 – 询价篇

美国贷款利率由于多种原因现在处于近50年来历史低位,而且反常的是15yr/30yr fixed利率比ARM低(jumbo除外),从而触发一波波澜壮阔的refinance潮。做refinance首先涉及到一个询价的问题,今天分享一下这方面的经验。首先,每个人的情况都不一样(e.g. 房产估值,贷款额,信用分,收入,自住还是投资,是否cashout,是否买点或出closing cost等),每天的利率也不一样,你能拿到的利率跟你的朋友、同事和邻居可能都不一样,所以不要跟别人做过多比较。

如果工作和生活很忙,没有太多时间研究和比价,那就找一二位你信得过的loan officer来问一下,或找现银行/lender和一位你信得过的loan officer。也许拿到的不是市场最低,但仍然会是不错的deal。其实即使你花很多时间研究和比价,也不一定拿到最低。

如果你有足够时间、精力和兴趣来比价,可以多找几家来估,自己也研究一下适合自己情况的选项。但我在熟谙多个行业之后,经常说一句话,隔行如隔山,you don’t know what you don’t know。即使你在IT行业是partneer engineer/mgr,在另一个行业可能是entry level engineer。我知道一部分人花了大量时间研究比较,但是一些很基本的信息仍然不太了解。下面我分享一些有用信息供参考 (注意这里说的主要针对conforming/high balance loans,大额jumbo loans请直接联系大银行)。

  1. 因为每天利率不一样每天可能变化几次,在不同的时间比价可能不准确。譬如说在一家好的broker估价的那天正好利率一般,但在另一家一般的broker那估的那天利率正好比较好,你可能觉得第二家更好但其实不一定。所以为了公平准确起见,最好在同一天同一时间段拿多个估价,因为上午下午可能也不一样。
  2. 估价需要好几个参数,不需要递交个人资料(工资单,w-2, SSN等),也不需要pull credit。有的公司或个人可能找你要不少资料,然后直接就pull你的credit,这种做法有尽快锁定客户的嫌疑。我从来不会这么做,quote时不要个人资料,也不pull credit,并且给客户足够时间shop around。另外,被pull了credit也不用太担心,30天内为了贷款pull的多次credit会被算为一次pull,对credit score的影响并不大,特别是在你信用分比较好的情况下,所以实际上锁定不了你。
  3. IT圏朋友特别喜欢网上估价,方便而且不用跟人沟通,但在网上询价要特别注意一些“坑”
    1. 估价本身其实是不需要名字,邮件,电话的,更不需要pull credit。如果你提供了个人信息,之后收到多个骚扰电话不要奇怪
    2. 网上可能用很低的利率来“吸引人”(引人上勾),譬如说15yr fixed 2.125%,但你一看APR,可能要2.7%
    3. 网上可能用好一丁点的利率吸引人,譬如你找broker拿到的是15yr fixed 2.5% no cost,网上可能给2.49% no cost
    4. 网上可能会说$1 lender fee,利率也不错。当你开始做的时候,他们会说还有一笔third party fees
    5. 在网上看到不错的利率,但有时你不知道最后给你做的人到底是谁,人品如何,是否会给你争取最大利益;能力如何,能不能搞定你的贷款,碰到unexpected情况怎么办等等;服务如何,是否快速反应,是否沟通顺畅等等。我好几年前在zillow mortgage上找过一家利率最低的做贷款,最后拿到的不是网上给的估价,他给的原因在我成为专业人士后才知道他骗了我,但我当时没有能力判断。
  4. 不要过于在乎少量的费用区别,譬如第一家15yr fixed 2.5% no cost,另一家15yr fixed 2.5% cost<$500,这时最好看哪家信得过,哪家更放心,更省心,哪家能够给你提供更多的value,哪家愿意为你争取最大利益,哪家你将来还会用到,毕竟贷款中间可能有unexpected situation,以后可能还有贷款方面的需求。
  5. 贷款可能有多个选项供选择:是否买点,是否付一些closing cost,是否cashout,是否把cost做进贷款里,是否要escrow account,15年还是30年,是否等ARM到期等等,很多问题的回答需要足够了解你的个人情况和做专业的分析,不是一个简单的no cost option就能cover的。
  6. 不要不好意思去找你认识信得过的朋友询价,千万不要有询价就要找他/她的压力。一位好的broker,会很乐意提供询价和有用信息,不管做不做这一单。如果他/她介意,那大概率不是一位好的broker,不找也没关系。找认识信得过的人的好处是你可以得到真正实在的信息(不至于被欺骗),可以得到更多的attention,会为你争取最大利益,沟通更好,总的来说会提供更好的服务。
  7. 一旦选一家开始做了,特别是锁定利率后,最好不要随意退出。细节可参考我的另一篇blog:贷款 – refinance利率锁定后退出?

贷款 – refinance利率锁定后退出?

今天聊一小部分人碰到的困扰,那就是找loan agent/broker做refinance,并锁定了利率,然后利率变低或者有人给更好的quote,产生退出的念头怎么办?

首先,refinance中途退出没有任何法律责任,一般也没有财务责任(可能要付已经发生的credit report fee和appraisal fee),主要伤的是人品/reputation。虽然法律上有权利退出,但lock rate代表一种promise。如果这么做,要问一下是否还会break 别的promise?

其次,退出在broker这边有一些后果

  1. 你的broker可能被lender/broker公司罚款。有的lender会看fallout比例,到一定比例lender会中止跟这位broker的合作。当然也许没有这些问题
  2. 对broker所在公司有一些负面影响
  3. 一旦利率锁定,很多人开始为你服务。虽然你看到的主要是broker,后面可能还有loan processor, lender公司多个人, title公司多个人, escrow公司多个人, appraisal公司多个人等,退出意味这些人的付出白费,也意味着broker的credit会被影响。

最后,要考虑一些退出的好处是什么,很多时候就是0.125%的利率或者一小部分closing cost。今年迄今为止利率总体而言在下降,如果锁定3.0%还没close,利率降到了2.875%想要退出,那之后降到2.75%或更低要不要再次退出?要知道,如果利率一直下降,半年之后总可以再次refi。还有,为了一点小小的好处,退出后换一个人做refi,你对这位新的人了解有多少,靠谱不靠谱,更低的quote之后是不是有tricks,服务质量如何等等都需要问,不能只看表面上的数字。

有少数可以理解的原因退出没有多大影响 ,譬如说工作或房子出了大问题,不过这比较少见。对于中途退出有两条好的建议如下

  1. 确定找一个人做refi之前尽量做好功课,找某一位多次 quote都没问题。锁定利率跟买股票类似,很难或几乎不可能拿到最低点,满足预期即可。利率每天都在变化,没人能预测未来。
  2. 如果一定要退出,跟broker说明白,尽量坦诚,也许能找到好的解决方案,退出是lose-lose,找到方案是win-win。如果找不到解决方案也没关系,好好沟通,以后也许还有合作机会,毕竟没有人是完美的,这个世界也不完美,有时中途退出这种不多见的体验对双方都是一种成长的机会。

Repurposing my blog

My original idea was to make this a purely technical blog. When I was working on building the core infrastructure in Azure I thought that I could write something about how it was built. However I couldn’t write much because most of the time I was working on confidential projects. Cloud computing is a very competitive area as you may know. The 2nd reason is that there is already so much useful info on technologies and I wouldn’t be able to provide the best value by focusing on only technical stuff.

Anyway I have decided to write whatever I think can provide more value. The audience is people in the United States. I will write in Chinese most of the time since there is less info in Chinese. The main focus will be IT and Real Estate.

Solving CORS issue became easy

// If you don’t have time just jump to the last section – takeaway. If you do that solving CORS issue will be easy.

CORS stands for cross origin resource sharing. Let me use a simple example to explain the basics.
Say that you have a web site http://foo.com and it has a client side javascript which uses AJAX to call http://bar.com to get some info. That is a CORS request. To make the request successfully the server needs some change to allow CORS requests.

What is considered cross origin? Use http://foo.com as an example the below urls are considered as different origins.
Https://foo.com (different scheme)
Https://foo.com:8080 (different port)
Https://api.foo.com (different subdomain)
Https://bar.com (different domain)

In some cases a complaint browser may send a preflight request before it allows the request to be sent. Let me use a real case to explain.
Scenario: Client side uses token authentication to get info from server side

1. The client side javascript sends a POST request to https://localhost:44300/Account with token (basically http header “Authorizattion”:”Bearer aAbdkkixlkid…”)

2. The browser determines a preflight request is needed and it sends preflight request
3. The server has to respond with status code 2XX AND with required headers. In this case Access-Control-Allow-Origin cannot be *. Access-Control-Allow-Headers has to contain Authorization.
4. The browser then sends the actual request.

preflight request

actual_request

Actual request

preflight_request

Takeaway

I spent a lot of time solving my particular case and read tens of links. In the end it I found that it could be much simpler and quicker if I knew the below two things.
1. Read just one link https://msdn.microsoft.com/en-us/magazine/dn532203.aspx
2. Do check the console message when useing developer tools in browser. I didn’t check the console message. Otherwise it could have saved me a lot of time. The below is a screenshot from Chrome. It tells exactly what went wrong. It makes trouble shooting much easier.

chrome_console

Big Bonus if you are using ASP.NET Web API

Web API presents a unique challenge that the “/Token” service is different from the normal Web API controllers and the nuget cors package only works for web API. Some people suggest adding the below in web.config. It will work for “/Token” service but NOT web api especially when you are using https. The chrome browser does not allow “*” in Access-Control-Allow-Origin.

<add name=”Access-Control-Allow-Origin” value=”*” />

The solution is adding the below code at the top in IdentityConfig.cs.

if (string.Equals(context.Request.Uri.PathAndQuery, “/Token”, StringComparison.OrdinalIgnoreCase))
{
context.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { “http://foobar.com” });
}

 

 

TF401189: The source branch has been modified since the last merge attempt

If you are using Visual Studio Online and setup your project using TFS doing a code review is straightforward in visual studio.

However ff you are using Visual Studio Online and setup your project Git you might wonder how you can conduct a code review. You may do a quick search and then try ‘new pull request’ . You might get a weird error message you don’t understand.

TF401189: The source branch has been modified since the last merge attempt

Then you do another search (bing or google) you won’t find anything useful.

After trial and error I found that the root cause is that I didn’t use a topic branch. It is that simple.

Two useful links as below

https://www.visualstudio.com/get-started/code/git/pullrequest
https://blogs.msdn.microsoft.com/visualstudioalm/2014/06/10/conduct-a-git-pull-request-on-visual-studio-online/ (unfortunately all the images are missing)

Debugging a weird process crash issue

I recently debugged a very interesting process crash issue. The code was running in the cloud so I could not just attach the debugger as easily as on my dev box. Besides using debugger should be the last option in my opinion. So I logged on to the machine. Well before that I had to go a through a process to get permission and get the environment ready for security/compliance reason. I observed that the process was recycling by using taskmgr. So I did the below things
1. Checked the logs. No exceptions. No errors. No logs related to the crash
2. Checked crash dump files. No files were there
3. Checked OS eventlog. No crash events. Normally for any process crash there is at least one event

I never saw a process crash like this. But I knew the related code which caused this. It was a background task kicked off by calling Task.Run(). I thought that there must be some unhandled exceptions but I checked the code. There was already a catch block which caught all exceptions. However it was still possible that the catch block might throw exception. So I put another catch all inside the catch block to make sure that no unhandled exception was thrown.

However it did not work. The process was still crashing. I asked around about how a process could crash since I was relatively new to the code base. I got some pointers but none of them could match what I observed.

Then I had to turn to the last option. I copied windbg to all the nodes (remember that this is distributed system and there are multiple replicas) and hooked it up with the process. Boom! I got an exception in the debugger and then the process crashed.

I looked at the exception which was a normal .net exception. I could not think of a reason how it could cause crash. Then I looked into the call stack and saw the below line.
000007f9`8538bb27 : 0000001b`9c29cf80 0000001c`2c50c880 0000001c`2c50c888 0000001c`2c50c890 : mscorlib_ni!System.Environment.Exit(Int32)+0x7b

That was really weird. My code was calling a library from another team. So I got the tool ILSpy (I don’t use .net reflector anymore btw) and looked into the code. Man the code was calling Environment.Exit() when a certain exception threw! That explained everything. As for why the coding was doing so this was a new library and I guess that the piece of code was copied from somewhere else.

From the above text it looked that it didn’t take much time. In reality two days passed.

StackOverflow Exception, Long time no see

I have not seen a stackoverflow exception for a long time. Today I happened to bump into one in random test code. I will explain how it happened. What the code is used for and why it was written this way are not interesting to me and are not in the scope of this post.

    public class ChildClass : BaseClass
    {
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                base.Dispose();
                //DO something
            }
        }
    }

    public class BaseClass
    {
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                //DO something
            }
        }
    }

As you can see the stackoverflow exception will happen as the below
1. Child.Dispose(bool)
2. Base.Dispose()
3. Child.Dispose(bool)
4. Base.Dispose()

Persistent MAC Address in Cloud (Microsoft Azure /Amazon AWS / Google GCP / OpenStack)

What is a NIC? It is the network adaptor on your machine. MAC address is the physical address of the adaptor. You can consider it as fixed on your machine for this discussion. However the NIC you get on a VM in cloud is a virtualized one. If you delete a VM and create a new one you will probably get a new MAC address. Why do we need a persistent MAC address? Many licensed software rely on MAC for the licenses. If you want to move licenses from one VM to another persistent MAC address becomes very useful.

It looks like a small feature. Let’s look at a few major cloud provides to see how they deal with this.

Microsoft Azure
NIC https://azure.microsoft.com/en-us/documentation/articles/resource-groups-networking/#nic

It is not available yet. According to the post it is already planned.
http://feedback.azure.com/forums/216843-virtual-machines/suggestions/5217933-static-mac-address

Amazon AWS
It is already supported. The below statement is from the official AWS documentation.
The interface maintains its private IP addresses, Elastic IP addresses, and MAC address.
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html

When did AWS start supporting this? I have no idea. From this post it was not supported yet in 3/2011 but it was supported in 2/2012. https://forums.aws.amazon.com/thread.jspa?messageID=107448&amp;

Google Cloud Platform (GCP)
Google does not seem to support persistent MAC address from the public official doc.
https://cloud.google.com/compute/docs/networking?hl=en#networks

OpenStack
It does not seem to support persistent MAC address from the public official doc.
https://www.openstack.org/software/openstack-networking/

C# import .pfx file to cert store and CryptographicException: Keyset does not exist

I had to add a .pfx file programmatically today but got an exception when accessing this file in code after importing . The simple demo code and the exception are as follows.

X509KeyStorageFlags.PersistKeySet);
X509Store certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadWrite);
certificateStore.Add(cert);

System.Security.Cryptography.CryptographicException: Keyset does not exist at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer) at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeP rovHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle) at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair() at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()

I did a search and the top results took me to the direction of adding permission on the private key. There are two versions of code for this task.

http://www.codeproject.com/script/Forums/View.aspx?fid=1649&msg=2062983

private static void AddAccessToCertificate(X509Certificate2 cert)
{
  RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;
  if (rsa != null) {
    string keyfilepath = FindKeyLocation(rsa.CspKeyContainerInfo.UniqueKeyContainerName);
    FileInfo file = new FileInfo(keyfilepath + “\\” + rsa.CspKeyContainerInfo.UniqueKeyContainerName);
    FileSecurity fs = file.GetAccessControl();
    var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
    var everyone = (NTAccount)sid.Translate(typeof(NTAccount)); 
    fs.AddAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.FullControl, AccessControlType.Allow));
    file.SetAccessControl(fs);
  }
}

http://stackoverflow.com/questions/425688/how-to-set-read-permission-on-the-private-key-file-of-x-509-certificate-from-ne

private static void AddAccessToCertificate(X509Certificate2 cert)
{
  RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;
  if (rsa != null) {
    var cspParams = new CspParameters(rsa.CspKeyContainerInfo.ProviderType, rsa.CspKeyContainerInfo.ProviderName, rsa.CspKeyContainerInfo.KeyContainerName)
  {
    Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore, CryptoKeySecurity = rsa.CspKeyContainerInfo.CryptoKeySecurity};
   cspParams.CryptoKeySecurity.AddAccessRule(new CryptoKeyAccessRule(WindowsIdentity.GetCurrent().User,
   CryptoKeyRights.FullControl, AccessControlType.Allow));
   using (var rsa2 = new RSACryptoServiceProvider(cspParams))
   { // Only created to persist the rule change in the CryptoKeySecurity }
  } 
}

Unfortunately both solutions did not work for me. This code was just a part of my other code. Every time I had to change the code, build and re-run test case. It took me a couple of hours to prove that both solutions didn’t work. I think that it was time to try in differet direction. So I read more search liks. It turned out that the fix is simple. Just adding X509KeyStorageFlags.PersistKeySet in the constructor of X509Certificate2 made it work.

var cert = new X509Certificate2(certificatePath, password, X509KeyStorageFlags.PersistKeySet);
X509Store certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadWrite);
certificateStore.Add(cert);

The source of this solution is http://support.microsoft.com/kb/950090